1use crate::compiler::LLVMCompiler;
2pub use inkwell::OptimizationLevel as LLVMOptLevel;
3use inkwell::targets::{
4 CodeModel, InitializationConfig, RelocMode, Target as InkwellTarget, TargetMachine,
5 TargetMachineOptions, TargetTriple,
6};
7use itertools::Itertools;
8use std::fs::File;
9use std::io::{self, Write};
10use std::path::PathBuf;
11use std::sync::Arc;
12use std::{fmt::Debug, num::NonZero};
13use target_lexicon::BinaryFormat;
14use wasmer_compiler::misc::{CompiledKind, function_kind_to_filename};
15use wasmer_compiler::{Compiler, CompilerConfig, Engine, EngineBuilder, ModuleMiddleware};
16use wasmer_types::{
17 Features,
18 target::{Architecture, OperatingSystem, Target, Triple},
19};
20
21pub type InkwellModule<'ctx> = inkwell::module::Module<'ctx>;
23
24pub type InkwellMemoryBuffer = inkwell::memory_buffer::MemoryBuffer;
26
27#[derive(Debug, Clone)]
29pub struct LLVMCallbacks {
30 debug_dir: PathBuf,
31}
32
33impl LLVMCallbacks {
34 pub fn new(debug_dir: PathBuf) -> Result<Self, io::Error> {
35 std::fs::create_dir_all(&debug_dir)?;
37 Ok(Self { debug_dir })
38 }
39
40 pub fn preopt_ir(&self, kind: &CompiledKind, module: &InkwellModule) {
41 let mut path = self.debug_dir.clone();
42 path.push(function_kind_to_filename(kind, ".preopt.ll"));
43 module
44 .print_to_file(&path)
45 .expect("Error while dumping pre optimized LLVM IR");
46 }
47 pub fn postopt_ir(&self, kind: &CompiledKind, module: &InkwellModule) {
48 let mut path = self.debug_dir.clone();
49 path.push(function_kind_to_filename(kind, ".postopt.ll"));
50 module
51 .print_to_file(&path)
52 .expect("Error while dumping post optimized LLVM IR");
53 }
54 pub fn obj_memory_buffer(&self, kind: &CompiledKind, memory_buffer: &InkwellMemoryBuffer) {
55 let mut path = self.debug_dir.clone();
56 path.push(function_kind_to_filename(kind, ".o"));
57 let mem_buf_slice = memory_buffer.as_slice();
58 let mut file =
59 File::create(path).expect("Error while creating debug object file from LLVM IR");
60 file.write_all(mem_buf_slice).unwrap();
61 }
62
63 pub fn asm_memory_buffer(&self, kind: &CompiledKind, asm_memory_buffer: &InkwellMemoryBuffer) {
64 let mut path = self.debug_dir.clone();
65 path.push(function_kind_to_filename(kind, ".s"));
66 let mem_buf_slice = asm_memory_buffer.as_slice();
67 let mut file =
68 File::create(path).expect("Error while creating debug assembly file from LLVM IR");
69 file.write_all(mem_buf_slice).unwrap();
70 }
71}
72
73#[derive(Debug, Clone)]
74pub struct LLVM {
75 pub(crate) enable_nan_canonicalization: bool,
76 pub(crate) enable_g0m0_opt: bool,
77 pub(crate) enable_verifier: bool,
78 pub(crate) enable_perfmap: bool,
79 pub(crate) opt_level: LLVMOptLevel,
80 is_pic: bool,
81 pub(crate) callbacks: Option<LLVMCallbacks>,
82 pub(crate) middlewares: Vec<Arc<dyn ModuleMiddleware>>,
84 pub(crate) num_threads: NonZero<usize>,
86}
87
88impl LLVM {
89 pub fn new() -> Self {
92 Self {
93 enable_nan_canonicalization: false,
94 enable_verifier: false,
95 enable_perfmap: false,
96 opt_level: LLVMOptLevel::Aggressive,
97 is_pic: false,
98 callbacks: None,
99 middlewares: vec![],
100 enable_g0m0_opt: false,
101 num_threads: std::thread::available_parallelism().unwrap_or(NonZero::new(1).unwrap()),
102 }
103 }
104
105 pub fn opt_level(&mut self, opt_level: LLVMOptLevel) -> &mut Self {
107 self.opt_level = opt_level;
108 self
109 }
110
111 pub fn enable_pass_params_opt(&mut self) -> &mut Self {
114 self.enable_g0m0_opt = true;
116 self
117 }
118
119 pub fn num_threads(&mut self, num_threads: NonZero<usize>) -> &mut Self {
120 self.num_threads = num_threads;
121 self
122 }
123
124 pub fn callbacks(&mut self, callbacks: Option<LLVMCallbacks>) -> &mut Self {
127 self.callbacks = callbacks;
128 self
129 }
130
131 fn reloc_mode(&self, binary_format: BinaryFormat) -> RelocMode {
132 if matches!(binary_format, BinaryFormat::Macho) {
133 return RelocMode::Static;
134 }
135
136 if self.is_pic {
137 RelocMode::PIC
138 } else {
139 RelocMode::Static
140 }
141 }
142
143 fn code_model(&self, binary_format: BinaryFormat) -> CodeModel {
144 if matches!(binary_format, BinaryFormat::Macho) {
150 return CodeModel::Default;
151 }
152
153 if self.is_pic {
154 CodeModel::Small
155 } else {
156 CodeModel::Large
157 }
158 }
159
160 pub(crate) fn target_operating_system(&self, target: &Target) -> OperatingSystem {
161 match target.triple().operating_system {
162 OperatingSystem::Darwin(deployment) if !self.is_pic => {
163 #[allow(clippy::match_single_binding)]
171 match target.triple().architecture {
172 Architecture::Aarch64(_) => OperatingSystem::Darwin(deployment),
173 _ => OperatingSystem::Linux,
174 }
175 }
176 other => other,
177 }
178 }
179
180 pub(crate) fn target_binary_format(&self, target: &Target) -> target_lexicon::BinaryFormat {
181 if self.is_pic {
182 target.triple().binary_format
183 } else {
184 match self.target_operating_system(target) {
185 OperatingSystem::Darwin(_) => target_lexicon::BinaryFormat::Macho,
186 _ => target_lexicon::BinaryFormat::Elf,
187 }
188 }
189 }
190
191 fn target_triple(&self, target: &Target) -> TargetTriple {
192 let architecture = if target.triple().architecture
193 == Architecture::Riscv64(target_lexicon::Riscv64Architecture::Riscv64gc)
194 {
195 target_lexicon::Architecture::Riscv64(target_lexicon::Riscv64Architecture::Riscv64)
196 } else {
197 target.triple().architecture
198 };
199 let operating_system = self.target_operating_system(target);
203 let binary_format = self.target_binary_format(target);
204
205 let triple = Triple {
206 architecture,
207 vendor: target.triple().vendor.clone(),
208 operating_system,
209 environment: target.triple().environment,
210 binary_format,
211 };
212 TargetTriple::create(&triple.to_string())
213 }
214
215 pub fn target_machine(&self, target: &Target) -> TargetMachine {
217 let triple = target.triple();
218 let cpu_features = &target.cpu_features();
219
220 match triple.architecture {
221 Architecture::X86_64 | Architecture::X86_32(_) => {
222 InkwellTarget::initialize_x86(&InitializationConfig {
223 asm_parser: true,
224 asm_printer: true,
225 base: true,
226 disassembler: true,
227 info: true,
228 machine_code: true,
229 })
230 }
231 Architecture::Aarch64(_) => InkwellTarget::initialize_aarch64(&InitializationConfig {
232 asm_parser: true,
233 asm_printer: true,
234 base: true,
235 disassembler: true,
236 info: true,
237 machine_code: true,
238 }),
239 Architecture::Riscv64(_) => InkwellTarget::initialize_riscv(&InitializationConfig {
240 asm_parser: true,
241 asm_printer: true,
242 base: true,
243 disassembler: true,
244 info: true,
245 machine_code: true,
246 }),
247 Architecture::LoongArch64 => {
248 InkwellTarget::initialize_loongarch(&InitializationConfig {
249 asm_parser: true,
250 asm_printer: true,
251 base: true,
252 disassembler: true,
253 info: true,
254 machine_code: true,
255 })
256 }
257 _ => unimplemented!("target {} not yet supported in Wasmer", triple),
266 }
267
268 let llvm_cpu_features = cpu_features
272 .iter()
273 .map(|feature| format!("+{feature}"))
274 .join(",");
275
276 let target_triple = self.target_triple(target);
277 let llvm_target = InkwellTarget::from_triple(&target_triple).unwrap();
278 let mut llvm_target_machine_options = TargetMachineOptions::new()
279 .set_cpu(match triple.architecture {
280 Architecture::Riscv64(_) => "generic-rv64",
281 Architecture::LoongArch64 => "generic-la64",
282 _ => "generic",
283 })
284 .set_features(match triple.architecture {
285 Architecture::Riscv64(_) => "+m,+a,+c,+d,+f",
286 Architecture::LoongArch64 => "+f,+d",
287 _ => &llvm_cpu_features,
288 })
289 .set_level(self.opt_level)
290 .set_reloc_mode(self.reloc_mode(self.target_binary_format(target)))
291 .set_code_model(match triple.architecture {
292 Architecture::LoongArch64 | Architecture::Riscv64(_) => CodeModel::Medium,
293 _ => self.code_model(self.target_binary_format(target)),
294 });
295 if let Architecture::Riscv64(_) = triple.architecture {
296 llvm_target_machine_options = llvm_target_machine_options.set_abi("lp64d");
297 }
298 llvm_target
299 .create_target_machine_from_options(&target_triple, llvm_target_machine_options)
300 .unwrap()
301 }
302}
303
304impl CompilerConfig for LLVM {
305 fn enable_pic(&mut self) {
307 self.is_pic = true;
310 }
311
312 fn enable_perfmap(&mut self) {
313 self.enable_perfmap = true
314 }
315
316 fn enable_verifier(&mut self) {
318 self.enable_verifier = true;
319 }
320
321 fn canonicalize_nans(&mut self, enable: bool) {
322 self.enable_nan_canonicalization = enable;
323 }
324
325 fn compiler(self: Box<Self>) -> Box<dyn Compiler> {
327 Box::new(LLVMCompiler::new(*self))
328 }
329
330 fn push_middleware(&mut self, middleware: Arc<dyn ModuleMiddleware>) {
332 self.middlewares.push(middleware);
333 }
334
335 fn supported_features_for_target(&self, _target: &Target) -> wasmer_types::Features {
336 let mut feats = Features::default();
337 feats.exceptions(true);
338 feats
339 }
340}
341
342impl Default for LLVM {
343 fn default() -> LLVM {
344 Self::new()
345 }
346}
347
348impl From<LLVM> for Engine {
349 fn from(config: LLVM) -> Self {
350 EngineBuilder::new(config).engine()
351 }
352}