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;
15use wasmer_compiler::{
16 Compiler, CompilerConfig, 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 fn base_path(&self, module_hash: &Option<String>) -> PathBuf {
39 let mut path = self.debug_dir.clone();
40 if let Some(hash) = module_hash {
41 path.push(hash);
42 }
43 std::fs::create_dir_all(&path)
44 .unwrap_or_else(|_| panic!("cannot create debug directory: {}", path.display()));
45 path
46 }
47
48 pub fn preopt_ir(&self, kind: &CompiledKind, module_hash: &Option<String>, mem_buffer: &[u8]) {
50 let mut path = self.base_path(module_hash);
51 path.push(function_kind_to_filename(kind, ".preopt.clif"));
52 let mut file =
53 File::create(path).expect("Error while creating debug file from Cranelift IR");
54 file.write_all(mem_buffer).unwrap();
55 }
56
57 pub fn obj_memory_buffer(
59 &self,
60 kind: &CompiledKind,
61 module_hash: &Option<String>,
62 mem_buffer: &[u8],
63 ) {
64 let mut path = self.base_path(module_hash);
65 path.push(function_kind_to_filename(kind, ".o"));
66 let mut file =
67 File::create(path).expect("Error while creating debug file from Cranelift object");
68 file.write_all(mem_buffer).unwrap();
69 }
70
71 pub fn asm_memory_buffer(
73 &self,
74 kind: &CompiledKind,
75 module_hash: &Option<String>,
76 arch: Architecture,
77 mem_buffer: &[u8],
78 ) -> Result<(), wasmer_types::CompileError> {
79 let mut path = self.base_path(module_hash);
80 path.push(function_kind_to_filename(kind, ".s"));
81 save_assembly_to_file(arch, path, mem_buffer, HashMap::<usize, String>::new())
82 }
83}
84
85#[non_exhaustive]
89#[derive(Clone, Debug)]
90pub enum CraneliftOptLevel {
91 None,
94 Speed,
96 SpeedAndSize,
99}
100
101#[derive(Debug, Clone)]
107pub struct Cranelift {
108 enable_nan_canonicalization: bool,
109 pub(crate) allow_experimental_unaligned_memory_accesses: bool,
110 enable_verifier: bool,
111 pub(crate) enable_perfmap: bool,
112 enable_pic: bool,
113 opt_level: CraneliftOptLevel,
114 pub num_threads: NonZero<usize>,
116 pub(crate) middlewares: Vec<Arc<dyn ModuleMiddleware>>,
118 pub(crate) callbacks: Option<CraneliftCallbacks>,
119}
120
121impl Cranelift {
122 pub fn new() -> Self {
125 Self {
126 enable_nan_canonicalization: false,
127 allow_experimental_unaligned_memory_accesses: false,
128 enable_verifier: false,
129 opt_level: CraneliftOptLevel::Speed,
130 enable_pic: false,
131 num_threads: std::thread::available_parallelism().unwrap_or(NonZero::new(1).unwrap()),
132 middlewares: vec![],
133 enable_perfmap: false,
134 callbacks: None,
135 }
136 }
137
138 pub fn canonicalize_nans(&mut self, enable: bool) -> &mut Self {
143 self.enable_nan_canonicalization = enable;
144 self
145 }
146
147 pub fn allow_experimental_unaligned_memory_accesses(&mut self, enable: bool) -> &mut Self {
153 self.allow_experimental_unaligned_memory_accesses = enable;
154 self
155 }
156
157 pub fn num_threads(&mut self, num_threads: NonZero<usize>) -> &mut Self {
159 self.num_threads = num_threads;
160 self
161 }
162
163 pub fn opt_level(&mut self, opt_level: CraneliftOptLevel) -> &mut Self {
165 self.opt_level = opt_level;
166 self
167 }
168
169 pub fn isa(&self, target: &Target) -> CodegenResult<Arc<dyn TargetIsa>> {
171 let mut builder =
172 lookup(target.triple().clone()).expect("construct Cranelift ISA for triple");
173 let cpu_features = target.cpu_features();
175 if target.triple().architecture == Architecture::X86_64
176 && !cpu_features.contains(CpuFeature::SSE2)
177 {
178 panic!("x86 support requires SSE2");
179 }
180 if cpu_features.contains(CpuFeature::SSE3) {
181 builder.enable("has_sse3").expect("should be valid flag");
182 }
183 if cpu_features.contains(CpuFeature::SSSE3) {
184 builder.enable("has_ssse3").expect("should be valid flag");
185 }
186 if cpu_features.contains(CpuFeature::SSE41) {
187 builder.enable("has_sse41").expect("should be valid flag");
188 }
189 if cpu_features.contains(CpuFeature::SSE42) {
190 builder.enable("has_sse42").expect("should be valid flag");
191 }
192 if cpu_features.contains(CpuFeature::POPCNT) {
193 builder.enable("has_popcnt").expect("should be valid flag");
194 }
195 if cpu_features.contains(CpuFeature::AVX) {
196 builder.enable("has_avx").expect("should be valid flag");
197 }
198 if cpu_features.contains(CpuFeature::BMI1) {
199 builder.enable("has_bmi1").expect("should be valid flag");
200 }
201 if cpu_features.contains(CpuFeature::BMI2) {
202 builder.enable("has_bmi2").expect("should be valid flag");
203 }
204 if cpu_features.contains(CpuFeature::AVX2) {
205 builder.enable("has_avx2").expect("should be valid flag");
206 }
207 if cpu_features.contains(CpuFeature::AVX512DQ) {
208 builder
209 .enable("has_avx512dq")
210 .expect("should be valid flag");
211 }
212 if cpu_features.contains(CpuFeature::AVX512VL) {
213 builder
214 .enable("has_avx512vl")
215 .expect("should be valid flag");
216 }
217 if cpu_features.contains(CpuFeature::LZCNT) {
218 builder.enable("has_lzcnt").expect("should be valid flag");
219 }
220
221 builder.finish(self.flags())
222 }
223
224 pub fn flags(&self) -> settings::Flags {
226 let mut flags = settings::builder();
227
228 flags
230 .enable("enable_probestack")
231 .expect("should be valid flag");
232
233 flags
235 .set("probestack_strategy", "inline")
236 .expect("should be valid flag");
237
238 if self.enable_pic {
239 flags.enable("is_pic").expect("should be a valid flag");
240 }
241
242 flags
244 .enable("use_colocated_libcalls")
245 .expect("should be a valid flag");
246
247 flags
250 .enable("enable_multi_ret_implicit_sret")
251 .expect("should be a valid flag");
252
253 flags
255 .set("enable_verifier", &self.enable_verifier.to_string())
256 .expect("should be valid flag");
257
258 flags
259 .set(
260 "opt_level",
261 match self.opt_level {
262 CraneliftOptLevel::None => "none",
263 CraneliftOptLevel::Speed => "speed",
264 CraneliftOptLevel::SpeedAndSize => "speed_and_size",
265 },
266 )
267 .expect("should be valid flag");
268
269 flags
270 .set(
271 "enable_nan_canonicalization",
272 &self.enable_nan_canonicalization.to_string(),
273 )
274 .expect("should be valid flag");
275
276 settings::Flags::new(flags)
277 }
278
279 pub fn callbacks(&mut self, callbacks: Option<CraneliftCallbacks>) -> &mut Self {
282 self.callbacks = callbacks;
283 self
284 }
285}
286
287impl CompilerConfig for Cranelift {
288 fn enable_pic(&mut self) {
289 self.enable_pic = true;
290 }
291
292 fn enable_verifier(&mut self) {
293 self.enable_verifier = true;
294 }
295
296 fn enable_perfmap(&mut self) {
297 self.enable_perfmap = true;
298 }
299
300 fn enable_experimental_unaligned_memory_accesses(&mut self) {
301 self.allow_experimental_unaligned_memory_accesses = true;
302 }
303
304 fn canonicalize_nans(&mut self, enable: bool) {
305 self.enable_nan_canonicalization = enable;
306 }
307
308 fn compiler(self: Box<Self>) -> Box<dyn Compiler> {
310 Box::new(CraneliftCompiler::new(*self))
311 }
312
313 fn push_middleware(&mut self, middleware: Arc<dyn ModuleMiddleware>) {
315 self.middlewares.push(middleware);
316 }
317
318 fn supported_features_for_target(&self, target: &Target) -> wasmer_types::Features {
319 let mut feats = Features::default();
320 if target.triple().operating_system == OperatingSystem::Linux {
321 feats.exceptions(true);
322 }
323 feats.relaxed_simd(true);
324 feats.wide_arithmetic(true);
325 feats
326 }
327}
328
329impl Default for Cranelift {
330 fn default() -> Self {
331 Self::new()
332 }
333}
334
335impl From<Cranelift> for Engine {
336 fn from(config: Cranelift) -> Self {
337 EngineBuilder::new(config).engine()
338 }
339}