1use std::cmp::Reverse;
5
6use crate::progress::ProgressContext;
7use crate::types::module::CompileModuleInfo;
8use crate::{
9 FunctionBodyData, ModuleTranslationState,
10 lib::std::{boxed::Box, sync::Arc},
11 translator::ModuleMiddleware,
12 types::function::Compilation,
13};
14use crossbeam_channel::unbounded;
15use enumset::EnumSet;
16use itertools::Itertools;
17use wasmer_types::{
18 CompilationProgressCallback, Features, LocalFunctionIndex,
19 entity::PrimaryMap,
20 error::CompileError,
21 target::{CpuFeature, Target, UserCompilerOptimizations},
22};
23#[cfg(feature = "translator")]
24use wasmparser::{Validator, WasmFeatures};
25
26pub trait CompilerConfig {
28 fn enable_pic(&mut self) {
34 }
37
38 fn enable_verifier(&mut self) {
43 }
46
47 fn enable_perfmap(&mut self) {
49 }
52
53 fn enable_non_volatile_memops(&mut self) {}
56
57 fn enable_experimental_unaligned_memory_accesses(&mut self) {}
62
63 fn enable_readonly_funcref_table(&mut self) {}
66
67 fn canonicalize_nans(&mut self, _enable: bool) {
72 }
75
76 fn compiler(self: Box<Self>) -> Box<dyn Compiler>;
78
79 fn default_features_for_target(&self, target: &Target) -> Features {
81 self.supported_features_for_target(target)
82 }
83
84 fn supported_features_for_target(&self, _target: &Target) -> Features {
86 Features::default()
87 }
88
89 fn push_middleware(&mut self, middleware: Arc<dyn ModuleMiddleware>);
91}
92
93impl<T> From<T> for Box<dyn CompilerConfig + 'static>
94where
95 T: CompilerConfig + 'static,
96{
97 fn from(other: T) -> Self {
98 Box::new(other)
99 }
100}
101
102pub trait Compiler: Send + std::fmt::Debug {
104 fn name(&self) -> &str;
108
109 fn deterministic_id(&self) -> String;
112
113 fn with_opts(
121 &mut self,
122 suggested_compiler_opts: &UserCompilerOptimizations,
123 ) -> Result<(), CompileError> {
124 _ = suggested_compiler_opts;
125 Ok(())
126 }
127
128 #[cfg(feature = "translator")]
132 fn validate_module(&self, features: &Features, data: &[u8]) -> Result<(), CompileError> {
133 let mut wasm_features = WasmFeatures::empty();
134 wasm_features.set(WasmFeatures::BULK_MEMORY, features.bulk_memory);
135 wasm_features.set(WasmFeatures::THREADS, features.threads);
136 wasm_features.set(WasmFeatures::REFERENCE_TYPES, features.reference_types);
137 wasm_features.set(WasmFeatures::MULTI_VALUE, features.multi_value);
138 wasm_features.set(WasmFeatures::SIMD, features.simd);
139 wasm_features.set(WasmFeatures::TAIL_CALL, features.tail_call);
140 wasm_features.set(WasmFeatures::MULTI_MEMORY, features.multi_memory);
141 wasm_features.set(WasmFeatures::MEMORY64, features.memory64);
142 wasm_features.set(WasmFeatures::EXCEPTIONS, features.exceptions);
143 wasm_features.set(WasmFeatures::EXTENDED_CONST, features.extended_const);
144 wasm_features.set(WasmFeatures::RELAXED_SIMD, features.relaxed_simd);
145 wasm_features.set(WasmFeatures::WIDE_ARITHMETIC, features.wide_arithmetic);
146 wasm_features.set(WasmFeatures::TAIL_CALL, features.tail_call);
147 wasm_features.set(WasmFeatures::MUTABLE_GLOBAL, true);
148 wasm_features.set(WasmFeatures::SATURATING_FLOAT_TO_INT, true);
149 wasm_features.set(WasmFeatures::FLOATS, true);
150 wasm_features.set(WasmFeatures::SIGN_EXTENSION, true);
151 wasm_features.set(WasmFeatures::GC_TYPES, true);
152
153 let mut validator = Validator::new_with_features(wasm_features);
154 validator
155 .validate_all(data)
156 .map_err(|e| CompileError::Validate(format!("{e}")))?;
157 Ok(())
158 }
159
160 fn compile_module(
164 &self,
165 target: &Target,
166 module: &CompileModuleInfo,
167 module_translation: &ModuleTranslationState,
168 function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
170 progress_callback: Option<&CompilationProgressCallback>,
171 ) -> Result<Compilation, CompileError>;
172
173 fn get_middlewares(&self) -> &[Arc<dyn ModuleMiddleware>];
175
176 fn enable_readonly_funcref_table(&self) -> bool {
178 false
179 }
180
181 fn get_cpu_features_used(&self, cpu_features: &EnumSet<CpuFeature>) -> EnumSet<CpuFeature> {
183 *cpu_features
184 }
185
186 fn get_perfmap_enabled(&self) -> bool {
188 false
189 }
190}
191
192pub struct FunctionBucket<'a> {
194 functions: Vec<(LocalFunctionIndex, &'a FunctionBodyData<'a>)>,
195 pub size: usize,
197}
198
199impl<'a> FunctionBucket<'a> {
200 pub fn new() -> Self {
202 Self {
203 functions: Vec::new(),
204 size: 0,
205 }
206 }
207}
208
209pub fn build_function_buckets<'a>(
211 function_body_inputs: &'a PrimaryMap<LocalFunctionIndex, FunctionBodyData<'a>>,
212 bucket_threshold_size: u64,
213) -> Vec<FunctionBucket<'a>> {
214 let mut function_bodies = function_body_inputs
215 .iter()
216 .sorted_by_key(|(id, body)| Reverse((body.data.len(), id.as_u32())))
217 .collect_vec();
218
219 let mut buckets = Vec::new();
220
221 while !function_bodies.is_empty() {
222 let mut next_function_body = Vec::with_capacity(function_bodies.len());
223 let mut bucket = FunctionBucket::new();
224
225 for (fn_index, fn_body) in function_bodies.into_iter() {
226 if bucket.size + fn_body.data.len() <= bucket_threshold_size as usize
227 || bucket.size == 0
229 {
230 bucket.size += fn_body.data.len();
231 bucket.functions.push((fn_index, fn_body));
232 } else {
233 next_function_body.push((fn_index, fn_body));
234 }
235 }
236
237 function_bodies = next_function_body;
238 buckets.push(bucket);
239 }
240
241 buckets
242}
243
244pub trait CompiledFunction {}
246
247pub trait FuncTranslator {}
249
250#[allow(clippy::too_many_arguments)]
252pub fn translate_function_buckets<'a, C, T, F, G>(
253 pool: &rayon::ThreadPool,
254 func_translator_builder: F,
255 translate_fn: G,
256 progress: Option<ProgressContext>,
257 buckets: &[FunctionBucket<'a>],
258) -> Result<Vec<C>, CompileError>
259where
260 T: FuncTranslator,
261 C: CompiledFunction + Send + Sync,
262 F: Fn() -> T + Send + Sync + Copy,
263 G: Fn(&mut T, &LocalFunctionIndex, &FunctionBodyData) -> Result<C, CompileError>
264 + Send
265 + Sync
266 + Copy,
267{
268 let progress = progress.as_ref();
269
270 let functions = pool.install(|| {
271 let (bucket_tx, bucket_rx) = unbounded::<&FunctionBucket<'a>>();
272 for bucket in buckets {
273 bucket_tx.send(bucket).map_err(|e| {
274 CompileError::Resource(format!("cannot allocate crossbeam channel item: {e}"))
275 })?;
276 }
277 drop(bucket_tx);
278
279 let (result_tx, result_rx) =
280 unbounded::<Result<Vec<(LocalFunctionIndex, C)>, CompileError>>();
281
282 pool.scope(|s| {
283 let worker_count = pool.current_num_threads().max(1);
284 for _ in 0..worker_count {
285 let bucket_rx = bucket_rx.clone();
286 let result_tx = result_tx.clone();
287 s.spawn(move |_| {
288 let mut func_translator = func_translator_builder();
289
290 while let Ok(bucket) = bucket_rx.recv() {
291 let bucket_result = (|| {
292 let mut translated_functions = Vec::new();
293 for (i, input) in bucket.functions.iter() {
294 let translated = translate_fn(&mut func_translator, i, input)?;
295 if let Some(progress) = progress {
296 progress.notify_steps(input.data.len() as u64)?;
297 }
298 translated_functions.push((*i, translated));
299 }
300 Ok(translated_functions)
301 })();
302
303 if result_tx.send(bucket_result).is_err() {
304 break;
305 }
306 }
307 });
308 }
309 });
310
311 drop(result_tx);
312 let mut functions = Vec::with_capacity(buckets.iter().map(|b| b.functions.len()).sum());
313 for _ in 0..buckets.len() {
314 match result_rx.recv().map_err(|e| {
315 CompileError::Resource(format!("cannot allocate crossbeam channel item: {e}"))
316 })? {
317 Ok(bucket_functions) => functions.extend(bucket_functions),
318 Err(err) => return Err(err),
319 }
320 }
321 Ok(functions)
322 })?;
323
324 Ok(functions
325 .into_iter()
326 .sorted_by_key(|x| x.0)
327 .map(|(_, body)| body)
328 .collect_vec())
329}
330
331pub const WASM_LARGE_FUNCTION_THRESHOLD: u64 = 100_000;
333
334pub const WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE: u64 = 1_000;