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::num::NonZero;
8use std::sync::Arc;
9use wasmer_compiler::{Compiler, CompilerConfig, Engine, EngineBuilder, ModuleMiddleware};
10use wasmer_types::target::{Architecture, CpuFeature, Target};
11
12// Runtime Environment
13
14/// Possible optimization levels for the Cranelift codegen backend.
15#[non_exhaustive]
16#[derive(Clone, Debug)]
17pub enum CraneliftOptLevel {
18    /// No optimizations performed, minimizes compilation time by disabling most
19    /// optimizations.
20    None,
21    /// Generates the fastest possible code, but may take longer.
22    Speed,
23    /// Similar to `speed`, but also performs transformations aimed at reducing
24    /// code size.
25    SpeedAndSize,
26}
27
28/// Global configuration options used to create an
29/// `wasmer_engine::Engine` and customize its behavior.
30///
31/// This structure exposes a builder-like interface and is primarily
32/// consumed by `wasmer_engine::Engine::new`.
33#[derive(Debug, Clone)]
34pub struct Cranelift {
35    enable_nan_canonicalization: bool,
36    enable_verifier: bool,
37    pub(crate) enable_perfmap: bool,
38    enable_pic: bool,
39    opt_level: CraneliftOptLevel,
40    /// The number of threads to use for compilation.
41    pub num_threads: NonZero<usize>,
42    /// The middleware chain.
43    pub(crate) middlewares: Vec<Arc<dyn ModuleMiddleware>>,
44}
45
46impl Cranelift {
47    /// Creates a new configuration object with the default configuration
48    /// specified.
49    pub fn new() -> Self {
50        Self {
51            enable_nan_canonicalization: false,
52            enable_verifier: false,
53            opt_level: CraneliftOptLevel::Speed,
54            enable_pic: false,
55            num_threads: std::thread::available_parallelism().unwrap_or(NonZero::new(1).unwrap()),
56            middlewares: vec![],
57            enable_perfmap: false,
58        }
59    }
60
61    /// Enable NaN canonicalization.
62    ///
63    /// NaN canonicalization is useful when trying to run WebAssembly
64    /// deterministically across different architectures.
65    pub fn canonicalize_nans(&mut self, enable: bool) -> &mut Self {
66        self.enable_nan_canonicalization = enable;
67        self
68    }
69
70    /// Set the number of threads to use for compilation.
71    pub fn num_threads(&mut self, num_threads: NonZero<usize>) -> &mut Self {
72        self.num_threads = num_threads;
73        self
74    }
75
76    /// The optimization levels when optimizing the IR.
77    pub fn opt_level(&mut self, opt_level: CraneliftOptLevel) -> &mut Self {
78        self.opt_level = opt_level;
79        self
80    }
81
82    /// Generates the ISA for the provided target
83    pub fn isa(&self, target: &Target) -> CodegenResult<Arc<dyn TargetIsa>> {
84        let mut builder =
85            lookup(target.triple().clone()).expect("construct Cranelift ISA for triple");
86        // Cpu Features
87        let cpu_features = target.cpu_features();
88        if target.triple().architecture == Architecture::X86_64
89            && !cpu_features.contains(CpuFeature::SSE2)
90        {
91            panic!("x86 support requires SSE2");
92        }
93        if cpu_features.contains(CpuFeature::SSE3) {
94            builder.enable("has_sse3").expect("should be valid flag");
95        }
96        if cpu_features.contains(CpuFeature::SSSE3) {
97            builder.enable("has_ssse3").expect("should be valid flag");
98        }
99        if cpu_features.contains(CpuFeature::SSE41) {
100            builder.enable("has_sse41").expect("should be valid flag");
101        }
102        if cpu_features.contains(CpuFeature::SSE42) {
103            builder.enable("has_sse42").expect("should be valid flag");
104        }
105        if cpu_features.contains(CpuFeature::POPCNT) {
106            builder.enable("has_popcnt").expect("should be valid flag");
107        }
108        if cpu_features.contains(CpuFeature::AVX) {
109            builder.enable("has_avx").expect("should be valid flag");
110        }
111        if cpu_features.contains(CpuFeature::BMI1) {
112            builder.enable("has_bmi1").expect("should be valid flag");
113        }
114        if cpu_features.contains(CpuFeature::BMI2) {
115            builder.enable("has_bmi2").expect("should be valid flag");
116        }
117        if cpu_features.contains(CpuFeature::AVX2) {
118            builder.enable("has_avx2").expect("should be valid flag");
119        }
120        if cpu_features.contains(CpuFeature::AVX512DQ) {
121            builder
122                .enable("has_avx512dq")
123                .expect("should be valid flag");
124        }
125        if cpu_features.contains(CpuFeature::AVX512VL) {
126            builder
127                .enable("has_avx512vl")
128                .expect("should be valid flag");
129        }
130        if cpu_features.contains(CpuFeature::LZCNT) {
131            builder.enable("has_lzcnt").expect("should be valid flag");
132        }
133
134        builder.finish(self.flags(target))
135    }
136
137    /// Generates the flags for the compiler
138    pub fn flags(&self, target: &Target) -> settings::Flags {
139        let mut flags = settings::builder();
140
141        // Enable probestack
142        flags
143            .enable("enable_probestack")
144            .expect("should be valid flag");
145
146        // Only inline probestack is supported on AArch64
147        if matches!(target.triple().architecture, Architecture::Aarch64(_)) {
148            flags
149                .set("probestack_strategy", "inline")
150                .expect("should be valid flag");
151        }
152
153        if self.enable_pic {
154            flags.enable("is_pic").expect("should be a valid flag");
155        }
156
157        // We set up libcall trampolines in engine-universal.
158        // These trampolines are always reachable through short jumps.
159        flags
160            .enable("use_colocated_libcalls")
161            .expect("should be a valid flag");
162
163        // Allow Cranelift to implicitly spill multi-value returns via a hidden
164        // StructReturn argument when register results are exhausted.
165        flags
166            .enable("enable_multi_ret_implicit_sret")
167            .expect("should be a valid flag");
168
169        // Invert cranelift's default-on verification to instead default off.
170        let enable_verifier = if self.enable_verifier {
171            "true"
172        } else {
173            "false"
174        };
175        flags
176            .set("enable_verifier", enable_verifier)
177            .expect("should be valid flag");
178        flags
179            .set("enable_safepoints", "true")
180            .expect("should be valid flag");
181
182        flags
183            .set(
184                "opt_level",
185                match self.opt_level {
186                    CraneliftOptLevel::None => "none",
187                    CraneliftOptLevel::Speed => "speed",
188                    CraneliftOptLevel::SpeedAndSize => "speed_and_size",
189                },
190            )
191            .expect("should be valid flag");
192
193        let enable_nan_canonicalization = if self.enable_nan_canonicalization {
194            "true"
195        } else {
196            "false"
197        };
198        flags
199            .set("enable_nan_canonicalization", enable_nan_canonicalization)
200            .expect("should be valid flag");
201
202        settings::Flags::new(flags)
203    }
204}
205
206impl CompilerConfig for Cranelift {
207    fn enable_pic(&mut self) {
208        self.enable_pic = true;
209    }
210
211    fn enable_verifier(&mut self) {
212        self.enable_verifier = true;
213    }
214
215    fn enable_perfmap(&mut self) {
216        self.enable_perfmap = true;
217    }
218
219    fn canonicalize_nans(&mut self, enable: bool) {
220        self.enable_nan_canonicalization = enable;
221    }
222
223    /// Transform it into the compiler
224    fn compiler(self: Box<Self>) -> Box<dyn Compiler> {
225        Box::new(CraneliftCompiler::new(*self))
226    }
227
228    /// Pushes a middleware onto the back of the middleware chain.
229    fn push_middleware(&mut self, middleware: Arc<dyn ModuleMiddleware>) {
230        self.middlewares.push(middleware);
231    }
232}
233
234impl Default for Cranelift {
235    fn default() -> Self {
236        Self::new()
237    }
238}
239
240impl From<Cranelift> for Engine {
241    fn from(config: Cranelift) -> Self {
242        EngineBuilder::new(config).engine()
243    }
244}