From 02bb9028efbcde53dafa3d05919218e82c1ffd33 Mon Sep 17 00:00:00 2001 From: Brian Carroll Date: Tue, 28 Sep 2021 08:06:59 +0100 Subject: [PATCH] Returning records on the stack from Wasm dev backend! --- compiler/gen_wasm/src/backend.rs | 199 ++++++----- compiler/gen_wasm/src/layout.rs | 83 +---- compiler/gen_wasm/src/lib.rs | 45 +-- compiler/gen_wasm/src/storage.rs | 156 +++++++++ compiler/gen_wasm/tests/wasm_records.rs | 431 +++++++++++------------- 5 files changed, 504 insertions(+), 410 deletions(-) create mode 100644 compiler/gen_wasm/src/storage.rs diff --git a/compiler/gen_wasm/src/backend.rs b/compiler/gen_wasm/src/backend.rs index 392dd176ab..278ca68966 100644 --- a/compiler/gen_wasm/src/backend.rs +++ b/compiler/gen_wasm/src/backend.rs @@ -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 { + 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, diff --git a/compiler/gen_wasm/src/layout.rs b/compiler/gen_wasm/src/layout.rs index 576316a049..8a067c412f 100644 --- a/compiler/gen_wasm/src/layout.rs +++ b/compiler/gen_wasm/src/layout.rs @@ -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 { - 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) -> 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 - } } diff --git a/compiler/gen_wasm/src/lib.rs b/compiler/gen_wasm/src/lib.rs index d439fca3cc..4a22343f45 100644 --- a/compiler/gen_wasm/src/lib.rs +++ b/compiler/gen_wasm/src/lib.rs @@ -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 { +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, 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(()) } diff --git a/compiler/gen_wasm/src/storage.rs b/compiler/gen_wasm/src/storage.rs new file mode 100644 index 0000000000..24b2e7a87c --- /dev/null +++ b/compiler/gen_wasm/src/storage.rs @@ -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, + to_pointer: LocalId, + to_offset: u32, + ) -> Result { + 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) + } + } + } +} diff --git a/compiler/gen_wasm/tests/wasm_records.rs b/compiler/gen_wasm/tests/wasm_records.rs index 7d4bc9af3f..d9776f97f2 100644 --- a/compiler/gen_wasm/tests/wasm_records.rs +++ b/compiler/gen_wasm/tests/wasm_records.rs @@ -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() {