diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs index 7a24f9fbf..e177cc375 100644 --- a/lib/llvm-backend/src/code.rs +++ b/lib/llvm-backend/src/code.rs @@ -2,21 +2,22 @@ use inkwell::{ builder::Builder, context::Context, module::Module, + passes::PassManager, types::{BasicType, BasicTypeEnum, FunctionType}, - values::{BasicValue, FunctionValue}, + values::{BasicValue, FunctionValue, PhiValue}, FloatPredicate, IntPredicate, }; use smallvec::SmallVec; use wasmer_runtime_core::{ module::ModuleInfo, structures::{Map, SliceMap, TypedIndex}, - types::{FuncIndex, FuncSig, LocalFuncIndex, LocalOrImport, SigIndex, Type}, + types::{FuncIndex, MemoryIndex, FuncSig, LocalFuncIndex, LocalOrImport, SigIndex, Type, MemoryType}, }; use wasmparser::{BinaryReaderError, CodeSectionReader, LocalsReader, Operator, OperatorsReader}; use crate::intrinsics::Intrinsics; use crate::read_info::type_to_type; -use crate::state::State; +use crate::state::{ControlFrame, IfElseState, State}; fn func_sig_to_llvm(context: &Context, intrinsics: &Intrinsics, sig: &FuncSig) -> FunctionType { let user_param_types = sig.params().iter().map(|&ty| type_to_llvm(intrinsics, ty)); @@ -116,10 +117,37 @@ fn parse_function( let llvm_sig = &signatures[sig_index]; let function = functions[func_index]; - let entry_block = context.append_basic_block(&function, "entry"); - builder.position_at_end(&entry_block); - let mut state = State::new(); + let entry_block = context.append_basic_block(&function, "entry"); + + let return_block = context.append_basic_block(&function, "return"); + builder.position_at_end(&return_block); + + let phis: SmallVec<[PhiValue; 1]> = func_sig + .returns() + .iter() + .map(|&wasmer_ty| type_to_llvm(intrinsics, wasmer_ty)) + .map(|ty| builder.build_phi(ty, &state.var_name())) + .collect(); + + match phis.as_slice() { + // No returns. + &[] => { + builder.build_return(None); + } + &[one_value] => { + let value = one_value.as_basic_value(); + builder.build_return(Some(&value)); + } + returns @ _ => { + // let struct_ty = llvm_sig.get_return_type().as_struct_type(); + // let ret_struct = struct_ty.const_zero(); + unimplemented!("multi-value returns not yet implemented") + } + } + + state.push_block(return_block, phis); + builder.position_at_end(&entry_block); let mut locals = Vec::with_capacity(locals_reader.get_count() as usize); locals.extend(function.get_param_iter().enumerate().map(|(index, param)| { @@ -163,7 +191,7 @@ fn parse_function( offset: -1isize as usize, })?; - let end_block = context.append_basic_block(&function, &state.block_name()); + let end_block = context.append_basic_block(&function, "end"); builder.position_at_end(&end_block); let phis = if let Ok(wasmer_ty) = type_to_type(ty) { @@ -180,12 +208,24 @@ fn parse_function( builder.position_at_end(¤t_block); } Operator::Loop { ty } => { + let loop_body = context.append_basic_block(&function, "loop_body"); + let loop_next = context.append_basic_block(&function, "loop_outer"); - // let loop_body = context.append_basic_block(&function, &state.block_name()); - // let next = context.append_basic_block(&function, &state.block_name()); - // builder.build_unconditional_branch(&body); - // let num_return_values = if ty == wasmparser::Type::EmptyBlockType { 0 } else { 1 }; - // state.push_loop(loop_body, next, num_return_values); + builder.build_unconditional_branch(&loop_body); + + builder.position_at_end(&loop_next); + let phis = if let Ok(wasmer_ty) = type_to_type(ty) { + let llvm_ty = type_to_llvm(intrinsics, wasmer_ty); + [llvm_ty] + .iter() + .map(|&ty| builder.build_phi(ty, &state.var_name())) + .collect() + } else { + SmallVec::new() + }; + + builder.position_at_end(&loop_body); + state.push_loop(loop_body, loop_next, phis); } Operator::Br { relative_depth } => { let frame = state.frame_at_depth(relative_depth)?; @@ -195,7 +235,13 @@ fn parse_function( offset: -1isize as usize, })?; - let values = state.peekn(frame.phis().len())?; + let value_len = if frame.is_loop() { + 0 + } else { + frame.phis().len() + }; + + let values = state.peekn(value_len)?; // For each result of the block we're branching to, // pop a value off the value stack and load it into @@ -204,11 +250,10 @@ fn parse_function( phi.add_incoming(&[(value, ¤t_block)]); } - builder.build_unconditional_branch(frame.dest()); + builder.build_unconditional_branch(frame.br_dest()); - state.popn(frame.phis().len())?; - - builder.build_unreachable(); + state.popn(value_len)?; + state.reachable = false; } Operator::BrIf { relative_depth } => { let cond = state.pop1()?; @@ -219,13 +264,19 @@ fn parse_function( offset: -1isize as usize, })?; - let param_stack = state.peekn(frame.phis().len())?; + let value_len = if frame.is_loop() { + 0 + } else { + frame.phis().len() + }; + + let param_stack = state.peekn(value_len)?; for (phi, value) in frame.phis().iter().zip(param_stack.iter()) { phi.add_incoming(&[(value, ¤t_block)]); } - let false_block = context.append_basic_block(&function, &state.block_name()); + let else_block = context.append_basic_block(&function, "else"); let cond_value = builder.build_int_compare( IntPredicate::NE, @@ -233,8 +284,8 @@ fn parse_function( intrinsics.i32_zero, &state.var_name(), ); - builder.build_conditional_branch(cond_value, frame.dest(), &false_block); - builder.position_at_end(&false_block); + builder.build_conditional_branch(cond_value, frame.br_dest(), &else_block); + builder.position_at_end(&else_block); } Operator::BrTable { ref table } => { let current_block = builder.get_insert_block().ok_or(BinaryReaderError { @@ -268,25 +319,131 @@ fn parse_function( phi.add_incoming(&[(value, ¤t_block)]); } - Ok((case_index_literal, frame.dest())) + Ok((case_index_literal, frame.br_dest())) }) .collect::>()?; - builder.build_switch(index.into_int_value(), default_frame.dest(), &cases[..]); + builder.build_switch(index.into_int_value(), default_frame.br_dest(), &cases[..]); state.popn(res_len)?; builder.build_unreachable(); } + Operator::If { ty } => { + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + let if_then_block = context.append_basic_block(&function, "if_then"); + let if_else_block = context.append_basic_block(&function, "if_else"); + let end_block = context.append_basic_block(&function, "if_end"); + + let end_phis = { + builder.position_at_end(&end_block); + + let phis = if let Ok(wasmer_ty) = type_to_type(ty) { + let llvm_ty = type_to_llvm(intrinsics, wasmer_ty); + [llvm_ty] + .iter() + .map(|&ty| builder.build_phi(ty, &state.var_name())) + .collect() + } else { + SmallVec::new() + }; + + builder.position_at_end(¤t_block); + phis + }; + + let cond = state.pop1()?; + + let cond_value = builder.build_int_compare( + IntPredicate::NE, + cond.into_int_value(), + intrinsics.i32_zero, + &state.var_name(), + ); + + builder.build_conditional_branch(cond_value, &if_then_block, &if_else_block); + builder.position_at_end(&if_then_block); + state.push_if(if_then_block, if_else_block, end_block, end_phis); + } + Operator::Else => { + if state.reachable { + let frame = state.frame_at_depth(0)?; + builder.build_unconditional_branch(frame.code_after()); + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + + for phi in frame.phis().to_vec().iter().rev() { + let value = state.pop1()?; + phi.add_incoming(&[(&value, ¤t_block)]) + } + } + + let (if_else_block, if_else_state) = if let ControlFrame::IfElse { + if_else, + if_else_state, + .. + } = state.frame_at_depth_mut(0)? + { + (if_else, if_else_state) + } else { + unreachable!() + }; + + *if_else_state = IfElseState::Else; + + builder.position_at_end(if_else_block); + state.reachable = true; + } Operator::End => { let frame = state.pop_frame()?; + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + + if state.reachable { + builder.build_unconditional_branch(frame.code_after()); + + for phi in frame.phis().iter().rev() { + let value = state.pop1()?; + phi.add_incoming(&[(&value, ¤t_block)]) + } + } + + if let ControlFrame::IfElse { + if_else, + next, + phis, + if_else_state, + .. + } = &frame + { + if let IfElseState::If = if_else_state { + builder.position_at_end(if_else); + builder.build_unconditional_branch(next); + } + } + + builder.position_at_end(frame.code_after()); + state.reset_stack(&frame); + + state.reachable = true; // Push each phi value to the value stack. for phi in frame.phis() { state.push1(phi.as_basic_value()); } + } + Operator::Return => { + let frame = state.outermost_frame()?; - state.reset_stack(&frame); + builder.build_unconditional_branch(frame.br_dest()); + state.reachable = false; } Operator::Unreachable => { @@ -294,6 +451,7 @@ fn parse_function( // If llvm cannot prove that this is never touched, // it will emit a `ud2` instruction on x86_64 arches. builder.build_unreachable(); + state.reachable = false; } /*************************** @@ -1021,6 +1179,26 @@ fn parse_function( unimplemented!("waiting on better bitcasting support in inkwell") } + Operator::MemoryGrow { reserved } => { + + let memory_grow_const = intrinsics.i32_ty.const_int(reserved as u64, false); + + let memory_index = MemoryIndex::new(reserved); + match memory_index.local_or_import(info) { + LocalOrImport::Local(local_mem_index) => { + let mem_desc = &info.memories[local_mem_index]; + match mem_desc.memory_type() { + MemoryType::Dynamic => { + + } + } + }, + LocalOrImport::Import(import_mem_index) => { + + }, + } + } + op @ _ => { println!("{}", module.print_to_string().to_string()); unimplemented!("{:?}", op); @@ -1028,5 +1206,12 @@ fn parse_function( } } + let pass_manager = PassManager::create_for_module(); + pass_manager.add_promote_memory_to_register_pass(); + pass_manager.add_cfg_simplification_pass(); + pass_manager.run_on_module(module); + + println!("{}", module.print_to_string().to_string()); + Ok(()) } diff --git a/lib/llvm-backend/src/intrinsics.rs b/lib/llvm-backend/src/intrinsics.rs index 8eab37071..ff6847b3b 100644 --- a/lib/llvm-backend/src/intrinsics.rs +++ b/lib/llvm-backend/src/intrinsics.rs @@ -54,6 +54,22 @@ pub struct Intrinsics { pub i64_zero: IntValue, pub f32_zero: FloatValue, pub f64_zero: FloatValue, + + // VM intrinsics. + pub memory_grow_dynamic_local: FunctionValue, + pub memory_grow_static_local: FunctionValue, + pub memory_grow_shared_local: FunctionValue, + pub memory_grow_dynamic_import: FunctionValue, + pub memory_grow_static_import: FunctionValue, + pub memory_grow_shared_import: FunctionValue, + + pub memory_size_dynamic_local: FunctionValue, + pub memory_size_static_local: FunctionValue, + pub memory_size_shared_local: FunctionValue, + pub memory_size_dynamic_import: FunctionValue, + pub memory_size_static_import: FunctionValue, + pub memory_size_shared_import: FunctionValue, + // pub ctx_ty: StructType, } impl Intrinsics { @@ -64,6 +80,7 @@ impl Intrinsics { let i64_ty = context.i64_type(); let f32_ty = context.f32_type(); let f64_ty = context.f64_type(); + // let ctx_ty = context.struct_type(&[], false); let i1_zero = i1_ty.const_int(0, false); let i32_zero = i32_ty.const_int(0, false); @@ -89,6 +106,9 @@ impl Intrinsics { let ret_f32_take_f32_f32 = f32_ty.fn_type(&[f32_ty_basic, f32_ty_basic], false); let ret_f64_take_f64_f64 = f64_ty.fn_type(&[f64_ty_basic, f64_ty_basic], false); + let ret_i32_take_i64_i32_i32 = i32_ty.fn_type(&[i64_ty, i32_ty, i32_ty], false); + let ret_i32_take_i64_i32 = i32_ty.fn_type(&[i64_ty, i32_ty], false); + Self { ctlz_i32: module.add_function("llvm.ctlz.i32", ret_i32_take_i32_i1, None), ctlz_i64: module.add_function("llvm.ctlz.i64", ret_i64_take_i64_i1, None), @@ -138,6 +158,25 @@ impl Intrinsics { i64_zero, f32_zero, f64_zero, + + // VM intrinsics. + memory_grow_dynamic_local: module.add_function("vm.memory.grow.dynamic.local", ret_i32_take_i64_i32_i32, None), + memory_grow_static_local: module.add_function("vm.memory.grow.static.local", ret_i32_take_i64_i32_i32, None), + memory_grow_shared_local: module.add_function("vm.memory.grow.shared.local", ret_i32_take_i64_i32_i32, None), + memory_grow_dynamic_import: module.add_function("vm.memory.grow.dynamic.import", ret_i32_take_i64_i32_i32, None), + memory_grow_static_import: module.add_function("vm.memory.grow.static.import", ret_i32_take_i64_i32_i32, None), + memory_grow_shared_import: module.add_function("vm.memory.grow.shared.import", ret_i32_take_i64_i32_i32, None), + + memory_size_dynamic_local: module.add_function("vm.memory.size.dynamic.local", ret_i32_take_i64_i32, None), + memory_size_static_local: module.add_function("vm.memory.size.static.local", ret_i32_take_i64_i32, None), + memory_size_shared_local: module.add_function("vm.memory.size.shared.local", ret_i32_take_i64_i32, None), + memory_size_dynamic_import: module.add_function("vm.memory.size.dynamic.import", ret_i32_take_i64_i32, None), + memory_size_static_import: module.add_function("vm.memory.size.static.import", ret_i32_take_i64_i32, None), + memory_size_shared_import: module.add_function("vm.memory.size.shared.import", ret_i32_take_i64_i32, None), } } } + +// pub struct CtxType { +// ctx_ty: StructType, +// } diff --git a/lib/llvm-backend/src/lib.rs b/lib/llvm-backend/src/lib.rs index 69ed0fa0d..b02b39cbf 100644 --- a/lib/llvm-backend/src/lib.rs +++ b/lib/llvm-backend/src/lib.rs @@ -30,21 +30,26 @@ impl Compiler for LLVMCompiler { #[test] fn test_read_module() { use wabt::wat2wasm; - let WAT: &'static str = r#" + let wat = r#" (module (type $t0 (func (param i32) (result i32))) - (import "env" "memory" (memory 1 1)) - (import "env" "table" (table 10 anyfunc)) - (import "env" "global" (global i32)) - (import "env" "print_i32" (func $print_i32 (type $t0))) - (func $identity (type $t0) (param $p0 i32) (result i32) - get_local $p0) - (func $print_num (export "print_num") (type $t0) (param $p0 i32) (result i32) - get_global 0 - call $identity - call $print_i32)) + (type $t1 (func (result i32))) + (func $foo (type $t0) (param i32) (result i32) + get_local 0 + (if + (then + i32.const 42 + set_local 0 + ) + (else + i32.const 24 + set_local 0 + ) + ) + get_local 0 + )) "#; - let wasm = wat2wasm(WAT).unwrap(); + let wasm = wat2wasm(wat).unwrap(); let (info, code_reader) = read_info::read_module(&wasm).unwrap(); diff --git a/lib/llvm-backend/src/read_info.rs b/lib/llvm-backend/src/read_info.rs index c1ac2cf6b..bed91aa90 100644 --- a/lib/llvm-backend/src/read_info.rs +++ b/lib/llvm-backend/src/read_info.rs @@ -285,7 +285,12 @@ pub fn type_to_type(ty: WpType) -> Result { offset: -1isize as usize, }); } - _ => panic!("broken invariant, invalid type"), + _ => { + return Err(BinaryReaderError { + message: "that type is not supported as a wasmer type", + offset: -1isize as usize, + }); + } }) } diff --git a/lib/llvm-backend/src/state.rs b/lib/llvm-backend/src/state.rs index e85c7a23c..674eee04c 100644 --- a/lib/llvm-backend/src/state.rs +++ b/lib/llvm-backend/src/state.rs @@ -6,33 +6,73 @@ use smallvec::SmallVec; use std::cell::Cell; use wasmparser::BinaryReaderError; +#[derive(Debug)] pub enum ControlFrame { Block { - end_label: BasicBlock, + next: BasicBlock, phis: SmallVec<[PhiValue; 1]>, stack_size_snapshot: usize, }, + Loop { + body: BasicBlock, + next: BasicBlock, + phis: SmallVec<[PhiValue; 1]>, + stack_size_snapshot: usize, + }, + IfElse { + if_then: BasicBlock, + if_else: BasicBlock, + next: BasicBlock, + phis: SmallVec<[PhiValue; 1]>, + stack_size_snapshot: usize, + if_else_state: IfElseState, + }, +} + +#[derive(Debug)] +pub enum IfElseState { + If, + Else, } impl ControlFrame { - pub fn dest(&self) -> &BasicBlock { + pub fn code_after(&self) -> &BasicBlock { match self { - ControlFrame::Block { ref end_label, .. } => end_label, + ControlFrame::Block { ref next, .. } + | ControlFrame::Loop { ref next, .. } + | ControlFrame::IfElse { ref next, .. } => next, + } + } + + pub fn br_dest(&self) -> &BasicBlock { + match self { + ControlFrame::Block { ref next, .. } | ControlFrame::IfElse { ref next, .. } => next, + ControlFrame::Loop { ref body, .. } => body, } } pub fn phis(&self) -> &[PhiValue] { match self { - ControlFrame::Block { ref phis, .. } => phis.as_slice(), + ControlFrame::Block { ref phis, .. } + | ControlFrame::Loop { ref phis, .. } + | ControlFrame::IfElse { ref phis, .. } => phis.as_slice(), + } + } + + pub fn is_loop(&self) -> bool { + match self { + ControlFrame::Loop { .. } => true, + _ => false, } } } +#[derive(Debug)] pub struct State { stack: Vec, control_stack: Vec, value_counter: Cell, - block_counter: Cell, + pub reachable: bool, } impl State { @@ -41,7 +81,7 @@ impl State { stack: vec![], control_stack: vec![], value_counter: Cell::new(0), - block_counter: Cell::new(0), + reachable: true, } } @@ -50,11 +90,26 @@ impl State { ControlFrame::Block { stack_size_snapshot, .. + } + | ControlFrame::Loop { + stack_size_snapshot, + .. + } + | ControlFrame::IfElse { + stack_size_snapshot, + .. } => *stack_size_snapshot, }; self.stack.truncate(stack_size_snapshot); } + pub fn outermost_frame(&self) -> Result<&ControlFrame, BinaryReaderError> { + self.control_stack.get(0).ok_or(BinaryReaderError { + message: "invalid control stack depth", + offset: -1isize as usize, + }) + } + pub fn frame_at_depth(&self, depth: u32) -> Result<&ControlFrame, BinaryReaderError> { let index = self.control_stack.len() - 1 - (depth as usize); self.control_stack.get(index).ok_or(BinaryReaderError { @@ -63,6 +118,17 @@ impl State { }) } + pub fn frame_at_depth_mut( + &mut self, + depth: u32, + ) -> Result<&mut ControlFrame, BinaryReaderError> { + let index = self.control_stack.len() - 1 - (depth as usize); + self.control_stack.get_mut(index).ok_or(BinaryReaderError { + message: "invalid control stack depth", + offset: -1isize as usize, + }) + } + pub fn pop_frame(&mut self) -> Result { self.control_stack.pop().ok_or(BinaryReaderError { message: "cannot pop from control stack", @@ -70,13 +136,6 @@ impl State { }) } - pub fn block_name(&self) -> String { - let counter = self.block_counter.get(); - let s = format!("block{}", counter); - self.block_counter.set(counter + 1); - s - } - pub fn var_name(&self) -> String { let counter = self.value_counter.get(); let s = format!("s{}", counter); @@ -129,6 +188,12 @@ impl State { }) } + pub fn popn_save(&mut self, n: usize) -> Result, BinaryReaderError> { + let v = self.peekn(n)?.to_vec(); + self.popn(n)?; + Ok(v) + } + pub fn popn(&mut self, n: usize) -> Result<(), BinaryReaderError> { if self.stack.len() < n { return Err(BinaryReaderError { @@ -142,11 +207,37 @@ impl State { Ok(()) } - pub fn push_block(&mut self, end_label: BasicBlock, phis: SmallVec<[PhiValue; 1]>) { + pub fn push_block(&mut self, next: BasicBlock, phis: SmallVec<[PhiValue; 1]>) { self.control_stack.push(ControlFrame::Block { - end_label, + next, phis, stack_size_snapshot: self.stack.len(), }); } + + pub fn push_loop(&mut self, body: BasicBlock, next: BasicBlock, phis: SmallVec<[PhiValue; 1]>) { + self.control_stack.push(ControlFrame::Loop { + body, + next, + phis, + stack_size_snapshot: self.stack.len(), + }); + } + + pub fn push_if( + &mut self, + if_then: BasicBlock, + if_else: BasicBlock, + next: BasicBlock, + phis: SmallVec<[PhiValue; 1]>, + ) { + self.control_stack.push(ControlFrame::IfElse { + if_then, + if_else, + next, + phis, + stack_size_snapshot: self.stack.len(), + if_else_state: IfElseState::If, + }); + } }