//! Support for compiling with Cranelift. use crate::address_map::get_function_address_map; use crate::config::CraneliftConfig; #[cfg(feature = "unwind")] use crate::dwarf::WriterRelocate; use crate::func_environ::{get_func_name, FuncEnvironment}; use crate::sink::{RelocSink, TrapSink}; use crate::trampoline::{ make_trampoline_dynamic_function, make_trampoline_function_call, FunctionBuilderContext, }; use crate::translator::{ compiled_function_unwind_info, signature_to_cranelift_ir, transform_jump_table, CraneliftUnwindInfo, FuncTranslator, }; use cranelift_codegen::ir; use cranelift_codegen::print_errors::pretty_error; use cranelift_codegen::{binemit, isa, Context}; #[cfg(feature = "unwind")] use gimli::write::{Address, Dwarf, EhFrame, EndianVec, FrameTable, Sections, Writer}; #[cfg(feature = "unwind")] use gimli::{RunTimeEndian, SectionId}; use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; use wasm_common::entity::{EntityRef, PrimaryMap}; use wasm_common::{ Features, FunctionIndex, FunctionType, LocalFunctionIndex, MemoryIndex, SignatureIndex, TableIndex, }; use wasmer_compiler::CompileError; use wasmer_compiler::{ Compilation, CompiledFunction, CompiledFunctionFrameInfo, CompiledFunctionUnwindInfo, Compiler, FunctionBody, FunctionBodyData, }; use wasmer_compiler::{CompilerConfig, ModuleTranslationState, Target}; use wasmer_runtime::{MemoryPlan, ModuleInfo, TablePlan}; /// A compiler that compiles a WebAssembly module with Cranelift, translating the Wasm to Cranelift IR, /// optimizing it and then translating to assembly. pub struct CraneliftCompiler { isa: Box, config: CraneliftConfig, } impl CraneliftCompiler { /// Creates a new Cranelift compiler pub fn new(config: &CraneliftConfig) -> Self { let isa = config.isa(); Self { isa, config: config.clone(), } } /// Retrieves the starget ISA fn isa(&self) -> &dyn isa::TargetIsa { &*self.isa } /// Gets the WebAssembly features for this Compiler pub fn config(&self) -> &CraneliftConfig { &self.config } } impl Compiler for CraneliftCompiler { /// Gets the WebAssembly features for this Compiler fn features(&self) -> &Features { self.config.features() } /// Gets the target associated to the Cranelift ISA. fn target(&self) -> &Target { self.config.target() } /// Compile the module using Cranelift, producing a compilation result with /// associated relocations. fn compile_module( &self, module: &ModuleInfo, module_translation: &ModuleTranslationState, function_body_inputs: PrimaryMap>, memory_plans: PrimaryMap, table_plans: PrimaryMap, ) -> Result { let isa = self.isa(); let frontend_config = isa.frontend_config(); let signatures = module .signatures .iter() .map(|(_sig_index, func_type)| signature_to_cranelift_ir(func_type, &frontend_config)) .collect::>(); // Generate the frametable let mut dwarf_frametable = FrameTable::default(); let cie_id = dwarf_frametable.add_cie(isa.create_systemv_cie().ok_or_else(|| { CompileError::Codegen( "Cranelift ISA doesn't support System-V unwind information".to_string(), ) })?); use std::sync::{Arc, Mutex}; let dwarf_frametable = Arc::new(Mutex::new(dwarf_frametable)); let functions = function_body_inputs .into_iter() .collect::)>>() .par_iter() .map_init(FuncTranslator::new, |func_translator, (i, input)| { let func_index = module.func_index(*i); let mut context = Context::new(); let mut func_env = FuncEnvironment::new( isa.frontend_config(), module, &signatures, &memory_plans, &table_plans, ); context.func.name = get_func_name(func_index); context.func.signature = signatures[module.functions[func_index]].clone(); // if generate_debug_info { // context.func.collect_debug_info(); // } func_translator.translate( module_translation, input.data, input.module_offset, &mut context.func, &mut func_env, )?; let mut code_buf: Vec = Vec::new(); let mut reloc_sink = RelocSink::new(module, func_index); let mut trap_sink = TrapSink::new(); let mut stackmap_sink = binemit::NullStackmapSink {}; context .compile_and_emit( isa, &mut code_buf, &mut reloc_sink, &mut trap_sink, &mut stackmap_sink, ) .map_err(|error| { CompileError::Codegen(pretty_error(&context.func, Some(isa), error)) })?; let unwind_info = match compiled_function_unwind_info(isa, &context)? { CraneliftUnwindInfo::FDE(fde) => { dwarf_frametable .lock() .expect("Cant write into Dwarf frametable") .add_fde( cie_id, fde.to_fde(Address::Symbol { // The symbol is the kind of relocation. // "0" is used for functions symbol: 0, // We use the addend as a way to specify the // function index addend: i.index() as _, }), ); // The unwind information is inserted into the dwarf section Some(CompiledFunctionUnwindInfo::Dwarf) } other => other.maybe_into_to_windows_unwind(), }; let address_map = get_function_address_map(&context, input, code_buf.len(), isa); // We transform the Cranelift JumpTable's into compiler JumpTables let func_jt_offsets = transform_jump_table(context.func.jt_offsets); Ok(CompiledFunction { body: FunctionBody { body: code_buf, unwind_info, }, jt_offsets: func_jt_offsets, relocations: reloc_sink.func_relocs, frame_info: CompiledFunctionFrameInfo { address_map, traps: trap_sink.traps, }, }) }) .collect::, CompileError>>()? .into_iter() .collect::>(); let mut eh_frame = EhFrame(WriterRelocate::default()); dwarf_frametable .lock() .unwrap() .write_eh_frame(&mut eh_frame) .unwrap(); let eh_frame_section = eh_frame.0.into_section(); println!("section: {:?}", eh_frame_section); let mut custom_sections = PrimaryMap::new(); custom_sections.push(eh_frame_section); Ok(Compilation::new(functions, custom_sections)) } fn compile_function_call_trampolines( &self, signatures: &[FunctionType], ) -> Result, CompileError> { signatures .par_iter() .map_init(FunctionBuilderContext::new, |mut cx, sig| { make_trampoline_function_call(&*self.isa, &mut cx, sig) }) .collect::, CompileError>>() } fn compile_dynamic_function_trampolines( &self, signatures: &[FunctionType], ) -> Result, CompileError> { use wasmer_runtime::VMOffsets; let isa = self.isa(); let frontend_config = isa.frontend_config(); let offsets = VMOffsets::new_for_trampolines(frontend_config.pointer_bytes()); Ok(signatures .par_iter() .map_init(FunctionBuilderContext::new, |mut cx, func_type| { make_trampoline_dynamic_function(&*self.isa, &offsets, &mut cx, &func_type) }) .collect::, CompileError>>()? .into_iter() .collect::>()) } }