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 pub fn preopt_ir(&self, kind: &CompiledKind, mem_buffer: &[u8]) {
40 let mut path = self.debug_dir.clone();
41 path.push(function_kind_to_filename(kind, ".preopt.clif"));
42 let mut file =
43 File::create(path).expect("Error while creating debug file from Cranelift IR");
44 file.write_all(mem_buffer).unwrap();
45 }
46
47 pub fn obj_memory_buffer(&self, kind: &CompiledKind, mem_buffer: &[u8]) {
49 let mut path = self.debug_dir.clone();
50 path.push(function_kind_to_filename(kind, ".o"));
51 let mut file =
52 File::create(path).expect("Error while creating debug file from Cranelift object");
53 file.write_all(mem_buffer).unwrap();
54 }
55
56 pub fn asm_memory_buffer(
58 &self,
59 kind: &CompiledKind,
60 arch: Architecture,
61 mem_buffer: &[u8],
62 ) -> Result<(), wasmer_types::CompileError> {
63 let mut path = self.debug_dir.clone();
64 path.push(function_kind_to_filename(kind, ".s"));
65 save_assembly_to_file(arch, path, mem_buffer, HashMap::<usize, String>::new())
66 }
67}
68
69#[non_exhaustive]
73#[derive(Clone, Debug)]
74pub enum CraneliftOptLevel {
75 None,
78 Speed,
80 SpeedAndSize,
83}
84
85#[derive(Debug, Clone)]
91pub struct Cranelift {
92 enable_nan_canonicalization: bool,
93 enable_verifier: bool,
94 pub(crate) enable_perfmap: bool,
95 enable_pic: bool,
96 opt_level: CraneliftOptLevel,
97 pub num_threads: NonZero<usize>,
99 pub(crate) middlewares: Vec<Arc<dyn ModuleMiddleware>>,
101 pub(crate) callbacks: Option<CraneliftCallbacks>,
102}
103
104impl Cranelift {
105 pub fn new() -> Self {
108 Self {
109 enable_nan_canonicalization: false,
110 enable_verifier: false,
111 opt_level: CraneliftOptLevel::Speed,
112 enable_pic: false,
113 num_threads: std::thread::available_parallelism().unwrap_or(NonZero::new(1).unwrap()),
114 middlewares: vec![],
115 enable_perfmap: false,
116 callbacks: None,
117 }
118 }
119
120 pub fn canonicalize_nans(&mut self, enable: bool) -> &mut Self {
125 self.enable_nan_canonicalization = enable;
126 self
127 }
128
129 pub fn num_threads(&mut self, num_threads: NonZero<usize>) -> &mut Self {
131 self.num_threads = num_threads;
132 self
133 }
134
135 pub fn opt_level(&mut self, opt_level: CraneliftOptLevel) -> &mut Self {
137 self.opt_level = opt_level;
138 self
139 }
140
141 pub fn isa(&self, target: &Target) -> CodegenResult<Arc<dyn TargetIsa>> {
143 let mut builder =
144 lookup(target.triple().clone()).expect("construct Cranelift ISA for triple");
145 let cpu_features = target.cpu_features();
147 if target.triple().architecture == Architecture::X86_64
148 && !cpu_features.contains(CpuFeature::SSE2)
149 {
150 panic!("x86 support requires SSE2");
151 }
152 if cpu_features.contains(CpuFeature::SSE3) {
153 builder.enable("has_sse3").expect("should be valid flag");
154 }
155 if cpu_features.contains(CpuFeature::SSSE3) {
156 builder.enable("has_ssse3").expect("should be valid flag");
157 }
158 if cpu_features.contains(CpuFeature::SSE41) {
159 builder.enable("has_sse41").expect("should be valid flag");
160 }
161 if cpu_features.contains(CpuFeature::SSE42) {
162 builder.enable("has_sse42").expect("should be valid flag");
163 }
164 if cpu_features.contains(CpuFeature::POPCNT) {
165 builder.enable("has_popcnt").expect("should be valid flag");
166 }
167 if cpu_features.contains(CpuFeature::AVX) {
168 builder.enable("has_avx").expect("should be valid flag");
169 }
170 if cpu_features.contains(CpuFeature::BMI1) {
171 builder.enable("has_bmi1").expect("should be valid flag");
172 }
173 if cpu_features.contains(CpuFeature::BMI2) {
174 builder.enable("has_bmi2").expect("should be valid flag");
175 }
176 if cpu_features.contains(CpuFeature::AVX2) {
177 builder.enable("has_avx2").expect("should be valid flag");
178 }
179 if cpu_features.contains(CpuFeature::AVX512DQ) {
180 builder
181 .enable("has_avx512dq")
182 .expect("should be valid flag");
183 }
184 if cpu_features.contains(CpuFeature::AVX512VL) {
185 builder
186 .enable("has_avx512vl")
187 .expect("should be valid flag");
188 }
189 if cpu_features.contains(CpuFeature::LZCNT) {
190 builder.enable("has_lzcnt").expect("should be valid flag");
191 }
192
193 builder.finish(self.flags())
194 }
195
196 pub fn flags(&self) -> settings::Flags {
198 let mut flags = settings::builder();
199
200 flags
202 .enable("enable_probestack")
203 .expect("should be valid flag");
204
205 flags
207 .set("probestack_strategy", "inline")
208 .expect("should be valid flag");
209
210 if self.enable_pic {
211 flags.enable("is_pic").expect("should be a valid flag");
212 }
213
214 flags
217 .enable("use_colocated_libcalls")
218 .expect("should be a valid flag");
219
220 flags
223 .enable("enable_multi_ret_implicit_sret")
224 .expect("should be a valid flag");
225
226 let enable_verifier = if self.enable_verifier {
228 "true"
229 } else {
230 "false"
231 };
232 flags
233 .set("enable_verifier", enable_verifier)
234 .expect("should be valid flag");
235
236 flags
237 .set(
238 "opt_level",
239 match self.opt_level {
240 CraneliftOptLevel::None => "none",
241 CraneliftOptLevel::Speed => "speed",
242 CraneliftOptLevel::SpeedAndSize => "speed_and_size",
243 },
244 )
245 .expect("should be valid flag");
246
247 let enable_nan_canonicalization = if self.enable_nan_canonicalization {
248 "true"
249 } else {
250 "false"
251 };
252 flags
253 .set("enable_nan_canonicalization", enable_nan_canonicalization)
254 .expect("should be valid flag");
255
256 settings::Flags::new(flags)
257 }
258
259 pub fn callbacks(&mut self, callbacks: Option<CraneliftCallbacks>) -> &mut Self {
262 self.callbacks = callbacks;
263 self
264 }
265}
266
267impl CompilerConfig for Cranelift {
268 fn enable_pic(&mut self) {
269 self.enable_pic = true;
270 }
271
272 fn enable_verifier(&mut self) {
273 self.enable_verifier = true;
274 }
275
276 fn enable_perfmap(&mut self) {
277 self.enable_perfmap = true;
278 }
279
280 fn canonicalize_nans(&mut self, enable: bool) {
281 self.enable_nan_canonicalization = enable;
282 }
283
284 fn compiler(self: Box<Self>) -> Box<dyn Compiler> {
286 Box::new(CraneliftCompiler::new(*self))
287 }
288
289 fn push_middleware(&mut self, middleware: Arc<dyn ModuleMiddleware>) {
291 self.middlewares.push(middleware);
292 }
293
294 fn supported_features_for_target(&self, target: &Target) -> wasmer_types::Features {
295 let mut feats = Features::default();
296 if target.triple().operating_system == OperatingSystem::Linux {
297 feats.exceptions(true);
298 }
299 feats
300 }
301}
302
303impl Default for Cranelift {
304 fn default() -> Self {
305 Self::new()
306 }
307}
308
309impl From<Cranelift> for Engine {
310 fn from(config: Cranelift) -> Self {
311 EngineBuilder::new(config).engine()
312 }
313}