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