* Validate modules while translating This commit is a change to cranelift-wasm to validate each function body as it is translated. Additionally top-level module translation functions will perform module validation. This commit builds on changes in wasmparser to perform module validation interwtwined with parsing and translation. This will be necessary for future wasm features such as module linking where the type behind a function index, for example, can be far away in another module. Additionally this also brings a nice benefit where parsing the binary only happens once (instead of having an up-front serial validation step) and validation can happen in parallel for each function. Most of the changes in this commit are plumbing to make sure everything lines up right. The major functional change here is that module compilation should be faster by validating in parallel (or skipping function validation entirely in the case of a cache hit). Otherwise from a user-facing perspective nothing should be that different. This commit does mean that cranelift's translation now inherently validates the input wasm module. This means that the Spidermonkey integration of cranelift-wasm will also be validating the function as it's being translated with cranelift. The associated PR for wasmparser (bytecodealliance/wasmparser#62) provides the necessary tools to create a `FuncValidator` for Gecko, but this is something I'll want careful review for before landing! * Read function operators until EOF This way we can let the validator take care of any issues with mismatched `end` instructions and/or trailing operators/bytes.
110 lines
3.7 KiB
Rust
110 lines
3.7 KiB
Rust
//! A `Compilation` contains the compiled function bodies for a WebAssembly
|
|
//! module.
|
|
|
|
use crate::{FunctionAddressMap, FunctionBodyData, ModuleTranslation};
|
|
use cranelift_codegen::{binemit, ir, isa, isa::unwind::UnwindInfo};
|
|
use cranelift_entity::PrimaryMap;
|
|
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, WasmError};
|
|
use serde::{Deserialize, Serialize};
|
|
use thiserror::Error;
|
|
|
|
#[allow(missing_docs)]
|
|
pub type CompiledFunctions = PrimaryMap<DefinedFuncIndex, CompiledFunction>;
|
|
|
|
/// Compiled function: machine code body, jump table offsets, and unwind information.
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
|
|
#[allow(missing_docs)]
|
|
pub struct CompiledFunction {
|
|
/// The machine code for this function.
|
|
pub body: Vec<u8>,
|
|
|
|
/// The jump tables offsets (in the body).
|
|
pub jt_offsets: ir::JumpTableOffsets,
|
|
|
|
/// The unwind information.
|
|
pub unwind_info: Option<UnwindInfo>,
|
|
|
|
pub relocations: Vec<Relocation>,
|
|
pub address_map: FunctionAddressMap,
|
|
pub value_labels_ranges: cranelift_codegen::ValueLabelsRanges,
|
|
pub stack_slots: ir::StackSlots,
|
|
pub traps: Vec<TrapInformation>,
|
|
pub stack_maps: Vec<StackMapInformation>,
|
|
}
|
|
|
|
/// A record of a relocation to perform.
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
|
pub struct Relocation {
|
|
/// The relocation code.
|
|
pub reloc: binemit::Reloc,
|
|
/// Relocation target.
|
|
pub reloc_target: RelocationTarget,
|
|
/// The offset where to apply the relocation.
|
|
pub offset: binemit::CodeOffset,
|
|
/// The addend to add to the relocation value.
|
|
pub addend: binemit::Addend,
|
|
}
|
|
|
|
/// Destination function. Can be either user function or some special one, like `memory.grow`.
|
|
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
|
|
pub enum RelocationTarget {
|
|
/// The user function index.
|
|
UserFunc(FuncIndex),
|
|
/// A compiler-generated libcall.
|
|
LibCall(ir::LibCall),
|
|
/// Jump table index.
|
|
JumpTable(FuncIndex, ir::JumpTable),
|
|
}
|
|
|
|
/// Information about trap.
|
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
|
pub struct TrapInformation {
|
|
/// The offset of the trapping instruction in native code. It is relative to the beginning of the function.
|
|
pub code_offset: binemit::CodeOffset,
|
|
/// Location of trapping instruction in WebAssembly binary module.
|
|
pub source_loc: ir::SourceLoc,
|
|
/// Code of the trap.
|
|
pub trap_code: ir::TrapCode,
|
|
}
|
|
|
|
/// The offset within a function of a GC safepoint, and its associated stack
|
|
/// map.
|
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
|
pub struct StackMapInformation {
|
|
/// The offset of the GC safepoint within the function's native code. It is
|
|
/// relative to the beginning of the function.
|
|
pub code_offset: binemit::CodeOffset,
|
|
|
|
/// The stack map for identifying live GC refs at the GC safepoint.
|
|
pub stack_map: binemit::StackMap,
|
|
}
|
|
|
|
/// An error while compiling WebAssembly to machine code.
|
|
#[derive(Error, Debug)]
|
|
pub enum CompileError {
|
|
/// A wasm translation error occured.
|
|
#[error("WebAssembly translation error")]
|
|
Wasm(#[from] WasmError),
|
|
|
|
/// A compilation error occured.
|
|
#[error("Compilation error: {0}")]
|
|
Codegen(String),
|
|
|
|
/// A compilation error occured.
|
|
#[error("Debug info is not supported with this configuration")]
|
|
DebugInfoNotSupported,
|
|
}
|
|
|
|
/// An implementation of a compiler from parsed WebAssembly module to native
|
|
/// code.
|
|
pub trait Compiler: Send + Sync {
|
|
/// Compile a function with the given `TargetIsa`.
|
|
fn compile_function(
|
|
&self,
|
|
translation: &ModuleTranslation<'_>,
|
|
index: DefinedFuncIndex,
|
|
data: FunctionBodyData<'_>,
|
|
isa: &dyn isa::TargetIsa,
|
|
) -> Result<CompiledFunction, CompileError>;
|
|
}
|