linera_wasmer_compiler_cranelift/
config.rs

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