move storing args to use storage manager

This commit is contained in:
Brendan Hansknecht 2022-02-17 22:41:34 -08:00
parent d578dae3ef
commit 9fa420f871
4 changed files with 141 additions and 244 deletions

View file

@ -1,8 +1,7 @@
use crate::generic64::{storage::StorageManager, Assembler, CallConv, RegTrait, SymbolStorage};
use crate::generic64::{storage::StorageManager, Assembler, CallConv, RegTrait};
use crate::Relocation;
use bumpalo::collections::Vec;
use packed_struct::prelude::*;
use roc_collections::all::MutMap;
use roc_error_macros::internal_error;
use roc_module::symbol::Symbol;
use roc_mono::layout::Layout;
@ -265,11 +264,17 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg, AArch64Assembler> for AArch64C
#[inline(always)]
fn store_args<'a>(
_buf: &mut Vec<'a, u8>,
_symbol_map: &MutMap<Symbol, SymbolStorage<AArch64GeneralReg, AArch64FloatReg>>,
_storage_manager: &mut StorageManager<
'a,
AArch64GeneralReg,
AArch64FloatReg,
AArch64Assembler,
AArch64Call,
>,
_args: &'a [Symbol],
_arg_layouts: &[Layout<'a>],
_ret_layout: &Layout<'a>,
) -> u32 {
) {
todo!("Storing args for AArch64");
}

View file

@ -82,12 +82,12 @@ pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait, ASM: Assembler<Gene
// It returns the amount of stack space needed to temporarily store the args.
fn store_args<'a>(
buf: &mut Vec<'a, u8>,
symbol_map: &MutMap<Symbol, SymbolStorage<GeneralReg, FloatReg>>,
storage_manager: &mut StorageManager<'a, GeneralReg, FloatReg, ASM, Self>,
args: &'a [Symbol],
arg_layouts: &[Layout<'a>],
// ret_layout is needed because if it is a complex type, we pass a pointer as the first arg.
ret_layout: &Layout<'a>,
) -> u32;
);
/// return_complex_symbol returns the specified complex/non-primative symbol.
/// It uses the layout to determine how the data should be returned.
@ -553,14 +553,13 @@ impl<
.push_used_caller_saved_regs_to_stack(&mut self.buf);
// Put values in param regs or on top of the stack.
let tmp_stack_size = CC::store_args(
CC::store_args(
&mut self.buf,
&self.symbol_storage_map,
&mut self.storage_manager,
args,
arg_layouts,
ret_layout,
);
self.fn_call_stack_size = std::cmp::max(self.fn_call_stack_size, tmp_stack_size);
// Call function and generate reloc.
ASM::call(&mut self.buf, &mut self.relocs, fn_name);
@ -754,14 +753,13 @@ impl<
self.storage_manager
.push_used_caller_saved_regs_to_stack(&mut self.buf);
let tmp_stack_size = CC::store_args(
CC::store_args(
&mut self.buf,
&self.symbol_storage_map,
&mut self.storage_manager,
args,
arg_layouts,
ret_layout,
);
self.fn_call_stack_size = std::cmp::max(self.fn_call_stack_size, tmp_stack_size);
let jmp_location = self.buf.len();
let start_offset = ASM::jmp_imm32(&mut self.buf, 0x1234_5678);

View file

@ -9,6 +9,7 @@ use roc_error_macros::internal_error;
use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout};
use roc_target::TargetInfo;
use std::cmp::max;
use std::marker::PhantomData;
use std::rc::Rc;
@ -105,6 +106,9 @@ pub struct StorageManager<
free_stack_chunks: Vec<'a, (i32, u32)>,
stack_size: u32,
// The amount of extra stack space needed to pass args for function calling.
fn_call_stack_size: u32,
}
pub fn new_storage_manager<
@ -132,6 +136,7 @@ pub fn new_storage_manager<
float_used_callee_saved_regs: MutSet::default(),
free_stack_chunks: bumpalo::vec![in env.arena],
stack_size: 0,
fn_call_stack_size: 0,
}
}
@ -158,6 +163,7 @@ impl<
.extend_from_slice(CC::FLOAT_DEFAULT_FREE_REGS);
self.free_stack_chunks.clear();
self.stack_size = 0;
self.fn_call_stack_size = 0;
}
// Returns true if the symbol is storing a primitive value.
@ -734,6 +740,11 @@ impl<
.insert(Symbol::RET_POINTER, Reg(General(reg)));
}
// updates the function call stack size to the max of its current value and the size need for this call.
pub fn update_fn_call_stack_size(&mut self, tmp_size: u32) {
self.fn_call_stack_size = max(self.fn_call_stack_size, tmp_size);
}
/// claim_stack_area is the public wrapper around claim_stack_size.
/// It also deals with updating symbol storage.
/// It returns the base offset of the stack area.

