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