1use crate::common::WasmFeatures;
5use anyhow::Result;
6use clap::Parser;
7#[cfg(doc)]
8use std::path::PathBuf;
9#[allow(unused_imports)]
10use std::sync::Arc;
11use wasmer_compiler::{CompilerConfig, EngineBuilder, Features};
12#[cfg(doc)]
13use wasmer_types::Type;
14use wasmer_types::target::{PointerWidth, Target};
15use wasmer_types::{MemoryStyle, MemoryType, Pages, TableStyle, TableType};
16
17#[derive(Clone)]
19pub struct SubsetTunables {
20 pub static_memory_bound: Pages,
22
23 pub static_memory_offset_guard_size: u64,
25
26 pub dynamic_memory_offset_guard_size: u64,
28}
29
30impl SubsetTunables {
31 pub fn for_target(target: &Target) -> Self {
33 let triple = target.triple();
34 let pointer_width: PointerWidth = triple.pointer_width().unwrap();
35 let (static_memory_bound, static_memory_offset_guard_size): (Pages, u64) =
36 match pointer_width {
37 PointerWidth::U16 => (0x400.into(), 0x1000),
38 PointerWidth::U32 => (0x4000.into(), 0x1_0000),
39 PointerWidth::U64 => (0x1_0000.into(), 0x8000_0000),
46 };
47
48 #[cfg(target_os = "windows")]
54 let dynamic_memory_offset_guard_size: u64 = 0x2_0000;
55 #[cfg(not(target_os = "windows"))]
56 let dynamic_memory_offset_guard_size: u64 = 0x1_0000;
57
58 Self {
59 static_memory_bound,
60 static_memory_offset_guard_size,
61 dynamic_memory_offset_guard_size,
62 }
63 }
64 pub fn memory_style(&self, memory: &MemoryType) -> MemoryStyle {
66 let maximum = memory.maximum.unwrap_or_else(Pages::max_value);
71 if maximum <= self.static_memory_bound {
72 MemoryStyle::Static {
73 bound: self.static_memory_bound,
75 offset_guard_size: self.static_memory_offset_guard_size,
76 }
77 } else {
78 MemoryStyle::Dynamic {
79 offset_guard_size: self.dynamic_memory_offset_guard_size,
80 }
81 }
82 }
83
84 pub fn table_style(&self, _table: &TableType) -> TableStyle {
86 TableStyle::CallerChecksSignature
87 }
88}
89
90#[derive(Debug, Clone, Parser, Default)]
91pub struct StoreOptions {
93 #[clap(flatten)]
94 compiler: CompilerOptions,
95}
96
97#[derive(Debug, Clone, Parser, Default)]
98pub struct CompilerOptions {
100 #[clap(long, conflicts_with_all = &["cranelift", "llvm"])]
102 singlepass: bool,
103
104 #[clap(long, conflicts_with_all = &["singlepass", "llvm"])]
106 cranelift: bool,
107
108 #[clap(long, conflicts_with_all = &["singlepass", "cranelift"])]
110 llvm: bool,
111
112 #[allow(unused)]
114 #[clap(long)]
115 #[allow(dead_code)]
116 enable_verifier: bool,
117
118 #[allow(unused)]
120 #[cfg(feature = "llvm")]
121 #[cfg_attr(feature = "llvm", clap(long, parse(from_os_str)))]
122 llvm_debug_dir: Option<PathBuf>,
123
124 #[clap(flatten)]
125 features: WasmFeatures,
126}
127
128impl CompilerOptions {
129 fn get_compiler(&self) -> Result<CompilerType> {
130 if self.cranelift {
131 Ok(CompilerType::Cranelift)
132 } else if self.llvm {
133 Ok(CompilerType::LLVM)
134 } else if self.singlepass {
135 Ok(CompilerType::Singlepass)
136 } else {
137 cfg_if::cfg_if! {
139 if #[cfg(all(feature = "cranelift", any(target_arch = "x86_64", target_arch = "aarch64")))] {
140 Ok(CompilerType::Cranelift)
141 }
142 else if #[cfg(all(feature = "singlepass", any(target_arch = "x86_64", target_arch = "aarch64")))] {
143 Ok(CompilerType::Singlepass)
144 }
145 else if #[cfg(feature = "llvm")] {
146 Ok(CompilerType::LLVM)
147 } else {
148 bail!("There are no available compilers for your architecture");
149 }
150 }
151 }
152 }
153
154 pub fn get_features(&self, mut features: Features) -> Result<Features> {
156 if self.features.threads || self.features.all {
157 features.threads(true);
158 }
159 if self.features.multi_value || self.features.all {
160 features.multi_value(true);
161 }
162 if self.features.simd || self.features.all {
163 features.simd(true);
164 }
165 if self.features.bulk_memory || self.features.all {
166 features.bulk_memory(true);
167 }
168 if self.features.reference_types || self.features.all {
169 features.reference_types(true);
170 }
171 Ok(features)
172 }
173
174 fn get_engine_by_type(
175 &self,
176 target: Target,
177 compiler_config: Box<dyn CompilerConfig>,
178 ) -> Result<EngineBuilder> {
179 let features = self.get_features(compiler_config.default_features_for_target(&target))?;
180 let engine: EngineBuilder = EngineBuilder::new(compiler_config)
181 .set_target(Some(target))
182 .set_features(Some(features));
183
184 Ok(engine)
185 }
186
187 #[allow(unused_variables)]
189 pub(crate) fn get_compiler_config(&self) -> Result<(Box<dyn CompilerConfig>, CompilerType)> {
190 let compiler = self.get_compiler()?;
191 let compiler_config: Box<dyn CompilerConfig> = match compiler {
192 CompilerType::Headless => bail!("The headless engine can't be chosen"),
193 #[cfg(feature = "singlepass")]
194 CompilerType::Singlepass => {
195 let mut config = wasmer_compiler_singlepass::Singlepass::new();
196 if self.enable_verifier {
197 config.enable_verifier();
198 }
199 Box::new(config)
200 }
201 #[cfg(feature = "cranelift")]
202 CompilerType::Cranelift => {
203 let mut config = wasmer_compiler_cranelift::Cranelift::new();
204 if self.enable_verifier {
205 config.enable_verifier();
206 }
207 Box::new(config)
208 }
209 #[cfg(feature = "llvm")]
210 CompilerType::LLVM => {
211 use std::fmt;
212 use std::fs::File;
213 use std::io::Write;
214 use wasmer_compiler_llvm::{
215 CompiledKind, InkwellMemoryBuffer, InkwellModule, LLVMCallbacks,
216 };
217 use wasmer_types::entity::EntityRef;
218 let mut config = LLVM::new();
219 struct Callbacks {
220 debug_dir: PathBuf,
221 }
222 impl Callbacks {
223 fn new(debug_dir: PathBuf) -> Result<Self> {
224 std::fs::create_dir_all(&debug_dir)?;
226 Ok(Self { debug_dir })
227 }
228 }
229 fn types_to_signature(types: &[Type]) -> String {
232 types
233 .iter()
234 .map(|ty| match ty {
235 Type::I32 => "i".to_string(),
236 Type::I64 => "I".to_string(),
237 Type::F32 => "f".to_string(),
238 Type::F64 => "F".to_string(),
239 Type::V128 => "v".to_string(),
240 Type::ExternRef => "e".to_string(),
241 Type::FuncRef => "r".to_string(),
242 })
243 .collect::<Vec<_>>()
244 .join("")
245 }
246 fn function_kind_to_filename(kind: &CompiledKind) -> String {
249 match kind {
250 CompiledKind::Local(local_index) => {
251 format!("function_{}", local_index.index())
252 }
253 CompiledKind::FunctionCallTrampoline(func_type) => format!(
254 "trampoline_call_{}_{}",
255 types_to_signature(&func_type.params()),
256 types_to_signature(&func_type.results())
257 ),
258 CompiledKind::DynamicFunctionTrampoline(func_type) => format!(
259 "trampoline_dynamic_{}_{}",
260 types_to_signature(&func_type.params()),
261 types_to_signature(&func_type.results())
262 ),
263 CompiledKind::Module => "module".into(),
264 }
265 }
266 impl LLVMCallbacks for Callbacks {
267 fn preopt_ir(&self, kind: &CompiledKind, module: &InkwellModule) {
268 let mut path = self.debug_dir.clone();
269 path.push(format!("{}.preopt.ll", function_kind_to_filename(kind)));
270 module
271 .print_to_file(&path)
272 .expect("Error while dumping pre optimized LLVM IR");
273 }
274 fn postopt_ir(&self, kind: &CompiledKind, module: &InkwellModule) {
275 let mut path = self.debug_dir.clone();
276 path.push(format!("{}.postopt.ll", function_kind_to_filename(kind)));
277 module
278 .print_to_file(&path)
279 .expect("Error while dumping post optimized LLVM IR");
280 }
281 fn obj_memory_buffer(
282 &self,
283 kind: &CompiledKind,
284 memory_buffer: &InkwellMemoryBuffer,
285 ) {
286 let mut path = self.debug_dir.clone();
287 path.push(format!("{}.o", function_kind_to_filename(kind)));
288 let mem_buf_slice = memory_buffer.as_slice();
289 let mut file = File::create(path)
290 .expect("Error while creating debug object file from LLVM IR");
291 let mut pos = 0;
292 while pos < mem_buf_slice.len() {
293 pos += file.write(&mem_buf_slice[pos..]).unwrap();
294 }
295 }
296
297 fn asm_memory_buffer(
298 &self,
299 kind: &CompiledKind,
300 asm_memory_buffer: &InkwellMemoryBuffer,
301 ) {
302 let mut path = self.debug_dir.clone();
303 path.push(format!("{}.s", function_kind_to_filename(kind)));
304 let mem_buf_slice = asm_memory_buffer.as_slice();
305 let mut file = File::create(path)
306 .expect("Error while creating debug object file from LLVM IR");
307 let mut pos = 0;
308 while pos < mem_buf_slice.len() {
309 pos += file.write(&mem_buf_slice[pos..]).unwrap();
310 }
311 }
312 }
313
314 impl fmt::Debug for Callbacks {
315 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
316 write!(f, "LLVMCallbacks")
317 }
318 }
319
320 if let Some(ref llvm_debug_dir) = self.llvm_debug_dir {
321 config.callbacks(Some(Arc::new(Callbacks::new(llvm_debug_dir.clone())?)));
322 }
323 if self.enable_verifier {
324 config.enable_verifier();
325 }
326 Box::new(config)
327 }
328 #[cfg(not(all(feature = "singlepass", feature = "cranelift", feature = "llvm")))]
329 compiler => {
330 bail!("The `{compiler}` compiler is not included in this binary.")
331 }
332 };
333
334 #[allow(unreachable_code)]
335 Ok((compiler_config, compiler))
336 }
337}
338
339#[derive(Debug, PartialEq, Eq)]
341pub enum CompilerType {
342 Singlepass,
344 Cranelift,
346 LLVM,
348 #[allow(dead_code)]
350 Headless,
351}
352
353impl CompilerType {
354 pub fn enabled() -> Vec<CompilerType> {
356 vec![
357 #[cfg(feature = "singlepass")]
358 Self::Singlepass,
359 #[cfg(feature = "cranelift")]
360 Self::Cranelift,
361 #[cfg(feature = "llvm")]
362 Self::LLVM,
363 ]
364 }
365}
366
367impl std::fmt::Display for CompilerType {
368 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
369 write!(
370 f,
371 "{}",
372 match self {
373 Self::Singlepass => "singlepass",
374 Self::Cranelift => "cranelift",
375 Self::LLVM => "llvm",
376 Self::Headless => "headless",
377 }
378 )
379 }
380}
381
382impl StoreOptions {
383 pub fn get_engine_for_target(&self, target: Target) -> Result<(EngineBuilder, CompilerType)> {
385 let (compiler_config, compiler_type) = self.compiler.get_compiler_config()?;
386 let engine = self.get_engine_with_compiler(target, compiler_config)?;
387 Ok((engine, compiler_type))
388 }
389
390 fn get_engine_with_compiler(
391 &self,
392 target: Target,
393 compiler_config: Box<dyn CompilerConfig>,
394 ) -> Result<EngineBuilder> {
395 self.compiler.get_engine_by_type(target, compiler_config)
396 }
397
398 pub fn get_tunables_for_target(&self, target: &Target) -> Result<SubsetTunables> {
400 let tunables = SubsetTunables::for_target(target);
401 Ok(tunables)
402 }
403}