1use crate::config::LLVM;
2use crate::config::OptimizationStyle;
3use crate::object_file::CompiledFunction;
4use crate::translator::FuncTrampoline;
5use crate::translator::FuncTranslator;
6use itertools::Itertools;
7use rayon::ThreadPoolBuilder;
8use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
9use std::{
10 borrow::Cow,
11 collections::{HashMap, HashSet},
12 sync::Arc,
13};
14use wasmer_compiler::progress::ProgressContext;
15use wasmer_compiler::types::function::Compilation;
16use wasmer_compiler::types::function::CompiledFunctionBody;
17use wasmer_compiler::types::function::{RkyvCompilation, UnwindInfo};
18use wasmer_compiler::types::module::CompileModuleInfo;
19use wasmer_compiler::types::relocation::RelocationKind;
20use wasmer_compiler::{
21 CompiledObjects, Compiler, FunctionBodyData, ModuleMiddleware, ModuleTranslationState,
22 WasmSourceMap, emit_metadata_and_link,
23 types::{
24 relocation::RelocationTarget,
25 section::{CustomSection, CustomSectionProtection, SectionBody, SectionIndex},
26 symbols::{Symbol, SymbolRegistry},
27 },
28};
29use wasmer_compiler::{
30 WASM_LARGE_FUNCTION_THRESHOLD, WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE, build_function_buckets,
31 translate_function_buckets,
32};
33use wasmer_types::ExportIndex;
34use wasmer_types::MetadataHeader;
35use wasmer_types::entity::{EntityRef, PrimaryMap};
36use wasmer_types::target::Target;
37use wasmer_types::{
38 CompilationProgressCallback, CompileError, FunctionIndex, LocalFunctionIndex, ModuleInfo,
39 SignatureIndex,
40};
41use wasmer_vm::LibCall;
42
43#[derive(Debug)]
46pub struct LLVMCompiler {
47 config: LLVM,
48}
49
50impl LLVMCompiler {
51 pub fn new(config: LLVM) -> LLVMCompiler {
53 LLVMCompiler { config }
54 }
55
56 fn config(&self) -> &LLVM {
58 &self.config
59 }
60}
61
62struct ShortNames {}
63
64impl SymbolRegistry for ShortNames {
65 fn symbol_to_name(&self, symbol: Symbol) -> String {
66 match symbol {
67 Symbol::Metadata => "M".to_string(),
68 Symbol::LocalFunction(index) => format!("f{}", index.index()),
69 Symbol::Section(index) => format!("s{}", index.index()),
70 Symbol::FunctionCallTrampoline(index) => format!("t{}", index.index()),
71 Symbol::DynamicFunctionTrampoline(index) => format!("d{}", index.index()),
72 }
73 }
74
75 fn name_to_symbol(&self, name: &str) -> Option<Symbol> {
76 if name.len() < 2 {
77 return None;
78 }
79 let (ty, idx) = name.split_at(1);
80 if ty.starts_with('M') {
81 return Some(Symbol::Metadata);
82 }
83
84 let idx = idx.parse::<u32>().ok()?;
85 match ty.chars().next().unwrap() {
86 'f' => Some(Symbol::LocalFunction(LocalFunctionIndex::from_u32(idx))),
87 's' => Some(Symbol::Section(SectionIndex::from_u32(idx))),
88 't' => Some(Symbol::FunctionCallTrampoline(SignatureIndex::from_u32(
89 idx,
90 ))),
91 'd' => Some(Symbol::DynamicFunctionTrampoline(FunctionIndex::from_u32(
92 idx,
93 ))),
94 _ => None,
95 }
96 }
97}
98
99pub(crate) struct ModuleBasedSymbolRegistry {
100 wasm_module: Arc<ModuleInfo>,
101 local_func_names: HashMap<String, LocalFunctionIndex>,
102 short_names: ShortNames,
103}
104
105impl ModuleBasedSymbolRegistry {
106 const PROBLEMATIC_PREFIXES: &[&'static str] = &[
107 ".L", "llvm.", ];
110
111 fn new(wasm_module: Arc<ModuleInfo>) -> Self {
112 let local_func_names = HashMap::from_iter(
113 wasm_module
114 .function_names
115 .iter()
116 .map(|(f, v)| (wasm_module.local_func_index(*f), v))
117 .filter(|(f, _)| f.is_some())
118 .map(|(f, v)| (format!("{}_{}", v.clone(), f.unwrap().as_u32()), f.unwrap())),
119 );
120 Self {
121 wasm_module,
122 local_func_names,
123 short_names: ShortNames {},
124 }
125 }
126
127 fn fixup_problematic_name(name: &str) -> Cow<'_, str> {
129 for prefix in Self::PROBLEMATIC_PREFIXES {
130 if name.starts_with(prefix) {
131 return format!("_{name}").into();
132 }
133 }
134 name.into()
135 }
136
137 fn unfixup_problematic_name(name: &str) -> &str {
141 if let Some(stripped_name) = name.strip_prefix('_') {
142 for prefix in Self::PROBLEMATIC_PREFIXES {
143 if stripped_name.starts_with(prefix) {
144 return stripped_name;
145 }
146 }
147 }
148
149 name
150 }
151}
152
153impl SymbolRegistry for ModuleBasedSymbolRegistry {
154 fn symbol_to_name(&self, symbol: Symbol) -> String {
155 match symbol {
156 Symbol::LocalFunction(index) => self
157 .wasm_module
158 .function_names
159 .get(&self.wasm_module.func_index(index))
160 .map(|name| format!("{}_{}", Self::fixup_problematic_name(name), index.as_u32()))
161 .unwrap_or(self.short_names.symbol_to_name(symbol)),
162 _ => self.short_names.symbol_to_name(symbol),
163 }
164 }
165
166 fn name_to_symbol(&self, name: &str) -> Option<Symbol> {
167 let name = Self::unfixup_problematic_name(name);
168 if let Some(idx) = self.local_func_names.get(name) {
169 Some(Symbol::LocalFunction(*idx))
170 } else {
171 self.short_names.name_to_symbol(name)
172 }
173 }
174}
175
176impl Compiler for LLVMCompiler {
177 fn name(&self) -> &str {
178 "llvm"
179 }
180
181 fn get_perfmap_enabled(&self) -> bool {
182 self.config.enable_perfmap
183 }
184
185 fn get_debugger(&self) -> Option<wasmer_compiler::Debugger> {
186 self.config.debugger
187 }
188
189 fn deterministic_id(&self) -> String {
190 use wasmer_compiler::DeterministicIdComponent as Component;
191
192 let mut components = vec![Component::Llvm];
193 components.push(match self.config.opt_level {
194 inkwell::OptimizationLevel::None => Component::OptNone,
195 inkwell::OptimizationLevel::Less => Component::OptLess,
196 inkwell::OptimizationLevel::Default => Component::OptDefault,
197 inkwell::OptimizationLevel::Aggressive => Component::OptAggressive,
198 });
199 if self.config.enable_nan_canonicalization {
200 components.push(Component::NanCanonicalization);
201 }
202 if self.config.enable_non_volatile_memops {
203 components.push(Component::NonVolatileMemops);
204 }
205 if self.config.is_pic {
206 components.push(Component::Pic);
207 }
208 if self.config.enable_readonly_funcref_table {
209 components.push(Component::ReadonlyFuncrefTable);
210 }
211 const _: () = assert!(
215 MetadataHeader::CURRENT_VERSION == 24,
216 "Rename Component::DisableM0",
217 );
218 if !self.config.enable_m0 {
219 components.push(Component::DisableM0);
220 }
221
222 components
223 .into_iter()
224 .map(|component| component.to_string())
225 .collect_vec()
226 .join("-")
227 }
228
229 fn artifact_format(&self) -> String {
230 if self.config.experimental_artifact {
231 wasmer_compiler::ArtifactFormat::Native
232 } else {
233 wasmer_compiler::ArtifactFormat::Rkyv
234 }
235 .to_string()
236 }
237
238 fn get_middlewares(&self) -> &[Arc<dyn ModuleMiddleware>] {
240 &self.config.middlewares
241 }
242
243 fn enable_readonly_funcref_table(&self) -> bool {
244 self.config.enable_readonly_funcref_table
245 }
246
247 fn compile_module(
250 &self,
251 target: &Target,
252 compile_info: &CompileModuleInfo,
253 compile_info_blob: &[u8],
254 module_translation: &ModuleTranslationState,
255 function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
256 progress_callback: Option<&CompilationProgressCallback>,
257 ) -> Result<Compilation, CompileError> {
258 wasmer_compiler::validate_module_fixed_table_size(
259 &compile_info.module,
260 self.config.max_table_elements,
261 )?;
262 let function_max_stack_usage = function_body_inputs.iter().map(|_| None).collect();
263 let binary_format = self.config.target_binary_format(target);
264
265 let module = &compile_info.module;
266 let module_hash = module.hash_string();
267
268 let total_function_call_trampolines = module.signatures.len();
269 let total_dynamic_trampolines = module.num_imported_functions;
270 let total_steps = WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE
271 * ((total_dynamic_trampolines + total_function_call_trampolines) as u64)
272 + function_body_inputs
273 .iter()
274 .map(|(_, body)| body.data.len() as u64)
275 .sum::<u64>();
276
277 let progress = progress_callback
278 .cloned()
279 .map(|cb| ProgressContext::new(cb, total_steps, "Compiling functions"));
280
281 let mut module_custom_sections = PrimaryMap::new();
284
285 let mut eh_frame_section_bytes = vec![];
286 let mut eh_frame_section_relocations = vec![];
287
288 let mut compact_unwind_section_bytes = vec![];
289 let mut compact_unwind_section_relocations = vec![];
290
291 let mut got_targets: HashSet<wasmer_compiler::types::relocation::RelocationTarget> = if matches!(
292 target.triple().binary_format,
293 target_lexicon::BinaryFormat::Macho
294 ) {
295 HashSet::from_iter(vec![RelocationTarget::LibCall(LibCall::EHPersonality)])
296 } else {
297 HashSet::default()
298 };
299
300 let symbol_registry = ModuleBasedSymbolRegistry::new(module.clone());
301 let module = &compile_info.module;
302 let memory_styles = &compile_info.memory_styles;
303 let table_styles = &compile_info.table_styles;
304 let signature_hashes = &module.signature_hashes;
305
306 let pool = ThreadPoolBuilder::new()
307 .num_threads(self.config.num_threads.get())
308 .build()
309 .map_err(|e| CompileError::Resource(e.to_string()))?;
310
311 let source_map = Arc::new(if self.config.experimental_artifact {
312 WasmSourceMap::new(module, module_translation, &function_body_inputs)
313 .map_err(CompileError::Codegen)?
314 } else {
315 WasmSourceMap::default()
316 });
317 let buckets =
318 build_function_buckets(&function_body_inputs, WASM_LARGE_FUNCTION_THRESHOLD / 3);
319 let largest_bucket = buckets.first().map(|b| b.size).unwrap_or_default();
320 tracing::debug!(buckets = buckets.len(), largest_bucket, "buckets built");
321
322 let functions = translate_function_buckets(
323 &pool,
324 || {
325 let compiler = &self;
326 let target_machines = enum_iterator::all::<OptimizationStyle>()
327 .map(|style| {
328 (
329 style,
330 compiler.config().target_machine_with_opt(target, style),
331 )
332 })
333 .collect();
334 let pointer_width = target.triple().pointer_width().unwrap().bytes();
335 FuncTranslator::new(
336 target.triple().clone(),
337 target_machines,
338 binary_format,
339 pointer_width,
340 *target.cpu_features(),
341 self.config.enable_non_volatile_memops,
342 source_map.clone(),
343 module
344 .exports
345 .get("__wasm_apply_data_relocs")
346 .and_then(|export| {
347 if let ExportIndex::Function(index) = export {
348 Some(*index)
349 } else {
350 None
351 }
352 }),
353 )
354 .unwrap()
355 },
356 |func_translator, i, input| {
357 func_translator.translate(
358 module,
359 module_translation,
360 signature_hashes,
361 i,
362 input,
363 self.config(),
364 memory_styles,
365 table_styles,
366 &symbol_registry,
367 target.triple(),
368 )
369 },
370 progress.clone(),
371 &buckets,
372 )?;
373
374 let progress = progress.clone();
375 let function_call_trampolines = pool.install(|| {
376 module
377 .signatures
378 .iter()
379 .collect::<Vec<_>>()
380 .par_iter()
381 .map_init(
382 || {
383 let target_machine = self.config().target_machine(target);
384 FuncTrampoline::new(target_machine, target.triple().clone(), binary_format)
385 .unwrap()
386 },
387 |func_trampoline, (sig_index, sig)| {
388 let kind = wasmer_compiler::misc::CompiledKind::FunctionCallTrampoline(
389 *sig_index,
390 (*sig).clone(),
391 );
392 let trampoline =
393 func_trampoline.trampoline(sig, self.config(), &kind, compile_info);
394 if let Some(progress) = progress.as_ref() {
395 progress.notify_steps(WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE)?;
396 }
397 trampoline
398 },
399 )
400 .collect::<Result<Vec<_>, _>>()
401 })?;
402
403 let dynamic_function_trampolines = {
408 let progress = progress.clone();
409 let target_machine = self.config().target_machine(target);
410 let func_trampoline =
411 FuncTrampoline::new(target_machine, target.triple().clone(), binary_format)
412 .unwrap();
413 module
414 .imported_function_types()
415 .collect::<Vec<_>>()
416 .into_iter()
417 .enumerate()
418 .map(|(index, func_type)| {
419 let kind = wasmer_compiler::misc::CompiledKind::DynamicFunctionTrampoline(
420 FunctionIndex::from_u32(index as u32),
421 func_type.clone(),
422 );
423 let trampoline = func_trampoline.dynamic_trampoline(
424 &func_type,
425 self.config(),
426 &kind,
427 index as u32,
428 &mut module_custom_sections,
429 &mut eh_frame_section_bytes,
430 &mut eh_frame_section_relocations,
431 &mut compact_unwind_section_bytes,
432 &mut compact_unwind_section_relocations,
433 &module_hash,
434 )?;
435 if let Some(progress) = progress.as_ref() {
436 progress.notify_steps(WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE)?;
437 }
438 Ok(trampoline)
439 })
440 .collect::<Result<Vec<_>, CompileError>>()?
441 };
442
443 if self.config.experimental_artifact {
444 let object_files = functions
445 .into_iter()
446 .map(|compiled_function| match compiled_function {
447 CompiledFunction::Elf(path) => path,
448 CompiledFunction::Rkyv(_) => {
449 unreachable!()
450 }
451 })
452 .collect::<Vec<Vec<u8>>>();
453 let trampolines_objects = function_call_trampolines
454 .into_iter()
455 .map(|f| match f {
456 CompiledFunctionBody::Elf(path) => path,
457 CompiledFunctionBody::Rkyv(_) => {
458 unreachable!()
459 }
460 })
461 .collect::<Vec<Vec<u8>>>();
462 let dynamic_trampolines_objects = dynamic_function_trampolines
463 .into_iter()
464 .map(|f| match f {
465 CompiledFunctionBody::Elf(path) => path,
466 CompiledFunctionBody::Rkyv(_) => unreachable!(),
467 })
468 .collect::<Vec<Vec<u8>>>();
469
470 let elf_content = emit_metadata_and_link(
471 &pool,
472 target,
473 compile_info_blob,
474 CompiledObjects {
475 object_files,
476 import_trampoline_object_files: Vec::new(),
477 trampoline_object_files: trampolines_objects,
478 dynamic_trampoline_object_files: dynamic_trampolines_objects,
479 },
480 self.config
481 .callbacks
482 .as_ref()
483 .map(|callbacks| callbacks.debug_dir().clone()),
484 module.hash().map(|hash| hash.to_string()),
485 )?;
486 Ok(Compilation::Elf {
487 data: elf_content,
488 function_max_stack_usage,
489 })
490 } else {
491 let functions = functions
492 .into_iter()
493 .map(|compiled_function| {
494 let CompiledFunction::Rkyv(mut compiled_function) = compiled_function else {
495 unreachable!()
496 };
497
498 let first_section = module_custom_sections.len() as u32;
499 for (section_index, custom_section) in compiled_function.custom_sections.iter()
500 {
501 let mut custom_section = custom_section.clone();
503 for reloc in &mut custom_section.relocations {
504 if let RelocationTarget::CustomSection(index) = reloc.reloc_target {
505 reloc.reloc_target = RelocationTarget::CustomSection(
506 SectionIndex::from_u32(first_section + index.as_u32()),
507 )
508 }
509
510 if reloc.kind.needs_got() {
511 got_targets.insert(reloc.reloc_target);
512 }
513 }
514
515 if compiled_function
516 .eh_frame_section_indices
517 .contains(§ion_index)
518 {
519 let offset = eh_frame_section_bytes.len() as u32;
520 for reloc in &mut custom_section.relocations {
521 reloc.offset += offset;
522 }
523 eh_frame_section_bytes
524 .extend_from_slice(custom_section.bytes.as_slice());
525 eh_frame_section_bytes.extend_from_slice(&[0, 0, 0, 0]);
527 eh_frame_section_relocations.extend(custom_section.relocations);
528 module_custom_sections.push(CustomSection {
530 protection: CustomSectionProtection::Read,
531 alignment: None,
532 bytes: SectionBody::new_with_vec(vec![]),
533 relocations: vec![],
534 });
535 } else if compiled_function
536 .compact_unwind_section_indices
537 .contains(§ion_index)
538 {
539 let offset = compact_unwind_section_bytes.len() as u32;
540 for reloc in &mut custom_section.relocations {
541 reloc.offset += offset;
542 }
543 compact_unwind_section_bytes
544 .extend_from_slice(custom_section.bytes.as_slice());
545 compact_unwind_section_relocations.extend(custom_section.relocations);
546 module_custom_sections.push(CustomSection {
548 protection: CustomSectionProtection::Read,
549 alignment: None,
550 bytes: SectionBody::new_with_vec(vec![]),
551 relocations: vec![],
552 });
553 } else {
554 module_custom_sections.push(custom_section);
555 }
556 }
557 for reloc in &mut compiled_function.compiled_function.relocations {
558 if let RelocationTarget::CustomSection(index) = reloc.reloc_target {
559 reloc.reloc_target = RelocationTarget::CustomSection(
560 SectionIndex::from_u32(first_section + index.as_u32()),
561 )
562 }
563
564 if reloc.kind.needs_got() {
565 got_targets.insert(reloc.reloc_target);
566 }
567 }
568 compiled_function.compiled_function
569 })
570 .collect::<PrimaryMap<LocalFunctionIndex, _>>();
571
572 let mut unwind_info = UnwindInfo::default();
573
574 if !eh_frame_section_bytes.is_empty() {
575 let eh_frame_idx = SectionIndex::from_u32(module_custom_sections.len() as u32);
576 module_custom_sections.push(CustomSection {
577 protection: CustomSectionProtection::Read,
578 alignment: None,
579 bytes: SectionBody::new_with_vec(eh_frame_section_bytes),
580 relocations: eh_frame_section_relocations,
581 });
582 unwind_info.eh_frame = Some(eh_frame_idx);
583 }
584
585 if !compact_unwind_section_bytes.is_empty() {
586 let cu_index = SectionIndex::from_u32(module_custom_sections.len() as u32);
587 module_custom_sections.push(CustomSection {
588 protection: CustomSectionProtection::Read,
589 alignment: None,
590 bytes: SectionBody::new_with_vec(compact_unwind_section_bytes),
591 relocations: compact_unwind_section_relocations,
592 });
593 unwind_info.compact_unwind = Some(cu_index);
594 }
595
596 let mut got = wasmer_compiler::types::function::GOT::empty();
597
598 if !got_targets.is_empty() {
599 let got_data: Vec<u8> = vec![0; got_targets.len() * 8];
600 let mut got_relocs = vec![];
601
602 for (i, target) in got_targets.into_iter().enumerate() {
603 got_relocs.push(wasmer_compiler::types::relocation::Relocation {
604 kind: RelocationKind::Abs8,
605 reloc_target: target,
606 offset: (i * 8) as u32,
607 addend: 0,
608 });
609 }
610
611 let got_idx = SectionIndex::from_u32(module_custom_sections.len() as u32);
612 module_custom_sections.push(CustomSection {
613 protection: CustomSectionProtection::Read,
614 alignment: None,
615 bytes: SectionBody::new_with_vec(got_data),
616 relocations: got_relocs,
617 });
618 got.index = Some(got_idx);
619 };
620
621 let function_call_trampolines = function_call_trampolines
622 .into_iter()
623 .map(|f| {
624 let CompiledFunctionBody::Rkyv(function) = f else {
625 unreachable!()
626 };
627 function
628 })
629 .collect();
630 let dynamic_function_trampolines = dynamic_function_trampolines
631 .into_iter()
632 .map(|f| {
633 let CompiledFunctionBody::Rkyv(function) = f else {
634 unreachable!()
635 };
636 function
637 })
638 .collect();
639
640 Ok(Compilation::Rkyv {
641 compilation: RkyvCompilation {
642 functions,
643 custom_sections: module_custom_sections,
644 function_call_trampolines,
645 dynamic_function_trampolines,
646 unwind_info,
647 got,
648 },
649 function_max_stack_usage,
650 })
651 }
652 }
653
654 fn with_opts(
656 &mut self,
657 _suggested_compiler_opts: &wasmer_types::target::UserCompilerOptimizations,
658 ) -> Result<(), CompileError> {
659 Ok(())
660 }
661}