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_x64::MachineX86_64;
15#[cfg(feature = "unwind")]
16use crate::unwind::{UnwindFrame, create_systemv_cie};
17use enumset::EnumSet;
18#[cfg(feature = "unwind")]
19use gimli::write::{EhFrame, FrameTable, Writer};
20#[cfg(feature = "rayon")]
21use rayon::prelude::{IntoParallelIterator, ParallelIterator};
22use std::collections::HashMap;
23use std::sync::Arc;
24use wasmer_compiler::misc::{CompiledKind, save_assembly_to_file, types_to_signature};
25use wasmer_compiler::{
26 Compiler, CompilerConfig, FunctionBinaryReader, FunctionBodyData, MiddlewareBinaryReader,
27 ModuleMiddleware, ModuleMiddlewareChain, ModuleTranslationState,
28 types::{
29 function::{Compilation, CompiledFunction, FunctionBody, UnwindInfo},
30 module::CompileModuleInfo,
31 section::SectionIndex,
32 },
33};
34use wasmer_types::entity::{EntityRef, PrimaryMap};
35use wasmer_types::target::{Architecture, CallingConvention, CpuFeature, Target};
36use wasmer_types::{
37 CompileError, FunctionIndex, FunctionType, LocalFunctionIndex, MemoryIndex, ModuleInfo,
38 TableIndex, TrapCode, TrapInformation, Type, VMOffsets,
39};
40
41#[derive(Debug)]
44pub struct SinglepassCompiler {
45 config: Singlepass,
46}
47
48impl SinglepassCompiler {
49 pub fn new(config: Singlepass) -> Self {
51 Self { config }
52 }
53
54 fn config(&self) -> &Singlepass {
56 &self.config
57 }
58}
59
60impl Compiler for SinglepassCompiler {
61 fn name(&self) -> &str {
62 "singlepass"
63 }
64
65 fn deterministic_id(&self) -> String {
66 String::from("singlepass")
67 }
68
69 fn get_middlewares(&self) -> &[Arc<dyn ModuleMiddleware>] {
71 &self.config.middlewares
72 }
73
74 fn compile_module(
77 &self,
78 target: &Target,
79 compile_info: &CompileModuleInfo,
80 _module_translation: &ModuleTranslationState,
81 function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
82 ) -> Result<Compilation, CompileError> {
83 let arch = target.triple().architecture;
84 match arch {
85 Architecture::X86_64 => {}
86 Architecture::Aarch64(_) => {}
87 _ => {
88 return Err(CompileError::UnsupportedTarget(
89 target.triple().architecture.to_string(),
90 ));
91 }
92 };
93
94 let calling_convention = match target.triple().default_calling_convention() {
95 Ok(CallingConvention::WindowsFastcall) => CallingConvention::WindowsFastcall,
96 Ok(CallingConvention::SystemV) => CallingConvention::SystemV,
97 Ok(CallingConvention::AppleAarch64) => CallingConvention::AppleAarch64,
98 _ => {
99 return Err(CompileError::UnsupportedTarget(
100 "Unsupported Calling convention for Singlepass compiler".to_string(),
101 ));
102 }
103 };
104
105 #[cfg(feature = "unwind")]
107 let dwarf_frametable = if function_body_inputs.is_empty() {
108 None
112 } else {
113 match target.triple().default_calling_convention() {
114 Ok(CallingConvention::SystemV) => {
115 match create_systemv_cie(target.triple().architecture) {
116 Some(cie) => {
117 let mut dwarf_frametable = FrameTable::default();
118 let cie_id = dwarf_frametable.add_cie(cie);
119 Some((dwarf_frametable, cie_id))
120 }
121 None => None,
122 }
123 }
124 _ => None,
125 }
126 };
127
128 let memory_styles = &compile_info.memory_styles;
129 let table_styles = &compile_info.table_styles;
130 let vmoffsets = VMOffsets::new(8, &compile_info.module);
131 let module = &compile_info.module;
132 #[cfg_attr(not(feature = "unwind"), allow(unused_mut))]
133 let mut custom_sections: PrimaryMap<SectionIndex, _> = (0..module.num_imported_functions)
134 .map(FunctionIndex::new)
135 .collect::<Vec<_>>()
136 .into_par_iter_if_rayon()
137 .map(|i| {
138 gen_import_call_trampoline(
139 &vmoffsets,
140 i,
141 &module.signatures[module.functions[i]],
142 target,
143 calling_convention,
144 )
145 })
146 .collect::<Result<Vec<_>, _>>()?
147 .into_iter()
148 .collect();
149 #[cfg_attr(not(feature = "unwind"), allow(unused_variables))]
150 let (functions, fdes): (Vec<CompiledFunction>, Vec<_>) = function_body_inputs
151 .iter()
152 .collect::<Vec<(LocalFunctionIndex, &FunctionBodyData<'_>)>>()
153 .into_par_iter_if_rayon()
154 .map(|(i, input)| {
155 let middleware_chain = self
156 .config
157 .middlewares
158 .generate_function_middleware_chain(i);
159 let mut reader =
160 MiddlewareBinaryReader::new_with_offset(input.data, input.module_offset);
161 reader.set_middleware_chain(middleware_chain);
162
163 let mut locals = vec![];
165 let num_locals = reader.read_local_count()?;
166 for _ in 0..num_locals {
167 let (count, ty) = reader.read_local_decl()?;
168 for _ in 0..count {
169 locals.push(ty);
170 }
171 }
172
173 match arch {
174 Architecture::X86_64 => {
175 let machine = MachineX86_64::new(Some(target.clone()))?;
176 let mut generator = FuncGen::new(
177 module,
178 &self.config,
179 &vmoffsets,
180 memory_styles,
181 table_styles,
182 i,
183 &locals,
184 machine,
185 calling_convention,
186 )?;
187 while generator.has_control_frames() {
188 generator.set_srcloc(reader.original_position() as u32);
189 let op = reader.read_operator()?;
190 generator.feed_operator(op)?;
191 }
192
193 generator.finalize(input, arch)
194 }
195 Architecture::Aarch64(_) => {
196 let machine = MachineARM64::new(Some(target.clone()));
197 let mut generator = FuncGen::new(
198 module,
199 &self.config,
200 &vmoffsets,
201 memory_styles,
202 table_styles,
203 i,
204 &locals,
205 machine,
206 calling_convention,
207 )?;
208 while generator.has_control_frames() {
209 generator.set_srcloc(reader.original_position() as u32);
210 let op = reader.read_operator()?;
211 generator.feed_operator(op)?;
212 }
213
214 generator.finalize(input, arch)
215 }
216 _ => unimplemented!(),
217 }
218 })
219 .collect::<Result<Vec<_>, CompileError>>()?
220 .into_iter()
221 .unzip();
222
223 let function_call_trampolines = module
224 .signatures
225 .values()
226 .collect::<Vec<_>>()
227 .into_par_iter_if_rayon()
228 .map(|func_type| -> Result<FunctionBody, CompileError> {
229 let body = gen_std_trampoline(func_type, target, calling_convention)?;
230 if let Some(callbacks) = self.config.callbacks.as_ref() {
231 callbacks.obj_memory_buffer(
232 &CompiledKind::FunctionCallTrampoline(func_type.clone()),
233 &body.body,
234 );
235 callbacks.asm_memory_buffer(
236 &CompiledKind::FunctionCallTrampoline(func_type.clone()),
237 arch,
238 &body.body,
239 HashMap::new(),
240 )?;
241 }
242
243 Ok(body)
244 })
245 .collect::<Result<Vec<_>, _>>()?
246 .into_iter()
247 .collect::<PrimaryMap<_, _>>();
248
249 let dynamic_function_trampolines = module
250 .imported_function_types()
251 .collect::<Vec<_>>()
252 .into_par_iter_if_rayon()
253 .map(|func_type| -> Result<FunctionBody, CompileError> {
254 let body = gen_std_dynamic_import_trampoline(
255 &vmoffsets,
256 &func_type,
257 target,
258 calling_convention,
259 )?;
260 if let Some(callbacks) = self.config.callbacks.as_ref() {
261 callbacks.obj_memory_buffer(
262 &CompiledKind::DynamicFunctionTrampoline(func_type.clone()),
263 &body.body,
264 );
265 callbacks.asm_memory_buffer(
266 &CompiledKind::DynamicFunctionTrampoline(func_type.clone()),
267 arch,
268 &body.body,
269 HashMap::new(),
270 )?;
271 }
272 Ok(body)
273 })
274 .collect::<Result<Vec<_>, _>>()?
275 .into_iter()
276 .collect::<PrimaryMap<FunctionIndex, FunctionBody>>();
277
278 #[allow(unused_mut)]
279 let mut unwind_info = UnwindInfo::default();
280
281 #[cfg(feature = "unwind")]
282 if let Some((mut dwarf_frametable, cie_id)) = dwarf_frametable {
283 for fde in fdes.into_iter().flatten() {
284 match fde {
285 UnwindFrame::SystemV(fde) => dwarf_frametable.add_fde(cie_id, fde),
286 }
287 }
288 let mut eh_frame = EhFrame(WriterRelocate::new(target.triple().endianness().ok()));
289 dwarf_frametable.write_eh_frame(&mut eh_frame).unwrap();
290 eh_frame.write(&[0, 0, 0, 0]).unwrap(); let eh_frame_section = eh_frame.0.into_section();
293 custom_sections.push(eh_frame_section);
294 unwind_info.eh_frame = Some(SectionIndex::new(custom_sections.len() - 1))
295 };
296
297 let got = wasmer_compiler::types::function::GOT::empty();
298
299 Ok(Compilation {
300 functions: functions.into_iter().collect(),
301 custom_sections,
302 function_call_trampolines,
303 dynamic_function_trampolines,
304 unwind_info,
305 got,
306 })
307 }
308
309 fn get_cpu_features_used(&self, cpu_features: &EnumSet<CpuFeature>) -> EnumSet<CpuFeature> {
310 let used = CpuFeature::AVX | CpuFeature::SSE42 | CpuFeature::LZCNT | CpuFeature::BMI1;
311 cpu_features.intersection(used)
312 }
313}
314
315trait IntoParIterIfRayon {
316 type Output;
317 fn into_par_iter_if_rayon(self) -> Self::Output;
318}
319
320impl<T: Send> IntoParIterIfRayon for Vec<T> {
321 #[cfg(not(feature = "rayon"))]
322 type Output = std::vec::IntoIter<T>;
323 #[cfg(feature = "rayon")]
324 type Output = rayon::vec::IntoIter<T>;
325
326 fn into_par_iter_if_rayon(self) -> Self::Output {
327 #[cfg(not(feature = "rayon"))]
328 return self.into_iter();
329 #[cfg(feature = "rayon")]
330 return self.into_par_iter();
331 }
332}
333
334#[cfg(test)]
335mod tests {
336 use super::*;
337 use std::str::FromStr;
338 use target_lexicon::triple;
339 use wasmer_compiler::Features;
340 use wasmer_types::{
341 MemoryStyle, TableStyle,
342 target::{CpuFeature, Triple},
343 };
344
345 fn dummy_compilation_ingredients<'a>() -> (
346 CompileModuleInfo,
347 ModuleTranslationState,
348 PrimaryMap<LocalFunctionIndex, FunctionBodyData<'a>>,
349 ) {
350 let compile_info = CompileModuleInfo {
351 features: Features::new(),
352 module: Arc::new(ModuleInfo::new()),
353 memory_styles: PrimaryMap::<MemoryIndex, MemoryStyle>::new(),
354 table_styles: PrimaryMap::<TableIndex, TableStyle>::new(),
355 };
356 let module_translation = ModuleTranslationState::new();
357 let function_body_inputs = PrimaryMap::<LocalFunctionIndex, FunctionBodyData<'_>>::new();
358 (compile_info, module_translation, function_body_inputs)
359 }
360
361 #[test]
362 fn errors_for_unsupported_targets() {
363 let compiler = SinglepassCompiler::new(Singlepass::default());
364
365 let linux32 = Target::new(triple!("i686-unknown-linux-gnu"), CpuFeature::for_host());
367 let (info, translation, inputs) = dummy_compilation_ingredients();
368 let result = compiler.compile_module(&linux32, &info, &translation, inputs);
369 match result.unwrap_err() {
370 CompileError::UnsupportedTarget(name) => assert_eq!(name, "i686"),
371 error => panic!("Unexpected error: {error:?}"),
372 };
373
374 let win32 = Target::new(triple!("i686-pc-windows-gnu"), CpuFeature::for_host());
376 let (info, translation, inputs) = dummy_compilation_ingredients();
377 let result = compiler.compile_module(&win32, &info, &translation, inputs);
378 match result.unwrap_err() {
379 CompileError::UnsupportedTarget(name) => assert_eq!(name, "i686"), error => panic!("Unexpected error: {error:?}"),
381 };
382 }
383
384 #[test]
385 fn errors_for_unsuported_cpufeatures() {
386 let compiler = SinglepassCompiler::new(Singlepass::default());
387 let mut features =
388 CpuFeature::AVX | CpuFeature::SSE42 | CpuFeature::LZCNT | CpuFeature::BMI1;
389 assert!(
391 compiler.get_cpu_features_used(&features).is_subset(
392 CpuFeature::AVX | CpuFeature::SSE42 | CpuFeature::LZCNT | CpuFeature::BMI1
393 )
394 );
395 assert!(
397 !compiler
398 .get_cpu_features_used(&features)
399 .is_subset(CpuFeature::SSE42 | CpuFeature::LZCNT | CpuFeature::BMI1)
400 );
401 features.insert_all(CpuFeature::AVX512DQ | CpuFeature::AVX512F);
403 assert!(
404 compiler.get_cpu_features_used(&features).is_subset(
405 CpuFeature::AVX | CpuFeature::SSE42 | CpuFeature::LZCNT | CpuFeature::BMI1
406 )
407 );
408 }
409}