Coverage Report

Created: 2024-10-16 07:58

/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
}