diff --git a/Cargo.lock b/Cargo.lock index 03a29e4ee..7995d9aa6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2300,7 +2300,7 @@ dependencies = [ "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "wasmer-runtime-core 0.3.0", - "wasmparser 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2433,11 +2433,6 @@ name = "wasmparser" version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "wasmparser" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "wasmparser" version = "0.29.2" @@ -2832,7 +2827,6 @@ dependencies = [ "checksum walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1" "checksum want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "797464475f30ddb8830cc529aaaae648d581f99e2036a928877dfde027ddf6b3" "checksum wasmparser 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b5e01c420bc7d36e778bd242e1167b079562ba8b34087122cc9057187026d060" -"checksum wasmparser 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)" = "40f426b1929bd26517fb10702e2a8e520d1845c49567aa4d244f426f10b206c1" "checksum wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)" = "981a8797cf89762e0233ec45fae731cb79a4dfaee12d9f0fe6cee01e4ac58d00" "checksum which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b57acb10231b9493c8472b20cb57317d0679a49e0bdbee44b3b803a6473af164" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" diff --git a/lib/llvm-backend/Cargo.toml b/lib/llvm-backend/Cargo.toml index 944db6d41..c1a1cbbdc 100644 --- a/lib/llvm-backend/Cargo.toml +++ b/lib/llvm-backend/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" [dependencies] wasmer-runtime-core = { path = "../runtime-core", version = "0.3.0" } inkwell = { git = "https://github.com/wasmerio/inkwell", branch = "llvm7-0" } -wasmparser = "0.28.0" +wasmparser = "0.29.2" hashbrown = "0.1.8" smallvec = "0.6.8" goblin = "0.0.20" diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs index 05cde0694..bc13bf281 100644 --- a/lib/llvm-backend/src/code.rs +++ b/lib/llvm-backend/src/code.rs @@ -8,7 +8,10 @@ use inkwell::{ AddressSpace, FloatPredicate, IntPredicate, }; use smallvec::SmallVec; +use std::{any::Any, collections::HashMap, sync::Arc}; use wasmer_runtime_core::{ + backend::Backend, + codegen::*, memory::MemoryType, module::ModuleInfo, structures::{Map, SliceMap, TypedIndex}, @@ -19,12 +22,14 @@ use wasmer_runtime_core::{ }; use wasmparser::{ BinaryReaderError, CodeSectionReader, LocalsReader, MemoryImmediate, Operator, OperatorsReader, + Type as WpType, }; use crate::intrinsics::{CtxType, GlobalCache, Intrinsics, MemoryCache}; use crate::read_info::type_to_type; use crate::state::{ControlFrame, IfElseState, State}; use crate::trampolines::generate_trampolines; +use crate::backend::LLVMBackend; 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)); @@ -2460,3 +2465,2246 @@ fn resolve_memory_ptr( 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: &'static str, +} + +pub struct LLVMModuleCodeGenerator { + context: Context, + builder: &'static Builder, + functions: Vec, + functions_llvm: &'static SliceMap, +// signatures: Option>>, + signatures: &'static SliceMap, + function_signatures: Option>>, + // function_labels: Option)>>, + // assembler: Option, + func_import_count: usize, + intrinsics: &'static Intrinsics, +// ctx: CtxType, +} + +// pub struct LLVMExecutionContext { +// // #[allow(dead_code)] +// // code: ExecutableBuffer, +// // #[allow(dead_code)] +// // functions: Vec, +// // function_pointers: Vec, +// // signatures: Arc>, +// // _br_table_data: Vec>, +// // breakpoints: Arc>>, +// // func_import_count: usize, +// } + +pub struct LLVMFunctionCodeGenerator { + state: State, + builder: &'static Builder, + context: &'static Context, + function: FunctionValue, + func_sig: &'static FuncSig, + intrinsics: &'static Intrinsics, + signatures: &'static SliceMap, + // signatures: Arc>, + + // function_signatures: Arc>, + + // assembler: Option, + // function_labels: Option)>>, + // br_table_data: Option>>, + // breakpoints: Option>>, + // returns: SmallVec<[WpType; 1]>, + locals: Vec, + // num_params: usize, + // num_locals: usize, + // value_stack: Vec<(Location, LocalOrTemp)>, + // control_stack: Vec, + // machine: Machine, + // unreachable_depth: usize, +} + +impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { + fn feed_return(&mut self, ty: WpType) -> Result<(), CodegenError> { + unimplemented!() + } + + fn feed_param(&mut self, ty: WpType) -> Result<(), CodegenError> { + unimplemented!() + } + + fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), CodegenError> { + unimplemented!() + } + + fn begin_body(&mut self) -> Result<(), CodegenError> { + unimplemented!() + } + + fn feed_event(&mut self, event: Event, module_info: &ModuleInfo) -> Result<(), CodegenError> { + +// let sig_index = info.func_assoc[func_index.convert_up(info)]; +// let func_sig = &info.signatures[sig_index]; +// +// let function = self.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(); +// +// state.push_block(return_block, phis); +// builder.position_at_end(&entry_block); +// +// let mut locals = Vec::with_capacity(locals_reader.get_count() as usize); // TODO fix capacity +// +// 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 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); +// +// 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(), +// }; +// +// for _ in 0..count { +// let alloca = builder.build_alloca(ty, &format!("local{}", param_len + local_idx)); +// +// builder.build_store(alloca, default_value); +// +// locals.push(alloca); +// local_idx += 1; +// } +// } +// +// 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 cache_builder = context.create_builder(); +// cache_builder.position_before(&entry_end_inst); + + + let mut state = &mut self.state; + let builder = self.builder; + let context = self.context; + let function = self.function; + let intrinsics = self.intrinsics; + let locals = &self.locals; + let info = module_info; + let signatures = self.signatures; + + + // TODO this should be done only once per function I believe + // just adding here to get compilation + let cache_builder = context.create_builder(); +// cache_builder.position_before(&entry_end_inst); + let mut ctx = intrinsics.ctx(info, builder, &function, cache_builder); +// self.ctx; + + + let op = match event { + Event::Wasm(x) => x, + Event::Internal(x) => { + unimplemented!() + } + }; + + let mut unreachable_depth = 0; + if !state.reachable { + match *op { + Operator::Block { ty: _ } | Operator::Loop { ty: _ } | Operator::If { ty: _ } => { + unreachable_depth += 1; +// continue; + return Ok(()); + } + Operator::Else => { + if unreachable_depth != 0 { + // continue; + return Ok(()); + } + } + Operator::End => { + if unreachable_depth != 0 { + unreachable_depth -= 1; + // continue; + return Ok(()); + } + } + _ => { + // continue; + return Ok(()); + } + } + } + + match *op { + /*************************** + * Control Flow instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#control-flow-instructions + ***************************/ + Operator::Block { ty } => { + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + + 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) { + 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() + }; + + state.push_block(end_block, phis); + 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"); + + 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)?; + + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + + 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 + // the corresponding phi. + for (phi, value) in frame.phis().iter().zip(values.iter()) { + phi.add_incoming(&[(value, ¤t_block)]); + } + + builder.build_unconditional_branch(frame.br_dest()); + + state.popn(value_len)?; + state.reachable = false; + } + Operator::BrIf { relative_depth } => { + let cond = state.pop1()?; + let frame = state.frame_at_depth(relative_depth)?; + + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + + 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 else_block = context.append_basic_block(&function, "else"); + + 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, 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 { + message: "not currently in a block", + offset: -1isize as usize, + })?; + + let (label_depths, default_depth) = table.read_table()?; + + let index = state.pop1()?; + + let default_frame = state.frame_at_depth(default_depth)?; + + let args = if default_frame.is_loop() { + &[] + } else { + let res_len = default_frame.phis().len(); + state.peekn(res_len)? + }; + + for (phi, value) in default_frame.phis().iter().zip(args.iter()) { + phi.add_incoming(&[(value, ¤t_block)]); + } + + let cases: Vec<_> = label_depths + .iter() + .enumerate() + .map(|(case_index, &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); + + for (phi, value) in frame.phis().iter().zip(args.iter()) { + phi.add_incoming(&[(value, ¤t_block)]); + } + + Ok((case_index_literal, frame.br_dest())) + }) + .collect::>()?; + + builder.build_switch(index.into_int_value(), default_frame.br_dest(), &cases[..]); + + state.popn(args.len())?; + state.reachable = false; + } + 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, + 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() { + if phi.count_incoming() != 0 { + state.push1(phi.as_basic_value()); + } else { + let basic_ty = phi.as_basic_value().get_type(); + let placeholder_value = match basic_ty { + BasicTypeEnum::IntType(int_ty) => { + int_ty.const_int(0, false).as_basic_value_enum() + } + BasicTypeEnum::FloatType(float_ty) => { + float_ty.const_float(0.0).as_basic_value_enum() + } + _ => unimplemented!(), + }; + state.push1(placeholder_value); + phi.as_instruction().erase_from_basic_block(); + } + } + } + Operator::Return => { + let frame = state.outermost_frame()?; + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + + builder.build_unconditional_branch(frame.br_dest()); + + let phis = frame.phis().to_vec(); + + for phi in phis.iter() { + let arg = state.pop1()?; + phi.add_incoming(&[(&arg, ¤t_block)]); + } + + state.reachable = false; + } + + Operator::Unreachable => { + // Emit an unreachable instruction. + // If llvm cannot prove that this is never touched, + // it will emit a `ud2` instruction on x86_64 arches. + + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_unreachable], + "throw", + ); + builder.build_unreachable(); + + state.reachable = false; + } + + /*************************** + * Basic instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#basic-instructions + ***************************/ + Operator::Nop => { + // Do nothing. + } + Operator::Drop => { + state.pop1()?; + } + + // Generate const values. + Operator::I32Const { value } => { + let i = intrinsics.i32_ty.const_int(value as u64, false); + state.push1(i); + } + Operator::I64Const { value } => { + let i = intrinsics.i64_ty.const_int(value as u64, false); + state.push1(i); + } + Operator::F32Const { value } => { + let bits = intrinsics.i32_ty.const_int(value.bits() as u64, false); + let space = + builder.build_alloca(intrinsics.f32_ty.as_basic_type_enum(), "const_space"); + let i32_space = + builder.build_pointer_cast(space, intrinsics.i32_ptr_ty, "i32_space"); + builder.build_store(i32_space, bits); + let f = builder.build_load(space, "f"); + state.push1(f); + } + Operator::F64Const { value } => { + let bits = intrinsics.i64_ty.const_int(value.bits(), false); + let space = + builder.build_alloca(intrinsics.f64_ty.as_basic_type_enum(), "const_space"); + let i64_space = + builder.build_pointer_cast(space, intrinsics.i64_ptr_ty, "i32_space"); + builder.build_store(i64_space, bits); + let f = builder.build_load(space, "f"); + state.push1(f); + } + + // Operate on locals. + Operator::GetLocal { local_index } => { + let pointer_value = locals[local_index as usize]; + let v = builder.build_load(pointer_value, &state.var_name()); + state.push1(v); + } + Operator::SetLocal { local_index } => { + let pointer_value = locals[local_index as usize]; + let v = state.pop1()?; + builder.build_store(pointer_value, v); + } + Operator::TeeLocal { local_index } => { + let pointer_value = locals[local_index as usize]; + let v = state.peek1()?; + builder.build_store(pointer_value, v); + } + + Operator::GetGlobal { global_index } => { + let index = GlobalIndex::new(global_index as usize); + let global_cache = ctx.global_cache(index); + match global_cache { + GlobalCache::Const { value } => { + state.push1(value); + } + GlobalCache::Mut { ptr_to_value } => { + let value = builder.build_load(ptr_to_value, "global_value"); + state.push1(value); + } + } + } + Operator::SetGlobal { global_index } => { + let value = state.pop1()?; + let index = GlobalIndex::new(global_index as usize); + let global_cache = ctx.global_cache(index); + match global_cache { + GlobalCache::Mut { ptr_to_value } => { + builder.build_store(ptr_to_value, value); + } + GlobalCache::Const { value: _ } => { + unreachable!("cannot set non-mutable globals") + } + } + } + + Operator::Select => { + let (v1, v2, cond) = state.pop3()?; + let cond_value = builder.build_int_compare( + IntPredicate::NE, + cond.into_int_value(), + intrinsics.i32_zero, + &state.var_name(), + ); + let res = builder.build_select(cond_value, v1, v2, &state.var_name()); + state.push1(res); + } + Operator::Call { function_index } => { + let func_index = FuncIndex::new(function_index as usize); + let sigindex = info.func_assoc[func_index]; + let llvm_sig = signatures[sigindex]; + let func_sig = &info.signatures[sigindex]; + + let call_site = match func_index.local_or_import(info) { + LocalOrImport::Local(local_func_index) => { + let params: Vec<_> = [ctx.basic()] + .iter() + .chain(state.peekn(func_sig.params().len())?.iter()) + .map(|v| *v) + .collect(); + + let func_ptr = ctx.local_func(local_func_index, llvm_sig); + + 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 params: Vec<_> = [ctx_ptr.as_basic_value_enum()] + .iter() + .chain(state.peekn(func_sig.params().len())?.iter()) + .map(|v| *v) + .collect(); + + let func_ptr_ty = llvm_sig.ptr_type(AddressSpace::Generic); + + let func_ptr = builder.build_pointer_cast( + func_ptr_untyped, + func_ptr_ty, + "typed_func_ptr", + ); + + builder.build_call(func_ptr, ¶ms, &state.var_name()) + } + }; + + state.popn(func_sig.params().len())?; + + if let Some(basic_value) = call_site.try_as_basic_value().left() { + match func_sig.returns().len() { + 1 => state.push1(basic_value), + count @ _ => { + // This is a multi-value return. + let struct_value = basic_value.into_struct_value(); + for i in 0..(count as u32) { + let value = builder + .build_extract_value(struct_value, i, &state.var_name()) + .unwrap(); + state.push1(value); + } + } + } + } + } + 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 func_index = state.pop1()?.into_int_value(); + + // We assume the table has the `anyfunc` element type. + let casted_table_base = builder.build_pointer_cast( + table_base, + intrinsics.anyfunc_ty.ptr_type(AddressSpace::Generic), + "casted_table_base", + ); + + let anyfunc_struct_ptr = unsafe { + builder.build_in_bounds_gep( + casted_table_base, + &[func_index], + "anyfunc_struct_ptr", + ) + }; + + // Load things from the anyfunc data structure. + let (func_ptr, ctx_ptr, found_dynamic_sigindex) = unsafe { + ( + builder + .build_load( + builder.build_struct_gep(anyfunc_struct_ptr, 0, "func_ptr_ptr"), + "func_ptr", + ) + .into_pointer_value(), + builder.build_load( + builder.build_struct_gep(anyfunc_struct_ptr, 1, "ctx_ptr_ptr"), + "ctx_ptr", + ), + builder + .build_load( + builder.build_struct_gep(anyfunc_struct_ptr, 2, "sigindex_ptr"), + "sigindex", + ) + .into_int_value(), + ) + }; + + let truncated_table_bounds = builder.build_int_truncate( + table_bound, + intrinsics.i32_ty, + "truncated_table_bounds", + ); + + // First, check if the index is outside of the table bounds. + let index_in_bounds = builder.build_int_compare( + IntPredicate::ULT, + func_index, + truncated_table_bounds, + "index_in_bounds", + ); + + let index_in_bounds = builder + .build_call( + intrinsics.expect_i1, + &[ + index_in_bounds.as_basic_value_enum(), + intrinsics.i1_ty.const_int(1, false).as_basic_value_enum(), + ], + "index_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( + index_in_bounds, + &in_bounds_continue_block, + ¬_in_bounds_block, + ); + builder.position_at_end(¬_in_bounds_block); + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_call_indirect_oob], + "throw", + ); + builder.build_unreachable(); + builder.position_at_end(&in_bounds_continue_block); + + // Next, check if the signature id is correct. + + let sigindices_equal = builder.build_int_compare( + IntPredicate::EQ, + expected_dynamic_sigindex, + found_dynamic_sigindex, + "sigindices_equal", + ); + + // Tell llvm that `expected_dynamic_sigindex` should equal `found_dynamic_sigindex`. + let sigindices_equal = builder + .build_call( + intrinsics.expect_i1, + &[ + sigindices_equal.as_basic_value_enum(), + intrinsics.i1_ty.const_int(1, false).as_basic_value_enum(), + ], + "sigindices_equal_expect", + ) + .try_as_basic_value() + .left() + .unwrap() + .into_int_value(); + + let continue_block = context.append_basic_block(&function, "continue_block"); + let sigindices_notequal_block = + context.append_basic_block(&function, "sigindices_notequal_block"); + builder.build_conditional_branch( + sigindices_equal, + &continue_block, + &sigindices_notequal_block, + ); + + builder.position_at_end(&sigindices_notequal_block); + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_call_indirect_sig], + "throw", + ); + builder.build_unreachable(); + builder.position_at_end(&continue_block); + + let wasmer_fn_sig = &info.signatures[sig_index]; + let fn_ty = signatures[sig_index]; + + let pushed_args = state.popn_save(wasmer_fn_sig.params().len())?; + + let args: Vec<_> = std::iter::once(ctx_ptr) + .chain(pushed_args.into_iter()) + .collect(); + + let typed_func_ptr = builder.build_pointer_cast( + func_ptr, + fn_ty.ptr_type(AddressSpace::Generic), + "typed_func_ptr", + ); + + let call_site = builder.build_call(typed_func_ptr, &args, "indirect_call"); + + match wasmer_fn_sig.returns() { + [] => {} + [_] => { + let value = call_site.try_as_basic_value().left().unwrap(); + state.push1(value); + } + _ => unimplemented!("multi-value returns"), + } + } + + /*************************** + * Integer Arithmetic instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#integer-arithmetic-instructions + ***************************/ + Operator::I32Add | Operator::I64Add => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_add(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32Sub | Operator::I64Sub => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_sub(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32Mul | Operator::I64Mul => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_mul(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32DivS | Operator::I64DivS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + + trap_if_zero_or_overflow(builder, intrinsics, context, &function, v1, v2); + + let res = builder.build_int_signed_div(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32DivU | Operator::I64DivU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + + trap_if_zero(builder, intrinsics, context, &function, v2); + + let res = builder.build_int_unsigned_div(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32RemS | Operator::I64RemS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + + trap_if_zero(builder, intrinsics, context, &function, v2); + + let res = builder.build_int_signed_rem(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32RemU | Operator::I64RemU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + + trap_if_zero(builder, intrinsics, context, &function, v2); + + let res = builder.build_int_unsigned_rem(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32And | Operator::I64And => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_and(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32Or | Operator::I64Or => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_or(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32Xor | Operator::I64Xor => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_xor(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32Shl | Operator::I64Shl => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_left_shift(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32ShrS | Operator::I64ShrS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_right_shift(v1, v2, true, &state.var_name()); + state.push1(res); + } + Operator::I32ShrU | Operator::I64ShrU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_right_shift(v1, v2, false, &state.var_name()); + state.push1(res); + } + Operator::I32Rotl => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let lhs = builder.build_left_shift(v1, v2, &state.var_name()); + let rhs = { + let int_width = intrinsics.i32_ty.const_int(32 as u64, false); + let rhs = builder.build_int_sub(int_width, v2, &state.var_name()); + builder.build_right_shift(v1, rhs, false, &state.var_name()) + }; + let res = builder.build_or(lhs, rhs, &state.var_name()); + state.push1(res); + } + Operator::I64Rotl => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let lhs = builder.build_left_shift(v1, v2, &state.var_name()); + let rhs = { + let int_width = intrinsics.i64_ty.const_int(64 as u64, false); + let rhs = builder.build_int_sub(int_width, v2, &state.var_name()); + builder.build_right_shift(v1, rhs, false, &state.var_name()) + }; + let res = builder.build_or(lhs, rhs, &state.var_name()); + state.push1(res); + } + Operator::I32Rotr => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let lhs = builder.build_right_shift(v1, v2, false, &state.var_name()); + let rhs = { + let int_width = intrinsics.i32_ty.const_int(32 as u64, false); + let rhs = builder.build_int_sub(int_width, v2, &state.var_name()); + builder.build_left_shift(v1, rhs, &state.var_name()) + }; + let res = builder.build_or(lhs, rhs, &state.var_name()); + state.push1(res); + } + Operator::I64Rotr => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let lhs = builder.build_right_shift(v1, v2, false, &state.var_name()); + let rhs = { + let int_width = intrinsics.i64_ty.const_int(64 as u64, false); + let rhs = builder.build_int_sub(int_width, v2, &state.var_name()); + builder.build_left_shift(v1, rhs, &state.var_name()) + }; + let res = builder.build_or(lhs, rhs, &state.var_name()); + state.push1(res); + } + Operator::I32Clz => { + let input = state.pop1()?; + let ensure_defined_zero = intrinsics + .i1_ty + .const_int(1 as u64, false) + .as_basic_value_enum(); + let res = builder + .build_call( + intrinsics.ctlz_i32, + &[input, ensure_defined_zero], + &state.var_name(), + ) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I64Clz => { + let input = state.pop1()?; + let ensure_defined_zero = intrinsics + .i1_ty + .const_int(1 as u64, false) + .as_basic_value_enum(); + let res = builder + .build_call( + intrinsics.ctlz_i64, + &[input, ensure_defined_zero], + &state.var_name(), + ) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I32Ctz => { + let input = state.pop1()?; + let ensure_defined_zero = intrinsics + .i1_ty + .const_int(1 as u64, false) + .as_basic_value_enum(); + let res = builder + .build_call( + intrinsics.cttz_i32, + &[input, ensure_defined_zero], + &state.var_name(), + ) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I64Ctz => { + let input = state.pop1()?; + let ensure_defined_zero = intrinsics + .i1_ty + .const_int(1 as u64, false) + .as_basic_value_enum(); + let res = builder + .build_call( + intrinsics.cttz_i64, + &[input, ensure_defined_zero], + &state.var_name(), + ) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I32Popcnt => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.ctpop_i32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I64Popcnt => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.ctpop_i64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I32Eqz => { + let input = state.pop1()?.into_int_value(); + let cond = builder.build_int_compare( + IntPredicate::EQ, + input, + intrinsics.i32_zero, + &state.var_name(), + ); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I64Eqz => { + let input = state.pop1()?.into_int_value(); + let cond = builder.build_int_compare( + IntPredicate::EQ, + input, + intrinsics.i64_zero, + &state.var_name(), + ); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + + /*************************** + * Floating-Point Arithmetic instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#floating-point-arithmetic-instructions + ***************************/ + Operator::F32Add | Operator::F64Add => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let res = builder.build_float_add(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::F32Sub | Operator::F64Sub => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let res = builder.build_float_sub(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::F32Mul | Operator::F64Mul => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let res = builder.build_float_mul(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::F32Div | Operator::F64Div => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let res = builder.build_float_div(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::F32Sqrt => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.sqrt_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Sqrt => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.sqrt_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Min => { + let (v1, v2) = state.pop2()?; + let res = builder + .build_call(intrinsics.minimum_f32, &[v1, v2], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Min => { + let (v1, v2) = state.pop2()?; + let res = builder + .build_call(intrinsics.minimum_f64, &[v1, v2], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Max => { + let (v1, v2) = state.pop2()?; + let res = builder + .build_call(intrinsics.maximum_f32, &[v1, v2], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Max => { + let (v1, v2) = state.pop2()?; + let res = builder + .build_call(intrinsics.maximum_f64, &[v1, v2], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Ceil => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.ceil_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Ceil => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.ceil_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Floor => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.floor_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Floor => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.floor_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Trunc => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.trunc_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Trunc => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.trunc_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Nearest => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.nearbyint_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Nearest => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.nearbyint_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Abs => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.fabs_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Abs => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.fabs_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Neg | Operator::F64Neg => { + let input = state.pop1()?.into_float_value(); + let res = builder.build_float_neg(input, &state.var_name()); + state.push1(res); + } + Operator::F32Copysign => { + let (mag, sgn) = state.pop2()?; + let res = builder + .build_call(intrinsics.copysign_f32, &[mag, sgn], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Copysign => { + let (msg, sgn) = state.pop2()?; + let res = builder + .build_call(intrinsics.copysign_f64, &[msg, sgn], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + + /*************************** + * Integer Comparison instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#integer-comparison-instructions + ***************************/ + Operator::I32Eq | Operator::I64Eq => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::EQ, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32Ne | Operator::I64Ne => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::NE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32LtS | Operator::I64LtS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::SLT, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32LtU | Operator::I64LtU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::ULT, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32LeS | Operator::I64LeS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::SLE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32LeU | Operator::I64LeU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::ULE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32GtS | Operator::I64GtS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::SGT, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32GtU | Operator::I64GtU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::UGT, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32GeS | Operator::I64GeS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::SGE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32GeU | Operator::I64GeU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::UGE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + + /*************************** + * Floating-Point Comparison instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#floating-point-comparison-instructions + ***************************/ + Operator::F32Eq | Operator::F64Eq => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let cond = + builder.build_float_compare(FloatPredicate::OEQ, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::F32Ne | Operator::F64Ne => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let cond = + builder.build_float_compare(FloatPredicate::UNE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::F32Lt | Operator::F64Lt => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let cond = + builder.build_float_compare(FloatPredicate::OLT, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::F32Le | Operator::F64Le => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let cond = + builder.build_float_compare(FloatPredicate::OLE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::F32Gt | Operator::F64Gt => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let cond = + builder.build_float_compare(FloatPredicate::OGT, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::F32Ge | Operator::F64Ge => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let cond = + builder.build_float_compare(FloatPredicate::OGE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + + /*************************** + * Conversion instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#conversion-instructions + ***************************/ + Operator::I32WrapI64 => { + let v1 = state.pop1()?.into_int_value(); + let res = builder.build_int_truncate(v1, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I64ExtendSI32 => { + let v1 = state.pop1()?.into_int_value(); + let res = builder.build_int_s_extend(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::I64ExtendUI32 => { + let v1 = state.pop1()?.into_int_value(); + let res = builder.build_int_z_extend(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::I32TruncSF32 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -2147483904.0, + 2147483648.0, + v1, + ); + let res = + builder.build_float_to_signed_int(v1, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32TruncSF64 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -2147483649.0, + 2147483648.0, + v1, + ); + let res = + builder.build_float_to_signed_int(v1, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32TruncSSatF32 | Operator::I32TruncSSatF64 => { + let v1 = state.pop1()?.into_float_value(); + let res = + builder.build_float_to_signed_int(v1, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I64TruncSF32 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -9223373136366403584.0, + 9223372036854775808.0, + v1, + ); + let res = + builder.build_float_to_signed_int(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::I64TruncSF64 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -9223372036854777856.0, + 9223372036854775808.0, + v1, + ); + let res = + builder.build_float_to_signed_int(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::I64TruncSSatF32 | Operator::I64TruncSSatF64 => { + let v1 = state.pop1()?.into_float_value(); + let res = + builder.build_float_to_signed_int(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::I32TruncUF32 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -1.0, + 4294967296.0, + v1, + ); + let res = + builder.build_float_to_unsigned_int(v1, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32TruncUF64 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -1.0, + 4294967296.0, + v1, + ); + let res = + builder.build_float_to_unsigned_int(v1, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32TruncUSatF32 | Operator::I32TruncUSatF64 => { + let v1 = state.pop1()?.into_float_value(); + let res = + builder.build_float_to_unsigned_int(v1, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I64TruncUF32 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -1.0, + 18446744073709551616.0, + v1, + ); + let res = + builder.build_float_to_unsigned_int(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::I64TruncUF64 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -1.0, + 18446744073709551616.0, + v1, + ); + let res = + builder.build_float_to_unsigned_int(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::I64TruncUSatF32 | Operator::I64TruncUSatF64 => { + let v1 = state.pop1()?.into_float_value(); + let res = + builder.build_float_to_unsigned_int(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::F32DemoteF64 => { + let v1 = state.pop1()?.into_float_value(); + let res = builder.build_float_trunc(v1, intrinsics.f32_ty, &state.var_name()); + state.push1(res); + } + Operator::F64PromoteF32 => { + let v1 = state.pop1()?.into_float_value(); + let res = builder.build_float_ext(v1, intrinsics.f64_ty, &state.var_name()); + state.push1(res); + } + Operator::F32ConvertSI32 | Operator::F32ConvertSI64 => { + let v1 = state.pop1()?.into_int_value(); + let res = + builder.build_signed_int_to_float(v1, intrinsics.f32_ty, &state.var_name()); + state.push1(res); + } + Operator::F64ConvertSI32 | Operator::F64ConvertSI64 => { + let v1 = state.pop1()?.into_int_value(); + let res = + builder.build_signed_int_to_float(v1, intrinsics.f64_ty, &state.var_name()); + state.push1(res); + } + Operator::F32ConvertUI32 | Operator::F32ConvertUI64 => { + let v1 = state.pop1()?.into_int_value(); + let res = + builder.build_unsigned_int_to_float(v1, intrinsics.f32_ty, &state.var_name()); + state.push1(res); + } + Operator::F64ConvertUI32 | Operator::F64ConvertUI64 => { + let v1 = state.pop1()?.into_int_value(); + let res = + builder.build_unsigned_int_to_float(v1, intrinsics.f64_ty, &state.var_name()); + state.push1(res); + } + Operator::I32ReinterpretF32 => { + let v = state.pop1()?; + let space = + builder.build_alloca(intrinsics.i32_ty.as_basic_type_enum(), &state.var_name()); + let f32_space = + builder.build_pointer_cast(space, intrinsics.f32_ptr_ty, &state.var_name()); + builder.build_store(f32_space, v); + let int = builder.build_load(space, &state.var_name()); + state.push1(int); + } + Operator::I64ReinterpretF64 => { + let v = state.pop1()?; + let space = + builder.build_alloca(intrinsics.i64_ty.as_basic_type_enum(), &state.var_name()); + let f64_space = + builder.build_pointer_cast(space, intrinsics.f64_ptr_ty, &state.var_name()); + builder.build_store(f64_space, v); + let int = builder.build_load(space, &state.var_name()); + state.push1(int); + } + Operator::F32ReinterpretI32 => { + let v = state.pop1()?; + let space = + builder.build_alloca(intrinsics.f32_ty.as_basic_type_enum(), &state.var_name()); + let i32_space = + builder.build_pointer_cast(space, intrinsics.i32_ptr_ty, &state.var_name()); + builder.build_store(i32_space, v); + let f = builder.build_load(space, &state.var_name()); + state.push1(f); + } + Operator::F64ReinterpretI64 => { + let v = state.pop1()?; + let space = + builder.build_alloca(intrinsics.f64_ty.as_basic_type_enum(), &state.var_name()); + let i64_space = + builder.build_pointer_cast(space, intrinsics.i64_ptr_ty, &state.var_name()); + builder.build_store(i64_space, v); + let f = builder.build_load(space, &state.var_name()); + state.push1(f); + } + + /*************************** + * Sign-extension operators. + * https://github.com/WebAssembly/sign-extension-ops/blob/master/proposals/sign-extension-ops/Overview.md + ***************************/ + Operator::I32Extend8S => { + let value = state.pop1()?.into_int_value(); + let narrow_value = + builder.build_int_truncate(value, intrinsics.i8_ty, &state.var_name()); + let extended_value = + builder.build_int_s_extend(narrow_value, intrinsics.i32_ty, &state.var_name()); + state.push1(extended_value); + } + Operator::I32Extend16S => { + let value = state.pop1()?.into_int_value(); + let narrow_value = + builder.build_int_truncate(value, intrinsics.i16_ty, &state.var_name()); + let extended_value = + builder.build_int_s_extend(narrow_value, intrinsics.i32_ty, &state.var_name()); + state.push1(extended_value); + } + Operator::I64Extend8S => { + let value = state.pop1()?.into_int_value(); + let narrow_value = + builder.build_int_truncate(value, intrinsics.i8_ty, &state.var_name()); + let extended_value = + builder.build_int_s_extend(narrow_value, intrinsics.i64_ty, &state.var_name()); + state.push1(extended_value); + } + Operator::I64Extend16S => { + let value = state.pop1()?.into_int_value(); + let narrow_value = + builder.build_int_truncate(value, intrinsics.i16_ty, &state.var_name()); + let extended_value = + builder.build_int_s_extend(narrow_value, intrinsics.i64_ty, &state.var_name()); + state.push1(extended_value); + } + Operator::I64Extend32S => { + let value = state.pop1()?.into_int_value(); + let narrow_value = + builder.build_int_truncate(value, intrinsics.i32_ty, &state.var_name()); + let extended_value = + builder.build_int_s_extend(narrow_value, intrinsics.i64_ty, &state.var_name()); + state.push1(extended_value); + } + + /*************************** + * Load and Store instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#load-and-store-instructions + ***************************/ + Operator::I32Load { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i32_ptr_ty, + )?; + let result = builder.build_load(effective_address, &state.var_name()); + state.push1(result); + } + Operator::I64Load { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i64_ptr_ty, + )?; + let result = builder.build_load(effective_address, &state.var_name()); + state.push1(result); + } + Operator::F32Load { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.f32_ptr_ty, + )?; + let result = builder.build_load(effective_address, &state.var_name()); + state.push1(result); + } + Operator::F64Load { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.f64_ptr_ty, + )?; + let result = builder.build_load(effective_address, &state.var_name()); + state.push1(result); + } + + Operator::I32Store { memarg } => { + let value = state.pop1()?; + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i32_ptr_ty, + )?; + builder.build_store(effective_address, value); + } + Operator::I64Store { memarg } => { + let value = state.pop1()?; + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i64_ptr_ty, + )?; + builder.build_store(effective_address, value); + } + Operator::F32Store { memarg } => { + let value = state.pop1()?; + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.f32_ptr_ty, + )?; + builder.build_store(effective_address, value); + } + Operator::F64Store { memarg } => { + let value = state.pop1()?; + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.f64_ptr_ty, + )?; + builder.build_store(effective_address, value); + } + + Operator::I32Load8S { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i8_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_s_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); + state.push1(result); + } + Operator::I32Load16S { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i16_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_s_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); + state.push1(result); + } + Operator::I64Load8S { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i8_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_s_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); + state.push1(result); + } + Operator::I64Load16S { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i16_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_s_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); + state.push1(result); + } + Operator::I64Load32S { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i32_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_s_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); + state.push1(result); + } + + Operator::I32Load8U { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i8_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_z_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); + state.push1(result); + } + Operator::I32Load16U { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i16_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_z_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); + state.push1(result); + } + Operator::I64Load8U { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i8_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); + state.push1(result); + } + Operator::I64Load16U { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i16_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); + state.push1(result); + } + Operator::I64Load32U { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i32_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); + state.push1(result); + } + + Operator::I32Store8 { memarg } | Operator::I64Store8 { memarg } => { + let value = state.pop1()?.into_int_value(); + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i8_ptr_ty, + )?; + let narrow_value = + builder.build_int_truncate(value, intrinsics.i8_ty, &state.var_name()); + builder.build_store(effective_address, narrow_value); + } + Operator::I32Store16 { memarg } | Operator::I64Store16 { memarg } => { + let value = state.pop1()?.into_int_value(); + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i16_ptr_ty, + )?; + let narrow_value = + builder.build_int_truncate(value, intrinsics.i16_ty, &state.var_name()); + builder.build_store(effective_address, narrow_value); + } + Operator::I64Store32 { memarg } => { + let value = state.pop1()?.into_int_value(); + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i32_ptr_ty, + )?; + let narrow_value = + builder.build_int_truncate(value, intrinsics.i32_ty, &state.var_name()); + builder.build_store(effective_address, narrow_value); + } + + Operator::MemoryGrow { reserved } => { + let memory_index = MemoryIndex::new(reserved as usize); + let func_value = 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 => intrinsics.memory_grow_dynamic_local, + MemoryType::Static => intrinsics.memory_grow_static_local, + MemoryType::SharedStatic => intrinsics.memory_grow_shared_local, + } + } + LocalOrImport::Import(import_mem_index) => { + let mem_desc = &info.imported_memories[import_mem_index].1; + match mem_desc.memory_type() { + MemoryType::Dynamic => intrinsics.memory_grow_dynamic_import, + MemoryType::Static => intrinsics.memory_grow_static_import, + MemoryType::SharedStatic => intrinsics.memory_grow_shared_import, + } + } + }; + + let memory_index_const = intrinsics + .i32_ty + .const_int(reserved as u64, false) + .as_basic_value_enum(); + let delta = state.pop1()?; + + let result = builder.build_call( + func_value, + &[ctx.basic(), memory_index_const, delta], + &state.var_name(), + ); + state.push1(result.try_as_basic_value().left().unwrap()); + } + Operator::MemorySize { reserved } => { + let memory_index = MemoryIndex::new(reserved as usize); + let func_value = 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 => intrinsics.memory_size_dynamic_local, + MemoryType::Static => intrinsics.memory_size_static_local, + MemoryType::SharedStatic => intrinsics.memory_size_shared_local, + } + } + LocalOrImport::Import(import_mem_index) => { + let mem_desc = &info.imported_memories[import_mem_index].1; + match mem_desc.memory_type() { + MemoryType::Dynamic => intrinsics.memory_size_dynamic_import, + MemoryType::Static => intrinsics.memory_size_static_import, + MemoryType::SharedStatic => intrinsics.memory_size_shared_import, + } + } + }; + + let memory_index_const = intrinsics + .i32_ty + .const_int(reserved as u64, false) + .as_basic_value_enum(); + let result = builder.build_call( + func_value, + &[ctx.basic(), memory_index_const], + &state.var_name(), + ); + state.push1(result.try_as_basic_value().left().unwrap()); + } + op @ _ => { + unimplemented!("{:?}", op); + } + } + + unimplemented!() + + } + + fn finalize(&mut self) -> Result<(), CodegenError> { + unimplemented!() + } +} + +impl From for CodegenError { + fn from(other: BinaryReaderError) -> CodegenError { + unimplemented!() + //CodegenError + } +} + +impl ModuleCodeGenerator + for LLVMModuleCodeGenerator +{ + fn new() -> LLVMModuleCodeGenerator { + let context = Context::create(); + let module = context.create_module("module"); + let builder = context.create_builder(); + + let intrinsics = Intrinsics::declare(&module, &context); + + let personality_func = module.add_function( + "__gxx_personality_v0", + intrinsics.i32_ty.fn_type(&[], false), + Some(Linkage::External), + ); + + + // TODO signatures and functions +// let signatures: Map = info +// .signatures +// .iter() +// .map(|(_, sig)| func_sig_to_llvm(&context, &intrinsics, sig)) +// .collect(); + let signatures = Map::new(); +// 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 functions = Map::new(); + +// let mut ctx = intrinsics.ctx(info, builder, &function, cache_builder); + + LLVMModuleCodeGenerator { + context: context, + builder: &builder, + functions: vec![], + functions_llvm: &functions, + signatures: &signatures, + function_signatures: None, + // function_labels: Some(HashMap::new()), + // assembler: Some(Assembler::new().unwrap()), + func_import_count: 0, + intrinsics: &intrinsics, +// ctx: ctx, + } + } + + fn backend_id() -> Backend { + Backend::LLVM + } + + fn check_precondition(&mut self, module_info: &ModuleInfo) -> Result<(), CodegenError> { + Ok(()) + } + + fn next_function(&mut self) -> Result<&mut LLVMFunctionCodeGenerator, CodegenError> { + // Creates a new function and returns the function-scope code generator for it. + + use std::mem; + + let func_sig_dummy: FuncSig = unsafe { mem::uninitialized() }; + let function_dummy: FunctionValue = unsafe { mem::uninitialized() }; + let intrinsics_dummy: &Intrinsics = unsafe { mem::uninitialized() }; + let context_dummy: &Context = unsafe { mem::uninitialized() }; + + let code = LLVMFunctionCodeGenerator { + state: State::new(), + builder: self.builder, + context: context_dummy, //&self.context, + function: function_dummy, + func_sig: &func_sig_dummy, + locals: vec![], + signatures: &Map::new(), +// context: self.fu + +// signatures: self.signatures.as_ref().unwrap().clone(), +// function_signatures: self.function_signatures.as_ref().unwrap().clone(), +// +// assembler: Some(assembler), +// function_labels: Some(function_labels), +// br_table_data: Some(br_table_data), +// breakpoints: Some(breakpoints), +// returns: smallvec![], +// locals: vec![], +// num_params: 0, +// num_locals: 0, +// value_stack: vec![], +// control_stack: vec![], +// machine: Machine::new(), +// unreachable_depth: 0, + intrinsics: intrinsics_dummy, + }; + self.functions.push(code); + Ok(self.functions.last_mut().unwrap()) + } + + fn finalize(self, module_info: &ModuleInfo) -> Result { + unimplemented!() + } + + fn feed_signatures(&mut self, signatures: Map) -> Result<(), CodegenError> { +// self.signatures = Some(Arc::new(signatures)); + Ok(()) + } + + fn feed_function_signatures(&mut self, assoc: Map) -> Result<(), CodegenError> { + self.function_signatures = Some(Arc::new(assoc)); + Ok(()) + } + + fn feed_import_function(&mut self) -> Result<(), CodegenError> { + // TODO + Ok(()) + } +} \ No newline at end of file diff --git a/lib/llvm-backend/src/lib.rs b/lib/llvm-backend/src/lib.rs index 6ce8139c3..ee5d4d2ef 100644 --- a/lib/llvm-backend/src/lib.rs +++ b/lib/llvm-backend/src/lib.rs @@ -26,6 +26,14 @@ impl LLVMCompiler { } } +use wasmer_runtime_core::codegen::SimpleStreamingCompilerGen; +pub type LLVMStreamingCompiler = SimpleStreamingCompilerGen< + code::LLVMModuleCodeGenerator, + code::LLVMFunctionCodeGenerator, + backend::LLVMBackend, + code::CodegenError, +>; + impl Compiler for LLVMCompiler { fn compile( &self, diff --git a/lib/spectests/examples/simple/main.rs b/lib/spectests/examples/simple/main.rs index 357adb5f7..8ac05dedb 100644 --- a/lib/spectests/examples/simple/main.rs +++ b/lib/spectests/examples/simple/main.rs @@ -18,8 +18,8 @@ fn get_compiler() -> impl Compiler { #[cfg(feature = "llvm")] fn get_compiler() -> impl Compiler { - use wasmer_llvm_backend::LLVMCompiler; - LLVMCompiler::new() + use wasmer_llvm_backend::LLVMStreamingCompiler; + LLVMStreamingCompiler::new() } #[cfg(feature = "singlepass")]