wasmer_compiler_llvm/
compiler.rs

1use crate::config::LLVM;
2use crate::trampoline::FuncTrampoline;
3use crate::translator::FuncTranslator;
4use crate::CompiledKind;
5use inkwell::context::Context;
6use inkwell::memory_buffer::MemoryBuffer;
7use inkwell::module::{Linkage, Module};
8use inkwell::targets::FileType;
9use inkwell::DLLStorageClass;
10use rayon::iter::ParallelBridge;
11use rayon::prelude::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
12use rayon::ThreadPoolBuilder;
13use std::collections::{HashMap, HashSet};
14use std::sync::Arc;
15use wasmer_compiler::types::function::{Compilation, UnwindInfo};
16use wasmer_compiler::types::module::CompileModuleInfo;
17use wasmer_compiler::types::relocation::RelocationKind;
18use wasmer_compiler::{
19    types::{
20        relocation::RelocationTarget,
21        section::{CustomSection, CustomSectionProtection, SectionBody, SectionIndex},
22        symbols::{Symbol, SymbolRegistry},
23    },
24    Compiler, FunctionBodyData, ModuleMiddleware, ModuleTranslationState,
25};
26use wasmer_types::entity::{EntityRef, PrimaryMap};
27use wasmer_types::target::Target;
28use wasmer_types::{CompileError, FunctionIndex, LocalFunctionIndex, ModuleInfo, SignatureIndex};
29use wasmer_vm::LibCall;
30
31/// A compiler that compiles a WebAssembly module with LLVM, translating the Wasm to LLVM IR,
32/// optimizing it and then translating to assembly.
33#[derive(Debug)]
34pub struct LLVMCompiler {
35    config: LLVM,
36}
37
38impl LLVMCompiler {
39    /// Creates a new LLVM compiler
40    pub fn new(config: LLVM) -> LLVMCompiler {
41        LLVMCompiler { config }
42    }
43
44    /// Gets the config for this Compiler
45    fn config(&self) -> &LLVM {
46        &self.config
47    }
48}
49
50struct ShortNames {}
51
52impl SymbolRegistry for ShortNames {
53    fn symbol_to_name(&self, symbol: Symbol) -> String {
54        match symbol {
55            Symbol::Metadata => "M".to_string(),
56            Symbol::LocalFunction(index) => format!("f{}", index.index()),
57            Symbol::Section(index) => format!("s{}", index.index()),
58            Symbol::FunctionCallTrampoline(index) => format!("t{}", index.index()),
59            Symbol::DynamicFunctionTrampoline(index) => format!("d{}", index.index()),
60        }
61    }
62
63    fn name_to_symbol(&self, name: &str) -> Option<Symbol> {
64        if name.len() < 2 {
65            return None;
66        }
67        let (ty, idx) = name.split_at(1);
68        if ty.starts_with('M') {
69            return Some(Symbol::Metadata);
70        }
71
72        let idx = idx.parse::<u32>().ok()?;
73        match ty.chars().next().unwrap() {
74            'f' => Some(Symbol::LocalFunction(LocalFunctionIndex::from_u32(idx))),
75            's' => Some(Symbol::Section(SectionIndex::from_u32(idx))),
76            't' => Some(Symbol::FunctionCallTrampoline(SignatureIndex::from_u32(
77                idx,
78            ))),
79            'd' => Some(Symbol::DynamicFunctionTrampoline(FunctionIndex::from_u32(
80                idx,
81            ))),
82            _ => None,
83        }
84    }
85}
86
87struct ModuleBasedSymbolRegistry {
88    wasm_module: Arc<ModuleInfo>,
89    local_func_names: HashMap<String, LocalFunctionIndex>,
90    short_names: ShortNames,
91}
92
93impl ModuleBasedSymbolRegistry {
94    fn new(wasm_module: Arc<ModuleInfo>) -> Self {
95        let local_func_names = HashMap::from_iter(
96            wasm_module
97                .function_names
98                .iter()
99                .map(|(f, v)| (wasm_module.local_func_index(*f), v))
100                .filter(|(f, _)| f.is_some())
101                .map(|(f, v)| (v.clone(), f.unwrap())),
102        );
103        Self {
104            wasm_module,
105            local_func_names,
106            short_names: ShortNames {},
107        }
108    }
109}
110
111impl SymbolRegistry for ModuleBasedSymbolRegistry {
112    fn symbol_to_name(&self, symbol: Symbol) -> String {
113        match symbol {
114            Symbol::LocalFunction(index) => self
115                .wasm_module
116                .function_names
117                .get(&self.wasm_module.func_index(index))
118                .cloned()
119                .unwrap_or(self.short_names.symbol_to_name(symbol)),
120            _ => self.short_names.symbol_to_name(symbol),
121        }
122    }
123
124    fn name_to_symbol(&self, name: &str) -> Option<Symbol> {
125        if let Some(idx) = self.local_func_names.get(name) {
126            Some(Symbol::LocalFunction(*idx))
127        } else {
128            self.short_names.name_to_symbol(name)
129        }
130    }
131}
132
133impl LLVMCompiler {
134    #[allow(clippy::too_many_arguments)]
135    fn compile_native_object(
136        &self,
137        target: &Target,
138        compile_info: &CompileModuleInfo,
139        module_translation: &ModuleTranslationState,
140        function_body_inputs: &PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
141        symbol_registry: &dyn SymbolRegistry,
142        wasmer_metadata: &[u8],
143        binary_format: target_lexicon::BinaryFormat,
144    ) -> Result<Vec<u8>, CompileError> {
145        let target_machine = self.config().target_machine(target);
146        let ctx = Context::create();
147
148        // TODO: https:/github.com/rayon-rs/rayon/issues/822
149
150        let merged_bitcode = function_body_inputs.into_iter().par_bridge().map_init(
151            || {
152                let target_machine = self.config().target_machine(target);
153                FuncTranslator::new(target_machine, binary_format).unwrap()
154            },
155            |func_translator, (i, input)| {
156                let module = func_translator.translate_to_module(
157                    &compile_info.module,
158                    module_translation,
159                    &i,
160                    input,
161                    self.config(),
162                    &compile_info.memory_styles,
163                    &compile_info.table_styles,
164                    symbol_registry,
165                )?;
166
167                Ok(module.write_bitcode_to_memory().as_slice().to_vec())
168            },
169        );
170
171        let trampolines_bitcode = compile_info.module.signatures.iter().par_bridge().map_init(
172            || {
173                let target_machine = self.config().target_machine(target);
174                FuncTrampoline::new(target_machine, binary_format).unwrap()
175            },
176            |func_trampoline, (i, sig)| {
177                let name = symbol_registry.symbol_to_name(Symbol::FunctionCallTrampoline(i));
178                let module = func_trampoline.trampoline_to_module(
179                    sig,
180                    self.config(),
181                    &name,
182                    compile_info,
183                )?;
184                Ok(module.write_bitcode_to_memory().as_slice().to_vec())
185            },
186        );
187
188        let dynamic_trampolines_bitcode =
189            compile_info.module.functions.iter().par_bridge().map_init(
190                || {
191                    let target_machine = self.config().target_machine(target);
192                    (
193                        FuncTrampoline::new(target_machine, binary_format).unwrap(),
194                        &compile_info.module.signatures,
195                    )
196                },
197                |(func_trampoline, signatures), (i, sig)| {
198                    let sig = &signatures[*sig];
199                    let name = symbol_registry.symbol_to_name(Symbol::DynamicFunctionTrampoline(i));
200                    let module =
201                        func_trampoline.dynamic_trampoline_to_module(sig, self.config(), &name)?;
202                    Ok(module.write_bitcode_to_memory().as_slice().to_vec())
203                },
204            );
205
206        let merged_bitcode = merged_bitcode
207            .chain(trampolines_bitcode)
208            .chain(dynamic_trampolines_bitcode)
209            .collect::<Result<Vec<_>, CompileError>>()?
210            .into_par_iter()
211            .reduce_with(|bc1, bc2| {
212                let ctx = Context::create();
213                let membuf = MemoryBuffer::create_from_memory_range(&bc1, "");
214                let m1 = Module::parse_bitcode_from_buffer(&membuf, &ctx).unwrap();
215                let membuf = MemoryBuffer::create_from_memory_range(&bc2, "");
216                let m2 = Module::parse_bitcode_from_buffer(&membuf, &ctx).unwrap();
217                m1.link_in_module(m2).unwrap();
218                m1.write_bitcode_to_memory().as_slice().to_vec()
219            });
220        let merged_module = if let Some(bc) = merged_bitcode {
221            let membuf = MemoryBuffer::create_from_memory_range(&bc, "");
222            Module::parse_bitcode_from_buffer(&membuf, &ctx).unwrap()
223        } else {
224            ctx.create_module("")
225        };
226
227        let i8_ty = ctx.i8_type();
228        let metadata_init = i8_ty.const_array(
229            wasmer_metadata
230                .iter()
231                .map(|v| i8_ty.const_int(*v as u64, false))
232                .collect::<Vec<_>>()
233                .as_slice(),
234        );
235        let metadata_gv = merged_module.add_global(
236            metadata_init.get_type(),
237            None,
238            &symbol_registry.symbol_to_name(wasmer_compiler::types::symbols::Symbol::Metadata),
239        );
240        metadata_gv.set_initializer(&metadata_init);
241        metadata_gv.set_linkage(Linkage::DLLExport);
242        metadata_gv.set_dll_storage_class(DLLStorageClass::Export);
243        metadata_gv.set_alignment(16);
244
245        if self.config().enable_verifier {
246            merged_module.verify().unwrap();
247        }
248
249        let memory_buffer = target_machine
250            .write_to_memory_buffer(&merged_module, FileType::Object)
251            .unwrap();
252        if let Some(ref callbacks) = self.config.callbacks {
253            callbacks.obj_memory_buffer(&CompiledKind::Module, &memory_buffer);
254        }
255
256        tracing::trace!("Finished compling the module!");
257        Ok(memory_buffer.as_slice().to_vec())
258    }
259}
260
261impl Compiler for LLVMCompiler {
262    fn name(&self) -> &str {
263        "llvm"
264    }
265
266    fn get_perfmap_enabled(&self) -> bool {
267        self.config.enable_perfmap
268    }
269
270    fn deterministic_id(&self) -> String {
271        let mut ret = format!(
272            "llvm-{}",
273            match self.config.opt_level {
274                inkwell::OptimizationLevel::None => "opt0",
275                inkwell::OptimizationLevel::Less => "optl",
276                inkwell::OptimizationLevel::Default => "optd",
277                inkwell::OptimizationLevel::Aggressive => "opta",
278            }
279        );
280
281        if self.config.enable_g0m0_opt {
282            ret.push_str("-g0m0");
283        }
284
285        ret
286    }
287
288    /// Get the middlewares for this compiler
289    fn get_middlewares(&self) -> &[Arc<dyn ModuleMiddleware>] {
290        &self.config.middlewares
291    }
292
293    fn experimental_native_compile_module(
294        &self,
295        target: &Target,
296        compile_info: &CompileModuleInfo,
297        module_translation: &ModuleTranslationState,
298        // The list of function bodies
299        function_body_inputs: &PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
300        symbol_registry: &dyn SymbolRegistry,
301        // The metadata to inject into the wasmer_metadata section of the object file.
302        wasmer_metadata: &[u8],
303    ) -> Option<Result<Vec<u8>, CompileError>> {
304        Some(self.compile_native_object(
305            target,
306            compile_info,
307            module_translation,
308            function_body_inputs,
309            symbol_registry,
310            wasmer_metadata,
311            self.config.target_binary_format(target),
312        ))
313    }
314
315    /// Compile the module using LLVM, producing a compilation result with
316    /// associated relocations.
317    fn compile_module(
318        &self,
319        target: &Target,
320        compile_info: &CompileModuleInfo,
321        module_translation: &ModuleTranslationState,
322        function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
323    ) -> Result<Compilation, CompileError> {
324        //let data = Arc::new(Mutex::new(0));
325
326        let memory_styles = &compile_info.memory_styles;
327        let table_styles = &compile_info.table_styles;
328        let binary_format = self.config.target_binary_format(target);
329
330        let module = &compile_info.module;
331
332        // TODO: merge constants in sections.
333
334        let mut module_custom_sections = PrimaryMap::new();
335
336        let mut eh_frame_section_bytes = vec![];
337        let mut eh_frame_section_relocations = vec![];
338
339        let mut compact_unwind_section_bytes = vec![];
340        let mut compact_unwind_section_relocations = vec![];
341
342        let mut got_targets: HashSet<wasmer_compiler::types::relocation::RelocationTarget> = if matches!(
343            target.triple().binary_format,
344            target_lexicon::BinaryFormat::Macho
345        ) {
346            HashSet::from_iter(vec![RelocationTarget::LibCall(LibCall::EHPersonality)])
347        } else {
348            HashSet::default()
349        };
350
351        let symbol_registry = ModuleBasedSymbolRegistry::new(module.clone());
352
353        let functions = if self.config.num_threads.get() > 1 {
354            let pool = ThreadPoolBuilder::new()
355                .num_threads(self.config.num_threads.get())
356                .build()
357                .map_err(|e| CompileError::Resource(e.to_string()))?;
358            pool.install(|| {
359                function_body_inputs
360                    .iter()
361                    .collect::<Vec<(LocalFunctionIndex, &FunctionBodyData<'_>)>>()
362                    .par_iter()
363                    .map_init(
364                        || {
365                            let target_machine = self.config().target_machine(target);
366                            FuncTranslator::new(target_machine, binary_format).unwrap()
367                        },
368                        |func_translator, (i, input)| {
369                            // TODO: remove (to serialize)
370                            //let _data = data.lock().unwrap();
371
372                            func_translator.translate(
373                                module,
374                                module_translation,
375                                i,
376                                input,
377                                self.config(),
378                                memory_styles,
379                                table_styles,
380                                &symbol_registry,
381                            )
382                        },
383                    )
384                    .collect::<Result<Vec<_>, CompileError>>()
385            })?
386        } else {
387            let target_machine = self.config().target_machine(target);
388            let func_translator = FuncTranslator::new(target_machine, binary_format).unwrap();
389
390            function_body_inputs
391                .iter()
392                .collect::<Vec<(LocalFunctionIndex, &FunctionBodyData<'_>)>>()
393                .into_iter()
394                .map(|(i, input)| {
395                    // TODO: remove (to serialize)
396                    //let _data = data.lock().unwrap();
397
398                    func_translator.translate(
399                        module,
400                        module_translation,
401                        &i,
402                        input,
403                        self.config(),
404                        memory_styles,
405                        table_styles,
406                        &symbol_registry,
407                    )
408                })
409                .collect::<Result<Vec<_>, CompileError>>()?
410        };
411
412        let functions = functions
413            .into_iter()
414            .map(|mut compiled_function| {
415                let first_section = module_custom_sections.len() as u32;
416                for (section_index, custom_section) in compiled_function.custom_sections.iter() {
417                    // TODO: remove this call to clone()
418                    let mut custom_section = custom_section.clone();
419                    for reloc in &mut custom_section.relocations {
420                        if let RelocationTarget::CustomSection(index) = reloc.reloc_target {
421                            reloc.reloc_target = RelocationTarget::CustomSection(
422                                SectionIndex::from_u32(first_section + index.as_u32()),
423                            )
424                        }
425
426                        if reloc.kind.needs_got() {
427                            got_targets.insert(reloc.reloc_target);
428                        }
429                    }
430
431                    if compiled_function
432                        .eh_frame_section_indices
433                        .contains(&section_index)
434                    {
435                        let offset = eh_frame_section_bytes.len() as u32;
436                        for reloc in &mut custom_section.relocations {
437                            reloc.offset += offset;
438                        }
439                        eh_frame_section_bytes.extend_from_slice(custom_section.bytes.as_slice());
440                        eh_frame_section_relocations.extend(custom_section.relocations);
441                        // TODO: we do this to keep the count right, remove it.
442                        module_custom_sections.push(CustomSection {
443                            protection: CustomSectionProtection::Read,
444                            alignment: None,
445                            bytes: SectionBody::new_with_vec(vec![]),
446                            relocations: vec![],
447                        });
448                    } else if compiled_function
449                        .compact_unwind_section_indices
450                        .contains(&section_index)
451                    {
452                        let offset = compact_unwind_section_bytes.len() as u32;
453                        for reloc in &mut custom_section.relocations {
454                            reloc.offset += offset;
455                        }
456                        compact_unwind_section_bytes
457                            .extend_from_slice(custom_section.bytes.as_slice());
458                        compact_unwind_section_relocations.extend(custom_section.relocations);
459                        // TODO: we do this to keep the count right, remove it.
460                        module_custom_sections.push(CustomSection {
461                            protection: CustomSectionProtection::Read,
462                            alignment: None,
463                            bytes: SectionBody::new_with_vec(vec![]),
464                            relocations: vec![],
465                        });
466                    } else {
467                        module_custom_sections.push(custom_section);
468                    }
469                }
470                for reloc in &mut compiled_function.compiled_function.relocations {
471                    if let RelocationTarget::CustomSection(index) = reloc.reloc_target {
472                        reloc.reloc_target = RelocationTarget::CustomSection(
473                            SectionIndex::from_u32(first_section + index.as_u32()),
474                        )
475                    }
476
477                    if reloc.kind.needs_got() {
478                        got_targets.insert(reloc.reloc_target);
479                    }
480                }
481                compiled_function.compiled_function
482            })
483            .collect::<PrimaryMap<LocalFunctionIndex, _>>();
484
485        let mut unwind_info = UnwindInfo::default();
486
487        if !eh_frame_section_bytes.is_empty() {
488            let eh_frame_idx = SectionIndex::from_u32(module_custom_sections.len() as u32);
489            // Do not terminate dwarf info with a zero-length CIE.
490            // Because more info will be added later
491            // in lib/object/src/module.rs emit_compilation
492            module_custom_sections.push(CustomSection {
493                protection: CustomSectionProtection::Read,
494                alignment: None,
495                bytes: SectionBody::new_with_vec(eh_frame_section_bytes),
496                relocations: eh_frame_section_relocations,
497            });
498            unwind_info.eh_frame = Some(eh_frame_idx);
499        }
500
501        if !compact_unwind_section_bytes.is_empty() {
502            let cu_index = SectionIndex::from_u32(module_custom_sections.len() as u32);
503            module_custom_sections.push(CustomSection {
504                protection: CustomSectionProtection::Read,
505                alignment: None,
506                bytes: SectionBody::new_with_vec(compact_unwind_section_bytes),
507                relocations: compact_unwind_section_relocations,
508            });
509            unwind_info.compact_unwind = Some(cu_index);
510        }
511
512        let function_call_trampolines = if self.config.num_threads.get() > 1 {
513            let pool = ThreadPoolBuilder::new()
514                .num_threads(self.config.num_threads.get())
515                .build()
516                .map_err(|e| CompileError::Resource(e.to_string()))?;
517            pool.install(|| {
518                module
519                    .signatures
520                    .values()
521                    .collect::<Vec<_>>()
522                    .par_iter()
523                    .map_init(
524                        || {
525                            let target_machine = self.config().target_machine(target);
526                            FuncTrampoline::new(target_machine, binary_format).unwrap()
527                        },
528                        |func_trampoline, sig| {
529                            func_trampoline.trampoline(sig, self.config(), "", compile_info)
530                        },
531                    )
532                    .collect::<Vec<_>>()
533                    .into_iter()
534                    .collect::<Result<PrimaryMap<_, _>, CompileError>>()
535            })?
536        } else {
537            let target_machine = self.config().target_machine(target);
538            let func_trampoline = FuncTrampoline::new(target_machine, binary_format).unwrap();
539            module
540                .signatures
541                .values()
542                .collect::<Vec<_>>()
543                .into_iter()
544                .map(|sig| func_trampoline.trampoline(sig, self.config(), "", compile_info))
545                .collect::<Vec<_>>()
546                .into_iter()
547                .collect::<Result<PrimaryMap<_, _>, CompileError>>()?
548        };
549
550        let dynamic_function_trampolines = if self.config.num_threads.get() > 1 {
551            let pool = ThreadPoolBuilder::new()
552                .num_threads(self.config.num_threads.get())
553                .build()
554                .map_err(|e| CompileError::Resource(e.to_string()))?;
555            pool.install(|| {
556                module
557                    .imported_function_types()
558                    .collect::<Vec<_>>()
559                    .par_iter()
560                    .map_init(
561                        || {
562                            let target_machine = self.config().target_machine(target);
563                            FuncTrampoline::new(target_machine, binary_format).unwrap()
564                        },
565                        |func_trampoline, func_type| {
566                            func_trampoline.dynamic_trampoline(func_type, self.config(), "")
567                        },
568                    )
569                    .collect::<Vec<_>>()
570                    .into_iter()
571                    .collect::<Result<PrimaryMap<_, _>, CompileError>>()
572            })?
573        } else {
574            let target_machine = self.config().target_machine(target);
575            let func_trampoline = FuncTrampoline::new(target_machine, binary_format).unwrap();
576            module
577                .imported_function_types()
578                .collect::<Vec<_>>()
579                .into_iter()
580                .map(|func_type| func_trampoline.dynamic_trampoline(&func_type, self.config(), ""))
581                .collect::<Vec<_>>()
582                .into_iter()
583                .collect::<Result<PrimaryMap<_, _>, CompileError>>()?
584        };
585
586        let mut got = wasmer_compiler::types::function::GOT::empty();
587
588        if !got_targets.is_empty() {
589            let pointer_width = target
590                .triple()
591                .pointer_width()
592                .map_err(|_| CompileError::Codegen("Could not get pointer width".to_string()))?;
593
594            let got_entry_size = match pointer_width {
595                target_lexicon::PointerWidth::U64 => 8,
596                target_lexicon::PointerWidth::U32 => 4,
597                target_lexicon::PointerWidth::U16 => todo!(),
598            };
599
600            let got_entry_reloc_kind = match pointer_width {
601                target_lexicon::PointerWidth::U64 => RelocationKind::Abs8,
602                target_lexicon::PointerWidth::U32 => RelocationKind::Abs4,
603                target_lexicon::PointerWidth::U16 => todo!(),
604            };
605
606            let got_data: Vec<u8> = vec![0; got_targets.len() * got_entry_size];
607            let mut got_relocs = vec![];
608
609            for (i, target) in got_targets.into_iter().enumerate() {
610                got_relocs.push(wasmer_compiler::types::relocation::Relocation {
611                    kind: got_entry_reloc_kind,
612                    reloc_target: target,
613                    offset: (i * got_entry_size) as u32,
614                    addend: 0,
615                });
616            }
617
618            let got_idx = SectionIndex::from_u32(module_custom_sections.len() as u32);
619            module_custom_sections.push(CustomSection {
620                protection: CustomSectionProtection::Read,
621                alignment: None,
622                bytes: SectionBody::new_with_vec(got_data),
623                relocations: got_relocs,
624            });
625            got.index = Some(got_idx);
626        };
627
628        tracing::trace!("Finished compling the module!");
629        Ok(Compilation {
630            functions,
631            custom_sections: module_custom_sections,
632            function_call_trampolines,
633            dynamic_function_trampolines,
634            unwind_info,
635            got,
636        })
637    }
638
639    fn with_opts(
640        &mut self,
641        suggested_compiler_opts: &wasmer_types::target::UserCompilerOptimizations,
642    ) -> Result<(), CompileError> {
643        if suggested_compiler_opts.pass_params.is_some_and(|v| v) {
644            self.config.enable_g0m0_opt = true;
645        }
646        Ok(())
647    }
648}