wasmer_compiler_singlepass/
compiler.rs

1//! Support for compiling with Singlepass.
2// Allow unused imports while developing.
3#![allow(unused_imports, dead_code)]
4
5use crate::codegen_x64::{
6    gen_import_call_trampoline, gen_std_dynamic_import_trampoline, gen_std_trampoline,
7    CodegenError, FuncGen,
8};
9use crate::config::Singlepass;
10#[cfg(feature = "rayon")]
11use rayon::prelude::{IntoParallelIterator, ParallelIterator};
12use std::sync::Arc;
13use wasmer_compiler::{
14    Architecture, CallingConvention, Compilation, CompileError, CompileModuleInfo,
15    CompiledFunction, Compiler, CompilerConfig, CpuFeature, FunctionBody, FunctionBodyData,
16    ModuleTranslationState, OperatingSystem, SectionIndex, Target, TrapInformation,
17};
18use wasmer_types::entity::{EntityRef, PrimaryMap};
19use wasmer_types::{
20    FunctionIndex, FunctionType, LocalFunctionIndex, MemoryIndex, ModuleInfo, TableIndex,
21};
22use wasmer_vm::{TrapCode, VMOffsets};
23
24/// A compiler that compiles a WebAssembly module with Singlepass.
25/// It does the compilation in one pass
26pub struct SinglepassCompiler {
27    config: Singlepass,
28}
29
30impl SinglepassCompiler {
31    /// Creates a new Singlepass compiler
32    pub fn new(config: Singlepass) -> Self {
33        Self { config }
34    }
35
36    /// Gets the config for this Compiler
37    fn config(&self) -> &Singlepass {
38        &self.config
39    }
40}
41
42impl Compiler for SinglepassCompiler {
43    /// Compile the module using Singlepass, producing a compilation result with
44    /// associated relocations.
45    fn compile_module(
46        &self,
47        target: &Target,
48        compile_info: &CompileModuleInfo,
49        module_translation: &ModuleTranslationState,
50        function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
51    ) -> Result<Compilation, CompileError> {
52        /*if target.triple().operating_system == OperatingSystem::Windows {
53            return Err(CompileError::UnsupportedTarget(
54                OperatingSystem::Windows.to_string(),
55            ));
56        }*/
57        if target.triple().architecture != Architecture::X86_64 {
58            return Err(CompileError::UnsupportedTarget(
59                target.triple().architecture.to_string(),
60            ));
61        }
62        if !target.cpu_features().contains(CpuFeature::AVX) {
63            return Err(CompileError::UnsupportedTarget(
64                "x86_64 without AVX".to_string(),
65            ));
66        }
67        if compile_info.features.multi_value {
68            return Err(CompileError::UnsupportedFeature("multivalue".to_string()));
69        }
70        let calling_convention = match target.triple().default_calling_convention() {
71            Ok(CallingConvention::WindowsFastcall) => CallingConvention::WindowsFastcall,
72            Ok(CallingConvention::SystemV) => CallingConvention::SystemV,
73            //Ok(CallingConvention::AppleAarch64) => AppleAarch64,
74            _ => panic!("Unsupported Calling convention for Singlepass compiler"),
75        };
76
77        let table_styles = &compile_info.table_styles;
78        let module = &compile_info.module;
79        let pointer_width = target
80            .triple()
81            .pointer_width()
82            .map_err(|()| {
83                CompileError::UnsupportedTarget("target with unknown pointer width".into())
84            })?
85            .bytes();
86        let vmoffsets = VMOffsets::new(pointer_width).with_module_info(&module);
87        let import_idxs = 0..module.import_counts.functions as usize;
88        let import_trampolines: PrimaryMap<SectionIndex, _> = import_idxs
89            .into_par_iter_if_rayon()
90            .map(|i| {
91                let i = FunctionIndex::new(i);
92                gen_import_call_trampoline(
93                    &vmoffsets,
94                    i,
95                    &module.signatures[module.functions[i]],
96                    calling_convention,
97                )
98            })
99            .collect::<Vec<_>>()
100            .into_iter()
101            .collect();
102        let functions = function_body_inputs
103            .iter()
104            .collect::<Vec<(LocalFunctionIndex, &FunctionBodyData<'_>)>>()
105            .into_par_iter_if_rayon()
106            .map(|(i, input)| {
107                let reader = wasmer_compiler::FunctionReader::new(input.module_offset, input.data);
108
109                let mut local_reader = reader.get_locals_reader()?;
110                // This local list excludes arguments.
111                let mut locals = vec![];
112                let num_locals = local_reader.get_count();
113                for _ in 0..num_locals {
114                    let (count, ty) = local_reader.read()?;
115                    for _ in 0..count {
116                        locals.push(ty);
117                    }
118                }
119
120                let mut generator = FuncGen::new(
121                    module,
122                    module_translation,
123                    &self.config,
124                    &vmoffsets,
125                    &table_styles,
126                    i,
127                    &locals,
128                    calling_convention,
129                )
130                .map_err(to_compile_error)?;
131
132                let mut operator_reader = reader.get_operators_reader()?.into_iter_with_offsets();
133                while generator.has_control_frames() {
134                    let (op, pos) = operator_reader.next().unwrap()?;
135                    generator.set_srcloc(pos as u32);
136                    generator.feed_operator(op).map_err(to_compile_error)?;
137                }
138
139                Ok(generator.finalize(&input))
140            })
141            .collect::<Result<Vec<CompiledFunction>, CompileError>>()?
142            .into_iter()
143            .collect::<PrimaryMap<LocalFunctionIndex, CompiledFunction>>();
144
145        let function_call_trampolines = module
146            .signatures
147            .values()
148            .collect::<Vec<_>>()
149            .into_par_iter_if_rayon()
150            .map(|func_type| gen_std_trampoline(&func_type, calling_convention))
151            .collect::<Vec<_>>()
152            .into_iter()
153            .collect::<PrimaryMap<_, _>>();
154
155        let dynamic_function_trampolines = module
156            .imported_function_types()
157            .collect::<Vec<_>>()
158            .into_par_iter_if_rayon()
159            .map(|func_type| {
160                gen_std_dynamic_import_trampoline(&vmoffsets, &func_type, calling_convention)
161            })
162            .collect::<Vec<_>>()
163            .into_iter()
164            .collect::<PrimaryMap<FunctionIndex, FunctionBody>>();
165
166        Ok(Compilation::new(
167            functions,
168            import_trampolines,
169            function_call_trampolines,
170            dynamic_function_trampolines,
171            None,
172            None,
173        ))
174    }
175}
176
177trait ToCompileError {
178    fn to_compile_error(self) -> CompileError;
179}
180
181impl ToCompileError for CodegenError {
182    fn to_compile_error(self) -> CompileError {
183        CompileError::Codegen(self.message)
184    }
185}
186
187fn to_compile_error<T: ToCompileError>(x: T) -> CompileError {
188    x.to_compile_error()
189}
190
191trait IntoParIterIfRayon {
192    type Output;
193    fn into_par_iter_if_rayon(self) -> Self::Output;
194}
195
196#[cfg(feature = "rayon")]
197impl<T: IntoParallelIterator + IntoIterator> IntoParIterIfRayon for T {
198    type Output = <T as IntoParallelIterator>::Iter;
199    fn into_par_iter_if_rayon(self) -> Self::Output {
200        return self.into_par_iter();
201    }
202}
203
204#[cfg(not(feature = "rayon"))]
205impl<T: IntoIterator> IntoParIterIfRayon for T {
206    type Output = <T as IntoIterator>::IntoIter;
207    fn into_par_iter_if_rayon(self) -> Self::Output {
208        return self.into_iter();
209    }
210}
211
212#[cfg(test)]
213mod tests {
214    use super::*;
215    use std::str::FromStr;
216    use target_lexicon::triple;
217    use wasmer_compiler::{CpuFeature, Features, Triple};
218    use wasmer_vm::{MemoryStyle, TableStyle};
219
220    fn dummy_compilation_ingredients<'a>() -> (
221        CompileModuleInfo,
222        ModuleTranslationState,
223        PrimaryMap<LocalFunctionIndex, FunctionBodyData<'a>>,
224    ) {
225        let compile_info = CompileModuleInfo {
226            features: Features::new(),
227            module: Arc::new(ModuleInfo::new()),
228            memory_styles: PrimaryMap::<MemoryIndex, MemoryStyle>::new(),
229            table_styles: PrimaryMap::<TableIndex, TableStyle>::new(),
230        };
231        let module_translation = ModuleTranslationState::new();
232        let function_body_inputs = PrimaryMap::<LocalFunctionIndex, FunctionBodyData<'_>>::new();
233        (compile_info, module_translation, function_body_inputs)
234    }
235
236    #[test]
237    fn errors_for_unsupported_targets() {
238        let compiler = SinglepassCompiler::new(Singlepass::default());
239
240        // Compile for win64
241        /*let win64 = Target::new(triple!("x86_64-pc-windows-msvc"), CpuFeature::for_host());
242        let (mut info, translation, inputs) = dummy_compilation_ingredients();
243        let result = compiler.compile_module(&win64, &mut info, &translation, inputs);
244        match result.unwrap_err() {
245            CompileError::UnsupportedTarget(name) => assert_eq!(name, "windows"),
246            error => panic!("Unexpected error: {:?}", error),
247        };*/
248
249        // Compile for 32bit Linux
250        let linux32 = Target::new(triple!("i686-unknown-linux-gnu"), CpuFeature::for_host());
251        let (mut info, translation, inputs) = dummy_compilation_ingredients();
252        let result = compiler.compile_module(&linux32, &mut info, &translation, inputs);
253        match result.unwrap_err() {
254            CompileError::UnsupportedTarget(name) => assert_eq!(name, "i686"),
255            error => panic!("Unexpected error: {:?}", error),
256        };
257
258        // Compile for win32
259        let win32 = Target::new(triple!("i686-pc-windows-gnu"), CpuFeature::for_host());
260        let (mut info, translation, inputs) = dummy_compilation_ingredients();
261        let result = compiler.compile_module(&win32, &mut info, &translation, inputs);
262        match result.unwrap_err() {
263            CompileError::UnsupportedTarget(name) => assert_eq!(name, "i686"), // Windows should be checked before architecture
264            error => panic!("Unexpected error: {:?}", error),
265        };
266    }
267}