1#![allow(unused_imports, dead_code)]
4
5use crate::codegen::FuncGen;
6use crate::config::{self, Singlepass};
7#[cfg(feature = "unwind")]
8use crate::dwarf::WriterRelocate;
9use crate::machine::Machine;
10use crate::machine::{
11 gen_import_call_trampoline, gen_std_dynamic_import_trampoline, gen_std_trampoline,
12};
13use crate::machine_arm64::MachineARM64;
14use crate::machine_riscv::MachineRiscv;
15use crate::machine_x64::MachineX86_64;
16#[cfg(feature = "unwind")]
17use crate::unwind::{UnwindFrame, create_systemv_cie};
18use enumset::EnumSet;
19#[cfg(feature = "unwind")]
20use gimli::write::{EhFrame, FrameTable, Writer};
21#[cfg(feature = "rayon")]
22use rayon::prelude::{IntoParallelIterator, ParallelIterator};
23use std::collections::HashMap;
24use std::sync::Arc;
25use wasmer_compiler::WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE;
26use wasmer_compiler::misc::{CompiledKind, save_assembly_to_file, types_to_signature};
27use wasmer_compiler::progress::ProgressContext;
28use wasmer_compiler::{
29 Compiler, CompilerConfig, FunctionBinaryReader, FunctionBodyData, MiddlewareBinaryReader,
30 ModuleMiddleware, ModuleMiddlewareChain, ModuleTranslationState,
31 types::{
32 function::{Compilation, CompiledFunction, FunctionBody, UnwindInfo},
33 module::CompileModuleInfo,
34 section::SectionIndex,
35 },
36};
37use wasmer_types::entity::{EntityRef, PrimaryMap};
38use wasmer_types::target::{Architecture, CallingConvention, CpuFeature, Target};
39use wasmer_types::{
40 CompilationProgressCallback, CompileError, FunctionIndex, FunctionType, LocalFunctionIndex,
41 MemoryIndex, ModuleInfo, TableIndex, TrapCode, TrapInformation, Type, VMOffsets,
42};
43
44#[derive(Debug)]
47pub struct SinglepassCompiler {
48 config: Singlepass,
49}
50
51impl SinglepassCompiler {
52 pub fn new(config: Singlepass) -> Self {
54 Self { config }
55 }
56
57 fn config(&self) -> &Singlepass {
59 &self.config
60 }
61
62 fn compile_module_internal(
63 &self,
64 target: &Target,
65 compile_info: &CompileModuleInfo,
66 function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
67 progress_callback: Option<&CompilationProgressCallback>,
68 ) -> Result<Compilation, CompileError> {
69 let arch = target.triple().architecture;
70 match arch {
71 Architecture::X86_64 => {}
72 Architecture::Aarch64(_) => {}
73 Architecture::Riscv64(_) => {}
74 _ => {
75 return Err(CompileError::UnsupportedTarget(
76 target.triple().architecture.to_string(),
77 ));
78 }
79 };
80
81 let calling_convention = match target.triple().default_calling_convention() {
82 Ok(CallingConvention::WindowsFastcall) => CallingConvention::WindowsFastcall,
83 Ok(CallingConvention::SystemV) => CallingConvention::SystemV,
84 Ok(CallingConvention::AppleAarch64) => CallingConvention::AppleAarch64,
85 _ => match target.triple().architecture {
86 Architecture::Riscv64(_) => CallingConvention::SystemV,
87 _ => {
88 return Err(CompileError::UnsupportedTarget(
89 "Unsupported Calling convention for Singlepass compiler".to_string(),
90 ));
91 }
92 },
93 };
94
95 let module = &compile_info.module;
96 let total_function_call_trampolines = module.signatures.len() as u64;
97 let total_dynamic_trampolines = module.num_imported_functions as u64;
98 let total_steps = WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE
99 * ((total_dynamic_trampolines + total_function_call_trampolines) as u64)
100 + function_body_inputs
101 .iter()
102 .map(|(_, body)| body.data.len() as u64)
103 .sum::<u64>();
104 let progress = progress_callback
105 .cloned()
106 .map(|cb| ProgressContext::new(cb, total_steps, "singlepass::functions"));
107
108 #[cfg(feature = "unwind")]
110 let dwarf_frametable = if function_body_inputs.is_empty() {
111 None
115 } else {
116 match target.triple().default_calling_convention() {
117 Ok(CallingConvention::SystemV) => {
118 match create_systemv_cie(target.triple().architecture) {
119 Some(cie) => {
120 let mut dwarf_frametable = FrameTable::default();
121 let cie_id = dwarf_frametable.add_cie(cie);
122 Some((dwarf_frametable, cie_id))
123 }
124 None => None,
125 }
126 }
127 _ => None,
128 }
129 };
130
131 let memory_styles = &compile_info.memory_styles;
132 let table_styles = &compile_info.table_styles;
133 let vmoffsets = VMOffsets::new(8, &compile_info.module);
134 let module = &compile_info.module;
135 #[cfg_attr(not(feature = "unwind"), allow(unused_mut))]
136 let mut custom_sections: PrimaryMap<SectionIndex, _> = (0..module.num_imported_functions)
137 .map(FunctionIndex::new)
138 .collect::<Vec<_>>()
139 .into_par_iter_if_rayon()
140 .map(|i| {
141 gen_import_call_trampoline(
142 &vmoffsets,
143 i,
144 &module.signatures[module.functions[i]],
145 target,
146 calling_convention,
147 )
148 })
149 .collect::<Result<Vec<_>, _>>()?
150 .into_iter()
151 .collect();
152 #[cfg_attr(not(feature = "unwind"), allow(unused_variables))]
153 let (functions, fdes): (Vec<CompiledFunction>, Vec<_>) = function_body_inputs
154 .iter()
155 .collect::<Vec<(LocalFunctionIndex, &FunctionBodyData<'_>)>>()
156 .into_par_iter_if_rayon()
157 .map(|(i, input)| {
158 let middleware_chain = self
159 .config
160 .middlewares
161 .generate_function_middleware_chain(i);
162 let mut reader =
163 MiddlewareBinaryReader::new_with_offset(input.data, input.module_offset);
164 reader.set_middleware_chain(middleware_chain);
165
166 let mut locals = vec![];
168 let num_locals = reader.read_local_count()?;
169 for _ in 0..num_locals {
170 let (count, ty) = reader.read_local_decl()?;
171 for _ in 0..count {
172 locals.push(ty);
173 }
174 }
175
176 let res = match arch {
177 Architecture::X86_64 => {
178 let machine = MachineX86_64::new(Some(target.clone()))?;
179 let mut generator = FuncGen::new(
180 module,
181 &self.config,
182 &vmoffsets,
183 memory_styles,
184 table_styles,
185 i,
186 &locals,
187 machine,
188 calling_convention,
189 )?;
190 while generator.has_control_frames() {
191 generator.set_srcloc(reader.original_position() as u32);
192 let op = reader.read_operator()?;
193 generator.feed_operator(op)?;
194 }
195
196 generator.finalize(input, arch)
197 }
198 Architecture::Aarch64(_) => {
199 let machine = MachineARM64::new(Some(target.clone()));
200 let mut generator = FuncGen::new(
201 module,
202 &self.config,
203 &vmoffsets,
204 memory_styles,
205 table_styles,
206 i,
207 &locals,
208 machine,
209 calling_convention,
210 )?;
211 while generator.has_control_frames() {
212 generator.set_srcloc(reader.original_position() as u32);
213 let op = reader.read_operator()?;
214 generator.feed_operator(op)?;
215 }
216
217 generator.finalize(input, arch)
218 }
219 Architecture::Riscv64(_) => {
220 let machine = MachineRiscv::new(
221 Some(target.clone()),
222 self.config.allow_experimental_unaligned_memory_accesses,
223 )?;
224 let mut generator = FuncGen::new(
225 module,
226 &self.config,
227 &vmoffsets,
228 memory_styles,
229 table_styles,
230 i,
231 &locals,
232 machine,
233 calling_convention,
234 )?;
235 while generator.has_control_frames() {
236 generator.set_srcloc(reader.original_position() as u32);
237 let op = reader.read_operator()?;
238 generator.feed_operator(op)?;
239 }
240
241 generator.finalize(input, arch)
242 }
243 _ => unimplemented!(),
244 }?;
245
246 if let Some(progress) = progress.as_ref() {
247 progress.notify_steps(input.data.len() as u64)?;
248 }
249
250 Ok(res)
251 })
252 .collect::<Result<Vec<_>, CompileError>>()?
253 .into_iter()
254 .unzip();
255
256 let module_hash = module.hash_string();
257 let function_call_trampolines = module
258 .signatures
259 .values()
260 .collect::<Vec<_>>()
261 .into_par_iter_if_rayon()
262 .map(|func_type| -> Result<FunctionBody, CompileError> {
263 let body = gen_std_trampoline(func_type, target, calling_convention)?;
264 if let Some(callbacks) = self.config.callbacks.as_ref() {
265 callbacks.obj_memory_buffer(
266 &CompiledKind::FunctionCallTrampoline(func_type.clone()),
267 &module_hash,
268 &body.body,
269 );
270 callbacks.asm_memory_buffer(
271 &CompiledKind::FunctionCallTrampoline(func_type.clone()),
272 &module_hash,
273 arch,
274 &body.body,
275 HashMap::new(),
276 )?;
277 }
278 if let Some(progress) = progress.as_ref() {
279 progress.notify_steps(WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE)?;
280 }
281
282 Ok(body)
283 })
284 .collect::<Result<Vec<_>, _>>()?
285 .into_iter()
286 .collect::<PrimaryMap<_, _>>();
287
288 let dynamic_function_trampolines = module
289 .imported_function_types()
290 .collect::<Vec<_>>()
291 .into_par_iter_if_rayon()
292 .map(|func_type| -> Result<FunctionBody, CompileError> {
293 let body = gen_std_dynamic_import_trampoline(
294 &vmoffsets,
295 &func_type,
296 target,
297 calling_convention,
298 )?;
299 if let Some(callbacks) = self.config.callbacks.as_ref() {
300 callbacks.obj_memory_buffer(
301 &CompiledKind::DynamicFunctionTrampoline(func_type.clone()),
302 &module_hash,
303 &body.body,
304 );
305 callbacks.asm_memory_buffer(
306 &CompiledKind::DynamicFunctionTrampoline(func_type.clone()),
307 &module_hash,
308 arch,
309 &body.body,
310 HashMap::new(),
311 )?;
312 }
313 if let Some(progress) = progress.as_ref() {
314 progress.notify_steps(WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE)?;
315 }
316 Ok(body)
317 })
318 .collect::<Result<Vec<_>, _>>()?
319 .into_iter()
320 .collect::<PrimaryMap<FunctionIndex, FunctionBody>>();
321
322 #[allow(unused_mut)]
323 let mut unwind_info = UnwindInfo::default();
324
325 #[cfg(feature = "unwind")]
326 if let Some((mut dwarf_frametable, cie_id)) = dwarf_frametable {
327 for fde in fdes.into_iter().flatten() {
328 match fde {
329 UnwindFrame::SystemV(fde) => dwarf_frametable.add_fde(cie_id, fde),
330 }
331 }
332 let mut eh_frame = EhFrame(WriterRelocate::new(target.triple().endianness().ok()));
333 dwarf_frametable.write_eh_frame(&mut eh_frame).unwrap();
334 eh_frame.write(&[0, 0, 0, 0]).unwrap(); let eh_frame_section = eh_frame.0.into_section();
337 custom_sections.push(eh_frame_section);
338 unwind_info.eh_frame = Some(SectionIndex::new(custom_sections.len() - 1))
339 };
340
341 let got = wasmer_compiler::types::function::GOT::empty();
342
343 Ok(Compilation {
344 functions: functions.into_iter().collect(),
345 custom_sections,
346 function_call_trampolines,
347 dynamic_function_trampolines,
348 unwind_info,
349 got,
350 })
351 }
352}
353
354impl Compiler for SinglepassCompiler {
355 fn name(&self) -> &str {
356 "singlepass"
357 }
358
359 fn deterministic_id(&self) -> String {
360 String::from("singlepass")
361 }
362
363 fn get_middlewares(&self) -> &[Arc<dyn ModuleMiddleware>] {
365 &self.config.middlewares
366 }
367
368 fn compile_module(
371 &self,
372 target: &Target,
373 compile_info: &CompileModuleInfo,
374 _module_translation: &ModuleTranslationState,
375 function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
376 progress_callback: Option<&CompilationProgressCallback>,
377 ) -> Result<Compilation, CompileError> {
378 #[cfg(feature = "rayon")]
379 {
380 let num_threads = self.config.num_threads.get();
381 let pool = rayon::ThreadPoolBuilder::new()
382 .num_threads(num_threads)
383 .build()
384 .map_err(|e| {
385 CompileError::Codegen(format!("failed to build rayon thread pool: {e}"))
386 })?;
387
388 pool.install(|| {
389 self.compile_module_internal(
390 target,
391 compile_info,
392 function_body_inputs,
393 progress_callback,
394 )
395 })
396 }
397
398 #[cfg(not(feature = "rayon"))]
399 {
400 self.compile_module_internal(
401 target,
402 compile_info,
403 function_body_inputs,
404 progress_callback,
405 )
406 }
407 }
408
409 fn get_cpu_features_used(&self, cpu_features: &EnumSet<CpuFeature>) -> EnumSet<CpuFeature> {
410 let used = CpuFeature::AVX | CpuFeature::SSE42 | CpuFeature::LZCNT | CpuFeature::BMI1;
411 cpu_features.intersection(used)
412 }
413}
414
415trait IntoParIterIfRayon {
416 type Output;
417 fn into_par_iter_if_rayon(self) -> Self::Output;
418}
419
420impl<T: Send> IntoParIterIfRayon for Vec<T> {
421 #[cfg(not(feature = "rayon"))]
422 type Output = std::vec::IntoIter<T>;
423 #[cfg(feature = "rayon")]
424 type Output = rayon::vec::IntoIter<T>;
425
426 fn into_par_iter_if_rayon(self) -> Self::Output {
427 #[cfg(not(feature = "rayon"))]
428 return self.into_iter();
429 #[cfg(feature = "rayon")]
430 return self.into_par_iter();
431 }
432}
433
434#[cfg(test)]
435mod tests {
436 use super::*;
437 use std::str::FromStr;
438 use target_lexicon::triple;
439 use wasmer_compiler::Features;
440 use wasmer_types::{
441 MemoryStyle, TableStyle,
442 target::{CpuFeature, Triple},
443 };
444
445 fn dummy_compilation_ingredients<'a>() -> (
446 CompileModuleInfo,
447 ModuleTranslationState,
448 PrimaryMap<LocalFunctionIndex, FunctionBodyData<'a>>,
449 ) {
450 let compile_info = CompileModuleInfo {
451 features: Features::new(),
452 module: Arc::new(ModuleInfo::new()),
453 memory_styles: PrimaryMap::<MemoryIndex, MemoryStyle>::new(),
454 table_styles: PrimaryMap::<TableIndex, TableStyle>::new(),
455 };
456 let module_translation = ModuleTranslationState::new();
457 let function_body_inputs = PrimaryMap::<LocalFunctionIndex, FunctionBodyData<'_>>::new();
458 (compile_info, module_translation, function_body_inputs)
459 }
460
461 #[test]
462 fn errors_for_unsupported_targets() {
463 let compiler = SinglepassCompiler::new(Singlepass::default());
464
465 let linux32 = Target::new(triple!("i686-unknown-linux-gnu"), CpuFeature::for_host());
467 let (info, translation, inputs) = dummy_compilation_ingredients();
468 let result = compiler.compile_module(&linux32, &info, &translation, inputs, None);
469 match result.unwrap_err() {
470 CompileError::UnsupportedTarget(name) => assert_eq!(name, "i686"),
471 error => panic!("Unexpected error: {error:?}"),
472 };
473
474 let win32 = Target::new(triple!("i686-pc-windows-gnu"), CpuFeature::for_host());
476 let (info, translation, inputs) = dummy_compilation_ingredients();
477 let result = compiler.compile_module(&win32, &info, &translation, inputs, None);
478 match result.unwrap_err() {
479 CompileError::UnsupportedTarget(name) => assert_eq!(name, "i686"), error => panic!("Unexpected error: {error:?}"),
481 };
482 }
483
484 #[test]
485 fn errors_for_unsupported_cpufeatures() {
486 let compiler = SinglepassCompiler::new(Singlepass::default());
487 let mut features =
488 CpuFeature::AVX | CpuFeature::SSE42 | CpuFeature::LZCNT | CpuFeature::BMI1;
489 assert!(
491 compiler.get_cpu_features_used(&features).is_subset(
492 CpuFeature::AVX | CpuFeature::SSE42 | CpuFeature::LZCNT | CpuFeature::BMI1
493 )
494 );
495 assert!(
497 !compiler
498 .get_cpu_features_used(&features)
499 .is_subset(CpuFeature::SSE42 | CpuFeature::LZCNT | CpuFeature::BMI1)
500 );
501 features.insert_all(CpuFeature::AVX512DQ | CpuFeature::AVX512F);
503 assert!(
504 compiler.get_cpu_features_used(&features).is_subset(
505 CpuFeature::AVX | CpuFeature::SSE42 | CpuFeature::LZCNT | CpuFeature::BMI1
506 )
507 );
508 }
509}