View file

@ -1,10 +1,9 @@
use crate::generic64::{storage::StorageManager, Assembler, CallConv, RegTrait, SymbolStorage};
use crate::generic64::{storage::StorageManager, Assembler, CallConv, RegTrait};
use crate::{
single_register_floats, single_register_integers, single_register_layouts, Relocation,
};
use bumpalo::collections::Vec;
use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_collections::all::MutMap;
use roc_error_macros::internal_error;
use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout};
@ -255,150 +254,88 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Syste
#[inline(always)]
fn store_args<'a>(
buf: &mut Vec<'a, u8>,
symbol_map: &MutMap<Symbol, SymbolStorage<X86_64GeneralReg, X86_64FloatReg>>,
storage_manager: &mut StorageManager<
'a,
X86_64GeneralReg,
X86_64FloatReg,
X86_64Assembler,
X86_64SystemV,
>,
args: &'a [Symbol],
arg_layouts: &[Layout<'a>],
ret_layout: &Layout<'a>,
) -> u32 {
let mut stack_offset = Self::SHADOW_SPACE_SIZE as i32;
) {
let mut tmp_stack_offset = Self::SHADOW_SPACE_SIZE as i32;
if Self::returns_via_arg_pointer(ret_layout) {
// Save space on the stack for the arg we will return.
storage_manager
.claim_stack_area(&Symbol::RET_POINTER, ret_layout.stack_size(TARGET_INFO));
todo!("claim first parama reg for the address");
}
let mut general_i = 0;
let mut float_i = 0;
// For most return layouts we will do nothing.
// In some cases, we need to put the return address as the first arg.
match ret_layout {
single_register_layouts!() | Layout::Builtin(Builtin::Str) | Layout::Struct([]) => {
// Nothing needs to be done for any of these cases.
}
x => {
todo!("receiving return type, {:?}", x);
}
}
for (i, layout) in arg_layouts.iter().enumerate() {
for (i, (sym, layout)) in args.iter().zip(arg_layouts.iter()).enumerate() {
match layout {
single_register_integers!() => {
let storage = match symbol_map.get(&args[i]) {
Some(storage) => storage,
None => {
internal_error!("function argument does not reference any symbol")
}
};
if general_i < Self::GENERAL_PARAM_REGS.len() {
// Load the value to the param reg.
let dst = Self::GENERAL_PARAM_REGS[general_i];
match storage {
SymbolStorage::GeneralReg(reg)
| SymbolStorage::BaseAndGeneralReg { reg, .. } => {
X86_64Assembler::mov_reg64_reg64(buf, dst, *reg);
}
SymbolStorage::Base { offset, .. } => {
X86_64Assembler::mov_reg64_base32(buf, dst, *offset);
}
SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg { .. } => {
internal_error!("Cannot load floating point symbol into GeneralReg")
}
}
storage_manager.load_to_specified_general_reg(
buf,
sym,
Self::GENERAL_PARAM_REGS[i],
);
general_i += 1;
} else {
// Load the value to the stack.
match storage {
SymbolStorage::GeneralReg(reg)
| SymbolStorage::BaseAndGeneralReg { reg, .. } => {
X86_64Assembler::mov_stack32_reg64(buf, stack_offset, *reg);
}
SymbolStorage::Base { offset, .. } => {
// Use RAX as a tmp reg because it will be free before function calls.
X86_64Assembler::mov_reg64_base32(
// Copy to stack using return reg as buffer.
storage_manager.load_to_specified_general_reg(
buf,
X86_64GeneralReg::RAX,
*offset,
sym,
Self::GENERAL_RETURN_REGS[0],
);
X86_64Assembler::mov_stack32_reg64(
buf,
stack_offset,
X86_64GeneralReg::RAX,
tmp_stack_offset,
Self::GENERAL_RETURN_REGS[0],
);
}
SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg { .. } => {
internal_error!("Cannot load floating point symbol into GeneralReg")
}
}
stack_offset += 8;
tmp_stack_offset += 8;
}
}
single_register_floats!() => {
let storage = match symbol_map.get(&args[i]) {
Some(storage) => storage,
None => {
internal_error!("function argument does not reference any symbol")
}
};
if float_i < Self::FLOAT_PARAM_REGS.len() {
// Load the value to the param reg.
let dst = Self::FLOAT_PARAM_REGS[float_i];
match storage {
SymbolStorage::FloatReg(reg)
| SymbolStorage::BaseAndFloatReg { reg, .. } => {
X86_64Assembler::mov_freg64_freg64(buf, dst, *reg);
}
SymbolStorage::Base { offset, .. } => {
X86_64Assembler::mov_freg64_base32(buf, dst, *offset);
}
SymbolStorage::GeneralReg(_)
| SymbolStorage::BaseAndGeneralReg { .. } => {
internal_error!("Cannot load general symbol into FloatReg")
}
}
storage_manager.load_to_specified_float_reg(
buf,
sym,
Self::FLOAT_PARAM_REGS[i],
);
float_i += 1;
} else {
// Load the value to the stack.
match storage {
SymbolStorage::FloatReg(reg)
| SymbolStorage::BaseAndFloatReg { reg, .. } => {
X86_64Assembler::mov_stack32_freg64(buf, stack_offset, *reg);
}
SymbolStorage::Base { offset, .. } => {
// Use XMM0 as a tmp reg because it will be free before function calls.
X86_64Assembler::mov_freg64_base32(
// Copy to stack using return reg as buffer.
storage_manager.load_to_specified_float_reg(
buf,
X86_64FloatReg::XMM0,
*offset,
sym,
Self::FLOAT_RETURN_REGS[0],
);
X86_64Assembler::mov_stack32_freg64(
buf,
stack_offset,
X86_64FloatReg::XMM0,
tmp_stack_offset,
Self::FLOAT_RETURN_REGS[0],
);
}
SymbolStorage::GeneralReg(_)
| SymbolStorage::BaseAndGeneralReg { .. } => {
internal_error!("Cannot load general symbol into FloatReg")
}
}
stack_offset += 8;
tmp_stack_offset += 8;
}
}
Layout::Builtin(Builtin::Str) => {
let storage = match symbol_map.get(&args[i]) {
Some(storage) => storage,
None => {
internal_error!("function argument does not reference any symbol")
}
};
if general_i + 1 < Self::GENERAL_PARAM_REGS.len() {
// Load the value to the param reg.
let dst1 = Self::GENERAL_PARAM_REGS[general_i];
let dst2 = Self::GENERAL_PARAM_REGS[general_i + 1];
match storage {
SymbolStorage::Base { offset, .. } => {
X86_64Assembler::mov_reg64_base32(buf, dst1, *offset);
X86_64Assembler::mov_reg64_base32(buf, dst2, *offset + 8);
}
_ => {
internal_error!(
"Strings only support being loaded from base offsets"
let (base_offset, _size) = storage_manager.stack_offset_and_size(sym);
debug_assert_eq!(base_offset % 8, 0);
X86_64Assembler::mov_reg64_base32(
buf,
Self::GENERAL_PARAM_REGS[general_i],
base_offset,
);
X86_64Assembler::mov_reg64_base32(
buf,
Self::GENERAL_PARAM_REGS[general_i + 1],
base_offset + 8,
);
}
}
general_i += 2;
} else {
todo!("calling functions with strings on the stack");
@ -410,7 +347,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Syste
}
}
}
stack_offset as u32
storage_manager.update_fn_call_stack_size(tmp_stack_offset as u32);
}
fn return_complex_symbol<'a>(
@ -431,7 +368,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Syste
}
Layout::Struct([]) => {}
Layout::Builtin(Builtin::Str | Builtin::List(_)) => {
let (base_offset, size) = storage_manager.stack_offset_and_size(sym);
let (base_offset, _size) = storage_manager.stack_offset_and_size(sym);
debug_assert_eq!(base_offset % 8, 0);
X86_64Assembler::mov_reg64_base32(buf, Self::GENERAL_RETURN_REGS[0], base_offset);
X86_64Assembler::mov_reg64_base32(
@ -650,122 +587,68 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Windo
#[inline(always)]
fn store_args<'a>(
buf: &mut Vec<'a, u8>,
symbol_map: &MutMap<Symbol, SymbolStorage<X86_64GeneralReg, X86_64FloatReg>>,
storage_manager: &mut StorageManager<
'a,
X86_64GeneralReg,
X86_64FloatReg,
X86_64Assembler,
X86_64WindowsFastcall,
>,
args: &'a [Symbol],
arg_layouts: &[Layout<'a>],
ret_layout: &Layout<'a>,
) -> u32 {
let mut stack_offset = Self::SHADOW_SPACE_SIZE as i32;
// For most return layouts we will do nothing.
// In some cases, we need to put the return address as the first arg.
match ret_layout {
single_register_layouts!() | Layout::Struct([]) => {
// Nothing needs to be done for any of these cases.
) {
let mut tmp_stack_offset = Self::SHADOW_SPACE_SIZE as i32;
if Self::returns_via_arg_pointer(ret_layout) {
// Save space on the stack for the arg we will return.
storage_manager
.claim_stack_area(&Symbol::RET_POINTER, ret_layout.stack_size(TARGET_INFO));
todo!("claim first parama reg for the address");
}
x => {
todo!("receiving return type, {:?}", x);
}
}
for (i, layout) in arg_layouts.iter().enumerate() {
for (i, (sym, layout)) in args.iter().zip(arg_layouts.iter()).enumerate() {
match layout {
single_register_integers!() => {
let storage = match symbol_map.get(&args[i]) {
Some(storage) => storage,
None => {
internal_error!("function argument does not reference any symbol")
}
};
if i < Self::GENERAL_PARAM_REGS.len() {
// Load the value to the param reg.
let dst = Self::GENERAL_PARAM_REGS[i];
match storage {
SymbolStorage::GeneralReg(reg)
| SymbolStorage::BaseAndGeneralReg { reg, .. } => {
X86_64Assembler::mov_reg64_reg64(buf, dst, *reg);
}
SymbolStorage::Base { offset, .. } => {
X86_64Assembler::mov_reg64_base32(buf, dst, *offset);
}
SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg { .. } => {
internal_error!("Cannot load floating point symbol into GeneralReg")
}
}
} else {
// Load the value to the stack.
match storage {
SymbolStorage::GeneralReg(reg)
| SymbolStorage::BaseAndGeneralReg { reg, .. } => {
X86_64Assembler::mov_stack32_reg64(buf, stack_offset, *reg);
}
SymbolStorage::Base { offset, .. } => {
// Use RAX as a tmp reg because it will be free before function calls.
X86_64Assembler::mov_reg64_base32(
storage_manager.load_to_specified_general_reg(
buf,
X86_64GeneralReg::RAX,
*offset,
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,
stack_offset,
X86_64GeneralReg::RAX,
tmp_stack_offset,
Self::GENERAL_RETURN_REGS[0],
);
}
SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg { .. } => {
internal_error!("Cannot load floating point symbol into GeneralReg")
}
}
stack_offset += 8;
tmp_stack_offset += 8;
}
}
single_register_floats!() => {
let storage = match symbol_map.get(&args[i]) {
Some(storage) => storage,
None => {
internal_error!("function argument does not reference any symbol")
}
};
if i < Self::FLOAT_PARAM_REGS.len() {
// Load the value to the param reg.
let dst = Self::FLOAT_PARAM_REGS[i];
match storage {
SymbolStorage::FloatReg(reg)
| SymbolStorage::BaseAndFloatReg { reg, .. } => {
X86_64Assembler::mov_freg64_freg64(buf, dst, *reg);
}
SymbolStorage::Base { offset, .. } => {
X86_64Assembler::mov_freg64_base32(buf, dst, *offset);
}
SymbolStorage::GeneralReg(_)
| SymbolStorage::BaseAndGeneralReg { .. } => {
internal_error!("Cannot load general symbol into FloatReg")
}
}
} else {
// Load the value to the stack.
match storage {
SymbolStorage::FloatReg(reg)
| SymbolStorage::BaseAndFloatReg { reg, .. } => {
X86_64Assembler::mov_stack32_freg64(buf, stack_offset, *reg);
}
SymbolStorage::Base { offset, .. } => {
// Use XMM0 as a tmp reg because it will be free before function calls.
X86_64Assembler::mov_freg64_base32(
storage_manager.load_to_specified_float_reg(
buf,
X86_64FloatReg::XMM0,
*offset,
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,
stack_offset,
X86_64FloatReg::XMM0,
tmp_stack_offset,
Self::FLOAT_RETURN_REGS[0],
);
}
SymbolStorage::GeneralReg(_)
| SymbolStorage::BaseAndGeneralReg { .. } => {
internal_error!("Cannot load general symbol into FloatReg")
}
}
stack_offset += 8;
tmp_stack_offset += 8;
}
}
Layout::Builtin(Builtin::Str) => {
@ -778,7 +661,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Windo
}
}
}
stack_offset as u32
storage_manager.update_fn_call_stack_size(tmp_stack_offset as u32);
}
fn return_complex_symbol<'a>(