mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-21 15:59:20 +03:00
Returning records on the stack from Wasm dev backend!
This commit is contained in:
parent
39fda3e675
commit
02bb9028ef
@ -11,6 +11,7 @@ use roc_mono::ir::{CallType, Expr, JoinPointId, Literal, Proc, Stmt};
|
||||
use roc_mono::layout::{Builtin, Layout};
|
||||
|
||||
use crate::layout::WasmLayout;
|
||||
use crate::storage::SymbolStorage;
|
||||
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.
|
||||
@ -20,54 +21,6 @@ const UNUSED_DATA_SECTION_BYTES: u32 = 1024;
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
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 {
|
||||
Parameter,
|
||||
Variable,
|
||||
@ -205,7 +158,7 @@ impl<'a> WasmBackend<'a> {
|
||||
wasm_layout: WasmLayout,
|
||||
symbol: Symbol,
|
||||
kind: LocalKind,
|
||||
) -> LocalId {
|
||||
) -> SymbolStorage {
|
||||
let local_index = (self.arg_types.len() + self.locals.len()) as u32;
|
||||
let local_id = LocalId(local_index);
|
||||
|
||||
@ -219,7 +172,10 @@ impl<'a> WasmBackend<'a> {
|
||||
value_type,
|
||||
size,
|
||||
},
|
||||
_ => SymbolStorage::ParamPointer { local_id },
|
||||
_ => SymbolStorage::ParamPointer {
|
||||
local_id,
|
||||
wasm_layout,
|
||||
},
|
||||
}
|
||||
}
|
||||
LocalKind::Variable => {
|
||||
@ -242,11 +198,13 @@ impl<'a> WasmBackend<'a> {
|
||||
let mut offset = self.stack_memory;
|
||||
offset += align - 1;
|
||||
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();
|
||||
|
||||
// initialise the local with the appropriate address
|
||||
// TODO: skip this the first time, no point adding zero offset!
|
||||
self.instructions.extend([
|
||||
GetLocal(frame_pointer.0),
|
||||
I32Const(offset),
|
||||
@ -258,15 +216,16 @@ impl<'a> WasmBackend<'a> {
|
||||
local_id,
|
||||
size,
|
||||
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 {
|
||||
@ -334,17 +293,20 @@ impl<'a> WasmBackend<'a> {
|
||||
// Saves us from having to copy it later
|
||||
let storage = SymbolStorage::ParamPointer {
|
||||
local_id: LocalId(0),
|
||||
wasm_layout,
|
||||
};
|
||||
self.symbol_storage_map.insert(*let_sym, storage);
|
||||
}
|
||||
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(())
|
||||
}
|
||||
|
||||
Stmt::Let(sym, expr, layout, following) => {
|
||||
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.instructions.push(SetLocal(local_id.0));
|
||||
@ -354,35 +316,37 @@ impl<'a> WasmBackend<'a> {
|
||||
}
|
||||
|
||||
Stmt::Ret(sym) => {
|
||||
use crate::layout::WasmLayout::*;
|
||||
use crate::storage::SymbolStorage::*;
|
||||
|
||||
let storage = self.symbol_storage_map.get(sym).unwrap();
|
||||
|
||||
match storage {
|
||||
SymbolStorage::ParamPrimitive { local_id, .. }
|
||||
| SymbolStorage::VarPrimitive { local_id, .. }
|
||||
| SymbolStorage::ParamPointer { local_id, .. }
|
||||
| SymbolStorage::VarHeapMemory { local_id, .. } => {
|
||||
self.instructions.push(GetLocal(local_id.0));
|
||||
self.instructions.push(Return);
|
||||
VarStackMemory {
|
||||
local_id,
|
||||
size,
|
||||
alignment_bytes,
|
||||
..
|
||||
}
|
||||
| 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, .. } => {
|
||||
let ret_wasm_layout = WasmLayout::new(ret_layout);
|
||||
if let StackMemory { alignment_bytes, .. } = ret_wasm_layout {
|
||||
let from = local_id.clone();
|
||||
let to = LocalId(0);
|
||||
let copy_size: u32 = *size;
|
||||
copy_memory(
|
||||
&mut self.instructions,
|
||||
from,
|
||||
to,
|
||||
copy_size,
|
||||
alignment_bytes,
|
||||
)?;
|
||||
} else {
|
||||
panic!("Return layout doesn't match");
|
||||
}
|
||||
ParamPrimitive { local_id, .. }
|
||||
| VarPrimitive { local_id, .. }
|
||||
| ParamPointer { local_id, .. }
|
||||
| VarHeapMemory { local_id, .. } => {
|
||||
self.instructions.push(GetLocal(local_id.0));
|
||||
self.instructions.push(Return); // TODO: branch instead of return so we can clean up stack
|
||||
}
|
||||
}
|
||||
|
||||
@ -446,8 +410,9 @@ 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(¶meter.layout);
|
||||
let local_id =
|
||||
self.insert_local(wasm_layout, parameter.symbol, LocalKind::Variable);
|
||||
let local_id = self
|
||||
.insert_local(wasm_layout, parameter.symbol, LocalKind::Variable)
|
||||
.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)),
|
||||
},
|
||||
|
||||
Expr::Struct(fields) => self.create_struct(sym, layout, fields),
|
||||
|
||||
x => Err(format!("Expression is not yet implemented {:?}", x)),
|
||||
}
|
||||
}
|
||||
@ -560,6 +527,78 @@ impl<'a> WasmBackend<'a> {
|
||||
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(
|
||||
&mut self,
|
||||
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 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
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum WasmLayout {
|
||||
// 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.
|
||||
@ -79,81 +79,4 @@ impl WasmLayout {
|
||||
_ => 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;
|
||||
pub mod from_wasm32_memory;
|
||||
mod layout;
|
||||
mod storage;
|
||||
|
||||
use bumpalo::Bump;
|
||||
use parity_wasm::builder;
|
||||
@ -110,13 +111,13 @@ pub fn build_module_help<'a>(
|
||||
Ok((backend.builder, main_function_index))
|
||||
}
|
||||
|
||||
fn encode_alignment(bytes: u32) -> Result<u32, String> {
|
||||
fn encode_alignment(bytes: u32) -> u32 {
|
||||
match bytes {
|
||||
1 => Ok(ALIGN_1),
|
||||
2 => Ok(ALIGN_2),
|
||||
4 => Ok(ALIGN_4),
|
||||
8 => Ok(ALIGN_8),
|
||||
_ => Err(format!("{:?}-byte alignment is not supported", bytes)),
|
||||
1 => ALIGN_1,
|
||||
2 => ALIGN_2,
|
||||
4 => ALIGN_4,
|
||||
8 => ALIGN_8,
|
||||
_ => panic!("{:?}-byte alignment is not supported", bytes),
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,32 +125,32 @@ fn copy_memory(
|
||||
instructions: &mut Vec<Instruction>,
|
||||
from_ptr: LocalId,
|
||||
to_ptr: LocalId,
|
||||
size_with_alignment: u32,
|
||||
size: u32,
|
||||
alignment_bytes: u32,
|
||||
offset: u32,
|
||||
) -> Result<(), String> {
|
||||
let alignment_flag = encode_alignment(alignment_bytes)?;
|
||||
let size = size_with_alignment - alignment_bytes;
|
||||
let mut offset = 0;
|
||||
while size - offset >= 8 {
|
||||
let alignment_flag = encode_alignment(alignment_bytes);
|
||||
let mut current_offset = offset;
|
||||
while size - current_offset >= 8 {
|
||||
instructions.push(GetLocal(to_ptr.0));
|
||||
instructions.push(GetLocal(from_ptr.0));
|
||||
instructions.push(I64Load(alignment_flag, offset));
|
||||
instructions.push(I64Store(alignment_flag, offset));
|
||||
offset += 8;
|
||||
instructions.push(I64Load(alignment_flag, current_offset));
|
||||
instructions.push(I64Store(alignment_flag, current_offset));
|
||||
current_offset += 8;
|
||||
}
|
||||
if size - offset >= 4 {
|
||||
if size - current_offset >= 4 {
|
||||
instructions.push(GetLocal(to_ptr.0));
|
||||
instructions.push(GetLocal(from_ptr.0));
|
||||
instructions.push(I32Load(alignment_flag, offset));
|
||||
instructions.push(I32Store(alignment_flag, offset));
|
||||
offset += 4;
|
||||
instructions.push(I32Load(alignment_flag, current_offset));
|
||||
instructions.push(I32Store(alignment_flag, current_offset));
|
||||
current_offset += 4;
|
||||
}
|
||||
while size - offset > 0 {
|
||||
while size - current_offset > 0 {
|
||||
instructions.push(GetLocal(to_ptr.0));
|
||||
instructions.push(GetLocal(from_ptr.0));
|
||||
instructions.push(I32Load8U(alignment_flag, offset));
|
||||
instructions.push(I32Store8(alignment_flag, offset));
|
||||
offset += 1;
|
||||
instructions.push(I32Load8U(alignment_flag, current_offset));
|
||||
instructions.push(I32Store8(alignment_flag, current_offset));
|
||||
current_offset += 1;
|
||||
}
|
||||
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]
|
||||
// fn i64_record1_literal() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { x: 3 }
|
||||
// "#
|
||||
// ),
|
||||
// 3,
|
||||
// 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]
|
||||
fn i64_record1_literal() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ x: 3 }
|
||||
"#
|
||||
),
|
||||
3,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i64_record1_literal() {
|
||||
fn i64_record2_literal() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ a: 3 }
|
||||
{ x: 3, y: 5 }
|
||||
"#
|
||||
),
|
||||
3,
|
||||
i64
|
||||
(3, 5),
|
||||
(i64, i64)
|
||||
);
|
||||
}
|
||||
|
||||
// // #[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, i64, i64, i64, i64, i64, i64, 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: 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]
|
||||
fn bool_literal() {
|
||||
@ -667,135 +642,135 @@ mod wasm_records {
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn return_record_2() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { x: 3, y: 5 }
|
||||
// "#
|
||||
// ),
|
||||
// [3, 5],
|
||||
// [i64; 2]
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn return_record_2() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ x: 3, y: 5 }
|
||||
"#
|
||||
),
|
||||
[3, 5],
|
||||
[i64; 2]
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn return_record_3() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { x: 3, y: 5, z: 4 }
|
||||
// "#
|
||||
// ),
|
||||
// (3, 5, 4),
|
||||
// (i64, i64, i64)
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn return_record_3() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ x: 3, y: 5, z: 4 }
|
||||
"#
|
||||
),
|
||||
(3, 5, 4),
|
||||
(i64, i64, i64)
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn return_record_4() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { a: 3, b: 5, c: 4, d: 2 }
|
||||
// "#
|
||||
// ),
|
||||
// [3, 5, 4, 2],
|
||||
// [i64; 4]
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn return_record_4() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ a: 3, b: 5, c: 4, d: 2 }
|
||||
"#
|
||||
),
|
||||
[3, 5, 4, 2],
|
||||
[i64; 4]
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn return_record_5() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { a: 3, b: 5, c: 4, d: 2, e: 1 }
|
||||
// "#
|
||||
// ),
|
||||
// [3, 5, 4, 2, 1],
|
||||
// [i64; 5]
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn return_record_5() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ a: 3, b: 5, c: 4, d: 2, e: 1 }
|
||||
"#
|
||||
),
|
||||
[3, 5, 4, 2, 1],
|
||||
[i64; 5]
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn return_record_6() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { a: 3, b: 5, c: 4, d: 2, e: 1, f: 7 }
|
||||
// "#
|
||||
// ),
|
||||
// [3, 5, 4, 2, 1, 7],
|
||||
// [i64; 6]
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn return_record_6() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ a: 3, b: 5, c: 4, d: 2, e: 1, f: 7 }
|
||||
"#
|
||||
),
|
||||
[3, 5, 4, 2, 1, 7],
|
||||
[i64; 6]
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn return_record_7() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { a: 3, b: 5, c: 4, d: 2, e: 1, f: 7, g: 8 }
|
||||
// "#
|
||||
// ),
|
||||
// [3, 5, 4, 2, 1, 7, 8],
|
||||
// [i64; 7]
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn return_record_7() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ a: 3, b: 5, c: 4, d: 2, e: 1, f: 7, g: 8 }
|
||||
"#
|
||||
),
|
||||
[3, 5, 4, 2, 1, 7, 8],
|
||||
[i64; 7]
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn return_record_float_int() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { a: 3.14, b: 0x1 }
|
||||
// "#
|
||||
// ),
|
||||
// (3.14, 0x1),
|
||||
// (f64, i64)
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn return_record_float_int() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ a: 3.14, b: 0x1 }
|
||||
"#
|
||||
),
|
||||
(3.14, 0x1),
|
||||
(f64, i64)
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn return_record_int_float() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { a: 0x1, b: 3.14 }
|
||||
// "#
|
||||
// ),
|
||||
// (0x1, 3.14),
|
||||
// (i64, f64)
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn return_record_int_float() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ a: 0x1, b: 3.14 }
|
||||
"#
|
||||
),
|
||||
(0x1, 3.14),
|
||||
(i64, f64)
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn return_record_float_float() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { a: 6.28, b: 3.14 }
|
||||
// "#
|
||||
// ),
|
||||
// (6.28, 3.14),
|
||||
// (f64, f64)
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn return_record_float_float() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ a: 6.28, b: 3.14 }
|
||||
"#
|
||||
),
|
||||
(6.28, 3.14),
|
||||
(f64, f64)
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn return_record_float_float_float() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { a: 6.28, b: 3.14, c: 0.1 }
|
||||
// "#
|
||||
// ),
|
||||
// (6.28, 3.14, 0.1),
|
||||
// (f64, f64, f64)
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn return_record_float_float_float() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ a: 6.28, b: 3.14, c: 0.1 }
|
||||
"#
|
||||
),
|
||||
(6.28, 3.14, 0.1),
|
||||
(f64, f64, f64)
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn return_nested_record() {
|
||||
|
Loading…
Reference in New Issue
Block a user