use crate::config::LLVM; use crate::trampoline::FuncTrampoline; use crate::translator::FuncTranslator; use crate::CompiledKind; use inkwell::context::Context; use inkwell::memory_buffer::MemoryBuffer; use inkwell::module::{Linkage, Module}; use inkwell::targets::FileType; use inkwell::DLLStorageClass; use rayon::iter::ParallelBridge; use rayon::prelude::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}; use std::collections::HashSet; use std::sync::Arc; use wasmer_compiler::types::function::{Compilation, UnwindInfo}; use wasmer_compiler::types::module::CompileModuleInfo; use wasmer_compiler::types::relocation::RelocationKind; use wasmer_compiler::{ types::{ relocation::RelocationTarget, section::{CustomSection, CustomSectionProtection, SectionBody, SectionIndex}, symbols::{Symbol, SymbolRegistry}, target::Target, }, Compiler, FunctionBodyData, ModuleMiddleware, ModuleTranslationState, }; use wasmer_types::entity::{EntityRef, PrimaryMap}; use wasmer_types::{CompileError, FunctionIndex, LocalFunctionIndex, SignatureIndex}; use wasmer_vm::LibCall; /// A compiler that compiles a WebAssembly module with LLVM, translating the Wasm to LLVM IR, /// optimizing it and then translating to assembly. pub struct LLVMCompiler { config: LLVM, } impl LLVMCompiler { /// Creates a new LLVM compiler pub fn new(config: LLVM) -> LLVMCompiler { LLVMCompiler { config } } /// Gets the config for this Compiler fn config(&self) -> &LLVM { &self.config } } struct ShortNames {} impl SymbolRegistry for ShortNames { fn symbol_to_name(&self, symbol: Symbol) -> String { match symbol { Symbol::Metadata => "M".to_string(), Symbol::LocalFunction(index) => format!("f{}", index.index()), Symbol::Section(index) => format!("s{}", index.index()), Symbol::FunctionCallTrampoline(index) => format!("t{}", index.index()), Symbol::DynamicFunctionTrampoline(index) => format!("d{}", index.index()), } } fn name_to_symbol(&self, name: &str) -> Option { if name.len() < 2 { return None; } let (ty, idx) = name.split_at(1); if ty.starts_with('M') { return Some(Symbol::Metadata); } let idx = idx.parse::().ok()?; match ty.chars().next().unwrap() { 'f' => Some(Symbol::LocalFunction(LocalFunctionIndex::from_u32(idx))), 's' => Some(Symbol::Section(SectionIndex::from_u32(idx))), 't' => Some(Symbol::FunctionCallTrampoline(SignatureIndex::from_u32( idx, ))), 'd' => Some(Symbol::DynamicFunctionTrampoline(FunctionIndex::from_u32( idx, ))), _ => None, } } } impl LLVMCompiler { #[allow(clippy::too_many_arguments)] fn compile_native_object( &self, target: &Target, compile_info: &CompileModuleInfo, module_translation: &ModuleTranslationState, function_body_inputs: &PrimaryMap>, symbol_registry: &dyn SymbolRegistry, wasmer_metadata: &[u8], binary_format: target_lexicon::BinaryFormat, ) -> Result, CompileError> { let target_machine = self.config().target_machine(target); let ctx = Context::create(); // TODO: https:/github.com/rayon-rs/rayon/issues/822 let merged_bitcode = function_body_inputs.into_iter().par_bridge().map_init( || { let target_machine = self.config().target_machine(target); FuncTranslator::new(target_machine, binary_format).unwrap() }, |func_translator, (i, input)| { let module = func_translator.translate_to_module( &compile_info.module, module_translation, &i, input, self.config(), &compile_info.memory_styles, &compile_info.table_styles, symbol_registry, )?; Ok(module.write_bitcode_to_memory().as_slice().to_vec()) }, ); let trampolines_bitcode = compile_info.module.signatures.iter().par_bridge().map_init( || { let target_machine = self.config().target_machine(target); FuncTrampoline::new(target_machine, binary_format).unwrap() }, |func_trampoline, (i, sig)| { let name = symbol_registry.symbol_to_name(Symbol::FunctionCallTrampoline(i)); let module = func_trampoline.trampoline_to_module(sig, self.config(), &name)?; Ok(module.write_bitcode_to_memory().as_slice().to_vec()) }, ); let dynamic_trampolines_bitcode = compile_info.module.functions.iter().par_bridge().map_init( || { let target_machine = self.config().target_machine(target); ( FuncTrampoline::new(target_machine, binary_format).unwrap(), &compile_info.module.signatures, ) }, |(func_trampoline, signatures), (i, sig)| { let sig = &signatures[*sig]; let name = symbol_registry.symbol_to_name(Symbol::DynamicFunctionTrampoline(i)); let module = func_trampoline.dynamic_trampoline_to_module(sig, self.config(), &name)?; Ok(module.write_bitcode_to_memory().as_slice().to_vec()) }, ); let merged_bitcode = merged_bitcode .chain(trampolines_bitcode) .chain(dynamic_trampolines_bitcode) .collect::, CompileError>>()? .into_par_iter() .reduce_with(|bc1, bc2| { let ctx = Context::create(); let membuf = MemoryBuffer::create_from_memory_range(&bc1, ""); let m1 = Module::parse_bitcode_from_buffer(&membuf, &ctx).unwrap(); let membuf = MemoryBuffer::create_from_memory_range(&bc2, ""); let m2 = Module::parse_bitcode_from_buffer(&membuf, &ctx).unwrap(); m1.link_in_module(m2).unwrap(); m1.write_bitcode_to_memory().as_slice().to_vec() }); let merged_module = if let Some(bc) = merged_bitcode { let membuf = MemoryBuffer::create_from_memory_range(&bc, ""); Module::parse_bitcode_from_buffer(&membuf, &ctx).unwrap() } else { ctx.create_module("") }; let i8_ty = ctx.i8_type(); let metadata_init = i8_ty.const_array( wasmer_metadata .iter() .map(|v| i8_ty.const_int(*v as u64, false)) .collect::>() .as_slice(), ); let metadata_gv = merged_module.add_global( metadata_init.get_type(), None, &symbol_registry.symbol_to_name(wasmer_compiler::types::symbols::Symbol::Metadata), ); metadata_gv.set_initializer(&metadata_init); metadata_gv.set_linkage(Linkage::DLLExport); metadata_gv.set_dll_storage_class(DLLStorageClass::Export); metadata_gv.set_alignment(16); if self.config().enable_verifier { merged_module.verify().unwrap(); } let memory_buffer = target_machine .write_to_memory_buffer(&merged_module, FileType::Object) .unwrap(); if let Some(ref callbacks) = self.config.callbacks { callbacks.obj_memory_buffer(&CompiledKind::Module, &memory_buffer); } tracing::trace!("Finished compling the module!"); Ok(memory_buffer.as_slice().to_vec()) } } impl Compiler for LLVMCompiler { fn name(&self) -> &str { "llvm" } /// Get the middlewares for this compiler fn get_middlewares(&self) -> &[Arc] { &self.config.middlewares } fn experimental_native_compile_module( &self, target: &Target, compile_info: &CompileModuleInfo, module_translation: &ModuleTranslationState, // The list of function bodies function_body_inputs: &PrimaryMap>, symbol_registry: &dyn SymbolRegistry, // The metadata to inject into the wasmer_metadata section of the object file. wasmer_metadata: &[u8], ) -> Option, CompileError>> { Some(self.compile_native_object( target, compile_info, module_translation, function_body_inputs, symbol_registry, wasmer_metadata, self.config.target_binary_format(target), )) } /// Compile the module using LLVM, producing a compilation result with /// associated relocations. fn compile_module( &self, target: &Target, compile_info: &CompileModuleInfo, module_translation: &ModuleTranslationState, function_body_inputs: PrimaryMap>, ) -> Result { //let data = Arc::new(Mutex::new(0)); let memory_styles = &compile_info.memory_styles; let table_styles = &compile_info.table_styles; let binary_format = self.config.target_binary_format(target); let module = &compile_info.module; // TODO: merge constants in sections. let mut module_custom_sections = PrimaryMap::new(); let mut eh_frame_section_bytes = vec![]; let mut eh_frame_section_relocations = vec![]; let mut compact_unwind_section_bytes = vec![]; let mut compact_unwind_section_relocations = vec![]; let mut got_targets: HashSet = HashSet::from_iter(vec![RelocationTarget::LibCall(LibCall::EHPersonality)]); let functions = function_body_inputs .iter() .collect::)>>() .par_iter() .map_init( || { let target_machine = self.config().target_machine(target); FuncTranslator::new(target_machine, binary_format).unwrap() }, |func_translator, (i, input)| { // TODO: remove (to serialize) //let _data = data.lock().unwrap(); func_translator.translate( module, module_translation, i, input, self.config(), memory_styles, table_styles, &ShortNames {}, ) }, ) .collect::, CompileError>>()? .into_iter() .map(|mut compiled_function| { let first_section = module_custom_sections.len() as u32; for (section_index, custom_section) in compiled_function.custom_sections.iter() { // TODO: remove this call to clone() let mut custom_section = custom_section.clone(); for reloc in &mut custom_section.relocations { if let RelocationTarget::CustomSection(index) = reloc.reloc_target { reloc.reloc_target = RelocationTarget::CustomSection( SectionIndex::from_u32(first_section + index.as_u32()), ) } if reloc.kind.needs_got() { got_targets.insert(reloc.reloc_target); } } if compiled_function .eh_frame_section_indices .contains(§ion_index) { let offset = eh_frame_section_bytes.len() as u32; for reloc in &mut custom_section.relocations { reloc.offset += offset; } eh_frame_section_bytes.extend_from_slice(custom_section.bytes.as_slice()); eh_frame_section_relocations.extend(custom_section.relocations); // TODO: we do this to keep the count right, remove it. module_custom_sections.push(CustomSection { protection: CustomSectionProtection::Read, bytes: SectionBody::new_with_vec(vec![]), relocations: vec![], }); } else if compiled_function .compact_unwind_section_indices .contains(§ion_index) { let offset = compact_unwind_section_bytes.len() as u32; for reloc in &mut custom_section.relocations { reloc.offset += offset; } compact_unwind_section_bytes .extend_from_slice(custom_section.bytes.as_slice()); compact_unwind_section_relocations.extend(custom_section.relocations); // TODO: we do this to keep the count right, remove it. module_custom_sections.push(CustomSection { protection: CustomSectionProtection::Read, bytes: SectionBody::new_with_vec(vec![]), relocations: vec![], }); } else { module_custom_sections.push(custom_section); } } for reloc in &mut compiled_function.compiled_function.relocations { if let RelocationTarget::CustomSection(index) = reloc.reloc_target { reloc.reloc_target = RelocationTarget::CustomSection( SectionIndex::from_u32(first_section + index.as_u32()), ) } if reloc.kind.needs_got() { got_targets.insert(reloc.reloc_target); } } compiled_function.compiled_function }) .collect::>(); let mut unwind_info = UnwindInfo::default(); if !eh_frame_section_bytes.is_empty() { let eh_frame_idx = SectionIndex::from_u32(module_custom_sections.len() as u32); // Do not terminate dwarf info with a zero-length CIE. // Because more info will be added later // in lib/object/src/module.rs emit_compilation module_custom_sections.push(CustomSection { protection: CustomSectionProtection::Read, bytes: SectionBody::new_with_vec(eh_frame_section_bytes), relocations: eh_frame_section_relocations, }); unwind_info.eh_frame = Some(eh_frame_idx); } if !compact_unwind_section_bytes.is_empty() { let cu_index = SectionIndex::from_u32(module_custom_sections.len() as u32); module_custom_sections.push(CustomSection { protection: CustomSectionProtection::Read, bytes: SectionBody::new_with_vec(compact_unwind_section_bytes), relocations: compact_unwind_section_relocations, }); unwind_info.compact_unwind = Some(cu_index); } let function_call_trampolines = module .signatures .values() .collect::>() .par_iter() .map_init( || { let target_machine = self.config().target_machine(target); FuncTrampoline::new(target_machine, binary_format).unwrap() }, |func_trampoline, sig| func_trampoline.trampoline(sig, self.config(), ""), ) .collect::>() .into_iter() .collect::, CompileError>>()?; let dynamic_function_trampolines = module .imported_function_types() .collect::>() .par_iter() .map_init( || { let target_machine = self.config().target_machine(target); FuncTrampoline::new(target_machine, binary_format).unwrap() }, |func_trampoline, func_type| { func_trampoline.dynamic_trampoline(func_type, self.config(), "") }, ) .collect::, CompileError>>()? .into_iter() .collect::>(); let mut got = wasmer_compiler::types::function::GOT::empty(); if !got_targets.is_empty() { let pointer_width = target .triple() .pointer_width() .map_err(|_| CompileError::Codegen("Could not get pointer width".to_string()))?; let got_entry_size = match pointer_width { target_lexicon::PointerWidth::U64 => 8, target_lexicon::PointerWidth::U32 => 4, target_lexicon::PointerWidth::U16 => todo!(), }; let got_entry_reloc_kind = match pointer_width { target_lexicon::PointerWidth::U64 => RelocationKind::Abs8, target_lexicon::PointerWidth::U32 => RelocationKind::Abs4, target_lexicon::PointerWidth::U16 => todo!(), }; let got_data: Vec = vec![0; got_targets.len() * got_entry_size]; let mut got_relocs = vec![]; for (i, target) in got_targets.into_iter().enumerate() { got_relocs.push(wasmer_compiler::types::relocation::Relocation { kind: got_entry_reloc_kind, reloc_target: target, offset: (i * got_entry_size) as u32, addend: 0, }); } let got_idx = SectionIndex::from_u32(module_custom_sections.len() as u32); module_custom_sections.push(CustomSection { protection: CustomSectionProtection::Read, bytes: SectionBody::new_with_vec(got_data), relocations: got_relocs, }); got.index = Some(got_idx); }; tracing::trace!("Finished compling the module!"); Ok(Compilation { functions, custom_sections: module_custom_sections, function_call_trampolines, dynamic_function_trampolines, unwind_info, got, }) } }