mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
Get rid of VM stack storage
This commit is contained in:
parent
8afb88f5a4
commit
5719384ed9
6 changed files with 67 additions and 252 deletions
|
@ -89,8 +89,8 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
fn_index_offset: u32,
|
fn_index_offset: u32,
|
||||||
helper_proc_gen: CodeGenHelp<'a>,
|
helper_proc_gen: CodeGenHelp<'a>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let can_relocate_heap = module.linking.find_internal_symbol("__heap_base").is_ok()
|
let has_heap_base = module.linking.find_internal_symbol("__heap_base").is_ok();
|
||||||
&& module.linking.find_internal_symbol("__heap_end").is_ok();
|
let has_heap_end = module.linking.find_internal_symbol("__heap_end").is_ok();
|
||||||
|
|
||||||
// We don't want to import any Memory or Tables
|
// We don't want to import any Memory or Tables
|
||||||
module.import.imports.retain(|import| {
|
module.import.imports.retain(|import| {
|
||||||
|
@ -135,7 +135,7 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
proc_lookup,
|
proc_lookup,
|
||||||
host_lookup,
|
host_lookup,
|
||||||
helper_proc_gen,
|
helper_proc_gen,
|
||||||
can_relocate_heap,
|
can_relocate_heap: has_heap_base && has_heap_end,
|
||||||
|
|
||||||
// Function-level data
|
// Function-level data
|
||||||
block_depth: 0,
|
block_depth: 0,
|
||||||
|
@ -795,21 +795,17 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
|
|
||||||
self.expr(sym, expr, layout, &sym_storage);
|
self.expr(sym, expr, layout, &sym_storage);
|
||||||
|
|
||||||
// If this value is stored in the VM stack, we need code_builder to track it
|
if let StoredValue::Local { local_id, .. } = sym_storage {
|
||||||
// (since every instruction can change the VM stack)
|
if !self.code_builder.stack_is_empty() {
|
||||||
if let Some(StoredValue::VirtualMachineStack { vm_state, .. }) =
|
self.code_builder.set_local(local_id);
|
||||||
self.storage.symbol_storage_map.get_mut(&sym)
|
}
|
||||||
{
|
|
||||||
*vm_state = self.code_builder.set_top_symbol(sym);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stmt_ret(&mut self, sym: Symbol) {
|
fn stmt_ret(&mut self, sym: Symbol) {
|
||||||
use crate::storage::StoredValue::*;
|
use crate::storage::StoredValue::*;
|
||||||
|
|
||||||
let storage = self.storage.symbol_storage_map.get(&sym).unwrap();
|
match self.storage.get(&sym) {
|
||||||
|
|
||||||
match storage {
|
|
||||||
StackMemory {
|
StackMemory {
|
||||||
location,
|
location,
|
||||||
size,
|
size,
|
||||||
|
@ -856,12 +852,6 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
// We may be able to improve this in the future with `Select`
|
// We may be able to improve this in the future with `Select`
|
||||||
// or `BrTable`
|
// or `BrTable`
|
||||||
|
|
||||||
// Ensure the condition value is not stored only in the VM stack
|
|
||||||
// Otherwise we can't reach it from inside the block
|
|
||||||
let cond_storage = self.storage.get(&cond_symbol).to_owned();
|
|
||||||
self.storage
|
|
||||||
.ensure_value_has_local(&mut self.code_builder, cond_symbol, cond_storage);
|
|
||||||
|
|
||||||
// create a block for each branch except the default
|
// create a block for each branch except the default
|
||||||
for _ in 0..branches.len() {
|
for _ in 0..branches.len() {
|
||||||
self.start_block()
|
self.start_block()
|
||||||
|
@ -929,17 +919,12 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
// make locals for join pointer parameters
|
// make locals for join pointer parameters
|
||||||
let mut jp_param_storages = Vec::with_capacity_in(parameters.len(), self.env.arena);
|
let mut jp_param_storages = Vec::with_capacity_in(parameters.len(), self.env.arena);
|
||||||
for parameter in parameters.iter() {
|
for parameter in parameters.iter() {
|
||||||
let mut param_storage = self.storage.allocate_var(
|
let param_storage = self.storage.allocate_var(
|
||||||
self.layout_interner,
|
self.layout_interner,
|
||||||
parameter.layout,
|
parameter.layout,
|
||||||
parameter.symbol,
|
parameter.symbol,
|
||||||
StoredVarKind::Variable,
|
StoredVarKind::Variable,
|
||||||
);
|
);
|
||||||
param_storage = self.storage.ensure_value_has_local(
|
|
||||||
&mut self.code_builder,
|
|
||||||
parameter.symbol,
|
|
||||||
param_storage,
|
|
||||||
);
|
|
||||||
jp_param_storages.push(param_storage);
|
jp_param_storages.push(param_storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -964,12 +949,8 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
|
|
||||||
for (arg_symbol, param_storage) in arguments.iter().zip(param_storages.iter()) {
|
for (arg_symbol, param_storage) in arguments.iter().zip(param_storages.iter()) {
|
||||||
let arg_storage = self.storage.get(arg_symbol).clone();
|
let arg_storage = self.storage.get(arg_symbol).clone();
|
||||||
self.storage.clone_value(
|
self.storage
|
||||||
&mut self.code_builder,
|
.clone_value(&mut self.code_builder, param_storage, &arg_storage);
|
||||||
param_storage,
|
|
||||||
&arg_storage,
|
|
||||||
*arg_symbol,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// jump
|
// jump
|
||||||
|
@ -1018,17 +999,11 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
|
|
||||||
// Get pointer and offset
|
// Get pointer and offset
|
||||||
let value_storage = self.storage.get(&value).to_owned();
|
let value_storage = self.storage.get(&value).to_owned();
|
||||||
let stored_with_local =
|
let (tag_local_id, tag_offset) = match value_storage {
|
||||||
self.storage
|
|
||||||
.ensure_value_has_local(&mut self.code_builder, value, value_storage);
|
|
||||||
let (tag_local_id, tag_offset) = match stored_with_local {
|
|
||||||
StoredValue::StackMemory { location, .. } => {
|
StoredValue::StackMemory { location, .. } => {
|
||||||
location.local_and_offset(self.storage.stack_frame_pointer)
|
location.local_and_offset(self.storage.stack_frame_pointer)
|
||||||
}
|
}
|
||||||
StoredValue::Local { local_id, .. } => (local_id, 0),
|
StoredValue::Local { local_id, .. } => (local_id, 0),
|
||||||
StoredValue::VirtualMachineStack { .. } => {
|
|
||||||
internal_error!("{:?} should have a local variable", value)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// load pointer, and add the offset to the pointer
|
// load pointer, and add the offset to the pointer
|
||||||
|
@ -1117,13 +1092,13 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
reuse,
|
reuse,
|
||||||
} => {
|
} => {
|
||||||
let reuse = reuse.map(|ru| ru.symbol);
|
let reuse = reuse.map(|ru| ru.symbol);
|
||||||
self.expr_tag(union_layout, *tag_id, arguments, sym, storage, reuse)
|
self.expr_tag(union_layout, *tag_id, arguments, storage, reuse)
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::GetTagId {
|
Expr::GetTagId {
|
||||||
structure,
|
structure,
|
||||||
union_layout,
|
union_layout,
|
||||||
} => self.expr_get_tag_id(*structure, union_layout, sym, storage),
|
} => self.expr_get_tag_id(*structure, union_layout, storage),
|
||||||
|
|
||||||
Expr::UnionAtIndex {
|
Expr::UnionAtIndex {
|
||||||
structure,
|
structure,
|
||||||
|
@ -1142,7 +1117,6 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
*tag_id,
|
*tag_id,
|
||||||
union_layout,
|
union_layout,
|
||||||
*index,
|
*index,
|
||||||
sym,
|
|
||||||
storage,
|
storage,
|
||||||
),
|
),
|
||||||
|
|
||||||
|
@ -1157,7 +1131,7 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
Expr::Alloca {
|
Expr::Alloca {
|
||||||
initializer,
|
initializer,
|
||||||
element_layout,
|
element_layout,
|
||||||
} => self.expr_alloca(*initializer, *element_layout, sym, storage),
|
} => self.expr_alloca(*initializer, *element_layout, storage),
|
||||||
|
|
||||||
Expr::RuntimeErrorFunction(_) => {
|
Expr::RuntimeErrorFunction(_) => {
|
||||||
todo!("Expression `{}`", expr.to_pretty(100, false))
|
todo!("Expression `{}`", expr.to_pretty(100, false))
|
||||||
|
@ -1179,7 +1153,7 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
};
|
};
|
||||||
|
|
||||||
match storage {
|
match storage {
|
||||||
StoredValue::VirtualMachineStack { value_type, .. } => {
|
StoredValue::Local { value_type, .. } => {
|
||||||
match (lit, value_type) {
|
match (lit, value_type) {
|
||||||
(Literal::Float(x), ValueType::F64) => self.code_builder.f64_const(*x),
|
(Literal::Float(x), ValueType::F64) => self.code_builder.f64_const(*x),
|
||||||
(Literal::Float(x), ValueType::F32) => self.code_builder.f32_const(*x as f32),
|
(Literal::Float(x), ValueType::F32) => self.code_builder.f32_const(*x as f32),
|
||||||
|
@ -1233,8 +1207,6 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
Literal::Bool(_) | Literal::Byte(_) => invalid_error(),
|
Literal::Bool(_) | Literal::Byte(_) => invalid_error(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => invalid_error(),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1540,12 +1512,8 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
if !fields.is_empty() {
|
if !fields.is_empty() {
|
||||||
// Struct expression but not Struct layout => single element. Copy it.
|
// Struct expression but not Struct layout => single element. Copy it.
|
||||||
let field_storage = self.storage.get(&fields[0]).to_owned();
|
let field_storage = self.storage.get(&fields[0]).to_owned();
|
||||||
self.storage.clone_value(
|
self.storage
|
||||||
&mut self.code_builder,
|
.clone_value(&mut self.code_builder, storage, &field_storage);
|
||||||
storage,
|
|
||||||
&field_storage,
|
|
||||||
fields[0],
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
// Empty record. Nothing to do.
|
// Empty record. Nothing to do.
|
||||||
}
|
}
|
||||||
|
@ -1575,12 +1543,6 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
debug_assert!(matches!(value_type, ValueType::I32));
|
debug_assert!(matches!(value_type, ValueType::I32));
|
||||||
(AddressValue::NotLoaded(*local_id), 0)
|
(AddressValue::NotLoaded(*local_id), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
StoredValue::VirtualMachineStack { .. } => {
|
|
||||||
self.storage
|
|
||||||
.load_symbols(&mut self.code_builder, &[structure]);
|
|
||||||
(AddressValue::Loaded, 0)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
for field in field_layouts.iter().take(index as usize) {
|
for field in field_layouts.iter().take(index as usize) {
|
||||||
offset += self.layout_interner.stack_size(*field);
|
offset += self.layout_interner.stack_size(*field);
|
||||||
|
@ -1691,7 +1653,6 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
union_layout: &UnionLayout<'a>,
|
union_layout: &UnionLayout<'a>,
|
||||||
tag_id: TagIdIntType,
|
tag_id: TagIdIntType,
|
||||||
arguments: &'a [Symbol],
|
arguments: &'a [Symbol],
|
||||||
symbol: Symbol,
|
|
||||||
stored: &StoredValue,
|
stored: &StoredValue,
|
||||||
maybe_reused: Option<Symbol>,
|
maybe_reused: Option<Symbol>,
|
||||||
) {
|
) {
|
||||||
|
@ -1705,12 +1666,7 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
let (data_size, data_alignment) =
|
let (data_size, data_alignment) =
|
||||||
union_layout.data_size_and_alignment(self.layout_interner);
|
union_layout.data_size_and_alignment(self.layout_interner);
|
||||||
|
|
||||||
// We're going to use the pointer many times, so put it in a local variable
|
let (local_id, data_offset) = match stored {
|
||||||
let stored_with_local =
|
|
||||||
self.storage
|
|
||||||
.ensure_value_has_local(&mut self.code_builder, symbol, stored.to_owned());
|
|
||||||
|
|
||||||
let (local_id, data_offset) = match stored_with_local {
|
|
||||||
StoredValue::StackMemory { location, .. } => {
|
StoredValue::StackMemory { location, .. } => {
|
||||||
location.local_and_offset(self.storage.stack_frame_pointer)
|
location.local_and_offset(self.storage.stack_frame_pointer)
|
||||||
}
|
}
|
||||||
|
@ -1722,23 +1678,20 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
self.code_builder.if_();
|
self.code_builder.if_();
|
||||||
{
|
{
|
||||||
self.storage.load_symbols(&mut self.code_builder, &[reused]);
|
self.storage.load_symbols(&mut self.code_builder, &[reused]);
|
||||||
self.code_builder.set_local(local_id);
|
self.code_builder.set_local(*local_id);
|
||||||
}
|
}
|
||||||
self.code_builder.else_();
|
self.code_builder.else_();
|
||||||
{
|
{
|
||||||
self.allocate_with_refcount(Some(data_size), data_alignment, 1);
|
self.allocate_with_refcount(Some(data_size), data_alignment, 1);
|
||||||
self.code_builder.set_local(local_id);
|
self.code_builder.set_local(*local_id);
|
||||||
}
|
}
|
||||||
self.code_builder.end();
|
self.code_builder.end();
|
||||||
} else {
|
} else {
|
||||||
// Call the allocator to get a memory address.
|
// Call the allocator to get a memory address.
|
||||||
self.allocate_with_refcount(Some(data_size), data_alignment, 1);
|
self.allocate_with_refcount(Some(data_size), data_alignment, 1);
|
||||||
self.code_builder.set_local(local_id);
|
self.code_builder.set_local(*local_id);
|
||||||
}
|
}
|
||||||
(local_id, 0)
|
(*local_id, 0)
|
||||||
}
|
|
||||||
StoredValue::VirtualMachineStack { .. } => {
|
|
||||||
internal_error!("{:?} should have a local variable", symbol)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1792,7 +1745,6 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
&mut self,
|
&mut self,
|
||||||
structure: Symbol,
|
structure: Symbol,
|
||||||
union_layout: &UnionLayout<'a>,
|
union_layout: &UnionLayout<'a>,
|
||||||
tag_id_symbol: Symbol,
|
|
||||||
stored_value: &StoredValue,
|
stored_value: &StoredValue,
|
||||||
) {
|
) {
|
||||||
use UnionLayout::*;
|
use UnionLayout::*;
|
||||||
|
@ -1805,13 +1757,8 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
NullableWrapped { nullable_id, .. } => {
|
NullableWrapped { nullable_id, .. } => {
|
||||||
let stored_with_local = self.storage.ensure_value_has_local(
|
let local_id = match stored_value {
|
||||||
&mut self.code_builder,
|
StoredValue::Local { local_id, .. } => *local_id,
|
||||||
tag_id_symbol,
|
|
||||||
stored_value.to_owned(),
|
|
||||||
);
|
|
||||||
let local_id = match stored_with_local {
|
|
||||||
StoredValue::Local { local_id, .. } => local_id,
|
|
||||||
_ => internal_error!("ensure_value_has_local didn't work"),
|
_ => internal_error!("ensure_value_has_local didn't work"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1903,19 +1850,11 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
|
|
||||||
// Get pointer and offset to the tag's data
|
// Get pointer and offset to the tag's data
|
||||||
let structure_storage = self.storage.get(&structure).to_owned();
|
let structure_storage = self.storage.get(&structure).to_owned();
|
||||||
let stored_with_local = self.storage.ensure_value_has_local(
|
let (tag_local_id, tag_offset) = match structure_storage {
|
||||||
&mut self.code_builder,
|
|
||||||
structure,
|
|
||||||
structure_storage,
|
|
||||||
);
|
|
||||||
let (tag_local_id, tag_offset) = match stored_with_local {
|
|
||||||
StoredValue::StackMemory { location, .. } => {
|
StoredValue::StackMemory { location, .. } => {
|
||||||
location.local_and_offset(self.storage.stack_frame_pointer)
|
location.local_and_offset(self.storage.stack_frame_pointer)
|
||||||
}
|
}
|
||||||
StoredValue::Local { local_id, .. } => (local_id, 0),
|
StoredValue::Local { local_id, .. } => (local_id, 0),
|
||||||
StoredValue::VirtualMachineStack { .. } => {
|
|
||||||
internal_error!("{:?} should have a local variable", structure)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let stores_tag_id_in_pointer = union_layout.stores_tag_id_in_pointer(TARGET_INFO);
|
let stores_tag_id_in_pointer = union_layout.stores_tag_id_in_pointer(TARGET_INFO);
|
||||||
|
@ -1944,7 +1883,6 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
tag_id: TagIdIntType,
|
tag_id: TagIdIntType,
|
||||||
union_layout: &UnionLayout<'a>,
|
union_layout: &UnionLayout<'a>,
|
||||||
index: u64,
|
index: u64,
|
||||||
symbol: Symbol,
|
|
||||||
storage: &StoredValue,
|
storage: &StoredValue,
|
||||||
) {
|
) {
|
||||||
use UnionLayout::*;
|
use UnionLayout::*;
|
||||||
|
@ -1977,20 +1915,11 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
.sum();
|
.sum();
|
||||||
|
|
||||||
// Get pointer and offset to the tag's data
|
// Get pointer and offset to the tag's data
|
||||||
let structure_storage = self.storage.get(&structure).to_owned();
|
let (tag_local_id, tag_offset) = match self.storage.get(&structure) {
|
||||||
let stored_with_local = self.storage.ensure_value_has_local(
|
|
||||||
&mut self.code_builder,
|
|
||||||
structure,
|
|
||||||
structure_storage,
|
|
||||||
);
|
|
||||||
let (tag_local_id, tag_offset) = match stored_with_local {
|
|
||||||
StoredValue::StackMemory { location, .. } => {
|
StoredValue::StackMemory { location, .. } => {
|
||||||
location.local_and_offset(self.storage.stack_frame_pointer)
|
location.local_and_offset(self.storage.stack_frame_pointer)
|
||||||
}
|
}
|
||||||
StoredValue::Local { local_id, .. } => (local_id, 0),
|
StoredValue::Local { local_id, .. } => (*local_id, 0),
|
||||||
StoredValue::VirtualMachineStack { .. } => {
|
|
||||||
internal_error!("{:?} should have a local variable", structure)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let stores_tag_id_in_pointer = union_layout.stores_tag_id_in_pointer(TARGET_INFO);
|
let stores_tag_id_in_pointer = union_layout.stores_tag_id_in_pointer(TARGET_INFO);
|
||||||
|
@ -2007,12 +1936,8 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
self.code_builder.i32_const(from_offset as _);
|
self.code_builder.i32_const(from_offset as _);
|
||||||
self.code_builder.i32_add();
|
self.code_builder.i32_add();
|
||||||
|
|
||||||
let symbol_local = match self.storage.ensure_value_has_local(
|
let symbol_local = match storage {
|
||||||
&mut self.code_builder,
|
StoredValue::Local { local_id, .. } => *local_id,
|
||||||
symbol,
|
|
||||||
storage.clone(),
|
|
||||||
) {
|
|
||||||
StoredValue::Local { local_id, .. } => local_id,
|
|
||||||
_ => internal_error!("A heap pointer will always be an i32"),
|
_ => internal_error!("A heap pointer will always be an i32"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2025,11 +1950,6 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
|
|
||||||
pub(crate) fn ptr_load(&mut self, ret_sym: Symbol, arg_sym: Symbol) {
|
pub(crate) fn ptr_load(&mut self, ret_sym: Symbol, arg_sym: Symbol) {
|
||||||
let (from_addr_val, from_offset) = match self.storage.get(&arg_sym) {
|
let (from_addr_val, from_offset) = match self.storage.get(&arg_sym) {
|
||||||
StoredValue::VirtualMachineStack { .. } => {
|
|
||||||
self.storage
|
|
||||||
.load_symbols(&mut self.code_builder, &[arg_sym]);
|
|
||||||
(AddressValue::Loaded, 0)
|
|
||||||
}
|
|
||||||
StoredValue::Local { local_id, .. } => (AddressValue::NotLoaded(*local_id), 0),
|
StoredValue::Local { local_id, .. } => (AddressValue::NotLoaded(*local_id), 0),
|
||||||
StoredValue::StackMemory { location, .. } => {
|
StoredValue::StackMemory { location, .. } => {
|
||||||
let (local_id, offset) =
|
let (local_id, offset) =
|
||||||
|
@ -2063,7 +1983,7 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
) {
|
) {
|
||||||
if !self.can_relocate_heap {
|
if !self.can_relocate_heap {
|
||||||
// This will probably only happen for test hosts.
|
// This will probably only happen for test hosts.
|
||||||
panic!("The app tries to allocate heap memory but the host doesn't support that. It needs to export __heap_base");
|
panic!("The app tries to allocate heap memory but the host doesn't support that. It needs to export symbols __heap_base and __heap_end");
|
||||||
}
|
}
|
||||||
// Add extra bytes for the refcount
|
// Add extra bytes for the refcount
|
||||||
let extra_bytes = alignment_bytes.max(PTR_SIZE);
|
let extra_bytes = alignment_bytes.max(PTR_SIZE);
|
||||||
|
@ -2158,7 +2078,6 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
&mut self,
|
&mut self,
|
||||||
initializer: Option<Symbol>,
|
initializer: Option<Symbol>,
|
||||||
element_layout: InLayout<'a>,
|
element_layout: InLayout<'a>,
|
||||||
ret_symbol: Symbol,
|
|
||||||
ret_storage: &StoredValue,
|
ret_storage: &StoredValue,
|
||||||
) {
|
) {
|
||||||
// Alloca : a -> Ptr a
|
// Alloca : a -> Ptr a
|
||||||
|
@ -2181,12 +2100,8 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a local variable for the pointer
|
// create a local variable for the pointer
|
||||||
let ptr_local_id = match self.storage.ensure_value_has_local(
|
let ptr_local_id = match ret_storage {
|
||||||
&mut self.code_builder,
|
StoredValue::Local { local_id, .. } => *local_id,
|
||||||
ret_symbol,
|
|
||||||
ret_storage.clone(),
|
|
||||||
) {
|
|
||||||
StoredValue::Local { local_id, .. } => local_id,
|
|
||||||
_ => internal_error!("A pointer will always be an i32"),
|
_ => internal_error!("A pointer will always be an i32"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -152,6 +152,11 @@ impl<'a> CodeBuilder<'a> {
|
||||||
|
|
||||||
***********************************************************/
|
***********************************************************/
|
||||||
|
|
||||||
|
pub fn stack_is_empty(&self) -> bool {
|
||||||
|
let block = self.vm_block_stack.last().unwrap();
|
||||||
|
block.value_stack.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
fn current_stack(&self) -> &Vec<'a, Symbol> {
|
fn current_stack(&self) -> &Vec<'a, Symbol> {
|
||||||
let block = self.vm_block_stack.last().unwrap();
|
let block = self.vm_block_stack.last().unwrap();
|
||||||
&block.value_stack
|
&block.value_stack
|
||||||
|
|
|
@ -243,11 +243,11 @@ pub struct WasmDebugSettings {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DEBUG_SETTINGS: WasmDebugSettings = WasmDebugSettings {
|
pub const DEBUG_SETTINGS: WasmDebugSettings = WasmDebugSettings {
|
||||||
proc_start_end: false && cfg!(debug_assertions),
|
proc_start_end: true && cfg!(debug_assertions),
|
||||||
user_procs_ir: false && cfg!(debug_assertions), // Note: we also have `ROC_PRINT_IR_AFTER_REFCOUNT=1 cargo test-gen-wasm`
|
user_procs_ir: true && cfg!(debug_assertions), // Note: we also have `ROC_PRINT_IR_AFTER_REFCOUNT=1 cargo test-gen-wasm`
|
||||||
helper_procs_ir: false && cfg!(debug_assertions),
|
helper_procs_ir: false && cfg!(debug_assertions),
|
||||||
let_stmt_ir: false && cfg!(debug_assertions),
|
let_stmt_ir: true && cfg!(debug_assertions),
|
||||||
instructions: false && cfg!(debug_assertions),
|
instructions: true && cfg!(debug_assertions),
|
||||||
storage_map: false && cfg!(debug_assertions),
|
storage_map: false && cfg!(debug_assertions),
|
||||||
keep_test_binary: false && cfg!(debug_assertions),
|
keep_test_binary: false && cfg!(debug_assertions),
|
||||||
};
|
};
|
||||||
|
|
|
@ -103,7 +103,6 @@ impl From<&StoredValue> for CodeGenNumType {
|
||||||
fn from(stored: &StoredValue) -> CodeGenNumType {
|
fn from(stored: &StoredValue) -> CodeGenNumType {
|
||||||
use StoredValue::*;
|
use StoredValue::*;
|
||||||
match stored {
|
match stored {
|
||||||
VirtualMachineStack { value_type, .. } => CodeGenNumType::from(*value_type),
|
|
||||||
Local { value_type, .. } => CodeGenNumType::from(*value_type),
|
Local { value_type, .. } => CodeGenNumType::from(*value_type),
|
||||||
StackMemory { format, .. } => CodeGenNumType::from(*format),
|
StackMemory { format, .. } => CodeGenNumType::from(*format),
|
||||||
}
|
}
|
||||||
|
@ -2093,8 +2092,7 @@ impl<'a> LowLevelCall<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Unreachable => match self.ret_storage {
|
Unreachable => match self.ret_storage {
|
||||||
StoredValue::VirtualMachineStack { value_type, .. }
|
StoredValue::Local { value_type, .. } => match value_type {
|
||||||
| StoredValue::Local { value_type, .. } => match value_type {
|
|
||||||
ValueType::I32 => backend.code_builder.i32_const(0),
|
ValueType::I32 => backend.code_builder.i32_const(0),
|
||||||
ValueType::I64 => backend.code_builder.i64_const(0),
|
ValueType::I64 => backend.code_builder.i64_const(0),
|
||||||
ValueType::F32 => backend.code_builder.f32_const(0.0),
|
ValueType::F32 => backend.code_builder.f32_const(0.0),
|
||||||
|
@ -2189,7 +2187,7 @@ impl<'a> LowLevelCall<'a> {
|
||||||
use StoredValue::*;
|
use StoredValue::*;
|
||||||
|
|
||||||
match backend.storage.get(&self.arguments[0]).to_owned() {
|
match backend.storage.get(&self.arguments[0]).to_owned() {
|
||||||
VirtualMachineStack { value_type, .. } | Local { value_type, .. } => {
|
Local { value_type, .. } => {
|
||||||
self.load_args(backend);
|
self.load_args(backend);
|
||||||
match self.lowlevel {
|
match self.lowlevel {
|
||||||
LowLevel::Eq => match value_type {
|
LowLevel::Eq => match value_type {
|
||||||
|
@ -2299,7 +2297,7 @@ fn num_is_nan(backend: &mut WasmBackend<'_, '_>, argument: Symbol) {
|
||||||
use StoredValue::*;
|
use StoredValue::*;
|
||||||
let stored = backend.storage.get(&argument).to_owned();
|
let stored = backend.storage.get(&argument).to_owned();
|
||||||
match stored {
|
match stored {
|
||||||
VirtualMachineStack { value_type, .. } | Local { value_type, .. } => {
|
Local { value_type, .. } => {
|
||||||
backend
|
backend
|
||||||
.storage
|
.storage
|
||||||
.load_symbols(&mut backend.code_builder, &[argument]);
|
.load_symbols(&mut backend.code_builder, &[argument]);
|
||||||
|
@ -2362,7 +2360,7 @@ fn num_is_infinite(backend: &mut WasmBackend<'_, '_>, argument: Symbol) {
|
||||||
use StoredValue::*;
|
use StoredValue::*;
|
||||||
let stored = backend.storage.get(&argument).to_owned();
|
let stored = backend.storage.get(&argument).to_owned();
|
||||||
match stored {
|
match stored {
|
||||||
VirtualMachineStack { value_type, .. } | Local { value_type, .. } => {
|
Local { value_type, .. } => {
|
||||||
backend
|
backend
|
||||||
.storage
|
.storage
|
||||||
.load_symbols(&mut backend.code_builder, &[argument]);
|
.load_symbols(&mut backend.code_builder, &[argument]);
|
||||||
|
@ -2405,7 +2403,7 @@ fn num_is_finite(backend: &mut WasmBackend<'_, '_>, argument: Symbol) {
|
||||||
use StoredValue::*;
|
use StoredValue::*;
|
||||||
let stored = backend.storage.get(&argument).to_owned();
|
let stored = backend.storage.get(&argument).to_owned();
|
||||||
match stored {
|
match stored {
|
||||||
VirtualMachineStack { value_type, .. } | Local { value_type, .. } => {
|
Local { value_type, .. } => {
|
||||||
backend
|
backend
|
||||||
.storage
|
.storage
|
||||||
.load_symbols(&mut backend.code_builder, &[argument]);
|
.load_symbols(&mut backend.code_builder, &[argument]);
|
||||||
|
|
|
@ -6,7 +6,7 @@ use roc_error_macros::internal_error;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_mono::layout::{InLayout, STLayoutInterner};
|
use roc_mono::layout::{InLayout, STLayoutInterner};
|
||||||
|
|
||||||
use crate::code_builder::{CodeBuilder, VmSymbolState};
|
use crate::code_builder::CodeBuilder;
|
||||||
use crate::layout::{stack_memory_arg_types, ReturnMethod, StackMemoryFormat, WasmLayout};
|
use crate::layout::{stack_memory_arg_types, ReturnMethod, StackMemoryFormat, WasmLayout};
|
||||||
use crate::{copy_memory, CopyMemoryConfig, PTR_TYPE};
|
use crate::{copy_memory, CopyMemoryConfig, PTR_TYPE};
|
||||||
use roc_wasm_module::{round_up_to_alignment, Align, LocalId, ValueType};
|
use roc_wasm_module::{round_up_to_alignment, Align, LocalId, ValueType};
|
||||||
|
@ -33,13 +33,6 @@ impl StackMemoryLocation {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum StoredValue {
|
pub enum StoredValue {
|
||||||
/// A value stored implicitly in the VM stack (primitives only)
|
|
||||||
VirtualMachineStack {
|
|
||||||
vm_state: VmSymbolState,
|
|
||||||
value_type: ValueType,
|
|
||||||
size: u32,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// A local variable in the Wasm function (primitives only)
|
/// A local variable in the Wasm function (primitives only)
|
||||||
Local {
|
Local {
|
||||||
local_id: LocalId,
|
local_id: LocalId,
|
||||||
|
@ -63,14 +56,12 @@ impl StoredValue {
|
||||||
use ValueType::*;
|
use ValueType::*;
|
||||||
match self {
|
match self {
|
||||||
// Simple numbers: 1 Roc argument => 1 Wasm argument
|
// Simple numbers: 1 Roc argument => 1 Wasm argument
|
||||||
Self::VirtualMachineStack { value_type, .. } | Self::Local { value_type, .. } => {
|
Self::Local { value_type, .. } => match value_type {
|
||||||
match value_type {
|
I32 => &[I32],
|
||||||
I32 => &[I32],
|
I64 => &[I64],
|
||||||
I64 => &[I64],
|
F32 => &[F32],
|
||||||
F32 => &[F32],
|
F64 => &[F64],
|
||||||
F64 => &[F64],
|
},
|
||||||
}
|
|
||||||
}
|
|
||||||
// Stack memory values: 1 Roc argument => 0-2 Wasm arguments
|
// Stack memory values: 1 Roc argument => 0-2 Wasm arguments
|
||||||
Self::StackMemory { size, format, .. } => stack_memory_arg_types(*size, *format),
|
Self::StackMemory { size, format, .. } => stack_memory_arg_types(*size, *format),
|
||||||
}
|
}
|
||||||
|
@ -178,12 +169,15 @@ impl<'a> Storage<'a> {
|
||||||
self.symbol_layouts.insert(symbol, layout);
|
self.symbol_layouts.insert(symbol, layout);
|
||||||
|
|
||||||
let storage = match wasm_layout {
|
let storage = match wasm_layout {
|
||||||
WasmLayout::Primitive(value_type, size) => StoredValue::VirtualMachineStack {
|
WasmLayout::Primitive(value_type, size) => {
|
||||||
vm_state: VmSymbolState::NotYetPushed,
|
let local_id = self.get_next_local_id();
|
||||||
value_type,
|
self.local_types.push(value_type);
|
||||||
size,
|
StoredValue::Local {
|
||||||
},
|
local_id,
|
||||||
|
value_type,
|
||||||
|
size,
|
||||||
|
}
|
||||||
|
}
|
||||||
WasmLayout::StackMemory {
|
WasmLayout::StackMemory {
|
||||||
size,
|
size,
|
||||||
alignment_bytes,
|
alignment_bytes,
|
||||||
|
@ -323,41 +317,6 @@ impl<'a> Storage<'a> {
|
||||||
fn load_symbol_ccc(&mut self, code_builder: &mut CodeBuilder, sym: Symbol) {
|
fn load_symbol_ccc(&mut self, code_builder: &mut CodeBuilder, sym: Symbol) {
|
||||||
let storage = self.get(&sym).to_owned();
|
let storage = self.get(&sym).to_owned();
|
||||||
match storage {
|
match storage {
|
||||||
StoredValue::VirtualMachineStack {
|
|
||||||
vm_state,
|
|
||||||
value_type,
|
|
||||||
size,
|
|
||||||
} => {
|
|
||||||
let next_local_id = self.get_next_local_id();
|
|
||||||
let maybe_next_vm_state = code_builder.load_symbol(sym, vm_state, next_local_id);
|
|
||||||
match maybe_next_vm_state {
|
|
||||||
// The act of loading the value changed the VM state, so update it
|
|
||||||
Some(next_vm_state) => {
|
|
||||||
self.symbol_storage_map.insert(
|
|
||||||
sym,
|
|
||||||
StoredValue::VirtualMachineStack {
|
|
||||||
vm_state: next_vm_state,
|
|
||||||
value_type,
|
|
||||||
size,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
// Loading the value required creating a new local, because
|
|
||||||
// it was not in a convenient position in the VM stack.
|
|
||||||
self.local_types.push(value_type);
|
|
||||||
self.symbol_storage_map.insert(
|
|
||||||
sym,
|
|
||||||
StoredValue::Local {
|
|
||||||
local_id: next_local_id,
|
|
||||||
value_type,
|
|
||||||
size,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StoredValue::Local { local_id, .. } => {
|
StoredValue::Local { local_id, .. } => {
|
||||||
code_builder.get_local(local_id);
|
code_builder.get_local(local_id);
|
||||||
code_builder.set_top_symbol(sym);
|
code_builder.set_top_symbol(sym);
|
||||||
|
@ -409,7 +368,7 @@ impl<'a> Storage<'a> {
|
||||||
fn load_return_address_ccc(&mut self, code_builder: &mut CodeBuilder, sym: Symbol) {
|
fn load_return_address_ccc(&mut self, code_builder: &mut CodeBuilder, sym: Symbol) {
|
||||||
let storage = self.get(&sym).to_owned();
|
let storage = self.get(&sym).to_owned();
|
||||||
match storage {
|
match storage {
|
||||||
StoredValue::VirtualMachineStack { .. } | StoredValue::Local { .. } => {
|
StoredValue::Local { .. } => {
|
||||||
internal_error!("these storage types are not returned by writing to a pointer")
|
internal_error!("these storage types are not returned by writing to a pointer")
|
||||||
}
|
}
|
||||||
StoredValue::StackMemory { location, size, .. } => {
|
StoredValue::StackMemory { location, size, .. } => {
|
||||||
|
@ -530,10 +489,7 @@ impl<'a> Storage<'a> {
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
|
|
||||||
StoredValue::VirtualMachineStack {
|
StoredValue::Local {
|
||||||
value_type, size, ..
|
|
||||||
}
|
|
||||||
| StoredValue::Local {
|
|
||||||
value_type, size, ..
|
value_type, size, ..
|
||||||
} => {
|
} => {
|
||||||
use roc_wasm_module::Align::*;
|
use roc_wasm_module::Align::*;
|
||||||
|
@ -604,10 +560,7 @@ impl<'a> Storage<'a> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
StoredValue::VirtualMachineStack {
|
StoredValue::Local {
|
||||||
value_type, size, ..
|
|
||||||
}
|
|
||||||
| StoredValue::Local {
|
|
||||||
value_type, size, ..
|
value_type, size, ..
|
||||||
} => {
|
} => {
|
||||||
use roc_wasm_module::Align::*;
|
use roc_wasm_module::Align::*;
|
||||||
|
@ -646,31 +599,10 @@ impl<'a> Storage<'a> {
|
||||||
code_builder: &mut CodeBuilder,
|
code_builder: &mut CodeBuilder,
|
||||||
to: &StoredValue,
|
to: &StoredValue,
|
||||||
from: &StoredValue,
|
from: &StoredValue,
|
||||||
from_symbol: Symbol,
|
|
||||||
) {
|
) {
|
||||||
use StoredValue::*;
|
use StoredValue::*;
|
||||||
|
|
||||||
match (to, from) {
|
match (to, from) {
|
||||||
(
|
|
||||||
Local {
|
|
||||||
local_id: to_local_id,
|
|
||||||
value_type: to_value_type,
|
|
||||||
size: to_size,
|
|
||||||
},
|
|
||||||
VirtualMachineStack {
|
|
||||||
value_type: from_value_type,
|
|
||||||
size: from_size,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
debug_assert!(to_value_type == from_value_type);
|
|
||||||
debug_assert!(to_size == from_size);
|
|
||||||
// Note: load_symbols will not destroy the value, so we can use it again later.
|
|
||||||
// It will leave a Popped marker in the VM stack model in CodeBuilder
|
|
||||||
self.load_symbols(code_builder, &[from_symbol]);
|
|
||||||
code_builder.set_local(*to_local_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
(
|
(
|
||||||
Local {
|
Local {
|
||||||
local_id: to_local_id,
|
local_id: to_local_id,
|
||||||
|
@ -726,39 +658,4 @@ impl<'a> Storage<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensure a StoredValue has an associated local (which could be the frame pointer!)
|
|
||||||
///
|
|
||||||
/// This is useful when a value needs to be accessed from a more deeply-nested block.
|
|
||||||
/// In that case we want to make sure it's not just stored in the VM stack, because
|
|
||||||
/// blocks can't access the VM stack from outer blocks, but they can access locals.
|
|
||||||
/// (In the case of structs in stack memory, we just use the stack frame pointer local)
|
|
||||||
pub fn ensure_value_has_local(
|
|
||||||
&mut self,
|
|
||||||
code_builder: &mut CodeBuilder,
|
|
||||||
symbol: Symbol,
|
|
||||||
storage: StoredValue,
|
|
||||||
) -> StoredValue {
|
|
||||||
if let StoredValue::VirtualMachineStack {
|
|
||||||
vm_state,
|
|
||||||
value_type,
|
|
||||||
size,
|
|
||||||
} = storage
|
|
||||||
{
|
|
||||||
let next_local_id = self.get_next_local_id();
|
|
||||||
code_builder.store_symbol_to_local(symbol, vm_state, next_local_id);
|
|
||||||
|
|
||||||
self.local_types.push(value_type);
|
|
||||||
let new_storage = StoredValue::Local {
|
|
||||||
local_id: next_local_id,
|
|
||||||
value_type,
|
|
||||||
size,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.symbol_storage_map.insert(symbol, new_storage.clone());
|
|
||||||
new_storage
|
|
||||||
} else {
|
|
||||||
storage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -207,7 +207,7 @@ impl<'a> ImportDispatcher for TestDispatcher<'a> {
|
||||||
let msg = match panic_tag {
|
let msg = match panic_tag {
|
||||||
0 => format!(r#"Roc failed with message: "{roc_msg}""#),
|
0 => format!(r#"Roc failed with message: "{roc_msg}""#),
|
||||||
1 => format!(r#"User crash with message: "{roc_msg}""#),
|
1 => format!(r#"User crash with message: "{roc_msg}""#),
|
||||||
tag => format!(r#"Got an invald panic tag: "{panic_tag}""#),
|
_ => format!(r#"Got an invald panic tag: "{panic_tag}""#),
|
||||||
};
|
};
|
||||||
panic!("{}", msg)
|
panic!("{}", msg)
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue