1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
use crate::{
    error::{CompileResult, RuntimeError},
    module::ModuleInner,
    state::ModuleStateMap,
    typed_func::Wasm,
    types::{LocalFuncIndex, SigIndex},
    vm,
};

use crate::{
    cache::{Artifact, Error as CacheError},
    codegen::BreakpointMap,
    module::ModuleInfo,
    sys::Memory,
};
use std::fmt;
use std::{any::Any, ptr::NonNull};

use std::collections::HashMap;

pub mod sys {
    pub use crate::sys::*;
}
pub use crate::sig_registry::SigRegistry;

/// The target architecture for code generation.
#[derive(Copy, Clone, Debug)]
pub enum Architecture {
    /// x86-64.
    X64,

    /// Aarch64 (ARM64).
    Aarch64,
}

/// The type of an inline breakpoint.
#[repr(u8)]
#[derive(Copy, Clone, Debug)]
pub enum InlineBreakpointType {
    /// A middleware invocation breakpoint.
    Middleware,
}

/// Information of an inline breakpoint.
#[derive(Clone, Debug)]
pub struct InlineBreakpoint {
    /// Size in bytes taken by this breakpoint's instruction sequence.
    pub size: usize,

    /// Type of the inline breakpoint.
    pub ty: InlineBreakpointType,
}

/// This type cannot be constructed from
/// outside the runtime crate.
pub struct Token {
    _private: (),
}

impl Token {
    pub(crate) fn generate() -> Self {
        Self { _private: () }
    }
}

#[derive(Copy, Clone, Debug)]
pub enum MemoryBoundCheckMode {
    Default,
    Enable,
    Disable,
}

impl Default for MemoryBoundCheckMode {
    fn default() -> MemoryBoundCheckMode {
        MemoryBoundCheckMode::Default
    }
}

/// Controls which experimental features will be enabled.
/// Features usually have a corresponding [WebAssembly proposal][wasm-props].
///
/// [wasm-props]: https://github.com/WebAssembly/proposals
#[derive(Debug, Default)]
pub struct Features {
    /// Whether support for the [SIMD proposal][simd-prop] is enabled.
    ///
    /// [simd-prop]: https://github.com/webassembly/simd
    pub simd: bool,
    /// Whether support for the [threads proposal][threads-prop] is enabled.
    ///
    /// [threads-prop]: https://github.com/webassembly/threads
    pub threads: bool,
}

/// Use this to point to a compiler config struct provided by the backend.
/// The backend struct must support runtime reflection with `Any`, which is any
/// struct that does not contain a non-`'static` reference.
#[derive(Debug)]
pub struct BackendCompilerConfig(pub Box<dyn Any + 'static>);

impl BackendCompilerConfig {
    /// Obtain the backend-specific compiler config struct.
    pub fn get_specific<T: 'static>(&self) -> Option<&T> {
        self.0.downcast_ref::<T>()
    }
}

/// Configuration data for the compiler
#[derive(Debug)]
pub struct CompilerConfig {
    /// Symbol information generated from emscripten; used for more detailed debug messages
    pub symbol_map: Option<HashMap<u32, String>>,

    /// How to make the decision whether to emit bounds checks for memory accesses.
    pub memory_bound_check_mode: MemoryBoundCheckMode,

    /// Whether to generate explicit native stack checks against `stack_lower_bound` in `InternalCtx`.
    ///
    /// Usually it's adequate to use hardware memory protection mechanisms such as `mprotect` on Unix to
    /// prevent stack overflow. But for low-level environments, e.g. the kernel, faults are generally
    /// not expected and relying on hardware memory protection would add too much complexity.
    pub enforce_stack_check: bool,

    /// Whether to enable state tracking. Necessary for managed mode.
    pub track_state: bool,

    /// Whether to enable full preemption checkpoint generation.
    ///
    /// This inserts checkpoints at critical locations such as loop backedges and function calls,
    /// allowing preemptive unwinding/task switching.
    ///
    /// When enabled there can be a small amount of runtime performance overhead.
    pub full_preemption: bool,

    /// Always choose a unique bit representation for NaN.
    /// Enabling this makes execution deterministic but increases runtime overhead.
    pub nan_canonicalization: bool,

    /// Turns on verification that is done by default when `debug_assertions` are enabled
    /// (for example in 'debug' builds). Disabling this flag will make compilation faster
    /// in debug mode at the cost of not detecting bugs in the compiler.
    ///
    /// These verifications are disabled by default in 'release' builds.
    pub enable_verification: bool,

    pub features: Features,

    // Target info. Presently only supported by LLVM.
    pub triple: Option<String>,
    pub cpu_name: Option<String>,
    pub cpu_features: Option<String>,

    pub backend_specific_config: Option<BackendCompilerConfig>,

    pub generate_debug_info: bool,
}

