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