Skip to main content

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