1#[cfg(feature = "unwind")]
4use crate::dwarf::WriterRelocate;
5
6#[cfg(feature = "unwind")]
7use crate::eh::{FunctionLsdaData, build_function_lsda, build_lsda_section, build_tag_section};
8
9#[cfg(feature = "unwind")]
10use crate::translator::CraneliftUnwindInfo;
11use crate::{
12 address_map::get_function_address_map,
13 config::Cranelift,
14 func_environ::{FuncEnvironment, get_function_name},
15 trampoline::{
16 FunctionBuilderContext, make_trampoline_dynamic_function, make_trampoline_function_call,
17 },
18 translator::{
19 FuncTranslator, compiled_function_unwind_info, irlibcall_to_libcall,
20 irreloc_to_relocationkind, signature_to_cranelift_ir,
21 },
22};
23use cranelift_codegen::{
24 Context, FinalizedMachReloc, FinalizedRelocTarget, MachTrap,
25 ir::{self, ExternalName, UserFuncName},
26};
27
28#[cfg(feature = "unwind")]
29use gimli::{
30 constants::DW_EH_PE_absptr,
31 write::{Address, EhFrame, FrameDescriptionEntry, FrameTable, Writer},
32};
33
34#[cfg(feature = "rayon")]
35use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
36#[cfg(feature = "unwind")]
37use std::collections::HashMap;
38use std::sync::Arc;
39use wasmer_compiler::WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE;
40
41use wasmer_compiler::progress::ProgressContext;
42#[cfg(feature = "unwind")]
43use wasmer_compiler::types::{section::SectionIndex, unwind::CompiledFunctionUnwindInfo};
44use wasmer_compiler::{
45 Compiler, FunctionBinaryReader, FunctionBodyData, MiddlewareBinaryReader, ModuleMiddleware,
46 ModuleMiddlewareChain, ModuleTranslationState,
47 types::{
48 function::{
49 Compilation, CompiledFunction, CompiledFunctionFrameInfo, FunctionBody, UnwindInfo,
50 },
51 module::CompileModuleInfo,
52 relocation::{Relocation, RelocationTarget},
53 },
54};
55#[cfg(feature = "rayon")]
56use wasmer_compiler::{build_function_buckets, translate_function_buckets};
57#[cfg(feature = "unwind")]
58use wasmer_types::entity::EntityRef;
59use wasmer_types::entity::PrimaryMap;
60#[cfg(feature = "unwind")]
61use wasmer_types::target::CallingConvention;
62use wasmer_types::target::Target;
63use wasmer_types::{
64 CompilationProgressCallback, CompileError, FunctionIndex, LocalFunctionIndex, ModuleInfo,
65 SignatureIndex, TrapCode, TrapInformation,
66};
67
68pub struct CraneliftCompiledFunction {
69 function: CompiledFunction,
70 #[cfg(feature = "unwind")]
71 fde: Option<FrameDescriptionEntry>,
72 #[cfg(feature = "unwind")]
73 function_lsda: Option<FunctionLsdaData>,
74}
75
76impl wasmer_compiler::CompiledFunction for CraneliftCompiledFunction {}
77
78#[derive(Debug)]
81pub struct CraneliftCompiler {
82 config: Cranelift,
83}
84
85impl CraneliftCompiler {
86 pub fn new(config: Cranelift) -> Self {
88 Self { config }
89 }
90
91 pub fn config(&self) -> &Cranelift {
93 &self.config
94 }
95
96 fn compile_module_internal(
99 &self,
100 target: &Target,
101 compile_info: &CompileModuleInfo,
102 module_translation_state: &ModuleTranslationState,
103 function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
104 progress_callback: Option<&CompilationProgressCallback>,
105 ) -> Result<Compilation, CompileError> {
106 let isa = self
107 .config()
108 .isa(target)
109 .map_err(|error| CompileError::Codegen(error.to_string()))?;
110 let frontend_config = isa.frontend_config();
111 #[cfg(feature = "unwind")]
112 let pointer_bytes = frontend_config.pointer_bytes();
113 let memory_styles = &compile_info.memory_styles;
114 let table_styles = &compile_info.table_styles;
115 let module = &compile_info.module;
116 let signatures = module
117 .signatures
118 .iter()
119 .map(|(_sig_index, func_type)| signature_to_cranelift_ir(func_type, frontend_config))
120 .collect::<PrimaryMap<SignatureIndex, ir::Signature>>();
121 let signature_hashes = &module.signature_hashes;
122
123 let total_function_call_trampolines = module.signatures.len();
124 let total_dynamic_trampolines = module.num_imported_functions;
125 let total_steps = WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE
126 * ((total_dynamic_trampolines + total_function_call_trampolines) as u64)
127 + function_body_inputs
128 .iter()
129 .map(|(_, body)| body.data.len() as u64)
130 .sum::<u64>();
131 let progress = progress_callback
132 .cloned()
133 .map(|cb| ProgressContext::new(cb, total_steps, "cranelift::functions"));
134
135 #[cfg(feature = "unwind")]
137 let dwarf_frametable = if function_body_inputs.is_empty() {
138 None
142 } else {
143 match target.triple().default_calling_convention() {
144 Ok(CallingConvention::SystemV) => match isa.create_systemv_cie() {
145 Some(mut cie) => {
146 cie.personality = Some((
147 DW_EH_PE_absptr,
148 Address::Symbol {
149 symbol: WriterRelocate::PERSONALITY_SYMBOL,
150 addend: 0,
151 },
152 ));
153 cie.lsda_encoding = Some(DW_EH_PE_absptr);
154 let mut dwarf_frametable = FrameTable::default();
155 let cie_id = dwarf_frametable.add_cie(cie);
156 Some((dwarf_frametable, cie_id))
157 }
158 None => None,
160 },
161 _ => None,
162 }
163 };
164
165 let compile_function = |func_translator: &mut FuncTranslator,
168 i: &LocalFunctionIndex,
169 input: &FunctionBodyData|
170 -> Result<CraneliftCompiledFunction, CompileError> {
171 let func_index = module.func_index(*i);
172 let mut context = Context::new();
173 let mut func_env = FuncEnvironment::new(
174 isa.frontend_config(),
175 module,
176 &signatures,
177 signature_hashes,
178 memory_styles,
179 table_styles,
180 );
181 context.func.name = match get_function_name(&mut context.func, func_index) {
182 ExternalName::User(nameref) => {
183 if context.func.params.user_named_funcs().is_valid(nameref) {
184 let name = &context.func.params.user_named_funcs()[nameref];
185 UserFuncName::User(name.clone())
186 } else {
187 UserFuncName::default()
188 }
189 }
190 ExternalName::TestCase(testcase) => UserFuncName::Testcase(testcase),
191 _ => UserFuncName::default(),
192 };
193 context.func.signature = signatures[module.functions[func_index]].clone();
194 let mut reader =
199 MiddlewareBinaryReader::new_with_offset(input.data, input.module_offset);
200 reader.set_middleware_chain(
201 self.config
202 .middlewares
203 .generate_function_middleware_chain(*i),
204 );
205
206 func_translator.translate(
207 module_translation_state,
208 &mut reader,
209 &mut context.func,
210 &mut func_env,
211 *i,
212 )?;
213
214 if let Some(callbacks) = self.config.callbacks.as_ref() {
215 use wasmer_compiler::misc::CompiledKind;
216
217 callbacks.preopt_ir(
218 &CompiledKind::Local(*i, compile_info.module.get_function_name(func_index)),
219 &compile_info.module.hash_string(),
220 context.func.display().to_string().as_bytes(),
221 );
222 }
223
224 let mut code_buf: Vec<u8> = Vec::new();
225 let mut ctrl_plane = Default::default();
226 let func_name_map = context.func.params.user_named_funcs().clone();
227 let result = context
228 .compile(&*isa, &mut ctrl_plane)
229 .map_err(|error| CompileError::Codegen(format!("{error:#?}")))?;
230 code_buf.extend_from_slice(result.code_buffer());
231
232 if let Some(callbacks) = self.config.callbacks.as_ref() {
233 use wasmer_compiler::misc::CompiledKind;
234
235 callbacks.obj_memory_buffer(
236 &CompiledKind::Local(*i, compile_info.module.get_function_name(func_index)),
237 &compile_info.module.hash_string(),
238 &code_buf,
239 );
240 callbacks.asm_memory_buffer(
241 &CompiledKind::Local(*i, compile_info.module.get_function_name(func_index)),
242 &compile_info.module.hash_string(),
243 target.triple().architecture,
244 &code_buf,
245 )?;
246 }
247
248 let func_relocs = result
249 .buffer
250 .relocs()
251 .iter()
252 .map(|r| mach_reloc_to_reloc(module, &func_name_map, r))
253 .collect::<Vec<_>>();
254
255 let traps = result
256 .buffer
257 .traps()
258 .iter()
259 .map(mach_trap_to_trap)
260 .collect::<Vec<_>>();
261
262 #[cfg(feature = "unwind")]
263 let function_lsda = if dwarf_frametable.is_some() {
264 build_function_lsda(
265 result.buffer.call_sites(),
266 result.buffer.data().len(),
267 pointer_bytes,
268 )
269 } else {
270 None
271 };
272
273 #[allow(unused)]
274 let (unwind_info, fde) = match compiled_function_unwind_info(&*isa, &context)? {
275 #[cfg(feature = "unwind")]
276 CraneliftUnwindInfo::Fde(fde) => {
277 if dwarf_frametable.is_some() {
278 let fde = fde.to_fde(Address::Symbol {
279 symbol: WriterRelocate::FUNCTION_SYMBOL,
282 addend: i.index() as _,
285 });
286 (Some(CompiledFunctionUnwindInfo::Dwarf), Some(fde))
288 } else {
289 (None, None)
290 }
291 }
292 #[cfg(feature = "unwind")]
293 other => (other.maybe_into_to_windows_unwind(), None),
294
295 #[cfg(not(feature = "unwind"))]
298 other => (other.maybe_into_to_windows_unwind(), None::<()>),
299 };
300
301 let range = reader.range();
302 let address_map = get_function_address_map(&context, range, code_buf.len());
303
304 Ok(CraneliftCompiledFunction {
305 function: CompiledFunction {
306 body: FunctionBody {
307 body: code_buf,
308 unwind_info,
309 },
310 relocations: func_relocs,
311 frame_info: CompiledFunctionFrameInfo { address_map, traps },
312 },
313 #[cfg(feature = "unwind")]
314 fde,
315 #[cfg(feature = "unwind")]
316 function_lsda,
317 })
318 };
319
320 #[cfg_attr(not(feature = "unwind"), allow(unused_mut))]
321 let mut custom_sections = PrimaryMap::new();
322
323 #[cfg(not(feature = "rayon"))]
324 let mut func_translator =
325 FuncTranslator::new(self.config.allow_experimental_unaligned_memory_accesses);
326 #[cfg(not(feature = "rayon"))]
327 let results = function_body_inputs
328 .iter()
329 .collect::<Vec<(LocalFunctionIndex, &FunctionBodyData<'_>)>>()
330 .into_iter()
331 .map(|(i, input)| {
332 let result = compile_function(&mut func_translator, &i, input)?;
333 if let Some(progress) = progress.as_ref() {
334 progress.notify_steps(input.data.len() as u64)?;
335 }
336 Ok(result)
337 })
338 .collect::<Result<Vec<_>, CompileError>>()?;
339 #[cfg(feature = "rayon")]
340 let results = {
341 use wasmer_compiler::WASM_LARGE_FUNCTION_THRESHOLD;
342
343 let buckets =
344 build_function_buckets(&function_body_inputs, WASM_LARGE_FUNCTION_THRESHOLD / 3);
345 let largest_bucket = buckets.first().map(|b| b.size).unwrap_or_default();
346 tracing::debug!(buckets = buckets.len(), largest_bucket, "buckets built");
347 let num_threads = self.config.num_threads.get();
348 let pool = rayon::ThreadPoolBuilder::new()
349 .num_threads(num_threads)
350 .build()
351 .unwrap();
352
353 translate_function_buckets(
354 &pool,
355 || FuncTranslator::new(self.config.allow_experimental_unaligned_memory_accesses),
356 |func_translator, i, input| compile_function(func_translator, i, input),
357 progress.clone(),
358 &buckets,
359 )?
360 };
361
362 let mut functions = Vec::with_capacity(function_body_inputs.len());
363 #[cfg(feature = "unwind")]
364 let mut fdes = Vec::with_capacity(function_body_inputs.len());
365 #[cfg(feature = "unwind")]
366 let mut lsda_data = Vec::with_capacity(function_body_inputs.len());
367
368 for compiled in results {
369 let CraneliftCompiledFunction {
370 function,
371 #[cfg(feature = "unwind")]
372 fde,
373 #[cfg(feature = "unwind")]
374 function_lsda,
375 } = compiled;
376 functions.push(function);
377 #[cfg(feature = "unwind")]
378 {
379 fdes.push(fde);
380 lsda_data.push(function_lsda);
381 }
382 }
383
384 #[cfg(feature = "unwind")]
385 let (_tag_section_index, lsda_section_index, function_lsda_offsets) =
386 if dwarf_frametable.is_some() {
387 let mut tag_section_index = None;
388 let mut tag_offsets = HashMap::new();
389 if let Some((tag_section, offsets)) = build_tag_section(&lsda_data) {
390 custom_sections.push(tag_section);
391 tag_section_index = Some(SectionIndex::new(custom_sections.len() - 1));
392 tag_offsets = offsets;
393 }
394 let lsda_vec = lsda_data;
395 let (lsda_section, offsets_per_function) =
396 build_lsda_section(lsda_vec, pointer_bytes, &tag_offsets, tag_section_index);
397 let mut lsda_section_index = None;
398 if let Some(section) = lsda_section {
399 custom_sections.push(section);
400 lsda_section_index = Some(SectionIndex::new(custom_sections.len() - 1));
401 }
402 (tag_section_index, lsda_section_index, offsets_per_function)
403 } else {
404 (None, None, vec![None; functions.len()])
405 };
406
407 #[cfg_attr(not(feature = "unwind"), allow(unused_mut))]
408 let mut unwind_info = UnwindInfo::default();
409
410 #[cfg(feature = "unwind")]
411 if let Some((mut dwarf_frametable, cie_id)) = dwarf_frametable {
412 for (func_idx, fde_opt) in fdes.into_iter().enumerate() {
413 if let Some(mut fde) = fde_opt {
414 let has_lsda = function_lsda_offsets
415 .get(func_idx)
416 .and_then(|v| *v)
417 .is_some();
418 let lsda_address = if has_lsda {
419 debug_assert!(
420 lsda_section_index.is_some(),
421 "LSDA offsets require an LSDA section"
422 );
423 if lsda_section_index.is_some() {
424 let symbol =
425 WriterRelocate::lsda_symbol(LocalFunctionIndex::new(func_idx));
426 Address::Symbol { symbol, addend: 0 }
427 } else {
428 Address::Constant(0)
429 }
430 } else {
431 Address::Constant(0)
432 };
433 fde.lsda = Some(lsda_address);
434 dwarf_frametable.add_fde(cie_id, fde);
435 }
436 }
437
438 let mut writer = WriterRelocate::new(target.triple().endianness().ok());
439 if let Some(lsda_section_index) = lsda_section_index {
440 for (func_idx, offset) in function_lsda_offsets.iter().enumerate() {
441 if let Some(offset) = offset {
442 writer.register_lsda_symbol(
443 WriterRelocate::lsda_symbol(LocalFunctionIndex::new(func_idx)),
444 RelocationTarget::CustomSection(lsda_section_index),
445 *offset,
446 );
447 }
448 }
449 }
450
451 let mut eh_frame = EhFrame(writer);
452 dwarf_frametable.write_eh_frame(&mut eh_frame).unwrap();
453 eh_frame.write(&[0, 0, 0, 0]).unwrap(); let eh_frame_section = eh_frame.0.into_section();
456 custom_sections.push(eh_frame_section);
457 unwind_info.eh_frame = Some(SectionIndex::new(custom_sections.len() - 1));
458 };
459
460 let module_hash = module.hash_string();
461
462 #[cfg(not(feature = "rayon"))]
464 let mut cx = FunctionBuilderContext::new();
465 #[cfg(not(feature = "rayon"))]
466 let function_call_trampolines = module
467 .signatures
468 .values()
469 .collect::<Vec<_>>()
470 .into_iter()
471 .map(|sig| {
472 let trampoline = make_trampoline_function_call(
473 &self.config().callbacks,
474 &*isa,
475 target.triple().architecture,
476 &mut cx,
477 sig,
478 &module_hash,
479 )?;
480 if let Some(progress) = progress.as_ref() {
481 progress.notify_steps(WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE)?;
482 }
483 Ok(trampoline)
484 })
485 .collect::<Result<Vec<FunctionBody>, CompileError>>()?
486 .into_iter()
487 .collect();
488 #[cfg(feature = "rayon")]
489 let function_call_trampolines = module
490 .signatures
491 .values()
492 .collect::<Vec<_>>()
493 .par_iter()
494 .map_init(FunctionBuilderContext::new, |cx, sig| {
495 let trampoline = make_trampoline_function_call(
496 &self.config().callbacks,
497 &*isa,
498 target.triple().architecture,
499 cx,
500 sig,
501 &module_hash,
502 )?;
503 if let Some(progress) = progress.as_ref() {
504 progress.notify_steps(WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE)?;
505 }
506 Ok(trampoline)
507 })
508 .collect::<Result<Vec<FunctionBody>, CompileError>>()?
509 .into_iter()
510 .collect();
511
512 use wasmer_types::VMOffsets;
513 let offsets = VMOffsets::new_for_trampolines(frontend_config.pointer_bytes());
514 #[cfg(not(feature = "rayon"))]
516 let mut cx = FunctionBuilderContext::new();
517 #[cfg(not(feature = "rayon"))]
518 let dynamic_function_trampolines = module
519 .imported_function_types()
520 .collect::<Vec<_>>()
521 .into_iter()
522 .map(|func_type| {
523 let trampoline = make_trampoline_dynamic_function(
524 &self.config().callbacks,
525 &*isa,
526 target.triple().architecture,
527 &offsets,
528 &mut cx,
529 &func_type,
530 &module_hash,
531 )?;
532 if let Some(progress) = progress.as_ref() {
533 progress.notify_steps(WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE)?;
534 }
535 Ok(trampoline)
536 })
537 .collect::<Result<Vec<_>, CompileError>>()?
538 .into_iter()
539 .collect();
540 #[cfg(feature = "rayon")]
541 let dynamic_function_trampolines = module
542 .imported_function_types()
543 .collect::<Vec<_>>()
544 .par_iter()
545 .map_init(FunctionBuilderContext::new, |cx, func_type| {
546 let trampoline = make_trampoline_dynamic_function(
547 &self.config().callbacks,
548 &*isa,
549 target.triple().architecture,
550 &offsets,
551 cx,
552 func_type,
553 &module_hash,
554 )?;
555 if let Some(progress) = progress.as_ref() {
556 progress.notify_steps(WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE)?;
557 }
558 Ok(trampoline)
559 })
560 .collect::<Result<Vec<_>, CompileError>>()?
561 .into_iter()
562 .collect();
563
564 let got = wasmer_compiler::types::function::GOT::empty();
565
566 Ok(Compilation {
567 functions: functions.into_iter().collect(),
568 custom_sections,
569 function_call_trampolines,
570 dynamic_function_trampolines,
571 unwind_info,
572 got,
573 })
574 }
575}
576
577impl Compiler for CraneliftCompiler {
578 fn name(&self) -> &str {
579 "cranelift"
580 }
581
582 fn get_perfmap_enabled(&self) -> bool {
583 self.config.enable_perfmap
584 }
585
586 fn deterministic_id(&self) -> String {
587 String::from("cranelift")
588 }
589
590 fn get_middlewares(&self) -> &[Arc<dyn ModuleMiddleware>] {
592 &self.config.middlewares
593 }
594
595 fn compile_module(
598 &self,
599 target: &Target,
600 compile_info: &CompileModuleInfo,
601 module_translation_state: &ModuleTranslationState,
602 function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
603 progress_callback: Option<&CompilationProgressCallback>,
604 ) -> Result<Compilation, CompileError> {
605 self.compile_module_internal(
606 target,
607 compile_info,
608 module_translation_state,
609 function_body_inputs,
610 progress_callback,
611 )
612 }
613}
614
615fn mach_reloc_to_reloc(
616 module: &ModuleInfo,
617 func_index_map: &cranelift_entity::PrimaryMap<ir::UserExternalNameRef, ir::UserExternalName>,
618 reloc: &FinalizedMachReloc,
619) -> Relocation {
620 let FinalizedMachReloc {
621 offset,
622 kind,
623 addend,
624 target,
625 } = &reloc;
626 let name = match target {
627 FinalizedRelocTarget::ExternalName(external_name) => external_name,
628 FinalizedRelocTarget::Func(_) => {
629 unimplemented!("relocations to offset in the same function are not yet supported")
630 }
631 };
632 let reloc_target: RelocationTarget = if let ExternalName::User(extname_ref) = name {
633 let func_index = func_index_map[*extname_ref].index;
634 RelocationTarget::LocalFunc(
636 module
637 .local_func_index(FunctionIndex::from_u32(func_index))
638 .expect("The provided function should be local"),
639 )
640 } else if let ExternalName::LibCall(libcall) = name {
641 RelocationTarget::LibCall(irlibcall_to_libcall(*libcall))
642 } else {
643 panic!("unrecognized external target")
644 };
645 Relocation {
646 kind: irreloc_to_relocationkind(*kind),
647 reloc_target,
648 offset: *offset,
649 addend: *addend,
650 }
651}
652
653fn mach_trap_to_trap(trap: &MachTrap) -> TrapInformation {
654 let &MachTrap { offset, code } = trap;
655 TrapInformation {
656 code_offset: offset,
657 trap_code: translate_ir_trapcode(code),
658 }
659}
660
661fn translate_ir_trapcode(trap: ir::TrapCode) -> TrapCode {
663 if trap == ir::TrapCode::STACK_OVERFLOW {
664 TrapCode::StackOverflow
665 } else if trap == ir::TrapCode::HEAP_OUT_OF_BOUNDS {
666 TrapCode::HeapAccessOutOfBounds
667 } else if trap == crate::TRAP_HEAP_MISALIGNED {
668 TrapCode::UnalignedAtomic
669 } else if trap == crate::TRAP_TABLE_OUT_OF_BOUNDS {
670 TrapCode::TableAccessOutOfBounds
671 } else if trap == crate::TRAP_INDIRECT_CALL_TO_NULL {
672 TrapCode::IndirectCallToNull
673 } else if trap == crate::TRAP_BAD_SIGNATURE {
674 TrapCode::BadSignature
675 } else if trap == ir::TrapCode::INTEGER_OVERFLOW {
676 TrapCode::IntegerOverflow
677 } else if trap == ir::TrapCode::INTEGER_DIVISION_BY_ZERO {
678 TrapCode::IntegerDivisionByZero
679 } else if trap == ir::TrapCode::BAD_CONVERSION_TO_INTEGER {
680 TrapCode::BadConversionToInteger
681 } else if trap == crate::TRAP_UNREACHABLE {
682 TrapCode::UnreachableCodeReached
683 } else if trap == crate::TRAP_INTERRUPT {
684 unimplemented!("Interrupts not supported")
685 } else if trap == crate::TRAP_NULL_REFERENCE || trap == crate::TRAP_NULL_I31_REF {
686 unimplemented!("Null reference not supported")
687 } else {
688 unimplemented!("Trap code {trap:?} not supported")
689 }
690}