impl Default for CompilerConfig {
    fn default() -> Self {
        Self {
            symbol_map: Default::default(),
            memory_bound_check_mode: Default::default(),
            enforce_stack_check: Default::default(),
            track_state: Default::default(),
            full_preemption: Default::default(),
            nan_canonicalization: Default::default(),
            features: Default::default(),
            triple: Default::default(),
            cpu_name: Default::default(),
            cpu_features: Default::default(),
            backend_specific_config: Default::default(),
            generate_debug_info: Default::default(),

            // Default verification to 'on' when testing or running in debug mode.
            // NOTE: cfg(test) probably does nothing when not running `cargo test`
            //       on this crate
            enable_verification: cfg!(test) || cfg!(debug_assertions),
        }
    }
}

impl CompilerConfig {
    /// Use this to check if we should be generating debug information.
    /// This function takes into account the features that runtime-core was
    /// compiled with in addition to the value of the `generate_debug_info` field.
    pub(crate) fn should_generate_debug_info(&self) -> bool {
        cfg!(feature = "generate-debug-information") && self.generate_debug_info
    }
}

/// An exception table for a `RunnableModule`.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct ExceptionTable {
    /// Mappings from offsets in generated machine code to the corresponding exception code.
    pub offset_to_code: HashMap<usize, ExceptionCode>,
}

impl ExceptionTable {
    pub fn new() -> Self {
        Self::default()
    }
}

/// The code of an exception.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub enum ExceptionCode {
    /// An `unreachable` opcode was executed.
    Unreachable = 0,
    /// Call indirect incorrect signature trap.
    IncorrectCallIndirectSignature = 1,
    /// Memory out of bounds trap.
    MemoryOutOfBounds = 2,
    /// Call indirect out of bounds trap.
    CallIndirectOOB = 3,
    /// An arithmetic exception, e.g. divided by zero.
    IllegalArithmetic = 4,
    /// Misaligned atomic access trap.
    MisalignedAtomicAccess = 5,
}

impl fmt::Display for ExceptionCode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{}",
            match self {
                ExceptionCode::Unreachable => "unreachable",
                ExceptionCode::IncorrectCallIndirectSignature => {
                    "incorrect `call_indirect` signature"
                }
                ExceptionCode::MemoryOutOfBounds => "memory out-of-bounds access",
                ExceptionCode::CallIndirectOOB => "`call_indirect` out-of-bounds",
                ExceptionCode::IllegalArithmetic => "illegal arithmetic operation",
                ExceptionCode::MisalignedAtomicAccess => "misaligned atomic access",
            }
        )
    }
}

pub trait Compiler {
    /// Compiles a `Module` from WebAssembly binary format.
    /// The `CompileToken` parameter ensures that this can only
    /// be called from inside the runtime.
    fn compile(
        &self,
        wasm: &[u8],
        comp_conf: CompilerConfig,
        _: Token,
    ) -> CompileResult<ModuleInner>;

    unsafe fn from_cache(&self, cache: Artifact, _: Token) -> Result<ModuleInner, CacheError>;
}

pub trait RunnableModule: Send + Sync {
    /// This returns a pointer to the function designated by the `local_func_index`
    /// parameter.
    fn get_func(
        &self,
        info: &ModuleInfo,
        local_func_index: LocalFuncIndex,
    ) -> Option<NonNull<vm::Func>>;

    fn get_module_state_map(&self) -> Option<ModuleStateMap> {
        None
    }

    fn get_breakpoints(&self) -> Option<BreakpointMap> {
        None
    }

    fn get_exception_table(&self) -> Option<&ExceptionTable> {
        None
    }

    unsafe fn patch_local_function(&self, _idx: usize, _target_address: usize) -> bool {
        false
    }

    /// A wasm trampoline contains the necessary data to dynamically call an exported wasm function.
    /// Given a particular signature index, we return a trampoline that is matched with that
    /// signature and an invoke function that can call the trampoline.
    fn get_trampoline(&self, info: &ModuleInfo, sig_index: SigIndex) -> Option<Wasm>;

    /// Trap an error.
    unsafe fn do_early_trap(&self, data: RuntimeError) -> !;

    /// Returns the machine code associated with this module.
    fn get_code(&self) -> Option<&[u8]> {
        None
    }

    /// Returns the beginning offsets of all functions, including import trampolines.
    fn get_offsets(&self) -> Option<Vec<usize>> {
        None
    }

    /// Returns the beginning offsets of all local functions.
    fn get_local_function_offsets(&self) -> Option<Vec<usize>> {
        None
    }

    /// Returns the inline breakpoint size corresponding to an Architecture (None in case is not implemented)
    fn get_inline_breakpoint_size(&self, _arch: Architecture) -> Option<usize> {
        None
    }

    /// Attempts to read an inline breakpoint from the code.
    ///
    /// Inline breakpoints are detected by special instruction sequences that never
    /// appear in valid code.
    fn read_inline_breakpoint(
        &self,
        _arch: Architecture,
        _code: &[u8],
    ) -> Option<InlineBreakpoint> {
        None
    }
}

pub trait CacheGen: Send + Sync {
    fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError>;
}