1use crate::config::LLVM;
2use crate::config::OptimizationStyle;
3use crate::translator::FuncTrampoline;
4use crate::translator::FuncTranslator;
5use rayon::ThreadPoolBuilder;
6use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
7use std::{
8 borrow::Cow,
9 collections::{HashMap, HashSet},
10 sync::Arc,
11};
12use wasmer_compiler::progress::ProgressContext;
13use wasmer_compiler::types::function::{Compilation, UnwindInfo};
14use wasmer_compiler::types::module::CompileModuleInfo;
15use wasmer_compiler::types::relocation::RelocationKind;
16use wasmer_compiler::{
17 Compiler, FunctionBodyData, ModuleMiddleware, ModuleTranslationState,
18 types::{
19 relocation::RelocationTarget,
20 section::{CustomSection, CustomSectionProtection, SectionBody, SectionIndex},
21 symbols::{Symbol, SymbolRegistry},
22 },
23};
24use wasmer_compiler::{
25 WASM_LARGE_FUNCTION_THRESHOLD, WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE, build_function_buckets,
26 translate_function_buckets,
27};
28use wasmer_types::ExportIndex;
29use wasmer_types::entity::{EntityRef, PrimaryMap};
30use wasmer_types::target::Target;
31use wasmer_types::{
32 CompilationProgressCallback, CompileError, FunctionIndex, LocalFunctionIndex, ModuleInfo,
33 SignatureIndex,
34};
35use wasmer_vm::LibCall;
36
37#[derive(Debug)]
40pub struct LLVMCompiler {
41 config: LLVM,
42}
43
44impl LLVMCompiler {
45 pub fn new(config: LLVM) -> LLVMCompiler {
47 LLVMCompiler { config }
48 }
49
50 fn config(&self) -> &LLVM {
52 &self.config
53 }
54}
55
56struct ShortNames {}
57
58impl SymbolRegistry for ShortNames {
59 fn symbol_to_name(&self, symbol: Symbol) -> String {
60 match symbol {
61 Symbol::Metadata => "M".to_string(),
62 Symbol::LocalFunction(index) => format!("f{}", index.index()),
63 Symbol::Section(index) => format!("s{}", index.index()),
64 Symbol::FunctionCallTrampoline(index) => format!("t{}", index.index()),
65 Symbol::DynamicFunctionTrampoline(index) => format!("d{}", index.index()),
66 }
67 }
68
69 fn name_to_symbol(&self, name: &str) -> Option<Symbol> {
70 if name.len() < 2 {
71 return None;
72 }
73 let (ty, idx) = name.split_at(1);
74 if ty.starts_with('M') {
75 return Some(Symbol::Metadata);
76 }
77
78 let idx = idx.parse::<u32>().ok()?;
79 match ty.chars().next().unwrap() {
80 'f' => Some(Symbol::LocalFunction(LocalFunctionIndex::from_u32(idx))),
81 's' => Some(Symbol::Section(SectionIndex::from_u32(idx))),
82 't' => Some(Symbol::FunctionCallTrampoline(SignatureIndex::from_u32(
83 idx,
84 ))),
85 'd' => Some(Symbol::DynamicFunctionTrampoline(FunctionIndex::from_u32(
86 idx,
87 ))),
88 _ => None,
89 }
90 }
91}
92
93pub(crate) struct ModuleBasedSymbolRegistry {
94 wasm_module: Arc<ModuleInfo>,
95 local_func_names: HashMap<String, LocalFunctionIndex>,
96 short_names: ShortNames,
97}
98
99impl ModuleBasedSymbolRegistry {
100 const PROBLEMATIC_PREFIXES: &[&'static str] = &[
101 ".L", "llvm.", ];
104
105 fn new(wasm_module: Arc<ModuleInfo>) -> Self {
106 let local_func_names = HashMap::from_iter(
107 wasm_module
108 .function_names
109 .iter()
110 .map(|(f, v)| (wasm_module.local_func_index(*f), v))
111 .filter(|(f, _)| f.is_some())
112 .map(|(f, v)| (format!("{}_{}", v.clone(), f.unwrap().as_u32()), f.unwrap())),
113 );
114 Self {
115 wasm_module,
116 local_func_names,
117 short_names: ShortNames {},
118 }
119 }
120
121 fn fixup_problematic_name(name: &str) -> Cow<'_, str> {
123 for prefix in Self::PROBLEMATIC_PREFIXES {
124 if name.starts_with(prefix) {
125 return format!("_{name}").into();
126 }
127 }
128 name.into()
129 }
130
131 fn unfixup_problematic_name(name: &str) -> &str {
135 if let Some(stripped_name) = name.strip_prefix('_') {
136 for prefix in Self::PROBLEMATIC_PREFIXES {
137 if stripped_name.starts_with(prefix) {
138 return stripped_name;
139 }
140 }
141 }
142
143 name
144 }
145}
146
147impl SymbolRegistry for ModuleBasedSymbolRegistry {
148 fn symbol_to_name(&self, symbol: Symbol) -> String {
149 match symbol {
150 Symbol::LocalFunction(index) => self
151 .wasm_module
152 .function_names
153 .get(&self.wasm_module.func_index(index))
154 .map(|name| format!("{}_{}", Self::fixup_problematic_name(name), index.as_u32()))
155 .unwrap_or(self.short_names.symbol_to_name(symbol)),
156 _ => self.short_names.symbol_to_name(symbol),
157 }
158 }
159
160 fn name_to_symbol(&self, name: &str) -> Option<Symbol> {
161 let name = Self::unfixup_problematic_name(name);
162 if let Some(idx) = self.local_func_names.get(name) {
163 Some(Symbol::LocalFunction(*idx))
164 } else {
165 self.short_names.name_to_symbol(name)
166 }
167 }
168}
169
170impl Compiler for LLVMCompiler {
171 fn name(&self) -> &str {
172 "llvm"
173 }
174
175 fn get_perfmap_enabled(&self) -> bool {
176 self.config.enable_perfmap
177 }
178
179 fn deterministic_id(&self) -> String {
180 format!(
181 "llvm-{}",
182 match self.config.opt_level {
183 inkwell::OptimizationLevel::None => "opt0",
184 inkwell::OptimizationLevel::Less => "optl",
185 inkwell::OptimizationLevel::Default => "optd",
186 inkwell::OptimizationLevel::Aggressive => "opta",
187 }
188 )
189 }
190
191 fn get_middlewares(&self) -> &[Arc<dyn ModuleMiddleware>] {
193 &self.config.middlewares
194 }
195
196 fn enable_readonly_funcref_table(&self) -> bool {
197 self.config.enable_readonly_funcref_table
198 }
199
200 fn compile_module(
203 &self,
204 target: &Target,
205 compile_info: &CompileModuleInfo,
206 module_translation: &ModuleTranslationState,
207 function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
208 progress_callback: Option<&CompilationProgressCallback>,
209 ) -> Result<Compilation, CompileError> {
210 let binary_format = self.config.target_binary_format(target);
211
212 let module = &compile_info.module;
213 let module_hash = module.hash_string();
214
215 let total_function_call_trampolines = module.signatures.len();
216 let total_dynamic_trampolines = module.num_imported_functions;
217 let total_steps = WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE
218 * ((total_dynamic_trampolines + total_function_call_trampolines) as u64)
219 + function_body_inputs
220 .iter()
221 .map(|(_, body)| body.data.len() as u64)
222 .sum::<u64>();
223
224 let progress = progress_callback
225 .cloned()
226 .map(|cb| ProgressContext::new(cb, total_steps, "Compiling functions"));
227
228 let mut module_custom_sections = PrimaryMap::new();
231
232 let mut eh_frame_section_bytes = vec![];
233 let mut eh_frame_section_relocations = vec![];
234
235 let mut compact_unwind_section_bytes = vec![];
236 let mut compact_unwind_section_relocations = vec![];
237
238 let mut got_targets: HashSet<wasmer_compiler::types::relocation::RelocationTarget> = if matches!(
239 target.triple().binary_format,
240 target_lexicon::BinaryFormat::Macho
241 ) {
242 HashSet::from_iter(vec![RelocationTarget::LibCall(LibCall::EHPersonality)])
243 } else {
244 HashSet::default()
245 };
246
247 let symbol_registry = ModuleBasedSymbolRegistry::new(module.clone());
248 let module = &compile_info.module;
249 let memory_styles = &compile_info.memory_styles;
250 let table_styles = &compile_info.table_styles;
251 let signature_hashes = &module.signature_hashes;
252
253 let pool = ThreadPoolBuilder::new()
254 .num_threads(self.config.num_threads.get())
255 .build()
256 .map_err(|e| CompileError::Resource(e.to_string()))?;
257
258 let buckets =
259 build_function_buckets(&function_body_inputs, WASM_LARGE_FUNCTION_THRESHOLD / 3);
260 let largest_bucket = buckets.first().map(|b| b.size).unwrap_or_default();
261 tracing::debug!(buckets = buckets.len(), largest_bucket, "buckets built");
262 let functions = translate_function_buckets(
263 &pool,
264 || {
265 let compiler = &self;
266 let target_machines = enum_iterator::all::<OptimizationStyle>()
267 .map(|style| {
268 (
269 style,
270 compiler.config().target_machine_with_opt(target, style),
271 )
272 })
273 .collect();
274 let pointer_width = target.triple().pointer_width().unwrap().bytes();
275 FuncTranslator::new(
276 target.triple().clone(),
277 target_machines,
278 binary_format,
279 pointer_width,
280 *target.cpu_features(),
281 self.config.enable_non_volatile_memops,
282 module
283 .exports
284 .get("__wasm_apply_data_relocs")
285 .and_then(|export| {
286 if let ExportIndex::Function(index) = export {
287 Some(*index)
288 } else {
289 None
290 }
291 }),
292 )
293 .unwrap()
294 },
295 |func_translator, i, input| {
296 func_translator.translate(
297 module,
298 module_translation,
299 signature_hashes,
300 i,
301 input,
302 self.config(),
303 memory_styles,
304 table_styles,
305 &symbol_registry,
306 target.triple(),
307 )
308 },
309 progress.clone(),
310 &buckets,
311 )?;
312
313 let functions = functions
314 .into_iter()
315 .map(|mut compiled_function| {
316 let first_section = module_custom_sections.len() as u32;
317 for (section_index, custom_section) in compiled_function.custom_sections.iter() {
318 let mut custom_section = custom_section.clone();
320 for reloc in &mut custom_section.relocations {
321 if let RelocationTarget::CustomSection(index) = reloc.reloc_target {
322 reloc.reloc_target = RelocationTarget::CustomSection(
323 SectionIndex::from_u32(first_section + index.as_u32()),
324 )
325 }
326
327 if reloc.kind.needs_got() {
328 got_targets.insert(reloc.reloc_target);
329 }
330 }
331
332 if compiled_function
333 .eh_frame_section_indices
334 .contains(§ion_index)
335 {
336 let offset = eh_frame_section_bytes.len() as u32;
337 for reloc in &mut custom_section.relocations {
338 reloc.offset += offset;
339 }
340 eh_frame_section_bytes.extend_from_slice(custom_section.bytes.as_slice());
341 eh_frame_section_bytes.extend_from_slice(&[0, 0, 0, 0]);
343 eh_frame_section_relocations.extend(custom_section.relocations);
344 module_custom_sections.push(CustomSection {
346 protection: CustomSectionProtection::Read,
347 alignment: None,
348 bytes: SectionBody::new_with_vec(vec![]),
349 relocations: vec![],
350 });
351 } else if compiled_function
352 .compact_unwind_section_indices
353 .contains(§ion_index)
354 {
355 let offset = compact_unwind_section_bytes.len() as u32;
356 for reloc in &mut custom_section.relocations {
357 reloc.offset += offset;
358 }
359 compact_unwind_section_bytes
360 .extend_from_slice(custom_section.bytes.as_slice());
361 compact_unwind_section_relocations.extend(custom_section.relocations);
362 module_custom_sections.push(CustomSection {
364 protection: CustomSectionProtection::Read,
365 alignment: None,
366 bytes: SectionBody::new_with_vec(vec![]),
367 relocations: vec![],
368 });
369 } else {
370 module_custom_sections.push(custom_section);
371 }
372 }
373 for reloc in &mut compiled_function.compiled_function.relocations {
374 if let RelocationTarget::CustomSection(index) = reloc.reloc_target {
375 reloc.reloc_target = RelocationTarget::CustomSection(
376 SectionIndex::from_u32(first_section + index.as_u32()),
377 )
378 }
379
380 if reloc.kind.needs_got() {
381 got_targets.insert(reloc.reloc_target);
382 }
383 }
384 compiled_function.compiled_function
385 })
386 .collect::<PrimaryMap<LocalFunctionIndex, _>>();
387
388 let progress = progress.clone();
389 let function_call_trampolines = pool.install(|| {
390 module
391 .signatures
392 .values()
393 .collect::<Vec<_>>()
394 .par_iter()
395 .map_init(
396 || {
397 let target_machine = self.config().target_machine(target);
398 FuncTrampoline::new(target_machine, target.triple().clone(), binary_format)
399 .unwrap()
400 },
401 |func_trampoline, sig| {
402 let trampoline =
403 func_trampoline.trampoline(sig, self.config(), "", compile_info);
404 if let Some(progress) = progress.as_ref() {
405 progress.notify_steps(WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE)?;
406 }
407 trampoline
408 },
409 )
410 .collect::<Vec<_>>()
411 .into_iter()
412 .collect::<Result<PrimaryMap<_, _>, CompileError>>()
413 })?;
414
415 let dynamic_function_trampolines = {
420 let progress = progress.clone();
421 let target_machine = self.config().target_machine(target);
422 let func_trampoline =
423 FuncTrampoline::new(target_machine, target.triple().clone(), binary_format)
424 .unwrap();
425 module
426 .imported_function_types()
427 .collect::<Vec<_>>()
428 .into_iter()
429 .enumerate()
430 .map(|(index, func_type)| {
431 let trampoline = func_trampoline.dynamic_trampoline(
432 &func_type,
433 self.config(),
434 "",
435 index as u32,
436 &mut module_custom_sections,
437 &mut eh_frame_section_bytes,
438 &mut eh_frame_section_relocations,
439 &mut compact_unwind_section_bytes,
440 &mut compact_unwind_section_relocations,
441 &module_hash,
442 )?;
443 if let Some(progress) = progress.as_ref() {
444 progress.notify_steps(WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE)?;
445 }
446 Ok(trampoline)
447 })
448 .collect::<Vec<_>>()
449 .into_iter()
450 .collect::<Result<PrimaryMap<_, _>, CompileError>>()?
451 };
452
453 let mut unwind_info = UnwindInfo::default();
454
455 if !eh_frame_section_bytes.is_empty() {
456 let eh_frame_idx = SectionIndex::from_u32(module_custom_sections.len() as u32);
457 module_custom_sections.push(CustomSection {
458 protection: CustomSectionProtection::Read,
459 alignment: None,
460 bytes: SectionBody::new_with_vec(eh_frame_section_bytes),
461 relocations: eh_frame_section_relocations,
462 });
463 unwind_info.eh_frame = Some(eh_frame_idx);
464 }
465
466 if !compact_unwind_section_bytes.is_empty() {
467 let cu_index = SectionIndex::from_u32(module_custom_sections.len() as u32);
468 module_custom_sections.push(CustomSection {
469 protection: CustomSectionProtection::Read,
470 alignment: None,
471 bytes: SectionBody::new_with_vec(compact_unwind_section_bytes),
472 relocations: compact_unwind_section_relocations,
473 });
474 unwind_info.compact_unwind = Some(cu_index);
475 }
476
477 let mut got = wasmer_compiler::types::function::GOT::empty();
478
479 if !got_targets.is_empty() {
480 let pointer_width = target
481 .triple()
482 .pointer_width()
483 .map_err(|_| CompileError::Codegen("Could not get pointer width".to_string()))?;
484
485 let got_entry_size = match pointer_width {
486 target_lexicon::PointerWidth::U64 => 8,
487 target_lexicon::PointerWidth::U32 => 4,
488 target_lexicon::PointerWidth::U16 => todo!(),
489 };
490
491 let got_entry_reloc_kind = match pointer_width {
492 target_lexicon::PointerWidth::U64 => RelocationKind::Abs8,
493 target_lexicon::PointerWidth::U32 => RelocationKind::Abs4,
494 target_lexicon::PointerWidth::U16 => todo!(),
495 };
496
497 let got_data: Vec<u8> = vec![0; got_targets.len() * got_entry_size];
498 let mut got_relocs = vec![];
499
500 for (i, target) in got_targets.into_iter().enumerate() {
501 got_relocs.push(wasmer_compiler::types::relocation::Relocation {
502 kind: got_entry_reloc_kind,
503 reloc_target: target,
504 offset: (i * got_entry_size) as u32,
505 addend: 0,
506 });
507 }
508
509 let got_idx = SectionIndex::from_u32(module_custom_sections.len() as u32);
510 module_custom_sections.push(CustomSection {
511 protection: CustomSectionProtection::Read,
512 alignment: None,
513 bytes: SectionBody::new_with_vec(got_data),
514 relocations: got_relocs,
515 });
516 got.index = Some(got_idx);
517 };
518
519 Ok(Compilation {
520 functions,
521 custom_sections: module_custom_sections,
522 function_call_trampolines,
523 dynamic_function_trampolines,
524 unwind_info,
525 got,
526 })
527 }
528
529 fn with_opts(
530 &mut self,
531 _suggested_compiler_opts: &wasmer_types::target::UserCompilerOptimizations,
532 ) -> Result<(), CompileError> {
533 Ok(())
534 }
535}