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
50pub struct CraneliftCompiler {
53 config: Cranelift,
54}
55
56impl CraneliftCompiler {
57 pub fn new(config: Cranelift) -> Self {
59 Self { config }
60 }
61
62 pub fn config(&self) -> &Cranelift {
64 &self.config
65 }
66}
67
68impl Compiler for CraneliftCompiler {
69 fn name(&self) -> &str {
70 "cranelift"
71 }
72
73 fn get_middlewares(&self) -> &[Arc<dyn ModuleMiddleware>] {
75 &self.config.middlewares
76 }
77
78 fn compile_module(
81 &self,
82 target: &Target,
83 compile_info: &CompileModuleInfo,
84 module_translation_state: &ModuleTranslationState,
85 function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
86 ) -> Result<Compilation, CompileError> {
87 let isa = self
88 .config()
89 .isa(target)
90 .map_err(|error| CompileError::Codegen(error.to_string()))?;
91 let frontend_config = isa.frontend_config();
92 let memory_styles = &compile_info.memory_styles;
93 let table_styles = &compile_info.table_styles;
94 let module = &compile_info.module;
95 let signatures = module
96 .signatures
97 .iter()
98 .map(|(_sig_index, func_type)| signature_to_cranelift_ir(func_type, frontend_config))
99 .collect::<PrimaryMap<SignatureIndex, ir::Signature>>();
100
101 #[cfg(feature = "unwind")]
103 let dwarf_frametable = if function_body_inputs.is_empty() {
104 None
108 } else {
109 match target.triple().default_calling_convention() {
110 Ok(CallingConvention::SystemV) => {
111 match isa.create_systemv_cie() {
112 Some(cie) => {
113 let mut dwarf_frametable = FrameTable::default();
114 let cie_id = dwarf_frametable.add_cie(cie);
115 Some((dwarf_frametable, cie_id))
116 }
117 None => None,
119 }
120 }
121 _ => None,
122 }
123 };
124
125 let mut custom_sections = PrimaryMap::new();
126
127 #[cfg(not(feature = "rayon"))]
128 let mut func_translator = FuncTranslator::new();
129 #[cfg(not(feature = "rayon"))]
130 let (functions, fdes): (Vec<CompiledFunction>, Vec<_>) = function_body_inputs
131 .iter()
132 .collect::<Vec<(LocalFunctionIndex, &FunctionBodyData<'_>)>>()
133 .into_iter()
134 .map(|(i, input)| {
135 let func_index = module.func_index(i);
136 let mut context = Context::new();
137 let mut func_env = FuncEnvironment::new(
138 isa.frontend_config(),
139 module,
140 &signatures,
141 &memory_styles,
142 table_styles,
143 );
144 context.func.name = match get_function_name(func_index) {
145 ExternalName::User(nameref) => {
146 if context.func.params.user_named_funcs().is_valid(nameref) {
147 let name = &context.func.params.user_named_funcs()[nameref];
148 UserFuncName::User(name.clone())
149 } else {
150 UserFuncName::default()
151 }
152 }
153 ExternalName::TestCase(testcase) => UserFuncName::Testcase(testcase),
154 _ => UserFuncName::default(),
155 };
156 context.func.signature = signatures[module.functions[func_index]].clone();
157 let mut reader =
161 MiddlewareBinaryReader::new_with_offset(input.data, input.module_offset);
162 reader.set_middleware_chain(
163 self.config
164 .middlewares
165 .generate_function_middleware_chain(i),
166 );
167
168 func_translator.translate(
169 module_translation_state,
170 &mut reader,
171 &mut context.func,
172 &mut func_env,
173 i,
174 )?;
175
176 let mut code_buf: Vec<u8> = Vec::new();
177 context
178 .compile_and_emit(&*isa, &mut code_buf, &mut Default::default())
179 .map_err(|error| CompileError::Codegen(error.inner.to_string()))?;
180
181 let result = context.compiled_code().unwrap();
182 let func_relocs = result
183 .buffer
184 .relocs()
185 .into_iter()
186 .map(|r| mach_reloc_to_reloc(module, r))
187 .collect::<Vec<_>>();
188
189 let traps = result
190 .buffer
191 .traps()
192 .into_iter()
193 .map(mach_trap_to_trap)
194 .collect::<Vec<_>>();
195
196 let (unwind_info, fde) = match compiled_function_unwind_info(&*isa, &context)? {
197 #[cfg(feature = "unwind")]
198 CraneliftUnwindInfo::Fde(fde) => {
199 if dwarf_frametable.is_some() {
200 let fde = fde.to_fde(Address::Symbol {
201 symbol: WriterRelocate::FUNCTION_SYMBOL,
204 addend: i.index() as _,
207 });
208 (Some(CompiledFunctionUnwindInfo::Dwarf), Some(fde))
210 } else {
211 (None, None)
212 }
213 }
214 #[cfg(feature = "unwind")]
215 other => (other.maybe_into_to_windows_unwind(), None),
216
217 #[cfg(not(feature = "unwind"))]
220 other => (other.maybe_into_to_windows_unwind(), None::<()>),
221 };
222
223 let range = reader.range();
224 let address_map = get_function_address_map(&context, range, code_buf.len());
225
226 Ok((
227 CompiledFunction {
228 body: FunctionBody {
229 body: code_buf,
230 unwind_info,
231 },
232 relocations: func_relocs,
233 frame_info: CompiledFunctionFrameInfo { address_map, traps },
234 },
235 fde,
236 ))
237 })
238 .collect::<Result<Vec<_>, CompileError>>()?
239 .into_iter()
240 .unzip();
241 #[cfg(feature = "rayon")]
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 context
291 .compile_and_emit(&*isa, &mut code_buf, &mut Default::default())
292 .map_err(|error| CompileError::Codegen(format!("{error:#?}")))?;
293
294 let result = context.compiled_code().unwrap();
295 let func_relocs = result
296 .buffer
297 .relocs()
298 .iter()
299 .map(|r| mach_reloc_to_reloc(module, r))
300 .collect::<Vec<_>>();
301
302 let traps = result
303 .buffer
304 .traps()
305 .iter()
306 .map(mach_trap_to_trap)
307 .collect::<Vec<_>>();
308
309 let (unwind_info, fde) = match compiled_function_unwind_info(&*isa, &context)? {
310 #[cfg(feature = "unwind")]
311 CraneliftUnwindInfo::Fde(fde) => {
312 if dwarf_frametable.is_some() {
313 let fde = fde.to_fde(Address::Symbol {
314 symbol: WriterRelocate::FUNCTION_SYMBOL,
317 addend: i.index() as _,
320 });
321 (Some(CompiledFunctionUnwindInfo::Dwarf), Some(fde))
323 } else {
324 (None, None)
325 }
326 }
327 #[cfg(feature = "unwind")]
328 other => (other.maybe_into_to_windows_unwind(), None),
329
330 #[cfg(not(feature = "unwind"))]
333 other => (other.maybe_into_to_windows_unwind(), None::<()>),
334 };
335
336 let range = reader.range();
337 let address_map = get_function_address_map(&context, range, code_buf.len());
338
339 Ok((
340 CompiledFunction {
341 body: FunctionBody {
342 body: code_buf,
343 unwind_info,
344 },
345 relocations: func_relocs,
346 frame_info: CompiledFunctionFrameInfo { address_map, traps },
347 },
348 fde,
349 ))
350 })
351 .collect::<Result<Vec<_>, CompileError>>()?
352 .into_iter()
353 .unzip();
354
355 let mut unwind_info = UnwindInfo::default();
356
357 #[cfg(feature = "unwind")]
358 if let Some((mut dwarf_frametable, cie_id)) = dwarf_frametable {
359 for fde in fdes.into_iter().flatten() {
360 dwarf_frametable.add_fde(cie_id, fde);
361 }
362 let mut eh_frame = EhFrame(WriterRelocate::new(target.triple().endianness().ok()));
363 dwarf_frametable.write_eh_frame(&mut eh_frame).unwrap();
364
365 let eh_frame_section = eh_frame.0.into_section();
366 custom_sections.push(eh_frame_section);
367 unwind_info.eh_frame = Some(SectionIndex::new(custom_sections.len() - 1));
368 };
369
370 #[cfg(not(feature = "rayon"))]
372 let mut cx = FunctionBuilderContext::new();
373 #[cfg(not(feature = "rayon"))]
374 let function_call_trampolines = module
375 .signatures
376 .values()
377 .collect::<Vec<_>>()
378 .into_iter()
379 .map(|sig| make_trampoline_function_call(&*isa, &mut cx, sig))
380 .collect::<Result<Vec<FunctionBody>, CompileError>>()?
381 .into_iter()
382 .collect::<PrimaryMap<SignatureIndex, FunctionBody>>();
383 #[cfg(feature = "rayon")]
384 let function_call_trampolines = module
385 .signatures
386 .values()
387 .collect::<Vec<_>>()
388 .par_iter()
389 .map_init(FunctionBuilderContext::new, |cx, sig| {
390 make_trampoline_function_call(&*isa, cx, sig)
391 })
392 .collect::<Result<Vec<FunctionBody>, CompileError>>()?
393 .into_iter()
394 .collect::<PrimaryMap<SignatureIndex, FunctionBody>>();
395
396 use wasmer_types::VMOffsets;
397 let offsets = VMOffsets::new_for_trampolines(frontend_config.pointer_bytes());
398 #[cfg(not(feature = "rayon"))]
400 let mut cx = FunctionBuilderContext::new();
401 #[cfg(not(feature = "rayon"))]
402 let dynamic_function_trampolines = module
403 .imported_function_types()
404 .collect::<Vec<_>>()
405 .into_iter()
406 .map(|func_type| make_trampoline_dynamic_function(&*isa, &offsets, &mut cx, &func_type))
407 .collect::<Result<Vec<_>, CompileError>>()?
408 .into_iter()
409 .collect::<PrimaryMap<FunctionIndex, FunctionBody>>();
410 #[cfg(feature = "rayon")]
411 let dynamic_function_trampolines = module
412 .imported_function_types()
413 .collect::<Vec<_>>()
414 .par_iter()
415 .map_init(FunctionBuilderContext::new, |cx, func_type| {
416 make_trampoline_dynamic_function(&*isa, &offsets, cx, func_type)
417 })
418 .collect::<Result<Vec<_>, CompileError>>()?
419 .into_iter()
420 .collect::<PrimaryMap<FunctionIndex, FunctionBody>>();
421
422 let got = wasmer_compiler::types::function::GOT::empty();
423
424 Ok(Compilation {
425 functions: functions.into_iter().collect(),
426 custom_sections,
427 function_call_trampolines,
428 dynamic_function_trampolines,
429 unwind_info,
430 got,
431 })
432 }
433}
434
435fn mach_reloc_to_reloc(module: &ModuleInfo, reloc: &FinalizedMachReloc) -> Relocation {
436 let FinalizedMachReloc {
437 offset,
438 kind,
439 addend,
440 target,
441 } = &reloc;
442 let name = match target {
443 FinalizedRelocTarget::ExternalName(external_name) => external_name,
444 FinalizedRelocTarget::Func(_) => {
445 unimplemented!("relocations to offset in the same function are not yet supported")
446 }
447 };
448 let reloc_target: RelocationTarget = if let ExternalName::User(extname_ref) = name {
449 RelocationTarget::LocalFunc(
451 module
452 .local_func_index(FunctionIndex::from_u32(extname_ref.as_u32()))
453 .expect("The provided function should be local"),
454 )
455 } else if let ExternalName::LibCall(libcall) = name {
456 RelocationTarget::LibCall(irlibcall_to_libcall(*libcall))
457 } else {
458 panic!("unrecognized external target")
459 };
460 Relocation {
461 kind: irreloc_to_relocationkind(*kind),
462 reloc_target,
463 offset: *offset,
464 addend: *addend,
465 }
466}
467
468fn mach_trap_to_trap(trap: &MachTrap) -> TrapInformation {
469 let &MachTrap { offset, code } = trap;
470 TrapInformation {
471 code_offset: offset,
472 trap_code: translate_ir_trapcode(code),
473 }
474}
475
476fn translate_ir_trapcode(trap: ir::TrapCode) -> TrapCode {
478 match trap {
479 ir::TrapCode::StackOverflow => TrapCode::StackOverflow,
480 ir::TrapCode::HeapOutOfBounds => TrapCode::HeapAccessOutOfBounds,
481 ir::TrapCode::HeapMisaligned => TrapCode::UnalignedAtomic,
482 ir::TrapCode::TableOutOfBounds => TrapCode::TableAccessOutOfBounds,
483 ir::TrapCode::IndirectCallToNull => TrapCode::IndirectCallToNull,
484 ir::TrapCode::BadSignature => TrapCode::BadSignature,
485 ir::TrapCode::IntegerOverflow => TrapCode::IntegerOverflow,
486 ir::TrapCode::IntegerDivisionByZero => TrapCode::IntegerDivisionByZero,
487 ir::TrapCode::BadConversionToInteger => TrapCode::BadConversionToInteger,
488 ir::TrapCode::UnreachableCodeReached => TrapCode::UnreachableCodeReached,
489 ir::TrapCode::Interrupt => unimplemented!("Interrupts not supported"),
490 ir::TrapCode::NullReference | ir::TrapCode::NullI31Ref => {
491 unimplemented!("Null reference not supported")
492 }
493 ir::TrapCode::User(_user_code) => unimplemented!("User trap code not supported"),
494 }
497}