add referenced primitives and move loading struct fields to storage manager

This commit is contained in:
Brendan Hansknecht 2022-02-17 16:32:28 -08:00
parent f4bb49427d
commit aa1c0c11b2
3 changed files with 111 additions and 44 deletions

View file

@ -1040,23 +1040,8 @@ impl<
index: u64, index: u64,
field_layouts: &'a [Layout<'a>], field_layouts: &'a [Layout<'a>],
) { ) {
if let Some(SymbolStorage::Base { offset, .. }) = self.symbol_storage_map.get(structure) { self.storage_manager
let mut data_offset = *offset; .load_field_at_index(sym, structure, index, field_layouts);
for i in 0..index {
let field_size = field_layouts[i as usize].stack_size(self.target_info);
data_offset += field_size as i32;
}
self.symbol_storage_map.insert(
*sym,
SymbolStorage::Base {
offset: data_offset,
size: field_layouts[index as usize].stack_size(self.target_info),
owned: false,
},
);
} else {
internal_error!("unknown struct: {:?}", structure);
}
} }
fn load_literal(&mut self, sym: &Symbol, layout: &Layout<'a>, lit: &Literal<'a>) { fn load_literal(&mut self, sym: &Symbol, layout: &Layout<'a>, lit: &Literal<'a>) {
@ -1247,7 +1232,7 @@ macro_rules! single_register_floats {
} }
#[macro_export] #[macro_export]
macro_rules! single_register_builtins { macro_rules! single_register_layouts {
() => { () => {
single_register_integers!() | single_register_floats!() single_register_integers!() | single_register_floats!()
}; };

View file

@ -1,5 +1,7 @@
use crate::generic64::{Assembler, CallConv, RegTrait}; use crate::{
use crate::Env; generic64::{Assembler, CallConv, RegTrait},
single_register_floats, single_register_integers, single_register_layouts, Env,
};
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use roc_builtins::bitcode::{FloatWidth, IntWidth}; use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_collections::all::{MutMap, MutSet}; use roc_collections::all::{MutMap, MutSet};
@ -25,12 +27,23 @@ 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.
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.
// They have no guarentees about alignment or zeroed bits.
// When they are loaded, they should be aligned and zeroed.
// After loading, they should just be stored in a register.
ReferencedPrimitive {
// Offset from the base pointer in bytes.
base_offset: i32,
// Size on the stack in bytes.
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.
@ -67,12 +80,12 @@ pub struct StorageManager<
// Data about where each symbol is stored. // Data about where each symbol is stored.
symbol_storage_map: MutMap<Symbol, Storage<GeneralReg, FloatReg>>, symbol_storage_map: MutMap<Symbol, Storage<GeneralReg, FloatReg>>,
// A map from child to parent storage. // A map from symbol to its owning allocation.
// This is only used for complex data on the stack and its references.
// In the case that subdata is still referenced from an overall structure, // In the case that subdata is still referenced from an overall structure,
// We can't free the entire structure until the subdata is no longer needed. // We can't free the entire structure until the subdata is no longer needed.
// If a symbol has no parent, it points to itself. // If a symbol has only one reference, we can free it.
// This in only used for data on the stack. allocation_map: MutMap<Symbol, Rc<(i32, u32)>>,
reference_map: MutMap<Symbol, Rc<Symbol>>,
// This should probably be smarter than a vec. // This should probably be smarter than a vec.
// There are certain registers we should always use first. With pushing and popping, this could get mixed. // There are certain registers we should always use first. With pushing and popping, this could get mixed.
@ -109,7 +122,7 @@ pub fn new_storage_manager<
env, env,
target_info, target_info,
symbol_storage_map: MutMap::default(), symbol_storage_map: MutMap::default(),
reference_map: MutMap::default(), allocation_map: MutMap::default(),
general_free_regs: bumpalo::vec![in env.arena], general_free_regs: bumpalo::vec![in env.arena],
general_used_regs: bumpalo::vec![in env.arena], general_used_regs: bumpalo::vec![in env.arena],
general_used_callee_saved_regs: MutSet::default(), general_used_callee_saved_regs: MutSet::default(),
@ -131,7 +144,7 @@ impl<
{ {
pub fn reset(&mut self) { pub fn reset(&mut self) {
self.symbol_storage_map.clear(); self.symbol_storage_map.clear();
self.reference_map.clear(); self.allocation_map.clear();
self.general_used_callee_saved_regs.clear(); self.general_used_callee_saved_regs.clear();
self.general_free_regs.clear(); self.general_free_regs.clear();
self.general_used_regs.clear(); self.general_used_regs.clear();
@ -267,6 +280,9 @@ impl<
); );
reg reg
} }
Stack(ReferencedPrimitive { .. }) => {
todo!("loading referenced primitives")
}
Stack(Complex { .. }) => { Stack(Complex { .. }) => {
internal_error!("Cannot load large values into general registers: {}", sym) internal_error!("Cannot load large values into general registers: {}", sym)
} }
@ -319,6 +335,9 @@ impl<
); );
reg reg
} }
Stack(ReferencedPrimitive { .. }) => {
todo!("loading referenced primitives")
}
Stack(Complex { .. }) => { Stack(Complex { .. }) => {
internal_error!("Cannot load large values into float registers: {}", sym) internal_error!("Cannot load large values into float registers: {}", sym)
} }
@ -328,6 +347,65 @@ impl<
} }
} }
// Loads a field from a struct or tag union.
// This is lazy by default. It will not copy anything around.
pub fn load_field_at_index(
&mut self,
sym: &Symbol,
structure: &Symbol,
index: u64,
field_layouts: &'a [Layout<'a>],
) {
debug_assert!(index < field_layouts.len());
let storage = if let Some(storage) = self.symbol_storage_map.get(structure) {
storage
} else {
internal_error!("Unknown symbol: {}", structure);
};
// This must be removed and reinserted for ownership and mutability reasons.
let owned_data = if let Some(owned_data) = self.allocation_map.remove(structure) {
owned_data
} else {
internal_error!("Unknown symbol: {}", structure);
};
self.allocation_map
.insert(*structure, Rc::clone(&owned_data));
match storage {
Stack(Complex { base_offset, size }) => {
let (base_offset, size) = (*base_offset, *size);
let mut data_offset = base_offset;
for i in 0..index as usize {
let field_size = field_layouts[i].stack_size(self.target_info);
data_offset += field_size as i32;
}
debug_assert!(data_offset < base_offset + size as i32);
self.allocation_map.insert(*sym, owned_data);
let layout = field_layouts[index as usize];
let size = layout.stack_size(self.target_info);
self.symbol_storage_map.insert(
*sym,
Stack(if is_primitive(&layout) {
ReferencedPrimitive {
base_offset: data_offset,
size,
}
} else {
Complex {
base_offset: data_offset,
size,
}
}),
);
}
_ => {
internal_error!(
"Cannot load field from data with storage type: {:?}",
storage
);
}
}
}
// 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,
@ -349,6 +427,8 @@ impl<
size: struct_size, size: struct_size,
}), }),
); );
self.allocation_map
.insert(*sym, Rc::new((base_offset, struct_size)));
if let Layout::Struct(field_layouts) = layout { if let Layout::Struct(field_layouts) = layout {
let mut current_offset = base_offset; let mut current_offset = base_offset;
@ -453,10 +533,8 @@ impl<
}), }),
); );
} }
Stack(Primitive { reg: None, .. }) => { NoData
internal_error!("Cannot free reg from symbol without a reg: {}", sym) | Stack(Complex { .. } | Primitive { reg: None, .. } | ReferencedPrimitive { .. }) => {
}
NoData | Stack(Complex { .. }) => {
internal_error!("Cannot free reg from symbol without a reg: {}", sym) internal_error!("Cannot free reg from symbol without a reg: {}", sym)
} }
} }
@ -509,20 +587,20 @@ impl<
} else { } else {
internal_error!("Unknown symbol: {}", sym); internal_error!("Unknown symbol: {}", sym);
}; };
let rc_sym = if let Some(rc_sym) = self.reference_map.remove(sym) {
rc_sym
} else {
internal_error!("Unknown symbol: {}", sym);
};
match storage { match storage {
// Free stack chunck if this is the last reference to the chunk. // Free stack chunck if this is the last reference to the chunk.
Stack(Primitive { base_offset, .. }) if Rc::strong_count(&rc_sym) == 1 => { Stack(Primitive { base_offset, .. }) => {
self.free_stack_chunk(base_offset, 8); self.free_stack_chunk(base_offset, 8);
} }
Stack(Complex { Stack(Complex { .. } | ReferencedPrimitive { .. }) => {
base_offset, size, .. let owned_data = if let Some(owned_data) = self.allocation_map.remove(sym) {
}) if Rc::strong_count(&rc_sym) == 1 => { owned_data
self.free_stack_chunk(base_offset, size); } else {
internal_error!("Unknown symbol: {}", sym);
};
if Rc::strong_count(&owned_data) == 1 {
self.free_stack_chunk(owned_data.0, owned_data.1);
}
} }
_ => {} _ => {}
} }
@ -621,3 +699,7 @@ impl<
} }
} }
} }
fn is_primitive<'a>(layout: &Layout<'a>) -> bool {
matches!(layout, single_register_layouts!())
}

