/src/wasmtime/crates/winch/src/compiler.rs
Line | Count | Source |
1 | | use anyhow::Result; |
2 | | use cranelift_codegen::isa::unwind::UnwindInfoKind; |
3 | | use object::write::{Object, SymbolId}; |
4 | | use std::any::Any; |
5 | | use std::mem; |
6 | | use std::sync::Mutex; |
7 | | use wasmparser::FuncValidatorAllocations; |
8 | | use wasmtime_cranelift::CompiledFunction; |
9 | | #[cfg(feature = "component-model")] |
10 | | use wasmtime_environ::component::ComponentTranslation; |
11 | | use wasmtime_environ::{ |
12 | | CompileError, CompiledFunctionBody, DefinedFuncIndex, FuncKey, FunctionBodyData, FunctionLoc, |
13 | | ModuleTranslation, ModuleTypesBuilder, PrimaryMap, StaticModuleIndex, Tunables, VMOffsets, |
14 | | }; |
15 | | use winch_codegen::{BuiltinFunctions, CallingConvention, TargetIsa}; |
16 | | |
17 | | /// Function compilation context. |
18 | | /// This struct holds information that can be shared globally across |
19 | | /// all function compilations. |
20 | | struct CompilationContext { |
21 | | /// Validator allocations. |
22 | | allocations: FuncValidatorAllocations, |
23 | | /// Builtin functions available to JIT code. |
24 | | builtins: BuiltinFunctions, |
25 | | } |
26 | | |
27 | | pub(crate) struct Compiler { |
28 | | isa: Box<dyn TargetIsa>, |
29 | | trampolines: NoInlineCompiler, |
30 | | contexts: Mutex<Vec<CompilationContext>>, |
31 | | tunables: Tunables, |
32 | | } |
33 | | |
34 | | impl Compiler { |
35 | 5.83k | pub fn new( |
36 | 5.83k | isa: Box<dyn TargetIsa>, |
37 | 5.83k | trampolines: Box<dyn wasmtime_environ::Compiler>, |
38 | 5.83k | tunables: Tunables, |
39 | 5.83k | ) -> Self { |
40 | 5.83k | Self { |
41 | 5.83k | isa, |
42 | 5.83k | trampolines: NoInlineCompiler(trampolines), |
43 | 5.83k | contexts: Mutex::new(Vec::new()), |
44 | 5.83k | tunables, |
45 | 5.83k | } |
46 | 5.83k | } |
47 | | |
48 | | /// Get a compilation context or create a new one if none available. |
49 | 75.0k | fn get_context(&self, translation: &ModuleTranslation) -> CompilationContext { |
50 | 75.0k | self.contexts.lock().unwrap().pop().unwrap_or_else(|| { |
51 | 5.39k | let pointer_size = self.isa.pointer_bytes(); |
52 | 5.39k | let vmoffsets = VMOffsets::new(pointer_size, &translation.module); |
53 | 5.39k | CompilationContext { |
54 | 5.39k | allocations: Default::default(), |
55 | 5.39k | builtins: BuiltinFunctions::new( |
56 | 5.39k | &vmoffsets, |
57 | 5.39k | self.isa.wasmtime_call_conv(), |
58 | 5.39k | CallingConvention::Default, |
59 | 5.39k | ), |
60 | 5.39k | } |
61 | 5.39k | }) |
62 | 75.0k | } |
63 | | |
64 | | /// Save a compilation context. |
65 | 75.0k | fn save_context(&self, mut context: CompilationContext, allocs: FuncValidatorAllocations) { |
66 | 75.0k | context.allocations = allocs; |
67 | 75.0k | self.contexts.lock().unwrap().push(context); |
68 | 75.0k | } |
69 | | |
70 | | /// Emit unwind info into the [`CompiledFunction`]. |
71 | 30.2k | fn emit_unwind_info( |
72 | 30.2k | &self, |
73 | 30.2k | compiled_function: &mut CompiledFunction, |
74 | 30.2k | ) -> Result<(), CompileError> { |
75 | 30.2k | let kind = match self.isa.triple().operating_system { |
76 | 0 | target_lexicon::OperatingSystem::Windows => UnwindInfoKind::Windows, |
77 | 30.2k | _ => UnwindInfoKind::SystemV, |
78 | | }; |
79 | | |
80 | 30.2k | if let Some(info) = self |
81 | 30.2k | .isa |
82 | 30.2k | .emit_unwind_info(&compiled_function.buffer, kind) |
83 | 30.2k | .map_err(|e| CompileError::Codegen(format!("{e:?}")))? |
84 | 30.2k | { |
85 | 30.2k | compiled_function.set_unwind_info(info); |
86 | 30.2k | } |
87 | | |
88 | 30.2k | Ok(()) |
89 | 30.2k | } |
90 | | } |
91 | | |
92 | 75.0k | fn box_dyn_any_compiled_function(f: CompiledFunction) -> Box<dyn Any + Send + Sync> { |
93 | 75.0k | let b = box_dyn_any(f); |
94 | 75.0k | debug_assert!(b.is::<CompiledFunction>()); |
95 | 75.0k | b |
96 | 75.0k | } |
97 | | |
98 | 75.0k | fn box_dyn_any(x: impl Any + Send + Sync) -> Box<dyn Any + Send + Sync> { |
99 | 75.0k | log::trace!( |
100 | 0 | "making Box<dyn Any + Send + Sync> of {}", |
101 | 0 | std::any::type_name_of_val(&x) |
102 | | ); |
103 | 75.0k | let b = Box::new(x); |
104 | 75.0k | let r: &(dyn Any + Sync + Send) = &*b; |
105 | 75.0k | log::trace!(" --> {r:#p}"); |
106 | 75.0k | b |
107 | 75.0k | } |
108 | | |
109 | | impl wasmtime_environ::Compiler for Compiler { |
110 | 32.1k | fn inlining_compiler(&self) -> Option<&dyn wasmtime_environ::InliningCompiler> { |
111 | 32.1k | None |
112 | 32.1k | } |
113 | | |
114 | 75.0k | fn compile_function( |
115 | 75.0k | &self, |
116 | 75.0k | translation: &ModuleTranslation<'_>, |
117 | 75.0k | key: FuncKey, |
118 | 75.0k | data: FunctionBodyData<'_>, |
119 | 75.0k | types: &ModuleTypesBuilder, |
120 | 75.0k | symbol: &str, |
121 | 75.0k | ) -> Result<CompiledFunctionBody, CompileError> { |
122 | 75.0k | log::trace!("compiling function: {key:?} = {symbol:?}"); |
123 | | |
124 | 75.0k | let (module_index, def_func_index) = key.unwrap_defined_wasm_function(); |
125 | 75.0k | debug_assert_eq!(module_index, translation.module_index()); |
126 | | |
127 | 75.0k | let index = translation.module.func_index(def_func_index); |
128 | 75.0k | let sig = translation.module.functions[index] |
129 | 75.0k | .signature |
130 | 75.0k | .unwrap_module_type_index(); |
131 | 75.0k | let ty = types[sig].unwrap_func(); |
132 | | let FunctionBodyData { |
133 | 75.0k | body, validator, .. |
134 | 75.0k | } = data; |
135 | 75.0k | let mut context = self.get_context(translation); |
136 | 75.0k | let mut validator = validator.into_validator(mem::take(&mut context.allocations)); |
137 | 75.0k | let func = self |
138 | 75.0k | .isa |
139 | 75.0k | .compile_function( |
140 | 75.0k | ty, |
141 | 75.0k | &body, |
142 | 75.0k | translation, |
143 | 75.0k | types, |
144 | 75.0k | &mut context.builtins, |
145 | 75.0k | &mut validator, |
146 | 75.0k | &self.tunables, |
147 | | ) |
148 | 75.0k | .map_err(|e| CompileError::Codegen(format!("{e:?}"))); |
149 | 75.0k | self.save_context(context, validator.into_allocations()); |
150 | 75.0k | let mut func = func?; |
151 | | |
152 | 75.0k | let reader = body.get_binary_reader(); |
153 | 75.0k | func.set_address_map( |
154 | 75.0k | reader.original_position() as u32, |
155 | 75.0k | reader.bytes_remaining() as u32, |
156 | 75.0k | self.tunables.generate_address_map, |
157 | | ); |
158 | | |
159 | 75.0k | if self.isa.flags().unwind_info() { |
160 | 30.2k | self.emit_unwind_info(&mut func)?; |
161 | 44.8k | } |
162 | | |
163 | 75.0k | Ok(CompiledFunctionBody { |
164 | 75.0k | code: box_dyn_any_compiled_function(func), |
165 | 75.0k | // TODO: Winch doesn't support GC objects and stack maps and all that yet. |
166 | 75.0k | needs_gc_heap: false, |
167 | 75.0k | }) |
168 | 75.0k | } |
169 | | |
170 | 12.1k | fn compile_array_to_wasm_trampoline( |
171 | 12.1k | &self, |
172 | 12.1k | translation: &ModuleTranslation<'_>, |
173 | 12.1k | types: &ModuleTypesBuilder, |
174 | 12.1k | key: FuncKey, |
175 | 12.1k | symbol: &str, |
176 | 12.1k | ) -> Result<CompiledFunctionBody, CompileError> { |
177 | 12.1k | self.trampolines |
178 | 12.1k | .compile_array_to_wasm_trampoline(translation, types, key, symbol) |
179 | 12.1k | } |
180 | | |
181 | 15.9k | fn compile_wasm_to_array_trampoline( |
182 | 15.9k | &self, |
183 | 15.9k | wasm_func_ty: &wasmtime_environ::WasmFuncType, |
184 | 15.9k | key: FuncKey, |
185 | 15.9k | symbol: &str, |
186 | 15.9k | ) -> Result<CompiledFunctionBody, CompileError> { |
187 | 15.9k | self.trampolines |
188 | 15.9k | .compile_wasm_to_array_trampoline(wasm_func_ty, key, symbol) |
189 | 15.9k | } |
190 | | |
191 | 24.5k | fn append_code( |
192 | 24.5k | &self, |
193 | 24.5k | obj: &mut Object<'static>, |
194 | 24.5k | funcs: &[(String, FuncKey, Box<dyn Any + Send + Sync>)], |
195 | 24.5k | resolve_reloc: &dyn Fn(usize, wasmtime_environ::FuncKey) -> usize, |
196 | 24.5k | ) -> Result<Vec<(SymbolId, FunctionLoc)>> { |
197 | 24.5k | self.trampolines.append_code(obj, funcs, resolve_reloc) |
198 | 24.5k | } |
199 | | |
200 | 82.6k | fn triple(&self) -> &target_lexicon::Triple { |
201 | 82.6k | self.isa.triple() |
202 | 82.6k | } |
203 | | |
204 | 30.4k | fn flags(&self) -> Vec<(&'static str, wasmtime_environ::FlagValue<'static>)> { |
205 | 30.4k | wasmtime_cranelift::clif_flags_to_wasmtime(self.isa.flags().iter()) |
206 | 30.4k | } |
207 | | |
208 | 30.4k | fn isa_flags(&self) -> Vec<(&'static str, wasmtime_environ::FlagValue<'static>)> { |
209 | 30.4k | wasmtime_cranelift::clif_flags_to_wasmtime(self.isa.isa_flags()) |
210 | 30.4k | } |
211 | | |
212 | 24.5k | fn is_branch_protection_enabled(&self) -> bool { |
213 | 24.5k | self.isa.is_branch_protection_enabled() |
214 | 24.5k | } |
215 | | |
216 | | #[cfg(feature = "component-model")] |
217 | 2 | fn component_compiler(&self) -> &dyn wasmtime_environ::component::ComponentCompiler { |
218 | 2 | self.trampolines.component_compiler() |
219 | 2 | } |
220 | | |
221 | 0 | fn append_dwarf<'a>( |
222 | 0 | &self, |
223 | 0 | _obj: &mut Object<'_>, |
224 | 0 | _translations: &'a PrimaryMap<StaticModuleIndex, ModuleTranslation<'a>>, |
225 | 0 | _get_func: &'a dyn Fn( |
226 | 0 | StaticModuleIndex, |
227 | 0 | DefinedFuncIndex, |
228 | 0 | ) -> (SymbolId, &'a (dyn Any + Send + Sync)), |
229 | 0 | _dwarf_package_bytes: Option<&'a [u8]>, |
230 | 0 | _tunables: &'a Tunables, |
231 | 0 | ) -> Result<()> { |
232 | 0 | todo!() |
233 | | } |
234 | | |
235 | 0 | fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> { |
236 | 0 | self.isa.create_systemv_cie() |
237 | 0 | } |
238 | | |
239 | 7.47k | fn compile_wasm_to_builtin( |
240 | 7.47k | &self, |
241 | 7.47k | key: FuncKey, |
242 | 7.47k | symbol: &str, |
243 | 7.47k | ) -> Result<CompiledFunctionBody, CompileError> { |
244 | 7.47k | self.trampolines.compile_wasm_to_builtin(key, symbol) |
245 | 7.47k | } |
246 | | |
247 | 103k | fn compiled_function_relocation_targets<'a>( |
248 | 103k | &'a self, |
249 | 103k | func: &'a dyn Any, |
250 | 103k | ) -> Box<dyn Iterator<Item = FuncKey> + 'a> { |
251 | 103k | self.trampolines.compiled_function_relocation_targets(func) |
252 | 103k | } |
253 | | } |
254 | | |
255 | | /// A wrapper around another `Compiler` implementation that may or may not be an |
256 | | /// inlining compiler and turns it into a non-inlining compiler. |
257 | | struct NoInlineCompiler(Box<dyn wasmtime_environ::Compiler>); |
258 | | |
259 | | impl wasmtime_environ::Compiler for NoInlineCompiler { |
260 | 0 | fn inlining_compiler(&self) -> Option<&dyn wasmtime_environ::InliningCompiler> { |
261 | 0 | None |
262 | 0 | } |
263 | | |
264 | 0 | fn compile_function( |
265 | 0 | &self, |
266 | 0 | translation: &ModuleTranslation<'_>, |
267 | 0 | key: FuncKey, |
268 | 0 | data: FunctionBodyData<'_>, |
269 | 0 | types: &ModuleTypesBuilder, |
270 | 0 | symbol: &str, |
271 | 0 | ) -> Result<CompiledFunctionBody, CompileError> { |
272 | 0 | let input = data.body.clone(); |
273 | 0 | let mut body = self |
274 | 0 | .0 |
275 | 0 | .compile_function(translation, key, data, types, symbol)?; |
276 | 0 | if let Some(c) = self.0.inlining_compiler() { |
277 | 0 | c.finish_compiling(&mut body, Some(input), symbol) |
278 | 0 | .map_err(|e| CompileError::Codegen(e.to_string()))?; |
279 | 0 | } |
280 | 0 | Ok(body) |
281 | 0 | } |
282 | | |
283 | 12.1k | fn compile_array_to_wasm_trampoline( |
284 | 12.1k | &self, |
285 | 12.1k | translation: &ModuleTranslation<'_>, |
286 | 12.1k | types: &ModuleTypesBuilder, |
287 | 12.1k | key: FuncKey, |
288 | 12.1k | symbol: &str, |
289 | 12.1k | ) -> Result<CompiledFunctionBody, CompileError> { |
290 | 12.1k | let mut body = self |
291 | 12.1k | .0 |
292 | 12.1k | .compile_array_to_wasm_trampoline(translation, types, key, symbol)?; |
293 | 12.1k | if let Some(c) = self.0.inlining_compiler() { |
294 | 12.1k | c.finish_compiling(&mut body, None, symbol) |
295 | 12.1k | .map_err(|e| CompileError::Codegen(e.to_string()))?; |
296 | 0 | } |
297 | 12.1k | Ok(body) |
298 | 12.1k | } |
299 | | |
300 | 15.9k | fn compile_wasm_to_array_trampoline( |
301 | 15.9k | &self, |
302 | 15.9k | wasm_func_ty: &wasmtime_environ::WasmFuncType, |
303 | 15.9k | key: FuncKey, |
304 | 15.9k | symbol: &str, |
305 | 15.9k | ) -> Result<CompiledFunctionBody, CompileError> { |
306 | 15.9k | let mut body = self |
307 | 15.9k | .0 |
308 | 15.9k | .compile_wasm_to_array_trampoline(wasm_func_ty, key, symbol)?; |
309 | 15.9k | if let Some(c) = self.0.inlining_compiler() { |
310 | 15.9k | c.finish_compiling(&mut body, None, symbol) |
311 | 15.9k | .map_err(|e| CompileError::Codegen(e.to_string()))?; |
312 | 0 | } |
313 | 15.9k | Ok(body) |
314 | 15.9k | } |
315 | | |
316 | 7.47k | fn compile_wasm_to_builtin( |
317 | 7.47k | &self, |
318 | 7.47k | key: FuncKey, |
319 | 7.47k | symbol: &str, |
320 | 7.47k | ) -> Result<CompiledFunctionBody, CompileError> { |
321 | 7.47k | let mut body = self.0.compile_wasm_to_builtin(key, symbol)?; |
322 | 7.47k | if let Some(c) = self.0.inlining_compiler() { |
323 | 7.47k | c.finish_compiling(&mut body, None, symbol) |
324 | 7.47k | .map_err(|e| CompileError::Codegen(e.to_string()))?; |
325 | 0 | } |
326 | 7.47k | Ok(body) |
327 | 7.47k | } |
328 | | |
329 | 103k | fn compiled_function_relocation_targets<'a>( |
330 | 103k | &'a self, |
331 | 103k | func: &'a dyn Any, |
332 | 103k | ) -> Box<dyn Iterator<Item = FuncKey> + 'a> { |
333 | 103k | self.0.compiled_function_relocation_targets(func) |
334 | 103k | } |
335 | | |
336 | 24.5k | fn append_code( |
337 | 24.5k | &self, |
338 | 24.5k | obj: &mut Object<'static>, |
339 | 24.5k | funcs: &[(String, FuncKey, Box<dyn Any + Send + Sync>)], |
340 | 24.5k | resolve_reloc: &dyn Fn(usize, FuncKey) -> usize, |
341 | 24.5k | ) -> Result<Vec<(SymbolId, FunctionLoc)>> { |
342 | 24.5k | self.0.append_code(obj, funcs, resolve_reloc) |
343 | 24.5k | } |
344 | | |
345 | 0 | fn triple(&self) -> &target_lexicon::Triple { |
346 | 0 | self.0.triple() |
347 | 0 | } |
348 | | |
349 | 0 | fn flags(&self) -> Vec<(&'static str, wasmtime_environ::FlagValue<'static>)> { |
350 | 0 | self.0.flags() |
351 | 0 | } |
352 | | |
353 | 0 | fn isa_flags(&self) -> Vec<(&'static str, wasmtime_environ::FlagValue<'static>)> { |
354 | 0 | self.0.isa_flags() |
355 | 0 | } |
356 | | |
357 | 0 | fn is_branch_protection_enabled(&self) -> bool { |
358 | 0 | self.0.is_branch_protection_enabled() |
359 | 0 | } |
360 | | |
361 | | #[cfg(feature = "component-model")] |
362 | 2 | fn component_compiler(&self) -> &dyn wasmtime_environ::component::ComponentCompiler { |
363 | 2 | self |
364 | 2 | } |
365 | | |
366 | 0 | fn append_dwarf<'a>( |
367 | 0 | &self, |
368 | 0 | obj: &mut Object<'_>, |
369 | 0 | translations: &'a PrimaryMap<StaticModuleIndex, ModuleTranslation<'a>>, |
370 | 0 | get_func: &'a dyn Fn( |
371 | 0 | StaticModuleIndex, |
372 | 0 | DefinedFuncIndex, |
373 | 0 | ) -> (SymbolId, &'a (dyn Any + Send + Sync)), |
374 | 0 | dwarf_package_bytes: Option<&'a [u8]>, |
375 | 0 | tunables: &'a Tunables, |
376 | 0 | ) -> Result<()> { |
377 | 0 | self.0 |
378 | 0 | .append_dwarf(obj, translations, get_func, dwarf_package_bytes, tunables) |
379 | 0 | } |
380 | | } |
381 | | |
382 | | #[cfg(feature = "component-model")] |
383 | | impl wasmtime_environ::component::ComponentCompiler for NoInlineCompiler { |
384 | 2 | fn compile_trampoline( |
385 | 2 | &self, |
386 | 2 | component: &wasmtime_environ::component::ComponentTranslation, |
387 | 2 | types: &wasmtime_environ::component::ComponentTypesBuilder, |
388 | 2 | key: FuncKey, |
389 | 2 | abi: wasmtime_environ::Abi, |
390 | 2 | tunables: &Tunables, |
391 | 2 | symbol: &str, |
392 | 2 | ) -> Result<CompiledFunctionBody> { |
393 | 2 | let mut body = self |
394 | 2 | .0 |
395 | 2 | .component_compiler() |
396 | 2 | .compile_trampoline(component, types, key, abi, tunables, symbol)?; |
397 | 2 | if let Some(c) = self.0.inlining_compiler() { |
398 | 2 | c.finish_compiling(&mut body, None, symbol) |
399 | 2 | .map_err(|e| CompileError::Codegen(e.to_string()))?; |
400 | 0 | } |
401 | 2 | Ok(body) |
402 | 2 | } |
403 | | |
404 | 0 | fn compile_intrinsic( |
405 | 0 | &self, |
406 | 0 | tunables: &Tunables, |
407 | 0 | component: &ComponentTranslation, |
408 | 0 | types: &wasmtime_environ::component::ComponentTypesBuilder, |
409 | 0 | intrinsic: wasmtime_environ::component::UnsafeIntrinsic, |
410 | 0 | abi: wasmtime_environ::Abi, |
411 | 0 | symbol: &str, |
412 | 0 | ) -> Result<CompiledFunctionBody> { |
413 | 0 | let mut body = self |
414 | 0 | .0 |
415 | 0 | .component_compiler() |
416 | 0 | .compile_intrinsic(tunables, component, types, intrinsic, abi, symbol)?; |
417 | 0 | if let Some(c) = self.0.inlining_compiler() { |
418 | 0 | c.finish_compiling(&mut body, None, symbol) |
419 | 0 | .map_err(|e| CompileError::Codegen(e.to_string()))?; |
420 | 0 | } |
421 | 0 | Ok(body) |
422 | 0 | } |
423 | | } |