wasmer_compiler_cranelift/
config.rs

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