wasmtime_winch/
compiler.rs

1use anyhow::Result;
2use cranelift_codegen::isa::unwind::UnwindInfoKind;
3use object::write::{Object, SymbolId};
4use std::any::Any;
5use std::mem;
6use std::sync::Mutex;
7use wasmparser::FuncValidatorAllocations;
8use wasmtime_cranelift::{CompiledFunction, ModuleTextBuilder};
9use wasmtime_environ::{
10    AddressMapSection, BuiltinFunctionIndex, CompileError, DefinedFuncIndex, FunctionBodyData,
11    FunctionLoc, ModuleTranslation, ModuleTypesBuilder, PrimaryMap, RelocationTarget,
12    StaticModuleIndex, TrapEncodingBuilder, Tunables, VMOffsets, WasmFunctionInfo,
13};
14use winch_codegen::{BuiltinFunctions, CallingConvention, TargetIsa};
15
16/// Function compilation context.
17/// This struct holds information that can be shared globally across
18/// all function compilations.
19struct CompilationContext {
20    /// Validator allocations.
21    allocations: FuncValidatorAllocations,
22    /// Builtin functions available to JIT code.
23    builtins: BuiltinFunctions,
24}
25
26pub(crate) struct Compiler {
27    isa: Box<dyn TargetIsa>,
28    trampolines: Box<dyn wasmtime_environ::Compiler>,
29    contexts: Mutex<Vec<CompilationContext>>,
30    tunables: Tunables,
31}
32
33impl Compiler {
34    pub fn new(
35        isa: Box<dyn TargetIsa>,
36        trampolines: Box<dyn wasmtime_environ::Compiler>,
37        tunables: Tunables,
38    ) -> Self {
39        Self {
40            isa,
41            trampolines,
42            contexts: Mutex::new(Vec::new()),
43            tunables,
44        }
45    }
46
47    /// Get a compilation context or create a new one if none available.
48    fn get_context(&self, translation: &ModuleTranslation) -> CompilationContext {
49        self.contexts.lock().unwrap().pop().unwrap_or_else(|| {
50            let pointer_size = self.isa.pointer_bytes();
51            let vmoffsets = VMOffsets::new(pointer_size, &translation.module);
52            CompilationContext {
53                allocations: Default::default(),
54                builtins: BuiltinFunctions::new(
55                    &vmoffsets,
56                    self.isa.wasmtime_call_conv(),
57                    CallingConvention::Default,
58                ),
59            }
60        })
61    }
62
63    /// Save a compilation context.
64    fn save_context(&self, mut context: CompilationContext, allocs: FuncValidatorAllocations) {
65        context.allocations = allocs;
66        self.contexts.lock().unwrap().push(context);
67    }
68
69    /// Emit unwind info into the [`CompiledFunction`].
70    fn emit_unwind_info(
71        &self,
72        compiled_function: &mut CompiledFunction,
73    ) -> Result<(), CompileError> {
74        let kind = match self.isa.triple().operating_system {
75            target_lexicon::OperatingSystem::Windows => UnwindInfoKind::Windows,
76            _ => UnwindInfoKind::SystemV,
77        };
78
79        if let Some(info) = self
80            .isa
81            .emit_unwind_info(&compiled_function.buffer, kind)
82            .map_err(|e| CompileError::Codegen(format!("{e:?}")))?
83        {
84            compiled_function.set_unwind_info(info);
85        }
86
87        Ok(())
88    }
89}
90
91impl wasmtime_environ::Compiler for Compiler {
92    fn compile_function(
93        &self,
94        translation: &ModuleTranslation<'_>,
95        index: DefinedFuncIndex,
96        data: FunctionBodyData<'_>,
97        types: &ModuleTypesBuilder,
98    ) -> Result<(WasmFunctionInfo, Box<dyn Any + Send>), CompileError> {
99        let index = translation.module.func_index(index);
100        let sig = translation.module.functions[index]
101            .signature
102            .unwrap_module_type_index();
103        let ty = types[sig].unwrap_func();
104        let FunctionBodyData {
105            body, validator, ..
106        } = data;
107        let mut context = self.get_context(translation);
108        let mut validator = validator.into_validator(mem::take(&mut context.allocations));
109        let func = self
110            .isa
111            .compile_function(
112                ty,
113                &body,
114                translation,
115                types,
116                &mut context.builtins,
117                &mut validator,
118                &self.tunables,
119            )
120            .map_err(|e| CompileError::Codegen(format!("{e:?}")));
121        self.save_context(context, validator.into_allocations());
122        let mut func = func?;
123
124        let reader = body.get_binary_reader();
125        func.set_address_map(
126            reader.original_position() as u32,
127            reader.bytes_remaining() as u32,
128            self.tunables.generate_address_map,
129        );
130
131        if self.isa.flags().unwind_info() {
132            self.emit_unwind_info(&mut func)?;
133        }
134
135        Ok((
136            WasmFunctionInfo {
137                start_srcloc: func.metadata().address_map.start_srcloc,
138                stack_maps: Box::new([]),
139            },
140            Box::new(func),
141        ))
142    }
143
144    fn compile_array_to_wasm_trampoline(
145        &self,
146        translation: &ModuleTranslation<'_>,
147        types: &ModuleTypesBuilder,
148        index: DefinedFuncIndex,
149    ) -> Result<Box<dyn Any + Send>, CompileError> {
150        self.trampolines
151            .compile_array_to_wasm_trampoline(translation, types, index)
152    }
153
154    fn compile_wasm_to_array_trampoline(
155        &self,
156        wasm_func_ty: &wasmtime_environ::WasmFuncType,
157    ) -> Result<Box<dyn Any + Send>, CompileError> {
158        self.trampolines
159            .compile_wasm_to_array_trampoline(wasm_func_ty)
160    }
161
162    fn append_code(
163        &self,
164        obj: &mut Object<'static>,
165        funcs: &[(String, Box<dyn Any + Send>)],
166        resolve_reloc: &dyn Fn(usize, wasmtime_environ::RelocationTarget) -> usize,
167    ) -> Result<Vec<(SymbolId, FunctionLoc)>> {
168        let mut builder =
169            ModuleTextBuilder::new(obj, self, self.isa.text_section_builder(funcs.len()));
170        let mut traps = TrapEncodingBuilder::default();
171        let mut addrs = AddressMapSection::default();
172
173        let mut ret = Vec::with_capacity(funcs.len());
174        for (i, (sym, func)) in funcs.iter().enumerate() {
175            let func = func.downcast_ref::<CompiledFunction>().unwrap();
176
177            let (sym, range) = builder.append_func(&sym, func, |idx| resolve_reloc(i, idx));
178            if self.tunables.generate_address_map {
179                addrs.push(range.clone(), &func.address_map().instructions);
180            }
181            traps.push(range.clone(), &func.traps().collect::<Vec<_>>());
182
183            let info = FunctionLoc {
184                start: u32::try_from(range.start).unwrap(),
185                length: u32::try_from(range.end - range.start).unwrap(),
186            };
187            ret.push((sym, info));
188        }
189        builder.finish();
190        if self.tunables.generate_address_map {
191            addrs.append_to(obj);
192        }
193        traps.append_to(obj);
194        Ok(ret)
195    }
196
197    fn triple(&self) -> &target_lexicon::Triple {
198        self.isa.triple()
199    }
200
201    fn flags(&self) -> Vec<(&'static str, wasmtime_environ::FlagValue<'static>)> {
202        wasmtime_cranelift::clif_flags_to_wasmtime(self.isa.flags().iter())
203    }
204
205    fn isa_flags(&self) -> Vec<(&'static str, wasmtime_environ::FlagValue<'static>)> {
206        wasmtime_cranelift::clif_flags_to_wasmtime(self.isa.isa_flags())
207    }
208
209    fn is_branch_protection_enabled(&self) -> bool {
210        self.isa.is_branch_protection_enabled()
211    }
212
213    #[cfg(feature = "component-model")]
214    fn component_compiler(&self) -> &dyn wasmtime_environ::component::ComponentCompiler {
215        self.trampolines.component_compiler()
216    }
217
218    fn append_dwarf<'a>(
219        &self,
220        _obj: &mut Object<'_>,
221        _translations: &'a PrimaryMap<StaticModuleIndex, ModuleTranslation<'a>>,
222        _get_func: &'a dyn Fn(
223            StaticModuleIndex,
224            DefinedFuncIndex,
225        ) -> (SymbolId, &'a (dyn Any + Send)),
226        _dwarf_package_bytes: Option<&'a [u8]>,
227        _tunables: &'a Tunables,
228    ) -> Result<()> {
229        todo!()
230    }
231
232    fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
233        self.isa.create_systemv_cie()
234    }
235
236    fn compile_wasm_to_builtin(
237        &self,
238        index: BuiltinFunctionIndex,
239    ) -> Result<Box<dyn Any + Send>, CompileError> {
240        self.trampolines.compile_wasm_to_builtin(index)
241    }
242
243    fn compiled_function_relocation_targets<'a>(
244        &'a self,
245        func: &'a dyn Any,
246    ) -> Box<dyn Iterator<Item = RelocationTarget> + 'a> {
247        self.trampolines.compiled_function_relocation_targets(func)
248    }
249}