mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 22:34:45 +00:00
Returning records on the stack from Wasm dev backend!
This commit is contained in:
parent
39fda3e675
commit
02bb9028ef
5 changed files with 504 additions and 410 deletions
|
@ -11,6 +11,7 @@ use roc_mono::ir::{CallType, Expr, JoinPointId, Literal, Proc, Stmt};
|
||||||
use roc_mono::layout::{Builtin, Layout};
|
use roc_mono::layout::{Builtin, Layout};
|
||||||
|
|
||||||
use crate::layout::WasmLayout;
|
use crate::layout::WasmLayout;
|
||||||
|
use crate::storage::SymbolStorage;
|
||||||
use crate::{allocate_stack_frame, copy_memory, free_stack_frame, LocalId, PTR_TYPE};
|
use crate::{allocate_stack_frame, copy_memory, free_stack_frame, LocalId, PTR_TYPE};
|
||||||
|
|
||||||
// Don't allocate any constant data at address zero or near it. Would be valid, but bug-prone.
|
// Don't allocate any constant data at address zero or near it. Would be valid, but bug-prone.
|
||||||
|
@ -20,54 +21,6 @@ const UNUSED_DATA_SECTION_BYTES: u32 = 1024;
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
struct LabelId(u32);
|
struct LabelId(u32);
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum SymbolStorage {
|
|
||||||
ParamPrimitive {
|
|
||||||
local_id: LocalId,
|
|
||||||
value_type: ValueType,
|
|
||||||
size: u32,
|
|
||||||
},
|
|
||||||
ParamPointer {
|
|
||||||
local_id: LocalId,
|
|
||||||
},
|
|
||||||
VarPrimitive {
|
|
||||||
local_id: LocalId,
|
|
||||||
value_type: ValueType,
|
|
||||||
size: u32,
|
|
||||||
},
|
|
||||||
VarStackMemory {
|
|
||||||
local_id: LocalId,
|
|
||||||
size: u32,
|
|
||||||
offset: u32,
|
|
||||||
},
|
|
||||||
VarHeapMemory {
|
|
||||||
local_id: LocalId,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SymbolStorage {
|
|
||||||
fn local_id(&self) -> LocalId {
|
|
||||||
match self {
|
|
||||||
Self::ParamPrimitive { local_id, .. } => *local_id,
|
|
||||||
Self::ParamPointer { local_id, .. } => *local_id,
|
|
||||||
Self::VarPrimitive { local_id, .. } => *local_id,
|
|
||||||
Self::VarStackMemory { local_id, .. } => *local_id,
|
|
||||||
Self::VarHeapMemory { local_id, .. } => *local_id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn value_type(&self) -> ValueType {
|
|
||||||
match self {
|
|
||||||
Self::ParamPrimitive { value_type, .. } => *value_type,
|
|
||||||
Self::VarPrimitive { value_type, .. } => *value_type,
|
|
||||||
Self::ParamPointer { .. } => ValueType::I32,
|
|
||||||
Self::VarStackMemory { .. } => ValueType::I32,
|
|
||||||
Self::VarHeapMemory { .. } => ValueType::I32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum LocalKind {
|
enum LocalKind {
|
||||||
Parameter,
|
Parameter,
|
||||||
Variable,
|
Variable,
|
||||||
|
@ -205,7 +158,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
wasm_layout: WasmLayout,
|
wasm_layout: WasmLayout,
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
kind: LocalKind,
|
kind: LocalKind,
|
||||||
) -> LocalId {
|
) -> SymbolStorage {
|
||||||
let local_index = (self.arg_types.len() + self.locals.len()) as u32;
|
let local_index = (self.arg_types.len() + self.locals.len()) as u32;
|
||||||
let local_id = LocalId(local_index);
|
let local_id = LocalId(local_index);
|
||||||
|
|
||||||
|
@ -219,7 +172,10 @@ impl<'a> WasmBackend<'a> {
|
||||||
value_type,
|
value_type,
|
||||||
size,
|
size,
|
||||||
},
|
},
|
||||||
_ => SymbolStorage::ParamPointer { local_id },
|
_ => SymbolStorage::ParamPointer {
|
||||||
|
local_id,
|
||||||
|
wasm_layout,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LocalKind::Variable => {
|
LocalKind::Variable => {
|
||||||
|
@ -242,11 +198,13 @@ impl<'a> WasmBackend<'a> {
|
||||||
let mut offset = self.stack_memory;
|
let mut offset = self.stack_memory;
|
||||||
offset += align - 1;
|
offset += align - 1;
|
||||||
offset &= -align;
|
offset &= -align;
|
||||||
self.stack_memory = offset + (size - alignment_bytes) as i32;
|
self.stack_memory = offset + size as i32;
|
||||||
|
|
||||||
|
// TODO: if we're creating the frame pointer just reuse the same local_id!
|
||||||
let frame_pointer = self.get_or_create_frame_pointer();
|
let frame_pointer = self.get_or_create_frame_pointer();
|
||||||
|
|
||||||
// initialise the local with the appropriate address
|
// initialise the local with the appropriate address
|
||||||
|
// TODO: skip this the first time, no point adding zero offset!
|
||||||
self.instructions.extend([
|
self.instructions.extend([
|
||||||
GetLocal(frame_pointer.0),
|
GetLocal(frame_pointer.0),
|
||||||
I32Const(offset),
|
I32Const(offset),
|
||||||
|
@ -258,15 +216,16 @@ impl<'a> WasmBackend<'a> {
|
||||||
local_id,
|
local_id,
|
||||||
size,
|
size,
|
||||||
offset: offset as u32,
|
offset: offset as u32,
|
||||||
|
alignment_bytes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.symbol_storage_map.insert(symbol, storage);
|
self.symbol_storage_map.insert(symbol, storage.clone());
|
||||||
|
|
||||||
local_id
|
storage
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_or_create_frame_pointer(&mut self) -> LocalId {
|
fn get_or_create_frame_pointer(&mut self) -> LocalId {
|
||||||
|
@ -334,17 +293,20 @@ impl<'a> WasmBackend<'a> {
|
||||||
// Saves us from having to copy it later
|
// Saves us from having to copy it later
|
||||||
let storage = SymbolStorage::ParamPointer {
|
let storage = SymbolStorage::ParamPointer {
|
||||||
local_id: LocalId(0),
|
local_id: LocalId(0),
|
||||||
|
wasm_layout,
|
||||||
};
|
};
|
||||||
self.symbol_storage_map.insert(*let_sym, storage);
|
self.symbol_storage_map.insert(*let_sym, storage);
|
||||||
}
|
}
|
||||||
self.build_expr(let_sym, expr, layout)?;
|
self.build_expr(let_sym, expr, layout)?;
|
||||||
self.instructions.push(Return);
|
self.instructions.push(Return); // TODO: branch instead of return so we can clean up stack
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
Stmt::Let(sym, expr, layout, following) => {
|
Stmt::Let(sym, expr, layout, following) => {
|
||||||
let wasm_layout = WasmLayout::new(layout);
|
let wasm_layout = WasmLayout::new(layout);
|
||||||
let local_id = self.insert_local(wasm_layout, *sym, LocalKind::Variable);
|
let local_id = self
|
||||||
|
.insert_local(wasm_layout, *sym, LocalKind::Variable)
|
||||||
|
.local_id();
|
||||||
|
|
||||||
self.build_expr(sym, expr, layout)?;
|
self.build_expr(sym, expr, layout)?;
|
||||||
self.instructions.push(SetLocal(local_id.0));
|
self.instructions.push(SetLocal(local_id.0));
|
||||||
|
@ -354,35 +316,37 @@ impl<'a> WasmBackend<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Stmt::Ret(sym) => {
|
Stmt::Ret(sym) => {
|
||||||
use crate::layout::WasmLayout::*;
|
use crate::storage::SymbolStorage::*;
|
||||||
|
|
||||||
let storage = self.symbol_storage_map.get(sym).unwrap();
|
let storage = self.symbol_storage_map.get(sym).unwrap();
|
||||||
|
|
||||||
match storage {
|
match storage {
|
||||||
SymbolStorage::ParamPrimitive { local_id, .. }
|
VarStackMemory {
|
||||||
| SymbolStorage::VarPrimitive { local_id, .. }
|
local_id,
|
||||||
| SymbolStorage::ParamPointer { local_id, .. }
|
size,
|
||||||
| SymbolStorage::VarHeapMemory { local_id, .. } => {
|
alignment_bytes,
|
||||||
self.instructions.push(GetLocal(local_id.0));
|
..
|
||||||
self.instructions.push(Return);
|
}
|
||||||
|
| ParamPointer {
|
||||||
|
local_id,
|
||||||
|
wasm_layout:
|
||||||
|
WasmLayout::StackMemory {
|
||||||
|
size,
|
||||||
|
alignment_bytes,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
} => {
|
||||||
|
let from = local_id.clone();
|
||||||
|
let to = LocalId(0);
|
||||||
|
copy_memory(&mut self.instructions, from, to, *size, *alignment_bytes, 0)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
SymbolStorage::VarStackMemory { local_id, size, .. } => {
|
ParamPrimitive { local_id, .. }
|
||||||
let ret_wasm_layout = WasmLayout::new(ret_layout);
|
| VarPrimitive { local_id, .. }
|
||||||
if let StackMemory { alignment_bytes, .. } = ret_wasm_layout {
|
| ParamPointer { local_id, .. }
|
||||||
let from = local_id.clone();
|
| VarHeapMemory { local_id, .. } => {
|
||||||
let to = LocalId(0);
|
self.instructions.push(GetLocal(local_id.0));
|
||||||
let copy_size: u32 = *size;
|
self.instructions.push(Return); // TODO: branch instead of return so we can clean up stack
|
||||||
copy_memory(
|
|
||||||
&mut self.instructions,
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
copy_size,
|
|
||||||
alignment_bytes,
|
|
||||||
)?;
|
|
||||||
} else {
|
|
||||||
panic!("Return layout doesn't match");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,8 +410,9 @@ impl<'a> WasmBackend<'a> {
|
||||||
let mut jp_parameter_local_ids = std::vec::Vec::with_capacity(parameters.len());
|
let mut jp_parameter_local_ids = std::vec::Vec::with_capacity(parameters.len());
|
||||||
for parameter in parameters.iter() {
|
for parameter in parameters.iter() {
|
||||||
let wasm_layout = WasmLayout::new(¶meter.layout);
|
let wasm_layout = WasmLayout::new(¶meter.layout);
|
||||||
let local_id =
|
let local_id = self
|
||||||
self.insert_local(wasm_layout, parameter.symbol, LocalKind::Variable);
|
.insert_local(wasm_layout, parameter.symbol, LocalKind::Variable)
|
||||||
|
.local_id();
|
||||||
|
|
||||||
jp_parameter_local_ids.push(local_id);
|
jp_parameter_local_ids.push(local_id);
|
||||||
}
|
}
|
||||||
|
@ -524,6 +489,8 @@ impl<'a> WasmBackend<'a> {
|
||||||
x => Err(format!("the call type, {:?}, is not yet implemented", x)),
|
x => Err(format!("the call type, {:?}, is not yet implemented", x)),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Expr::Struct(fields) => self.create_struct(sym, layout, fields),
|
||||||
|
|
||||||
x => Err(format!("Expression is not yet implemented {:?}", x)),
|
x => Err(format!("Expression is not yet implemented {:?}", x)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -560,6 +527,78 @@ impl<'a> WasmBackend<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_struct(
|
||||||
|
&mut self,
|
||||||
|
sym: &Symbol,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
fields: &'a [Symbol],
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let storage = self.get_symbol_storage(sym)?.to_owned();
|
||||||
|
|
||||||
|
if let Layout::Struct(field_layouts) = layout {
|
||||||
|
match storage {
|
||||||
|
SymbolStorage::VarStackMemory { local_id, size, .. }
|
||||||
|
| SymbolStorage::ParamPointer {
|
||||||
|
local_id,
|
||||||
|
wasm_layout: WasmLayout::StackMemory { size, .. },
|
||||||
|
} => {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(format!("Cannot create struct {:?} with storage {:?}", sym, storage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} 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)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn copy_symbol_to_pointer_at_offset(
|
||||||
|
&mut self,
|
||||||
|
to_ptr: LocalId,
|
||||||
|
to_offset: u32,
|
||||||
|
from_symbol: &Symbol,
|
||||||
|
) -> Result<u32, String> {
|
||||||
|
let from_storage = self.get_symbol_storage(from_symbol)?.to_owned();
|
||||||
|
from_storage.copy_to_memory(&mut self.instructions, to_ptr, to_offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn copy_storage(&mut self, to: &SymbolStorage, from: &SymbolStorage) -> Result<(), String> {
|
||||||
|
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().0));
|
||||||
|
self.instructions.push(SetLocal(to.local_id().0));
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
let (size, alignment_bytes) = from.stack_size_and_alignment();
|
||||||
|
copy_memory(
|
||||||
|
&mut self.instructions,
|
||||||
|
from.local_id(),
|
||||||
|
to.local_id(),
|
||||||
|
size,
|
||||||
|
alignment_bytes,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn build_call_low_level(
|
fn build_call_low_level(
|
||||||
&mut self,
|
&mut self,
|
||||||
lowlevel: &LowLevel,
|
lowlevel: &LowLevel,
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use parity_wasm::elements::{Instruction, Instruction::*, ValueType};
|
use parity_wasm::elements::ValueType;
|
||||||
use roc_mono::layout::{Layout, UnionLayout};
|
use roc_mono::layout::{Layout, UnionLayout};
|
||||||
|
|
||||||
use crate::{copy_memory, LocalId, ALIGN_1, ALIGN_2, ALIGN_4, ALIGN_8, PTR_SIZE, PTR_TYPE};
|
use crate::{PTR_SIZE, PTR_TYPE};
|
||||||
|
|
||||||
// See README for background information on Wasm locals, memory and function calls
|
// See README for background information on Wasm locals, memory and function calls
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum WasmLayout {
|
pub enum WasmLayout {
|
||||||
// Primitive number value. Just a Wasm local, without any stack memory.
|
// Primitive number value. Just a Wasm local, without any stack memory.
|
||||||
// For example, Roc i8 is represented as Wasm i32. Store the type and the original size.
|
// For example, Roc i8 is represented as Wasm i32. Store the type and the original size.
|
||||||
|
@ -79,81 +79,4 @@ impl WasmLayout {
|
||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn load(&self, offset: u32) -> Result<Instruction, String> {
|
|
||||||
use crate::layout::WasmLayout::*;
|
|
||||||
use ValueType::*;
|
|
||||||
|
|
||||||
match self {
|
|
||||||
LocalOnly(I32, 4) => Ok(I32Load(ALIGN_4, offset)),
|
|
||||||
LocalOnly(I32, 2) => Ok(I32Load16S(ALIGN_2, offset)),
|
|
||||||
LocalOnly(I32, 1) => Ok(I32Load8S(ALIGN_1, offset)),
|
|
||||||
LocalOnly(I64, 8) => Ok(I64Load(ALIGN_8, offset)),
|
|
||||||
LocalOnly(F64, 8) => Ok(F64Load(ALIGN_8, offset)),
|
|
||||||
LocalOnly(F32, 4) => Ok(F32Load(ALIGN_4, offset)),
|
|
||||||
|
|
||||||
// TODO: Come back to this when we need to access fields of structs
|
|
||||||
// LocalOnly(F32, 2) => Ok(), // convert F16 to F32 (lowlevel function? Wasm-only?)
|
|
||||||
// StackMemory(size) => Ok(), // would this be some kind of memcpy in the IR?
|
|
||||||
|
|
||||||
HeapMemory => {
|
|
||||||
if PTR_TYPE == I64 {
|
|
||||||
Ok(I64Load(ALIGN_8, offset))
|
|
||||||
} else {
|
|
||||||
Ok(I32Load(ALIGN_4, offset))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => Err(format!(
|
|
||||||
"Failed to generate load instruction for WasmLayout {:?}",
|
|
||||||
self
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn store(&self, offset: u32, instructions: &mut Vec<Instruction>) -> Result<(), String> {
|
|
||||||
use crate::layout::WasmLayout::*;
|
|
||||||
use ValueType::*;
|
|
||||||
|
|
||||||
let mut result = Ok(());
|
|
||||||
match self {
|
|
||||||
LocalOnly(I32, 4) => instructions.push(I32Store(ALIGN_4, offset)),
|
|
||||||
LocalOnly(I32, 2) => instructions.push(I32Store16(ALIGN_2, offset)),
|
|
||||||
LocalOnly(I32, 1) => instructions.push(I32Store8(ALIGN_1, offset)),
|
|
||||||
LocalOnly(I64, 8) => instructions.push(I64Store(ALIGN_8, offset)),
|
|
||||||
LocalOnly(F64, 8) => instructions.push(F64Store(ALIGN_8, offset)),
|
|
||||||
LocalOnly(F32, 4) => instructions.push(F32Store(ALIGN_4, offset)),
|
|
||||||
|
|
||||||
StackMemory {
|
|
||||||
size,
|
|
||||||
alignment_bytes,
|
|
||||||
} => {
|
|
||||||
// TODO
|
|
||||||
// Need extra arguments for this case that we don't need for primitives.
|
|
||||||
// Maybe it should be somewhere we have more relevant context?
|
|
||||||
// Come back to it when we need to insert things into structs.
|
|
||||||
let from_ptr = LocalId(0); // TODO
|
|
||||||
let to_ptr = LocalId(0); // TODO
|
|
||||||
copy_memory(instructions, from_ptr, to_ptr, *size, *alignment_bytes)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
HeapMemory => {
|
|
||||||
if PTR_TYPE == I64 {
|
|
||||||
instructions.push(I64Store(ALIGN_8, offset));
|
|
||||||
} else {
|
|
||||||
instructions.push(I32Store(ALIGN_4, offset));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {
|
|
||||||
result = Err(format!(
|
|
||||||
"Failed to generate store instruction for WasmLayout {:?}",
|
|
||||||
self
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
mod backend;
|
mod backend;
|
||||||
pub mod from_wasm32_memory;
|
pub mod from_wasm32_memory;
|
||||||
mod layout;
|
mod layout;
|
||||||
|
mod storage;
|
||||||
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use parity_wasm::builder;
|
use parity_wasm::builder;
|
||||||
|
@ -110,13 +111,13 @@ pub fn build_module_help<'a>(
|
||||||
Ok((backend.builder, main_function_index))
|
Ok((backend.builder, main_function_index))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_alignment(bytes: u32) -> Result<u32, String> {
|
fn encode_alignment(bytes: u32) -> u32 {
|
||||||
match bytes {
|
match bytes {
|
||||||
1 => Ok(ALIGN_1),
|
1 => ALIGN_1,
|
||||||
2 => Ok(ALIGN_2),
|
2 => ALIGN_2,
|
||||||
4 => Ok(ALIGN_4),
|
4 => ALIGN_4,
|
||||||
8 => Ok(ALIGN_8),
|
8 => ALIGN_8,
|
||||||
_ => Err(format!("{:?}-byte alignment is not supported", bytes)),
|
_ => panic!("{:?}-byte alignment is not supported", bytes),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,32 +125,32 @@ fn copy_memory(
|
||||||
instructions: &mut Vec<Instruction>,
|
instructions: &mut Vec<Instruction>,
|
||||||
from_ptr: LocalId,
|
from_ptr: LocalId,
|
||||||
to_ptr: LocalId,
|
to_ptr: LocalId,
|
||||||
size_with_alignment: u32,
|
size: u32,
|
||||||
alignment_bytes: u32,
|
alignment_bytes: u32,
|
||||||
|
offset: u32,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let alignment_flag = encode_alignment(alignment_bytes)?;
|
let alignment_flag = encode_alignment(alignment_bytes);
|
||||||
let size = size_with_alignment - alignment_bytes;
|
let mut current_offset = offset;
|
||||||
let mut offset = 0;
|
while size - current_offset >= 8 {
|
||||||
while size - offset >= 8 {
|
|
||||||
instructions.push(GetLocal(to_ptr.0));
|
instructions.push(GetLocal(to_ptr.0));
|
||||||
instructions.push(GetLocal(from_ptr.0));
|
instructions.push(GetLocal(from_ptr.0));
|
||||||
instructions.push(I64Load(alignment_flag, offset));
|
instructions.push(I64Load(alignment_flag, current_offset));
|
||||||
instructions.push(I64Store(alignment_flag, offset));
|
instructions.push(I64Store(alignment_flag, current_offset));
|
||||||
offset += 8;
|
current_offset += 8;
|
||||||
}
|
}
|
||||||
if size - offset >= 4 {
|
if size - current_offset >= 4 {
|
||||||
instructions.push(GetLocal(to_ptr.0));
|
instructions.push(GetLocal(to_ptr.0));
|
||||||
instructions.push(GetLocal(from_ptr.0));
|
instructions.push(GetLocal(from_ptr.0));
|
||||||
instructions.push(I32Load(alignment_flag, offset));
|
instructions.push(I32Load(alignment_flag, current_offset));
|
||||||
instructions.push(I32Store(alignment_flag, offset));
|
instructions.push(I32Store(alignment_flag, current_offset));
|
||||||
offset += 4;
|
current_offset += 4;
|
||||||
}
|
}
|
||||||
while size - offset > 0 {
|
while size - current_offset > 0 {
|
||||||
instructions.push(GetLocal(to_ptr.0));
|
instructions.push(GetLocal(to_ptr.0));
|
||||||
instructions.push(GetLocal(from_ptr.0));
|
instructions.push(GetLocal(from_ptr.0));
|
||||||
instructions.push(I32Load8U(alignment_flag, offset));
|
instructions.push(I32Load8U(alignment_flag, current_offset));
|
||||||
instructions.push(I32Store8(alignment_flag, offset));
|
instructions.push(I32Store8(alignment_flag, current_offset));
|
||||||
offset += 1;
|
current_offset += 1;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
156
compiler/gen_wasm/src/storage.rs
Normal file
156
compiler/gen_wasm/src/storage.rs
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
use crate::{copy_memory, layout::WasmLayout, LocalId, ALIGN_1, ALIGN_2, ALIGN_4, ALIGN_8};
|
||||||
|
use parity_wasm::elements::{Instruction, Instruction::*, ValueType};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum SymbolStorage {
|
||||||
|
ParamPrimitive {
|
||||||
|
local_id: LocalId,
|
||||||
|
value_type: ValueType,
|
||||||
|
size: u32,
|
||||||
|
},
|
||||||
|
ParamPointer {
|
||||||
|
local_id: LocalId,
|
||||||
|
wasm_layout: WasmLayout,
|
||||||
|
},
|
||||||
|
VarPrimitive {
|
||||||
|
local_id: LocalId,
|
||||||
|
value_type: ValueType,
|
||||||
|
size: u32,
|
||||||
|
},
|
||||||
|
VarStackMemory {
|
||||||
|
local_id: LocalId,
|
||||||
|
size: u32,
|
||||||
|
offset: u32,
|
||||||
|
alignment_bytes: u32,
|
||||||
|
},
|
||||||
|
VarHeapMemory {
|
||||||
|
local_id: LocalId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SymbolStorage {
|
||||||
|
pub fn local_id(&self) -> LocalId {
|
||||||
|
match self {
|
||||||
|
Self::ParamPrimitive { local_id, .. } => *local_id,
|
||||||
|
Self::ParamPointer { local_id, .. } => *local_id,
|
||||||
|
Self::VarPrimitive { local_id, .. } => *local_id,
|
||||||
|
Self::VarStackMemory { local_id, .. } => *local_id,
|
||||||
|
Self::VarHeapMemory { local_id, .. } => *local_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn value_type(&self) -> ValueType {
|
||||||
|
match self {
|
||||||
|
Self::ParamPrimitive { value_type, .. } => *value_type,
|
||||||
|
Self::VarPrimitive { value_type, .. } => *value_type,
|
||||||
|
Self::ParamPointer { .. } => ValueType::I32,
|
||||||
|
Self::VarStackMemory { .. } => ValueType::I32,
|
||||||
|
Self::VarHeapMemory { .. } => ValueType::I32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_stack_memory(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::ParamPointer {
|
||||||
|
wasm_layout: WasmLayout::StackMemory { .. },
|
||||||
|
..
|
||||||
|
} => true,
|
||||||
|
Self::ParamPointer { .. } => false,
|
||||||
|
Self::VarStackMemory { .. } => true,
|
||||||
|
Self::ParamPrimitive { .. } => false,
|
||||||
|
Self::VarPrimitive { .. } => false,
|
||||||
|
Self::VarHeapMemory { .. } => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stack_size_and_alignment(&self) -> (u32, u32) {
|
||||||
|
match self {
|
||||||
|
Self::VarStackMemory {
|
||||||
|
size,
|
||||||
|
alignment_bytes,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| Self::ParamPointer {
|
||||||
|
wasm_layout:
|
||||||
|
WasmLayout::StackMemory {
|
||||||
|
size,
|
||||||
|
alignment_bytes,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
} => (*size, *alignment_bytes),
|
||||||
|
|
||||||
|
_ => (0, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn copy_to_memory(
|
||||||
|
&self,
|
||||||
|
instructions: &mut Vec<Instruction>,
|
||||||
|
to_pointer: LocalId,
|
||||||
|
to_offset: u32,
|
||||||
|
) -> Result<u32, String> {
|
||||||
|
match self {
|
||||||
|
Self::ParamPrimitive {
|
||||||
|
local_id,
|
||||||
|
value_type,
|
||||||
|
size,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| Self::VarPrimitive {
|
||||||
|
local_id,
|
||||||
|
value_type,
|
||||||
|
size,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let store_instruction = match (value_type, size) {
|
||||||
|
(ValueType::I64, 8) => I64Store(ALIGN_8, to_offset),
|
||||||
|
(ValueType::I32, 4) => I32Store(ALIGN_4, to_offset),
|
||||||
|
(ValueType::I32, 2) => I32Store16(ALIGN_2, to_offset),
|
||||||
|
(ValueType::I32, 1) => I32Store8(ALIGN_1, to_offset),
|
||||||
|
(ValueType::F32, 4) => F32Store(ALIGN_4, to_offset),
|
||||||
|
(ValueType::F64, 8) => F64Store(ALIGN_8, to_offset),
|
||||||
|
_ => {
|
||||||
|
return Err(format!("Cannot store {:?} with alignment of {:?}", value_type, size));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
instructions.push(GetLocal(to_pointer.0));
|
||||||
|
instructions.push(GetLocal(local_id.0));
|
||||||
|
instructions.push(store_instruction);
|
||||||
|
Ok(*size)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::ParamPointer {
|
||||||
|
local_id,
|
||||||
|
wasm_layout:
|
||||||
|
WasmLayout::StackMemory {
|
||||||
|
size,
|
||||||
|
alignment_bytes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
| Self::VarStackMemory {
|
||||||
|
local_id,
|
||||||
|
size,
|
||||||
|
alignment_bytes,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
copy_memory(
|
||||||
|
instructions,
|
||||||
|
*local_id,
|
||||||
|
to_pointer,
|
||||||
|
*size,
|
||||||
|
*alignment_bytes,
|
||||||
|
to_offset,
|
||||||
|
)?;
|
||||||
|
Ok(*size)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::ParamPointer { local_id, .. } | Self::VarHeapMemory { local_id, .. } => {
|
||||||
|
instructions.push(GetLocal(to_pointer.0));
|
||||||
|
instructions.push(GetLocal(local_id.0));
|
||||||
|
instructions.push(I32Store(ALIGN_4, to_offset));
|
||||||
|
Ok(4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -308,125 +308,100 @@ mod wasm_records {
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// #[test]
|
#[test]
|
||||||
// fn i64_record1_literal() {
|
fn i64_record1_literal() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(
|
indoc!(
|
||||||
// r#"
|
r#"
|
||||||
// { x: 3 }
|
{ x: 3 }
|
||||||
// "#
|
"#
|
||||||
// ),
|
),
|
||||||
// 3,
|
3,
|
||||||
// i64
|
i64
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn i64_record2_literal() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// { x: 3, y: 5 }
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// (3, 5),
|
|
||||||
// (i64, i64)
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // #[test]
|
|
||||||
// // fn i64_record3_literal() {
|
|
||||||
// // assert_evals_to!(
|
|
||||||
// // indoc!(
|
|
||||||
// // r#"
|
|
||||||
// // { x: 3, y: 5, z: 17 }
|
|
||||||
// // "#
|
|
||||||
// // ),
|
|
||||||
// // (3, 5, 17),
|
|
||||||
// // (i64, i64, i64)
|
|
||||||
// // );
|
|
||||||
// // }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn f64_record2_literal() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// { x: 3.1, y: 5.1 }
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// (3.1, 5.1),
|
|
||||||
// (f64, f64)
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // #[test]
|
|
||||||
// // fn f64_record3_literal() {
|
|
||||||
// // assert_evals_to!(
|
|
||||||
// // indoc!(
|
|
||||||
// // r#"
|
|
||||||
// // { x: 3.1, y: 5.1, z: 17.1 }
|
|
||||||
// // "#
|
|
||||||
// // ),
|
|
||||||
// // (3.1, 5.1, 17.1),
|
|
||||||
// // (f64, f64, f64)
|
|
||||||
// // );
|
|
||||||
// // }
|
|
||||||
|
|
||||||
// // #[test]
|
|
||||||
// // fn bool_record4_literal() {
|
|
||||||
// // assert_evals_to!(
|
|
||||||
// // indoc!(
|
|
||||||
// // r#"
|
|
||||||
// // record : { a : Bool, b : Bool, c : Bool, d : Bool }
|
|
||||||
// // record = { a: True, b: True, c : True, d : Bool }
|
|
||||||
|
|
||||||
// // record
|
|
||||||
// // "#
|
|
||||||
// // ),
|
|
||||||
// // (true, false, false, true),
|
|
||||||
// // (bool, bool, bool, bool)
|
|
||||||
// // );
|
|
||||||
// // }
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn i64_record1_literal() {
|
fn i64_record2_literal() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
{ a: 3 }
|
{ x: 3, y: 5 }
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
3,
|
(3, 5),
|
||||||
i64
|
(i64, i64)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// // #[test]
|
#[test]
|
||||||
// // fn i64_record9_literal() {
|
fn i64_record3_literal() {
|
||||||
// // assert_evals_to!(
|
assert_evals_to!(
|
||||||
// // indoc!(
|
indoc!(
|
||||||
// // r#"
|
r#"
|
||||||
// // { a: 3, b: 5, c: 17, d: 1, e: 9, f: 12, g: 13, h: 14, i: 15 }
|
{ x: 3, y: 5, z: 17 }
|
||||||
// // "#
|
"#
|
||||||
// // ),
|
),
|
||||||
// // (3, 5, 17, 1, 9, 12, 13, 14, 15),
|
(3, 5, 17),
|
||||||
// // (i64, i64, i64, i64, i64, i64, i64, i64, i64)
|
(i64, i64, i64)
|
||||||
// // );
|
);
|
||||||
// // }
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn f64_record2_literal() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ x: 3.1, y: 5.1 }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
(3.1, 5.1),
|
||||||
|
(f64, f64)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn f64_record3_literal() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ x: 3.1, y: 5.1, z: 17.1 }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
(3.1, 5.1, 17.1),
|
||||||
|
(f64, f64, f64)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bool_record4_literal() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
record : { a : Bool, b : Bool, c : Bool, d : Bool }
|
||||||
|
record = { a: True, b: False, c : False, d : True }
|
||||||
|
|
||||||
|
record
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
[true, false, false, true],
|
||||||
|
[bool; 4]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn i64_record9_literal() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ a: 3, b: 5, c: 17, d: 1, e: 9, f: 12, g: 13, h: 14, i: 15 }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
[3, 5, 17, 1, 9, 12, 13, 14, 15],
|
||||||
|
[i64; 9]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// // #[test]
|
|
||||||
// // fn f64_record3_literal() {
|
|
||||||
// // assert_evals_to!(
|
|
||||||
// // indoc!(
|
|
||||||
// // r#"
|
|
||||||
// // { x: 3.1, y: 5.1, z: 17.1 }
|
|
||||||
// // "#
|
|
||||||
// // ),
|
|
||||||
// // (3.1, 5.1, 17.1),
|
|
||||||
// // (f64, f64, f64)
|
|
||||||
// // );
|
|
||||||
// // }
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bool_literal() {
|
fn bool_literal() {
|
||||||
|
@ -667,135 +642,135 @@ mod wasm_records {
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn return_record_2() {
|
fn return_record_2() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(
|
indoc!(
|
||||||
// r#"
|
r#"
|
||||||
// { x: 3, y: 5 }
|
{ x: 3, y: 5 }
|
||||||
// "#
|
"#
|
||||||
// ),
|
),
|
||||||
// [3, 5],
|
[3, 5],
|
||||||
// [i64; 2]
|
[i64; 2]
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn return_record_3() {
|
fn return_record_3() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(
|
indoc!(
|
||||||
// r#"
|
r#"
|
||||||
// { x: 3, y: 5, z: 4 }
|
{ x: 3, y: 5, z: 4 }
|
||||||
// "#
|
"#
|
||||||
// ),
|
),
|
||||||
// (3, 5, 4),
|
(3, 5, 4),
|
||||||
// (i64, i64, i64)
|
(i64, i64, i64)
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn return_record_4() {
|
fn return_record_4() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(
|
indoc!(
|
||||||
// r#"
|
r#"
|
||||||
// { a: 3, b: 5, c: 4, d: 2 }
|
{ a: 3, b: 5, c: 4, d: 2 }
|
||||||
// "#
|
"#
|
||||||
// ),
|
),
|
||||||
// [3, 5, 4, 2],
|
[3, 5, 4, 2],
|
||||||
// [i64; 4]
|
[i64; 4]
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn return_record_5() {
|
fn return_record_5() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(
|
indoc!(
|
||||||
// r#"
|
r#"
|
||||||
// { a: 3, b: 5, c: 4, d: 2, e: 1 }
|
{ a: 3, b: 5, c: 4, d: 2, e: 1 }
|
||||||
// "#
|
"#
|
||||||
// ),
|
),
|
||||||
// [3, 5, 4, 2, 1],
|
[3, 5, 4, 2, 1],
|
||||||
// [i64; 5]
|
[i64; 5]
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn return_record_6() {
|
fn return_record_6() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(
|
indoc!(
|
||||||
// r#"
|
r#"
|
||||||
// { a: 3, b: 5, c: 4, d: 2, e: 1, f: 7 }
|
{ a: 3, b: 5, c: 4, d: 2, e: 1, f: 7 }
|
||||||
// "#
|
"#
|
||||||
// ),
|
),
|
||||||
// [3, 5, 4, 2, 1, 7],
|
[3, 5, 4, 2, 1, 7],
|
||||||
// [i64; 6]
|
[i64; 6]
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn return_record_7() {
|
fn return_record_7() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(
|
indoc!(
|
||||||
// r#"
|
r#"
|
||||||
// { a: 3, b: 5, c: 4, d: 2, e: 1, f: 7, g: 8 }
|
{ a: 3, b: 5, c: 4, d: 2, e: 1, f: 7, g: 8 }
|
||||||
// "#
|
"#
|
||||||
// ),
|
),
|
||||||
// [3, 5, 4, 2, 1, 7, 8],
|
[3, 5, 4, 2, 1, 7, 8],
|
||||||
// [i64; 7]
|
[i64; 7]
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn return_record_float_int() {
|
fn return_record_float_int() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(
|
indoc!(
|
||||||
// r#"
|
r#"
|
||||||
// { a: 3.14, b: 0x1 }
|
{ a: 3.14, b: 0x1 }
|
||||||
// "#
|
"#
|
||||||
// ),
|
),
|
||||||
// (3.14, 0x1),
|
(3.14, 0x1),
|
||||||
// (f64, i64)
|
(f64, i64)
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn return_record_int_float() {
|
fn return_record_int_float() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(
|
indoc!(
|
||||||
// r#"
|
r#"
|
||||||
// { a: 0x1, b: 3.14 }
|
{ a: 0x1, b: 3.14 }
|
||||||
// "#
|
"#
|
||||||
// ),
|
),
|
||||||
// (0x1, 3.14),
|
(0x1, 3.14),
|
||||||
// (i64, f64)
|
(i64, f64)
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn return_record_float_float() {
|
fn return_record_float_float() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(
|
indoc!(
|
||||||
// r#"
|
r#"
|
||||||
// { a: 6.28, b: 3.14 }
|
{ a: 6.28, b: 3.14 }
|
||||||
// "#
|
"#
|
||||||
// ),
|
),
|
||||||
// (6.28, 3.14),
|
(6.28, 3.14),
|
||||||
// (f64, f64)
|
(f64, f64)
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn return_record_float_float_float() {
|
fn return_record_float_float_float() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(
|
indoc!(
|
||||||
// r#"
|
r#"
|
||||||
// { a: 6.28, b: 3.14, c: 0.1 }
|
{ a: 6.28, b: 3.14, c: 0.1 }
|
||||||
// "#
|
"#
|
||||||
// ),
|
),
|
||||||
// (6.28, 3.14, 0.1),
|
(6.28, 3.14, 0.1),
|
||||||
// (f64, f64, f64)
|
(f64, f64, f64)
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
// #[test]
|
||||||
// fn return_nested_record() {
|
// fn return_nested_record() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue