1#[cfg(feature = "unwind")]
4use crate::dwarf::WriterRelocate;
5
6#[cfg(feature = "unwind")]
7use crate::translator::CraneliftUnwindInfo;
8use crate::{
9 address_map::get_function_address_map,
10 config::Cranelift,
11 func_environ::{FuncEnvironment, get_function_name},
12 trampoline::{
13 FunctionBuilderContext, make_trampoline_dynamic_function, make_trampoline_function_call,
14 },
15 translator::{
16 FuncTranslator, compiled_function_unwind_info, irlibcall_to_libcall,
17 irreloc_to_relocationkind, signature_to_cranelift_ir,
18 },
19};
20use cranelift_codegen::{
21 Context, FinalizedMachReloc, FinalizedRelocTarget, MachTrap,
22 ir::{self, ExternalName, UserFuncName},
23};
24
25#[cfg(feature = "unwind")]
26use gimli::write::{Address, EhFrame, FrameTable, Writer};
27
28#[cfg(feature = "rayon")]
29use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
30use std::sync::Arc;
31
32#[cfg(feature = "unwind")]
33use wasmer_compiler::types::{section::SectionIndex, unwind::CompiledFunctionUnwindInfo};
34use wasmer_compiler::{
35 Compiler, FunctionBinaryReader, FunctionBodyData, MiddlewareBinaryReader, ModuleMiddleware,
36 ModuleMiddlewareChain, ModuleTranslationState,
37 types::{
38 function::{
39 Compilation, CompiledFunction, CompiledFunctionFrameInfo, FunctionBody, UnwindInfo,
40 },
41 module::CompileModuleInfo,
42 relocation::{Relocation, RelocationTarget},
43 },
44};
45#[cfg(feature = "unwind")]
46use wasmer_types::entity::EntityRef;
47use wasmer_types::entity::PrimaryMap;
48#[cfg(feature = "unwind")]
49use wasmer_types::target::CallingConvention;
50use wasmer_types::target::Target;
51use wasmer_types::{
52 CompileError, FunctionIndex, LocalFunctionIndex, ModuleInfo, SignatureIndex, TrapCode,
53 TrapInformation,
54};
55
56#[derive(Debug)]
59pub struct CraneliftCompiler {
60 config: Cranelift,
61}
62
63impl CraneliftCompiler {
64 pub fn new(config: Cranelift) -> Self {
66 Self { config }
67 }
68
69 pub fn config(&self) -> &Cranelift {
71 &self.config
72 }
73
74 fn compile_module_internal(
77 &self,
78 target: &Target,
79 compile_info: &CompileModuleInfo,
80 module_translation_state: &ModuleTranslationState,
81 function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
82 ) -> Result<Compilation, CompileError> {
83 let isa = self
84 .config()
85 .isa(target)
86 .map_err(|error| CompileError::Codegen(error.to_string()))?;
87 let frontend_config = isa.frontend_config();
88 let memory_styles = &compile_info.memory_styles;
89 let table_styles = &compile_info.table_styles;
90 let module = &compile_info.module;
91 let signatures = module
92 .signatures
93 .iter()
94 .map(|(_sig_index, func_type)| signature_to_cranelift_ir(func_type, frontend_config))
95 .collect::<PrimaryMap<SignatureIndex, ir::Signature>>();
96
97 #[cfg(feature = "unwind")]
99 let dwarf_frametable = if function_body_inputs.is_empty() {
100 None
104 } else {
105 match target.triple().default_calling_convention() {
106 Ok(CallingConvention::SystemV) => {
107 match isa.create_systemv_cie() {
108 Some(cie) => {
109 let mut dwarf_frametable = FrameTable::default();
110 let cie_id = dwarf_frametable.add_cie(cie);
111 Some((dwarf_frametable, cie_id))
112 }
113 None => None,
115 }
116 }
117 _ => None,
118 }
119 };
120
121 #[cfg_attr(not(feature = "unwind"), allow(unused_mut))]
122 let mut custom_sections = PrimaryMap::new();
123
124 #[cfg(not(feature = "rayon"))]
125 let mut func_translator = FuncTranslator::new();
126 #[cfg(not(feature = "rayon"))]
127 #[cfg_attr(not(feature = "unwind"), allow(unused_variables))]
128 let (functions, fdes): (Vec<CompiledFunction>, Vec<_>) = function_body_inputs
129 .iter()
130 .collect::<Vec<(LocalFunctionIndex, &FunctionBodyData<'_>)>>()
131 .into_iter()
132 .map(|(i, input)| {
133 let func_index = module.func_index(i);
134 let mut context = Context::new();
135 let mut func_env = FuncEnvironment::new(
136 isa.frontend_config(),
137 module,
138 &signatures,
139 &memory_styles,
140 table_styles,
141 );
142 context.func.name = match get_function_name(func_index) {
143 ExternalName::User(nameref) => {
144 if context.func.params.user_named_funcs().is_valid(nameref) {
145 let name = &context.func.params.user_named_funcs()[nameref];
146 UserFuncName::User(name.clone())
147 } else {
148 UserFuncName::default()
149 }
150 }
151 ExternalName::TestCase(testcase) => UserFuncName::Testcase(testcase),
152 _ => UserFuncName::default(),
153 };
154 context.func.signature = signatures[module.functions[func_index]].clone();
155 let mut reader =
159 MiddlewareBinaryReader::new_with_offset(input.data, input.module_offset);
160 reader.set_middleware_chain(
161 self.config
162 .middlewares
163 .generate_function_middleware_chain(i),
164 );
165
166 func_translator.translate(
167 module_translation_state,
168 &mut reader,
169 &mut context.func,
170 &mut func_env,
171 i,
172 )?;
173
174 let mut code_buf: Vec<u8> = Vec::new();
175 let mut ctrl_plane = Default::default();
176 let result = context
177 .compile(&*isa, &mut ctrl_plane)
178 .map_err(|error| CompileError::Codegen(error.inner.to_string()))?;
179 code_buf.extend_from_slice(result.code_buffer());
180
181 let func_relocs = result
182 .buffer
183 .relocs()
184 .into_iter()
185 .map(|r| mach_reloc_to_reloc(module, r))
186 .collect::<Vec<_>>();
187
188 let traps = result
189 .buffer
190 .traps()
191 .into_iter()
192 .map(mach_trap_to_trap)
193 .collect::<Vec<_>>();
194
195 let (unwind_info, fde) = match compiled_function_unwind_info(&*isa, &context)? {
196 #[cfg(feature = "unwind")]
197 CraneliftUnwindInfo::Fde(fde) => {
198 if dwarf_frametable.is_some() {
199 let fde = fde.to_fde(Address::Symbol {
200 symbol: WriterRelocate::FUNCTION_SYMBOL,
203 addend: i.index() as _,
206 });
207 (Some(CompiledFunctionUnwindInfo::Dwarf), Some(fde))
209 } else {
210 (None, None)
211 }
212 }
213 #[cfg(feature = "unwind")]
214 other => (other.maybe_into_to_windows_unwind(), None),
215
216 #[cfg(not(feature = "unwind"))]
219 other => (other.maybe_into_to_windows_unwind(), None::<()>),
220 };
221
222 let range = reader.range();
223 let address_map = get_function_address_map(&context, range, code_buf.len());
224
225 Ok((
226 CompiledFunction {
227 body: FunctionBody {
228 body: code_buf,
229 unwind_info,
230 },
231 relocations: func_relocs,
232 frame_info: CompiledFunctionFrameInfo { address_map, traps },
233 },
234 fde,
235 ))
236 })
237 .collect::<Result<Vec<_>, CompileError>>()?
238 .into_iter()
239 .unzip();
240 #[cfg(feature = "rayon")]
241 #[cfg_attr(not(feature = "unwind"), allow(unused_variables))]
242 let (functions, fdes): (Vec<CompiledFunction>, Vec<_>) = function_body_inputs
243 .iter()
244 .collect::<Vec<(LocalFunctionIndex, &FunctionBodyData<'_>)>>()
245 .par_iter()
246 .map_init(FuncTranslator::new, |func_translator, (i, input)| {
247 let func_index = module.func_index(*i);
248 let mut context = Context::new();
249 let mut func_env = FuncEnvironment::new(
250 isa.frontend_config(),
251 module,
252 &signatures,
253 memory_styles,
254 table_styles,
255 );
256 context.func.name = match get_function_name(func_index) {
257 ExternalName::User(nameref) => {
258 if context.func.params.user_named_funcs().is_valid(nameref) {
259 let name = &context.func.params.user_named_funcs()[nameref];
260 UserFuncName::User(name.clone())
261 } else {
262 UserFuncName::default()
263 }
264 }
265 ExternalName::TestCase(testcase) => UserFuncName::Testcase(testcase),
266 _ => UserFuncName::default(),
267 };
268 context.func.signature = signatures[module.functions[func_index]].clone();
269 let mut reader =
274 MiddlewareBinaryReader::new_with_offset(input.data, input.module_offset);
275 reader.set_middleware_chain(
276 self.config
277 .middlewares
278 .generate_function_middleware_chain(*i),
279 );
280
281 func_translator.translate(
282 module_translation_state,
283 &mut reader,
284 &mut context.func,
285 &mut func_env,
286 *i,
287 )?;
288
289 let mut code_buf: Vec<u8> = Vec::new();
290 let mut ctrl_plane = Default::default();
291 let result = context
292 .compile(&*isa, &mut ctrl_plane)
293 .map_err(|error| CompileError::Codegen(format!("{error:#?}")))?;
294 code_buf.extend_from_slice(result.code_buffer());
295
296 let func_relocs = result
297 .buffer
298 .relocs()
299 .iter()
300 .map(|r| mach_reloc_to_reloc(module, r))
301 .collect::<Vec<_>>();
302
303 let traps = result
304 .buffer
305 .traps()
306 .iter()
307 .map(mach_trap_to_trap)
308 .collect::<Vec<_>>();
309
310 let (unwind_info, fde) = match compiled_function_unwind_info(&*isa, &context)? {
311 #[cfg(feature = "unwind")]
312 CraneliftUnwindInfo::Fde(fde) => {
313 if dwarf_frametable.is_some() {
314 let fde = fde.to_fde(Address::Symbol {
315 symbol: WriterRelocate::FUNCTION_SYMBOL,
318 addend: i.index() as _,
321 });
322 (Some(CompiledFunctionUnwindInfo::Dwarf), Some(fde))
324 } else {
325 (None, None)
326 }
327 }
328 #[cfg(feature = "unwind")]
329 other => (other.maybe_into_to_windows_unwind(), None),
330
331 #[cfg(not(feature = "unwind"))]
334 other => (other.maybe_into_to_windows_unwind(), None::<()>),
335 };
336
337 let range = reader.range();
338 let address_map = get_function_address_map(&context, range, code_buf.len());
339
340 Ok((
341 CompiledFunction {
342 body: FunctionBody {
343 body: code_buf,
344 unwind_info,
345 },
346 relocations: func_relocs,
347 frame_info: CompiledFunctionFrameInfo { address_map, traps },
348 },
349 fde,
350 ))
351 })
352 .collect::<Result<Vec<_>, CompileError>>()?
353 .into_iter()
354 .unzip();
355
356 #[cfg_attr(not(feature = "unwind"), allow(unused_mut))]
357 let mut unwind_info = UnwindInfo::default();
358
359 #[cfg(feature = "unwind")]
360 if let Some((mut dwarf_frametable, cie_id)) = dwarf_frametable {
361 for fde in fdes.into_iter().flatten() {
362 dwarf_frametable.add_fde(cie_id, fde);
363 }
364 let mut eh_frame = EhFrame(WriterRelocate::new(target.triple().endianness().ok()));
365 dwarf_frametable.write_eh_frame(&mut eh_frame).unwrap();
366 eh_frame.write(&[0, 0, 0, 0]).unwrap(); let eh_frame_section = eh_frame.0.into_section();
369 custom_sections.push(eh_frame_section);
370 unwind_info.eh_frame = Some(SectionIndex::new(custom_sections.len() - 1));
371 };
372
373 #[cfg(not(feature = "rayon"))]
375 let mut cx = FunctionBuilderContext::new();
376 #[cfg(not(feature = "rayon"))]
377 let function_call_trampolines = module
378 .signatures
379 .values()
380 .collect::<Vec<_>>()
381 .into_iter()
382 .map(|sig| make_trampoline_function_call(&*isa, &mut cx, sig))
383 .collect::<Result<Vec<FunctionBody>, CompileError>>()?
384 .into_iter()
385 .collect::<PrimaryMap<SignatureIndex, FunctionBody>>();
386 #[cfg(feature = "rayon")]
387 let function_call_trampolines = module
388 .signatures
389 .values()
390 .collect::<Vec<_>>()
391 .par_iter()
392 .map_init(FunctionBuilderContext::new, |cx, sig| {
393 make_trampoline_function_call(&*isa, cx, sig)
394 })
395 .collect::<Result<Vec<FunctionBody>, CompileError>>()?
396 .into_iter()
397 .collect::<PrimaryMap<SignatureIndex, FunctionBody>>();
398
399 use wasmer_types::VMOffsets;
400 let offsets = VMOffsets::new_for_trampolines(frontend_config.pointer_bytes());
401 #[cfg(not(feature = "rayon"))]
403 let mut cx = FunctionBuilderContext::new();
404 #[cfg(not(feature = "rayon"))]
405 let dynamic_function_trampolines = module
406 .imported_function_types()
407 .collect::<Vec<_>>()
408 .into_iter()
409 .map(|func_type| make_trampoline_dynamic_function(&*isa, &offsets, &mut cx, &func_type))
410 .collect::<Result<Vec<_>, CompileError>>()?
411 .into_iter()
412 .collect::<PrimaryMap<FunctionIndex, FunctionBody>>();
413 #[cfg(feature = "rayon")]
414 let dynamic_function_trampolines = module
415 .imported_function_types()
416 .collect::<Vec<_>>()
417 .par_iter()
418 .map_init(FunctionBuilderContext::new, |cx, func_type| {
419 make_trampoline_dynamic_function(&*isa, &offsets, cx, func_type)
420 })
421 .collect::<Result<Vec<_>, CompileError>>()?
422 .into_iter()
423 .collect::<PrimaryMap<FunctionIndex, FunctionBody>>();
424
425 let got = wasmer_compiler::types::function::GOT::empty();
426
427 Ok(Compilation {
428 functions: functions.into_iter().collect(),
429 custom_sections,
430 function_call_trampolines,
431 dynamic_function_trampolines,
432 unwind_info,
433 got,
434 })
435 }
436}
437
438impl Compiler for CraneliftCompiler {
439 fn name(&self) -> &str {
440 "cranelift"
441 }
442
443 fn get_perfmap_enabled(&self) -> bool {
444 self.config.enable_perfmap
445 }
446
447 fn deterministic_id(&self) -> String {
448 String::from("cranelift")
449 }
450
451 fn get_middlewares(&self) -> &[Arc<dyn ModuleMiddleware>] {
453 &self.config.middlewares
454 }
455
456 fn compile_module(
459 &self,
460 target: &Target,
461 compile_info: &CompileModuleInfo,
462 module_translation_state: &ModuleTranslationState,
463 function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
464 ) -> Result<Compilation, CompileError> {
465 #[cfg(feature = "rayon")]
466 {
467 let num_threads = self.config.num_threads.get();
468 let pool = rayon::ThreadPoolBuilder::new()
469 .num_threads(num_threads)
470 .build()
471 .unwrap();
472
473 pool.install(|| {
474 self.compile_module_internal(
475 target,
476 compile_info,
477 module_translation_state,
478 function_body_inputs,
479 )
480 })
481 }
482
483 #[cfg(not(feature = "rayon"))]
484 {
485 self.compile_module_internal(
486 target,
487 compile_info,
488 module_translation_state,
489 function_body_inputs,
490 )
491 }
492 }
493}
494
495fn mach_reloc_to_reloc(module: &ModuleInfo, reloc: &FinalizedMachReloc) -> Relocation {
496 let FinalizedMachReloc {
497 offset,
498 kind,
499 addend,
500 target,
501 } = &reloc;
502 let name = match target {
503 FinalizedRelocTarget::ExternalName(external_name) => external_name,
504 FinalizedRelocTarget::Func(_) => {
505 unimplemented!("relocations to offset in the same function are not yet supported")
506 }
507 };
508 let reloc_target: RelocationTarget = if let ExternalName::User(extname_ref) = name {
509 RelocationTarget::LocalFunc(
511 module
512 .local_func_index(FunctionIndex::from_u32(extname_ref.as_u32()))
513 .expect("The provided function should be local"),
514 )
515 } else if let ExternalName::LibCall(libcall) = name {
516 RelocationTarget::LibCall(irlibcall_to_libcall(*libcall))
517 } else {
518 panic!("unrecognized external target")
519 };
520 Relocation {
521 kind: irreloc_to_relocationkind(*kind),
522 reloc_target,
523 offset: *offset,
524 addend: *addend,
525 }
526}
527
528fn mach_trap_to_trap(trap: &MachTrap) -> TrapInformation {
529 let &MachTrap { offset, code } = trap;
530 TrapInformation {
531 code_offset: offset,
532 trap_code: translate_ir_trapcode(code),
533 }
534}
535
536fn translate_ir_trapcode(trap: ir::TrapCode) -> TrapCode {
538 if trap == ir::TrapCode::STACK_OVERFLOW {
539 TrapCode::StackOverflow
540 } else if trap == ir::TrapCode::HEAP_OUT_OF_BOUNDS {
541 TrapCode::HeapAccessOutOfBounds
542 } else if trap == crate::TRAP_HEAP_MISALIGNED {
543 TrapCode::UnalignedAtomic
544 } else if trap == crate::TRAP_TABLE_OUT_OF_BOUNDS {
545 TrapCode::TableAccessOutOfBounds
546 } else if trap == crate::TRAP_INDIRECT_CALL_TO_NULL {
547 TrapCode::IndirectCallToNull
548 } else if trap == crate::TRAP_BAD_SIGNATURE {
549 TrapCode::BadSignature
550 } else if trap == ir::TrapCode::INTEGER_OVERFLOW {
551 TrapCode::IntegerOverflow
552 } else if trap == ir::TrapCode::INTEGER_DIVISION_BY_ZERO {
553 TrapCode::IntegerDivisionByZero
554 } else if trap == ir::TrapCode::BAD_CONVERSION_TO_INTEGER {
555 TrapCode::BadConversionToInteger
556 } else if trap == crate::TRAP_UNREACHABLE {
557 TrapCode::UnreachableCodeReached
558 } else if trap == crate::TRAP_INTERRUPT {
559 unimplemented!("Interrupts not supported")
560 } else if trap == crate::TRAP_NULL_REFERENCE || trap == crate::TRAP_NULL_I31_REF {
561 unimplemented!("Null reference not supported")
562 } else {
563 unimplemented!("Trap code {trap:?} not supported")
564 }
565}