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 enable_verifier: bool,
110 pub(crate) enable_perfmap: bool,
111 enable_pic: bool,
112 opt_level: CraneliftOptLevel,
113 pub num_threads: NonZero<usize>,
115 pub(crate) middlewares: Vec<Arc<dyn ModuleMiddleware>>,
117 pub(crate) callbacks: Option<CraneliftCallbacks>,
118}
119
120impl Cranelift {
121 pub fn new() -> Self {
124 Self {
125 enable_nan_canonicalization: false,
126 enable_verifier: false,
127 opt_level: CraneliftOptLevel::Speed,
128 enable_pic: false,
129 num_threads: std::thread::available_parallelism().unwrap_or(NonZero::new(1).unwrap()),
130 middlewares: vec![],
131 enable_perfmap: false,
132 callbacks: None,
133 }
134 }
135
136 pub fn canonicalize_nans(&mut self, enable: bool) -> &mut Self {
141 self.enable_nan_canonicalization = enable;
142 self
143 }
144
145 pub fn num_threads(&mut self, num_threads: NonZero<usize>) -> &mut Self {
147 self.num_threads = num_threads;
148 self
149 }
150
151 pub fn opt_level(&mut self, opt_level: CraneliftOptLevel) -> &mut Self {
153 self.opt_level = opt_level;
154 self
155 }
156
157 pub fn isa(&self, target: &Target) -> CodegenResult<Arc<dyn TargetIsa>> {
159 let mut builder =
160 lookup(target.triple().clone()).expect("construct Cranelift ISA for triple");
161 let cpu_features = target.cpu_features();
163 if target.triple().architecture == Architecture::X86_64
164 && !cpu_features.contains(CpuFeature::SSE2)
165 {
166 panic!("x86 support requires SSE2");
167 }
168 if cpu_features.contains(CpuFeature::SSE3) {
169 builder.enable("has_sse3").expect("should be valid flag");
170 }
171 if cpu_features.contains(CpuFeature::SSSE3) {
172 builder.enable("has_ssse3").expect("should be valid flag");
173 }
174 if cpu_features.contains(CpuFeature::SSE41) {
175 builder.enable("has_sse41").expect("should be valid flag");
176 }
177 if cpu_features.contains(CpuFeature::SSE42) {
178 builder.enable("has_sse42").expect("should be valid flag");
179 }
180 if cpu_features.contains(CpuFeature::POPCNT) {
181 builder.enable("has_popcnt").expect("should be valid flag");
182 }
183 if cpu_features.contains(CpuFeature::AVX) {
184 builder.enable("has_avx").expect("should be valid flag");
185 }
186 if cpu_features.contains(CpuFeature::BMI1) {
187 builder.enable("has_bmi1").expect("should be valid flag");
188 }
189 if cpu_features.contains(CpuFeature::BMI2) {
190 builder.enable("has_bmi2").expect("should be valid flag");
191 }
192 if cpu_features.contains(CpuFeature::AVX2) {
193 builder.enable("has_avx2").expect("should be valid flag");
194 }
195 if cpu_features.contains(CpuFeature::AVX512DQ) {
196 builder
197 .enable("has_avx512dq")
198 .expect("should be valid flag");
199 }
200 if cpu_features.contains(CpuFeature::AVX512VL) {
201 builder
202 .enable("has_avx512vl")
203 .expect("should be valid flag");
204 }
205 if cpu_features.contains(CpuFeature::LZCNT) {
206 builder.enable("has_lzcnt").expect("should be valid flag");
207 }
208
209 builder.finish(self.flags())
210 }
211
212 pub fn flags(&self) -> settings::Flags {
214 let mut flags = settings::builder();
215
216 flags
218 .enable("enable_probestack")
219 .expect("should be valid flag");
220
221 flags
223 .set("probestack_strategy", "inline")
224 .expect("should be valid flag");
225
226 if self.enable_pic {
227 flags.enable("is_pic").expect("should be a valid flag");
228 }
229
230 flags
233 .enable("use_colocated_libcalls")
234 .expect("should be a valid flag");
235
236 flags
239 .enable("enable_multi_ret_implicit_sret")
240 .expect("should be a valid flag");
241
242 flags
244 .set("enable_verifier", &self.enable_verifier.to_string())
245 .expect("should be valid flag");
246
247 flags
248 .set(
249 "opt_level",
250 match self.opt_level {
251 CraneliftOptLevel::None => "none",
252 CraneliftOptLevel::Speed => "speed",
253 CraneliftOptLevel::SpeedAndSize => "speed_and_size",
254 },
255 )
256 .expect("should be valid flag");
257
258 flags
259 .set(
260 "enable_nan_canonicalization",
261 &self.enable_nan_canonicalization.to_string(),
262 )
263 .expect("should be valid flag");
264
265 settings::Flags::new(flags)
266 }
267
268 pub fn callbacks(&mut self, callbacks: Option<CraneliftCallbacks>) -> &mut Self {
271 self.callbacks = callbacks;
272 self
273 }
274}
275
276impl CompilerConfig for Cranelift {
277 fn enable_pic(&mut self) {
278 self.enable_pic = true;
279 }
280
281 fn enable_verifier(&mut self) {
282 self.enable_verifier = true;
283 }
284
285 fn enable_perfmap(&mut self) {
286 self.enable_perfmap = true;
287 }
288
289 fn canonicalize_nans(&mut self, enable: bool) {
290 self.enable_nan_canonicalization = enable;
291 }
292
293 fn compiler(self: Box<Self>) -> Box<dyn Compiler> {
295 Box::new(CraneliftCompiler::new(*self))
296 }
297
298 fn push_middleware(&mut self, middleware: Arc<dyn ModuleMiddleware>) {
300 self.middlewares.push(middleware);
301 }
302
303 fn supported_features_for_target(&self, target: &Target) -> wasmer_types::Features {
304 let mut feats = Features::default();
305 if target.triple().operating_system == OperatingSystem::Linux {
306 feats.exceptions(true);
307 }
308 feats.relaxed_simd(true);
309 feats.wide_arithmetic(true);
310 feats
311 }
312}
313
314impl Default for Cranelift {
315 fn default() -> Self {
316 Self::new()
317 }
318}
319
320impl From<Cranelift> for Engine {
321 fn from(config: Cranelift) -> Self {
322 EngineBuilder::new(config).engine()
323 }
324}