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(Some(target.clone()))?;
221 let mut generator = FuncGen::new(
222 module,
223 &self.config,
224 &vmoffsets,
225 memory_styles,
226 table_styles,
227 i,
228 &locals,
229 machine,
230 calling_convention,
231 )?;
232 while generator.has_control_frames() {
233 generator.set_srcloc(reader.original_position() as u32);
234 let op = reader.read_operator()?;
235 generator.feed_operator(op)?;
236 }
237
238 generator.finalize(input, arch)
239 }
240 _ => unimplemented!(),
241 }?;
242
243 if let Some(progress) = progress.as_ref() {
244 progress.notify_steps(input.data.len() as u64)?;
245 }
246
247 Ok(res)
248 })
249 .collect::<Result<Vec<_>, CompileError>>()?
250 .into_iter()
251 .unzip();
252
253 let module_hash = module.hash_string();
254 let function_call_trampolines = module
255 .signatures
256 .values()
257 .collect::<Vec<_>>()
258 .into_par_iter_if_rayon()
259 .map(|func_type| -> Result<FunctionBody, CompileError> {
260 let body = gen_std_trampoline(func_type, target, calling_convention)?;
261 if let Some(callbacks) = self.config.callbacks.as_ref() {
262 callbacks.obj_memory_buffer(
263 &CompiledKind::FunctionCallTrampoline(func_type.clone()),
264 &module_hash,
265 &body.body,
266 );
267 callbacks.asm_memory_buffer(
268 &CompiledKind::FunctionCallTrampoline(func_type.clone()),
269 &module_hash,
270 arch,
271 &body.body,
272 HashMap::new(),
273 )?;
274 }
275 if let Some(progress) = progress.as_ref() {
276 progress.notify_steps(WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE)?;
277 }
278
279 Ok(body)
280 })
281 .collect::<Result<Vec<_>, _>>()?
282 .into_iter()
283 .collect::<PrimaryMap<_, _>>();
284
285 let dynamic_function_trampolines = module
286 .imported_function_types()
287 .collect::<Vec<_>>()
288 .into_par_iter_if_rayon()
289 .map(|func_type| -> Result<FunctionBody, CompileError> {
290 let body = gen_std_dynamic_import_trampoline(
291 &vmoffsets,
292 &func_type,
293 target,
294 calling_convention,
295 )?;
296 if let Some(callbacks) = self.config.callbacks.as_ref() {
297 callbacks.obj_memory_buffer(
298 &CompiledKind::DynamicFunctionTrampoline(func_type.clone()),
299 &module_hash,
300 &body.body,
301 );
302 callbacks.asm_memory_buffer(
303 &CompiledKind::DynamicFunctionTrampoline(func_type.clone()),
304 &module_hash,
305 arch,
306 &body.body,
307 HashMap::new(),
308 )?;
309 }
310 if let Some(progress) = progress.as_ref() {
311 progress.notify_steps(WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE)?;
312 }
313 Ok(body)
314 })
315 .collect::<Result<Vec<_>, _>>()?
316 .into_iter()
317 .collect::<PrimaryMap<FunctionIndex, FunctionBody>>();
318
319 #[allow(unused_mut)]
320 let mut unwind_info = UnwindInfo::default();
321
322 #[cfg(feature = "unwind")]
323 if let Some((mut dwarf_frametable, cie_id)) = dwarf_frametable {
324 for fde in fdes.into_iter().flatten() {
325 match fde {
326 UnwindFrame::SystemV(fde) => dwarf_frametable.add_fde(cie_id, fde),
327 }
328 }
329 let mut eh_frame = EhFrame(WriterRelocate::new(target.triple().endianness().ok()));
330 dwarf_frametable.write_eh_frame(&mut eh_frame).unwrap();
331 eh_frame.write(&[0, 0, 0, 0]).unwrap(); let eh_frame_section = eh_frame.0.into_section();
334 custom_sections.push(eh_frame_section);
335 unwind_info.eh_frame = Some(SectionIndex::new(custom_sections.len() - 1))
336 };
337
338 let got = wasmer_compiler::types::function::GOT::empty();
339
340 Ok(Compilation {
341 functions: functions.into_iter().collect(),
342 custom_sections,
343 function_call_trampolines,
344 dynamic_function_trampolines,
345 unwind_info,
346 got,
347 })
348 }
349}
350
351impl Compiler for SinglepassCompiler {
352 fn name(&self) -> &str {
353 "singlepass"
354 }
355
356 fn deterministic_id(&self) -> String {
357 String::from("singlepass")
358 }
359
360 fn get_middlewares(&self) -> &[Arc<dyn ModuleMiddleware>] {
362 &self.config.middlewares
363 }
364
365 fn compile_module(
368 &self,
369 target: &Target,
370 compile_info: &CompileModuleInfo,
371 _module_translation: &ModuleTranslationState,
372 function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
373 progress_callback: Option<&CompilationProgressCallback>,
374 ) -> Result<Compilation, CompileError> {
375 #[cfg(feature = "rayon")]
376 {
377 let num_threads = self.config.num_threads.get();
378 let pool = rayon::ThreadPoolBuilder::new()
379 .num_threads(num_threads)
380 .build()
381 .map_err(|e| {
382 CompileError::Codegen(format!("failed to build rayon thread pool: {e}"))
383 })?;
384
385 pool.install(|| {
386 self.compile_module_internal(
387 target,
388 compile_info,
389 function_body_inputs,
390 progress_callback,
391 )
392 })
393 }
394
395 #[cfg(not(feature = "rayon"))]
396 {
397 self.compile_module_internal(
398 target,
399 compile_info,
400 function_body_inputs,
401 progress_callback,
402 )
403 }
404 }
405
406 fn get_cpu_features_used(&self, cpu_features: &EnumSet<CpuFeature>) -> EnumSet<CpuFeature> {
407 let used = CpuFeature::AVX | CpuFeature::SSE42 | CpuFeature::LZCNT | CpuFeature::BMI1;
408 cpu_features.intersection(used)
409 }
410}
411
412trait IntoParIterIfRayon {
413 type Output;
414 fn into_par_iter_if_rayon(self) -> Self::Output;
415}
416
417impl<T: Send> IntoParIterIfRayon for Vec<T> {
418 #[cfg(not(feature = "rayon"))]
419 type Output = std::vec::IntoIter<T>;
420 #[cfg(feature = "rayon")]
421 type Output = rayon::vec::IntoIter<T>;
422
423 fn into_par_iter_if_rayon(self) -> Self::Output {
424 #[cfg(not(feature = "rayon"))]
425 return self.into_iter();
426 #[cfg(feature = "rayon")]
427 return self.into_par_iter();
428 }
429}
430
431#[cfg(test)]
432mod tests {
433 use super::*;
434 use std::str::FromStr;
435 use target_lexicon::triple;
436 use wasmer_compiler::Features;
437 use wasmer_types::{
438 MemoryStyle, TableStyle,
439 target::{CpuFeature, Triple},
440 };
441
442 fn dummy_compilation_ingredients<'a>() -> (
443 CompileModuleInfo,
444 ModuleTranslationState,
445 PrimaryMap<LocalFunctionIndex, FunctionBodyData<'a>>,
446 ) {
447 let compile_info = CompileModuleInfo {
448 features: Features::new(),
449 module: Arc::new(ModuleInfo::new()),
450 memory_styles: PrimaryMap::<MemoryIndex, MemoryStyle>::new(),
451 table_styles: PrimaryMap::<TableIndex, TableStyle>::new(),
452 };
453 let module_translation = ModuleTranslationState::new();
454 let function_body_inputs = PrimaryMap::<LocalFunctionIndex, FunctionBodyData<'_>>::new();
455 (compile_info, module_translation, function_body_inputs)
456 }
457
458 #[test]
459 fn errors_for_unsupported_targets() {
460 let compiler = SinglepassCompiler::new(Singlepass::default());
461
462 let linux32 = Target::new(triple!("i686-unknown-linux-gnu"), CpuFeature::for_host());
464 let (info, translation, inputs) = dummy_compilation_ingredients();
465 let result = compiler.compile_module(&linux32, &info, &translation, inputs, None);
466 match result.unwrap_err() {
467 CompileError::UnsupportedTarget(name) => assert_eq!(name, "i686"),
468 error => panic!("Unexpected error: {error:?}"),
469 };
470
471 let win32 = Target::new(triple!("i686-pc-windows-gnu"), CpuFeature::for_host());
473 let (info, translation, inputs) = dummy_compilation_ingredients();
474 let result = compiler.compile_module(&win32, &info, &translation, inputs, None);
475 match result.unwrap_err() {
476 CompileError::UnsupportedTarget(name) => assert_eq!(name, "i686"), error => panic!("Unexpected error: {error:?}"),
478 };
479 }
480
481 #[test]
482 fn errors_for_unsuported_cpufeatures() {
483 let compiler = SinglepassCompiler::new(Singlepass::default());
484 let mut features =
485 CpuFeature::AVX | CpuFeature::SSE42 | CpuFeature::LZCNT | CpuFeature::BMI1;
486 assert!(
488 compiler.get_cpu_features_used(&features).is_subset(
489 CpuFeature::AVX | CpuFeature::SSE42 | CpuFeature::LZCNT | CpuFeature::BMI1
490 )
491 );
492 assert!(
494 !compiler
495 .get_cpu_features_used(&features)
496 .is_subset(CpuFeature::SSE42 | CpuFeature::LZCNT | CpuFeature::BMI1)
497 );
498 features.insert_all(CpuFeature::AVX512DQ | CpuFeature::AVX512F);
500 assert!(
501 compiler.get_cpu_features_used(&features).is_subset(
502 CpuFeature::AVX | CpuFeature::SSE42 | CpuFeature::LZCNT | CpuFeature::BMI1
503 )
504 );
505 }
506}