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