/src/wasmer/lib/compiler-llvm/src/config.rs
Line | Count | Source (jump to first uncovered line) |
1 | | use crate::compiler::LLVMCompiler; |
2 | | use inkwell::targets::{ |
3 | | CodeModel, InitializationConfig, RelocMode, Target as InkwellTarget, TargetMachine, |
4 | | TargetTriple, |
5 | | }; |
6 | | pub use inkwell::OptimizationLevel as LLVMOptLevel; |
7 | | use itertools::Itertools; |
8 | | use std::fmt::Debug; |
9 | | use std::sync::Arc; |
10 | | use target_lexicon::Architecture; |
11 | | use wasmer_compiler::{Compiler, CompilerConfig, Engine, EngineBuilder, ModuleMiddleware}; |
12 | | use wasmer_types::{FunctionType, LocalFunctionIndex, Target, Triple}; |
13 | | |
14 | | /// The InkWell ModuleInfo type |
15 | | pub type InkwellModule<'ctx> = inkwell::module::Module<'ctx>; |
16 | | |
17 | | /// The InkWell MemoryBuffer type |
18 | | pub type InkwellMemoryBuffer = inkwell::memory_buffer::MemoryBuffer; |
19 | | |
20 | | /// The compiled function kind, used for debugging in the `LLVMCallbacks`. |
21 | | #[derive(Debug, Clone)] |
22 | | pub enum CompiledKind { |
23 | | // A locally-defined function in the Wasm file. |
24 | | Local(LocalFunctionIndex), |
25 | | // A function call trampoline for a given signature. |
26 | | FunctionCallTrampoline(FunctionType), |
27 | | // A dynamic function trampoline for a given signature. |
28 | | DynamicFunctionTrampoline(FunctionType), |
29 | | // An entire Wasm module. |
30 | | Module, |
31 | | } |
32 | | |
33 | | /// Callbacks to the different LLVM compilation phases. |
34 | | pub trait LLVMCallbacks: Debug + Send + Sync { |
35 | | fn preopt_ir(&self, function: &CompiledKind, module: &InkwellModule); |
36 | | fn postopt_ir(&self, function: &CompiledKind, module: &InkwellModule); |
37 | | fn obj_memory_buffer(&self, function: &CompiledKind, memory_buffer: &InkwellMemoryBuffer); |
38 | | } |
39 | | |
40 | | #[derive(Debug, Clone)] |
41 | | pub struct LLVM { |
42 | | pub(crate) enable_nan_canonicalization: bool, |
43 | | pub(crate) enable_verifier: bool, |
44 | | pub(crate) opt_level: LLVMOptLevel, |
45 | | is_pic: bool, |
46 | | pub(crate) callbacks: Option<Arc<dyn LLVMCallbacks>>, |
47 | | /// The middleware chain. |
48 | | pub(crate) middlewares: Vec<Arc<dyn ModuleMiddleware>>, |
49 | | } |
50 | | |
51 | | impl LLVM { |
52 | | /// Creates a new configuration object with the default configuration |
53 | | /// specified. |
54 | 5.22k | pub fn new() -> Self { |
55 | 5.22k | Self { |
56 | 5.22k | enable_nan_canonicalization: false, |
57 | 5.22k | enable_verifier: false, |
58 | 5.22k | opt_level: LLVMOptLevel::Aggressive, |
59 | 5.22k | is_pic: false, |
60 | 5.22k | callbacks: None, |
61 | 5.22k | middlewares: vec![], |
62 | 5.22k | } |
63 | 5.22k | } <wasmer_compiler_llvm::config::LLVM>::new Line | Count | Source | 54 | 214 | pub fn new() -> Self { | 55 | 214 | Self { | 56 | 214 | enable_nan_canonicalization: false, | 57 | 214 | enable_verifier: false, | 58 | 214 | opt_level: LLVMOptLevel::Aggressive, | 59 | 214 | is_pic: false, | 60 | 214 | callbacks: None, | 61 | 214 | middlewares: vec![], | 62 | 214 | } | 63 | 214 | } |
Unexecuted instantiation: <wasmer_compiler_llvm::config::LLVM>::new <wasmer_compiler_llvm::config::LLVM>::new Line | Count | Source | 54 | 5.00k | pub fn new() -> Self { | 55 | 5.00k | Self { | 56 | 5.00k | enable_nan_canonicalization: false, | 57 | 5.00k | enable_verifier: false, | 58 | 5.00k | opt_level: LLVMOptLevel::Aggressive, | 59 | 5.00k | is_pic: false, | 60 | 5.00k | callbacks: None, | 61 | 5.00k | middlewares: vec![], | 62 | 5.00k | } | 63 | 5.00k | } |
|
64 | | |
65 | | /// The optimization levels when optimizing the IR. |
66 | 0 | pub fn opt_level(&mut self, opt_level: LLVMOptLevel) -> &mut Self { |
67 | 0 | self.opt_level = opt_level; |
68 | 0 | self |
69 | 0 | } |
70 | | |
71 | | /// Callbacks that will triggered in the different compilation |
72 | | /// phases in LLVM. |
73 | 0 | pub fn callbacks(&mut self, callbacks: Option<Arc<dyn LLVMCallbacks>>) -> &mut Self { |
74 | 0 | self.callbacks = callbacks; |
75 | 0 | self |
76 | 0 | } |
77 | | |
78 | 171k | fn reloc_mode(&self) -> RelocMode { |
79 | 171k | if self.is_pic { |
80 | 0 | RelocMode::PIC |
81 | | } else { |
82 | 171k | RelocMode::Static |
83 | | } |
84 | 171k | } |
85 | | |
86 | 171k | fn code_model(&self) -> CodeModel { |
87 | 171k | // We normally use the large code model, but when targeting shared |
88 | 171k | // objects, we are required to use PIC. If we use PIC anyways, we lose |
89 | 171k | // any benefit from large code model and there's some cost on all |
90 | 171k | // platforms, plus some platforms (MachO) don't support PIC + large |
91 | 171k | // at all. |
92 | 171k | if self.is_pic { |
93 | 0 | CodeModel::Small |
94 | | } else { |
95 | 171k | CodeModel::Large |
96 | | } |
97 | 171k | } |
98 | | |
99 | 171k | fn target_triple(&self, target: &Target) -> TargetTriple { |
100 | 171k | let architecture = if target.triple().architecture |
101 | 171k | == Architecture::Riscv64(target_lexicon::Riscv64Architecture::Riscv64gc) |
102 | | { |
103 | 0 | target_lexicon::Architecture::Riscv64(target_lexicon::Riscv64Architecture::Riscv64) |
104 | | } else { |
105 | 171k | target.triple().architecture |
106 | | }; |
107 | | // Hack: we're using is_pic to determine whether this is a native |
108 | | // build or not. |
109 | 171k | let operating_system = if target.triple().operating_system |
110 | 171k | == wasmer_types::OperatingSystem::Darwin |
111 | 0 | && !self.is_pic |
112 | | { |
113 | | // LLVM detects static relocation + darwin + 64-bit and |
114 | | // force-enables PIC because MachO doesn't support that |
115 | | // combination. They don't check whether they're targeting |
116 | | // MachO, they check whether the OS is set to Darwin. |
117 | | // |
118 | | // Since both linux and darwin use SysV ABI, this should work. |
119 | | // but not in the case of Aarch64, there the ABI is slightly different |
120 | | #[allow(clippy::match_single_binding)] |
121 | 0 | match target.triple().architecture { |
122 | 0 | _ => wasmer_types::OperatingSystem::Linux, |
123 | | } |
124 | | } else { |
125 | 171k | target.triple().operating_system |
126 | | }; |
127 | 171k | let binary_format = if self.is_pic { |
128 | 0 | target.triple().binary_format |
129 | | } else { |
130 | 171k | target_lexicon::BinaryFormat::Elf |
131 | | }; |
132 | 171k | let triple = Triple { |
133 | 171k | architecture, |
134 | 171k | vendor: target.triple().vendor.clone(), |
135 | 171k | operating_system, |
136 | 171k | environment: target.triple().environment, |
137 | 171k | binary_format, |
138 | 171k | }; |
139 | 171k | TargetTriple::create(&triple.to_string()) |
140 | 171k | } |
141 | | |
142 | | /// Generates the target machine for the current target |
143 | 171k | pub fn target_machine(&self, target: &Target) -> TargetMachine { |
144 | 171k | let triple = target.triple(); |
145 | 171k | let cpu_features = &target.cpu_features(); |
146 | 171k | |
147 | 171k | match triple.architecture { |
148 | | Architecture::X86_64 | Architecture::X86_32(_) => { |
149 | 171k | InkwellTarget::initialize_x86(&InitializationConfig { |
150 | 171k | asm_parser: true, |
151 | 171k | asm_printer: true, |
152 | 171k | base: true, |
153 | 171k | disassembler: true, |
154 | 171k | info: true, |
155 | 171k | machine_code: true, |
156 | 171k | }) |
157 | | } |
158 | 0 | Architecture::Aarch64(_) => InkwellTarget::initialize_aarch64(&InitializationConfig { |
159 | 0 | asm_parser: true, |
160 | 0 | asm_printer: true, |
161 | 0 | base: true, |
162 | 0 | disassembler: true, |
163 | 0 | info: true, |
164 | 0 | machine_code: true, |
165 | 0 | }), |
166 | 0 | Architecture::Riscv64(_) => InkwellTarget::initialize_riscv(&InitializationConfig { |
167 | 0 | asm_parser: true, |
168 | 0 | asm_printer: true, |
169 | 0 | base: true, |
170 | 0 | disassembler: true, |
171 | 0 | info: true, |
172 | 0 | machine_code: true, |
173 | 0 | }), |
174 | | // Architecture::Arm(_) => InkwellTarget::initialize_arm(&InitializationConfig { |
175 | | // asm_parser: true, |
176 | | // asm_printer: true, |
177 | | // base: true, |
178 | | // disassembler: true, |
179 | | // info: true, |
180 | | // machine_code: true, |
181 | | // }), |
182 | 0 | _ => unimplemented!("target {} not yet supported in Wasmer", triple), |
183 | | } |
184 | | |
185 | | // The CPU features formatted as LLVM strings |
186 | | // We can safely map to gcc-like features as the CPUFeatures |
187 | | // are compliant with the same string representations as gcc. |
188 | 171k | let llvm_cpu_features = cpu_features |
189 | 171k | .iter() |
190 | 1.88M | .map(|feature| format!("+{}", feature.to_string())) |
191 | 171k | .join(","); |
192 | 171k | |
193 | 171k | let target_triple = self.target_triple(target); |
194 | 171k | let llvm_target = InkwellTarget::from_triple(&target_triple).unwrap(); |
195 | 171k | let llvm_target_machine = llvm_target |
196 | 171k | .create_target_machine( |
197 | 171k | &target_triple, |
198 | 171k | match triple.architecture { |
199 | 0 | Architecture::Riscv64(_) => "generic-rv64", |
200 | 171k | _ => "generic", |
201 | | }, |
202 | 171k | match triple.architecture { |
203 | 0 | Architecture::Riscv64(_) => "+m,+a,+c,+d,+f", |
204 | 171k | _ => &llvm_cpu_features, |
205 | | }, |
206 | 171k | self.opt_level, |
207 | 171k | self.reloc_mode(), |
208 | 171k | match triple.architecture { |
209 | 0 | Architecture::Riscv64(_) => CodeModel::Medium, |
210 | 171k | _ => self.code_model(), |
211 | | }, |
212 | | ) |
213 | 171k | .unwrap(); |
214 | 171k | |
215 | 171k | if let Architecture::Riscv64(_) = triple.architecture { |
216 | | // TODO: totally non-portable way to change ABI |
217 | | unsafe { |
218 | | // This structure mimic the internal structure from inkwell |
219 | | // that is defined as |
220 | | // #[derive(Debug)] |
221 | | // pub struct TargetMachine { |
222 | | // pub(crate) target_machine: LLVMTargetMachineRef, |
223 | | // } |
224 | | pub struct MyTargetMachine { |
225 | | pub target_machine: *const u8, |
226 | | } |
227 | | // It is use to live patch the create LLVMTargetMachine |
228 | | // to hard change the ABI and force "-mabi=lp64d" ABI |
229 | | // instead of the default that don't use float registers |
230 | | // because there is no current way to do this change |
231 | | |
232 | 0 | let my_target_machine: MyTargetMachine = std::mem::transmute(llvm_target_machine); |
233 | 0 |
|
234 | 0 | *((my_target_machine.target_machine as *mut u8).offset(0x410) as *mut u64) = 5; |
235 | 0 | std::ptr::copy_nonoverlapping( |
236 | 0 | "lp64d\0".as_ptr(), |
237 | 0 | (my_target_machine.target_machine as *mut u8).offset(0x418), |
238 | 0 | 6, |
239 | 0 | ); |
240 | 0 |
|
241 | 0 | std::mem::transmute(my_target_machine) |
242 | | } |
243 | | } else { |
244 | 171k | llvm_target_machine |
245 | | } |
246 | 171k | } |
247 | | } |
248 | | |
249 | | impl CompilerConfig for LLVM { |
250 | | /// Emit code suitable for dlopen. |
251 | 0 | fn enable_pic(&mut self) { |
252 | 0 | // TODO: although we can emit PIC, the object file parser does not yet |
253 | 0 | // support all the relocations. |
254 | 0 | self.is_pic = true; |
255 | 0 | } Unexecuted instantiation: <wasmer_compiler_llvm::config::LLVM as wasmer_compiler::compiler::CompilerConfig>::enable_pic Unexecuted instantiation: <wasmer_compiler_llvm::config::LLVM as wasmer_compiler::compiler::CompilerConfig>::enable_pic |
256 | | |
257 | | /// Whether to verify compiler IR. |
258 | 5.22k | fn enable_verifier(&mut self) { |
259 | 5.22k | self.enable_verifier = true; |
260 | 5.22k | } <wasmer_compiler_llvm::config::LLVM as wasmer_compiler::compiler::CompilerConfig>::enable_verifier Line | Count | Source | 258 | 214 | fn enable_verifier(&mut self) { | 259 | 214 | self.enable_verifier = true; | 260 | 214 | } |
Unexecuted instantiation: <wasmer_compiler_llvm::config::LLVM as wasmer_compiler::compiler::CompilerConfig>::enable_verifier <wasmer_compiler_llvm::config::LLVM as wasmer_compiler::compiler::CompilerConfig>::enable_verifier Line | Count | Source | 258 | 5.00k | fn enable_verifier(&mut self) { | 259 | 5.00k | self.enable_verifier = true; | 260 | 5.00k | } |
|
261 | | |
262 | 5.22k | fn canonicalize_nans(&mut self, enable: bool) { |
263 | 5.22k | self.enable_nan_canonicalization = enable; |
264 | 5.22k | } <wasmer_compiler_llvm::config::LLVM as wasmer_compiler::compiler::CompilerConfig>::canonicalize_nans Line | Count | Source | 262 | 214 | fn canonicalize_nans(&mut self, enable: bool) { | 263 | 214 | self.enable_nan_canonicalization = enable; | 264 | 214 | } |
Unexecuted instantiation: <wasmer_compiler_llvm::config::LLVM as wasmer_compiler::compiler::CompilerConfig>::canonicalize_nans <wasmer_compiler_llvm::config::LLVM as wasmer_compiler::compiler::CompilerConfig>::canonicalize_nans Line | Count | Source | 262 | 5.00k | fn canonicalize_nans(&mut self, enable: bool) { | 263 | 5.00k | self.enable_nan_canonicalization = enable; | 264 | 5.00k | } |
|
265 | | |
266 | | /// Transform it into the compiler. |
267 | 5.22k | fn compiler(self: Box<Self>) -> Box<dyn Compiler> { |
268 | 5.22k | Box::new(LLVMCompiler::new(*self)) |
269 | 5.22k | } |
270 | | |
271 | | /// Pushes a middleware onto the back of the middleware chain. |
272 | 0 | fn push_middleware(&mut self, middleware: Arc<dyn ModuleMiddleware>) { |
273 | 0 | self.middlewares.push(middleware); |
274 | 0 | } |
275 | | } |
276 | | |
277 | | impl Default for LLVM { |
278 | 5.22k | fn default() -> LLVM { |
279 | 5.22k | Self::new() |
280 | 5.22k | } <wasmer_compiler_llvm::config::LLVM as core::default::Default>::default Line | Count | Source | 278 | 214 | fn default() -> LLVM { | 279 | 214 | Self::new() | 280 | 214 | } |
Unexecuted instantiation: <wasmer_compiler_llvm::config::LLVM as core::default::Default>::default <wasmer_compiler_llvm::config::LLVM as core::default::Default>::default Line | Count | Source | 278 | 5.00k | fn default() -> LLVM { | 279 | 5.00k | Self::new() | 280 | 5.00k | } |
|
281 | | } |
282 | | |
283 | | impl From<LLVM> for Engine { |
284 | 214 | fn from(config: LLVM) -> Self { |
285 | 214 | EngineBuilder::new(config).engine() |
286 | 214 | } |
287 | | } |