mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 07:41:12 +00:00
Merge pull request #5365 from roc-lang/dev-backend-cli
Dev backend list tests
This commit is contained in:
commit
deb1e9952d
13 changed files with 1035 additions and 351 deletions
|
@ -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 {}
|
||||
|
|
|
@ -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)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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!(
|
||||
|
|
|
@ -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!(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue