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