Merge pull request #5365 from roc-lang/dev-backend-cli

Dev backend list tests
This commit is contained in:
Folkert de Vries 2023-05-07 14:47:39 +02:00 committed by GitHub
commit deb1e9952d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 1035 additions and 351 deletions

View file

@ -1245,6 +1245,24 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
};
cset_reg64_cond(buf, dst, cond);
}
fn mov_freg64_mem64_offset32(
_buf: &mut Vec<'_, u8>,
_dst: AArch64FloatReg,
_src: AArch64GeneralReg,
_offset: i32,
) {
todo!()
}
fn mov_freg32_mem32_offset32(
_buf: &mut Vec<'_, u8>,
_dst: AArch64FloatReg,
_src: AArch64GeneralReg,
_offset: i32,
) {
todo!()
}
}
impl AArch64Assembler {}

View file

@ -13,7 +13,8 @@ use roc_mono::ir::{
SelfRecursive, Stmt,
};
use roc_mono::layout::{
Builtin, InLayout, Layout, LayoutInterner, STLayoutInterner, TagIdIntType, UnionLayout,
Builtin, InLayout, Layout, LayoutIds, LayoutInterner, STLayoutInterner, TagIdIntType,
UnionLayout,
};
use roc_mono::low_level::HigherOrder;
use roc_target::TargetInfo;
@ -335,6 +336,19 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
);
fn mov_reg8_mem8_offset32(buf: &mut Vec<'_, u8>, dst: GeneralReg, src: GeneralReg, offset: i32);
fn mov_freg64_mem64_offset32(
buf: &mut Vec<'_, u8>,
dst: FloatReg,
src: GeneralReg,
offset: i32,
);
fn mov_freg32_mem32_offset32(
buf: &mut Vec<'_, u8>,
dst: FloatReg,
src: GeneralReg,
offset: i32,
);
// move from register to memory
fn mov_mem64_offset32_reg64(
buf: &mut Vec<'_, u8>,
@ -688,6 +702,9 @@ impl<
fn helper_proc_symbols(&self) -> &Vec<'a, (Symbol, ProcLayout<'a>)> {
&self.helper_proc_symbols
}
fn caller_procs(&self) -> &Vec<'a, CallerProc<'a>> {
&self.caller_procs
}
fn reset(&mut self, name: String, is_self_recursive: SelfRecursive) {
self.proc_name = Some(name);
@ -913,6 +930,7 @@ impl<
fn build_switch(
&mut self,
layout_ids: &mut LayoutIds<'a>,
cond_symbol: &Symbol,
_cond_layout: &InLayout<'a>, // cond_layout must be a integer due to potential jump table optimizations.
branches: &'a [(u64, BranchInfo<'a>, Stmt<'a>)],
@ -945,7 +963,7 @@ impl<
// Build all statements in this branch. Using storage as from before any branch.
self.storage_manager = base_storage.clone();
self.literal_map = base_literal_map.clone();
self.build_stmt(stmt, ret_layout);
self.build_stmt(layout_ids, stmt, ret_layout);
// Build unconditional jump to the end of this switch.
// Since we don't know the offset yet, set it to 0 and overwrite later.
@ -971,7 +989,7 @@ impl<
self.storage_manager
.update_stack_size(max_branch_stack_size);
let (_branch_info, stmt) = default_branch;
self.build_stmt(stmt, ret_layout);
self.build_stmt(layout_ids, stmt, ret_layout);
// Update all return jumps to jump past the default case.
let ret_offset = self.buf.len();
@ -987,6 +1005,7 @@ impl<
fn build_join(
&mut self,
layout_ids: &mut LayoutIds<'a>,
id: &JoinPointId,
parameters: &'a [Param<'a>],
body: &'a Stmt<'a>,
@ -1005,12 +1024,12 @@ impl<
self.join_map.insert(*id, bumpalo::vec![in self.env.arena]);
// Build remainder of function first. It is what gets run and jumps to join.
self.build_stmt(remainder, ret_layout);
self.build_stmt(layout_ids, remainder, ret_layout);
let join_location = self.buf.len() as u64;
// Build all statements in body.
self.build_stmt(body, ret_layout);
self.build_stmt(layout_ids, body, ret_layout);
// Overwrite the all jumps to the joinpoint with the correct offset.
let mut tmp = bumpalo::vec![in self.env.arena];
@ -1725,6 +1744,44 @@ impl<
)
}
fn build_indirect_inc(&mut self, layout: InLayout<'a>) -> Symbol {
let ident_ids = self
.interns
.all_ident_ids
.get_mut(&self.env.module_id)
.unwrap();
let (refcount_proc_name, linker_data) = self.helper_proc_gen.gen_refcount_proc(
ident_ids,
self.layout_interner,
layout,
HelperOp::IndirectInc,
);
self.helper_proc_symbols_mut().extend(linker_data);
refcount_proc_name
}
fn build_indirect_dec(&mut self, layout: InLayout<'a>) -> Symbol {
let ident_ids = self
.interns
.all_ident_ids
.get_mut(&self.env.module_id)
.unwrap();
let (refcount_proc_name, linker_data) = self.helper_proc_gen.gen_refcount_proc(
ident_ids,
self.layout_interner,
layout,
HelperOp::IndirectDec,
);
self.helper_proc_symbols_mut().extend(linker_data);
refcount_proc_name
}
fn build_higher_order_lowlevel(
&mut self,
dst: &Symbol,
@ -1737,13 +1794,6 @@ impl<
.get_mut(&self.env.module_id)
.unwrap();
let (inc_n_data_symbol, inc_n_data_linker_data) = self.helper_proc_gen.gen_refcount_proc(
ident_ids,
self.layout_interner,
Layout::UNIT,
HelperOp::Inc,
);
let caller_proc = CallerProc::new(
self.env.arena,
self.env.module_id,
@ -1772,17 +1822,6 @@ impl<
self.load_layout_stack_size(old_element_layout, old_element_width);
self.load_layout_stack_size(new_element_layout, new_element_width);
self.helper_proc_symbols.extend(inc_n_data_linker_data);
self.helper_proc_symbols
.extend([(caller_proc.proc_symbol, caller_proc.proc_layout)]);
let inc_n_data_string = self.function_symbol_to_string(
inc_n_data_symbol,
std::iter::empty(),
None,
Layout::UNIT,
);
let caller_string = self.function_symbol_to_string(
caller_proc.proc_symbol,
std::iter::empty(),
@ -1790,10 +1829,17 @@ impl<
Layout::UNIT,
);
// self.helper_proc_symbols .extend([(caller_proc.proc_symbol, caller_proc.proc_layout)]);
self.caller_procs.push(caller_proc);
let inc_n_data = Symbol::DEV_TMP;
self.build_fn_pointer(&inc_n_data, inc_n_data_string);
// function pointer to a function that takes a pointer, and increments
let inc_n_data = if let Some(closure_env_layout) = higher_order.closure_env_layout {
self.increment_fn_pointer(closure_env_layout)
} else {
// null pointer
self.load_literal_i64(&Symbol::DEV_TMP, 0);
Symbol::DEV_TMP
};
self.build_fn_pointer(&caller, caller_string);
@ -1817,10 +1863,14 @@ impl<
self.load_literal(&data, &Layout::U64, &Literal::Int(0u128.to_be_bytes()));
}
// we pass a null pointer when the data is not owned. the zig code must not call this!
let data_is_owned = higher_order.closure_env_layout.is_some()
&& higher_order.passed_function.owns_captured_environment;
self.load_literal(
&Symbol::DEV_TMP2,
&Layout::BOOL,
&Literal::Bool(higher_order.passed_function.owns_captured_environment),
&Literal::Bool(data_is_owned),
);
// list: RocList,
@ -2424,7 +2474,10 @@ impl<
// Refactor this and switch to one external match.
// We also could make loadining indivitual literals much faster
let element_symbol = match elem {
ListLiteralElement::Symbol(sym) => *sym,
ListLiteralElement::Symbol(sym) => {
self.load_literal_symbols(&[*sym]);
*sym
}
ListLiteralElement::Literal(lit) => {
self.load_literal(&Symbol::DEV_TMP, element_in_layout, lit);
Symbol::DEV_TMP
@ -3218,6 +3271,11 @@ impl<
let mut copied = 0;
let size = stack_size as i32;
if size == 0 {
storage_manager.no_data(&dst);
return;
}
let base_offset = storage_manager.claim_stack_area(&dst, stack_size);
if size - copied >= 8 {
@ -3289,9 +3347,13 @@ impl<
ASM::mov_reg8_mem8_offset32(buf, dst_reg, ptr_reg, 0);
}
},
Builtin::Float(_) => {
Builtin::Float(FloatWidth::F64) => {
let dst_reg = storage_manager.claim_float_reg(buf, &dst);
ASM::mov_freg64_freg64(buf, dst_reg, CC::FLOAT_RETURN_REGS[0]);
ASM::mov_freg64_mem64_offset32(buf, dst_reg, ptr_reg, 0);
}
Builtin::Float(FloatWidth::F32) => {
let dst_reg = storage_manager.claim_float_reg(buf, &dst);
ASM::mov_freg32_mem32_offset32(buf, dst_reg, ptr_reg, 0);
}
Builtin::Bool => {
// the same as an 8-bit integer
@ -3332,6 +3394,17 @@ impl<
});
}
Layout::LambdaSet(lambda_set) => {
Self::ptr_read(
buf,
storage_manager,
layout_interner,
ptr_reg,
lambda_set.runtime_representation(),
dst,
);
}
_ => todo!("unboxing of {:?}", layout_interner.dbg(element_in_layout)),
}
}

