wasmer_compiler_singlepass/
config.rs

1// Allow unused imports while developing
2#![allow(unused_imports, dead_code)]
3
4use crate::{compiler::SinglepassCompiler, machine::AssemblyComment};
5use std::{
6    collections::HashMap,
7    fs::File,
8    io::{self, Write},
9    num::NonZero,
10    path::PathBuf,
11    sync::Arc,
12};
13use target_lexicon::Architecture;
14use wasmer_compiler::{
15    Compiler, CompilerConfig, Engine, EngineBuilder, ModuleMiddleware,
16    misc::{CompiledKind, function_kind_to_filename, save_assembly_to_file},
17};
18use wasmer_types::{
19    Features,
20    target::{CpuFeature, Target},
21};
22
23/// Callbacks to the different Cranelift compilation phases.
24#[derive(Debug, Clone)]
25pub struct SinglepassCallbacks {
26    debug_dir: PathBuf,
27}
28
29impl SinglepassCallbacks {
30    /// Creates a new instance of `SinglepassCallbacks` with the specified debug directory.
31    pub fn new(debug_dir: PathBuf) -> Result<Self, io::Error> {
32        // Create the debug dir in case it doesn't exist
33        std::fs::create_dir_all(&debug_dir)?;
34        Ok(Self { debug_dir })
35    }
36
37    fn base_path(&self, module_hash: &Option<String>) -> PathBuf {
38        let mut path = self.debug_dir.clone();
39        if let Some(hash) = module_hash {
40            path.push(hash);
41        }
42        std::fs::create_dir_all(&path)
43            .unwrap_or_else(|_| panic!("cannot create debug directory: {}", path.display()));
44        path
45    }
46
47    /// Writes the object file memory buffer to a debug file.
48    pub fn obj_memory_buffer(
49        &self,
50        kind: &CompiledKind,
51        module_hash: &Option<String>,
52        mem_buffer: &[u8],
53    ) {
54        let mut path = self.base_path(module_hash);
55        path.push(function_kind_to_filename(kind, ".o"));
56        let mut file =
57            File::create(path).expect("Error while creating debug file from Cranelift object");
58        file.write_all(mem_buffer).unwrap();
59    }
60
61    /// Writes the assembly memory buffer to a debug file.
62    pub fn asm_memory_buffer(
63        &self,
64        kind: &CompiledKind,
65        module_hash: &Option<String>,
66        arch: Architecture,
67        mem_buffer: &[u8],
68        assembly_comments: HashMap<usize, AssemblyComment>,
69    ) -> Result<(), wasmer_types::CompileError> {
70        let mut path = self.base_path(module_hash);
71        path.push(function_kind_to_filename(kind, ".s"));
72        save_assembly_to_file(arch, path, mem_buffer, assembly_comments)
73    }
74}
75
76#[derive(Debug, Clone)]
77pub struct Singlepass {
78    pub(crate) enable_nan_canonicalization: bool,
79    pub(crate) allow_experimental_unaligned_memory_accesses: bool,
80
81    /// The middleware chain.
82    pub(crate) middlewares: Vec<Arc<dyn ModuleMiddleware>>,
83
84    pub(crate) callbacks: Option<SinglepassCallbacks>,
85
86    /// The number of threads to use for compilation.
87    pub num_threads: NonZero<usize>,
88}
89
90impl Singlepass {
91    /// Creates a new configuration object with the default configuration
92    /// specified.
93    pub fn new() -> Self {
94        Self {
95            enable_nan_canonicalization: true,
96            allow_experimental_unaligned_memory_accesses: false,
97            middlewares: vec![],
98            callbacks: None,
99            num_threads: std::thread::available_parallelism().unwrap_or(NonZero::new(1).unwrap()),
100        }
101    }
102
103    pub fn canonicalize_nans(&mut self, enable: bool) -> &mut Self {
104        self.enable_nan_canonicalization = enable;
105        self
106    }
107
108    /// Enable run-time handling of potentially unaligned memory accesses.
109    /// Unaligned memory accesses occur when you try to read N bytes of data starting
110    /// from an address that is not evenly divisible by N.
111    ///
112    /// This feature is experimental and currently supports only Cranelift scalar types
113    /// and Singlepass on RISC-V for integral types.
114    pub fn allow_experimental_unaligned_memory_accesses(&mut self, enable: bool) -> &mut Self {
115        self.allow_experimental_unaligned_memory_accesses = enable;
116        self
117    }
118
119    /// Callbacks that will triggered in the different compilation
120    /// phases in Singlepass.
121    pub fn callbacks(&mut self, callbacks: Option<SinglepassCallbacks>) -> &mut Self {
122        self.callbacks = callbacks;
123        self
124    }
125
126    /// Set the number of threads to use for compilation.
127    pub fn num_threads(&mut self, num_threads: NonZero<usize>) -> &mut Self {
128        self.num_threads = num_threads;
129        self
130    }
131}
132
133impl CompilerConfig for Singlepass {
134    fn enable_pic(&mut self) {
135        // Do nothing, since singlepass already emits
136        // PIC code.
137    }
138
139    /// Transform it into the compiler
140    fn compiler(self: Box<Self>) -> Box<dyn Compiler> {
141        Box::new(SinglepassCompiler::new(*self))
142    }
143
144    /// Gets the supported features for this compiler in the given target
145    fn supported_features_for_target(&self, _target: &Target) -> Features {
146        Features::default()
147    }
148
149    /// Pushes a middleware onto the back of the middleware chain.
150    fn push_middleware(&mut self, middleware: Arc<dyn ModuleMiddleware>) {
151        self.middlewares.push(middleware);
152    }
153}
154
155impl Default for Singlepass {
156    fn default() -> Singlepass {
157        Self::new()
158    }
159}
160
161impl From<Singlepass> for Engine {
162    fn from(config: Singlepass) -> Self {
163        EngineBuilder::new(config).engine()
164    }
165}