use hashbrown::HashMap; use inkwell::{ basic_block::BasicBlock, builder::Builder, context::Context, module::Module, types::{BasicType, BasicTypeEnum, FunctionType}, values::{AggregateValue, BasicValue, BasicValueEnum, FunctionValue}, IntPredicate, }; use wasmer_runtime_core::{ module::ModuleInfo, structures::{Map, SliceMap, TypedIndex}, types::{FuncIndex, FuncSig, LocalFuncIndex, LocalOrImport, SigIndex, Type}, }; use wasmparser::{BinaryReaderError, CodeSectionReader, LocalsReader, Operator, OperatorsReader}; use crate::intrinsics::Intrinsics; use crate::read_info::type_to_type; use crate::state::State; fn func_sig_to_llvm(context: &Context, sig: &FuncSig) -> FunctionType { let param_types: Vec<_> = sig .params() .iter() .map(|&ty| type_to_llvm(context, ty)) .collect(); match sig.returns() { [] => context.void_type().fn_type(¶m_types, false), [single_value] => type_to_llvm(context, *single_value).fn_type(¶m_types, false), returns @ _ => { let basic_types: Vec<_> = returns .iter() .map(|&ty| type_to_llvm(context, ty)) .collect(); context .struct_type(&basic_types, false) .fn_type(¶m_types, false) } } } fn type_to_llvm(context: &Context, ty: Type) -> BasicTypeEnum { match ty { Type::I32 => context.i32_type().as_basic_type_enum(), Type::I64 => context.i64_type().as_basic_type_enum(), Type::F32 => context.f32_type().as_basic_type_enum(), Type::F64 => context.f64_type().as_basic_type_enum(), } } pub fn parse_function_bodies( info: &ModuleInfo, code_reader: CodeSectionReader, ) -> Result<(), BinaryReaderError> { let context = Context::create(); let module = context.create_module("module"); let builder = context.create_builder(); let intrinsics = Intrinsics::declare(&module, &context); let signatures: Map<SigIndex, FunctionType> = info .signatures .iter() .map(|(_, sig)| func_sig_to_llvm(&context, sig)) .collect(); let functions: Map<LocalFuncIndex, FunctionValue> = info .func_assoc .iter() .skip(info.imported_functions.len()) .map(|(func_index, &sig_index)| { module.add_function( &format!("fn:{}", func_index.index()), signatures[sig_index], None, ) }) .collect(); for (local_func_index, body) in code_reader.into_iter().enumerate() { let body = body?; let locals_reader = body.get_locals_reader()?; let op_reader = body.get_operators_reader()?; parse_function( &context, &module, &builder, &intrinsics, info, &signatures, &functions, LocalFuncIndex::new(local_func_index), locals_reader, op_reader, )?; } Ok(()) } fn parse_function( context: &Context, module: &Module, builder: &Builder, intrinsics: &Intrinsics, info: &ModuleInfo, signatures: &SliceMap<SigIndex, FunctionType>, functions: &SliceMap<LocalFuncIndex, FunctionValue>, func_index: LocalFuncIndex, locals_reader: LocalsReader, op_reader: OperatorsReader, ) -> Result<(), BinaryReaderError> { let llvm_sig = &signatures[info.func_assoc[func_index.convert_up(info)]]; let function = functions[func_index]; let entry_block = context.append_basic_block(&function, "entry"); builder.position_at_end(&entry_block); let mut state = State::new(); let mut locals = Vec::with_capacity(locals_reader.get_count() as usize); locals.extend(function.get_param_iter().enumerate().map(|(index, param)| { let ty = param.get_type(); let alloca = builder.build_alloca(ty, &state.var_name()); builder.build_store(alloca, param); alloca })); for (index, local) in locals_reader.into_iter().enumerate().skip(locals.len()) { let (_, ty) = local?; let wasmer_ty = type_to_type(ty)?; let ty = type_to_llvm(context, wasmer_ty); let alloca = builder.build_alloca(ty, &state.var_name()); let default_value = match wasmer_ty { Type::I32 => context.i32_type().const_int(0, false).as_basic_value_enum(), Type::I64 => context.i64_type().const_int(0, false).as_basic_value_enum(), Type::F32 => context.f32_type().const_float(0.0).as_basic_value_enum(), Type::F64 => context.f64_type().const_float(0.0).as_basic_value_enum(), }; builder.build_store(alloca, default_value); locals.push(alloca); } for op in op_reader { match op? { /*************************** * 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 = context.i32_type().const_int(value as u64, false); state.push1(i); } Operator::I64Const { value } => { let i = context.i64_type().const_int(value as u64, false); state.push1(i); } Operator::F32Const { value } => { let f = context .f32_type() .const_float(f64::from_bits(value.bits() as u64)); state.push1(f); } Operator::F64Const { value } => { let f = context.f64_type().const_float(f64::from_bits(value.bits())); 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 } => unimplemented!(), Operator::SetGlobal { global_index } => unimplemented!(), Operator::Select => { let (v1, v2, cond) = state.pop3()?; let cond = cond.into_int_value(); let res = builder.build_select(cond, 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]; match func_index.local_or_import(info) { LocalOrImport::Local(local_func_index) => { let func_sig = &info.signatures[sigindex]; let func_value = functions[local_func_index]; let call_site = builder.build_call( func_value, &state.peekn(func_sig.params().len())?.to_vec(), &state.var_name(), ); 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(), ); state.push1(value); } } } } } LocalOrImport::Import(import_func_index) => { // unimplemented!() } } } Operator::CallIndirect { index, table_index } => { unimplemented!("{}, {}", index, table_index); } /*************************** * 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()); 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()); 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()); 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()); 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 = context.i32_type().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 = context.i64_type().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 = context.i32_type().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 = context.i64_type().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 = context .bool_type() .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 = context .bool_type() .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 = context .bool_type() .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 = context .bool_type() .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 zero = context.i32_type().const_int(0, false); let res = builder.build_int_compare(IntPredicate::EQ, input, zero, &state.var_name()); state.push1(res); } Operator::I64Eqz => { let input = state.pop1()?.into_int_value(); let zero = context.i64_type().const_int(0, false); let res = builder.build_int_compare(IntPredicate::EQ, input, zero, &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::F32Sub => { 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 input = state.pop1()?; let res = builder .build_call(intrinsics.copysign_f32, &[input], &state.var_name()) .try_as_basic_value() .left() .unwrap(); state.push1(res); } Operator::F64Copysign => { let input = state.pop1()?; let res = builder .build_call(intrinsics.copysign_f64, &[input], &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 res = builder.build_int_compare(IntPredicate::EQ, v1, v2, &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 res = builder.build_int_compare(IntPredicate::NE, v1, v2, &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 res = builder.build_int_compare(IntPredicate::SLT, v1, v2, &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 res = builder.build_int_compare(IntPredicate::ULT, v1, v2, &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 res = builder.build_int_compare(IntPredicate::SLE, v1, v2, &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 res = builder.build_int_compare(IntPredicate::ULE, v1, v2, &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 res = builder.build_int_compare(IntPredicate::SGT, v1, v2, &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 res = builder.build_int_compare(IntPredicate::UGT, v1, v2, &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 res = builder.build_int_compare(IntPredicate::SGE, v1, v2, &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 res = builder.build_int_compare(IntPredicate::UGE, v1, v2, &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::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_unreachable(); } op @ _ => { println!("{}", module.print_to_string().to_string()); unimplemented!("{:?}", op); } } } Ok(()) }