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::fmt::Debug;
9use std::sync::Arc;
10use wasmer_compiler::{
11 types::target::{Architecture, OperatingSystem, Target, Triple},
12 Compiler, CompilerConfig, Engine, EngineBuilder, ModuleMiddleware,
13};
14use wasmer_types::{FunctionType, LocalFunctionIndex};
15
16pub type InkwellModule<'ctx> = inkwell::module::Module<'ctx>;
18
19pub type InkwellMemoryBuffer = inkwell::memory_buffer::MemoryBuffer;
21
22#[derive(Debug, Clone)]
24pub enum CompiledKind {
25 Local(LocalFunctionIndex),
27 FunctionCallTrampoline(FunctionType),
29 DynamicFunctionTrampoline(FunctionType),
31 Module,
33}
34
35pub trait LLVMCallbacks: Debug + Send + Sync {
37 fn preopt_ir(&self, function: &CompiledKind, module: &InkwellModule);
38 fn postopt_ir(&self, function: &CompiledKind, module: &InkwellModule);
39 fn obj_memory_buffer(&self, function: &CompiledKind, memory_buffer: &InkwellMemoryBuffer);
40}
41
42#[derive(Debug, Clone)]
43pub struct LLVM {
44 pub(crate) enable_nan_canonicalization: bool,
45 pub(crate) enable_verifier: bool,
46 pub(crate) opt_level: LLVMOptLevel,
47 is_pic: bool,
48 pub(crate) callbacks: Option<Arc<dyn LLVMCallbacks>>,
49 pub(crate) middlewares: Vec<Arc<dyn ModuleMiddleware>>,
51}
52
53impl LLVM {
54 pub fn new() -> Self {
57 Self {
58 enable_nan_canonicalization: false,
59 enable_verifier: false,
60 opt_level: LLVMOptLevel::Aggressive,
61 is_pic: false,
62 callbacks: None,
63 middlewares: vec![],
64 }
65 }
66
67 pub fn opt_level(&mut self, opt_level: LLVMOptLevel) -> &mut Self {
69 self.opt_level = opt_level;
70 self
71 }
72
73 pub fn callbacks(&mut self, callbacks: Option<Arc<dyn LLVMCallbacks>>) -> &mut Self {
76 self.callbacks = callbacks;
77 self
78 }
79
80 fn reloc_mode(&self) -> RelocMode {
81 if self.is_pic {
82 RelocMode::PIC
83 } else {
84 RelocMode::Static
85 }
86 }
87
88 fn code_model(&self) -> CodeModel {
89 if self.is_pic {
95 CodeModel::Small
96 } else {
97 CodeModel::Large
98 }
99 }
100
101 fn target_triple(&self, target: &Target) -> TargetTriple {
102 let architecture = if target.triple().architecture
103 == Architecture::Riscv64(target_lexicon::Riscv64Architecture::Riscv64gc)
104 {
105 target_lexicon::Architecture::Riscv64(target_lexicon::Riscv64Architecture::Riscv64)
106 } else {
107 target.triple().architecture
108 };
109 let operating_system =
113 if target.triple().operating_system == OperatingSystem::Darwin && !self.is_pic {
114 #[allow(clippy::match_single_binding)]
122 match target.triple().architecture {
123 Architecture::Aarch64(_) => OperatingSystem::Darwin,
124 _ => OperatingSystem::Linux,
125 }
126 } else {
127 target.triple().operating_system
128 };
129
130 let binary_format = if self.is_pic {
131 target.triple().binary_format
132 } else {
133 target_lexicon::BinaryFormat::Elf
134 };
135 let triple = Triple {
136 architecture,
137 vendor: target.triple().vendor.clone(),
138 operating_system,
139 environment: target.triple().environment,
140 binary_format,
141 };
142 TargetTriple::create(&triple.to_string())
143 }
144
145 pub fn target_machine(&self, target: &Target) -> TargetMachine {
147 let triple = target.triple();
148 let cpu_features = &target.cpu_features();
149
150 match triple.architecture {
151 Architecture::X86_64 | Architecture::X86_32(_) => {
152 InkwellTarget::initialize_x86(&InitializationConfig {
153 asm_parser: true,
154 asm_printer: true,
155 base: true,
156 disassembler: true,
157 info: true,
158 machine_code: true,
159 })
160 }
161 Architecture::Aarch64(_) => InkwellTarget::initialize_aarch64(&InitializationConfig {
162 asm_parser: true,
163 asm_printer: true,
164 base: true,
165 disassembler: true,
166 info: true,
167 machine_code: true,
168 }),
169 Architecture::Riscv64(_) => InkwellTarget::initialize_riscv(&InitializationConfig {
170 asm_parser: true,
171 asm_printer: true,
172 base: true,
173 disassembler: true,
174 info: true,
175 machine_code: true,
176 }),
177 Architecture::LoongArch64 => {
178 InkwellTarget::initialize_loongarch(&InitializationConfig {
179 asm_parser: true,
180 asm_printer: true,
181 base: true,
182 disassembler: true,
183 info: true,
184 machine_code: true,
185 })
186 }
187 _ => unimplemented!("target {} not yet supported in Wasmer", triple),
196 }
197
198 let llvm_cpu_features = cpu_features
202 .iter()
203 .map(|feature| format!("+{}", feature))
204 .join(",");
205
206 let target_triple = self.target_triple(target);
207 let llvm_target = InkwellTarget::from_triple(&target_triple).unwrap();
208 let llvm_target_machine = llvm_target
209 .create_target_machine(
210 &target_triple,
211 match triple.architecture {
212 Architecture::Riscv64(_) => "generic-rv64",
213 Architecture::LoongArch64 => "generic-la64",
214 _ => "generic",
215 },
216 match triple.architecture {
217 Architecture::Riscv64(_) => "+m,+a,+c,+d,+f",
218 Architecture::LoongArch64 => "+f,+d",
219 _ => &llvm_cpu_features,
220 },
221 self.opt_level,
222 self.reloc_mode(),
223 match triple.architecture {
224 Architecture::LoongArch64 | Architecture::Riscv64(_) => CodeModel::Medium,
225 _ => self.code_model(),
226 },
227 )
228 .unwrap();
229
230 if let Architecture::Riscv64(_) = triple.architecture {
231 unsafe {
233 pub struct MyTargetMachine {
240 pub target_machine: *const u8,
241 }
242 let my_target_machine: MyTargetMachine = std::mem::transmute(llvm_target_machine);
248
249 *((my_target_machine.target_machine as *mut u8).offset(0x410) as *mut u64) = 5;
250 std::ptr::copy_nonoverlapping(
251 "lp64d\0".as_ptr(),
252 (my_target_machine.target_machine as *mut u8).offset(0x418),
253 6,
254 );
255
256 std::mem::transmute::<MyTargetMachine, inkwell::targets::TargetMachine>(
257 my_target_machine,
258 )
259 }
260 } else {
261 llvm_target_machine
262 }
263 }
264}
265
266impl CompilerConfig for LLVM {
267 fn enable_pic(&mut self) {
269 self.is_pic = true;
272 }
273
274 fn enable_verifier(&mut self) {
276 self.enable_verifier = true;
277 }
278
279 fn canonicalize_nans(&mut self, enable: bool) {
280 self.enable_nan_canonicalization = enable;
281 }
282
283 fn compiler(self: Box<Self>) -> Box<dyn Compiler> {
285 Box::new(LLVMCompiler::new(*self))
286 }
287
288 fn push_middleware(&mut self, middleware: Arc<dyn ModuleMiddleware>) {
290 self.middlewares.push(middleware);
291 }
292}
293
294impl Default for LLVM {
295 fn default() -> LLVM {
296 Self::new()
297 }
298}
299
300impl From<LLVM> for Engine {
301 fn from(config: LLVM) -> Self {
302 EngineBuilder::new(config).engine()
303 }
304}