#![allow(clippy::forget_copy)] // Used by dynasm. #![warn(unused_imports)] use crate::emitter_x64::*; use crate::machine::*; use crate::protect_unix; use dynasmrt::{x64::Assembler, AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi}; use smallvec::SmallVec; use std::ptr::NonNull; use std::{ any::Any, collections::{BTreeMap, HashMap}, sync::{Arc, RwLock}, }; use wasmer_runtime_core::{ backend::{ sys::Memory, Backend, CacheGen, CompilerConfig, MemoryBoundCheckMode, RunnableModule, Token, }, cache::{Artifact, Error as CacheError}, codegen::*, fault::raw::register_preservation_trampoline, loader::CodeMemory, memory::MemoryType, module::{ModuleInfo, ModuleInner}, state::{ x64::new_machine_state, x64::X64Register, FunctionStateMap, MachineState, MachineValue, ModuleStateMap, OffsetInfo, SuspendOffset, WasmAbstractValue, }, structures::{Map, TypedIndex}, typed_func::Wasm, types::{ FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex, TableIndex, Type, }, vm::{self, LocalGlobal, LocalTable, INTERNALS_SIZE}, }; use wasmparser::{MemoryImmediate, Operator, Type as WpType, TypeOrFuncType as WpTypeOrFuncType}; lazy_static! { /// Performs a System V call to `target` with [stack_top..stack_base] as the argument list, from right to left. static ref CONSTRUCT_STACK_AND_CALL_WASM: unsafe extern "C" fn (stack_top: *const u64, stack_base: *const u64, ctx: *mut vm::Ctx, target: *const vm::Func) -> u64 = { let mut assembler = Assembler::new().unwrap(); let offset = assembler.offset(); dynasm!( assembler ; push r15 ; push r14 ; push r13 ; push r12 ; push r11 ; push rbp ; mov rbp, rsp ; mov r15, rdi ; mov r14, rsi ; mov r13, rdx ; mov r12, rcx ; mov rdi, r13 // ctx ; sub r14, 8 ; cmp r14, r15 ; jb >stack_ready ; mov rsi, [r14] ; sub r14, 8 ; cmp r14, r15 ; jb >stack_ready ; mov rdx, [r14] ; sub r14, 8 ; cmp r14, r15 ; jb >stack_ready ; mov rcx, [r14] ; sub r14, 8 ; cmp r14, r15 ; jb >stack_ready ; mov r8, [r14] ; sub r14, 8 ; cmp r14, r15 ; jb >stack_ready ; mov r9, [r14] ; sub r14, 8 ; cmp r14, r15 ; jb >stack_ready ; mov rax, r14 ; sub rax, r15 ; sub rsp, rax ; sub rsp, 8 ; mov rax, QWORD 0xfffffffffffffff0u64 as i64 ; and rsp, rax ; mov rax, rsp ; loop_begin: ; mov r11, [r14] ; mov [rax], r11 ; sub r14, 8 ; add rax, 8 ; cmp r14, r15 ; jb >stack_ready ; jmp <loop_begin ; stack_ready: ; mov rax, QWORD 0xfffffffffffffff0u64 as i64 ; and rsp, rax ; call r12 ; mov rsp, rbp ; pop rbp ; pop r11 ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret ); let buf = assembler.finalize().unwrap(); let ret = unsafe { ::std::mem::transmute(buf.ptr(offset)) }; ::std::mem::forget(buf); ret }; } pub struct X64ModuleCodeGenerator { functions: Vec<X64FunctionCode>, signatures: Option<Arc<Map<SigIndex, FuncSig>>>, function_signatures: Option<Arc<Map<FuncIndex, SigIndex>>>, function_labels: Option<HashMap<usize, (DynamicLabel, Option<AssemblyOffset>)>>, assembler: Option<Assembler>, func_import_count: usize, config: Option<Arc<CodegenConfig>>, } pub struct X64FunctionCode { local_function_id: usize, signatures: Arc<Map<SigIndex, FuncSig>>, function_signatures: Arc<Map<FuncIndex, SigIndex>>, fsm: FunctionStateMap, offset: usize, assembler: Option<Assembler>, function_labels: Option<HashMap<usize, (DynamicLabel, Option<AssemblyOffset>)>>, breakpoints: Option< HashMap< AssemblyOffset, Box<dyn Fn(BreakpointInfo) -> Result<(), Box<dyn Any>> + Send + Sync + 'static>, >, >, returns: SmallVec<[WpType; 1]>, locals: Vec<Location>, num_params: usize, num_locals: usize, value_stack: Vec<Location>, control_stack: Vec<ControlFrame>, machine: Machine, unreachable_depth: usize, config: Arc<CodegenConfig>, } enum FuncPtrInner {} #[repr(transparent)] #[derive(Copy, Clone, Debug)] struct FuncPtr(*const FuncPtrInner); unsafe impl Send for FuncPtr {} unsafe impl Sync for FuncPtr {} pub struct X64ExecutionContext { #[allow(dead_code)] code: CodeMemory, #[allow(dead_code)] functions: Vec<X64FunctionCode>, function_pointers: Vec<FuncPtr>, function_offsets: Vec<AssemblyOffset>, signatures: Arc<Map<SigIndex, FuncSig>>, breakpoints: BreakpointMap, func_import_count: usize, msm: ModuleStateMap, } #[derive(Debug)] pub struct ControlFrame { pub label: DynamicLabel, pub loop_like: bool, pub if_else: IfElseState, pub returns: SmallVec<[WpType; 1]>, pub value_stack_depth: usize, pub state: MachineState, pub state_diff_id: usize, } #[derive(Debug, Copy, Clone)] pub enum IfElseState { None, If(DynamicLabel), Else, } impl RunnableModule for X64ExecutionContext { fn get_func( &self, _: &ModuleInfo, local_func_index: LocalFuncIndex, ) -> Option<NonNull<vm::Func>> { self.function_pointers[self.func_import_count..] .get(local_func_index.index()) .and_then(|ptr| NonNull::new(ptr.0 as *mut vm::Func)) } fn get_module_state_map(&self) -> Option<ModuleStateMap> { Some(self.msm.clone()) } fn get_breakpoints(&self) -> Option<BreakpointMap> { Some(self.breakpoints.clone()) } unsafe fn patch_local_function(&self, idx: usize, target_address: usize) -> bool { /* 0: 48 b8 42 42 42 42 42 42 42 42 movabsq $4774451407313060418, %rax a: 49 bb 43 43 43 43 43 43 43 43 movabsq $4846791580151137091, %r11 14: 41 ff e3 jmpq *%r11 */ #[repr(packed)] struct Trampoline { movabsq_rax: [u8; 2], addr_rax: u64, movabsq_r11: [u8; 2], addr_r11: u64, jmpq_r11: [u8; 3], } self.code.make_writable(); let trampoline = &mut *(self.function_pointers[self.func_import_count + idx].0 as *const Trampoline as *mut Trampoline); trampoline.movabsq_rax[0] = 0x48; trampoline.movabsq_rax[1] = 0xb8; trampoline.addr_rax = target_address as u64; trampoline.movabsq_r11[0] = 0x49; trampoline.movabsq_r11[1] = 0xbb; trampoline.addr_r11 = register_preservation_trampoline as unsafe extern "C" fn() as usize as u64; trampoline.jmpq_r11[0] = 0x41; trampoline.jmpq_r11[1] = 0xff; trampoline.jmpq_r11[2] = 0xe3; self.code.make_executable(); true } fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option<Wasm> { use std::ffi::c_void; use wasmer_runtime_core::typed_func::WasmTrapInfo; unsafe extern "C" fn invoke( _trampoline: unsafe extern "C" fn( *mut vm::Ctx, NonNull<vm::Func>, *const u64, *mut u64, ), ctx: *mut vm::Ctx, func: NonNull<vm::Func>, args: *const u64, rets: *mut u64, trap_info: *mut WasmTrapInfo, user_error: *mut Option<Box<dyn Any>>, num_params_plus_one: Option<NonNull<c_void>>, ) -> bool { let rm: &Box<dyn RunnableModule> = &(&*(*ctx).module).runnable_module; let execution_context = ::std::mem::transmute_copy::<&dyn RunnableModule, &X64ExecutionContext>(&&**rm); let args = std::slice::from_raw_parts( args, num_params_plus_one.unwrap().as_ptr() as usize - 1, ); let args_reverse: SmallVec<[u64; 8]> = args.iter().cloned().rev().collect(); let ret = match protect_unix::call_protected( || { CONSTRUCT_STACK_AND_CALL_WASM( args_reverse.as_ptr(), args_reverse.as_ptr().offset(args_reverse.len() as isize), ctx, func.as_ptr(), ) }, Some(execution_context.breakpoints.clone()), ) { Ok(x) => { if !rets.is_null() { *rets = x; } true } Err(err) => { match err { protect_unix::CallProtError::Trap(info) => *trap_info = info, protect_unix::CallProtError::Error(data) => *user_error = Some(data), } false } }; ret } unsafe extern "C" fn dummy_trampoline( _: *mut vm::Ctx, _: NonNull<vm::Func>, _: *const u64, _: *mut u64, ) { unreachable!() } Some(unsafe { Wasm::from_raw_parts( dummy_trampoline, invoke, NonNull::new((self.signatures.get(sig_index).unwrap().params().len() + 1) as _), // +1 to keep it non-zero ) }) } unsafe fn do_early_trap(&self, data: Box<dyn Any>) -> ! { protect_unix::TRAP_EARLY_DATA.with(|x| x.set(Some(data))); protect_unix::trigger_trap(); } fn get_code(&self) -> Option<&[u8]> { Some(&self.code) } fn get_offsets(&self) -> Option<Vec<usize>> { Some(self.function_offsets.iter().map(|x| x.0).collect()) } fn get_local_function_offsets(&self) -> Option<Vec<usize>> { Some( self.function_offsets[self.func_import_count..] .iter() .map(|x| x.0) .collect(), ) } } #[derive(Debug)] pub struct CodegenError { pub message: String, } #[derive(Copy, Clone, Debug)] struct CodegenConfig { memory_bound_check_mode: MemoryBoundCheckMode, enforce_stack_check: bool, track_state: bool, } impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError> for X64ModuleCodeGenerator { fn new() -> X64ModuleCodeGenerator { X64ModuleCodeGenerator { functions: vec![], signatures: None, function_signatures: None, function_labels: Some(HashMap::new()), assembler: Some(Assembler::new().unwrap()), func_import_count: 0, config: None, } } fn backend_id() -> Backend { Backend::Singlepass } fn check_precondition(&mut self, _module_info: &ModuleInfo) -> Result<(), CodegenError> { Ok(()) } fn next_function( &mut self, _module_info: Arc<RwLock<ModuleInfo>>, ) -> Result<&mut X64FunctionCode, CodegenError> { let (mut assembler, mut function_labels, breakpoints) = match self.functions.last_mut() { Some(x) => ( x.assembler.take().unwrap(), x.function_labels.take().unwrap(), x.breakpoints.take().unwrap(), ), None => ( self.assembler.take().unwrap(), self.function_labels.take().unwrap(), HashMap::new(), ), }; let begin_offset = assembler.offset(); let begin_label_info = function_labels .entry(self.functions.len() + self.func_import_count) .or_insert_with(|| (assembler.new_dynamic_label(), None)); begin_label_info.1 = Some(begin_offset); let begin_label = begin_label_info.0; let mut machine = Machine::new(); machine.track_state = self.config.as_ref().unwrap().track_state; dynasm!( assembler ; => begin_label //; int 3 ); let code = X64FunctionCode { local_function_id: self.functions.len(), signatures: self.signatures.as_ref().unwrap().clone(), function_signatures: self.function_signatures.as_ref().unwrap().clone(), fsm: FunctionStateMap::new(new_machine_state(), self.functions.len(), 32, vec![]), // only a placeholder; this is initialized later in `begin_body` offset: begin_offset.0, assembler: Some(assembler), function_labels: Some(function_labels), breakpoints: Some(breakpoints), returns: smallvec![], locals: vec![], num_params: 0, num_locals: 0, value_stack: vec![], control_stack: vec![], machine, unreachable_depth: 0, config: self.config.as_ref().unwrap().clone(), }; self.functions.push(code); Ok(self.functions.last_mut().unwrap()) } fn finalize( mut self, _: &ModuleInfo, ) -> Result<(X64ExecutionContext, Box<dyn CacheGen>), CodegenError> { let (assembler, function_labels, breakpoints) = match self.functions.last_mut() { Some(x) => ( x.assembler.take().unwrap(), x.function_labels.take().unwrap(), x.breakpoints.take().unwrap(), ), None => ( self.assembler.take().unwrap(), self.function_labels.take().unwrap(), HashMap::new(), ), }; let total_size = assembler.get_offset().0; let _output = assembler.finalize().unwrap(); let mut output = CodeMemory::new(_output.len()); output[0.._output.len()].copy_from_slice(&_output); output.make_executable(); let mut out_labels: Vec<FuncPtr> = vec![]; let mut out_offsets: Vec<AssemblyOffset> = vec![]; for i in 0..function_labels.len() { let (_, offset) = match function_labels.get(&i) { Some(x) => x, None => { return Err(CodegenError { message: format!("label not found"), }); } }; let offset = match offset { Some(x) => x, None => { return Err(CodegenError { message: format!("offset is none"), }); } }; out_labels.push(FuncPtr( unsafe { output.as_ptr().offset(offset.0 as isize) } as _, )); out_offsets.push(*offset); } let breakpoints: Arc<HashMap<_, _>> = Arc::new( breakpoints .into_iter() .map(|(offset, f)| { ( unsafe { output.as_ptr().offset(offset.0 as isize) } as usize, f, ) }) .collect(), ); let local_function_maps: BTreeMap<usize, FunctionStateMap> = self .functions .iter() .map(|x| (x.offset, x.fsm.clone())) .collect(); struct Placeholder; impl CacheGen for Placeholder { fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError> { Err(CacheError::Unknown( "the singlepass backend doesn't support caching yet".to_string(), )) } } Ok(( X64ExecutionContext { code: output, functions: self.functions, signatures: self.signatures.as_ref().unwrap().clone(), breakpoints: breakpoints, func_import_count: self.func_import_count, function_pointers: out_labels, function_offsets: out_offsets, msm: ModuleStateMap { local_functions: local_function_maps, total_size, }, }, Box::new(Placeholder), )) } fn feed_signatures(&mut self, signatures: Map<SigIndex, FuncSig>) -> Result<(), CodegenError> { self.signatures = Some(Arc::new(signatures)); Ok(()) } fn feed_function_signatures( &mut self, assoc: Map<FuncIndex, SigIndex>, ) -> Result<(), CodegenError> { self.function_signatures = Some(Arc::new(assoc)); Ok(()) } fn feed_import_function(&mut self) -> Result<(), CodegenError> { let labels = self.function_labels.as_mut().unwrap(); let id = labels.len(); let a = self.assembler.as_mut().unwrap(); let offset = a.offset(); let label = a.get_label(); a.emit_label(label); labels.insert(id, (label, Some(offset))); // Emits a tail call trampoline that loads the address of the target import function // from Ctx and jumps to it. a.emit_mov( Size::S64, Location::Memory(GPR::RDI, vm::Ctx::offset_imported_funcs() as i32), Location::GPR(GPR::RAX), ); a.emit_mov( Size::S64, Location::Memory( GPR::RAX, (vm::ImportedFunc::size() as usize * id + vm::ImportedFunc::offset_func() as usize) as i32, ), Location::GPR(GPR::RAX), ); a.emit_jmp_location(Location::GPR(GPR::RAX)); self.func_import_count += 1; Ok(()) } fn feed_compiler_config(&mut self, config: &CompilerConfig) -> Result<(), CodegenError> { self.config = Some(Arc::new(CodegenConfig { memory_bound_check_mode: config.memory_bound_check_mode, enforce_stack_check: config.enforce_stack_check, track_state: config.track_state, })); Ok(()) } unsafe fn from_cache(_artifact: Artifact, _: Token) -> Result<ModuleInner, CacheError> { Err(CacheError::Unknown( "the singlepass compiler API doesn't support caching yet".to_string(), )) } } impl X64FunctionCode { fn mark_trappable( a: &mut Assembler, m: &Machine, fsm: &mut FunctionStateMap, control_stack: &mut [ControlFrame], ) { let state_diff_id = Self::get_state_diff(m, fsm, control_stack); let offset = a.get_offset().0; fsm.trappable_offsets.insert( offset, OffsetInfo { end_offset: offset + 1, activate_offset: offset, diff_id: state_diff_id, }, ); fsm.wasm_offset_to_target_offset .insert(m.state.wasm_inst_offset, SuspendOffset::Trappable(offset)); } /// Moves `loc` to a valid location for `div`/`idiv`. fn emit_relaxed_xdiv( a: &mut Assembler, m: &mut Machine, op: fn(&mut Assembler, Size, Location), sz: Size, loc: Location, fsm: &mut FunctionStateMap, control_stack: &mut [ControlFrame], ) { m.state.wasm_stack_private_depth += 1; match loc { Location::Imm64(_) | Location::Imm32(_) => { a.emit_mov(sz, loc, Location::GPR(GPR::RCX)); // must not be used during div (rax, rdx) Self::mark_trappable(a, m, fsm, control_stack); op(a, sz, Location::GPR(GPR::RCX)); } _ => { Self::mark_trappable(a, m, fsm, control_stack); op(a, sz, loc); } } m.state.wasm_stack_private_depth -= 1; } /// Moves `src` and `dst` to valid locations for `movzx`/`movsx`. fn emit_relaxed_zx_sx( a: &mut Assembler, m: &mut Machine, op: fn(&mut Assembler, Size, Location, Size, Location), sz_src: Size, mut src: Location, sz_dst: Size, dst: Location, ) { let tmp_src = m.acquire_temp_gpr().unwrap(); let tmp_dst = m.acquire_temp_gpr().unwrap(); match src { Location::Imm32(_) | Location::Imm64(_) => { a.emit_mov(Size::S64, src, Location::GPR(tmp_src)); src = Location::GPR(tmp_src); } Location::Memory(_, _) | Location::GPR(_) => {} _ => unreachable!(), } match dst { Location::Imm32(_) | Location::Imm64(_) => unreachable!(), Location::Memory(_, _) => { op(a, sz_src, src, sz_dst, Location::GPR(tmp_dst)); a.emit_mov(Size::S64, Location::GPR(tmp_dst), dst); } Location::GPR(_) => { op(a, sz_src, src, sz_dst, dst); } _ => unreachable!(), } m.release_temp_gpr(tmp_dst); m.release_temp_gpr(tmp_src); } /// Moves `src` and `dst` to valid locations for generic instructions. fn emit_relaxed_binop( a: &mut Assembler, m: &mut Machine, op: fn(&mut Assembler, Size, Location, Location), sz: Size, src: Location, dst: Location, ) { enum RelaxMode { Direct, SrcToGPR, DstToGPR, BothToGPR, } let mode = match (src, dst) { (Location::GPR(_), Location::GPR(_)) if (op as *const u8 == Assembler::emit_imul as *const u8) => { RelaxMode::Direct } _ if (op as *const u8 == Assembler::emit_imul as *const u8) => RelaxMode::BothToGPR, (Location::Memory(_, _), Location::Memory(_, _)) => RelaxMode::SrcToGPR, (Location::Imm64(_), Location::Imm64(_)) | (Location::Imm64(_), Location::Imm32(_)) => { RelaxMode::BothToGPR } (_, Location::Imm32(_)) | (_, Location::Imm64(_)) => RelaxMode::DstToGPR, (Location::Imm64(_), Location::Memory(_, _)) => RelaxMode::SrcToGPR, (Location::Imm64(_), Location::GPR(_)) if (op as *const u8 != Assembler::emit_mov as *const u8) => { RelaxMode::SrcToGPR } (_, Location::XMM(_)) => RelaxMode::SrcToGPR, _ => RelaxMode::Direct, }; match mode { RelaxMode::SrcToGPR => { let temp = m.acquire_temp_gpr().unwrap(); a.emit_mov(sz, src, Location::GPR(temp)); op(a, sz, Location::GPR(temp), dst); m.release_temp_gpr(temp); } RelaxMode::DstToGPR => { let temp = m.acquire_temp_gpr().unwrap(); a.emit_mov(sz, dst, Location::GPR(temp)); op(a, sz, src, Location::GPR(temp)); m.release_temp_gpr(temp); } RelaxMode::BothToGPR => { let temp_src = m.acquire_temp_gpr().unwrap(); let temp_dst = m.acquire_temp_gpr().unwrap(); a.emit_mov(sz, src, Location::GPR(temp_src)); a.emit_mov(sz, dst, Location::GPR(temp_dst)); op(a, sz, Location::GPR(temp_src), Location::GPR(temp_dst)); match dst { Location::Memory(_, _) | Location::GPR(_) => { a.emit_mov(sz, Location::GPR(temp_dst), dst); } _ => {} } m.release_temp_gpr(temp_dst); m.release_temp_gpr(temp_src); } RelaxMode::Direct => { op(a, sz, src, dst); } } } /// Moves `src1` and `src2` to valid locations and possibly adds a layer of indirection for `dst` for AVX instructions. fn emit_relaxed_avx( a: &mut Assembler, m: &mut Machine, op: fn(&mut Assembler, XMM, XMMOrMemory, XMM), src1: Location, src2: Location, dst: Location, ) { Self::emit_relaxed_avx_base( a, m, |a, _, src1, src2, dst| op(a, src1, src2, dst), src1, src2, dst, ) } /// Moves `src1` and `src2` to valid locations and possibly adds a layer of indirection for `dst` for AVX instructions. fn emit_relaxed_avx_base<F: FnOnce(&mut Assembler, &mut Machine, XMM, XMMOrMemory, XMM)>( a: &mut Assembler, m: &mut Machine, op: F, src1: Location, src2: Location, dst: Location, ) { let tmp1 = m.acquire_temp_xmm().unwrap(); let tmp2 = m.acquire_temp_xmm().unwrap(); let tmp3 = m.acquire_temp_xmm().unwrap(); let tmpg = m.acquire_temp_gpr().unwrap(); let src1 = match src1 { Location::XMM(x) => x, Location::GPR(_) | Location::Memory(_, _) => { a.emit_mov(Size::S64, src1, Location::XMM(tmp1)); tmp1 } Location::Imm32(_) => { a.emit_mov(Size::S32, src1, Location::GPR(tmpg)); a.emit_mov(Size::S32, Location::GPR(tmpg), Location::XMM(tmp1)); tmp1 } Location::Imm64(_) => { a.emit_mov(Size::S64, src1, Location::GPR(tmpg)); a.emit_mov(Size::S64, Location::GPR(tmpg), Location::XMM(tmp1)); tmp1 } _ => unreachable!(), }; let src2 = match src2 { Location::XMM(x) => XMMOrMemory::XMM(x), Location::Memory(base, disp) => XMMOrMemory::Memory(base, disp), Location::GPR(_) => { a.emit_mov(Size::S64, src2, Location::XMM(tmp2)); XMMOrMemory::XMM(tmp2) } Location::Imm32(_) => { a.emit_mov(Size::S32, src2, Location::GPR(tmpg)); a.emit_mov(Size::S32, Location::GPR(tmpg), Location::XMM(tmp2)); XMMOrMemory::XMM(tmp2) } Location::Imm64(_) => { a.emit_mov(Size::S64, src2, Location::GPR(tmpg)); a.emit_mov(Size::S64, Location::GPR(tmpg), Location::XMM(tmp2)); XMMOrMemory::XMM(tmp2) } _ => unreachable!(), }; match dst { Location::XMM(x) => { op(a, m, src1, src2, x); } Location::Memory(_, _) | Location::GPR(_) => { op(a, m, src1, src2, tmp3); a.emit_mov(Size::S64, Location::XMM(tmp3), dst); } _ => unreachable!(), } m.release_temp_gpr(tmpg); m.release_temp_xmm(tmp3); m.release_temp_xmm(tmp2); m.release_temp_xmm(tmp1); } /// I32 binary operation with both operands popped from the virtual stack. fn emit_binop_i32( a: &mut Assembler, m: &mut Machine, value_stack: &mut Vec<Location>, f: fn(&mut Assembler, Size, Location, Location), ) { // Using Red Zone here. let loc_b = get_location_released(a, m, value_stack.pop().unwrap()); let loc_a = get_location_released(a, m, value_stack.pop().unwrap()); let ret = m.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(value_stack.len()))], false, )[0]; if loc_a != ret { let tmp = m.acquire_temp_gpr().unwrap(); Self::emit_relaxed_binop( a, m, Assembler::emit_mov, Size::S32, loc_a, Location::GPR(tmp), ); Self::emit_relaxed_binop(a, m, f, Size::S32, loc_b, Location::GPR(tmp)); Self::emit_relaxed_binop( a, m, Assembler::emit_mov, Size::S32, Location::GPR(tmp), ret, ); m.release_temp_gpr(tmp); } else { Self::emit_relaxed_binop(a, m, f, Size::S32, loc_b, ret); } value_stack.push(ret); } /// I64 binary operation with both operands popped from the virtual stack. fn emit_binop_i64( a: &mut Assembler, m: &mut Machine, value_stack: &mut Vec<Location>, f: fn(&mut Assembler, Size, Location, Location), ) { // Using Red Zone here. let loc_b = get_location_released(a, m, value_stack.pop().unwrap()); let loc_a = get_location_released(a, m, value_stack.pop().unwrap()); let ret = m.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(value_stack.len()))], false, )[0]; if loc_a != ret { let tmp = m.acquire_temp_gpr().unwrap(); Self::emit_relaxed_binop( a, m, Assembler::emit_mov, Size::S64, loc_a, Location::GPR(tmp), ); Self::emit_relaxed_binop(a, m, f, Size::S64, loc_b, Location::GPR(tmp)); Self::emit_relaxed_binop( a, m, Assembler::emit_mov, Size::S64, Location::GPR(tmp), ret, ); m.release_temp_gpr(tmp); } else { Self::emit_relaxed_binop(a, m, f, Size::S64, loc_b, ret); } value_stack.push(ret); } /// I32 comparison with `loc_b` from input. fn emit_cmpop_i32_dynamic_b( a: &mut Assembler, m: &mut Machine, value_stack: &mut Vec<Location>, c: Condition, loc_b: Location, ) { // Using Red Zone here. let loc_a = get_location_released(a, m, value_stack.pop().unwrap()); let ret = m.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(value_stack.len()))], false, )[0]; match ret { Location::GPR(x) => { Self::emit_relaxed_binop(a, m, Assembler::emit_cmp, Size::S32, loc_b, loc_a); a.emit_set(c, x); a.emit_and(Size::S32, Location::Imm32(0xff), Location::GPR(x)); } Location::Memory(_, _) => { let tmp = m.acquire_temp_gpr().unwrap(); Self::emit_relaxed_binop(a, m, Assembler::emit_cmp, Size::S32, loc_b, loc_a); a.emit_set(c, tmp); a.emit_and(Size::S32, Location::Imm32(0xff), Location::GPR(tmp)); a.emit_mov(Size::S32, Location::GPR(tmp), ret); m.release_temp_gpr(tmp); } _ => unreachable!(), } value_stack.push(ret); } /// I32 comparison with both operands popped from the virtual stack. fn emit_cmpop_i32( a: &mut Assembler, m: &mut Machine, value_stack: &mut Vec<Location>, c: Condition, ) { let loc_b = get_location_released(a, m, value_stack.pop().unwrap()); Self::emit_cmpop_i32_dynamic_b(a, m, value_stack, c, loc_b); } /// I64 comparison with `loc_b` from input. fn emit_cmpop_i64_dynamic_b( a: &mut Assembler, m: &mut Machine, value_stack: &mut Vec<Location>, c: Condition, loc_b: Location, ) { // Using Red Zone here. let loc_a = get_location_released(a, m, value_stack.pop().unwrap()); let ret = m.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(value_stack.len()))], false, )[0]; match ret { Location::GPR(x) => { Self::emit_relaxed_binop(a, m, Assembler::emit_cmp, Size::S64, loc_b, loc_a); a.emit_set(c, x); a.emit_and(Size::S32, Location::Imm32(0xff), Location::GPR(x)); } Location::Memory(_, _) => { let tmp = m.acquire_temp_gpr().unwrap(); Self::emit_relaxed_binop(a, m, Assembler::emit_cmp, Size::S64, loc_b, loc_a); a.emit_set(c, tmp); a.emit_and(Size::S32, Location::Imm32(0xff), Location::GPR(tmp)); a.emit_mov(Size::S32, Location::GPR(tmp), ret); m.release_temp_gpr(tmp); } _ => unreachable!(), } value_stack.push(ret); } /// I64 comparison with both operands popped from the virtual stack. fn emit_cmpop_i64( a: &mut Assembler, m: &mut Machine, value_stack: &mut Vec<Location>, c: Condition, ) { let loc_b = get_location_released(a, m, value_stack.pop().unwrap()); Self::emit_cmpop_i64_dynamic_b(a, m, value_stack, c, loc_b); } /// I32 `lzcnt`/`tzcnt`/`popcnt` with operand popped from the virtual stack. fn emit_xcnt_i32( a: &mut Assembler, m: &mut Machine, value_stack: &mut Vec<Location>, f: fn(&mut Assembler, Size, Location, Location), ) { let loc = get_location_released(a, m, value_stack.pop().unwrap()); let ret = m.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(value_stack.len()))], false, )[0]; match loc { Location::Imm32(_) => { let tmp = m.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S32, loc, Location::GPR(tmp)); if let Location::Memory(_, _) = ret { let out_tmp = m.acquire_temp_gpr().unwrap(); f(a, Size::S32, Location::GPR(tmp), Location::GPR(out_tmp)); a.emit_mov(Size::S32, Location::GPR(out_tmp), ret); m.release_temp_gpr(out_tmp); } else { f(a, Size::S32, Location::GPR(tmp), ret); } m.release_temp_gpr(tmp); } Location::Memory(_, _) | Location::GPR(_) => { if let Location::Memory(_, _) = ret { let out_tmp = m.acquire_temp_gpr().unwrap(); f(a, Size::S32, loc, Location::GPR(out_tmp)); a.emit_mov(Size::S32, Location::GPR(out_tmp), ret); m.release_temp_gpr(out_tmp); } else { f(a, Size::S32, loc, ret); } } _ => unreachable!(), } value_stack.push(ret); } /// I64 `lzcnt`/`tzcnt`/`popcnt` with operand popped from the virtual stack. fn emit_xcnt_i64( a: &mut Assembler, m: &mut Machine, value_stack: &mut Vec<Location>, f: fn(&mut Assembler, Size, Location, Location), ) { let loc = get_location_released(a, m, value_stack.pop().unwrap()); let ret = m.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(value_stack.len()))], false, )[0]; match loc { Location::Imm64(_) | Location::Imm32(_) => { let tmp = m.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S64, loc, Location::GPR(tmp)); if let Location::Memory(_, _) = ret { let out_tmp = m.acquire_temp_gpr().unwrap(); f(a, Size::S64, Location::GPR(tmp), Location::GPR(out_tmp)); a.emit_mov(Size::S64, Location::GPR(out_tmp), ret); m.release_temp_gpr(out_tmp); } else { f(a, Size::S64, Location::GPR(tmp), ret); } m.release_temp_gpr(tmp); } Location::Memory(_, _) | Location::GPR(_) => { if let Location::Memory(_, _) = ret { let out_tmp = m.acquire_temp_gpr().unwrap(); f(a, Size::S64, loc, Location::GPR(out_tmp)); a.emit_mov(Size::S64, Location::GPR(out_tmp), ret); m.release_temp_gpr(out_tmp); } else { f(a, Size::S64, loc, ret); } } _ => unreachable!(), } value_stack.push(ret); } /// I32 shift with both operands popped from the virtual stack. fn emit_shift_i32( a: &mut Assembler, m: &mut Machine, value_stack: &mut Vec<Location>, f: fn(&mut Assembler, Size, Location, Location), ) { let loc_b = get_location_released(a, m, value_stack.pop().unwrap()); let loc_a = get_location_released(a, m, value_stack.pop().unwrap()); let ret = m.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(value_stack.len()))], false, )[0]; a.emit_mov(Size::S32, loc_b, Location::GPR(GPR::RCX)); if loc_a != ret { Self::emit_relaxed_binop(a, m, Assembler::emit_mov, Size::S32, loc_a, ret); } f(a, Size::S32, Location::GPR(GPR::RCX), ret); value_stack.push(ret); } /// I64 shift with both operands popped from the virtual stack. fn emit_shift_i64( a: &mut Assembler, m: &mut Machine, value_stack: &mut Vec<Location>, f: fn(&mut Assembler, Size, Location, Location), ) { let loc_b = get_location_released(a, m, value_stack.pop().unwrap()); let loc_a = get_location_released(a, m, value_stack.pop().unwrap()); let ret = m.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(value_stack.len()))], false, )[0]; a.emit_mov(Size::S64, loc_b, Location::GPR(GPR::RCX)); if loc_a != ret { Self::emit_relaxed_binop(a, m, Assembler::emit_mov, Size::S64, loc_a, ret); } f(a, Size::S64, Location::GPR(GPR::RCX), ret); value_stack.push(ret); } /// Floating point (AVX) binary operation with both operands popped from the virtual stack. fn emit_fp_binop_avx( a: &mut Assembler, m: &mut Machine, value_stack: &mut Vec<Location>, f: fn(&mut Assembler, XMM, XMMOrMemory, XMM), ) { let loc_b = get_location_released(a, m, value_stack.pop().unwrap()); let loc_a = get_location_released(a, m, value_stack.pop().unwrap()); let ret = m.acquire_locations( a, &[(WpType::F64, MachineValue::WasmStack(value_stack.len()))], false, )[0]; value_stack.push(ret); Self::emit_relaxed_avx(a, m, f, loc_a, loc_b, ret); } /// Floating point (AVX) comparison with both operands popped from the virtual stack. fn emit_fp_cmpop_avx( a: &mut Assembler, m: &mut Machine, value_stack: &mut Vec<Location>, f: fn(&mut Assembler, XMM, XMMOrMemory, XMM), ) { let loc_b = get_location_released(a, m, value_stack.pop().unwrap()); let loc_a = get_location_released(a, m, value_stack.pop().unwrap()); let ret = m.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(value_stack.len()))], false, )[0]; value_stack.push(ret); Self::emit_relaxed_avx(a, m, f, loc_a, loc_b, ret); a.emit_and(Size::S32, Location::Imm32(1), ret); // FIXME: Why? } /// Floating point (AVX) binary operation with both operands popped from the virtual stack. fn emit_fp_unop_avx( a: &mut Assembler, m: &mut Machine, value_stack: &mut Vec<Location>, f: fn(&mut Assembler, XMM, XMMOrMemory, XMM), ) { let loc = get_location_released(a, m, value_stack.pop().unwrap()); let ret = m.acquire_locations( a, &[(WpType::F64, MachineValue::WasmStack(value_stack.len()))], false, )[0]; value_stack.push(ret); Self::emit_relaxed_avx(a, m, f, loc, loc, ret); } /// Emits a System V call sequence. /// /// This function must not use RAX before `cb` is called. fn emit_call_sysv<I: Iterator<Item = Location>, F: FnOnce(&mut Assembler)>( a: &mut Assembler, m: &mut Machine, cb: F, params: I, state_context: Option<(&mut FunctionStateMap, &mut [ControlFrame])>, ) { // Values pushed in this function are above the shadow region. m.state.stack_values.push(MachineValue::ExplicitShadow); let params: Vec<_> = params.collect(); // Save used GPRs. let used_gprs = m.get_used_gprs(); for r in used_gprs.iter() { a.emit_push(Size::S64, Location::GPR(*r)); let content = m.state.register_values[X64Register::GPR(*r).to_index().0].clone(); assert!(content != MachineValue::Undefined); m.state.stack_values.push(content); } // Save used XMM registers. let used_xmms = m.get_used_xmms(); if used_xmms.len() > 0 { a.emit_sub( Size::S64, Location::Imm32((used_xmms.len() * 8) as u32), Location::GPR(GPR::RSP), ); // FIXME: Possible dynasm bug. This is a workaround. // Using RSP as the source/destination operand of a `mov` instruction produces invalid code. a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RCX)); for (i, r) in used_xmms.iter().enumerate() { a.emit_mov( Size::S64, Location::XMM(*r), Location::Memory(GPR::RCX, (i * 8) as i32), ); } for r in used_xmms.iter().rev() { let content = m.state.register_values[X64Register::XMM(*r).to_index().0].clone(); assert!(content != MachineValue::Undefined); m.state.stack_values.push(content); } } let mut stack_offset: usize = 0; // Calculate stack offset. for (i, _param) in params.iter().enumerate() { let loc = Machine::get_param_location(1 + i); match loc { Location::Memory(_, _) => { stack_offset += 8; } _ => {} } } // Align stack to 16 bytes. if (m.get_stack_offset() + used_gprs.len() * 8 + used_xmms.len() * 8 + stack_offset) % 16 != 0 { a.emit_sub(Size::S64, Location::Imm32(8), Location::GPR(GPR::RSP)); stack_offset += 8; m.state.stack_values.push(MachineValue::Undefined); } let mut call_movs: Vec<(Location, GPR)> = vec![]; // Prepare register & stack parameters. for (i, param) in params.iter().enumerate().rev() { let loc = Machine::get_param_location(1 + i); match loc { Location::GPR(x) => { call_movs.push((*param, x)); } Location::Memory(_, _) => { match *param { Location::GPR(x) => { let content = m.state.register_values[X64Register::GPR(x).to_index().0].clone(); // FIXME: There might be some corner cases (release -> emit_call_sysv -> acquire?) that cause this assertion to fail. // Hopefully nothing would be incorrect at runtime. //assert!(content != MachineValue::Undefined); m.state.stack_values.push(content); } Location::XMM(x) => { let content = m.state.register_values[X64Register::XMM(x).to_index().0].clone(); //assert!(content != MachineValue::Undefined); m.state.stack_values.push(content); } Location::Memory(reg, offset) => { if reg != GPR::RBP { unreachable!(); } m.state .stack_values .push(MachineValue::CopyStackBPRelative(offset)); // TODO: Read value at this offset } _ => { m.state.stack_values.push(MachineValue::Undefined); } } match *param { // Dynasm bug: RSP in memory operand does not work Location::Imm64(_) | Location::XMM(_) => { a.emit_mov( Size::S64, Location::GPR(GPR::RAX), Location::XMM(XMM::XMM0), ); a.emit_mov( Size::S64, Location::GPR(GPR::RCX), Location::XMM(XMM::XMM1), ); a.emit_sub(Size::S64, Location::Imm32(8), Location::GPR(GPR::RSP)); a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RCX)); a.emit_mov(Size::S64, *param, Location::GPR(GPR::RAX)); a.emit_mov( Size::S64, Location::GPR(GPR::RAX), Location::Memory(GPR::RCX, 0), ); a.emit_mov( Size::S64, Location::XMM(XMM::XMM0), Location::GPR(GPR::RAX), ); a.emit_mov( Size::S64, Location::XMM(XMM::XMM1), Location::GPR(GPR::RCX), ); } _ => a.emit_push(Size::S64, *param), } } _ => unreachable!(), } } // Sort register moves so that register are not overwritten before read. sort_call_movs(&mut call_movs); // Emit register moves. for (loc, gpr) in call_movs { if loc != Location::GPR(gpr) { a.emit_mov(Size::S64, loc, Location::GPR(gpr)); } } // Put vmctx as the first parameter. a.emit_mov( Size::S64, Location::GPR(Machine::get_vmctx_reg()), Machine::get_param_location(0), ); // vmctx assert!(m.state.stack_values.len() % 2 == 1); // explicit shadow takes one slot cb(a); // Offset needs to be after the 'call' instruction. if let Some((fsm, control_stack)) = state_context { let state_diff_id = Self::get_state_diff(m, fsm, control_stack); let offset = a.get_offset().0; fsm.call_offsets.insert( offset, OffsetInfo { end_offset: offset + 1, activate_offset: offset, diff_id: state_diff_id, }, ); fsm.wasm_offset_to_target_offset .insert(m.state.wasm_inst_offset, SuspendOffset::Call(offset)); } // Restore stack. if stack_offset > 0 { a.emit_add( Size::S64, Location::Imm32(stack_offset as u32), Location::GPR(GPR::RSP), ); assert!(stack_offset % 8 == 0); for _ in 0..stack_offset / 8 { m.state.stack_values.pop().unwrap(); } } // Restore XMMs. if used_xmms.len() > 0 { // FIXME: Possible dynasm bug. This is a workaround. a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RDX)); for (i, r) in used_xmms.iter().enumerate() { a.emit_mov( Size::S64, Location::Memory(GPR::RDX, (i * 8) as i32), Location::XMM(*r), ); } a.emit_add( Size::S64, Location::Imm32((used_xmms.len() * 8) as u32), Location::GPR(GPR::RSP), ); for _ in 0..used_xmms.len() { m.state.stack_values.pop().unwrap(); } } // Restore GPRs. for r in used_gprs.iter().rev() { a.emit_pop(Size::S64, Location::GPR(*r)); m.state.stack_values.pop().unwrap(); } assert_eq!( m.state.stack_values.pop().unwrap(), MachineValue::ExplicitShadow ); } /// Emits a System V call sequence, specialized for labels as the call target. fn emit_call_sysv_label<I: Iterator<Item = Location>>( a: &mut Assembler, m: &mut Machine, label: DynamicLabel, params: I, state_context: Option<(&mut FunctionStateMap, &mut [ControlFrame])>, ) { Self::emit_call_sysv(a, m, |a| a.emit_call_label(label), params, state_context) } /// Emits a memory operation. fn emit_memory_op<F: FnOnce(&mut Assembler, &mut Machine, GPR)>( module_info: &ModuleInfo, config: &CodegenConfig, a: &mut Assembler, m: &mut Machine, addr: Location, memarg: &MemoryImmediate, check_alignment: bool, value_size: usize, cb: F, ) { // If the memory is dynamic, we need to do bound checking at runtime. let mem_desc = match MemoryIndex::new(0).local_or_import(module_info) { LocalOrImport::Local(local_mem_index) => &module_info.memories[local_mem_index], LocalOrImport::Import(import_mem_index) => { &module_info.imported_memories[import_mem_index].1 } }; let need_check = match config.memory_bound_check_mode { MemoryBoundCheckMode::Default => match mem_desc.memory_type() { MemoryType::Dynamic => true, MemoryType::Static | MemoryType::SharedStatic => false, }, MemoryBoundCheckMode::Enable => true, MemoryBoundCheckMode::Disable => false, }; let tmp_addr = m.acquire_temp_gpr().unwrap(); let tmp_base = m.acquire_temp_gpr().unwrap(); // Load base into temporary register. a.emit_mov( Size::S64, Location::Memory( Machine::get_vmctx_reg(), vm::Ctx::offset_memory_base() as i32, ), Location::GPR(tmp_base), ); if need_check { let tmp_bound = m.acquire_temp_gpr().unwrap(); a.emit_mov( Size::S64, Location::Memory( Machine::get_vmctx_reg(), vm::Ctx::offset_memory_bound() as i32, ), Location::GPR(tmp_bound), ); // Adds base to bound so `tmp_bound` now holds the end of linear memory. a.emit_add(Size::S64, Location::GPR(tmp_base), Location::GPR(tmp_bound)); a.emit_mov(Size::S32, addr, Location::GPR(tmp_addr)); // This branch is used for emitting "faster" code for the special case of (offset + value_size) not exceeding u32 range. match (memarg.offset as u32).checked_add(value_size as u32) { Some(0) => {} Some(x) => { a.emit_add(Size::S64, Location::Imm32(x), Location::GPR(tmp_addr)); } None => { a.emit_add( Size::S64, Location::Imm32(memarg.offset as u32), Location::GPR(tmp_addr), ); a.emit_add( Size::S64, Location::Imm32(value_size as u32), Location::GPR(tmp_addr), ); } } // Trap if the end address of the requested area is above that of the linear memory. a.emit_add(Size::S64, Location::GPR(tmp_base), Location::GPR(tmp_addr)); a.emit_cmp(Size::S64, Location::GPR(tmp_bound), Location::GPR(tmp_addr)); a.emit_conditional_trap(Condition::Above); m.release_temp_gpr(tmp_bound); } // Calculates the real address, and loads from it. a.emit_mov(Size::S32, addr, Location::GPR(tmp_addr)); if memarg.offset != 0 { a.emit_add( Size::S64, Location::Imm32(memarg.offset as u32), Location::GPR(tmp_addr), ); } a.emit_add(Size::S64, Location::GPR(tmp_base), Location::GPR(tmp_addr)); m.release_temp_gpr(tmp_base); let align = match memarg.flags & 3 { 0 => 1, 1 => 2, 2 => 4, 3 => 8, _ => unreachable!("this match is fully covered"), }; if check_alignment && align != 1 { let tmp_aligncheck = m.acquire_temp_gpr().unwrap(); a.emit_mov( Size::S32, Location::GPR(tmp_addr), Location::GPR(tmp_aligncheck), ); a.emit_and( Size::S64, Location::Imm32(align - 1), Location::GPR(tmp_aligncheck), ); a.emit_conditional_trap(Condition::NotEqual); m.release_temp_gpr(tmp_aligncheck); } cb(a, m, tmp_addr); m.release_temp_gpr(tmp_addr); } /// Emits a memory operation. fn emit_compare_and_swap<F: FnOnce(&mut Assembler, &mut Machine, GPR, GPR)>( module_info: &ModuleInfo, config: &CodegenConfig, a: &mut Assembler, m: &mut Machine, loc: Location, target: Location, ret: Location, memarg: &MemoryImmediate, value_size: usize, memory_sz: Size, stack_sz: Size, cb: F, ) { assert!(memory_sz <= stack_sz); let compare = m.reserve_unused_temp_gpr(GPR::RAX); let value = if loc == Location::GPR(GPR::R14) { GPR::R13 } else { GPR::R14 }; a.emit_push(Size::S64, Location::GPR(value)); a.emit_mov(stack_sz, loc, Location::GPR(value)); let retry = a.get_label(); a.emit_label(retry); Self::emit_memory_op( module_info, config, a, m, target, memarg, true, value_size, |a, m, addr| { a.emit_mov(memory_sz, Location::Memory(addr, 0), Location::GPR(compare)); a.emit_mov(stack_sz, Location::GPR(compare), ret); cb(a, m, compare, value); a.emit_lock_cmpxchg(memory_sz, Location::GPR(value), Location::Memory(addr, 0)); }, ); a.emit_jmp(Condition::NotEqual, retry); a.emit_pop(Size::S64, Location::GPR(value)); m.release_temp_gpr(compare); } // Checks for underflow/overflow/nan before IxxTrunc{U/S}F32. fn emit_f32_int_conv_check( a: &mut Assembler, m: &mut Machine, reg: XMM, lower_bound: f32, upper_bound: f32, ) { let lower_bound = f32::to_bits(lower_bound); let upper_bound = f32::to_bits(upper_bound); let trap = a.get_label(); let end = a.get_label(); let tmp = m.acquire_temp_gpr().unwrap(); let tmp_x = m.acquire_temp_xmm().unwrap(); // Underflow. a.emit_mov(Size::S32, Location::Imm32(lower_bound), Location::GPR(tmp)); a.emit_mov(Size::S32, Location::GPR(tmp), Location::XMM(tmp_x)); a.emit_vcmpless(reg, XMMOrMemory::XMM(tmp_x), tmp_x); a.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); a.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); a.emit_jmp(Condition::NotEqual, trap); // Overflow. a.emit_mov(Size::S32, Location::Imm32(upper_bound), Location::GPR(tmp)); a.emit_mov(Size::S32, Location::GPR(tmp), Location::XMM(tmp_x)); a.emit_vcmpgess(reg, XMMOrMemory::XMM(tmp_x), tmp_x); a.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); a.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); a.emit_jmp(Condition::NotEqual, trap); // NaN. a.emit_vcmpeqss(reg, XMMOrMemory::XMM(reg), tmp_x); a.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); a.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); a.emit_jmp(Condition::Equal, trap); a.emit_jmp(Condition::None, end); a.emit_label(trap); a.emit_ud2(); a.emit_label(end); m.release_temp_xmm(tmp_x); m.release_temp_gpr(tmp); } // Checks for underflow/overflow/nan before IxxTrunc{U/S}F64. fn emit_f64_int_conv_check( a: &mut Assembler, m: &mut Machine, reg: XMM, lower_bound: f64, upper_bound: f64, ) { let lower_bound = f64::to_bits(lower_bound); let upper_bound = f64::to_bits(upper_bound); let trap = a.get_label(); let end = a.get_label(); let tmp = m.acquire_temp_gpr().unwrap(); let tmp_x = m.acquire_temp_xmm().unwrap(); // Underflow. a.emit_mov(Size::S64, Location::Imm64(lower_bound), Location::GPR(tmp)); a.emit_mov(Size::S64, Location::GPR(tmp), Location::XMM(tmp_x)); a.emit_vcmplesd(reg, XMMOrMemory::XMM(tmp_x), tmp_x); a.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); a.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); a.emit_jmp(Condition::NotEqual, trap); // Overflow. a.emit_mov(Size::S64, Location::Imm64(upper_bound), Location::GPR(tmp)); a.emit_mov(Size::S64, Location::GPR(tmp), Location::XMM(tmp_x)); a.emit_vcmpgesd(reg, XMMOrMemory::XMM(tmp_x), tmp_x); a.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); a.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); a.emit_jmp(Condition::NotEqual, trap); // NaN. a.emit_vcmpeqsd(reg, XMMOrMemory::XMM(reg), tmp_x); a.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); a.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); a.emit_jmp(Condition::Equal, trap); a.emit_jmp(Condition::None, end); a.emit_label(trap); a.emit_ud2(); a.emit_label(end); m.release_temp_xmm(tmp_x); m.release_temp_gpr(tmp); } pub fn get_state_diff( m: &Machine, fsm: &mut FunctionStateMap, control_stack: &mut [ControlFrame], ) -> usize { if !m.track_state { return ::std::usize::MAX; } let last_frame = control_stack.last_mut().unwrap(); let mut diff = m.state.diff(&last_frame.state); diff.last = Some(last_frame.state_diff_id); let id = fsm.diffs.len(); last_frame.state = m.state.clone(); last_frame.state_diff_id = id; fsm.diffs.push(diff); id } } impl FunctionCodeGenerator<CodegenError> for X64FunctionCode { fn feed_return(&mut self, ty: WpType) -> Result<(), CodegenError> { self.returns.push(ty); Ok(()) } fn feed_param(&mut self, _ty: WpType) -> Result<(), CodegenError> { self.num_params += 1; self.num_locals += 1; Ok(()) } fn feed_local(&mut self, _ty: WpType, n: usize) -> Result<(), CodegenError> { self.num_locals += n; Ok(()) } fn begin_body(&mut self, _module_info: &ModuleInfo) -> Result<(), CodegenError> { let a = self.assembler.as_mut().unwrap(); let start_label = a.get_label(); // skip the patchpoint during normal execution a.emit_jmp(Condition::None, start_label); // patchpoint of 32 1-byte nops for _ in 0..32 { a.emit_nop(); } a.emit_label(start_label); a.emit_push(Size::S64, Location::GPR(GPR::RBP)); a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RBP)); // Stack check. if self.config.enforce_stack_check { a.emit_cmp( Size::S64, Location::Memory( GPR::RDI, // first parameter is vmctx vm::Ctx::offset_stack_lower_bound() as i32, ), Location::GPR(GPR::RSP), ); a.emit_conditional_trap(Condition::Below); } self.locals = self .machine .init_locals(a, self.num_locals, self.num_params); self.machine.state.register_values [X64Register::GPR(Machine::get_vmctx_reg()).to_index().0] = MachineValue::Vmctx; self.fsm = FunctionStateMap::new( new_machine_state(), self.local_function_id, 32, (0..self.locals.len()) .map(|_| WasmAbstractValue::Runtime) .collect(), ); let diff = self.machine.state.diff(&new_machine_state()); let state_diff_id = self.fsm.diffs.len(); self.fsm.diffs.push(diff); //println!("initial state = {:?}", self.machine.state); a.emit_sub(Size::S64, Location::Imm32(32), Location::GPR(GPR::RSP)); // simulate "red zone" if not supported by the platform self.control_stack.push(ControlFrame { label: a.get_label(), loop_like: false, if_else: IfElseState::None, returns: self.returns.clone(), value_stack_depth: 0, state: self.machine.state.clone(), state_diff_id, }); // Check interrupt signal without branching let activate_offset = a.get_offset().0; a.emit_mov( Size::S64, Location::Memory( Machine::get_vmctx_reg(), vm::Ctx::offset_interrupt_signal_mem() as i32, ), Location::GPR(GPR::RAX), ); self.fsm.loop_offsets.insert( a.get_offset().0, OffsetInfo { end_offset: a.get_offset().0 + 1, activate_offset, diff_id: state_diff_id, }, ); self.fsm.wasm_function_header_target_offset = Some(SuspendOffset::Loop(a.get_offset().0)); a.emit_mov( Size::S64, Location::Memory(GPR::RAX, 0), Location::GPR(GPR::RAX), ); assert_eq!(self.machine.state.wasm_inst_offset, ::std::usize::MAX); Ok(()) } fn finalize(&mut self) -> Result<(), CodegenError> { let a = self.assembler.as_mut().unwrap(); a.emit_ud2(); Ok(()) } fn feed_event(&mut self, ev: Event, module_info: &ModuleInfo) -> Result<(), CodegenError> { let a = self.assembler.as_mut().unwrap(); match ev { Event::Internal(InternalEvent::FunctionBegin(_)) | Event::Internal(InternalEvent::FunctionEnd) => { return Ok(()); } _ => {} } self.machine.state.wasm_inst_offset = self.machine.state.wasm_inst_offset.wrapping_add(1); //println!("{:?} {}", op, self.value_stack.len()); let was_unreachable; if self.unreachable_depth > 0 { was_unreachable = true; if let Event::Wasm(op) = ev { match *op { Operator::Block { .. } | Operator::Loop { .. } | Operator::If { .. } => { self.unreachable_depth += 1; } Operator::End => { self.unreachable_depth -= 1; } Operator::Else => { // We are in a reachable true branch if self.unreachable_depth == 1 { if let Some(IfElseState::If(_)) = self.control_stack.last().map(|x| x.if_else) { self.unreachable_depth -= 1; } } } _ => {} } } if self.unreachable_depth > 0 { return Ok(()); } } else { was_unreachable = false; } let op = match ev { Event::Wasm(x) => x, Event::WasmOwned(ref x) => x, Event::Internal(x) => { match x { InternalEvent::Breakpoint(callback) => { a.emit_bkpt(); self.breakpoints .as_mut() .unwrap() .insert(a.get_offset(), callback); } InternalEvent::FunctionBegin(_) | InternalEvent::FunctionEnd => {} InternalEvent::GetInternal(idx) => { let idx = idx as usize; assert!(idx < INTERNALS_SIZE); let tmp = self.machine.acquire_temp_gpr().unwrap(); // Load `internals` pointer. a.emit_mov( Size::S64, Location::Memory( Machine::get_vmctx_reg(), vm::Ctx::offset_internals() as i32, ), Location::GPR(tmp), ); let loc = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(loc); // Move internal into the result location. Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S64, Location::Memory(tmp, (idx * 8) as i32), loc, ); self.machine.release_temp_gpr(tmp); } InternalEvent::SetInternal(idx) => { let idx = idx as usize; assert!(idx < INTERNALS_SIZE); let tmp = self.machine.acquire_temp_gpr().unwrap(); // Load `internals` pointer. a.emit_mov( Size::S64, Location::Memory( Machine::get_vmctx_reg(), vm::Ctx::offset_internals() as i32, ), Location::GPR(tmp), ); let loc = get_location_released( a, &mut self.machine, self.value_stack.pop().unwrap(), ); // Move internal into storage. Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S64, loc, Location::Memory(tmp, (idx * 8) as i32), ); self.machine.release_temp_gpr(tmp); } //_ => unimplemented!(), } return Ok(()); } }; match *op { Operator::GetGlobal { global_index } => { let global_index = global_index as usize; let tmp = self.machine.acquire_temp_gpr().unwrap(); let loc = match GlobalIndex::new(global_index).local_or_import(module_info) { LocalOrImport::Local(local_index) => { a.emit_mov( Size::S64, Location::Memory( Machine::get_vmctx_reg(), vm::Ctx::offset_globals() as i32, ), Location::GPR(tmp), ); a.emit_mov( Size::S64, Location::Memory(tmp, (local_index.index() as i32) * 8), Location::GPR(tmp), ); self.machine.acquire_locations( a, &[( type_to_wp_type(module_info.globals[local_index].desc.ty), MachineValue::WasmStack(self.value_stack.len()), )], false, )[0] } LocalOrImport::Import(import_index) => { a.emit_mov( Size::S64, Location::Memory( Machine::get_vmctx_reg(), vm::Ctx::offset_imported_globals() as i32, ), Location::GPR(tmp), ); a.emit_mov( Size::S64, Location::Memory(tmp, (import_index.index() as i32) * 8), Location::GPR(tmp), ); self.machine.acquire_locations( a, &[( type_to_wp_type(module_info.imported_globals[import_index].1.ty), MachineValue::WasmStack(self.value_stack.len()), )], false, )[0] } }; self.value_stack.push(loc); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S64, Location::Memory(tmp, LocalGlobal::offset_data() as i32), loc, ); self.machine.release_temp_gpr(tmp); } Operator::SetGlobal { global_index } => { let mut global_index = global_index as usize; let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let tmp = self.machine.acquire_temp_gpr().unwrap(); if global_index < module_info.imported_globals.len() { a.emit_mov( Size::S64, Location::Memory( Machine::get_vmctx_reg(), vm::Ctx::offset_imported_globals() as i32, ), Location::GPR(tmp), ); } else { global_index -= module_info.imported_globals.len(); assert!(global_index < module_info.globals.len()); a.emit_mov( Size::S64, Location::Memory( Machine::get_vmctx_reg(), vm::Ctx::offset_globals() as i32, ), Location::GPR(tmp), ); } a.emit_mov( Size::S64, Location::Memory(tmp, (global_index as i32) * 8), Location::GPR(tmp), ); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S64, loc, Location::Memory(tmp, LocalGlobal::offset_data() as i32), ); self.machine.release_temp_gpr(tmp); } Operator::GetLocal { local_index } => { let local_index = local_index as usize; let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S64, self.locals[local_index], ret, ); self.value_stack.push(ret); } Operator::SetLocal { local_index } => { let local_index = local_index as usize; let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S64, loc, self.locals[local_index], ); } Operator::TeeLocal { local_index } => { let local_index = local_index as usize; let loc = *self.value_stack.last().unwrap(); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S64, loc, self.locals[local_index], ); } Operator::I32Const { value } => { self.value_stack.push(Location::Imm32(value as u32)); self.machine .state .wasm_stack .push(WasmAbstractValue::Const(value as u32 as u64)); } Operator::I32Add => Self::emit_binop_i32( a, &mut self.machine, &mut self.value_stack, Assembler::emit_add, ), Operator::I32Sub => Self::emit_binop_i32( a, &mut self.machine, &mut self.value_stack, Assembler::emit_sub, ), Operator::I32Mul => Self::emit_binop_i32( a, &mut self.machine, &mut self.value_stack, Assembler::emit_imul, ), Operator::I32DivU => { // We assume that RAX and RDX are temporary registers here. let loc_b = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let loc_a = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; a.emit_mov(Size::S32, loc_a, Location::GPR(GPR::RAX)); a.emit_xor(Size::S32, Location::GPR(GPR::RDX), Location::GPR(GPR::RDX)); Self::emit_relaxed_xdiv( a, &mut self.machine, Assembler::emit_div, Size::S32, loc_b, &mut self.fsm, &mut self.control_stack, ); a.emit_mov(Size::S32, Location::GPR(GPR::RAX), ret); self.value_stack.push(ret); } Operator::I32DivS => { // We assume that RAX and RDX are temporary registers here. let loc_b = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let loc_a = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; a.emit_mov(Size::S32, loc_a, Location::GPR(GPR::RAX)); a.emit_cdq(); Self::emit_relaxed_xdiv( a, &mut self.machine, Assembler::emit_idiv, Size::S32, loc_b, &mut self.fsm, &mut self.control_stack, ); a.emit_mov(Size::S32, Location::GPR(GPR::RAX), ret); self.value_stack.push(ret); } Operator::I32RemU => { // We assume that RAX and RDX are temporary registers here. let loc_b = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let loc_a = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; a.emit_mov(Size::S32, loc_a, Location::GPR(GPR::RAX)); a.emit_xor(Size::S32, Location::GPR(GPR::RDX), Location::GPR(GPR::RDX)); Self::emit_relaxed_xdiv( a, &mut self.machine, Assembler::emit_div, Size::S32, loc_b, &mut self.fsm, &mut self.control_stack, ); a.emit_mov(Size::S32, Location::GPR(GPR::RDX), ret); self.value_stack.push(ret); } Operator::I32RemS => { // We assume that RAX and RDX are temporary registers here. let loc_b = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let loc_a = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; let normal_path = a.get_label(); let end = a.get_label(); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_cmp, Size::S32, Location::Imm32(0x80000000), loc_a, ); a.emit_jmp(Condition::NotEqual, normal_path); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_cmp, Size::S32, Location::Imm32(0xffffffff), loc_b, ); a.emit_jmp(Condition::NotEqual, normal_path); a.emit_mov(Size::S32, Location::Imm32(0), ret); a.emit_jmp(Condition::None, end); a.emit_label(normal_path); a.emit_mov(Size::S32, loc_a, Location::GPR(GPR::RAX)); a.emit_cdq(); Self::emit_relaxed_xdiv( a, &mut self.machine, Assembler::emit_idiv, Size::S32, loc_b, &mut self.fsm, &mut self.control_stack, ); a.emit_mov(Size::S32, Location::GPR(GPR::RDX), ret); self.value_stack.push(ret); a.emit_label(end); } Operator::I32And => Self::emit_binop_i32( a, &mut self.machine, &mut self.value_stack, Assembler::emit_and, ), Operator::I32Or => Self::emit_binop_i32( a, &mut self.machine, &mut self.value_stack, Assembler::emit_or, ), Operator::I32Xor => Self::emit_binop_i32( a, &mut self.machine, &mut self.value_stack, Assembler::emit_xor, ), Operator::I32Eq => Self::emit_cmpop_i32( a, &mut self.machine, &mut self.value_stack, Condition::Equal, ), Operator::I32Ne => Self::emit_cmpop_i32( a, &mut self.machine, &mut self.value_stack, Condition::NotEqual, ), Operator::I32Eqz => Self::emit_cmpop_i32_dynamic_b( a, &mut self.machine, &mut self.value_stack, Condition::Equal, Location::Imm32(0), ), Operator::I32Clz => Self::emit_xcnt_i32( a, &mut self.machine, &mut self.value_stack, Assembler::emit_lzcnt, ), Operator::I32Ctz => Self::emit_xcnt_i32( a, &mut self.machine, &mut self.value_stack, Assembler::emit_tzcnt, ), Operator::I32Popcnt => Self::emit_xcnt_i32( a, &mut self.machine, &mut self.value_stack, Assembler::emit_popcnt, ), Operator::I32Shl => Self::emit_shift_i32( a, &mut self.machine, &mut self.value_stack, Assembler::emit_shl, ), Operator::I32ShrU => Self::emit_shift_i32( a, &mut self.machine, &mut self.value_stack, Assembler::emit_shr, ), Operator::I32ShrS => Self::emit_shift_i32( a, &mut self.machine, &mut self.value_stack, Assembler::emit_sar, ), Operator::I32Rotl => Self::emit_shift_i32( a, &mut self.machine, &mut self.value_stack, Assembler::emit_rol, ), Operator::I32Rotr => Self::emit_shift_i32( a, &mut self.machine, &mut self.value_stack, Assembler::emit_ror, ), Operator::I32LtU => Self::emit_cmpop_i32( a, &mut self.machine, &mut self.value_stack, Condition::Below, ), Operator::I32LeU => Self::emit_cmpop_i32( a, &mut self.machine, &mut self.value_stack, Condition::BelowEqual, ), Operator::I32GtU => Self::emit_cmpop_i32( a, &mut self.machine, &mut self.value_stack, Condition::Above, ), Operator::I32GeU => Self::emit_cmpop_i32( a, &mut self.machine, &mut self.value_stack, Condition::AboveEqual, ), Operator::I32LtS => { Self::emit_cmpop_i32(a, &mut self.machine, &mut self.value_stack, Condition::Less) } Operator::I32LeS => Self::emit_cmpop_i32( a, &mut self.machine, &mut self.value_stack, Condition::LessEqual, ), Operator::I32GtS => Self::emit_cmpop_i32( a, &mut self.machine, &mut self.value_stack, Condition::Greater, ), Operator::I32GeS => Self::emit_cmpop_i32( a, &mut self.machine, &mut self.value_stack, Condition::GreaterEqual, ), Operator::I64Const { value } => { let value = value as u64; self.value_stack.push(Location::Imm64(value)); self.machine .state .wasm_stack .push(WasmAbstractValue::Const(value)); } Operator::I64Add => Self::emit_binop_i64( a, &mut self.machine, &mut self.value_stack, Assembler::emit_add, ), Operator::I64Sub => Self::emit_binop_i64( a, &mut self.machine, &mut self.value_stack, Assembler::emit_sub, ), Operator::I64Mul => Self::emit_binop_i64( a, &mut self.machine, &mut self.value_stack, Assembler::emit_imul, ), Operator::I64DivU => { // We assume that RAX and RDX are temporary registers here. let loc_b = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let loc_a = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; a.emit_mov(Size::S64, loc_a, Location::GPR(GPR::RAX)); a.emit_xor(Size::S64, Location::GPR(GPR::RDX), Location::GPR(GPR::RDX)); Self::emit_relaxed_xdiv( a, &mut self.machine, Assembler::emit_div, Size::S64, loc_b, &mut self.fsm, &mut self.control_stack, ); a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); self.value_stack.push(ret); } Operator::I64DivS => { // We assume that RAX and RDX are temporary registers here. let loc_b = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let loc_a = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; a.emit_mov(Size::S64, loc_a, Location::GPR(GPR::RAX)); a.emit_cqo(); Self::emit_relaxed_xdiv( a, &mut self.machine, Assembler::emit_idiv, Size::S64, loc_b, &mut self.fsm, &mut self.control_stack, ); a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); self.value_stack.push(ret); } Operator::I64RemU => { // We assume that RAX and RDX are temporary registers here. let loc_b = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let loc_a = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; a.emit_mov(Size::S64, loc_a, Location::GPR(GPR::RAX)); a.emit_xor(Size::S64, Location::GPR(GPR::RDX), Location::GPR(GPR::RDX)); Self::emit_relaxed_xdiv( a, &mut self.machine, Assembler::emit_div, Size::S64, loc_b, &mut self.fsm, &mut self.control_stack, ); a.emit_mov(Size::S64, Location::GPR(GPR::RDX), ret); self.value_stack.push(ret); } Operator::I64RemS => { // We assume that RAX and RDX are temporary registers here. let loc_b = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let loc_a = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; let normal_path = a.get_label(); let end = a.get_label(); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_cmp, Size::S64, Location::Imm64(0x8000000000000000u64), loc_a, ); a.emit_jmp(Condition::NotEqual, normal_path); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_cmp, Size::S64, Location::Imm64(0xffffffffffffffffu64), loc_b, ); a.emit_jmp(Condition::NotEqual, normal_path); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S64, Location::Imm64(0), ret, ); a.emit_jmp(Condition::None, end); a.emit_label(normal_path); a.emit_mov(Size::S64, loc_a, Location::GPR(GPR::RAX)); a.emit_cqo(); Self::emit_relaxed_xdiv( a, &mut self.machine, Assembler::emit_idiv, Size::S64, loc_b, &mut self.fsm, &mut self.control_stack, ); a.emit_mov(Size::S64, Location::GPR(GPR::RDX), ret); self.value_stack.push(ret); a.emit_label(end); } Operator::I64And => Self::emit_binop_i64( a, &mut self.machine, &mut self.value_stack, Assembler::emit_and, ), Operator::I64Or => Self::emit_binop_i64( a, &mut self.machine, &mut self.value_stack, Assembler::emit_or, ), Operator::I64Xor => Self::emit_binop_i64( a, &mut self.machine, &mut self.value_stack, Assembler::emit_xor, ), Operator::I64Eq => Self::emit_cmpop_i64( a, &mut self.machine, &mut self.value_stack, Condition::Equal, ), Operator::I64Ne => Self::emit_cmpop_i64( a, &mut self.machine, &mut self.value_stack, Condition::NotEqual, ), Operator::I64Eqz => Self::emit_cmpop_i64_dynamic_b( a, &mut self.machine, &mut self.value_stack, Condition::Equal, Location::Imm64(0), ), Operator::I64Clz => Self::emit_xcnt_i64( a, &mut self.machine, &mut self.value_stack, Assembler::emit_lzcnt, ), Operator::I64Ctz => Self::emit_xcnt_i64( a, &mut self.machine, &mut self.value_stack, Assembler::emit_tzcnt, ), Operator::I64Popcnt => Self::emit_xcnt_i64( a, &mut self.machine, &mut self.value_stack, Assembler::emit_popcnt, ), Operator::I64Shl => Self::emit_shift_i64( a, &mut self.machine, &mut self.value_stack, Assembler::emit_shl, ), Operator::I64ShrU => Self::emit_shift_i64( a, &mut self.machine, &mut self.value_stack, Assembler::emit_shr, ), Operator::I64ShrS => Self::emit_shift_i64( a, &mut self.machine, &mut self.value_stack, Assembler::emit_sar, ), Operator::I64Rotl => Self::emit_shift_i64( a, &mut self.machine, &mut self.value_stack, Assembler::emit_rol, ), Operator::I64Rotr => Self::emit_shift_i64( a, &mut self.machine, &mut self.value_stack, Assembler::emit_ror, ), Operator::I64LtU => Self::emit_cmpop_i64( a, &mut self.machine, &mut self.value_stack, Condition::Below, ), Operator::I64LeU => Self::emit_cmpop_i64( a, &mut self.machine, &mut self.value_stack, Condition::BelowEqual, ), Operator::I64GtU => Self::emit_cmpop_i64( a, &mut self.machine, &mut self.value_stack, Condition::Above, ), Operator::I64GeU => Self::emit_cmpop_i64( a, &mut self.machine, &mut self.value_stack, Condition::AboveEqual, ), Operator::I64LtS => { Self::emit_cmpop_i64(a, &mut self.machine, &mut self.value_stack, Condition::Less) } Operator::I64LeS => Self::emit_cmpop_i64( a, &mut self.machine, &mut self.value_stack, Condition::LessEqual, ), Operator::I64GtS => Self::emit_cmpop_i64( a, &mut self.machine, &mut self.value_stack, Condition::Greater, ), Operator::I64GeS => Self::emit_cmpop_i64( a, &mut self.machine, &mut self.value_stack, Condition::GreaterEqual, ), Operator::I64ExtendUI32 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S32, loc, ret, ); } Operator::I64ExtendSI32 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_relaxed_zx_sx( a, &mut self.machine, Assembler::emit_movsx, Size::S32, loc, Size::S64, ret, ); } Operator::I32WrapI64 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S32, loc, ret, ); } Operator::F32Const { value } => { self.value_stack.push(Location::Imm32(value.bits())); self.machine .state .wasm_stack .push(WasmAbstractValue::Const(value.bits() as u64)); } Operator::F32Add => Self::emit_fp_binop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vaddss, ), Operator::F32Sub => Self::emit_fp_binop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vsubss, ), Operator::F32Mul => Self::emit_fp_binop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vmulss, ), Operator::F32Div => Self::emit_fp_binop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vdivss, ), Operator::F32Max => Self::emit_fp_binop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vmaxss, ), Operator::F32Min => Self::emit_fp_binop_avx( a, &mut self.machine, &mut self.value_stack, |a, src1, src2, dst| { // TODO: use temp_gpr (or at least check that src2 isn't // XMMOrMemory that uses AX or DX. a.emit_mov(Size::S32, Location::XMM(src1), Location::GPR(GPR::RAX)); match src2 { XMMOrMemory::XMM(x) => { a.emit_mov(Size::S32, Location::XMM(x), Location::GPR(GPR::RDX)) } XMMOrMemory::Memory(base, disp) => a.emit_mov( Size::S32, Location::Memory(base, disp), Location::GPR(GPR::RDX), ), }; // TODO: we can skip this when dst is an XMM reg. let tmp_xmm = if src1 == XMM::XMM0 { if src2 == XMMOrMemory::XMM(XMM::XMM1) { XMM::XMM2 } else { XMM::XMM1 } } else { XMM::XMM0 }; match src2 { XMMOrMemory::XMM(x) => { a.emit_mov(Size::S64, Location::XMM(x), Location::XMM(tmp_xmm)) } XMMOrMemory::Memory(base, disp) => a.emit_mov( Size::S64, Location::Memory(base, disp), Location::XMM(tmp_xmm), ), }; a.emit_ucomiss(XMMOrMemory::XMM(src1), tmp_xmm); let do_vminss = a.get_label(); a.emit_jmp(Condition::NotEqual, do_vminss); a.emit_jmp(Condition::ParityEven, do_vminss); a.emit_cmp(Size::S32, Location::GPR(GPR::RAX), Location::GPR(GPR::RDX)); a.emit_jmp(Condition::Equal, do_vminss); static NEG_ZERO: u128 = 0x80000000; match src2 { XMMOrMemory::XMM(x) => { a.emit_mov( Size::S64, Location::Imm64((&NEG_ZERO as *const u128) as u64), Location::GPR(GPR::RDX), ); a.emit_mov(Size::S64, Location::Memory(GPR::RDX, 0), Location::XMM(x)); } XMMOrMemory::Memory(base, disp) => { let neg_zero_base = if base == GPR::RDX { GPR::RAX } else { GPR::RDX }; a.emit_mov( Size::S64, Location::Imm64((&NEG_ZERO as *const u128) as u64), Location::GPR(neg_zero_base), ); a.emit_mov( Size::S64, Location::Memory(neg_zero_base, 0), Location::Memory(base, disp), ); } }; a.emit_label(do_vminss); a.emit_vminss(src1, src2, dst); }, ), Operator::F32Eq => Self::emit_fp_cmpop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcmpeqss, ), Operator::F32Ne => Self::emit_fp_cmpop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcmpneqss, ), Operator::F32Lt => Self::emit_fp_cmpop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcmpltss, ), Operator::F32Le => Self::emit_fp_cmpop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcmpless, ), Operator::F32Gt => Self::emit_fp_cmpop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcmpgtss, ), Operator::F32Ge => Self::emit_fp_cmpop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcmpgess, ), Operator::F32Nearest => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vroundss_nearest, ), Operator::F32Floor => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vroundss_floor, ), Operator::F32Ceil => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vroundss_ceil, ), Operator::F32Trunc => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vroundss_trunc, ), Operator::F32Sqrt => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vsqrtss, ), Operator::F32Copysign => { let loc_b = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let loc_a = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::F32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let tmp1 = self.machine.acquire_temp_gpr().unwrap(); let tmp2 = self.machine.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S32, loc_a, Location::GPR(tmp1)); a.emit_mov(Size::S32, loc_b, Location::GPR(tmp2)); a.emit_and( Size::S32, Location::Imm32(0x7fffffffu32), Location::GPR(tmp1), ); a.emit_and( Size::S32, Location::Imm32(0x80000000u32), Location::GPR(tmp2), ); a.emit_or(Size::S32, Location::GPR(tmp2), Location::GPR(tmp1)); a.emit_mov(Size::S32, Location::GPR(tmp1), ret); self.machine.release_temp_gpr(tmp2); self.machine.release_temp_gpr(tmp1); } Operator::F32Abs => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::F32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let tmp = self.machine.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S32, loc, Location::GPR(tmp)); a.emit_and( Size::S32, Location::Imm32(0x7fffffffu32), Location::GPR(tmp), ); a.emit_mov(Size::S32, Location::GPR(tmp), ret); self.machine.release_temp_gpr(tmp); } Operator::F32Neg => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::F32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let tmp = self.machine.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S32, loc, Location::GPR(tmp)); a.emit_btc_gpr_imm8_32(31, tmp); a.emit_mov(Size::S32, Location::GPR(tmp), ret); self.machine.release_temp_gpr(tmp); } Operator::F64Const { value } => { self.value_stack.push(Location::Imm64(value.bits())); self.machine .state .wasm_stack .push(WasmAbstractValue::Const(value.bits())); } Operator::F64Add => Self::emit_fp_binop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vaddsd, ), Operator::F64Sub => Self::emit_fp_binop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vsubsd, ), Operator::F64Mul => Self::emit_fp_binop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vmulsd, ), Operator::F64Div => Self::emit_fp_binop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vdivsd, ), Operator::F64Max => Self::emit_fp_binop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vmaxsd, ), Operator::F64Min => Self::emit_fp_binop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vminsd, ), Operator::F64Eq => Self::emit_fp_cmpop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcmpeqsd, ), Operator::F64Ne => Self::emit_fp_cmpop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcmpneqsd, ), Operator::F64Lt => Self::emit_fp_cmpop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcmpltsd, ), Operator::F64Le => Self::emit_fp_cmpop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcmplesd, ), Operator::F64Gt => Self::emit_fp_cmpop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcmpgtsd, ), Operator::F64Ge => Self::emit_fp_cmpop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcmpgesd, ), Operator::F64Nearest => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vroundsd_nearest, ), Operator::F64Floor => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vroundsd_floor, ), Operator::F64Ceil => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vroundsd_ceil, ), Operator::F64Trunc => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vroundsd_trunc, ), Operator::F64Sqrt => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vsqrtsd, ), Operator::F64Copysign => { let loc_b = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let loc_a = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::F64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let tmp1 = self.machine.acquire_temp_gpr().unwrap(); let tmp2 = self.machine.acquire_temp_gpr().unwrap(); let c = self.machine.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S64, loc_a, Location::GPR(tmp1)); a.emit_mov(Size::S64, loc_b, Location::GPR(tmp2)); a.emit_mov( Size::S64, Location::Imm64(0x7fffffffffffffffu64), Location::GPR(c), ); a.emit_and(Size::S64, Location::GPR(c), Location::GPR(tmp1)); a.emit_mov( Size::S64, Location::Imm64(0x8000000000000000u64), Location::GPR(c), ); a.emit_and(Size::S64, Location::GPR(c), Location::GPR(tmp2)); a.emit_or(Size::S64, Location::GPR(tmp2), Location::GPR(tmp1)); a.emit_mov(Size::S64, Location::GPR(tmp1), ret); self.machine.release_temp_gpr(c); self.machine.release_temp_gpr(tmp2); self.machine.release_temp_gpr(tmp1); } Operator::F64Abs => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::F64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let tmp = self.machine.acquire_temp_gpr().unwrap(); let c = self.machine.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S64, loc, Location::GPR(tmp)); a.emit_mov( Size::S64, Location::Imm64(0x7fffffffffffffffu64), Location::GPR(c), ); a.emit_and(Size::S64, Location::GPR(c), Location::GPR(tmp)); a.emit_mov(Size::S64, Location::GPR(tmp), ret); self.machine.release_temp_gpr(c); self.machine.release_temp_gpr(tmp); } Operator::F64Neg => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::F64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let tmp = self.machine.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S64, loc, Location::GPR(tmp)); a.emit_btc_gpr_imm8_64(63, tmp); a.emit_mov(Size::S64, Location::GPR(tmp), ret); self.machine.release_temp_gpr(tmp); } Operator::F64PromoteF32 => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcvtss2sd, ), Operator::F32DemoteF64 => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcvtsd2ss, ), Operator::I32ReinterpretF32 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); if loc != ret { Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S32, loc, ret, ); } } Operator::F32ReinterpretI32 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::F32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); if loc != ret { Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S32, loc, ret, ); } } Operator::I64ReinterpretF64 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); if loc != ret { Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S64, loc, ret, ); } } Operator::F64ReinterpretI64 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::F64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); if loc != ret { Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S64, loc, ret, ); } } Operator::I32TruncUF32 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let tmp_out = self.machine.acquire_temp_gpr().unwrap(); let tmp_in = self.machine.acquire_temp_xmm().unwrap(); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S32, loc, Location::XMM(tmp_in), ); Self::emit_f32_int_conv_check(a, &mut self.machine, tmp_in, -1.0, 4294967296.0); a.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); a.emit_mov(Size::S32, Location::GPR(tmp_out), ret); self.machine.release_temp_xmm(tmp_in); self.machine.release_temp_gpr(tmp_out); } Operator::I32TruncSF32 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let tmp_out = self.machine.acquire_temp_gpr().unwrap(); let tmp_in = self.machine.acquire_temp_xmm().unwrap(); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S32, loc, Location::XMM(tmp_in), ); Self::emit_f32_int_conv_check( a, &mut self.machine, tmp_in, -2147483904.0, 2147483648.0, ); a.emit_cvttss2si_32(XMMOrMemory::XMM(tmp_in), tmp_out); a.emit_mov(Size::S32, Location::GPR(tmp_out), ret); self.machine.release_temp_xmm(tmp_in); self.machine.release_temp_gpr(tmp_out); } Operator::I64TruncSF32 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let tmp_out = self.machine.acquire_temp_gpr().unwrap(); let tmp_in = self.machine.acquire_temp_xmm().unwrap(); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S32, loc, Location::XMM(tmp_in), ); Self::emit_f32_int_conv_check( a, &mut self.machine, tmp_in, -9223373136366403584.0, 9223372036854775808.0, ); a.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); a.emit_mov(Size::S64, Location::GPR(tmp_out), ret); self.machine.release_temp_xmm(tmp_in); self.machine.release_temp_gpr(tmp_out); } Operator::I64TruncUF32 => { /* ; movq xmm5, r15 ; mov r15d, 1593835520u32 as i32 //float 9.22337203E+18 ; movd xmm1, r15d ; movd xmm2, Rd(reg as u8) ; movd xmm3, Rd(reg as u8) ; subss xmm2, xmm1 ; cvttss2si Rq(reg as u8), xmm2 ; mov r15, QWORD 0x8000000000000000u64 as i64 ; xor r15, Rq(reg as u8) ; cvttss2si Rq(reg as u8), xmm3 ; ucomiss xmm3, xmm1 ; cmovae Rq(reg as u8), r15 ; movq r15, xmm5 */ let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let tmp_out = self.machine.acquire_temp_gpr().unwrap(); let tmp_in = self.machine.acquire_temp_xmm().unwrap(); // xmm2 Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S32, loc, Location::XMM(tmp_in), ); Self::emit_f32_int_conv_check( a, &mut self.machine, tmp_in, -1.0, 18446744073709551616.0, ); let tmp = self.machine.acquire_temp_gpr().unwrap(); // r15 let tmp_x1 = self.machine.acquire_temp_xmm().unwrap(); // xmm1 let tmp_x2 = self.machine.acquire_temp_xmm().unwrap(); // xmm3 a.emit_mov( Size::S32, Location::Imm32(1593835520u32), Location::GPR(tmp), ); //float 9.22337203E+18 a.emit_mov(Size::S32, Location::GPR(tmp), Location::XMM(tmp_x1)); a.emit_mov(Size::S32, Location::XMM(tmp_in), Location::XMM(tmp_x2)); a.emit_vsubss(tmp_in, XMMOrMemory::XMM(tmp_x1), tmp_in); a.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); a.emit_mov( Size::S64, Location::Imm64(0x8000000000000000u64), Location::GPR(tmp), ); a.emit_xor(Size::S64, Location::GPR(tmp_out), Location::GPR(tmp)); a.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_x2), tmp_out); a.emit_ucomiss(XMMOrMemory::XMM(tmp_x1), tmp_x2); a.emit_cmovae_gpr_64(tmp, tmp_out); a.emit_mov(Size::S64, Location::GPR(tmp_out), ret); self.machine.release_temp_xmm(tmp_x2); self.machine.release_temp_xmm(tmp_x1); self.machine.release_temp_gpr(tmp); self.machine.release_temp_xmm(tmp_in); self.machine.release_temp_gpr(tmp_out); } Operator::I32TruncUF64 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let tmp_out = self.machine.acquire_temp_gpr().unwrap(); let tmp_in = self.machine.acquire_temp_xmm().unwrap(); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S64, loc, Location::XMM(tmp_in), ); Self::emit_f64_int_conv_check(a, &mut self.machine, tmp_in, -1.0, 4294967296.0); a.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); a.emit_mov(Size::S32, Location::GPR(tmp_out), ret); self.machine.release_temp_xmm(tmp_in); self.machine.release_temp_gpr(tmp_out); } Operator::I32TruncSF64 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let tmp_out = self.machine.acquire_temp_gpr().unwrap(); let tmp_in = self.machine.acquire_temp_xmm().unwrap(); let real_in = match loc { Location::Imm32(_) | Location::Imm64(_) => { a.emit_mov(Size::S64, loc, Location::GPR(tmp_out)); a.emit_mov(Size::S64, Location::GPR(tmp_out), Location::XMM(tmp_in)); tmp_in } Location::XMM(x) => x, _ => { a.emit_mov(Size::S64, loc, Location::XMM(tmp_in)); tmp_in } }; Self::emit_f64_int_conv_check( a, &mut self.machine, real_in, -2147483649.0, 2147483648.0, ); a.emit_cvttsd2si_32(XMMOrMemory::XMM(real_in), tmp_out); a.emit_mov(Size::S32, Location::GPR(tmp_out), ret); self.machine.release_temp_xmm(tmp_in); self.machine.release_temp_gpr(tmp_out); } Operator::I64TruncSF64 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let tmp_out = self.machine.acquire_temp_gpr().unwrap(); let tmp_in = self.machine.acquire_temp_xmm().unwrap(); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S64, loc, Location::XMM(tmp_in), ); Self::emit_f64_int_conv_check( a, &mut self.machine, tmp_in, -9223372036854777856.0, 9223372036854775808.0, ); a.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); a.emit_mov(Size::S64, Location::GPR(tmp_out), ret); self.machine.release_temp_xmm(tmp_in); self.machine.release_temp_gpr(tmp_out); } Operator::I64TruncUF64 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let tmp_out = self.machine.acquire_temp_gpr().unwrap(); let tmp_in = self.machine.acquire_temp_xmm().unwrap(); // xmm2 Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S64, loc, Location::XMM(tmp_in), ); Self::emit_f64_int_conv_check( a, &mut self.machine, tmp_in, -1.0, 18446744073709551616.0, ); let tmp = self.machine.acquire_temp_gpr().unwrap(); // r15 let tmp_x1 = self.machine.acquire_temp_xmm().unwrap(); // xmm1 let tmp_x2 = self.machine.acquire_temp_xmm().unwrap(); // xmm3 a.emit_mov( Size::S64, Location::Imm64(4890909195324358656u64), Location::GPR(tmp), ); //double 9.2233720368547758E+18 a.emit_mov(Size::S64, Location::GPR(tmp), Location::XMM(tmp_x1)); a.emit_mov(Size::S64, Location::XMM(tmp_in), Location::XMM(tmp_x2)); a.emit_vsubsd(tmp_in, XMMOrMemory::XMM(tmp_x1), tmp_in); a.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); a.emit_mov( Size::S64, Location::Imm64(0x8000000000000000u64), Location::GPR(tmp), ); a.emit_xor(Size::S64, Location::GPR(tmp_out), Location::GPR(tmp)); a.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_x2), tmp_out); a.emit_ucomisd(XMMOrMemory::XMM(tmp_x1), tmp_x2); a.emit_cmovae_gpr_64(tmp, tmp_out); a.emit_mov(Size::S64, Location::GPR(tmp_out), ret); self.machine.release_temp_xmm(tmp_x2); self.machine.release_temp_xmm(tmp_x1); self.machine.release_temp_gpr(tmp); self.machine.release_temp_xmm(tmp_in); self.machine.release_temp_gpr(tmp_out); } Operator::F32ConvertSI32 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::F32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let tmp_out = self.machine.acquire_temp_xmm().unwrap(); let tmp_in = self.machine.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S32, loc, Location::GPR(tmp_in)); a.emit_vcvtsi2ss_32(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); a.emit_mov(Size::S32, Location::XMM(tmp_out), ret); self.machine.release_temp_gpr(tmp_in); self.machine.release_temp_xmm(tmp_out); } Operator::F32ConvertUI32 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::F32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let tmp_out = self.machine.acquire_temp_xmm().unwrap(); let tmp_in = self.machine.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S32, loc, Location::GPR(tmp_in)); a.emit_vcvtsi2ss_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); a.emit_mov(Size::S32, Location::XMM(tmp_out), ret); self.machine.release_temp_gpr(tmp_in); self.machine.release_temp_xmm(tmp_out); } Operator::F32ConvertSI64 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::F32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let tmp_out = self.machine.acquire_temp_xmm().unwrap(); let tmp_in = self.machine.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S64, loc, Location::GPR(tmp_in)); a.emit_vcvtsi2ss_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); a.emit_mov(Size::S32, Location::XMM(tmp_out), ret); self.machine.release_temp_gpr(tmp_in); self.machine.release_temp_xmm(tmp_out); } Operator::F32ConvertUI64 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::F32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let tmp_out = self.machine.acquire_temp_xmm().unwrap(); let tmp_in = self.machine.acquire_temp_gpr().unwrap(); let tmp = self.machine.acquire_temp_gpr().unwrap(); let do_convert = a.get_label(); let end_convert = a.get_label(); a.emit_mov(Size::S64, loc, Location::GPR(tmp_in)); a.emit_test_gpr_64(tmp_in); a.emit_jmp(Condition::Signed, do_convert); a.emit_vcvtsi2ss_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); a.emit_jmp(Condition::None, end_convert); a.emit_label(do_convert); a.emit_mov(Size::S64, Location::GPR(tmp_in), Location::GPR(tmp)); a.emit_and(Size::S64, Location::Imm32(1), Location::GPR(tmp)); a.emit_shr(Size::S64, Location::Imm8(1), Location::GPR(tmp_in)); a.emit_or(Size::S64, Location::GPR(tmp), Location::GPR(tmp_in)); a.emit_vcvtsi2ss_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); a.emit_vaddss(tmp_out, XMMOrMemory::XMM(tmp_out), tmp_out); a.emit_label(end_convert); a.emit_mov(Size::S32, Location::XMM(tmp_out), ret); self.machine.release_temp_gpr(tmp); self.machine.release_temp_gpr(tmp_in); self.machine.release_temp_xmm(tmp_out); } Operator::F64ConvertSI32 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::F64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let tmp_out = self.machine.acquire_temp_xmm().unwrap(); let tmp_in = self.machine.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S32, loc, Location::GPR(tmp_in)); a.emit_vcvtsi2sd_32(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); a.emit_mov(Size::S64, Location::XMM(tmp_out), ret); self.machine.release_temp_gpr(tmp_in); self.machine.release_temp_xmm(tmp_out); } Operator::F64ConvertUI32 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::F64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let tmp_out = self.machine.acquire_temp_xmm().unwrap(); let tmp_in = self.machine.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S32, loc, Location::GPR(tmp_in)); a.emit_vcvtsi2sd_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); a.emit_mov(Size::S64, Location::XMM(tmp_out), ret); self.machine.release_temp_gpr(tmp_in); self.machine.release_temp_xmm(tmp_out); } Operator::F64ConvertSI64 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::F64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let tmp_out = self.machine.acquire_temp_xmm().unwrap(); let tmp_in = self.machine.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S64, loc, Location::GPR(tmp_in)); a.emit_vcvtsi2sd_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); a.emit_mov(Size::S64, Location::XMM(tmp_out), ret); self.machine.release_temp_gpr(tmp_in); self.machine.release_temp_xmm(tmp_out); } Operator::F64ConvertUI64 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::F64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let tmp_out = self.machine.acquire_temp_xmm().unwrap(); let tmp_in = self.machine.acquire_temp_gpr().unwrap(); let tmp = self.machine.acquire_temp_gpr().unwrap(); let do_convert = a.get_label(); let end_convert = a.get_label(); a.emit_mov(Size::S64, loc, Location::GPR(tmp_in)); a.emit_test_gpr_64(tmp_in); a.emit_jmp(Condition::Signed, do_convert); a.emit_vcvtsi2sd_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); a.emit_jmp(Condition::None, end_convert); a.emit_label(do_convert); a.emit_mov(Size::S64, Location::GPR(tmp_in), Location::GPR(tmp)); a.emit_and(Size::S64, Location::Imm32(1), Location::GPR(tmp)); a.emit_shr(Size::S64, Location::Imm8(1), Location::GPR(tmp_in)); a.emit_or(Size::S64, Location::GPR(tmp), Location::GPR(tmp_in)); a.emit_vcvtsi2sd_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); a.emit_vaddsd(tmp_out, XMMOrMemory::XMM(tmp_out), tmp_out); a.emit_label(end_convert); a.emit_mov(Size::S64, Location::XMM(tmp_out), ret); self.machine.release_temp_gpr(tmp); self.machine.release_temp_gpr(tmp_in); self.machine.release_temp_xmm(tmp_out); } Operator::Call { function_index } => { let function_index = function_index as usize; let label = self .function_labels .as_mut() .unwrap() .entry(function_index) .or_insert_with(|| (a.get_label(), None)) .0; let sig_index = *self .function_signatures .get(FuncIndex::new(function_index)) .unwrap(); let sig = self.signatures.get(sig_index).unwrap(); let param_types: SmallVec<[WpType; 8]> = sig.params().iter().cloned().map(type_to_wp_type).collect(); let return_types: SmallVec<[WpType; 1]> = sig.returns().iter().cloned().map(type_to_wp_type).collect(); let params: SmallVec<[_; 8]> = self .value_stack .drain(self.value_stack.len() - param_types.len()..) .collect(); self.machine.release_locations_only_regs(¶ms); self.machine.release_locations_only_osr_state(params.len()); Self::emit_call_sysv_label( a, &mut self.machine, label, params.iter().map(|x| *x), Some((&mut self.fsm, &mut self.control_stack)), ); self.machine.release_locations_only_stack(a, ¶ms); if return_types.len() > 0 { let ret = self.machine.acquire_locations( a, &[( return_types[0], MachineValue::WasmStack(self.value_stack.len()), )], false, )[0]; self.value_stack.push(ret); a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); } } Operator::CallIndirect { index, table_index } => { assert_eq!(table_index, 0); let sig = self.signatures.get(SigIndex::new(index as usize)).unwrap(); let param_types: SmallVec<[WpType; 8]> = sig.params().iter().cloned().map(type_to_wp_type).collect(); let return_types: SmallVec<[WpType; 1]> = sig.returns().iter().cloned().map(type_to_wp_type).collect(); let func_index = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let params: SmallVec<[_; 8]> = self .value_stack .drain(self.value_stack.len() - param_types.len()..) .collect(); self.machine.release_locations_only_regs(¶ms); let table_base = self.machine.acquire_temp_gpr().unwrap(); let table_count = self.machine.acquire_temp_gpr().unwrap(); let sigidx = self.machine.acquire_temp_gpr().unwrap(); a.emit_mov( Size::S64, Location::Memory( Machine::get_vmctx_reg(), match TableIndex::new(0).local_or_import(module_info) { LocalOrImport::Local(_) => vm::Ctx::offset_tables(), LocalOrImport::Import(_) => vm::Ctx::offset_imported_tables(), } as i32, ), Location::GPR(table_base), ); a.emit_mov( Size::S64, Location::Memory(table_base, 0), Location::GPR(table_base), ); a.emit_mov( Size::S32, Location::Memory(table_base, LocalTable::offset_count() as i32), Location::GPR(table_count), ); a.emit_mov( Size::S64, Location::Memory(table_base, LocalTable::offset_base() as i32), Location::GPR(table_base), ); a.emit_cmp(Size::S32, func_index, Location::GPR(table_count)); a.emit_conditional_trap(Condition::BelowEqual); a.emit_mov(Size::S64, func_index, Location::GPR(table_count)); a.emit_imul_imm32_gpr64(vm::Anyfunc::size() as u32, table_count); a.emit_add( Size::S64, Location::GPR(table_base), Location::GPR(table_count), ); a.emit_mov( Size::S64, Location::Memory( Machine::get_vmctx_reg(), vm::Ctx::offset_signatures() as i32, ), Location::GPR(sigidx), ); a.emit_mov( Size::S32, Location::Memory(sigidx, (index * 4) as i32), Location::GPR(sigidx), ); a.emit_cmp( Size::S32, Location::GPR(sigidx), Location::Memory(table_count, (vm::Anyfunc::offset_sig_id() as usize) as i32), ); a.emit_conditional_trap(Condition::NotEqual); self.machine.release_temp_gpr(sigidx); self.machine.release_temp_gpr(table_count); self.machine.release_temp_gpr(table_base); if table_count != GPR::RAX { a.emit_mov( Size::S64, Location::GPR(table_count), Location::GPR(GPR::RAX), ); } self.machine.release_locations_only_osr_state(params.len()); Self::emit_call_sysv( a, &mut self.machine, |a| { a.emit_call_location(Location::Memory( GPR::RAX, (vm::Anyfunc::offset_func() as usize) as i32, )); }, params.iter().map(|x| *x), Some((&mut self.fsm, &mut self.control_stack)), ); self.machine.release_locations_only_stack(a, ¶ms); if return_types.len() > 0 { let ret = self.machine.acquire_locations( a, &[( return_types[0], MachineValue::WasmStack(self.value_stack.len()), )], false, )[0]; self.value_stack.push(ret); a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); } } Operator::If { ty } => { let label_end = a.get_label(); let label_else = a.get_label(); let cond = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let frame = ControlFrame { label: label_end, loop_like: false, if_else: IfElseState::If(label_else), returns: match ty { WpTypeOrFuncType::Type(WpType::EmptyBlockType) => smallvec![], WpTypeOrFuncType::Type(inner_ty) => smallvec![inner_ty], _ => { return Err(CodegenError { message: format!("multi-value returns not yet implemented"), }) } }, value_stack_depth: self.value_stack.len(), state: self.machine.state.clone(), state_diff_id: Self::get_state_diff( &self.machine, &mut self.fsm, &mut self.control_stack, ), }; self.control_stack.push(frame); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_cmp, Size::S32, Location::Imm32(0), cond, ); a.emit_jmp(Condition::Equal, label_else); } Operator::Else => { let mut frame = self.control_stack.last_mut().unwrap(); if !was_unreachable && frame.returns.len() > 0 { let loc = *self.value_stack.last().unwrap(); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S64, loc, Location::GPR(GPR::RAX), ); } let released: &[Location] = &self.value_stack[frame.value_stack_depth..]; self.machine.release_locations(a, released); self.value_stack.truncate(frame.value_stack_depth); match frame.if_else { IfElseState::If(label) => { a.emit_jmp(Condition::None, frame.label); a.emit_label(label); frame.if_else = IfElseState::Else; } _ => unreachable!(), } } Operator::Select => { let cond = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let v_b = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let v_a = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let end_label = a.get_label(); let zero_label = a.get_label(); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_cmp, Size::S32, Location::Imm32(0), cond, ); a.emit_jmp(Condition::Equal, zero_label); if v_a != ret { Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S64, v_a, ret, ); } a.emit_jmp(Condition::None, end_label); a.emit_label(zero_label); if v_b != ret { Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S64, v_b, ret, ); } a.emit_label(end_label); } Operator::Block { ty } => { let frame = ControlFrame { label: a.get_label(), loop_like: false, if_else: IfElseState::None, returns: match ty { WpTypeOrFuncType::Type(WpType::EmptyBlockType) => smallvec![], WpTypeOrFuncType::Type(inner_ty) => smallvec![inner_ty], _ => { return Err(CodegenError { message: format!("multi-value returns not yet implemented"), }) } }, value_stack_depth: self.value_stack.len(), state: self.machine.state.clone(), state_diff_id: Self::get_state_diff( &self.machine, &mut self.fsm, &mut self.control_stack, ), }; self.control_stack.push(frame); } Operator::Loop { ty } => { let label = a.get_label(); let state_diff_id = Self::get_state_diff(&self.machine, &mut self.fsm, &mut self.control_stack); let activate_offset = a.get_offset().0; self.control_stack.push(ControlFrame { label: label, loop_like: true, if_else: IfElseState::None, returns: match ty { WpTypeOrFuncType::Type(WpType::EmptyBlockType) => smallvec![], WpTypeOrFuncType::Type(inner_ty) => smallvec![inner_ty], _ => { return Err(CodegenError { message: format!("multi-value returns not yet implemented"), }) } }, value_stack_depth: self.value_stack.len(), state: self.machine.state.clone(), state_diff_id, }); a.emit_label(label); // Check interrupt signal without branching a.emit_mov( Size::S64, Location::Memory( Machine::get_vmctx_reg(), vm::Ctx::offset_interrupt_signal_mem() as i32, ), Location::GPR(GPR::RAX), ); self.fsm.loop_offsets.insert( a.get_offset().0, OffsetInfo { end_offset: a.get_offset().0 + 1, activate_offset, diff_id: state_diff_id, }, ); self.fsm.wasm_offset_to_target_offset.insert( self.machine.state.wasm_inst_offset, SuspendOffset::Loop(a.get_offset().0), ); a.emit_mov( Size::S64, Location::Memory(GPR::RAX, 0), Location::GPR(GPR::RAX), ); } Operator::Nop => {} Operator::MemorySize { reserved } => { let memory_index = MemoryIndex::new(reserved as usize); a.emit_mov( Size::S64, Location::Memory( Machine::get_vmctx_reg(), vm::Ctx::offset_intrinsics() as i32, ), Location::GPR(GPR::RAX), ); a.emit_mov( Size::S64, Location::Memory(GPR::RAX, vm::Intrinsics::offset_memory_size() as i32), Location::GPR(GPR::RAX), ); Self::emit_call_sysv( a, &mut self.machine, |a| { a.emit_call_location(Location::GPR(GPR::RAX)); }, ::std::iter::once(Location::Imm32(memory_index.index() as u32)), None, ); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); } Operator::MemoryGrow { reserved } => { let memory_index = MemoryIndex::new(reserved as usize); let param_pages = self.value_stack.pop().unwrap(); self.machine.release_locations_only_regs(&[param_pages]); a.emit_mov( Size::S64, Location::Memory( Machine::get_vmctx_reg(), vm::Ctx::offset_intrinsics() as i32, ), Location::GPR(GPR::RAX), ); a.emit_mov( Size::S64, Location::Memory(GPR::RAX, vm::Intrinsics::offset_memory_grow() as i32), Location::GPR(GPR::RAX), ); self.machine.release_locations_only_osr_state(1); Self::emit_call_sysv( a, &mut self.machine, |a| { a.emit_call_location(Location::GPR(GPR::RAX)); }, ::std::iter::once(Location::Imm32(memory_index.index() as u32)) .chain(::std::iter::once(param_pages)), None, ); self.machine.release_locations_only_stack(a, &[param_pages]); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); } Operator::I32Load { ref memarg } => { let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, false, 4, |a, m, addr| { Self::emit_relaxed_binop( a, m, Assembler::emit_mov, Size::S32, Location::Memory(addr, 0), ret, ); }, ); } Operator::F32Load { ref memarg } => { let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::F32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, false, 4, |a, m, addr| { Self::emit_relaxed_binop( a, m, Assembler::emit_mov, Size::S32, Location::Memory(addr, 0), ret, ); }, ); } Operator::I32Load8U { ref memarg } => { let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, false, 1, |a, m, addr| { Self::emit_relaxed_zx_sx( a, m, Assembler::emit_movzx, Size::S8, Location::Memory(addr, 0), Size::S32, ret, ); }, ); } Operator::I32Load8S { ref memarg } => { let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, false, 1, |a, m, addr| { Self::emit_relaxed_zx_sx( a, m, Assembler::emit_movsx, Size::S8, Location::Memory(addr, 0), Size::S32, ret, ); }, ); } Operator::I32Load16U { ref memarg } => { let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, false, 2, |a, m, addr| { Self::emit_relaxed_zx_sx( a, m, Assembler::emit_movzx, Size::S16, Location::Memory(addr, 0), Size::S32, ret, ); }, ); } Operator::I32Load16S { ref memarg } => { let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, false, 2, |a, m, addr| { Self::emit_relaxed_zx_sx( a, m, Assembler::emit_movsx, Size::S16, Location::Memory(addr, 0), Size::S32, ret, ); }, ); } Operator::I32Store { ref memarg } => { let target_value = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target_addr = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target_addr, memarg, false, 4, |a, m, addr| { Self::emit_relaxed_binop( a, m, Assembler::emit_mov, Size::S32, target_value, Location::Memory(addr, 0), ); }, ); } Operator::F32Store { ref memarg } => { let target_value = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target_addr = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target_addr, memarg, false, 4, |a, m, addr| { Self::emit_relaxed_binop( a, m, Assembler::emit_mov, Size::S32, target_value, Location::Memory(addr, 0), ); }, ); } Operator::I32Store8 { ref memarg } => { let target_value = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target_addr = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target_addr, memarg, false, 1, |a, m, addr| { Self::emit_relaxed_binop( a, m, Assembler::emit_mov, Size::S8, target_value, Location::Memory(addr, 0), ); }, ); } Operator::I32Store16 { ref memarg } => { let target_value = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target_addr = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target_addr, memarg, false, 2, |a, m, addr| { Self::emit_relaxed_binop( a, m, Assembler::emit_mov, Size::S16, target_value, Location::Memory(addr, 0), ); }, ); } Operator::I64Load { ref memarg } => { let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, false, 8, |a, m, addr| { Self::emit_relaxed_binop( a, m, Assembler::emit_mov, Size::S64, Location::Memory(addr, 0), ret, ); }, ); } Operator::F64Load { ref memarg } => { let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::F64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, false, 8, |a, m, addr| { Self::emit_relaxed_binop( a, m, Assembler::emit_mov, Size::S64, Location::Memory(addr, 0), ret, ); }, ); } Operator::I64Load8U { ref memarg } => { let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, false, 1, |a, m, addr| { Self::emit_relaxed_zx_sx( a, m, Assembler::emit_movzx, Size::S8, Location::Memory(addr, 0), Size::S64, ret, ); }, ); } Operator::I64Load8S { ref memarg } => { let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, false, 1, |a, m, addr| { Self::emit_relaxed_zx_sx( a, m, Assembler::emit_movsx, Size::S8, Location::Memory(addr, 0), Size::S64, ret, ); }, ); } Operator::I64Load16U { ref memarg } => { let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, false, 2, |a, m, addr| { Self::emit_relaxed_zx_sx( a, m, Assembler::emit_movzx, Size::S16, Location::Memory(addr, 0), Size::S64, ret, ); }, ); } Operator::I64Load16S { ref memarg } => { let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, false, 2, |a, m, addr| { Self::emit_relaxed_zx_sx( a, m, Assembler::emit_movsx, Size::S16, Location::Memory(addr, 0), Size::S64, ret, ); }, ); } Operator::I64Load32U { ref memarg } => { let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, false, 4, |a, m, addr| { match ret { Location::GPR(_) => {} Location::Memory(base, offset) => { a.emit_mov( Size::S32, Location::Imm32(0), Location::Memory(base, offset + 4), ); // clear upper bits } _ => unreachable!(), } Self::emit_relaxed_binop( a, m, Assembler::emit_mov, Size::S32, Location::Memory(addr, 0), ret, ); }, ); } Operator::I64Load32S { ref memarg } => { let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, false, 4, |a, m, addr| { Self::emit_relaxed_zx_sx( a, m, Assembler::emit_movsx, Size::S32, Location::Memory(addr, 0), Size::S64, ret, ); }, ); } Operator::I64Store { ref memarg } => { let target_value = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target_addr = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target_addr, memarg, false, 8, |a, m, addr| { Self::emit_relaxed_binop( a, m, Assembler::emit_mov, Size::S64, target_value, Location::Memory(addr, 0), ); }, ); } Operator::F64Store { ref memarg } => { let target_value = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target_addr = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target_addr, memarg, false, 8, |a, m, addr| { Self::emit_relaxed_binop( a, m, Assembler::emit_mov, Size::S64, target_value, Location::Memory(addr, 0), ); }, ); } Operator::I64Store8 { ref memarg } => { let target_value = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target_addr = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target_addr, memarg, false, 1, |a, m, addr| { Self::emit_relaxed_binop( a, m, Assembler::emit_mov, Size::S8, target_value, Location::Memory(addr, 0), ); }, ); } Operator::I64Store16 { ref memarg } => { let target_value = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target_addr = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target_addr, memarg, false, 2, |a, m, addr| { Self::emit_relaxed_binop( a, m, Assembler::emit_mov, Size::S16, target_value, Location::Memory(addr, 0), ); }, ); } Operator::I64Store32 { ref memarg } => { let target_value = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target_addr = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target_addr, memarg, false, 4, |a, m, addr| { Self::emit_relaxed_binop( a, m, Assembler::emit_mov, Size::S32, target_value, Location::Memory(addr, 0), ); }, ); } Operator::Unreachable => { Self::mark_trappable(a, &self.machine, &mut self.fsm, &mut self.control_stack); a.emit_ud2(); self.unreachable_depth = 1; } Operator::Return => { let frame = &self.control_stack[0]; if frame.returns.len() > 0 { assert_eq!(frame.returns.len(), 1); let loc = *self.value_stack.last().unwrap(); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S64, loc, Location::GPR(GPR::RAX), ); } let released = &self.value_stack[frame.value_stack_depth..]; self.machine.release_locations_keep_state(a, released); a.emit_jmp(Condition::None, frame.label); self.unreachable_depth = 1; } Operator::Br { relative_depth } => { let frame = &self.control_stack[self.control_stack.len() - 1 - (relative_depth as usize)]; if !frame.loop_like && frame.returns.len() > 0 { assert_eq!(frame.returns.len(), 1); let loc = *self.value_stack.last().unwrap(); a.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX)); } let released = &self.value_stack[frame.value_stack_depth..]; self.machine.release_locations_keep_state(a, released); a.emit_jmp(Condition::None, frame.label); self.unreachable_depth = 1; } Operator::BrIf { relative_depth } => { let after = a.get_label(); let cond = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_cmp, Size::S32, Location::Imm32(0), cond, ); a.emit_jmp(Condition::Equal, after); let frame = &self.control_stack[self.control_stack.len() - 1 - (relative_depth as usize)]; if !frame.loop_like && frame.returns.len() > 0 { assert_eq!(frame.returns.len(), 1); let loc = *self.value_stack.last().unwrap(); a.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX)); } let released = &self.value_stack[frame.value_stack_depth..]; self.machine.release_locations_keep_state(a, released); a.emit_jmp(Condition::None, frame.label); a.emit_label(after); } Operator::BrTable { ref table } => { let (targets, default_target) = table.read_table().unwrap(); let cond = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let table_label = a.get_label(); let mut table: Vec<DynamicLabel> = vec![]; let default_br = a.get_label(); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_cmp, Size::S32, Location::Imm32(targets.len() as u32), cond, ); a.emit_jmp(Condition::AboveEqual, default_br); a.emit_lea_label(table_label, Location::GPR(GPR::RCX)); a.emit_mov(Size::S32, cond, Location::GPR(GPR::RDX)); a.emit_imul_imm32_gpr64(5, GPR::RDX); a.emit_add(Size::S64, Location::GPR(GPR::RCX), Location::GPR(GPR::RDX)); a.emit_jmp_location(Location::GPR(GPR::RDX)); for target in targets.iter() { let label = a.get_label(); a.emit_label(label); table.push(label); let frame = &self.control_stack[self.control_stack.len() - 1 - (*target as usize)]; if !frame.loop_like && frame.returns.len() > 0 { assert_eq!(frame.returns.len(), 1); let loc = *self.value_stack.last().unwrap(); a.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX)); } let released = &self.value_stack[frame.value_stack_depth..]; self.machine.release_locations_keep_state(a, released); a.emit_jmp(Condition::None, frame.label); } a.emit_label(default_br); { let frame = &self.control_stack [self.control_stack.len() - 1 - (default_target as usize)]; if !frame.loop_like && frame.returns.len() > 0 { assert_eq!(frame.returns.len(), 1); let loc = *self.value_stack.last().unwrap(); a.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX)); } let released = &self.value_stack[frame.value_stack_depth..]; self.machine.release_locations_keep_state(a, released); a.emit_jmp(Condition::None, frame.label); } a.emit_label(table_label); for x in table { a.emit_jmp(Condition::None, x); } self.unreachable_depth = 1; } Operator::Drop => { get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); } Operator::End => { let frame = self.control_stack.pop().unwrap(); if !was_unreachable && frame.returns.len() > 0 { let loc = *self.value_stack.last().unwrap(); Self::emit_relaxed_binop( a, &mut self.machine, Assembler::emit_mov, Size::S64, loc, Location::GPR(GPR::RAX), ); } if self.control_stack.len() == 0 { a.emit_label(frame.label); self.machine.finalize_locals(a, &self.locals); a.emit_mov(Size::S64, Location::GPR(GPR::RBP), Location::GPR(GPR::RSP)); a.emit_pop(Size::S64, Location::GPR(GPR::RBP)); a.emit_ret(); } else { let released = &self.value_stack[frame.value_stack_depth..]; self.machine.release_locations(a, released); self.value_stack.truncate(frame.value_stack_depth); if !frame.loop_like { a.emit_label(frame.label); } if let IfElseState::If(label) = frame.if_else { a.emit_label(label); } if frame.returns.len() > 0 { assert_eq!(frame.returns.len(), 1); let loc = self.machine.acquire_locations( a, &[( frame.returns[0], MachineValue::WasmStack(self.value_stack.len()), )], false, )[0]; a.emit_mov(Size::S64, Location::GPR(GPR::RAX), loc); self.value_stack.push(loc); } } } Operator::Fence { flags: _ } => { // Fence is a nop. // // Fence was added to preserve information about fences from // source languages. If in the future Wasm extends the memory // model, and if we hadn't recorded what fences used to be there, // it would lead to data races that weren't present in the // original source language. } Operator::I32AtomicLoad { ref memarg } => { let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 4, |a, m, addr| { Self::emit_relaxed_binop( a, m, Assembler::emit_mov, Size::S32, Location::Memory(addr, 0), ret, ); }, ); } Operator::I32AtomicLoad8U { ref memarg } => { let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 1, |a, m, addr| { Self::emit_relaxed_zx_sx( a, m, Assembler::emit_movzx, Size::S8, Location::Memory(addr, 0), Size::S32, ret, ); }, ); } Operator::I32AtomicLoad16U { ref memarg } => { let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 2, |a, m, addr| { Self::emit_relaxed_zx_sx( a, m, Assembler::emit_movzx, Size::S16, Location::Memory(addr, 0), Size::S32, ret, ); }, ); } Operator::I32AtomicStore { ref memarg } => { let target_value = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target_addr = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target_addr, memarg, true, 4, |a, m, addr| { Self::emit_relaxed_binop( a, m, Assembler::emit_xchg, Size::S32, target_value, Location::Memory(addr, 0), ); }, ); } Operator::I32AtomicStore8 { ref memarg } => { let target_value = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target_addr = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target_addr, memarg, true, 1, |a, m, addr| { Self::emit_relaxed_binop( a, m, Assembler::emit_xchg, Size::S8, target_value, Location::Memory(addr, 0), ); }, ); } Operator::I32AtomicStore16 { ref memarg } => { let target_value = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target_addr = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target_addr, memarg, true, 2, |a, m, addr| { Self::emit_relaxed_binop( a, m, Assembler::emit_xchg, Size::S16, target_value, Location::Memory(addr, 0), ); }, ); } Operator::I64AtomicLoad { ref memarg } => { let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 8, |a, m, addr| { Self::emit_relaxed_binop( a, m, Assembler::emit_mov, Size::S64, Location::Memory(addr, 0), ret, ); }, ); } Operator::I64AtomicLoad8U { ref memarg } => { let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 1, |a, m, addr| { Self::emit_relaxed_zx_sx( a, m, Assembler::emit_movzx, Size::S8, Location::Memory(addr, 0), Size::S64, ret, ); }, ); } Operator::I64AtomicLoad16U { ref memarg } => { let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 2, |a, m, addr| { Self::emit_relaxed_zx_sx( a, m, Assembler::emit_movzx, Size::S16, Location::Memory(addr, 0), Size::S64, ret, ); }, ); } Operator::I64AtomicLoad32U { ref memarg } => { let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 4, |a, m, addr| { match ret { Location::GPR(_) => {} Location::Memory(base, offset) => { a.emit_mov( Size::S32, Location::Imm32(0), Location::Memory(base, offset + 4), ); // clear upper bits } _ => unreachable!(), } Self::emit_relaxed_binop( a, m, Assembler::emit_mov, Size::S32, Location::Memory(addr, 0), ret, ); }, ); } Operator::I64AtomicStore { ref memarg } => { let target_value = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target_addr = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target_addr, memarg, true, 8, |a, m, addr| { Self::emit_relaxed_binop( a, m, Assembler::emit_xchg, Size::S64, target_value, Location::Memory(addr, 0), ); }, ); } Operator::I64AtomicStore8 { ref memarg } => { let target_value = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target_addr = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target_addr, memarg, true, 1, |a, m, addr| { Self::emit_relaxed_binop( a, m, Assembler::emit_xchg, Size::S8, target_value, Location::Memory(addr, 0), ); }, ); } Operator::I64AtomicStore16 { ref memarg } => { let target_value = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target_addr = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target_addr, memarg, true, 2, |a, m, addr| { Self::emit_relaxed_binop( a, m, Assembler::emit_xchg, Size::S16, target_value, Location::Memory(addr, 0), ); }, ); } Operator::I64AtomicStore32 { ref memarg } => { let target_value = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target_addr = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target_addr, memarg, true, 4, |a, m, addr| { Self::emit_relaxed_binop( a, m, Assembler::emit_xchg, Size::S32, target_value, Location::Memory(addr, 0), ); }, ); } Operator::I32AtomicRmwAdd { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let value = self.machine.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S32, loc, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 4, |a, _m, addr| { a.emit_lock_xadd(Size::S32, Location::GPR(value), Location::Memory(addr, 0)) }, ); a.emit_mov(Size::S32, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } Operator::I64AtomicRmwAdd { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let value = self.machine.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S64, loc, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 8, |a, _m, addr| { a.emit_lock_xadd(Size::S64, Location::GPR(value), Location::Memory(addr, 0)) }, ); a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } Operator::I32AtomicRmw8UAdd { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let value = self.machine.acquire_temp_gpr().unwrap(); a.emit_movzx(Size::S8, loc, Size::S32, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 1, |a, _m, addr| { a.emit_lock_xadd(Size::S8, Location::GPR(value), Location::Memory(addr, 0)) }, ); a.emit_mov(Size::S32, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } Operator::I32AtomicRmw16UAdd { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let value = self.machine.acquire_temp_gpr().unwrap(); a.emit_movzx(Size::S16, loc, Size::S32, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 2, |a, _m, addr| { a.emit_lock_xadd(Size::S16, Location::GPR(value), Location::Memory(addr, 0)) }, ); a.emit_mov(Size::S32, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } Operator::I64AtomicRmw8UAdd { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let value = self.machine.acquire_temp_gpr().unwrap(); a.emit_movzx(Size::S8, loc, Size::S64, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 1, |a, _m, addr| { a.emit_lock_xadd(Size::S8, Location::GPR(value), Location::Memory(addr, 0)) }, ); a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } Operator::I64AtomicRmw16UAdd { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let value = self.machine.acquire_temp_gpr().unwrap(); a.emit_movzx(Size::S16, loc, Size::S64, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 2, |a, _m, addr| { a.emit_lock_xadd(Size::S16, Location::GPR(value), Location::Memory(addr, 0)) }, ); a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } Operator::I64AtomicRmw32UAdd { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let value = self.machine.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S32, loc, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 4, |a, _m, addr| { a.emit_lock_xadd(Size::S32, Location::GPR(value), Location::Memory(addr, 0)) }, ); a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } Operator::I32AtomicRmwSub { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let value = self.machine.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S32, loc, Location::GPR(value)); a.emit_neg(Size::S32, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 4, |a, _m, addr| { a.emit_lock_xadd(Size::S32, Location::GPR(value), Location::Memory(addr, 0)) }, ); a.emit_mov(Size::S32, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } Operator::I64AtomicRmwSub { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let value = self.machine.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S64, loc, Location::GPR(value)); a.emit_neg(Size::S64, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 8, |a, _m, addr| { a.emit_lock_xadd(Size::S64, Location::GPR(value), Location::Memory(addr, 0)) }, ); a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } Operator::I32AtomicRmw8USub { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let value = self.machine.acquire_temp_gpr().unwrap(); a.emit_movzx(Size::S8, loc, Size::S32, Location::GPR(value)); a.emit_neg(Size::S8, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 1, |a, _m, addr| { a.emit_lock_xadd(Size::S8, Location::GPR(value), Location::Memory(addr, 0)) }, ); a.emit_mov(Size::S32, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } Operator::I32AtomicRmw16USub { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let value = self.machine.acquire_temp_gpr().unwrap(); a.emit_movzx(Size::S16, loc, Size::S32, Location::GPR(value)); a.emit_neg(Size::S16, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 2, |a, _m, addr| { a.emit_lock_xadd(Size::S16, Location::GPR(value), Location::Memory(addr, 0)) }, ); a.emit_mov(Size::S32, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } Operator::I64AtomicRmw8USub { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let value = self.machine.acquire_temp_gpr().unwrap(); a.emit_movzx(Size::S8, loc, Size::S64, Location::GPR(value)); a.emit_neg(Size::S8, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 1, |a, _m, addr| { a.emit_lock_xadd(Size::S8, Location::GPR(value), Location::Memory(addr, 0)) }, ); a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } Operator::I64AtomicRmw16USub { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let value = self.machine.acquire_temp_gpr().unwrap(); a.emit_movzx(Size::S16, loc, Size::S64, Location::GPR(value)); a.emit_neg(Size::S16, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 2, |a, _m, addr| { a.emit_lock_xadd(Size::S16, Location::GPR(value), Location::Memory(addr, 0)) }, ); a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } Operator::I64AtomicRmw32USub { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let value = self.machine.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S32, loc, Location::GPR(value)); a.emit_neg(Size::S32, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 2, |a, _m, addr| { a.emit_lock_xadd(Size::S32, Location::GPR(value), Location::Memory(addr, 0)) }, ); a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } Operator::I32AtomicRmwAnd { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_compare_and_swap( module_info, &self.config, a, &mut self.machine, loc, target, ret, memarg, 4, Size::S32, Size::S32, |a, _m, src, dst| { a.emit_and(Size::S32, Location::GPR(src), Location::GPR(dst)); }, ); } Operator::I64AtomicRmwAnd { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_compare_and_swap( module_info, &self.config, a, &mut self.machine, loc, target, ret, memarg, 8, Size::S64, Size::S64, |a, _m, src, dst| { a.emit_and(Size::S64, Location::GPR(src), Location::GPR(dst)); }, ); } Operator::I32AtomicRmw8UAnd { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_compare_and_swap( module_info, &self.config, a, &mut self.machine, loc, target, ret, memarg, 1, Size::S8, Size::S32, |a, _m, src, dst| { a.emit_and(Size::S32, Location::GPR(src), Location::GPR(dst)); }, ); } Operator::I32AtomicRmw16UAnd { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_compare_and_swap( module_info, &self.config, a, &mut self.machine, loc, target, ret, memarg, 1, Size::S16, Size::S32, |a, _m, src, dst| { a.emit_and(Size::S32, Location::GPR(src), Location::GPR(dst)); }, ); } Operator::I64AtomicRmw8UAnd { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_compare_and_swap( module_info, &self.config, a, &mut self.machine, loc, target, ret, memarg, 1, Size::S8, Size::S64, |a, _m, src, dst| { a.emit_and(Size::S64, Location::GPR(src), Location::GPR(dst)); }, ); } Operator::I64AtomicRmw16UAnd { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_compare_and_swap( module_info, &self.config, a, &mut self.machine, loc, target, ret, memarg, 1, Size::S16, Size::S64, |a, _m, src, dst| { a.emit_and(Size::S64, Location::GPR(src), Location::GPR(dst)); }, ); } Operator::I64AtomicRmw32UAnd { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_compare_and_swap( module_info, &self.config, a, &mut self.machine, loc, target, ret, memarg, 1, Size::S32, Size::S64, |a, _m, src, dst| { a.emit_and(Size::S64, Location::GPR(src), Location::GPR(dst)); }, ); } Operator::I32AtomicRmwOr { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_compare_and_swap( module_info, &self.config, a, &mut self.machine, loc, target, ret, memarg, 4, Size::S32, Size::S32, |a, _m, src, dst| { a.emit_or(Size::S32, Location::GPR(src), Location::GPR(dst)); }, ); } Operator::I64AtomicRmwOr { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_compare_and_swap( module_info, &self.config, a, &mut self.machine, loc, target, ret, memarg, 8, Size::S64, Size::S64, |a, _m, src, dst| { a.emit_or(Size::S64, Location::GPR(src), Location::GPR(dst)); }, ); } Operator::I32AtomicRmw8UOr { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_compare_and_swap( module_info, &self.config, a, &mut self.machine, loc, target, ret, memarg, 1, Size::S8, Size::S32, |a, _m, src, dst| { a.emit_or(Size::S32, Location::GPR(src), Location::GPR(dst)); }, ); } Operator::I32AtomicRmw16UOr { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_compare_and_swap( module_info, &self.config, a, &mut self.machine, loc, target, ret, memarg, 1, Size::S16, Size::S32, |a, _m, src, dst| { a.emit_or(Size::S32, Location::GPR(src), Location::GPR(dst)); }, ); } Operator::I64AtomicRmw8UOr { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_compare_and_swap( module_info, &self.config, a, &mut self.machine, loc, target, ret, memarg, 1, Size::S8, Size::S64, |a, _m, src, dst| { a.emit_or(Size::S64, Location::GPR(src), Location::GPR(dst)); }, ); } Operator::I64AtomicRmw16UOr { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_compare_and_swap( module_info, &self.config, a, &mut self.machine, loc, target, ret, memarg, 1, Size::S16, Size::S64, |a, _m, src, dst| { a.emit_or(Size::S64, Location::GPR(src), Location::GPR(dst)); }, ); } Operator::I64AtomicRmw32UOr { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_compare_and_swap( module_info, &self.config, a, &mut self.machine, loc, target, ret, memarg, 1, Size::S32, Size::S64, |a, _m, src, dst| { a.emit_or(Size::S64, Location::GPR(src), Location::GPR(dst)); }, ); } Operator::I32AtomicRmwXor { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_compare_and_swap( module_info, &self.config, a, &mut self.machine, loc, target, ret, memarg, 4, Size::S32, Size::S32, |a, _m, src, dst| { a.emit_xor(Size::S32, Location::GPR(src), Location::GPR(dst)); }, ); } Operator::I64AtomicRmwXor { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_compare_and_swap( module_info, &self.config, a, &mut self.machine, loc, target, ret, memarg, 8, Size::S64, Size::S64, |a, _m, src, dst| { a.emit_xor(Size::S64, Location::GPR(src), Location::GPR(dst)); }, ); } Operator::I32AtomicRmw8UXor { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_compare_and_swap( module_info, &self.config, a, &mut self.machine, loc, target, ret, memarg, 1, Size::S8, Size::S32, |a, _m, src, dst| { a.emit_xor(Size::S32, Location::GPR(src), Location::GPR(dst)); }, ); } Operator::I32AtomicRmw16UXor { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_compare_and_swap( module_info, &self.config, a, &mut self.machine, loc, target, ret, memarg, 1, Size::S16, Size::S32, |a, _m, src, dst| { a.emit_xor(Size::S32, Location::GPR(src), Location::GPR(dst)); }, ); } Operator::I64AtomicRmw8UXor { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_compare_and_swap( module_info, &self.config, a, &mut self.machine, loc, target, ret, memarg, 1, Size::S8, Size::S64, |a, _m, src, dst| { a.emit_xor(Size::S64, Location::GPR(src), Location::GPR(dst)); }, ); } Operator::I64AtomicRmw16UXor { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_compare_and_swap( module_info, &self.config, a, &mut self.machine, loc, target, ret, memarg, 1, Size::S16, Size::S64, |a, _m, src, dst| { a.emit_xor(Size::S64, Location::GPR(src), Location::GPR(dst)); }, ); } Operator::I64AtomicRmw32UXor { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); Self::emit_compare_and_swap( module_info, &self.config, a, &mut self.machine, loc, target, ret, memarg, 1, Size::S32, Size::S64, |a, _m, src, dst| { a.emit_xor(Size::S64, Location::GPR(src), Location::GPR(dst)); }, ); } Operator::I32AtomicRmwXchg { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let value = self.machine.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S32, loc, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 4, |a, _m, addr| { a.emit_xchg(Size::S32, Location::GPR(value), Location::Memory(addr, 0)) }, ); a.emit_mov(Size::S32, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } Operator::I64AtomicRmwXchg { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let value = self.machine.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S64, loc, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 8, |a, _m, addr| { a.emit_xchg(Size::S64, Location::GPR(value), Location::Memory(addr, 0)) }, ); a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } Operator::I32AtomicRmw8UXchg { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let value = self.machine.acquire_temp_gpr().unwrap(); a.emit_movzx(Size::S8, loc, Size::S32, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 1, |a, _m, addr| { a.emit_xchg(Size::S8, Location::GPR(value), Location::Memory(addr, 0)) }, ); a.emit_mov(Size::S32, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } Operator::I32AtomicRmw16UXchg { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let value = self.machine.acquire_temp_gpr().unwrap(); a.emit_movzx(Size::S16, loc, Size::S32, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 2, |a, _m, addr| { a.emit_xchg(Size::S16, Location::GPR(value), Location::Memory(addr, 0)) }, ); a.emit_mov(Size::S32, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } Operator::I64AtomicRmw8UXchg { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let value = self.machine.acquire_temp_gpr().unwrap(); a.emit_movzx(Size::S8, loc, Size::S64, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 1, |a, _m, addr| { a.emit_xchg(Size::S8, Location::GPR(value), Location::Memory(addr, 0)) }, ); a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } Operator::I64AtomicRmw16UXchg { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let value = self.machine.acquire_temp_gpr().unwrap(); a.emit_movzx(Size::S16, loc, Size::S64, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 2, |a, _m, addr| { a.emit_xchg(Size::S16, Location::GPR(value), Location::Memory(addr, 0)) }, ); a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } Operator::I64AtomicRmw32UXchg { ref memarg } => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let value = self.machine.acquire_temp_gpr().unwrap(); a.emit_mov(Size::S32, loc, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 4, |a, _m, addr| { a.emit_xchg(Size::S32, Location::GPR(value), Location::Memory(addr, 0)) }, ); a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } Operator::I32AtomicRmwCmpxchg { ref memarg } => { let new = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let cmp = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let compare = self.machine.reserve_unused_temp_gpr(GPR::RAX); let value = if cmp == Location::GPR(GPR::R14) { if new == Location::GPR(GPR::R13) { GPR::R12 } else { GPR::R13 } } else { GPR::R14 }; a.emit_push(Size::S64, Location::GPR(value)); a.emit_mov(Size::S32, cmp, Location::GPR(compare)); a.emit_mov(Size::S32, new, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 4, |a, _m, addr| { a.emit_lock_cmpxchg( Size::S32, Location::GPR(value), Location::Memory(addr, 0), ); a.emit_mov(Size::S32, Location::GPR(compare), ret); }, ); a.emit_pop(Size::S64, Location::GPR(value)); self.machine.release_temp_gpr(compare); } Operator::I64AtomicRmwCmpxchg { ref memarg } => { let new = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let cmp = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let compare = self.machine.reserve_unused_temp_gpr(GPR::RAX); let value = if cmp == Location::GPR(GPR::R14) { if new == Location::GPR(GPR::R13) { GPR::R12 } else { GPR::R13 } } else { GPR::R14 }; a.emit_push(Size::S64, Location::GPR(value)); a.emit_mov(Size::S64, cmp, Location::GPR(compare)); a.emit_mov(Size::S64, new, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 8, |a, _m, addr| { a.emit_lock_cmpxchg( Size::S64, Location::GPR(value), Location::Memory(addr, 0), ); a.emit_mov(Size::S64, Location::GPR(compare), ret); }, ); a.emit_pop(Size::S64, Location::GPR(value)); self.machine.release_temp_gpr(compare); } Operator::I32AtomicRmw8UCmpxchg { ref memarg } => { let new = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let cmp = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let compare = self.machine.reserve_unused_temp_gpr(GPR::RAX); let value = if cmp == Location::GPR(GPR::R14) { if new == Location::GPR(GPR::R13) { GPR::R12 } else { GPR::R13 } } else { GPR::R14 }; a.emit_push(Size::S64, Location::GPR(value)); a.emit_mov(Size::S32, cmp, Location::GPR(compare)); a.emit_mov(Size::S32, new, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 1, |a, _m, addr| { a.emit_lock_cmpxchg( Size::S8, Location::GPR(value), Location::Memory(addr, 0), ); a.emit_movzx(Size::S8, Location::GPR(compare), Size::S32, ret); }, ); a.emit_pop(Size::S64, Location::GPR(value)); self.machine.release_temp_gpr(compare); } Operator::I32AtomicRmw16UCmpxchg { ref memarg } => { let new = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let cmp = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let compare = self.machine.reserve_unused_temp_gpr(GPR::RAX); let value = if cmp == Location::GPR(GPR::R14) { if new == Location::GPR(GPR::R13) { GPR::R12 } else { GPR::R13 } } else { GPR::R14 }; a.emit_push(Size::S64, Location::GPR(value)); a.emit_mov(Size::S32, cmp, Location::GPR(compare)); a.emit_mov(Size::S32, new, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 1, |a, _m, addr| { a.emit_lock_cmpxchg( Size::S16, Location::GPR(value), Location::Memory(addr, 0), ); a.emit_movzx(Size::S16, Location::GPR(compare), Size::S32, ret); }, ); a.emit_pop(Size::S64, Location::GPR(value)); self.machine.release_temp_gpr(compare); } Operator::I64AtomicRmw8UCmpxchg { ref memarg } => { let new = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let cmp = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let compare = self.machine.reserve_unused_temp_gpr(GPR::RAX); let value = if cmp == Location::GPR(GPR::R14) { if new == Location::GPR(GPR::R13) { GPR::R12 } else { GPR::R13 } } else { GPR::R14 }; a.emit_push(Size::S64, Location::GPR(value)); a.emit_mov(Size::S64, cmp, Location::GPR(compare)); a.emit_mov(Size::S64, new, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 1, |a, _m, addr| { a.emit_lock_cmpxchg( Size::S8, Location::GPR(value), Location::Memory(addr, 0), ); a.emit_movzx(Size::S8, Location::GPR(compare), Size::S64, ret); }, ); a.emit_pop(Size::S64, Location::GPR(value)); self.machine.release_temp_gpr(compare); } Operator::I64AtomicRmw16UCmpxchg { ref memarg } => { let new = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let cmp = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let compare = self.machine.reserve_unused_temp_gpr(GPR::RAX); let value = if cmp == Location::GPR(GPR::R14) { if new == Location::GPR(GPR::R13) { GPR::R12 } else { GPR::R13 } } else { GPR::R14 }; a.emit_push(Size::S64, Location::GPR(value)); a.emit_mov(Size::S64, cmp, Location::GPR(compare)); a.emit_mov(Size::S64, new, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 1, |a, _m, addr| { a.emit_lock_cmpxchg( Size::S16, Location::GPR(value), Location::Memory(addr, 0), ); a.emit_movzx(Size::S16, Location::GPR(compare), Size::S64, ret); }, ); a.emit_pop(Size::S64, Location::GPR(value)); self.machine.release_temp_gpr(compare); } Operator::I64AtomicRmw32UCmpxchg { ref memarg } => { let new = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let cmp = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let target = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], false, )[0]; self.value_stack.push(ret); let compare = self.machine.reserve_unused_temp_gpr(GPR::RAX); let value = if cmp == Location::GPR(GPR::R14) { if new == Location::GPR(GPR::R13) { GPR::R12 } else { GPR::R13 } } else { GPR::R14 }; a.emit_push(Size::S64, Location::GPR(value)); a.emit_mov(Size::S64, cmp, Location::GPR(compare)); a.emit_mov(Size::S64, new, Location::GPR(value)); Self::emit_memory_op( module_info, &self.config, a, &mut self.machine, target, memarg, true, 1, |a, _m, addr| { a.emit_lock_cmpxchg( Size::S32, Location::GPR(value), Location::Memory(addr, 0), ); a.emit_mov(Size::S32, Location::GPR(compare), ret); }, ); a.emit_pop(Size::S64, Location::GPR(value)); self.machine.release_temp_gpr(compare); } _ => { return Err(CodegenError { message: format!("not yet implemented: {:?}", op), }); } } Ok(()) } } fn type_to_wp_type(ty: Type) -> WpType { match ty { Type::I32 => WpType::I32, Type::I64 => WpType::I64, Type::F32 => WpType::F32, Type::F64 => WpType::F64, Type::V128 => WpType::V128, } } fn get_location_released(a: &mut Assembler, m: &mut Machine, loc: Location) -> Location { m.release_locations(a, &[loc]); loc } fn sort_call_movs(movs: &mut [(Location, GPR)]) { for i in 0..movs.len() { for j in (i + 1)..movs.len() { if let Location::GPR(src_gpr) = movs[j].0 { if src_gpr == movs[i].1 { movs.swap(i, j); } } } } /* { use std::collections::{HashMap, HashSet, VecDeque}; let mut mov_map: HashMap<GPR, HashSet<GPR>> = HashMap::new(); for mov in movs.iter() { if let Location::GPR(src_gpr) = mov.0 { if src_gpr != mov.1 { mov_map.entry(src_gpr).or_insert_with(|| HashSet::new()).insert(mov.1); } } } for (start, _) in mov_map.iter() { let mut q: VecDeque<GPR> = VecDeque::new(); let mut black: HashSet<GPR> = HashSet::new(); q.push_back(*start); black.insert(*start); while q.len() > 0 { let reg = q.pop_front().unwrap(); let empty_set = HashSet::new(); for x in mov_map.get(®).unwrap_or(&empty_set).iter() { if black.contains(x) { panic!("cycle detected"); } q.push_back(*x); black.insert(*x); } } } } */ }