1use crate::compiler::LLVMCompiler;
2use inkwell::targets::{
3 CodeModel, InitializationConfig, RelocMode, Target as InkwellTarget, TargetMachine,
4 TargetTriple,
5};
6pub use inkwell::OptimizationLevel as LLVMOptLevel;
7use itertools::Itertools;
8use std::sync::Arc;
9use std::{fmt::Debug, num::NonZero};
10use target_lexicon::BinaryFormat;
11use wasmer_compiler::{Compiler, CompilerConfig, Engine, EngineBuilder, ModuleMiddleware};
12use wasmer_types::{
13 target::{Architecture, OperatingSystem, Target, Triple},
14 Features, FunctionType, LocalFunctionIndex,
15};
16
17pub type InkwellModule<'ctx> = inkwell::module::Module<'ctx>;
19
20pub type InkwellMemoryBuffer = inkwell::memory_buffer::MemoryBuffer;
22
23#[derive(Debug, Clone)]
25pub enum CompiledKind {
26 Local(LocalFunctionIndex),
28 FunctionCallTrampoline(FunctionType),
30 DynamicFunctionTrampoline(FunctionType),
32 Module,
34}
35
36pub trait LLVMCallbacks: Debug + Send + Sync {
38 fn preopt_ir(&self, function: &CompiledKind, module: &InkwellModule);
39 fn postopt_ir(&self, function: &CompiledKind, module: &InkwellModule);
40 fn obj_memory_buffer(&self, function: &CompiledKind, memory_buffer: &InkwellMemoryBuffer);
41}
42
43#[derive(Debug, Clone)]
44pub struct LLVM {
45 pub(crate) enable_nan_canonicalization: bool,
46 pub(crate) enable_g0m0_opt: bool,
47 pub(crate) enable_verifier: bool,
48 pub(crate) enable_perfmap: bool,
49 pub(crate) opt_level: LLVMOptLevel,
50 is_pic: bool,
51 pub(crate) callbacks: Option<Arc<dyn LLVMCallbacks>>,
52 pub(crate) middlewares: Vec<Arc<dyn ModuleMiddleware>>,
54 pub(crate) num_threads: NonZero<usize>,
56}
57
58impl LLVM {
59 pub fn new() -> Self {
62 Self {
63 enable_nan_canonicalization: false,
64 enable_verifier: false,
65 enable_perfmap: false,
66 opt_level: LLVMOptLevel::Aggressive,
67 is_pic: false,
68 callbacks: None,
69 middlewares: vec![],
70 enable_g0m0_opt: false,
71 num_threads: std::thread::available_parallelism().unwrap_or(NonZero::new(1).unwrap()),
72 }
73 }
74
75 pub fn opt_level(&mut self, opt_level: LLVMOptLevel) -> &mut Self {
77 self.opt_level = opt_level;
78 self
79 }
80
81 pub fn enable_pass_params_opt(&mut self) -> &mut Self {
84 self.enable_g0m0_opt = true;
86 self
87 }
88
89 pub fn num_threads(&mut self, num_threads: NonZero<usize>) -> &mut Self {
90 self.num_threads = num_threads;
91 self
92 }
93
94 pub fn callbacks(&mut self, callbacks: Option<Arc<dyn LLVMCallbacks>>) -> &mut Self {
97 self.callbacks = callbacks;
98 self
99 }
100
101 fn reloc_mode(&self, binary_format: BinaryFormat) -> RelocMode {
102 if matches!(binary_format, BinaryFormat::Macho) {
103 return RelocMode::Static;
104 }
105
106 if self.is_pic {
107 RelocMode::PIC
108 } else {
109 RelocMode::Static
110 }
111 }
112
113 fn code_model(&self, binary_format: BinaryFormat) -> CodeModel {
114 if matches!(binary_format, BinaryFormat::Macho) {
120 return CodeModel::Default;
121 }
122
123 if self.is_pic {
124 CodeModel::Small
125 } else {
126 CodeModel::Large
127 }
128 }
129
130 pub(crate) fn target_operating_system(&self, target: &Target) -> OperatingSystem {
131 if target.triple().operating_system == OperatingSystem::Darwin && !self.is_pic {
132 #[allow(clippy::match_single_binding)]
140 match target.triple().architecture {
141 Architecture::Aarch64(_) => OperatingSystem::Darwin,
142 _ => OperatingSystem::Linux,
143 }
144 } else {
145 target.triple().operating_system
146 }
147 }
148
149 pub(crate) fn target_binary_format(&self, target: &Target) -> target_lexicon::BinaryFormat {
150 if self.is_pic {
151 target.triple().binary_format
152 } else {
153 match self.target_operating_system(target) {
154 OperatingSystem::Darwin => target_lexicon::BinaryFormat::Macho,
155 _ => target_lexicon::BinaryFormat::Elf,
156 }
157 }
158 }
159
160 fn target_triple(&self, target: &Target) -> TargetTriple {
161 let architecture = if target.triple().architecture
162 == Architecture::Riscv64(target_lexicon::Riscv64Architecture::Riscv64gc)
163 {
164 target_lexicon::Architecture::Riscv64(target_lexicon::Riscv64Architecture::Riscv64)
165 } else {
166 target.triple().architecture
167 };
168 let operating_system = self.target_operating_system(target);
172 let binary_format = self.target_binary_format(target);
173
174 let triple = Triple {
175 architecture,
176 vendor: target.triple().vendor.clone(),
177 operating_system,
178 environment: target.triple().environment,
179 binary_format,
180 };
181 TargetTriple::create(&triple.to_string())
182 }
183
184 pub fn target_machine(&self, target: &Target) -> TargetMachine {
186 let triple = target.triple();
187 let cpu_features = &target.cpu_features();
188
189 match triple.architecture {
190 Architecture::X86_64 | Architecture::X86_32(_) => {
191 InkwellTarget::initialize_x86(&InitializationConfig {
192 asm_parser: true,
193 asm_printer: true,
194 base: true,
195 disassembler: true,
196 info: true,
197 machine_code: true,
198 })
199 }
200 Architecture::Aarch64(_) => InkwellTarget::initialize_aarch64(&InitializationConfig {
201 asm_parser: true,
202 asm_printer: true,
203 base: true,
204 disassembler: true,
205 info: true,
206 machine_code: true,
207 }),
208 Architecture::Riscv64(_) => InkwellTarget::initialize_riscv(&InitializationConfig {
209 asm_parser: true,
210 asm_printer: true,
211 base: true,
212 disassembler: true,
213 info: true,
214 machine_code: true,
215 }),
216 Architecture::LoongArch64 => {
217 InkwellTarget::initialize_loongarch(&InitializationConfig {
218 asm_parser: true,
219 asm_printer: true,
220 base: true,
221 disassembler: true,
222 info: true,
223 machine_code: true,
224 })
225 }
226 _ => unimplemented!("target {} not yet supported in Wasmer", triple),
235 }
236
237 let llvm_cpu_features = cpu_features
241 .iter()
242 .map(|feature| format!("+{feature}"))
243 .join(",");
244
245 let target_triple = self.target_triple(target);
246 let llvm_target = InkwellTarget::from_triple(&target_triple).unwrap();
247 let llvm_target_machine = llvm_target
248 .create_target_machine(
249 &target_triple,
250 match triple.architecture {
251 Architecture::Riscv64(_) => "generic-rv64",
252 Architecture::LoongArch64 => "generic-la64",
253 _ => "generic",
254 },
255 match triple.architecture {
256 Architecture::Riscv64(_) => "+m,+a,+c,+d,+f",
257 Architecture::LoongArch64 => "+f,+d",
258 _ => &llvm_cpu_features,
259 },
260 self.opt_level,
261 self.reloc_mode(self.target_binary_format(target)),
262 match triple.architecture {
263 Architecture::LoongArch64 | Architecture::Riscv64(_) => CodeModel::Medium,
264 _ => self.code_model(self.target_binary_format(target)),
265 },
266 )
267 .unwrap();
268
269 if let Architecture::Riscv64(_) = triple.architecture {
270 unsafe {
272 pub struct MyTargetMachine {
279 pub target_machine: *const u8,
280 }
281 let my_target_machine: MyTargetMachine = std::mem::transmute(llvm_target_machine);
287
288 *((my_target_machine.target_machine as *mut u8).offset(0x410) as *mut u64) = 5;
289 std::ptr::copy_nonoverlapping(
290 "lp64d\0".as_ptr(),
291 (my_target_machine.target_machine as *mut u8).offset(0x418),
292 6,
293 );
294
295 std::mem::transmute::<MyTargetMachine, inkwell::targets::TargetMachine>(
296 my_target_machine,
297 )
298 }
299 } else {
300 llvm_target_machine
301 }
302 }
303}
304
305impl CompilerConfig for LLVM {
306 fn enable_pic(&mut self) {
308 self.is_pic = true;
311 }
312
313 fn enable_perfmap(&mut self) {
314 self.enable_perfmap = true
315 }
316
317 fn enable_verifier(&mut self) {
319 self.enable_verifier = true;
320 }
321
322 fn canonicalize_nans(&mut self, enable: bool) {
323 self.enable_nan_canonicalization = enable;
324 }
325
326 fn compiler(self: Box<Self>) -> Box<dyn Compiler> {
328 Box::new(LLVMCompiler::new(*self))
329 }
330
331 fn push_middleware(&mut self, middleware: Arc<dyn ModuleMiddleware>) {
333 self.middlewares.push(middleware);
334 }
335
336 fn supported_features_for_target(&self, _target: &Target) -> wasmer_types::Features {
337 let mut feats = Features::default();
338 feats.exceptions(true);
339 feats
340 }
341}
342
343impl Default for LLVM {
344 fn default() -> LLVM {
345 Self::new()
346 }
347}
348
349impl From<LLVM> for Engine {
350 fn from(config: LLVM) -> Self {
351 EngineBuilder::new(config).engine()
352 }
353}