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