Refactor SymbolStorage local_id code, and copy_memory

SymbolStorage had some rather ad-hoc methods for extracting pieces of data.
This change makes that more intentional and organised.

Also cleaned up the API for the related function copy_memory,
as it had lots of positional arguments with the same types.
Created a struct for this just to name them and make the code clearer.
This commit is contained in:
Brian Carroll 2021-10-02 22:07:12 +01:00
parent 6aed70978d
commit 79a4f2bbf7
3 changed files with 217 additions and 183 deletions

View file

@ -13,8 +13,8 @@ use roc_mono::layout::{Builtin, Layout};
use crate::layout::WasmLayout;
use crate::storage::{StackMemoryLocation, SymbolStorage};
use crate::{
pop_stack_frame, push_stack_frame, round_up_to_alignment, LocalId, MemoryCopy, PTR_SIZE,
PTR_TYPE,
copy_memory, pop_stack_frame, push_stack_frame, round_up_to_alignment, CopyMemoryConfig,
LocalId, PTR_SIZE, PTR_TYPE,
};
// Don't allocate any constant data at address zero or near it. Would be valid, but bug-prone.
@ -164,7 +164,7 @@ impl<'a> WasmBackend<'a> {
wasm_layout: WasmLayout,
symbol: Symbol,
kind: LocalKind,
) -> SymbolStorage {
) -> Option<LocalId> {
let next_local_id = LocalId((self.arg_types.len() + self.locals.len()) as u32);
match kind {
@ -176,32 +176,33 @@ impl<'a> WasmBackend<'a> {
}
}
let storage = match wasm_layout {
WasmLayout::LocalOnly(value_type, size) => SymbolStorage::Local {
local_id: next_local_id,
value_type,
size,
},
let (maybe_local_id, storage) = match wasm_layout {
WasmLayout::LocalOnly(value_type, size) => (
Some(next_local_id),
SymbolStorage::Local {
local_id: next_local_id,
value_type,
size,
},
),
WasmLayout::HeapMemory => SymbolStorage::Local {
local_id: next_local_id,
value_type: PTR_TYPE,
size: PTR_SIZE,
},
WasmLayout::HeapMemory => (
Some(next_local_id),
SymbolStorage::Local {
local_id: next_local_id,
value_type: PTR_TYPE,
size: PTR_SIZE,
},
),
WasmLayout::StackMemory {
size,
alignment_bytes,
} => {
let location = match kind {
LocalKind::Parameter => StackMemoryLocation::ExternalPointer(next_local_id),
LocalKind::Parameter => StackMemoryLocation::CallerFrame(next_local_id),
LocalKind::Variable => {
let offset =
round_up_to_alignment(self.stack_memory, alignment_bytes as i32);
self.stack_memory = offset + size as i32;
match self.stack_frame_pointer {
Some(_) => {}
None => {
@ -209,20 +210,29 @@ impl<'a> WasmBackend<'a> {
}
};
StackMemoryLocation::InternalOffset(offset as u32)
let offset =
round_up_to_alignment(self.stack_memory, alignment_bytes as i32);
self.stack_memory = offset + size as i32;
StackMemoryLocation::OwnFrame(offset as u32)
}
};
SymbolStorage::StackMemory {
location,
size,
alignment_bytes,
}
(
None,
SymbolStorage::StackMemory {
location,
size,
alignment_bytes,
},
)
}
};
self.symbol_storage_map.insert(symbol, storage.clone());
self.symbol_storage_map.insert(symbol, storage);
storage
maybe_local_id
}
fn get_symbol_storage(&self, sym: &Symbol) -> &SymbolStorage {
@ -236,20 +246,42 @@ impl<'a> WasmBackend<'a> {
fn local_id_from_symbol(&self, sym: &Symbol) -> LocalId {
let storage = self.get_symbol_storage(sym);
storage.local_id(self.stack_frame_pointer)
match storage {
SymbolStorage::Local { local_id, .. } => *local_id,
_ => {
panic!("{:?} does not have a local_id", sym);
}
}
}
/// Load a symbol, e.g. for passing to a function call
fn load_symbol(&mut self, sym: &Symbol) {
let storage = self.get_symbol_storage(sym);
let index: u32 = storage.local_id(self.stack_frame_pointer).0;
self.instructions.push(GetLocal(index));
let storage = self.get_symbol_storage(sym).to_owned();
match storage {
SymbolStorage::Local { local_id, .. }
| SymbolStorage::StackMemory {
location: StackMemoryLocation::CallerFrame(local_id),
..
} => {
self.instructions.push(GetLocal(local_id.0));
}
SymbolStorage::StackMemory {
location: StackMemoryLocation::OwnFrame(offset),
..
} => {
self.instructions.extend([
GetLocal(self.stack_frame_pointer.unwrap().0),
I32Const(offset as i32),
I32Add,
]);
}
}
}
/// start a loop that leaves a value on the stack
fn start_loop_with_return(&mut self, value_type: ValueType) {
self.block_depth += 1;
// self.instructions.push(Loop(BlockType::NoResult));
self.instructions.push(Loop(BlockType::Value(value_type)));
}
@ -276,7 +308,7 @@ impl<'a> WasmBackend<'a> {
// Map this symbol to the first argument (pointer into caller's stack)
// Saves us from having to copy it later
let storage = SymbolStorage::StackMemory {
location: StackMemoryLocation::ExternalPointer(LocalId(0)),
location: StackMemoryLocation::CallerFrame(LocalId(0)),
size,
alignment_bytes,
};
@ -289,16 +321,12 @@ impl<'a> WasmBackend<'a> {
Stmt::Let(sym, expr, layout, following) => {
let wasm_layout = WasmLayout::new(layout);
let local_id = self
.insert_local(wasm_layout, *sym, LocalKind::Variable)
.local_id(self.stack_frame_pointer);
let maybe_local_id = self.insert_local(wasm_layout, *sym, LocalKind::Variable);
self.build_expr(sym, expr, layout)?;
// If this local is shared with the stack frame pointer, it's already assigned
match self.stack_frame_pointer {
Some(sfp) if sfp == local_id => {}
_ => self.instructions.push(SetLocal(local_id.0)),
if let Some(local_id) = maybe_local_id {
self.instructions.push(SetLocal(local_id.0));
}
self.build_stmt(following, ret_layout)?;
@ -317,21 +345,23 @@ impl<'a> WasmBackend<'a> {
alignment_bytes,
} => {
let (from_ptr, from_offset) = match location {
StackMemoryLocation::ExternalPointer(local_id) => (*local_id, 0),
StackMemoryLocation::InternalOffset(offset) => {
StackMemoryLocation::CallerFrame(local_id) => (*local_id, 0),
StackMemoryLocation::OwnFrame(offset) => {
(self.stack_frame_pointer.unwrap(), *offset)
}
};
let copy = MemoryCopy {
from_ptr,
from_offset,
to_ptr: LocalId(0),
to_offset: 0,
size: *size,
alignment_bytes: *alignment_bytes,
};
copy.generate(&mut self.instructions);
copy_memory(
&mut self.instructions,
CopyMemoryConfig {
from_ptr,
from_offset,
to_ptr: LocalId(0),
to_offset: 0,
size: *size,
alignment_bytes: *alignment_bytes,
},
);
}
Local { local_id, .. } => {
@ -400,11 +430,10 @@ impl<'a> WasmBackend<'a> {
let mut jp_parameter_local_ids = std::vec::Vec::with_capacity(parameters.len());
for parameter in parameters.iter() {
let wasm_layout = WasmLayout::new(&parameter.layout);
let local_id = self
.insert_local(wasm_layout, parameter.symbol, LocalKind::Variable)
.local_id(self.stack_frame_pointer);
jp_parameter_local_ids.push(local_id);
let maybe_local_id =
self.insert_local(wasm_layout, parameter.symbol, LocalKind::Variable);
let jp_param_id = maybe_local_id.unwrap();
jp_parameter_local_ids.push(jp_param_id);
}
self.start_block(BlockType::NoResult);
@ -526,9 +555,22 @@ impl<'a> WasmBackend<'a> {
let storage = self.get_symbol_storage(sym).to_owned();
if let Layout::Struct(field_layouts) = layout {
let (local_id, size) = match storage {
SymbolStorage::StackMemory { size, .. } => {
(storage.local_id(self.stack_frame_pointer), size)
match storage {
SymbolStorage::StackMemory { location, size, .. } => {
if size > 0 {
let (local_id, struct_offset) =
location.local_and_offset(self.stack_frame_pointer);
let mut field_offset = struct_offset;
for (field, _) in fields.iter().zip(field_layouts.iter()) {
field_offset += self.copy_symbol_to_pointer_at_offset(
local_id,
field_offset,
field,
);
}
} else {
return Err(format!("Not supported yet: zero-size struct at {:?}", sym));
}
}
_ => {
return Err(format!(
@ -537,20 +579,14 @@ impl<'a> WasmBackend<'a> {
));
}
};
if size > 0 {
let mut relative_offset = 0;
for (field, _) in fields.iter().zip(field_layouts.iter()) {
relative_offset +=
self.copy_symbol_to_pointer_at_offset(local_id, relative_offset, field);
}
} else {
return Err(format!("Not supported yet: zero-size struct at {:?}", sym));
}
} else {
// Struct expression but not Struct layout => single element. Copy it.
let field_storage = self.get_symbol_storage(&fields[0]).to_owned();
self.copy_storage(&storage, &field_storage);
storage.copy_from(
&field_storage,
&mut self.instructions,
self.stack_frame_pointer,
);
}
Ok(())
}
@ -570,30 +606,6 @@ impl<'a> WasmBackend<'a> {
)
}
fn copy_storage(&mut self, to: &SymbolStorage, from: &SymbolStorage) {
let has_stack_memory = to.has_stack_memory();
debug_assert!(from.has_stack_memory() == has_stack_memory);
if !has_stack_memory {
debug_assert!(from.value_type() == to.value_type());
self.instructions
.push(GetLocal(from.local_id(self.stack_frame_pointer).0));
self.instructions
.push(SetLocal(to.local_id(self.stack_frame_pointer).0));
} else {
let (size, alignment_bytes) = from.stack_size_and_alignment();
let copy = MemoryCopy {
from_ptr: from.local_id(self.stack_frame_pointer),
to_ptr: to.local_id(self.stack_frame_pointer),
from_offset: from.address_offset().unwrap(),
to_offset: to.address_offset().unwrap(),
size,
alignment_bytes,
};
copy.generate(&mut self.instructions);
}
}
fn build_call_low_level(
&mut self,
lowlevel: &LowLevel,