diff --git a/CHANGELOG.md b/CHANGELOG.md index f8a367e344d..b3f8313136f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ Blocks of changes will separated by version increments. ## **[Unreleased]** +- [#413](https://github.com/wasmerio/wasmer/pull/413) Update LLVM backend to use new parser codegen traits + ## 0.4.1 - 2018-05-06 - [#426](https://github.com/wasmerio/wasmer/pull/426) Update wapm-cli submodule, bump version to 0.4.1 diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs index 2795e6271de..028207a8c4f 100644 --- a/lib/llvm-backend/src/code.rs +++ b/lib/llvm-backend/src/code.rs @@ -8,19 +8,21 @@ use inkwell::{ AddressSpace, FloatPredicate, IntPredicate, }; use smallvec::SmallVec; +use std::sync::Arc; use wasmer_runtime_core::{ + backend::{Backend, CacheGen, Token}, + cache::{Artifact, Error as CacheError}, + codegen::*, memory::MemoryType, - module::ModuleInfo, - structures::{Map, SliceMap, TypedIndex}, + module::{ModuleInfo, ModuleInner}, + structures::{Map, TypedIndex}, types::{ - FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex, - TableIndex, Type, + FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, SigIndex, TableIndex, Type, }, }; -use wasmparser::{ - BinaryReaderError, CodeSectionReader, LocalsReader, MemoryImmediate, Operator, OperatorsReader, -}; +use wasmparser::{BinaryReaderError, MemoryImmediate, Operator, Type as WpType}; +use crate::backend::LLVMBackend; use crate::intrinsics::{CtxType, GlobalCache, Intrinsics, MemoryCache}; use crate::read_info::type_to_type; use crate::state::{ControlFrame, IfElseState, State}; @@ -58,195 +60,446 @@ fn type_to_llvm(intrinsics: &Intrinsics, ty: Type) -> BasicTypeEnum { } } -pub fn parse_function_bodies( - info: &ModuleInfo, - code_reader: CodeSectionReader, -) -> Result<(Module, Intrinsics), BinaryReaderError> { - let context = Context::create(); - let module = context.create_module("module"); - let builder = context.create_builder(); +fn trap_if_not_representatable_as_int( + builder: &Builder, + intrinsics: &Intrinsics, + context: &Context, + function: &FunctionValue, + lower_bounds: f64, + upper_bound: f64, + value: FloatValue, +) { + enum FloatSize { + Bits32, + Bits64, + } - let intrinsics = Intrinsics::declare(&module, &context); + let failure_block = context.append_basic_block(function, "conversion_failure_block"); + let continue_block = context.append_basic_block(function, "conversion_success_block"); - let personality_func = module.add_function( - "__gxx_personality_v0", - intrinsics.i32_ty.fn_type(&[], false), - Some(Linkage::External), - ); + let float_ty = value.get_type(); + let (int_ty, float_ptr_ty, float_size) = if float_ty == intrinsics.f32_ty { + (intrinsics.i32_ty, intrinsics.f32_ptr_ty, FloatSize::Bits32) + } else if float_ty == intrinsics.f64_ty { + (intrinsics.i64_ty, intrinsics.f64_ptr_ty, FloatSize::Bits64) + } else { + unreachable!() + }; - let signatures: Map = info - .signatures - .iter() - .map(|(_, sig)| func_sig_to_llvm(&context, &intrinsics, sig)) - .collect(); - let functions: Map = info - .func_assoc - .iter() - .skip(info.imported_functions.len()) - .map(|(func_index, &sig_index)| { - let func = module.add_function( - &format!("fn{}", func_index.index()), - signatures[sig_index], - Some(Linkage::External), - ); - func.set_personality_function(personality_func); - func - }) - .collect(); + let (exponent, invalid_exponent) = { + let float_bits = { + let space = builder.build_alloca(int_ty, "space"); + let float_ptr = builder.build_pointer_cast(space, float_ptr_ty, "float_ptr"); + builder.build_store(float_ptr, value); + builder.build_load(space, "float_bits").into_int_value() + }; - for (local_func_index, body) in code_reader.into_iter().enumerate() { - let body = body?; + let (shift_amount, exponent_mask, invalid_exponent) = match float_size { + FloatSize::Bits32 => (23, 0b01111111100000000000000000000000, 0b11111111), + FloatSize::Bits64 => ( + 52, + 0b0111111111110000000000000000000000000000000000000000000000000000, + 0b11111111111, + ), + }; - let locals_reader = body.get_locals_reader()?; - let op_reader = body.get_operators_reader()?; + builder.build_and( + float_bits, + int_ty.const_int(exponent_mask, false), + "masked_bits", + ); - parse_function( - &context, - &builder, - &intrinsics, - info, - &signatures, - &functions, - LocalFuncIndex::new(local_func_index), - locals_reader, - op_reader, + ( + builder.build_right_shift( + float_bits, + int_ty.const_int(shift_amount, false), + false, + "exponent", + ), + invalid_exponent, ) - .map_err(|e| BinaryReaderError { - message: e.message, - offset: local_func_index, - })?; - } - - // module.print_to_stderr(); - - generate_trampolines(info, &signatures, &module, &context, &builder, &intrinsics); + }; - let pass_manager = PassManager::create_for_module(); - // pass_manager.add_verifier_pass(); - pass_manager.add_function_inlining_pass(); - pass_manager.add_promote_memory_to_register_pass(); - pass_manager.add_cfg_simplification_pass(); - // pass_manager.add_instruction_combining_pass(); - pass_manager.add_aggressive_inst_combiner_pass(); - pass_manager.add_merged_load_store_motion_pass(); - // pass_manager.add_sccp_pass(); - // pass_manager.add_gvn_pass(); - pass_manager.add_new_gvn_pass(); - pass_manager.add_aggressive_dce_pass(); - pass_manager.run_on_module(&module); + let is_invalid_float = builder.build_or( + builder.build_int_compare( + IntPredicate::EQ, + exponent, + int_ty.const_int(invalid_exponent, false), + "is_not_normal", + ), + builder.build_or( + builder.build_float_compare( + FloatPredicate::ULT, + value, + float_ty.const_float(lower_bounds), + "less_than_lower_bounds", + ), + builder.build_float_compare( + FloatPredicate::UGT, + value, + float_ty.const_float(upper_bound), + "greater_than_upper_bounds", + ), + "float_not_in_bounds", + ), + "is_invalid_float", + ); - // module.print_to_stderr(); + let is_invalid_float = builder + .build_call( + intrinsics.expect_i1, + &[ + is_invalid_float.as_basic_value_enum(), + intrinsics.i1_ty.const_int(0, false).as_basic_value_enum(), + ], + "is_invalid_float_expect", + ) + .try_as_basic_value() + .left() + .unwrap() + .into_int_value(); - Ok((module, intrinsics)) + builder.build_conditional_branch(is_invalid_float, &failure_block, &continue_block); + builder.position_at_end(&failure_block); + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_illegal_arithmetic], + "throw", + ); + builder.build_unreachable(); + builder.position_at_end(&continue_block); } -fn parse_function( - context: &Context, +fn trap_if_zero_or_overflow( builder: &Builder, intrinsics: &Intrinsics, - info: &ModuleInfo, - signatures: &SliceMap, - functions: &SliceMap, - func_index: LocalFuncIndex, - locals_reader: LocalsReader, - op_reader: OperatorsReader, -) -> Result<(), BinaryReaderError> { - let sig_index = info.func_assoc[func_index.convert_up(info)]; - let func_sig = &info.signatures[sig_index]; - - let function = functions[func_index]; - 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(); + context: &Context, + function: &FunctionValue, + left: IntValue, + right: IntValue, +) { + let int_type = left.get_type(); - state.push_block(return_block, phis); - builder.position_at_end(&entry_block); + let (min_value, neg_one_value) = if int_type == intrinsics.i32_ty { + let min_value = int_type.const_int(i32::min_value() as u64, false); + let neg_one_value = int_type.const_int(-1i32 as u32 as u64, false); + (min_value, neg_one_value) + } else if int_type == intrinsics.i64_ty { + let min_value = int_type.const_int(i64::min_value() as u64, false); + let neg_one_value = int_type.const_int(-1i64 as u64, false); + (min_value, neg_one_value) + } else { + unreachable!() + }; - let mut locals = Vec::with_capacity(locals_reader.get_count() as usize); // TODO fix capacity + let should_trap = builder.build_or( + builder.build_int_compare( + IntPredicate::EQ, + right, + int_type.const_int(0, false), + "divisor_is_zero", + ), + builder.build_and( + builder.build_int_compare(IntPredicate::EQ, left, min_value, "left_is_min"), + builder.build_int_compare(IntPredicate::EQ, right, neg_one_value, "right_is_neg_one"), + "div_will_overflow", + ), + "div_should_trap", + ); - locals.extend( - function - .get_param_iter() - .skip(1) - .enumerate() - .map(|(index, param)| { - let ty = param.get_type(); + let should_trap = builder + .build_call( + intrinsics.expect_i1, + &[ + should_trap.as_basic_value_enum(), + intrinsics.i1_ty.const_int(0, false).as_basic_value_enum(), + ], + "should_trap_expect", + ) + .try_as_basic_value() + .left() + .unwrap() + .into_int_value(); - let alloca = builder.build_alloca(ty, &format!("local{}", index)); - builder.build_store(alloca, param); - alloca - }), + let shouldnt_trap_block = context.append_basic_block(function, "shouldnt_trap_block"); + let should_trap_block = context.append_basic_block(function, "should_trap_block"); + builder.build_conditional_branch(should_trap, &should_trap_block, &shouldnt_trap_block); + builder.position_at_end(&should_trap_block); + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_illegal_arithmetic], + "throw", ); + builder.build_unreachable(); + builder.position_at_end(&shouldnt_trap_block); +} - let param_len = locals.len(); - - let mut local_idx = 0; - for local in locals_reader.into_iter() { - let (count, ty) = local?; - let wasmer_ty = type_to_type(ty)?; - let ty = type_to_llvm(intrinsics, wasmer_ty); +fn trap_if_zero( + builder: &Builder, + intrinsics: &Intrinsics, + context: &Context, + function: &FunctionValue, + value: IntValue, +) { + let int_type = value.get_type(); + let should_trap = builder.build_int_compare( + IntPredicate::EQ, + value, + int_type.const_int(0, false), + "divisor_is_zero", + ); - let default_value = match wasmer_ty { - Type::I32 => intrinsics.i32_zero.as_basic_value_enum(), - Type::I64 => intrinsics.i64_zero.as_basic_value_enum(), - Type::F32 => intrinsics.f32_zero.as_basic_value_enum(), - Type::F64 => intrinsics.f64_zero.as_basic_value_enum(), - }; + let should_trap = builder + .build_call( + intrinsics.expect_i1, + &[ + should_trap.as_basic_value_enum(), + intrinsics.i1_ty.const_int(0, false).as_basic_value_enum(), + ], + "should_trap_expect", + ) + .try_as_basic_value() + .left() + .unwrap() + .into_int_value(); - for _ in 0..count { - let alloca = builder.build_alloca(ty, &format!("local{}", param_len + local_idx)); + let shouldnt_trap_block = context.append_basic_block(function, "shouldnt_trap_block"); + let should_trap_block = context.append_basic_block(function, "should_trap_block"); + builder.build_conditional_branch(should_trap, &should_trap_block, &shouldnt_trap_block); + builder.position_at_end(&should_trap_block); + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_illegal_arithmetic], + "throw", + ); + builder.build_unreachable(); + builder.position_at_end(&shouldnt_trap_block); +} - builder.build_store(alloca, default_value); +fn resolve_memory_ptr( + builder: &Builder, + intrinsics: &Intrinsics, + context: &Context, + function: &FunctionValue, + state: &mut State, + ctx: &mut CtxType, + memarg: &MemoryImmediate, + ptr_ty: PointerType, +) -> Result { + // Ignore alignment hint for the time being. + let imm_offset = intrinsics.i64_ty.const_int(memarg.offset as u64, false); + let var_offset_i32 = state.pop1()?.into_int_value(); + let var_offset = + builder.build_int_z_extend(var_offset_i32, intrinsics.i64_ty, &state.var_name()); + let effective_offset = builder.build_int_add(var_offset, imm_offset, &state.var_name()); + let memory_cache = ctx.memory(MemoryIndex::new(0), intrinsics); - locals.push(alloca); - local_idx += 1; - } - } + let mem_base_int = match memory_cache { + MemoryCache::Dynamic { + ptr_to_base_ptr, + ptr_to_bounds, + } => { + let base = builder + .build_load(ptr_to_base_ptr, "base") + .into_pointer_value(); + let bounds = builder.build_load(ptr_to_bounds, "bounds").into_int_value(); - let start_of_code_block = context.append_basic_block(&function, "start_of_code"); - let entry_end_inst = builder.build_unconditional_branch(&start_of_code_block); - builder.position_at_end(&start_of_code_block); + let base_as_int = builder.build_ptr_to_int(base, intrinsics.i64_ty, "base_as_int"); - let cache_builder = context.create_builder(); - cache_builder.position_before(&entry_end_inst); - let mut ctx = intrinsics.ctx(info, builder, &function, cache_builder); - let mut unreachable_depth = 0; + let base_in_bounds = builder.build_int_compare( + IntPredicate::ULT, + effective_offset, + bounds, + "base_in_bounds", + ); - for op in op_reader { - let op = op?; - if !state.reachable { - match op { - Operator::Block { ty: _ } | Operator::Loop { ty: _ } | Operator::If { ty: _ } => { - unreachable_depth += 1; - continue; - } - Operator::Else => { - if unreachable_depth != 0 { - continue; - } + let base_in_bounds = builder + .build_call( + intrinsics.expect_i1, + &[ + base_in_bounds.as_basic_value_enum(), + intrinsics.i1_ty.const_int(1, false).as_basic_value_enum(), + ], + "base_in_bounds_expect", + ) + .try_as_basic_value() + .left() + .unwrap() + .into_int_value(); + + let in_bounds_continue_block = + context.append_basic_block(function, "in_bounds_continue_block"); + let not_in_bounds_block = context.append_basic_block(function, "not_in_bounds_block"); + builder.build_conditional_branch( + base_in_bounds, + &in_bounds_continue_block, + ¬_in_bounds_block, + ); + builder.position_at_end(¬_in_bounds_block); + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_memory_oob], + "throw", + ); + builder.build_unreachable(); + builder.position_at_end(&in_bounds_continue_block); + + base_as_int + } + MemoryCache::Static { + base_ptr, + bounds: _, + } => builder.build_ptr_to_int(base_ptr, intrinsics.i64_ty, "base_as_int"), + }; + + let effective_address_int = + builder.build_int_add(mem_base_int, effective_offset, &state.var_name()); + Ok(builder.build_int_to_ptr(effective_address_int, ptr_ty, &state.var_name())) +} + +#[derive(Debug)] +pub struct CodegenError { + pub message: String, +} + +pub struct LLVMModuleCodeGenerator { + context: Option, + builder: Option, + intrinsics: Option, + functions: Vec, + signatures: Map, + signatures_raw: Map, + function_signatures: Option>>, + func_import_count: usize, + personality_func: FunctionValue, + module: Module, +} + +pub struct LLVMFunctionCodeGenerator { + context: Option, + builder: Option, + intrinsics: Option, + state: State, + function: FunctionValue, + func_sig: FuncSig, + signatures: Map, + locals: Vec, // Contains params and locals + num_params: usize, + ctx: Option>, + unreachable_depth: usize, +} + +impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { + fn feed_return(&mut self, _ty: WpType) -> Result<(), CodegenError> { + Ok(()) + } + + fn feed_param(&mut self, _ty: WpType) -> Result<(), CodegenError> { + Ok(()) + } + + fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), CodegenError> { + let param_len = self.num_params; + + let mut local_idx = 0; + // let (count, ty) = local?; + let count = n; + let wasmer_ty = type_to_type(ty)?; + + let intrinsics = self.intrinsics.as_ref().unwrap(); + let ty = type_to_llvm(intrinsics, wasmer_ty); + + let default_value = match wasmer_ty { + Type::I32 => intrinsics.i32_zero.as_basic_value_enum(), + Type::I64 => intrinsics.i64_zero.as_basic_value_enum(), + Type::F32 => intrinsics.f32_zero.as_basic_value_enum(), + Type::F64 => intrinsics.f64_zero.as_basic_value_enum(), + }; + + let builder = self.builder.as_ref().unwrap(); + + for _ in 0..count { + let alloca = builder.build_alloca(ty, &format!("local{}", param_len + local_idx)); + + builder.build_store(alloca, default_value); + + self.locals.push(alloca); + local_idx += 1; + } + Ok(()) + } + + fn begin_body(&mut self, module_info: &ModuleInfo) -> Result<(), CodegenError> { + let start_of_code_block = self + .context + .as_ref() + .unwrap() + .append_basic_block(&self.function, "start_of_code"); + let entry_end_inst = self + .builder + .as_ref() + .unwrap() + .build_unconditional_branch(&start_of_code_block); + self.builder + .as_ref() + .unwrap() + .position_at_end(&start_of_code_block); + + let cache_builder = self.context.as_ref().unwrap().create_builder(); + cache_builder.position_before(&entry_end_inst); + let module_info = + unsafe { ::std::mem::transmute::<&ModuleInfo, &'static ModuleInfo>(module_info) }; + let function = unsafe { + ::std::mem::transmute::<&FunctionValue, &'static FunctionValue>(&self.function) + }; + let ctx = CtxType::new(module_info, function, cache_builder); + + self.ctx = Some(ctx); + Ok(()) + } + + fn feed_event(&mut self, event: Event, module_info: &ModuleInfo) -> Result<(), CodegenError> { + let op = match event { + Event::Wasm(x) => x, + Event::Internal(_x) => { + return Ok(()); + } + }; + + let mut state = &mut self.state; + let builder = self.builder.as_ref().unwrap(); + let context = self.context.as_ref().unwrap(); + let function = self.function; + let intrinsics = self.intrinsics.as_ref().unwrap(); + let locals = &self.locals; + let info = module_info; + let signatures = &self.signatures; + let mut ctx = self.ctx.as_mut().unwrap(); + + if !state.reachable { + match *op { + Operator::Block { ty: _ } | Operator::Loop { ty: _ } | Operator::If { ty: _ } => { + self.unreachable_depth += 1; + return Ok(()); + } + Operator::Else => { + if self.unreachable_depth != 0 { + return Ok(()); + } } Operator::End => { - if unreachable_depth != 0 { - unreachable_depth -= 1; - continue; + if self.unreachable_depth != 0 { + self.unreachable_depth -= 1; + return Ok(()); } } _ => { - continue; + return Ok(()); } } } - match op { + match *op { /*************************** * Control Flow instructions. * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#control-flow-instructions @@ -380,7 +633,12 @@ fn parse_function( .iter() .enumerate() .map(|(case_index, &depth)| { - let frame = state.frame_at_depth(depth)?; + let frame_result: Result<&ControlFrame, BinaryReaderError> = + state.frame_at_depth(depth); + let frame = match frame_result { + Ok(v) => v, + Err(e) => return Err(e), + }; let case_index_literal = context.i32_type().const_int(case_index as u64, false); @@ -394,7 +652,8 @@ fn parse_function( builder.build_switch(index.into_int_value(), default_frame.br_dest(), &cases[..]); - state.popn(args.len())?; + let args_len = args.len(); + state.popn(args_len)?; state.reachable = false; } Operator::If { ty } => { @@ -616,7 +875,7 @@ fn parse_function( Operator::GetGlobal { global_index } => { let index = GlobalIndex::new(global_index as usize); - let global_cache = ctx.global_cache(index); + let global_cache = ctx.global_cache(index, intrinsics); match global_cache { GlobalCache::Const { value } => { state.push1(value); @@ -630,7 +889,7 @@ fn parse_function( Operator::SetGlobal { global_index } => { let value = state.pop1()?; let index = GlobalIndex::new(global_index as usize); - let global_cache = ctx.global_cache(index); + let global_cache = ctx.global_cache(index, intrinsics); match global_cache { GlobalCache::Mut { ptr_to_value } => { builder.build_store(ptr_to_value, value); @@ -666,12 +925,14 @@ fn parse_function( .map(|v| *v) .collect(); - let func_ptr = ctx.local_func(local_func_index, llvm_sig); + let func_ptr = + ctx.local_func(local_func_index, llvm_sig, intrinsics, builder); builder.build_call(func_ptr, ¶ms, &state.var_name()) } LocalOrImport::Import(import_func_index) => { - let (func_ptr_untyped, ctx_ptr) = ctx.imported_func(import_func_index); + let (func_ptr_untyped, ctx_ptr) = + ctx.imported_func(import_func_index, intrinsics); let params: Vec<_> = [ctx_ptr.as_basic_value_enum()] .iter() .chain(state.peekn(func_sig.params().len())?.iter()) @@ -710,8 +971,9 @@ fn parse_function( } Operator::CallIndirect { index, table_index } => { let sig_index = SigIndex::new(index as usize); - let expected_dynamic_sigindex = ctx.dynamic_sigindex(sig_index); - let (table_base, table_bound) = ctx.table(TableIndex::new(table_index as usize)); + let expected_dynamic_sigindex = ctx.dynamic_sigindex(sig_index, intrinsics); + let (table_base, table_bound) = + ctx.table(TableIndex::new(table_index as usize), intrinsics, builder); let func_index = state.pop1()?.into_int_value(); // We assume the table has the `anyfunc` element type. @@ -1468,7 +1730,7 @@ fn parse_function( context, &function, -2147483904.0, - 2_147_483_648.0, + 2147483648.0, v1, ); let res = @@ -1482,8 +1744,8 @@ fn parse_function( intrinsics, context, &function, - -2_147_483_649.0, - 2_147_483_648.0, + -2147483649.0, + 2147483648.0, v1, ); let res = @@ -1503,8 +1765,8 @@ fn parse_function( intrinsics, context, &function, - -9_223_373_136_366_403_584.0, - 9_223_372_036_854_775_808.0, + -9223373136366403584.0, + 9223372036854775808.0, v1, ); let res = @@ -1728,7 +1990,7 @@ fn parse_function( * Load and Store instructions. * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#load-and-store-instructions ***************************/ - Operator::I32Load { memarg } => { + Operator::I32Load { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1742,7 +2004,7 @@ fn parse_function( let result = builder.build_load(effective_address, &state.var_name()); state.push1(result); } - Operator::I64Load { memarg } => { + Operator::I64Load { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1756,7 +2018,7 @@ fn parse_function( let result = builder.build_load(effective_address, &state.var_name()); state.push1(result); } - Operator::F32Load { memarg } => { + Operator::F32Load { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1770,7 +2032,7 @@ fn parse_function( let result = builder.build_load(effective_address, &state.var_name()); state.push1(result); } - Operator::F64Load { memarg } => { + Operator::F64Load { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1785,7 +2047,7 @@ fn parse_function( state.push1(result); } - Operator::I32Store { memarg } => { + Operator::I32Store { ref memarg } => { let value = state.pop1()?; let effective_address = resolve_memory_ptr( builder, @@ -1799,7 +2061,7 @@ fn parse_function( )?; builder.build_store(effective_address, value); } - Operator::I64Store { memarg } => { + Operator::I64Store { ref memarg } => { let value = state.pop1()?; let effective_address = resolve_memory_ptr( builder, @@ -1813,7 +2075,7 @@ fn parse_function( )?; builder.build_store(effective_address, value); } - Operator::F32Store { memarg } => { + Operator::F32Store { ref memarg } => { let value = state.pop1()?; let effective_address = resolve_memory_ptr( builder, @@ -1827,7 +2089,7 @@ fn parse_function( )?; builder.build_store(effective_address, value); } - Operator::F64Store { memarg } => { + Operator::F64Store { ref memarg } => { let value = state.pop1()?; let effective_address = resolve_memory_ptr( builder, @@ -1842,7 +2104,7 @@ fn parse_function( builder.build_store(effective_address, value); } - Operator::I32Load8S { memarg } => { + Operator::I32Load8S { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1860,7 +2122,7 @@ fn parse_function( builder.build_int_s_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); state.push1(result); } - Operator::I32Load16S { memarg } => { + Operator::I32Load16S { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1878,7 +2140,7 @@ fn parse_function( builder.build_int_s_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); state.push1(result); } - Operator::I64Load8S { memarg } => { + Operator::I64Load8S { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1896,7 +2158,7 @@ fn parse_function( builder.build_int_s_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); state.push1(result); } - Operator::I64Load16S { memarg } => { + Operator::I64Load16S { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1914,7 +2176,7 @@ fn parse_function( builder.build_int_s_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); state.push1(result); } - Operator::I64Load32S { memarg } => { + Operator::I64Load32S { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1933,7 +2195,7 @@ fn parse_function( state.push1(result); } - Operator::I32Load8U { memarg } => { + Operator::I32Load8U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1951,7 +2213,7 @@ fn parse_function( builder.build_int_z_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); state.push1(result); } - Operator::I32Load16U { memarg } => { + Operator::I32Load16U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1969,7 +2231,7 @@ fn parse_function( builder.build_int_z_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); state.push1(result); } - Operator::I64Load8U { memarg } => { + Operator::I64Load8U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1987,7 +2249,7 @@ fn parse_function( builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); state.push1(result); } - Operator::I64Load16U { memarg } => { + Operator::I64Load16U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -2005,7 +2267,7 @@ fn parse_function( builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); state.push1(result); } - Operator::I64Load32U { memarg } => { + Operator::I64Load32U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -2024,7 +2286,7 @@ fn parse_function( state.push1(result); } - Operator::I32Store8 { memarg } | Operator::I64Store8 { memarg } => { + Operator::I32Store8 { ref memarg } | Operator::I64Store8 { ref memarg } => { let value = state.pop1()?.into_int_value(); let effective_address = resolve_memory_ptr( builder, @@ -2040,7 +2302,7 @@ fn parse_function( builder.build_int_truncate(value, intrinsics.i8_ty, &state.var_name()); builder.build_store(effective_address, narrow_value); } - Operator::I32Store16 { memarg } | Operator::I64Store16 { memarg } => { + Operator::I32Store16 { ref memarg } | Operator::I64Store16 { ref memarg } => { let value = state.pop1()?.into_int_value(); let effective_address = resolve_memory_ptr( builder, @@ -2056,7 +2318,7 @@ fn parse_function( builder.build_int_truncate(value, intrinsics.i16_ty, &state.var_name()); builder.build_store(effective_address, narrow_value); } - Operator::I64Store32 { memarg } => { + Operator::I64Store32 { ref memarg } => { let value = state.pop1()?.into_int_value(); let effective_address = resolve_memory_ptr( builder, @@ -2139,324 +2401,239 @@ fn parse_function( ); state.push1(result.try_as_basic_value().left().unwrap()); } - op @ _ => { + _ => { unimplemented!("{:?}", op); } } + + Ok(()) } - let results = state.popn_save(func_sig.returns().len())?; + fn finalize(&mut self) -> Result<(), CodegenError> { + let results = self.state.popn_save(self.func_sig.returns().len())?; - match results.as_slice() { - [] => { - builder.build_return(None); - } - [one_value] => { - builder.build_return(Some(one_value)); - } - _ => { - // 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") + match results.as_slice() { + [] => { + self.builder.as_ref().unwrap().build_return(None); + } + [one_value] => { + self.builder.as_ref().unwrap().build_return(Some(one_value)); + } + _ => unimplemented!("multi-value returns not yet implemented"), } + Ok(()) } - - Ok(()) } -fn trap_if_not_representatable_as_int( - builder: &Builder, - intrinsics: &Intrinsics, - context: &Context, - function: &FunctionValue, - lower_bounds: f64, - upper_bound: f64, - value: FloatValue, -) { - enum FloatSize { - Bits32, - Bits64, +impl From for CodegenError { + fn from(other: BinaryReaderError) -> CodegenError { + CodegenError { + message: format!("{:?}", other), + } } +} - let failure_block = context.append_basic_block(function, "conversion_failure_block"); - let continue_block = context.append_basic_block(function, "conversion_success_block"); +impl ModuleCodeGenerator + for LLVMModuleCodeGenerator +{ + fn new() -> LLVMModuleCodeGenerator { + let context = Context::create(); + let module = context.create_module("module"); + let builder = context.create_builder(); - let float_ty = value.get_type(); - let (int_ty, float_ptr_ty, float_size) = if float_ty == intrinsics.f32_ty { - (intrinsics.i32_ty, intrinsics.f32_ptr_ty, FloatSize::Bits32) - } else if float_ty == intrinsics.f64_ty { - (intrinsics.i64_ty, intrinsics.f64_ptr_ty, FloatSize::Bits64) - } else { - unreachable!() - }; + let intrinsics = Intrinsics::declare(&module, &context); - let (exponent, invalid_exponent) = { - let float_bits = { - let space = builder.build_alloca(int_ty, "space"); - let float_ptr = builder.build_pointer_cast(space, float_ptr_ty, "float_ptr"); - builder.build_store(float_ptr, value); - builder.build_load(space, "float_bits").into_int_value() - }; + let personality_func = module.add_function( + "__gxx_personality_v0", + intrinsics.i32_ty.fn_type(&[], false), + Some(Linkage::External), + ); - let (shift_amount, exponent_mask, invalid_exponent) = match float_size { - FloatSize::Bits32 => (23, 0b01111111100000000000000000000000, 0b11111111), - FloatSize::Bits64 => ( - 52, - 0b0111111111110000000000000000000000000000000000000000000000000000, - 0b11111111111, - ), - }; + let signatures = Map::new(); + + LLVMModuleCodeGenerator { + context: Some(context), + builder: Some(builder), + intrinsics: Some(intrinsics), + module, + functions: vec![], + signatures, + signatures_raw: Map::new(), + function_signatures: None, + func_import_count: 0, + personality_func, + } + } - builder.build_and( - float_bits, - int_ty.const_int(exponent_mask, false), - "masked_bits", - ); + fn backend_id() -> Backend { + Backend::LLVM + } - ( - builder.build_right_shift( - float_bits, - int_ty.const_int(shift_amount, false), - false, - "exponent", - ), - invalid_exponent, - ) - }; + fn check_precondition(&mut self, _module_info: &ModuleInfo) -> Result<(), CodegenError> { + Ok(()) + } - let is_invalid_float = builder.build_or( - builder.build_int_compare( - IntPredicate::EQ, - exponent, - int_ty.const_int(invalid_exponent, false), - "is_not_normal", - ), - builder.build_or( - builder.build_float_compare( - FloatPredicate::ULT, - value, - float_ty.const_float(lower_bounds), - "less_than_lower_bounds", + fn next_function(&mut self) -> Result<&mut LLVMFunctionCodeGenerator, CodegenError> { + // Creates a new function and returns the function-scope code generator for it. + let (context, builder, intrinsics) = match self.functions.last_mut() { + Some(x) => ( + x.context.take().unwrap(), + x.builder.take().unwrap(), + x.intrinsics.take().unwrap(), ), - builder.build_float_compare( - FloatPredicate::UGT, - value, - float_ty.const_float(upper_bound), - "greater_than_upper_bounds", + None => ( + self.context.take().unwrap(), + self.builder.take().unwrap(), + self.intrinsics.take().unwrap(), ), - "float_not_in_bounds", - ), - "is_invalid_float", - ); - - let is_invalid_float = builder - .build_call( - intrinsics.expect_i1, - &[ - is_invalid_float.as_basic_value_enum(), - intrinsics.i1_ty.const_int(0, false).as_basic_value_enum(), - ], - "is_invalid_float_expect", - ) - .try_as_basic_value() - .left() - .unwrap() - .into_int_value(); - - builder.build_conditional_branch(is_invalid_float, &failure_block, &continue_block); - builder.position_at_end(&failure_block); - builder.build_call( - intrinsics.throw_trap, - &[intrinsics.trap_illegal_arithmetic], - "throw", - ); - builder.build_unreachable(); - builder.position_at_end(&continue_block); -} - -fn trap_if_zero_or_overflow( - builder: &Builder, - intrinsics: &Intrinsics, - context: &Context, - function: &FunctionValue, - left: IntValue, - right: IntValue, -) { - let int_type = left.get_type(); - - let (min_value, neg_one_value) = if int_type == intrinsics.i32_ty { - let min_value = int_type.const_int(i32::min_value() as u64, false); - let neg_one_value = int_type.const_int(-1i32 as u32 as u64, false); - (min_value, neg_one_value) - } else if int_type == intrinsics.i64_ty { - let min_value = int_type.const_int(i64::min_value() as u64, false); - let neg_one_value = int_type.const_int(-1i64 as u64, false); - (min_value, neg_one_value) - } else { - unreachable!() - }; - - let should_trap = builder.build_or( - builder.build_int_compare( - IntPredicate::EQ, - right, - int_type.const_int(0, false), - "divisor_is_zero", - ), - builder.build_and( - builder.build_int_compare(IntPredicate::EQ, left, min_value, "left_is_min"), - builder.build_int_compare(IntPredicate::EQ, right, neg_one_value, "right_is_neg_one"), - "div_will_overflow", - ), - "div_should_trap", - ); - - let should_trap = builder - .build_call( - intrinsics.expect_i1, - &[ - should_trap.as_basic_value_enum(), - intrinsics.i1_ty.const_int(0, false).as_basic_value_enum(), - ], - "should_trap_expect", - ) - .try_as_basic_value() - .left() - .unwrap() - .into_int_value(); - - let shouldnt_trap_block = context.append_basic_block(function, "shouldnt_trap_block"); - let should_trap_block = context.append_basic_block(function, "should_trap_block"); - builder.build_conditional_branch(should_trap, &should_trap_block, &shouldnt_trap_block); - builder.position_at_end(&should_trap_block); - builder.build_call( - intrinsics.throw_trap, - &[intrinsics.trap_illegal_arithmetic], - "throw", - ); - builder.build_unreachable(); - builder.position_at_end(&shouldnt_trap_block); -} - -fn trap_if_zero( - builder: &Builder, - intrinsics: &Intrinsics, - context: &Context, - function: &FunctionValue, - value: IntValue, -) { - let int_type = value.get_type(); - let should_trap = builder.build_int_compare( - IntPredicate::EQ, - value, - int_type.const_int(0, false), - "divisor_is_zero", - ); + }; - let should_trap = builder - .build_call( - intrinsics.expect_i1, - &[ - should_trap.as_basic_value_enum(), - intrinsics.i1_ty.const_int(0, false).as_basic_value_enum(), - ], - "should_trap_expect", - ) - .try_as_basic_value() - .left() - .unwrap() - .into_int_value(); + let sig_id = self.function_signatures.as_ref().unwrap() + [FuncIndex::new(self.func_import_count + self.functions.len())]; + let func_sig = self.signatures_raw[sig_id].clone(); - let shouldnt_trap_block = context.append_basic_block(function, "shouldnt_trap_block"); - let should_trap_block = context.append_basic_block(function, "should_trap_block"); - builder.build_conditional_branch(should_trap, &should_trap_block, &shouldnt_trap_block); - builder.position_at_end(&should_trap_block); - builder.build_call( - intrinsics.throw_trap, - &[intrinsics.trap_illegal_arithmetic], - "throw", - ); - builder.build_unreachable(); - builder.position_at_end(&shouldnt_trap_block); -} + let function = self.module.add_function( + &format!("fn{}", self.func_import_count + self.functions.len()), + self.signatures[sig_id], + Some(Linkage::External), + ); + function.set_personality_function(self.personality_func); + + 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(); + + state.push_block(return_block, phis); + builder.position_at_end(&entry_block); + + let mut locals = Vec::new(); + locals.extend( + function + .get_param_iter() + .skip(1) + .enumerate() + .map(|(index, param)| { + let ty = param.get_type(); + + let alloca = builder.build_alloca(ty, &format!("local{}", index)); + builder.build_store(alloca, param); + alloca + }), + ); + let num_params = locals.len(); + + let code = LLVMFunctionCodeGenerator { + state, + context: Some(context), + builder: Some(builder), + intrinsics: Some(intrinsics), + function, + func_sig: func_sig, + locals, + signatures: self.signatures.clone(), + num_params, + ctx: None, + unreachable_depth: 0, + }; + self.functions.push(code); + Ok(self.functions.last_mut().unwrap()) + } -fn resolve_memory_ptr( - builder: &Builder, - intrinsics: &Intrinsics, - context: &Context, - function: &FunctionValue, - state: &mut State, - ctx: &mut CtxType, - memarg: MemoryImmediate, - ptr_ty: PointerType, -) -> Result { - // Ignore alignment hint for the time being. - let imm_offset = intrinsics.i64_ty.const_int(memarg.offset as u64, false); - let var_offset_i32 = state.pop1()?.into_int_value(); - let var_offset = - builder.build_int_z_extend(var_offset_i32, intrinsics.i64_ty, &state.var_name()); - let effective_offset = builder.build_int_add(var_offset, imm_offset, &state.var_name()); - let memory_cache = ctx.memory(MemoryIndex::new(0)); + fn finalize( + mut self, + module_info: &ModuleInfo, + ) -> Result<(LLVMBackend, Box), CodegenError> { + let (context, builder, intrinsics) = match self.functions.last_mut() { + Some(x) => ( + x.context.take().unwrap(), + x.builder.take().unwrap(), + x.intrinsics.take().unwrap(), + ), + None => ( + self.context.take().unwrap(), + self.builder.take().unwrap(), + self.intrinsics.take().unwrap(), + ), + }; + self.context = Some(context); + self.builder = Some(builder); + self.intrinsics = Some(intrinsics); + + generate_trampolines( + module_info, + &self.signatures, + &self.module, + self.context.as_ref().unwrap(), + self.builder.as_ref().unwrap(), + self.intrinsics.as_ref().unwrap(), + ); - let mem_base_int = match memory_cache { - MemoryCache::Dynamic { - ptr_to_base_ptr, - ptr_to_bounds, - } => { - let base = builder - .build_load(ptr_to_base_ptr, "base") - .into_pointer_value(); - let bounds = builder.build_load(ptr_to_bounds, "bounds").into_int_value(); + let pass_manager = PassManager::create_for_module(); + if cfg!(test) { + pass_manager.add_verifier_pass(); + } + pass_manager.add_function_inlining_pass(); + pass_manager.add_promote_memory_to_register_pass(); + pass_manager.add_cfg_simplification_pass(); + pass_manager.add_aggressive_inst_combiner_pass(); + pass_manager.add_merged_load_store_motion_pass(); + pass_manager.add_new_gvn_pass(); + pass_manager.add_aggressive_dce_pass(); + pass_manager.run_on_module(&self.module); + + // self.module.print_to_stderr(); + + let (backend, cache_gen) = LLVMBackend::new(self.module, self.intrinsics.take().unwrap()); + Ok((backend, Box::new(cache_gen))) + } - let base_as_int = builder.build_ptr_to_int(base, intrinsics.i64_ty, "base_as_int"); + fn feed_signatures(&mut self, signatures: Map) -> Result<(), CodegenError> { + self.signatures = signatures + .iter() + .map(|(_, sig)| { + func_sig_to_llvm( + self.context.as_ref().unwrap(), + self.intrinsics.as_ref().unwrap(), + sig, + ) + }) + .collect(); + self.signatures_raw = signatures.clone(); + Ok(()) + } - let base_in_bounds = builder.build_int_compare( - IntPredicate::ULT, - effective_offset, - bounds, - "base_in_bounds", - ); + fn feed_function_signatures( + &mut self, + assoc: Map, + ) -> Result<(), CodegenError> { + self.function_signatures = Some(Arc::new(assoc)); + Ok(()) + } - let base_in_bounds = builder - .build_call( - intrinsics.expect_i1, - &[ - base_in_bounds.as_basic_value_enum(), - intrinsics.i1_ty.const_int(1, false).as_basic_value_enum(), - ], - "base_in_bounds_expect", - ) - .try_as_basic_value() - .left() - .unwrap() - .into_int_value(); + fn feed_import_function(&mut self) -> Result<(), CodegenError> { + self.func_import_count += 1; + Ok(()) + } - let in_bounds_continue_block = - context.append_basic_block(function, "in_bounds_continue_block"); - let not_in_bounds_block = context.append_basic_block(function, "not_in_bounds_block"); - builder.build_conditional_branch( - base_in_bounds, - &in_bounds_continue_block, - ¬_in_bounds_block, - ); - builder.position_at_end(¬_in_bounds_block); - builder.build_call( - intrinsics.throw_trap, - &[intrinsics.trap_memory_oob], - "throw", - ); - builder.build_unreachable(); - builder.position_at_end(&in_bounds_continue_block); + unsafe fn from_cache(artifact: Artifact, _: Token) -> Result { + let (info, _, memory) = artifact.consume(); + let (backend, cache_gen) = + LLVMBackend::from_buffer(memory).map_err(CacheError::DeserializeError)?; - base_as_int - } - MemoryCache::Static { - base_ptr, - bounds: _, - } => builder.build_ptr_to_int(base_ptr, intrinsics.i64_ty, "base_as_int"), - }; + Ok(ModuleInner { + runnable_module: Box::new(backend), + cache_gen: Box::new(cache_gen), - let effective_address_int = - builder.build_int_add(mem_base_int, effective_offset, &state.var_name()); - Ok(builder.build_int_to_ptr(effective_address_int, ptr_ty, &state.var_name())) + info, + }) + } } diff --git a/lib/llvm-backend/src/intrinsics.rs b/lib/llvm-backend/src/intrinsics.rs index 1b367f5d4f8..6250fd4fd44 100644 --- a/lib/llvm-backend/src/intrinsics.rs +++ b/lib/llvm-backend/src/intrinsics.rs @@ -369,31 +369,6 @@ impl Intrinsics { ctx_ptr_ty, } } - - pub fn ctx<'a>( - &'a self, - info: &'a ModuleInfo, - builder: &'a Builder, - func_value: &'a FunctionValue, - cache_builder: Builder, - ) -> CtxType<'a> { - CtxType { - ctx_ptr_value: func_value.get_nth_param(0).unwrap().into_pointer_value(), - - builder, - intrinsics: self, - info, - cache_builder, - - cached_memories: HashMap::new(), - cached_tables: HashMap::new(), - cached_sigindices: HashMap::new(), - cached_globals: HashMap::new(), - cached_imported_functions: HashMap::new(), - - _phantom: PhantomData, - } - } } #[derive(Clone, Copy)] @@ -429,8 +404,6 @@ struct ImportedFuncCache { pub struct CtxType<'a> { ctx_ptr_value: PointerValue, - builder: &'a Builder, - intrinsics: &'a Intrinsics, info: &'a ModuleInfo, cache_builder: Builder, @@ -444,16 +417,36 @@ pub struct CtxType<'a> { } impl<'a> CtxType<'a> { + pub fn new( + info: &'a ModuleInfo, + func_value: &'a FunctionValue, + cache_builder: Builder, + ) -> CtxType<'a> { + CtxType { + ctx_ptr_value: func_value.get_nth_param(0).unwrap().into_pointer_value(), + + info, + cache_builder, + + cached_memories: HashMap::new(), + cached_tables: HashMap::new(), + cached_sigindices: HashMap::new(), + cached_globals: HashMap::new(), + cached_imported_functions: HashMap::new(), + + _phantom: PhantomData, + } + } + pub fn basic(&self) -> BasicValueEnum { self.ctx_ptr_value.as_basic_value_enum() } - pub fn memory(&mut self, index: MemoryIndex) -> MemoryCache { - let (cached_memories, info, ctx_ptr_value, intrinsics, cache_builder) = ( + pub fn memory(&mut self, index: MemoryIndex, intrinsics: &Intrinsics) -> MemoryCache { + let (cached_memories, info, ctx_ptr_value, cache_builder) = ( &mut self.cached_memories, self.info, self.ctx_ptr_value, - self.intrinsics, &self.cache_builder, ); @@ -514,13 +507,16 @@ impl<'a> CtxType<'a> { }) } - pub fn table(&mut self, index: TableIndex) -> (PointerValue, IntValue) { - let (cached_tables, builder, info, ctx_ptr_value, intrinsics, cache_builder) = ( + pub fn table( + &mut self, + index: TableIndex, + intrinsics: &Intrinsics, + builder: &Builder, + ) -> (PointerValue, IntValue) { + let (cached_tables, info, ctx_ptr_value, cache_builder) = ( &mut self.cached_tables, - self.builder, self.info, self.ctx_ptr_value, - self.intrinsics, &self.cache_builder, ); @@ -575,41 +571,39 @@ impl<'a> CtxType<'a> { ) } - pub fn local_func(&mut self, index: LocalFuncIndex, fn_ty: FunctionType) -> PointerValue { - let local_func_array_ptr_ptr = unsafe { - self.builder - .build_struct_gep(self.ctx_ptr_value, 8, "local_func_array_ptr_ptr") - }; - let local_func_array_ptr = self - .builder + pub fn local_func( + &mut self, + index: LocalFuncIndex, + fn_ty: FunctionType, + intrinsics: &Intrinsics, + builder: &Builder, + ) -> PointerValue { + let local_func_array_ptr_ptr = + unsafe { builder.build_struct_gep(self.ctx_ptr_value, 8, "local_func_array_ptr_ptr") }; + let local_func_array_ptr = builder .build_load(local_func_array_ptr_ptr, "local_func_array_ptr") .into_pointer_value(); let local_func_ptr_ptr = unsafe { - self.builder.build_in_bounds_gep( + builder.build_in_bounds_gep( local_func_array_ptr, - &[self - .intrinsics - .i32_ty - .const_int(index.index() as u64, false)], + &[intrinsics.i32_ty.const_int(index.index() as u64, false)], "local_func_ptr_ptr", ) }; - let local_func_ptr = self - .builder + let local_func_ptr = builder .build_load(local_func_ptr_ptr, "local_func_ptr") .into_pointer_value(); - self.builder.build_pointer_cast( + builder.build_pointer_cast( local_func_ptr, fn_ty.ptr_type(AddressSpace::Generic), "local_func_ptr", ) } - pub fn dynamic_sigindex(&mut self, index: SigIndex) -> IntValue { - let (cached_sigindices, ctx_ptr_value, intrinsics, cache_builder) = ( + pub fn dynamic_sigindex(&mut self, index: SigIndex, intrinsics: &Intrinsics) -> IntValue { + let (cached_sigindices, ctx_ptr_value, cache_builder) = ( &mut self.cached_sigindices, self.ctx_ptr_value, - self.intrinsics, &self.cache_builder, ); @@ -636,12 +630,11 @@ impl<'a> CtxType<'a> { }) } - pub fn global_cache(&mut self, index: GlobalIndex) -> GlobalCache { - let (cached_globals, ctx_ptr_value, info, intrinsics, cache_builder) = ( + pub fn global_cache(&mut self, index: GlobalIndex, intrinsics: &Intrinsics) -> GlobalCache { + let (cached_globals, ctx_ptr_value, info, cache_builder) = ( &mut self.cached_globals, self.ctx_ptr_value, self.info, - self.intrinsics, &self.cache_builder, ); @@ -712,11 +705,14 @@ impl<'a> CtxType<'a> { }) } - pub fn imported_func(&mut self, index: ImportedFuncIndex) -> (PointerValue, PointerValue) { - let (cached_imported_functions, ctx_ptr_value, intrinsics, cache_builder) = ( + pub fn imported_func( + &mut self, + index: ImportedFuncIndex, + intrinsics: &Intrinsics, + ) -> (PointerValue, PointerValue) { + let (cached_imported_functions, ctx_ptr_value, cache_builder) = ( &mut self.cached_imported_functions, self.ctx_ptr_value, - self.intrinsics, &self.cache_builder, ); diff --git a/lib/llvm-backend/src/lib.rs b/lib/llvm-backend/src/lib.rs index 6ce8139c327..1d9a030e6ce 100644 --- a/lib/llvm-backend/src/lib.rs +++ b/lib/llvm-backend/src/lib.rs @@ -1,13 +1,5 @@ #![cfg_attr(nightly, feature(unwind_attributes))] -use wasmer_runtime_core::{ - backend::{Compiler, CompilerConfig, Token}, - cache::{Artifact, Error as CacheError}, - error::CompileError, - module::ModuleInner, -}; -use wasmparser::{self, WasmDecoder}; - mod backend; mod code; mod intrinsics; @@ -16,111 +8,11 @@ mod read_info; mod state; mod trampolines; -pub struct LLVMCompiler { - _private: (), -} - -impl LLVMCompiler { - pub fn new() -> Self { - Self { _private: () } - } -} - -impl Compiler for LLVMCompiler { - fn compile( - &self, - wasm: &[u8], - compiler_config: CompilerConfig, - _: Token, - ) -> Result { - validate(wasm)?; - - let (info, code_reader) = read_info::read_module(wasm, compiler_config).unwrap(); - let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap(); - - let (backend, cache_gen) = backend::LLVMBackend::new(module, intrinsics); - - Ok(ModuleInner { - runnable_module: Box::new(backend), - cache_gen: Box::new(cache_gen), - - info, - }) - } - - unsafe fn from_cache(&self, artifact: Artifact, _: Token) -> Result { - let (info, _, memory) = artifact.consume(); - let (backend, cache_gen) = - backend::LLVMBackend::from_buffer(memory).map_err(CacheError::DeserializeError)?; - - Ok(ModuleInner { - runnable_module: Box::new(backend), - cache_gen: Box::new(cache_gen), - - info, - }) - } -} - -fn validate(bytes: &[u8]) -> Result<(), CompileError> { - let mut parser = wasmparser::ValidatingParser::new( - bytes, - Some(wasmparser::ValidatingParserConfig { - operator_config: wasmparser::OperatorValidatorConfig { - enable_threads: false, - enable_reference_types: false, - enable_simd: false, - enable_bulk_memory: false, - }, - mutable_global_imports: false, - }), - ); - - loop { - let state = parser.read(); - match *state { - wasmparser::ParserState::EndWasm => break Ok(()), - wasmparser::ParserState::Error(err) => Err(CompileError::ValidationError { - msg: err.message.to_string(), - })?, - _ => {} - } - } -} - -#[test] -fn test_read_module() { - use std::mem::transmute; - use wabt::wat2wasm; - use wasmer_runtime_core::{ - backend::RunnableModule, structures::TypedIndex, types::LocalFuncIndex, vm, - }; - // let wasm = include_bytes!("../../spectests/examples/simple/simple.wasm") as &[u8]; - let wat = r#" - (module - (type $t0 (func (param i32) (result i32))) - (type $t1 (func (result i32))) - (memory 1) - (global $g0 (mut i32) (i32.const 0)) - (func $foo (type $t0) (param i32) (result i32) - get_local 0 - )) - "#; - let wasm = wat2wasm(wat).unwrap(); - - let (info, code_reader) = read_info::read_module(&wasm, Default::default()).unwrap(); - - let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap(); - - let (backend, _) = backend::LLVMBackend::new(module, intrinsics); - - let func_ptr = backend.get_func(&info, LocalFuncIndex::new(0)).unwrap(); - - println!("func_ptr: {:p}", func_ptr.as_ptr()); +use wasmer_runtime_core::codegen::SimpleStreamingCompilerGen; - unsafe { - let func: unsafe extern "C" fn(*mut vm::Ctx, i32) -> i32 = transmute(func_ptr); - let result = func(0 as _, 42); - println!("result: {}", result); - } -} +pub type LLVMCompiler = SimpleStreamingCompilerGen< + code::LLVMModuleCodeGenerator, + code::LLVMFunctionCodeGenerator, + backend::LLVMBackend, + code::CodegenError, +>; diff --git a/lib/llvm-backend/src/read_info.rs b/lib/llvm-backend/src/read_info.rs index 5ebfa5868f4..572469ce2b2 100644 --- a/lib/llvm-backend/src/read_info.rs +++ b/lib/llvm-backend/src/read_info.rs @@ -1,289 +1,5 @@ -use wasmer_runtime_core::{ - backend::{Backend, CompilerConfig}, - module::{ - DataInitializer, ExportIndex, ImportName, ModuleInfo, StringTable, StringTableBuilder, - TableInitializer, - }, - structures::{Map, TypedIndex}, - types::{ - ElementType, FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit, - ImportedGlobalIndex, Initializer, MemoryDescriptor, MemoryIndex, SigIndex, TableDescriptor, - TableIndex, Type, Value, - }, - units::Pages, -}; -use wasmparser::{ - BinaryReaderError, CodeSectionReader, Data, DataKind, Element, ElementKind, Export, - ExternalKind, FuncType, Import, ImportSectionEntryType, InitExpr, ModuleReader, Operator, - SectionCode, Type as WpType, -}; - -use hashbrown::HashMap; - -pub fn read_module( - wasm: &[u8], - compiler_config: CompilerConfig, -) -> Result<(ModuleInfo, CodeSectionReader), BinaryReaderError> { - let mut info = ModuleInfo { - memories: Map::new(), - globals: Map::new(), - tables: Map::new(), - - imported_functions: Map::new(), - imported_memories: Map::new(), - imported_tables: Map::new(), - imported_globals: Map::new(), - - exports: Default::default(), - - data_initializers: Vec::new(), - elem_initializers: Vec::new(), - - start_func: None, - - func_assoc: Map::new(), - signatures: Map::new(), - backend: Backend::LLVM, - - namespace_table: StringTable::new(), - name_table: StringTable::new(), - - em_symbol_map: compiler_config.symbol_map.clone(), - - custom_sections: HashMap::new(), - }; - - let mut reader = ModuleReader::new(wasm)?; - let mut code_reader = None; - - loop { - if reader.eof() { - return Ok((info, code_reader.unwrap())); - } - - let section = reader.read()?; - - match section.code { - SectionCode::Type => { - let type_reader = section.get_type_section_reader()?; - - for ty in type_reader { - let ty = ty?; - info.signatures.push(func_type_to_func_sig(ty)?); - } - } - SectionCode::Import => { - let import_reader = section.get_import_section_reader()?; - let mut namespace_builder = StringTableBuilder::new(); - let mut name_builder = StringTableBuilder::new(); - - for import in import_reader { - let Import { module, field, ty } = import?; - - let namespace_index = namespace_builder.register(module); - let name_index = name_builder.register(field); - let import_name = ImportName { - namespace_index, - name_index, - }; - - match ty { - ImportSectionEntryType::Function(sigindex) => { - let sigindex = SigIndex::new(sigindex as usize); - info.imported_functions.push(import_name); - info.func_assoc.push(sigindex); - } - ImportSectionEntryType::Table(table_ty) => { - assert_eq!(table_ty.element_type, WpType::AnyFunc); - let table_desc = TableDescriptor { - element: ElementType::Anyfunc, - minimum: table_ty.limits.initial, - maximum: table_ty.limits.maximum, - }; - - info.imported_tables.push((import_name, table_desc)); - } - ImportSectionEntryType::Memory(memory_ty) => { - let mem_desc = MemoryDescriptor { - minimum: Pages(memory_ty.limits.initial), - maximum: memory_ty.limits.maximum.map(|max| Pages(max)), - shared: memory_ty.shared, - }; - info.imported_memories.push((import_name, mem_desc)); - } - ImportSectionEntryType::Global(global_ty) => { - let global_desc = GlobalDescriptor { - mutable: global_ty.mutable, - ty: type_to_type(global_ty.content_type)?, - }; - info.imported_globals.push((import_name, global_desc)); - } - } - } - - info.namespace_table = namespace_builder.finish(); - info.name_table = name_builder.finish(); - } - SectionCode::Function => { - let func_decl_reader = section.get_function_section_reader()?; - - for sigindex in func_decl_reader { - let sigindex = sigindex?; - - let sigindex = SigIndex::new(sigindex as usize); - info.func_assoc.push(sigindex); - } - } - SectionCode::Table => { - let table_decl_reader = section.get_table_section_reader()?; - - for table_ty in table_decl_reader { - let table_ty = table_ty?; - - let table_desc = TableDescriptor { - element: ElementType::Anyfunc, - minimum: table_ty.limits.initial, - maximum: table_ty.limits.maximum, - }; - - info.tables.push(table_desc); - } - } - SectionCode::Memory => { - let mem_decl_reader = section.get_memory_section_reader()?; - - for memory_ty in mem_decl_reader { - let memory_ty = memory_ty?; - - let mem_desc = MemoryDescriptor { - minimum: Pages(memory_ty.limits.initial), - maximum: memory_ty.limits.maximum.map(|max| Pages(max)), - shared: memory_ty.shared, - }; - - info.memories.push(mem_desc); - } - } - SectionCode::Global => { - let global_decl_reader = section.get_global_section_reader()?; - - for global in global_decl_reader { - let global = global?; - - let desc = GlobalDescriptor { - mutable: global.ty.mutable, - ty: type_to_type(global.ty.content_type)?, - }; - - let global_init = GlobalInit { - desc, - init: eval_init_expr(&global.init_expr)?, - }; - - info.globals.push(global_init); - } - } - SectionCode::Export => { - let export_reader = section.get_export_section_reader()?; - - for export in export_reader { - let Export { field, kind, index } = export?; - - let export_index = match kind { - ExternalKind::Function => ExportIndex::Func(FuncIndex::new(index as usize)), - ExternalKind::Table => ExportIndex::Table(TableIndex::new(index as usize)), - ExternalKind::Memory => { - ExportIndex::Memory(MemoryIndex::new(index as usize)) - } - ExternalKind::Global => { - ExportIndex::Global(GlobalIndex::new(index as usize)) - } - }; - - info.exports.insert(field.to_string(), export_index); - } - } - SectionCode::Start => { - let start_index = section.get_start_section_content()?; - - info.start_func = Some(FuncIndex::new(start_index as usize)); - } - SectionCode::Element => { - let element_reader = section.get_element_section_reader()?; - - for element in element_reader { - let Element { kind, items } = element?; - - match kind { - ElementKind::Active { - table_index, - init_expr, - } => { - let table_index = TableIndex::new(table_index as usize); - let base = eval_init_expr(&init_expr)?; - let items_reader = items.get_items_reader()?; - - let elements: Vec<_> = items_reader - .into_iter() - .map(|res| res.map(|index| FuncIndex::new(index as usize))) - .collect::>()?; - - let table_init = TableInitializer { - table_index, - base, - elements, - }; - - info.elem_initializers.push(table_init); - } - ElementKind::Passive(_ty) => { - return Err(BinaryReaderError { - message: "passive tables are not yet supported", - offset: -1isize as usize, - }); - } - } - } - } - SectionCode::Code => { - code_reader = Some(section.get_code_section_reader()?); - } - SectionCode::Data => { - let data_reader = section.get_data_section_reader()?; - - for data in data_reader { - let Data { kind, data } = data?; - - match kind { - DataKind::Active { - memory_index, - init_expr, - } => { - let memory_index = MemoryIndex::new(memory_index as usize); - let base = eval_init_expr(&init_expr)?; - - let data_init = DataInitializer { - memory_index, - base, - data: data.to_vec(), - }; - - info.data_initializers.push(data_init); - } - DataKind::Passive => { - return Err(BinaryReaderError { - message: "passive memories are not yet supported", - offset: -1isize as usize, - }); - } - } - } - } - SectionCode::DataCount => {} - SectionCode::Custom { .. } => {} - } - } -} +use wasmer_runtime_core::types::Type; +use wasmparser::{BinaryReaderError, Type as WpType}; pub fn type_to_type(ty: WpType) -> Result { Ok(match ty { @@ -305,46 +21,3 @@ pub fn type_to_type(ty: WpType) -> Result { } }) } - -fn func_type_to_func_sig(func_ty: FuncType) -> Result { - assert_eq!(func_ty.form, WpType::Func); - - Ok(FuncSig::new( - func_ty - .params - .iter() - .cloned() - .map(type_to_type) - .collect::, _>>()?, - func_ty - .returns - .iter() - .cloned() - .map(type_to_type) - .collect::, _>>()?, - )) -} - -fn eval_init_expr(expr: &InitExpr) -> Result { - let mut reader = expr.get_operators_reader(); - let (op, offset) = reader.read_with_offset()?; - Ok(match op { - Operator::GetGlobal { global_index } => { - Initializer::GetGlobal(ImportedGlobalIndex::new(global_index as usize)) - } - Operator::I32Const { value } => Initializer::Const(Value::I32(value)), - Operator::I64Const { value } => Initializer::Const(Value::I64(value)), - Operator::F32Const { value } => { - Initializer::Const(Value::F32(f32::from_bits(value.bits()))) - } - Operator::F64Const { value } => { - Initializer::Const(Value::F64(f64::from_bits(value.bits()))) - } - _ => { - return Err(BinaryReaderError { - message: "init expr evaluation failed: unsupported opcode", - offset, - }); - } - }) -} diff --git a/lib/runtime-core/src/codegen.rs b/lib/runtime-core/src/codegen.rs index 777104001f1..2ba85863c94 100644 --- a/lib/runtime-core/src/codegen.rs +++ b/lib/runtime-core/src/codegen.rs @@ -8,10 +8,12 @@ use crate::{ types::{FuncIndex, FuncSig, SigIndex}, }; use smallvec::SmallVec; +use std::fmt; use std::fmt::Debug; use std::marker::PhantomData; use wasmparser::{Operator, Type as WpType}; +#[derive(Debug)] pub enum Event<'a, 'b> { Internal(InternalEvent), Wasm(&'b Operator<'a>), @@ -25,6 +27,19 @@ pub enum InternalEvent { GetInternal(u32), } +impl fmt::Debug for InternalEvent { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + InternalEvent::FunctionBegin(_) => write!(f, "FunctionBegin"), + InternalEvent::FunctionEnd => write!(f, "FunctionEnd"), + InternalEvent::Breakpoint(_) => write!(f, "Breakpoint"), + InternalEvent::SetInternal(_) => write!(f, "SetInternal"), + InternalEvent::GetInternal(_) => write!(f, "GetInternal"), + _ => panic!("unknown event"), + } + } +} + pub struct BkptInfo {} pub trait ModuleCodeGenerator, RM: RunnableModule, E: Debug> { @@ -34,7 +49,7 @@ pub trait ModuleCodeGenerator, RM: RunnableModule, /// Creates a new function and returns the function-scope code generator for it. fn next_function(&mut self) -> Result<&mut FCG, E>; - fn finalize(self, module_info: &ModuleInfo) -> Result; + fn finalize(self, module_info: &ModuleInfo) -> Result<(RM, Box), E>; fn feed_signatures(&mut self, signatures: Map) -> Result<(), E>; /// Sets function signatures. @@ -42,6 +57,8 @@ pub trait ModuleCodeGenerator, RM: RunnableModule, /// Adds an import function. fn feed_import_function(&mut self) -> Result<(), E>; + + unsafe fn from_cache(cache: Artifact, _: Token) -> Result; } pub struct StreamingCompiler< @@ -115,15 +132,6 @@ impl< compiler_config: CompilerConfig, _: Token, ) -> CompileResult { - struct Placeholder; - impl CacheGen for Placeholder { - fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError> { - Err(CacheError::Unknown( - "the streaming compiler API doesn't support caching yet".to_string(), - )) - } - } - let mut mcg = MCG::new(); let mut chain = (self.middleware_chain_generator)(); let info = crate::parse::read_module( @@ -133,22 +141,24 @@ impl< &mut chain, &compiler_config, )?; - let exec_context = mcg - .finalize(&info) - .map_err(|x| CompileError::InternalError { - msg: format!("{:?}", x), - })?; + let (exec_context, cache_gen) = + mcg.finalize(&info) + .map_err(|x| CompileError::InternalError { + msg: format!("{:?}", x), + })?; Ok(ModuleInner { - cache_gen: Box::new(Placeholder), + cache_gen, runnable_module: Box::new(exec_context), - info: info, + info, }) } - unsafe fn from_cache(&self, _artifact: Artifact, _: Token) -> Result { - Err(CacheError::Unknown( - "the streaming compiler API doesn't support caching yet".to_string(), - )) + unsafe fn from_cache( + &self, + artifact: Artifact, + token: Token, + ) -> Result { + MCG::from_cache(artifact, token) } } @@ -245,7 +255,7 @@ pub trait FunctionCodeGenerator { fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), E>; /// Called before the first call to `feed_opcode`. - fn begin_body(&mut self) -> Result<(), E>; + fn begin_body(&mut self, module_info: &ModuleInfo) -> Result<(), E>; /// Called for each operator. fn feed_event(&mut self, op: Event, module_info: &ModuleInfo) -> Result<(), E>; diff --git a/lib/runtime-core/src/parse.rs b/lib/runtime-core/src/parse.rs index e8fabf1947c..e7e80fc2971 100644 --- a/lib/runtime-core/src/parse.rs +++ b/lib/runtime-core/src/parse.rs @@ -244,7 +244,7 @@ pub fn read_module< ParserState::CodeOperator(ref op) => { if !body_begun { body_begun = true; - fcg.begin_body() + fcg.begin_body(&info) .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; } middlewares diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs index 783ca13af1e..22687cd9fc6 100644 --- a/lib/singlepass-backend/src/codegen_x64.rs +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -10,10 +10,11 @@ use smallvec::SmallVec; use std::ptr::NonNull; use std::{any::Any, collections::HashMap, sync::Arc}; use wasmer_runtime_core::{ - backend::{Backend, RunnableModule}, + backend::{sys::Memory, Backend, CacheGen, RunnableModule, Token}, + cache::{Artifact, Error as CacheError}, codegen::*, memory::MemoryType, - module::ModuleInfo, + module::{ModuleInfo, ModuleInner}, structures::{Map, TypedIndex}, typed_func::Wasm, types::{ @@ -349,7 +350,10 @@ impl ModuleCodeGenerator Ok(self.functions.last_mut().unwrap()) } - fn finalize(mut self, _: &ModuleInfo) -> Result { + fn finalize( + mut self, + _: &ModuleInfo, + ) -> Result<(X64ExecutionContext, Box), CodegenError> { let (assembler, mut br_table_data, breakpoints) = match self.functions.last_mut() { Some(x) => ( x.assembler.take().unwrap(), @@ -404,15 +408,27 @@ impl ModuleCodeGenerator .collect(), ); - Ok(X64ExecutionContext { - code: output, - functions: self.functions, - signatures: self.signatures.as_ref().unwrap().clone(), - _br_table_data: br_table_data, - breakpoints: breakpoints, - func_import_count: self.func_import_count, - function_pointers: out_labels, - }) + struct Placeholder; + impl CacheGen for Placeholder { + fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError> { + Err(CacheError::Unknown( + "the singlepass backend doesn't support caching yet".to_string(), + )) + } + } + + Ok(( + X64ExecutionContext { + code: output, + functions: self.functions, + signatures: self.signatures.as_ref().unwrap().clone(), + _br_table_data: br_table_data, + breakpoints: breakpoints, + func_import_count: self.func_import_count, + function_pointers: out_labels, + }, + Box::new(Placeholder), + )) } fn feed_signatures(&mut self, signatures: Map) -> Result<(), CodegenError> { @@ -461,6 +477,12 @@ impl ModuleCodeGenerator Ok(()) } + + unsafe fn from_cache(artifact: Artifact, _: Token) -> Result { + Err(CacheError::Unknown( + "the singlepass compiler API doesn't support caching yet".to_string(), + )) + } } impl X64FunctionCode { @@ -1387,7 +1409,7 @@ impl FunctionCodeGenerator for X64FunctionCode { Ok(()) } - fn begin_body(&mut self) -> Result<(), CodegenError> { + fn begin_body(&mut self, module_info: &ModuleInfo) -> Result<(), CodegenError> { let a = self.assembler.as_mut().unwrap(); a.emit_push(Size::S64, Location::GPR(GPR::RBP)); a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RBP));