wasmer_compiler_cranelift/
config.rs1use 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, Vendor};
15use wasmer_compiler::{
16 Compiler, CompilerConfig, Debugger, 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#[derive(Debug, Clone)]
26pub struct CraneliftCallbacks {
27 debug_dir: PathBuf,
28}
29
30impl CraneliftCallbacks {
31 pub fn new(debug_dir: PathBuf) -> Result<Self, io::Error> {
33 std::fs::create_dir_all(&debug_dir)?;
35 Ok(Self { debug_dir })
36 }
37
38 pub fn debug_dir(&self) -> &PathBuf {
40 &self.debug_dir
41 }
42
43 fn base_path(&self, module_hash: &Option<String>) -> PathBuf {
44 let mut path = self.debug_dir.clone();
45 if let Some(hash) = module_hash {
46 path.push(hash);
47 }
48 std::fs::create_dir_all(&path)
49 .unwrap_or_else(|_| panic!("cannot create debug directory: {}", path.display()));
50 path
51 }
52
53 pub fn preopt_ir(&self, kind: &CompiledKind, module_hash: &Option<String>, mem_buffer: &[u8]) {
55 let mut path = self.base_path(module_hash);
56 path.push(function_kind_to_filename(kind, ".preopt.clif"));
57 let mut file =
58 File::create(path).expect("Error while creating debug file from Cranelift IR");
59 file.write_all(mem_buffer).unwrap();
60 }
61
62 pub fn obj_memory_buffer(
64 &self,
65 kind: &CompiledKind,
66 module_hash: &Option<String>,
67 mem_buffer: &[u8],
68 ) {
69 let mut path = self.base_path(module_hash);
70 path.push(function_kind_to_filename(kind, ".o"));
71 let mut file =
72 File::create(path).expect("Error while creating debug file from Cranelift object");
73 file.write_all(mem_buffer).unwrap();
74 }
75
76 pub fn asm_memory_buffer(
78 &self,
79 kind: &CompiledKind,
80 module_hash: &Option<String>,
81 arch: Architecture,
82 mem_buffer: &[u8],
83 ) -> Result<(), wasmer_types::CompileError> {
84 let mut path = self.base_path(module_hash);
85 path.push(function_kind_to_filename(kind, ".s"));
86 save_assembly_to_file(arch, path, mem_buffer, HashMap::<usize, String>::new())
87 }
88}
89
90#[non_exhaustive]
94#[derive(Clone, Debug)]
95pub enum CraneliftOptLevel {
96 None,
99 Speed,
101 SpeedAndSize,
104}
105
106#[derive(Debug, Clone)]
112pub struct Cranelift {
113 pub(crate) enable_nan_canonicalization: bool,
114 pub(crate) allow_experimental_unaligned_memory_accesses: bool,
115 enable_verifier: bool,
116 pub(crate) enable_perfmap: bool,
117 pub(crate) debugger: Option<Debugger>,
118 pub(crate) enable_pic: bool,
119 pub(crate) experimental_artifact: bool,
120 pub(crate) opt_level: CraneliftOptLevel,
121 pub num_threads: NonZero<usize>,
123 pub(crate) middlewares: Vec<Arc<dyn ModuleMiddleware>>,
125 pub(crate) callbacks: Option<CraneliftCallbacks>,
126}
127
128impl Cranelift {
129 pub fn new() -> Self {
132 Self {
133 enable_nan_canonicalization: false,
134 allow_experimental_unaligned_memory_accesses: false,
135 enable_verifier: false,
136 opt_level: CraneliftOptLevel::Speed,
137 enable_pic: false,
138 experimental_artifact: false,
139 num_threads: std::thread::available_parallelism().unwrap_or(NonZero::new(1).unwrap()),
140 middlewares: vec![],
141 enable_perfmap: false,
142 debugger: None,
143 callbacks: None,
144 }
145 }
146
147 pub fn experimental_artifact(&mut self, enable: bool) -> &mut Self {
149 self.experimental_artifact = enable;
150 self
151 }
152
153 pub fn canonicalize_nans(&mut self, enable: bool) -> &mut Self {
158 self.enable_nan_canonicalization = enable;
159 self
160 }
161
162 pub fn allow_experimental_unaligned_memory_accesses(&mut self, enable: bool) -> &mut Self {
168 self.allow_experimental_unaligned_memory_accesses = enable;
169 self
170 }
171
172 pub fn num_threads(&mut self, num_threads: NonZero<usize>) -> &mut Self {
174 self.num_threads = num_threads;
175 self
176 }
177
178 pub fn opt_level(&mut self, opt_level: CraneliftOptLevel) -> &mut Self {
180 self.opt_level = opt_level;
181 self
182 }
183
184 pub fn isa(&self, target: &Target) -> CodegenResult<Arc<dyn TargetIsa>> {
186 let mut builder =
187 lookup(target.triple().clone()).expect("construct Cranelift ISA for triple");
188 let cpu_features = target.cpu_features();
190 if target.triple().architecture == Architecture::X86_64
191 && !cpu_features.contains(CpuFeature::SSE2)
192 {
193 panic!("x86 support requires SSE2");
194 }
195 if cpu_features.contains(CpuFeature::SSE3) {
196 builder.enable("has_sse3").expect("should be valid flag");
197 }
198 if cpu_features.contains(CpuFeature::SSSE3) {
199 builder.enable("has_ssse3").expect("should be valid flag");
200 }
201 if cpu_features.contains(CpuFeature::SSE41) {
202 builder.enable("has_sse41").expect("should be valid flag");
203 }
204 if cpu_features.contains(CpuFeature::SSE42) {
205 builder.enable("has_sse42").expect("should be valid flag");
206 }
207 if cpu_features.contains(CpuFeature::POPCNT) {
208 builder.enable("has_popcnt").expect("should be valid flag");
209 }
210 if cpu_features.contains(CpuFeature::AVX) {
211 builder.enable("has_avx").expect("should be valid flag");
212 }
213 if cpu_features.contains(CpuFeature::BMI1) {
214 builder.enable("has_bmi1").expect("should be valid flag");
215 }
216 if cpu_features.contains(CpuFeature::BMI2) {
217 builder.enable("has_bmi2").expect("should be valid flag");
218 }
219 if cpu_features.contains(CpuFeature::AVX2) {
220 builder.enable("has_avx2").expect("should be valid flag");
221 }
222 if cpu_features.contains(CpuFeature::AVX512DQ) {
223 builder
224 .enable("has_avx512dq")
225 .expect("should be valid flag");
226 }
227 if cpu_features.contains(CpuFeature::AVX512VL) {
228 builder
229 .enable("has_avx512vl")
230 .expect("should be valid flag");
231 }
232 if cpu_features.contains(CpuFeature::LZCNT) {
233 builder.enable("has_lzcnt").expect("should be valid flag");
234 }
235
236 builder.finish(self.flags(target))
237 }
238
239 pub fn flags(&self, target: &Target) -> settings::Flags {
241 let mut flags = settings::builder();
242
243 flags
245 .enable("enable_probestack")
246 .expect("should be valid flag");
247
248 flags
250 .set("probestack_strategy", "inline")
251 .expect("should be valid flag");
252
253 if self.enable_pic {
254 flags.enable("is_pic").expect("should be a valid flag");
255 }
256
257 flags
259 .enable("use_colocated_libcalls")
260 .expect("should be a valid flag");
261
262 if matches!(target.triple().operating_system, OperatingSystem::Windows) {
263 flags
265 .enable("enable_multi_ret_implicit_sret")
266 .expect("should be a valid flag");
267 }
268
269 flags
271 .set("enable_verifier", &self.enable_verifier.to_string())
272 .expect("should be valid flag");
273
274 flags
275 .set(
276 "opt_level",
277 match self.opt_level {
278 CraneliftOptLevel::None => "none",
279 CraneliftOptLevel::Speed => "speed",
280 CraneliftOptLevel::SpeedAndSize => "speed_and_size",
281 },
282 )
283 .expect("should be valid flag");
284
285 flags
286 .set(
287 "enable_nan_canonicalization",
288 &self.enable_nan_canonicalization.to_string(),
289 )
290 .expect("should be valid flag");
291
292 if matches!(target.triple().vendor, Vendor::Apple) {
293 flags
294 .enable("enable_compact_unwind_abi")
295 .expect("should be valid flag");
296 }
297
298 settings::Flags::new(flags)
299 }
300
301 pub fn callbacks(&mut self, callbacks: Option<CraneliftCallbacks>) -> &mut Self {
304 self.callbacks = callbacks;
305 self
306 }
307}
308
309impl CompilerConfig for Cranelift {
310 fn experimental_artifact(&mut self, enable: bool) {
311 self.experimental_artifact = enable;
312 }
313
314 fn enable_pic(&mut self) {
315 self.enable_pic = true;
316 }
317
318 fn enable_verifier(&mut self) {
319 self.enable_verifier = true;
320 }
321
322 fn enable_perfmap(&mut self) {
323 self.enable_perfmap = true;
324 }
325
326 fn enable_debugger(&mut self, debugger: Debugger) {
327 self.debugger = Some(debugger);
328 }
329
330 fn enable_experimental_unaligned_memory_accesses(&mut self) {
331 self.allow_experimental_unaligned_memory_accesses = true;
332 }
333
334 fn canonicalize_nans(&mut self, enable: bool) {
335 self.enable_nan_canonicalization = enable;
336 }
337
338 fn compiler(self: Box<Self>) -> Box<dyn Compiler> {
340 Box::new(CraneliftCompiler::new(*self))
341 }
342
343 fn push_middleware(&mut self, middleware: Arc<dyn ModuleMiddleware>) {
345 self.middlewares.push(middleware);
346 }
347
348 fn supported_features_for_target(&self, target: &Target) -> wasmer_types::Features {
349 let mut feats = Features::default();
350
351 if matches!(
352 target.triple().operating_system,
353 OperatingSystem::Linux | OperatingSystem::Darwin(_)
354 ) {
355 feats.exceptions(true);
356 }
357 feats.exceptions(true);
358 feats.relaxed_simd(true);
359 feats.wide_arithmetic(true);
360 feats
361 }
362}
363
364impl Default for Cranelift {
365 fn default() -> Self {
366 Self::new()
367 }
368}
369
370impl From<Cranelift> for Engine {
371 fn from(config: Cranelift) -> Self {
372 EngineBuilder::new(config).engine()
373 }
374}