diff --git a/compiler/gen_wasm/src/backend.rs b/compiler/gen_wasm/src/backend.rs index b66c4847d7..3aef282b2c 100644 --- a/compiler/gen_wasm/src/backend.rs +++ b/compiler/gen_wasm/src/backend.rs @@ -1,6 +1,8 @@ use parity_wasm::builder; use parity_wasm::builder::{CodeLocation, ModuleBuilder}; -use parity_wasm::elements::{Instruction, Instruction::*, Instructions, Local, ValueType}; +use parity_wasm::elements::{ + BlockType, Instruction, Instruction::*, Instructions, Local, ValueType, +}; use roc_collections::all::MutMap; use roc_module::low_level::LowLevel; @@ -30,6 +32,12 @@ struct WasmLayout { impl WasmLayout { fn new(layout: &Layout) -> Result { match layout { + Layout::Builtin(Builtin::Int1 | Builtin::Int8 | Builtin::Int16 | Builtin::Int32) => { + Ok(Self { + value_type: ValueType::I32, + stack_memory: 0, + }) + } Layout::Builtin(Builtin::Int64) => Ok(Self { value_type: ValueType::I64, stack_memory: 0, @@ -207,6 +215,60 @@ impl<'a> WasmBackend<'a> { )) } } + + Stmt::Switch { + cond_symbol, + cond_layout: _, + branches, + default_branch, + ret_layout: _, + } => { + // NOTE currently implemented as a series of conditional jumps + // We may be able to improve this in the future with `Select` + // or `BrTable` + + // create (number_of_branches - 1) new blocks. + // + // Every branch ends in a `return`, + // so the block leaves no values on the stack + for _ in 0..branches.len() { + self.instructions.push(Block(BlockType::NoResult)); + } + + // the LocalId of the symbol that we match on + let matched_on = match self.symbol_storage_map.get(cond_symbol) { + Some(SymbolStorage(local_id, _)) => local_id.0, + None => unreachable!("symbol not defined: {:?}", cond_symbol), + }; + + // then, we jump whenever the value under scrutiny is equal to the value of a branch + for (i, (value, _, _)) in branches.iter().enumerate() { + // put the cond_symbol on the top of the stack + self.instructions.push(GetLocal(matched_on)); + + self.instructions.push(I32Const(*value as i32)); + + // compare the 2 topmost values + self.instructions.push(I32Eq); + + // "break" out of `i` surrounding blocks + self.instructions.push(BrIf(i as u32)); + } + + // if we never jumped because a value matched, we're in the default case + self.build_stmt(default_branch.1, ret_layout)?; + + // now put in the actual body of each branch in order + // (the first branch would have broken out of 1 block, + // hence we must generate its code first) + for (_, _, branch) in branches.iter() { + self.instructions.push(End); + + self.build_stmt(branch, ret_layout)?; + } + + Ok(()) + } x => Err(format!("statement not yet implemented: {:?}", x)), } } @@ -248,6 +310,14 @@ impl<'a> WasmBackend<'a> { fn load_literal(&mut self, lit: &Literal<'a>) -> Result<(), String> { match lit { + Literal::Bool(x) => { + self.instructions.push(I32Const(*x as i32)); + Ok(()) + } + Literal::Byte(x) => { + self.instructions.push(I32Const(*x as i32)); + Ok(()) + } Literal::Int(x) => { self.instructions.push(I64Const(*x as i64)); Ok(()) diff --git a/compiler/gen_wasm/tests/helpers/eval_simple.rs b/compiler/gen_wasm/tests/helpers/eval_simple.rs index ede52522b3..d3e69431f8 100644 --- a/compiler/gen_wasm/tests/helpers/eval_simple.rs +++ b/compiler/gen_wasm/tests/helpers/eval_simple.rs @@ -147,6 +147,7 @@ where Err(e) => Err(format!("{:?}", e)), Ok(result) => { let integer = match result[0] { + wasmer::Value::I32(a) => a as i64, wasmer::Value::I64(a) => a, wasmer::Value::F64(a) => a.to_bits() as i64, _ => panic!(), diff --git a/compiler/gen_wasm/tests/wasm_num.rs b/compiler/gen_wasm/tests/wasm_num.rs index 57ef682f76..06ca50cf39 100644 --- a/compiler/gen_wasm/tests/wasm_num.rs +++ b/compiler/gen_wasm/tests/wasm_num.rs @@ -49,6 +49,73 @@ mod dev_num { ); } + #[test] + fn if_then_else() { + assert_evals_to!( + indoc!( + r#" + cond : Bool + cond = True + + if cond then + 0 + else + 1 + "# + ), + 0, + i64 + ); + } + + #[test] + fn rgb_red() { + assert_evals_to!( + indoc!( + r#" + when Red is + Red -> 111 + Green -> 222 + Blue -> 333 + "# + ), + 111, + i64 + ); + } + + #[test] + fn rgb_green() { + assert_evals_to!( + indoc!( + r#" + when Green is + Red -> 111 + Green -> 222 + Blue -> 333 + "# + ), + 222, + i64 + ); + } + + #[test] + fn rgb_blue() { + assert_evals_to!( + indoc!( + r#" + when Blue is + Red -> 111 + Green -> 222 + Blue -> 333 + "# + ), + 333, + i64 + ); + } + // #[test] // fn gen_add_f64() { // assert_evals_to!(