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