remove todo and use doc comments in more places

This commit is contained in:
Brendan Hansknecht 2022-02-18 21:29:18 -08:00
parent 7df6b34a21
commit 469ecbe6c5
2 changed files with 75 additions and 82 deletions

View file

@ -14,14 +14,6 @@ pub(crate) mod aarch64;
pub(crate) mod storage; pub(crate) mod storage;
pub(crate) mod x86_64; pub(crate) mod x86_64;
// TODO: StorageManager is still not fully integrated.
// General pieces needed:
// - function call stack fully moved to storage manager
// - remove data that is duplicated here and in storage manager
// To make joinpoints better:
// - consider their parameters to be alive until the last jump to them
// - save the location of the parameters at the start of the joinpoint in a special location.
// - When jumping to them, move the args to the location of the parameters (pushing to stack if necessary)
use storage::StorageManager; use storage::StorageManager;
pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait, ASM: Assembler<GeneralReg, FloatReg>>: pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait, ASM: Assembler<GeneralReg, FloatReg>>:
@ -66,8 +58,8 @@ pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait, ASM: Assembler<Gene
fn_call_stack_size: i32, fn_call_stack_size: i32,
); );
// load_args updates the symbol map to know where every arg is stored. /// load_args updates the symbol map to know where every arg is stored.
// It returns the total stack space after loading the args. /// It returns the total stack space after loading the args.
fn load_args<'a>( fn load_args<'a>(
buf: &mut Vec<'a, u8>, buf: &mut Vec<'a, u8>,
storage_manager: &mut StorageManager<'a, GeneralReg, FloatReg, ASM, Self>, storage_manager: &mut StorageManager<'a, GeneralReg, FloatReg, ASM, Self>,
@ -76,8 +68,8 @@ pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait, ASM: Assembler<Gene
ret_layout: &Layout<'a>, ret_layout: &Layout<'a>,
); );
// store_args stores the args in registers and on the stack for function calling. /// store_args stores the args in registers and on the stack for function calling.
// It returns the amount of stack space needed to temporarily store the args. /// It returns the amount of stack space needed to temporarily store the args.
fn store_args<'a>( fn store_args<'a>(
buf: &mut Vec<'a, u8>, buf: &mut Vec<'a, u8>,
storage_manager: &mut StorageManager<'a, GeneralReg, FloatReg, ASM, Self>, storage_manager: &mut StorageManager<'a, GeneralReg, FloatReg, ASM, Self>,
@ -137,16 +129,16 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized {
fn call(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>, fn_name: String); fn call(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>, fn_name: String);
// Jumps by an offset of offset bytes unconditionally. /// Jumps by an offset of offset bytes unconditionally.
// It should always generate the same number of bytes to enable replacement if offset changes. /// It should always generate the same number of bytes to enable replacement if offset changes.
// It returns the base offset to calculate the jump from (generally the instruction after the jump). /// It returns the base offset to calculate the jump from (generally the instruction after the jump).
fn jmp_imm32(buf: &mut Vec<'_, u8>, offset: i32) -> usize; fn jmp_imm32(buf: &mut Vec<'_, u8>, offset: i32) -> usize;
fn tail_call(buf: &mut Vec<'_, u8>) -> u64; fn tail_call(buf: &mut Vec<'_, u8>) -> u64;
// Jumps by an offset of offset bytes if reg is not equal to imm. /// Jumps by an offset of offset bytes if reg is not equal to imm.
// It should always generate the same number of bytes to enable replacement if offset changes. /// It should always generate the same number of bytes to enable replacement if offset changes.
// It returns the base offset to calculate the jump from (generally the instruction after the jump). /// It returns the base offset to calculate the jump from (generally the instruction after the jump).
fn jne_reg64_imm64_imm32( fn jne_reg64_imm64_imm32(
buf: &mut Vec<'_, u8>, buf: &mut Vec<'_, u8>,
reg: GeneralReg, reg: GeneralReg,
@ -985,7 +977,7 @@ impl<
CC: CallConv<GeneralReg, FloatReg, ASM>, CC: CallConv<GeneralReg, FloatReg, ASM>,
> Backend64Bit<'a, GeneralReg, FloatReg, ASM, CC> > Backend64Bit<'a, GeneralReg, FloatReg, ASM, CC>
{ {
// Updates a jump instruction to a new offset and returns the number of bytes written. /// Updates a jump instruction to a new offset and returns the number of bytes written.
fn update_jmp_imm32_offset( fn update_jmp_imm32_offset(
&mut self, &mut self,
tmp: &mut Vec<'a, u8>, tmp: &mut Vec<'a, u8>,

View file

@ -28,31 +28,31 @@ enum RegStorage<GeneralReg: RegTrait, FloatReg: RegTrait> {
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum StackStorage<GeneralReg: RegTrait, FloatReg: RegTrait> { enum StackStorage<GeneralReg: RegTrait, FloatReg: RegTrait> {
// Primitives are 8 bytes or less. That generally live in registers but can move stored on the stack. /// Primitives are 8 bytes or less. That generally live in registers but can move stored on the stack.
// Their data must always be 8 byte aligned and will be moved as a block. /// Their data must always be 8 byte aligned and will be moved as a block.
// They are never part of a struct, union, or more complex value. /// They are never part of a struct, union, or more complex value.
// The rest of the bytes should be zero due to how these are loaded. /// The rest of the bytes should be zero due to how these are loaded.
Primitive { Primitive {
// Offset from the base pointer in bytes. // Offset from the base pointer in bytes.
base_offset: i32, base_offset: i32,
// Optional register also holding the value. // Optional register also holding the value.
reg: Option<RegStorage<GeneralReg, FloatReg>>, reg: Option<RegStorage<GeneralReg, FloatReg>>,
}, },
// Referenced Primitives are primitives within a complex structure. /// Referenced Primitives are primitives within a complex structure.
// They have no guarentees about alignment or zeroed bits. /// They have no guarentees about alignment or zeroed bits.
// When they are loaded, they should be aligned and zeroed. /// When they are loaded, they should be aligned and zeroed.
// After loading, they should just be stored in a register. /// After loading, they should just be stored in a register.
ReferencedPrimitive { ReferencedPrimitive {
// Offset from the base pointer in bytes. // Offset from the base pointer in bytes.
base_offset: i32, base_offset: i32,
// Size on the stack in bytes. // Size on the stack in bytes.
size: u32, size: u32,
}, },
// Complex data (lists, unions, structs, str) stored on the stack. /// Complex data (lists, unions, structs, str) stored on the stack.
// Note, this is also used for referencing a value within a struct/union. /// Note, this is also used for referencing a value within a struct/union.
// It has no alignment guarantees. /// It has no alignment guarantees.
// When a primitive value is being loaded from this, it should be moved into a register. /// When a primitive value is being loaded from this, it should be moved into a register.
// To start, the primitive can just be loaded as a ReferencePrimitive. /// To start, the primitive can just be loaded as a ReferencePrimitive.
Complex { Complex {
// Offset from the base pointer in bytes. // Offset from the base pointer in bytes.
base_offset: i32, base_offset: i32,
@ -196,7 +196,7 @@ impl<
used_regs used_regs
} }
// Returns true if the symbol is storing a primitive value. /// Returns true if the symbol is storing a primitive value.
pub fn is_stored_primitive(&self, sym: &Symbol) -> bool { pub fn is_stored_primitive(&self, sym: &Symbol) -> bool {
matches!( matches!(
self.get_storage_for_sym(sym), self.get_storage_for_sym(sym),
@ -204,8 +204,8 @@ impl<
) )
} }
// Get a general register from the free list. /// Get a general register from the free list.
// Will free data to the stack if necessary to get the register. /// Will free data to the stack if necessary to get the register.
fn get_general_reg(&mut self, buf: &mut Vec<'a, u8>) -> GeneralReg { fn get_general_reg(&mut self, buf: &mut Vec<'a, u8>) -> GeneralReg {
if let Some(reg) = self.general_free_regs.pop() { if let Some(reg) = self.general_free_regs.pop() {
if CC::general_callee_saved(&reg) { if CC::general_callee_saved(&reg) {
@ -221,8 +221,8 @@ impl<
} }
} }
// Get a float register from the free list. /// Get a float register from the free list.
// Will free data to the stack if necessary to get the register. /// Will free data to the stack if necessary to get the register.
fn get_float_reg(&mut self, buf: &mut Vec<'a, u8>) -> FloatReg { fn get_float_reg(&mut self, buf: &mut Vec<'a, u8>) -> FloatReg {
if let Some(reg) = self.float_free_regs.pop() { if let Some(reg) = self.float_free_regs.pop() {
if CC::float_callee_saved(&reg) { if CC::float_callee_saved(&reg) {
@ -238,8 +238,8 @@ impl<
} }
} }
// Claims a general reg for a specific symbol. /// Claims a general reg for a specific symbol.
// They symbol should not already have storage. /// They symbol should not already have storage.
pub fn claim_general_reg(&mut self, buf: &mut Vec<'a, u8>, sym: &Symbol) -> GeneralReg { 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);
let reg = self.get_general_reg(buf); let reg = self.get_general_reg(buf);
@ -248,8 +248,8 @@ impl<
reg reg
} }
// Claims a float reg for a specific symbol. /// Claims a float reg for a specific symbol.
// They symbol should not already have storage. /// They symbol should not already have storage.
pub fn claim_float_reg(&mut self, buf: &mut Vec<'a, u8>, sym: &Symbol) -> FloatReg { pub fn claim_float_reg(&mut self, buf: &mut Vec<'a, u8>, sym: &Symbol) -> FloatReg {
debug_assert_eq!(self.symbol_storage_map.get(sym), None); debug_assert_eq!(self.symbol_storage_map.get(sym), None);
let reg = self.get_float_reg(buf); let reg = self.get_float_reg(buf);
@ -258,8 +258,8 @@ impl<
reg reg
} }
// This claims a temporary general register and enables is used in the passed in function. /// This claims a temporary general register and enables is used in the passed in function.
// Temporary registers are not safe across call instructions. /// Temporary registers are not safe across call instructions.
pub fn with_tmp_general_reg<F: FnOnce(&mut Self, &mut Vec<'a, u8>, GeneralReg)>( pub fn with_tmp_general_reg<F: FnOnce(&mut Self, &mut Vec<'a, u8>, GeneralReg)>(
&mut self, &mut self,
buf: &mut Vec<'a, u8>, buf: &mut Vec<'a, u8>,
@ -271,8 +271,8 @@ impl<
} }
#[allow(dead_code)] #[allow(dead_code)]
// This claims a temporary float register and enables is used in the passed in function. /// This claims a temporary float register and enables is used in the passed in function.
// Temporary registers are not safe across call instructions. /// Temporary registers are not safe across call instructions.
pub fn with_tmp_float_reg<F: FnOnce(&mut Self, &mut Vec<'a, u8>, FloatReg)>( pub fn with_tmp_float_reg<F: FnOnce(&mut Self, &mut Vec<'a, u8>, FloatReg)>(
&mut self, &mut self,
buf: &mut Vec<'a, u8>, buf: &mut Vec<'a, u8>,
@ -283,10 +283,10 @@ impl<
self.float_free_regs.push(reg); self.float_free_regs.push(reg);
} }
// Loads a symbol into a general reg and returns that register. /// Loads a symbol into a general reg and returns that register.
// The symbol must already be stored somewhere. /// The symbol must already be stored somewhere.
// Will fail on values stored in float regs. /// Will fail on values stored in float regs.
// Will fail for values that don't fit in a single register. /// Will fail for values that don't fit in a single register.
pub fn load_to_general_reg(&mut self, buf: &mut Vec<'a, u8>, sym: &Symbol) -> GeneralReg { pub fn load_to_general_reg(&mut self, buf: &mut Vec<'a, u8>, sym: &Symbol) -> GeneralReg {
let storage = self.remove_storage_for_sym(sym); let storage = self.remove_storage_for_sym(sym);
match storage { match storage {
@ -345,10 +345,10 @@ impl<
} }
} }
// Loads a symbol into a float reg and returns that register. /// Loads a symbol into a float reg and returns that register.
// The symbol must already be stored somewhere. /// The symbol must already be stored somewhere.
// Will fail on values stored in general regs. /// Will fail on values stored in general regs.
// Will fail for values that don't fit in a single register. /// Will fail for values that don't fit in a single register.
pub fn load_to_float_reg(&mut self, buf: &mut Vec<'a, u8>, sym: &Symbol) -> FloatReg { pub fn load_to_float_reg(&mut self, buf: &mut Vec<'a, u8>, sym: &Symbol) -> FloatReg {
let storage = self.remove_storage_for_sym(sym); let storage = self.remove_storage_for_sym(sym);
match storage { match storage {
@ -407,11 +407,11 @@ impl<
} }
} }
// Loads the symbol to the specified register. /// Loads the symbol to the specified register.
// It will fail if the symbol is stored in a float register. /// It will fail if the symbol is stored in a float register.
// This is only made to be used in special cases where exact regs are needed (function args and returns). /// This is only made to be used in special cases where exact regs are needed (function args and returns).
// It will not try to free the register first. /// It will not try to free the register first.
// This will not track the symbol change (it makes no assumptions about the new reg). /// This will not track the symbol change (it makes no assumptions about the new reg).
pub fn load_to_specified_general_reg( pub fn load_to_specified_general_reg(
&self, &self,
buf: &mut Vec<'a, u8>, buf: &mut Vec<'a, u8>,
@ -461,11 +461,11 @@ impl<
} }
} }
// Loads the symbol to the specified register. /// Loads the symbol to the specified register.
// It will fail if the symbol is stored in a general register. /// It will fail if the symbol is stored in a general register.
// This is only made to be used in special cases where exact regs are needed (function args and returns). /// This is only made to be used in special cases where exact regs are needed (function args and returns).
// It will not try to free the register first. /// It will not try to free the register first.
// This will not track the symbol change (it makes no assumptions about the new reg). /// This will not track the symbol change (it makes no assumptions about the new reg).
pub fn load_to_specified_float_reg(&self, buf: &mut Vec<'a, u8>, sym: &Symbol, reg: FloatReg) { pub fn load_to_specified_float_reg(&self, buf: &mut Vec<'a, u8>, sym: &Symbol, reg: FloatReg) {
match self.get_storage_for_sym(sym) { match self.get_storage_for_sym(sym) {
Reg(Float(old_reg)) Reg(Float(old_reg))
@ -510,8 +510,8 @@ impl<
} }
} }
// Loads a field from a struct or tag union. /// Loads a field from a struct or tag union.
// This is lazy by default. It will not copy anything around. /// This is lazy by default. It will not copy anything around.
pub fn load_field_at_index( pub fn load_field_at_index(
&mut self, &mut self,
sym: &Symbol, sym: &Symbol,
@ -564,7 +564,7 @@ impl<
} }
} }
// Creates a struct on the stack, moving the data in fields into the struct. /// Creates a struct on the stack, moving the data in fields into the struct.
pub fn create_struct( pub fn create_struct(
&mut self, &mut self,
buf: &mut Vec<'a, u8>, buf: &mut Vec<'a, u8>,
@ -593,10 +593,10 @@ impl<
} }
} }
// Copies a symbol to the specified stack offset. This is used for things like filling structs. /// Copies a symbol to the specified stack offset. This is used for things like filling structs.
// The offset is not guarenteed to be perfectly aligned, it follows Roc's alignment plan. /// The offset is not guarenteed to be perfectly aligned, it follows Roc's alignment plan.
// This means that, for example 2 I32s might be back to back on the stack. /// This means that, for example 2 I32s might be back to back on the stack.
// Always interact with the stack using aligned 64bit movement. /// Always interact with the stack using aligned 64bit movement.
fn copy_symbol_to_stack_offset( fn copy_symbol_to_stack_offset(
&mut self, &mut self,
buf: &mut Vec<'a, u8>, buf: &mut Vec<'a, u8>,
@ -687,8 +687,8 @@ impl<
} }
} }
// Frees `wanted_reg` which is currently owned by `sym` by making sure the value is loaded on the stack. /// Frees `wanted_reg` which is currently owned by `sym` by making sure the value is loaded on the stack.
// Note, used and free regs are expected to be updated outside of this function. /// Note, used and free regs are expected to be updated outside of this function.
fn free_to_stack( fn free_to_stack(
&mut self, &mut self,
buf: &mut Vec<'a, u8>, buf: &mut Vec<'a, u8>,
@ -731,8 +731,8 @@ impl<
} }
} }
// gets the stack offset and size of the specified symbol. /// gets the stack offset and size of the specified symbol.
// the symbol must already be stored on the stack. /// the symbol must already be stored on the stack.
pub fn stack_offset_and_size(&self, sym: &Symbol) -> (i32, u32) { pub fn stack_offset_and_size(&self, sym: &Symbol) -> (i32, u32) {
match self.get_storage_for_sym(sym) { match self.get_storage_for_sym(sym) {
Stack(Primitive { base_offset, .. }) => (*base_offset, 8), Stack(Primitive { base_offset, .. }) => (*base_offset, 8),
@ -749,21 +749,21 @@ impl<
} }
} }
// Specifies a symbol is loaded at the specified general register. /// Specifies a symbol is loaded at the specified general register.
pub fn general_reg_arg(&mut self, sym: &Symbol, reg: GeneralReg) { pub fn general_reg_arg(&mut self, sym: &Symbol, reg: GeneralReg) {
self.symbol_storage_map.insert(*sym, Reg(General(reg))); self.symbol_storage_map.insert(*sym, Reg(General(reg)));
self.general_free_regs.retain(|r| *r != reg); self.general_free_regs.retain(|r| *r != reg);
self.general_used_regs.push((reg, *sym)); self.general_used_regs.push((reg, *sym));
} }
// Specifies a symbol is loaded at the specified float register. /// Specifies a symbol is loaded at the specified float register.
pub fn float_reg_arg(&mut self, sym: &Symbol, reg: FloatReg) { pub fn float_reg_arg(&mut self, sym: &Symbol, reg: FloatReg) {
self.symbol_storage_map.insert(*sym, Reg(Float(reg))); self.symbol_storage_map.insert(*sym, Reg(Float(reg)));
self.float_free_regs.retain(|r| *r != reg); self.float_free_regs.retain(|r| *r != reg);
self.float_used_regs.push((reg, *sym)); self.float_used_regs.push((reg, *sym));
} }
// Specifies a primitive is loaded at the specific base offset. /// Specifies a primitive is loaded at the specific base offset.
pub fn primitive_stack_arg(&mut self, sym: &Symbol, base_offset: i32) { pub fn primitive_stack_arg(&mut self, sym: &Symbol, base_offset: i32) {
self.symbol_storage_map.insert( self.symbol_storage_map.insert(
*sym, *sym,
@ -774,13 +774,13 @@ impl<
); );
} }
// Loads the arg pointer symbol to the specified general reg. /// Loads the arg pointer symbol to the specified general reg.
pub fn ret_pionter_arg(&mut self, reg: GeneralReg) { pub fn ret_pionter_arg(&mut self, reg: GeneralReg) {
self.symbol_storage_map self.symbol_storage_map
.insert(Symbol::RET_POINTER, Reg(General(reg))); .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. /// 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) { 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); self.fn_call_stack_size = max(self.fn_call_stack_size, tmp_size);
} }
@ -830,8 +830,8 @@ impl<
self.join_param_map.insert(*id, param_storage); self.join_param_map.insert(*id, param_storage);
} }
// Setup jump loads the parameters for the joinpoint. /// Setup jump loads the parameters for the joinpoint.
// This enables the jump to correctly passe arguments to the joinpoint. /// This enables the jump to correctly passe arguments to the joinpoint.
pub fn setup_jump( pub fn setup_jump(
&mut self, &mut self,
buf: &mut Vec<'a, u8>, buf: &mut Vec<'a, u8>,
@ -882,6 +882,7 @@ impl<
} }
self.join_param_map.insert(*id, param_storage); self.join_param_map.insert(*id, param_storage);
} }
/// claim_stack_area is the public wrapper around claim_stack_size. /// claim_stack_area is the public wrapper around claim_stack_size.
/// It also deals with updating symbol storage. /// It also deals with updating symbol storage.
/// It returns the base offset of the stack area. /// It returns the base offset of the stack area.
@ -969,7 +970,7 @@ impl<
} }
} }
// Frees an reference and release an allocation if it is no longer used. /// Frees an reference and release an allocation if it is no longer used.
fn free_reference(&mut self, sym: &Symbol) { fn free_reference(&mut self, sym: &Symbol) {
let owned_data = if let Some(owned_data) = self.allocation_map.remove(sym) { let owned_data = if let Some(owned_data) = self.allocation_map.remove(sym) {
owned_data owned_data