View file

@ -1,6 +1,6 @@
use crate::generic64::{Assembler, CallConv, RegTrait, SymbolStorage}; use crate::generic64::{Assembler, CallConv, RegTrait, SymbolStorage};
use crate::{ use crate::{
single_register_builtins, single_register_floats, single_register_integers, Relocation, single_register_floats, single_register_integers, single_register_layouts, Relocation,
}; };
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use roc_builtins::bitcode::{FloatWidth, IntWidth}; use roc_builtins::bitcode::{FloatWidth, IntWidth};
@ -294,7 +294,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
// For most return layouts we will do nothing. // For most return layouts we will do nothing.
// In some cases, we need to put the return address as the first arg. // In some cases, we need to put the return address as the first arg.
match ret_layout { match ret_layout {
single_register_builtins!() | Layout::Builtin(Builtin::Str) | Layout::Struct([]) => { single_register_layouts!() | Layout::Builtin(Builtin::Str) | Layout::Struct([]) => {
// Nothing needs to be done for any of these cases. // Nothing needs to be done for any of these cases.
} }
x => { x => {
@ -613,7 +613,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
} }
} else { } else {
arg_offset += match layout { arg_offset += match layout {
single_register_builtins!() => 8, single_register_layouts!() => 8,
x => { x => {
todo!("Loading args with layout {:?}", x); todo!("Loading args with layout {:?}", x);
} }
@ -643,7 +643,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
// For most return layouts we will do nothing. // For most return layouts we will do nothing.
// In some cases, we need to put the return address as the first arg. // In some cases, we need to put the return address as the first arg.
match ret_layout { match ret_layout {
single_register_builtins!() | Layout::Struct([]) => { single_register_layouts!() | Layout::Struct([]) => {
// Nothing needs to be done for any of these cases. // Nothing needs to be done for any of these cases.
} }
x => { x => {