wasmer_compiler_singlepass/
compiler.rs1#![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
24pub struct SinglepassCompiler {
27 config: Singlepass,
28}
29
30impl SinglepassCompiler {
31 pub fn new(config: Singlepass) -> Self {
33 Self { config }
34 }
35
36 fn config(&self) -> &Singlepass {
38 &self.config
39 }
40}
41
42impl Compiler for SinglepassCompiler {
43 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().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 _ => 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 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 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 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"), error => panic!("Unexpected error: {:?}", error),
265 };
266 }
267}