View file

@ -252,7 +252,11 @@ impl<
/// Claims a general reg for a specific symbol.
/// They symbol should not already have storage.
pub fn claim_general_reg(&mut self, buf: &mut Vec<'a, u8>, sym: &Symbol) -> GeneralReg {
debug_assert_eq!(self.symbol_storage_map.get(sym), None);
debug_assert_eq!(
self.symbol_storage_map.get(sym),
None,
"Symbol {sym:?} is already in the storage map!"
);
let reg = self.get_general_reg(buf);
self.general_used_regs.push((reg, *sym));
self.symbol_storage_map.insert(*sym, Reg(General(reg)));
@ -542,13 +546,20 @@ impl<
field_layouts: &'a [InLayout<'a>],
) {
debug_assert!(index < field_layouts.len() as u64);
let storage = *self.get_storage_for_sym(structure);
if let NoData = storage {
return self.no_data(sym);
}
// This must be removed and reinserted for ownership and mutability reasons.
let owned_data = self.remove_allocation_for_sym(structure);
self.allocation_map
.insert(*structure, Rc::clone(&owned_data));
match self.get_storage_for_sym(structure) {
match storage {
Stack(Complex { base_offset, size }) => {
let (base_offset, size) = (*base_offset, *size);
let mut data_offset = base_offset;
for layout in field_layouts.iter().take(index as usize) {
let field_size = layout_interner.stack_size(*layout);
@ -829,8 +840,6 @@ impl<
Builtin::Decimal => todo!(),
Builtin::Str | Builtin::List(_) => {
let (from_offset, size) = self.stack_offset_and_size(sym);
debug_assert_eq!(from_offset % 8, 0);
debug_assert_eq!(size % 8, 0);
debug_assert_eq!(size, layout_interner.stack_size(*layout));
self.copy_to_stack_offset(buf, size, from_offset, to_offset)
}
@ -873,6 +882,14 @@ impl<
let size = size as i32;
self.with_tmp_general_reg(buf, |_storage_manager, buf, reg| {
// on targets beside x86, misaligned copies might be a problem
for _ in 0..size % 8 {
ASM::mov_reg8_base32(buf, reg, from_offset + copied);
ASM::mov_base32_reg8(buf, to_offset + copied, reg);
copied += 1;
}
if size - copied >= 8 {
for _ in (0..(size - copied)).step_by(8) {
ASM::mov_reg64_base32(buf, reg, from_offset + copied);
@ -1067,6 +1084,7 @@ impl<
}
| Complex { base_offset, size },
) => (*base_offset, *size),
NoData => (0, 0),
storage => {
internal_error!(
"Data not on the stack for sym {:?} with storage {:?}",
@ -1134,6 +1152,44 @@ impl<
self.fn_call_stack_size = max(self.fn_call_stack_size, tmp_size);
}
fn joinpoint_argument_stack_storage(
&mut self,
layout_interner: &mut STLayoutInterner<'a>,
symbol: Symbol,
layout: InLayout<'a>,
) {
match layout {
single_register_layouts!() => {
let base_offset = self.claim_stack_size(8);
self.symbol_storage_map.insert(
symbol,
Stack(Primitive {
base_offset,
reg: None,
}),
);
self.allocation_map
.insert(symbol, Rc::new((base_offset, 8)));
}
_ => {
if let Layout::LambdaSet(lambda_set) = layout_interner.get(layout) {
self.joinpoint_argument_stack_storage(
layout_interner,
symbol,
lambda_set.runtime_representation(),
)
} else {
let stack_size = layout_interner.stack_size(layout);
if stack_size == 0 {
self.no_data(&symbol);
} else {
self.claim_stack_area(&symbol, stack_size);
}
}
}
}
}
/// Setups a join point.
/// To do this, each of the join pionts params are given a storage location.
/// Then those locations are stored.
@ -1155,33 +1211,49 @@ impl<
{
// Claim a location for every join point parameter to be loaded at.
// Put everything on the stack for simplicity.
match *layout {
single_register_layouts!() => {
let base_offset = self.claim_stack_size(8);
self.symbol_storage_map.insert(
*symbol,
Stack(Primitive {
base_offset,
reg: None,
}),
);
self.allocation_map
.insert(*symbol, Rc::new((base_offset, 8)));
}
_ => {
let stack_size = layout_interner.stack_size(*layout);
if stack_size == 0 {
self.symbol_storage_map.insert(*symbol, NoData);
} else {
self.claim_stack_area(symbol, stack_size);
}
}
}
self.joinpoint_argument_stack_storage(layout_interner, *symbol, *layout);
param_storage.push(*self.get_storage_for_sym(symbol));
}
self.join_param_map.insert(*id, param_storage);
}
fn jump_argument_stack_storage(
&mut self,
layout_interner: &mut STLayoutInterner<'a>,
buf: &mut Vec<'a, u8>,
symbol: Symbol,
layout: InLayout<'a>,
base_offset: i32,
) {
match layout {
single_register_integers!() => {
let reg = self.load_to_general_reg(buf, &symbol);
ASM::mov_base32_reg64(buf, base_offset, reg);
}
single_register_floats!() => {
let reg = self.load_to_float_reg(buf, &symbol);
ASM::mov_base32_freg64(buf, base_offset, reg);
}
_ => {
if let Layout::LambdaSet(lambda_set) = layout_interner.get(layout) {
self.jump_argument_stack_storage(
layout_interner,
buf,
symbol,
lambda_set.runtime_representation(),
base_offset,
);
} else {
internal_error!(
r"cannot load non-primitive layout ({:?}) to primitive stack location",
layout
)
}
}
}
}
/// Setup jump loads the parameters for the joinpoint.
/// This enables the jump to correctly passe arguments to the joinpoint.
pub fn setup_jump(
@ -1225,22 +1297,15 @@ impl<
Stack(Primitive {
base_offset,
reg: None,
}) => match *layout {
single_register_integers!() => {
let reg = self.load_to_general_reg(buf, sym);
ASM::mov_base32_reg64(buf, *base_offset, reg);
}
single_register_floats!() => {
let reg = self.load_to_float_reg(buf, sym);
ASM::mov_base32_freg64(buf, *base_offset, reg);
}
_ => {
internal_error!(
"cannot load non-primitive layout ({:?}) to primitive stack location",
layout
);
}
},
}) => {
self.jump_argument_stack_storage(
layout_interner,
buf,
*sym,
*layout,
*base_offset,
);
}
NoData => {}
Stack(Primitive { reg: Some(_), .. }) => {
internal_error!(

View file

@ -598,22 +598,20 @@ impl X64_64SystemVStoreArgs {
storage_manager: &mut X86_64StorageManager<'a, '_, X86_64SystemV>,
sym: Symbol,
) {
if self.general_i < Self::GENERAL_PARAM_REGS.len() {
storage_manager.load_to_specified_general_reg(
buf,
&sym,
Self::GENERAL_PARAM_REGS[self.general_i],
);
self.general_i += 1;
} else {
// Copy to stack using return reg as buffer.
storage_manager.load_to_specified_general_reg(buf, &sym, Self::GENERAL_RETURN_REGS[0]);
X86_64Assembler::mov_stack32_reg64(
buf,
self.tmp_stack_offset,
Self::GENERAL_RETURN_REGS[0],
);
self.tmp_stack_offset += 8;
match Self::GENERAL_PARAM_REGS.get(self.general_i) {
Some(reg) => {
storage_manager.load_to_specified_general_reg(buf, &sym, *reg);
self.general_i += 1;
}
None => {
// Copy to stack using return reg as buffer.
let tmp = Self::GENERAL_RETURN_REGS[0];
storage_manager.load_to_specified_general_reg(buf, &sym, tmp);
X86_64Assembler::mov_stack32_reg64(buf, self.tmp_stack_offset, tmp);
self.tmp_stack_offset += 8;
}
}
}
@ -623,22 +621,20 @@ impl X64_64SystemVStoreArgs {
storage_manager: &mut X86_64StorageManager<'a, '_, X86_64SystemV>,
sym: Symbol,
) {
if self.float_i < Self::FLOAT_PARAM_REGS.len() {
storage_manager.load_to_specified_float_reg(
buf,
&sym,
Self::FLOAT_PARAM_REGS[self.float_i],
);
self.float_i += 1;
} else {
// Copy to stack using return reg as buffer.
storage_manager.load_to_specified_float_reg(buf, &sym, Self::FLOAT_RETURN_REGS[0]);
X86_64Assembler::mov_stack32_freg64(
buf,
self.tmp_stack_offset,
Self::FLOAT_RETURN_REGS[0],
);
self.tmp_stack_offset += 8;
match Self::FLOAT_PARAM_REGS.get(self.float_i) {
Some(reg) => {
storage_manager.load_to_specified_float_reg(buf, &sym, *reg);
self.float_i += 1;
}
None => {
// Copy to stack using return reg as buffer.
let tmp = Self::FLOAT_RETURN_REGS[0];
storage_manager.load_to_specified_float_reg(buf, &sym, tmp);
X86_64Assembler::mov_stack32_freg64(buf, self.tmp_stack_offset, tmp);
self.tmp_stack_offset += 8;
}
}
}
}
@ -724,8 +720,8 @@ impl X64_64SystemVLoadArgs {
storage_manager: &mut X86_64StorageManager<'_, '_, X86_64SystemV>,
sym: Symbol,
) {
if self.general_i < X86_64SystemV::GENERAL_PARAM_REGS.len() {
let reg = X86_64SystemV::FLOAT_PARAM_REGS[self.general_i];
if self.float_i < X86_64SystemV::FLOAT_PARAM_REGS.len() {
let reg = X86_64SystemV::FLOAT_PARAM_REGS[self.float_i];
storage_manager.float_reg_arg(&sym, reg);
self.float_i += 1;
} else {
@ -884,37 +880,45 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Windo
ret_layout: &InLayout<'a>,
) {
let mut arg_offset = Self::SHADOW_SPACE_SIZE as i32 + 16; // 16 is the size of the pushed return address and base pointer.
let mut i = 0;
let mut general_registers_used = 0;
let mut float_registers_used = 0;
if X86_64WindowsFastcall::returns_via_arg_pointer(layout_interner, ret_layout) {
storage_manager.ret_pointer_arg(Self::GENERAL_PARAM_REGS[i]);
i += 1;
storage_manager.ret_pointer_arg(Self::GENERAL_PARAM_REGS[0]);
general_registers_used += 1;
}
for (layout, sym) in args.iter() {
if i < Self::GENERAL_PARAM_REGS.len() {
match *layout {
single_register_integers!() => {
storage_manager.general_reg_arg(sym, Self::GENERAL_PARAM_REGS[i]);
i += 1;
}
single_register_floats!() => {
storage_manager.float_reg_arg(sym, Self::FLOAT_PARAM_REGS[i]);
i += 1;
}
x if layout_interner.stack_size(x) == 0 => {}
x => {
todo!("Loading args with layout {:?}", x);
match *layout {
single_register_integers!() => {
match Self::GENERAL_PARAM_REGS.get(general_registers_used) {
Some(reg) => {
storage_manager.general_reg_arg(sym, *reg);
general_registers_used += 1;
}
None => {
storage_manager.primitive_stack_arg(sym, arg_offset);
arg_offset += 8;
}
}
}
} else {
match *layout {
single_register_layouts!() => {
storage_manager.primitive_stack_arg(sym, arg_offset);
arg_offset += 8;
single_register_floats!() => {
match Self::FLOAT_PARAM_REGS.get(float_registers_used) {
Some(reg) => {
storage_manager.float_reg_arg(sym, *reg);
float_registers_used += 1;
}
None => {
storage_manager.primitive_stack_arg(sym, arg_offset);
arg_offset += 8;
}
}
x => {
todo!("Loading args with layout {:?}", x);
}
};
}
x if layout_interner.stack_size(x) == 0 => {}
x => {
todo!("Loading args with layout {:?}", x);
}
}
}
}
@ -942,50 +946,44 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Windo
storage_manager.claim_stack_area(dst, layout_interner.stack_size(*ret_layout));
todo!("claim first parama reg for the address");
}
for (i, (sym, layout)) in args.iter().zip(arg_layouts.iter()).enumerate() {
let mut general_registers_used = 0;
let mut float_registers_used = 0;
for (sym, layout) in args.iter().zip(arg_layouts.iter()) {
match *layout {
single_register_integers!() => {
if i < Self::GENERAL_PARAM_REGS.len() {
storage_manager.load_to_specified_general_reg(
buf,
sym,
Self::GENERAL_PARAM_REGS[i],
);
} else {
// Copy to stack using return reg as buffer.
storage_manager.load_to_specified_general_reg(
buf,
sym,
Self::GENERAL_RETURN_REGS[0],
);
X86_64Assembler::mov_stack32_reg64(
buf,
tmp_stack_offset,
Self::GENERAL_RETURN_REGS[0],
);
tmp_stack_offset += 8;
match Self::GENERAL_PARAM_REGS.get(general_registers_used) {
Some(reg) => {
storage_manager.load_to_specified_general_reg(buf, sym, *reg);
general_registers_used += 1;
}
None => {
// Copy to stack using return reg as buffer.
let tmp = Self::GENERAL_RETURN_REGS[0];
storage_manager.load_to_specified_general_reg(buf, sym, tmp);
X86_64Assembler::mov_stack32_reg64(buf, tmp_stack_offset, tmp);
tmp_stack_offset += 8;
}
}
}
single_register_floats!() => {
if i < Self::FLOAT_PARAM_REGS.len() {
storage_manager.load_to_specified_float_reg(
buf,
sym,
Self::FLOAT_PARAM_REGS[i],
);
} else {
// Copy to stack using return reg as buffer.
storage_manager.load_to_specified_float_reg(
buf,
sym,
Self::FLOAT_RETURN_REGS[0],
);
X86_64Assembler::mov_stack32_freg64(
buf,
tmp_stack_offset,
Self::FLOAT_RETURN_REGS[0],
);
tmp_stack_offset += 8;
match Self::FLOAT_PARAM_REGS.get(float_registers_used) {
Some(reg) => {
storage_manager.load_to_specified_float_reg(buf, sym, *reg);
float_registers_used += 1;
}
None => {
// Copy to stack using return reg as buffer.
let tmp = Self::FLOAT_RETURN_REGS[0];
storage_manager.load_to_specified_float_reg(buf, sym, tmp);
X86_64Assembler::mov_stack32_freg64(buf, tmp_stack_offset, tmp);
tmp_stack_offset += 8;
}
}
}
x if layout_interner.stack_size(x) == 0 => {}
@ -1512,6 +1510,26 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
raw_movsx_reg_reg(buf, input_width, dst, src);
}
#[inline(always)]
fn mov_freg64_mem64_offset32(
buf: &mut Vec<'_, u8>,
dst: X86_64FloatReg,
src: X86_64GeneralReg,
offset: i32,
) {
movsd_freg64_base64_offset32(buf, dst, src, offset)
}
#[inline(always)]
fn mov_freg32_mem32_offset32(
buf: &mut Vec<'_, u8>,
dst: X86_64FloatReg,
src: X86_64GeneralReg,
offset: i32,
) {
movss_freg32_base32_offset32(buf, dst, src, offset)
}
#[inline(always)]
fn mov_freg64_base32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, offset: i32) {
movsd_freg64_base64_offset32(buf, dst, X86_64GeneralReg::RBP, offset)
@ -3041,7 +3059,7 @@ fn movsd_freg64_rip_offset32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, offset:
buf.extend(offset.to_le_bytes());
}
/// `MOVSD r/m64,xmm1` -> Move xmm1 to r/m64. where m64 references the base pointer.
// `MOVSD r/m64,xmm1` -> Move xmm1 to r/m64. where m64 references the base pointer.
#[inline(always)]
fn movsd_base64_offset32_freg64(
buf: &mut Vec<'_, u8>,
@ -3091,6 +3109,31 @@ fn movsd_freg64_base64_offset32(
buf.extend(offset.to_le_bytes());
}
/// `MOVSS xmm1,r/m32` -> Move r/m32 to xmm1. where m64 references the base pointer.
#[inline(always)]
fn movss_freg32_base32_offset32(
buf: &mut Vec<'_, u8>,
dst: X86_64FloatReg,
base: X86_64GeneralReg,
offset: i32,
) {
let rex = add_rm_extension(base, REX_W);
let rex = add_reg_extension(dst, rex);
let dst_mod = (dst as u8 % 8) << 3;
let base_mod = base as u8 % 8;
buf.reserve(10);
buf.push(0xF3);
if dst as u8 > 7 || base as u8 > 7 {
buf.push(rex);
}
buf.extend([0x0F, 0x10, 0x80 | dst_mod | base_mod]);
// Using RSP or R12 requires a secondary index byte.
if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 {
buf.push(0x24);
}
buf.extend(offset.to_le_bytes());
}
/// `NEG r/m64` -> Two's complement negate r/m64.
#[inline(always)]
fn neg_reg64(buf: &mut Vec<'_, u8>, reg: X86_64GeneralReg) {
@ -3779,6 +3822,17 @@ mod tests {
);
}
#[test]
fn test_movss_freg32_base32_offset32() {
disassembler_test!(
movss_freg32_base32_offset32,
|reg1, reg2, imm| format!("movss {}, dword ptr [{} + 0x{:x}]", reg1, reg2, imm),
ALL_FLOAT_REGS,
ALL_GENERAL_REGS,
[TEST_I32]
);
}
#[test]
fn test_movsd_base64_offset32_freg64() {
disassembler_test!(