1use std::cmp::Reverse;
5
6use crate::progress::ProgressContext;
7use crate::types::{module::CompileModuleInfo, symbols::SymbolRegistry};
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 canonicalize_nans(&mut self, _enable: bool) {
58 }
61
62 fn compiler(self: Box<Self>) -> Box<dyn Compiler>;
64
65 fn default_features_for_target(&self, target: &Target) -> Features {
67 self.supported_features_for_target(target)
68 }
69
70 fn supported_features_for_target(&self, _target: &Target) -> Features {
72 Features::default()
73 }
74
75 fn push_middleware(&mut self, middleware: Arc<dyn ModuleMiddleware>);
77}
78
79impl<T> From<T> for Box<dyn CompilerConfig + 'static>
80where
81 T: CompilerConfig + 'static,
82{
83 fn from(other: T) -> Self {
84 Box::new(other)
85 }
86}
87
88pub trait Compiler: Send + std::fmt::Debug {
90 fn name(&self) -> &str;
94
95 fn deterministic_id(&self) -> String;
98
99 fn with_opts(
107 &mut self,
108 suggested_compiler_opts: &UserCompilerOptimizations,
109 ) -> Result<(), CompileError> {
110 _ = suggested_compiler_opts;
111 Ok(())
112 }
113
114 #[cfg(feature = "translator")]
118 fn validate_module(&self, features: &Features, data: &[u8]) -> Result<(), CompileError> {
119 let mut wasm_features = WasmFeatures::default();
120 wasm_features.set(WasmFeatures::BULK_MEMORY, features.bulk_memory);
121 wasm_features.set(WasmFeatures::THREADS, features.threads);
122 wasm_features.set(WasmFeatures::REFERENCE_TYPES, features.reference_types);
123 wasm_features.set(WasmFeatures::MULTI_VALUE, features.multi_value);
124 wasm_features.set(WasmFeatures::SIMD, features.simd);
125 wasm_features.set(WasmFeatures::TAIL_CALL, features.tail_call);
126 wasm_features.set(WasmFeatures::MULTI_MEMORY, features.multi_memory);
127 wasm_features.set(WasmFeatures::MEMORY64, features.memory64);
128 wasm_features.set(WasmFeatures::EXCEPTIONS, features.exceptions);
129 wasm_features.set(WasmFeatures::EXTENDED_CONST, features.extended_const);
130 wasm_features.set(WasmFeatures::RELAXED_SIMD, features.relaxed_simd);
131 wasm_features.set(WasmFeatures::MUTABLE_GLOBAL, true);
132 wasm_features.set(WasmFeatures::SATURATING_FLOAT_TO_INT, true);
133 wasm_features.set(WasmFeatures::FLOATS, true);
134 wasm_features.set(WasmFeatures::SIGN_EXTENSION, true);
135 wasm_features.set(WasmFeatures::GC_TYPES, true);
136
137 wasm_features.set(WasmFeatures::COMPONENT_MODEL, false);
139 wasm_features.set(WasmFeatures::FUNCTION_REFERENCES, false);
140 wasm_features.set(WasmFeatures::MEMORY_CONTROL, false);
141 wasm_features.set(WasmFeatures::GC, false);
142 wasm_features.set(WasmFeatures::CM_VALUES, false);
143 wasm_features.set(WasmFeatures::CM_NESTED_NAMES, false);
144
145 let mut validator = Validator::new_with_features(wasm_features);
146 validator
147 .validate_all(data)
148 .map_err(|e| CompileError::Validate(format!("{e}")))?;
149 Ok(())
150 }
151
152 fn compile_module(
156 &self,
157 target: &Target,
158 module: &CompileModuleInfo,
159 module_translation: &ModuleTranslationState,
160 function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
162 progress_callback: Option<&CompilationProgressCallback>,
163 ) -> Result<Compilation, CompileError>;
164
165 fn experimental_native_compile_module(
169 &self,
170 _target: &Target,
171 _module: &CompileModuleInfo,
172 _module_translation: &ModuleTranslationState,
173 _function_body_inputs: &PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
175 _symbol_registry: &dyn SymbolRegistry,
176 _wasmer_metadata: &[u8],
178 ) -> Option<Result<Vec<u8>, CompileError>> {
179 None
180 }
181
182 fn get_middlewares(&self) -> &[Arc<dyn ModuleMiddleware>];
184
185 fn get_cpu_features_used(&self, cpu_features: &EnumSet<CpuFeature>) -> EnumSet<CpuFeature> {
187 *cpu_features
188 }
189
190 fn get_perfmap_enabled(&self) -> bool {
192 false
193 }
194}
195
196pub struct FunctionBucket<'a> {
198 functions: Vec<(LocalFunctionIndex, &'a FunctionBodyData<'a>)>,
199 pub size: usize,
201}
202
203impl<'a> FunctionBucket<'a> {
204 pub fn new() -> Self {
206 Self {
207 functions: Vec::new(),
208 size: 0,
209 }
210 }
211}
212
213pub fn build_function_buckets<'a>(
215 function_body_inputs: &'a PrimaryMap<LocalFunctionIndex, FunctionBodyData<'a>>,
216 bucket_threshold_size: u64,
217) -> Vec<FunctionBucket<'a>> {
218 let mut function_bodies = function_body_inputs
219 .iter()
220 .sorted_by_key(|(id, body)| Reverse((body.data.len(), id.as_u32())))
221 .collect_vec();
222
223 let mut buckets = Vec::new();
224
225 while !function_bodies.is_empty() {
226 let mut next_function_body = Vec::with_capacity(function_bodies.len());
227 let mut bucket = FunctionBucket::new();
228
229 for (fn_index, fn_body) in function_bodies.into_iter() {
230 if bucket.size + fn_body.data.len() <= bucket_threshold_size as usize
231 || bucket.size == 0
233 {
234 bucket.size += fn_body.data.len();
235 bucket.functions.push((fn_index, fn_body));
236 } else {
237 next_function_body.push((fn_index, fn_body));
238 }
239 }
240
241 function_bodies = next_function_body;
242 buckets.push(bucket);
243 }
244
245 buckets
246}
247
248pub trait CompiledFunction {}
250
251pub trait FuncTranslator {}
253
254#[allow(clippy::too_many_arguments)]
256pub fn translate_function_buckets<'a, C, T, F, G>(
257 pool: &rayon::ThreadPool,
258 func_translator_builder: F,
259 translate_fn: G,
260 progress: Option<ProgressContext>,
261 buckets: &[FunctionBucket<'a>],
262) -> Result<Vec<C>, CompileError>
263where
264 T: FuncTranslator,
265 C: CompiledFunction + Send + Sync,
266 F: Fn() -> T + Send + Sync + Copy,
267 G: Fn(&mut T, &LocalFunctionIndex, &FunctionBodyData) -> Result<C, CompileError>
268 + Send
269 + Sync
270 + Copy,
271{
272 let progress = progress.as_ref();
273
274 let functions = pool.install(|| {
275 let (bucket_tx, bucket_rx) = unbounded::<&FunctionBucket<'a>>();
276 for bucket in buckets {
277 bucket_tx.send(bucket).map_err(|e| {
278 CompileError::Resource(format!("cannot allocate crossbeam channel item: {e}"))
279 })?;
280 }
281 drop(bucket_tx);
282
283 let (result_tx, result_rx) =
284 unbounded::<Result<Vec<(LocalFunctionIndex, C)>, CompileError>>();
285
286 pool.scope(|s| {
287 let worker_count = pool.current_num_threads().max(1);
288 for _ in 0..worker_count {
289 let bucket_rx = bucket_rx.clone();
290 let result_tx = result_tx.clone();
291 s.spawn(move |_| {
292 let mut func_translator = func_translator_builder();
293
294 while let Ok(bucket) = bucket_rx.recv() {
295 let bucket_result = (|| {
296 let mut translated_functions = Vec::new();
297 for (i, input) in bucket.functions.iter() {
298 let translated = translate_fn(&mut func_translator, i, input)?;
299 if let Some(progress) = progress {
300 progress.notify_steps(input.data.len() as u64)?;
301 }
302 translated_functions.push((*i, translated));
303 }
304 Ok(translated_functions)
305 })();
306
307 if result_tx.send(bucket_result).is_err() {
308 break;
309 }
310 }
311 });
312 }
313 });
314
315 drop(result_tx);
316 let mut functions = Vec::with_capacity(buckets.iter().map(|b| b.functions.len()).sum());
317 for _ in 0..buckets.len() {
318 match result_rx.recv().map_err(|e| {
319 CompileError::Resource(format!("cannot allocate crossbeam channel item: {e}"))
320 })? {
321 Ok(bucket_functions) => functions.extend(bucket_functions),
322 Err(err) => return Err(err),
323 }
324 }
325 Ok(functions)
326 })?;
327
328 Ok(functions
329 .into_iter()
330 .sorted_by_key(|x| x.0)
331 .map(|(_, body)| body)
332 .collect_vec())
333}
334
335pub const WASM_LARGE_FUNCTION_THRESHOLD: u64 = 100_000;
337
338pub const WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE: u64 = 1_000;