wasmer_compiler_cranelift/
config.rs

1use crate::compiler::CraneliftCompiler;
2use cranelift_codegen::{
3    CodegenResult,
4    isa::{TargetIsa, lookup},
5    settings::{self, Configurable},
6};
7use std::{
8    collections::HashMap,
9    fs::File,
10    io::{self, Write},
11    sync::Arc,
12};
13use std::{num::NonZero, path::PathBuf};
14use target_lexicon::OperatingSystem;
15use wasmer_compiler::{
16    Compiler, CompilerConfig, Engine, EngineBuilder, ModuleMiddleware,
17    misc::{CompiledKind, function_kind_to_filename, save_assembly_to_file},
18};
19use wasmer_types::{
20    Features,
21    target::{Architecture, CpuFeature, Target},
22};
23
24/// Callbacks to the different Cranelift compilation phases.
25#[derive(Debug, Clone)]
26pub struct CraneliftCallbacks {
27    debug_dir: PathBuf,
28}
29
30impl CraneliftCallbacks {
31    /// Creates a new instance of `CraneliftCallbacks` with the specified debug directory.
32    pub fn new(debug_dir: PathBuf) -> Result<Self, io::Error> {
33        // Create the debug dir in case it doesn't exist
34        std::fs::create_dir_all(&debug_dir)?;
35        Ok(Self { debug_dir })
36    }
37
38    fn base_path(&self, module_hash: &Option<String>) -> PathBuf {
39        let mut path = self.debug_dir.clone();
40        if let Some(hash) = module_hash {
41            path.push(hash);
42        }
43        std::fs::create_dir_all(&path)
44            .unwrap_or_else(|_| panic!("cannot create debug directory: {}", path.display()));
45        path
46    }
47
48    /// Writes the pre-optimization intermediate representation to a debug file.
49    pub fn preopt_ir(&self, kind: &CompiledKind, module_hash: &Option<String>, mem_buffer: &[u8]) {
50        let mut path = self.base_path(module_hash);
51        path.push(function_kind_to_filename(kind, ".preopt.clif"));
52        let mut file =
53            File::create(path).expect("Error while creating debug file from Cranelift IR");
54        file.write_all(mem_buffer).unwrap();
55    }
56
57    /// Writes the object file memory buffer to a debug file.
58    pub fn obj_memory_buffer(
59        &self,
60        kind: &CompiledKind,
61        module_hash: &Option<String>,
62        mem_buffer: &[u8],
63    ) {
64        let mut path = self.base_path(module_hash);
65        path.push(function_kind_to_filename(kind, ".o"));
66        let mut file =
67            File::create(path).expect("Error while creating debug file from Cranelift object");
68        file.write_all(mem_buffer).unwrap();
69    }
70
71    /// Writes the assembly memory buffer to a debug file.
72    pub fn asm_memory_buffer(
73        &self,
74        kind: &CompiledKind,
75        module_hash: &Option<String>,
76        arch: Architecture,
77        mem_buffer: &[u8],
78    ) -> Result<(), wasmer_types::CompileError> {
79        let mut path = self.base_path(module_hash);
80        path.push(function_kind_to_filename(kind, ".s"));
81        save_assembly_to_file(arch, path, mem_buffer, HashMap::<usize, String>::new())
82    }
83}
84
85// Runtime Environment
86
87/// Possible optimization levels for the Cranelift codegen backend.
88#[non_exhaustive]
89#[derive(Clone, Debug)]
90pub enum CraneliftOptLevel {
91    /// No optimizations performed, minimizes compilation time by disabling most
92    /// optimizations.
93    None,
94    /// Generates the fastest possible code, but may take longer.
95    Speed,
96    /// Similar to `speed`, but also performs transformations aimed at reducing
97    /// code size.
98    SpeedAndSize,
99}
100
101/// Global configuration options used to create an
102/// `wasmer_engine::Engine` and customize its behavior.
103///
104/// This structure exposes a builder-like interface and is primarily
105/// consumed by `wasmer_engine::Engine::new`.
106#[derive(Debug, Clone)]
107pub struct Cranelift {
108    enable_nan_canonicalization: bool,
109    pub(crate) allow_experimental_unaligned_memory_accesses: bool,
110    enable_verifier: bool,
111    pub(crate) enable_perfmap: bool,
112    enable_pic: bool,
113    opt_level: CraneliftOptLevel,
114    /// The number of threads to use for compilation.
115    pub num_threads: NonZero<usize>,
116    /// The middleware chain.
117    pub(crate) middlewares: Vec<Arc<dyn ModuleMiddleware>>,
118    pub(crate) callbacks: Option<CraneliftCallbacks>,
119}
120
121impl Cranelift {
122    /// Creates a new configuration object with the default configuration
123    /// specified.
124    pub fn new() -> Self {
125        Self {
126            enable_nan_canonicalization: false,
127            allow_experimental_unaligned_memory_accesses: false,
128            enable_verifier: false,
129            opt_level: CraneliftOptLevel::Speed,
130            enable_pic: false,
131            num_threads: std::thread::available_parallelism().unwrap_or(NonZero::new(1).unwrap()),
132            middlewares: vec![],
133            enable_perfmap: false,
134            callbacks: None,
135        }
136    }
137
138    /// Enable NaN canonicalization.
139    ///
140    /// NaN canonicalization is useful when trying to run WebAssembly
141    /// deterministically across different architectures.
142    pub fn canonicalize_nans(&mut self, enable: bool) -> &mut Self {
143        self.enable_nan_canonicalization = enable;
144        self
145    }
146
147    /// Enable run-time handling of potentially unaligned memory accesses.
148    /// Unaligned memory accesses occur when you try to read N bytes of data starting
149    /// from an address that is not evenly divisible by N.
150    ///
151    /// This feature is experimental and currently supports only scalar types.
152    pub fn allow_experimental_unaligned_memory_accesses(&mut self, enable: bool) -> &mut Self {
153        self.allow_experimental_unaligned_memory_accesses = enable;
154        self
155    }
156
157    /// Set the number of threads to use for compilation.
158    pub fn num_threads(&mut self, num_threads: NonZero<usize>) -> &mut Self {
159        self.num_threads = num_threads;
160        self
161    }
162
163    /// The optimization levels when optimizing the IR.
164    pub fn opt_level(&mut self, opt_level: CraneliftOptLevel) -> &mut Self {
165        self.opt_level = opt_level;
166        self
167    }
168
169    /// Generates the ISA for the provided target
170    pub fn isa(&self, target: &Target) -> CodegenResult<Arc<dyn TargetIsa>> {
171        let mut builder =
172            lookup(target.triple().clone()).expect("construct Cranelift ISA for triple");
173        // Cpu Features
174        let cpu_features = target.cpu_features();
175        if target.triple().architecture == Architecture::X86_64
176            && !cpu_features.contains(CpuFeature::SSE2)
177        {
178            panic!("x86 support requires SSE2");
179        }
180        if cpu_features.contains(CpuFeature::SSE3) {
181            builder.enable("has_sse3").expect("should be valid flag");
182        }
183        if cpu_features.contains(CpuFeature::SSSE3) {
184            builder.enable("has_ssse3").expect("should be valid flag");
185        }
186        if cpu_features.contains(CpuFeature::SSE41) {
187            builder.enable("has_sse41").expect("should be valid flag");
188        }
189        if cpu_features.contains(CpuFeature::SSE42) {
190            builder.enable("has_sse42").expect("should be valid flag");
191        }
192        if cpu_features.contains(CpuFeature::POPCNT) {
193            builder.enable("has_popcnt").expect("should be valid flag");
194        }
195        if cpu_features.contains(CpuFeature::AVX) {
196            builder.enable("has_avx").expect("should be valid flag");
197        }
198        if cpu_features.contains(CpuFeature::BMI1) {
199            builder.enable("has_bmi1").expect("should be valid flag");
200        }
201        if cpu_features.contains(CpuFeature::BMI2) {
202            builder.enable("has_bmi2").expect("should be valid flag");
203        }
204        if cpu_features.contains(CpuFeature::AVX2) {
205            builder.enable("has_avx2").expect("should be valid flag");
206        }
207        if cpu_features.contains(CpuFeature::AVX512DQ) {
208            builder
209                .enable("has_avx512dq")
210                .expect("should be valid flag");
211        }
212        if cpu_features.contains(CpuFeature::AVX512VL) {
213            builder
214                .enable("has_avx512vl")
215                .expect("should be valid flag");
216        }
217        if cpu_features.contains(CpuFeature::LZCNT) {
218            builder.enable("has_lzcnt").expect("should be valid flag");
219        }
220
221        builder.finish(self.flags())
222    }
223
224    /// Generates the flags for the compiler
225    pub fn flags(&self) -> settings::Flags {
226        let mut flags = settings::builder();
227
228        // Enable probestack
229        flags
230            .enable("enable_probestack")
231            .expect("should be valid flag");
232
233        // Always use inline stack probes (otherwise the call to Probestack needs to be relocated).
234        flags
235            .set("probestack_strategy", "inline")
236            .expect("should be valid flag");
237
238        if self.enable_pic {
239            flags.enable("is_pic").expect("should be a valid flag");
240        }
241
242        // These trampolines are always reachable through short jumps.
243        flags
244            .enable("use_colocated_libcalls")
245            .expect("should be a valid flag");
246
247        // Allow Cranelift to implicitly spill multi-value returns via a hidden
248        // StructReturn argument when register results are exhausted.
249        flags
250            .enable("enable_multi_ret_implicit_sret")
251            .expect("should be a valid flag");
252
253        // Invert cranelift's default-on verification to instead default off.
254        flags
255            .set("enable_verifier", &self.enable_verifier.to_string())
256            .expect("should be valid flag");
257
258        flags
259            .set(
260                "opt_level",
261                match self.opt_level {
262                    CraneliftOptLevel::None => "none",
263                    CraneliftOptLevel::Speed => "speed",
264                    CraneliftOptLevel::SpeedAndSize => "speed_and_size",
265                },
266            )
267            .expect("should be valid flag");
268
269        flags
270            .set(
271                "enable_nan_canonicalization",
272                &self.enable_nan_canonicalization.to_string(),
273            )
274            .expect("should be valid flag");
275
276        settings::Flags::new(flags)
277    }
278
279    /// Callbacks that will triggered in the different compilation
280    /// phases in Cranelift.
281    pub fn callbacks(&mut self, callbacks: Option<CraneliftCallbacks>) -> &mut Self {
282        self.callbacks = callbacks;
283        self
284    }
285}
286
287impl CompilerConfig for Cranelift {
288    fn enable_pic(&mut self) {
289        self.enable_pic = true;
290    }
291
292    fn enable_verifier(&mut self) {
293        self.enable_verifier = true;
294    }
295
296    fn enable_perfmap(&mut self) {
297        self.enable_perfmap = true;
298    }
299
300    fn enable_experimental_unaligned_memory_accesses(&mut self) {
301        self.allow_experimental_unaligned_memory_accesses = true;
302    }
303
304    fn canonicalize_nans(&mut self, enable: bool) {
305        self.enable_nan_canonicalization = enable;
306    }
307
308    /// Transform it into the compiler
309    fn compiler(self: Box<Self>) -> Box<dyn Compiler> {
310        Box::new(CraneliftCompiler::new(*self))
311    }
312
313    /// Pushes a middleware onto the back of the middleware chain.
314    fn push_middleware(&mut self, middleware: Arc<dyn ModuleMiddleware>) {
315        self.middlewares.push(middleware);
316    }
317
318    fn supported_features_for_target(&self, target: &Target) -> wasmer_types::Features {
319        let mut feats = Features::default();
320        if target.triple().operating_system == OperatingSystem::Linux {
321            feats.exceptions(true);
322        }
323        feats.relaxed_simd(true);
324        feats.wide_arithmetic(true);
325        feats
326    }
327}
328
329impl Default for Cranelift {
330    fn default() -> Self {
331        Self::new()
332    }
333}
334
335impl From<Cranelift> for Engine {
336    fn from(config: Cranelift) -> Self {
337        EngineBuilder::new(config).engine()
338    }
339}