mirror of
https://github.com/fluencelabs/wasmer
synced 2025-03-26 04:31:04 +00:00
7241 lines
273 KiB
Rust
7241 lines
273 KiB
Rust
#![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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
}
|