diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index d538a89a37..9262ed7eb9 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -109,19 +109,19 @@ jobs: uses: Swatinem/rust-cache@v2 - name: Build (default features) env: - RUSTFLAGS: "--cfg debug_assertions" + RUSTFLAGS: "-C debug-assertions" run: cargo build --tests --workspace --locked --verbose - name: Test (default features) env: - RUSTFLAGS: "--cfg debug_assertions" + RUSTFLAGS: "-C debug-assertions" run: cargo test --workspace --locked - name: Build (all features) env: - RUSTFLAGS: "--cfg debug_assertions" + RUSTFLAGS: "-C debug-assertions" run: cargo build --tests --workspace --locked --all-features --verbose - name: Test (all features) env: - RUSTFLAGS: "--cfg debug_assertions" + RUSTFLAGS: "-C debug-assertions" run: cargo test --workspace --locked --all-features fmt: diff --git a/crates/collections/src/arena/mod.rs b/crates/collections/src/arena/mod.rs index 354c7f2c00..dfc83bff96 100644 --- a/crates/collections/src/arena/mod.rs +++ b/crates/collections/src/arena/mod.rs @@ -38,7 +38,7 @@ pub struct Arena { unsafe impl Send for Arena where T: Send {} /// [`Arena`] does not store `Idx` therefore it is `Sync` without its bound. -unsafe impl Sync for Arena where T: Send {} +unsafe impl Sync for Arena where T: Sync {} impl Default for Arena { fn default() -> Self { diff --git a/crates/wasmi/src/engine/code_map.rs b/crates/wasmi/src/engine/code_map.rs index 8c9a3bc90f..d4b72727f1 100644 --- a/crates/wasmi/src/engine/code_map.rs +++ b/crates/wasmi/src/engine/code_map.rs @@ -96,6 +96,16 @@ impl Default for EngineFuncSpan { } impl EngineFuncSpan { + /// Creates a new [`EngineFuncSpan`] for `start..end`. + /// + /// # Panics + /// + /// If `start` index is not less than or equal to `end` index. + pub fn new(start: EngineFunc, end: EngineFunc) -> Self { + assert!(start <= end); + Self { start, end } + } + /// Creates an empty [`EngineFuncSpan`]. #[inline] pub fn empty() -> Self { @@ -108,11 +118,13 @@ impl EngineFuncSpan { /// Returns `true` if `self` is empty. #[inline] pub fn is_empty(&self) -> bool { + debug_assert!(self.start <= self.end); self.start == self.end } /// Returns the number of [`EngineFunc`] in `self`. pub fn len(&self) -> u32 { + debug_assert!(self.start <= self.end); let start = self.start.0; let end = self.end.0; end - start @@ -122,6 +134,7 @@ impl EngineFuncSpan { /// /// Returns `None` if `n` is out of bounds. pub fn get(&self, n: u32) -> Option { + debug_assert!(self.start <= self.end); if n >= self.len() { return None; } @@ -132,6 +145,7 @@ impl EngineFuncSpan { /// /// Returns `None` if `func` is not contained in `self`. pub fn position(&self, func: EngineFunc) -> Option { + debug_assert!(self.start <= self.end); if func < self.start || func >= self.end { return None; } @@ -145,6 +159,7 @@ impl EngineFuncSpan { /// If `n` is out of bounds. #[track_caller] pub fn get_or_panic(&self, n: u32) -> EngineFunc { + debug_assert!(self.start <= self.end); self.get(n) .unwrap_or_else(|| panic!("out of bounds `EngineFunc` index: {n}")) } @@ -152,6 +167,7 @@ impl EngineFuncSpan { /// Returns an iterator over the [`EngineFunc`]s in `self`. #[inline] pub fn iter(&self) -> EngineFuncSpanIter { + debug_assert!(self.start <= self.end); EngineFuncSpanIter { span: *self } } } @@ -218,7 +234,7 @@ impl CodeMap { /// - [`CodeMap::init_func_as_uncompiled`] pub fn alloc_funcs(&self, amount: usize) -> EngineFuncSpan { let Range { start, end } = self.funcs.lock().alloc_many(amount); - EngineFuncSpan { start, end } + EngineFuncSpan::new(start, end) } /// Initializes the [`EngineFunc`] with its [`CompiledFuncEntity`]. @@ -586,16 +602,36 @@ impl UncompiledFuncEntity { fuel: Option<&mut Fuel>, features: &WasmFeatures, ) -> Result { + /// The amount of fuel required to compile a function body per byte. + /// + /// This does _not_ include validation. + /// + /// # Note + /// + /// This fuel amount was chosen after extensive worst-case translation benchmarking. + const COMPILE_FUEL_PER_BYTE: u64 = 7; + /// The amount of fuel required to validate a function body per byte. + /// + /// This does _not_ include compilation. + /// + /// # Note + /// + /// This fuel amount was chosen after extensive worst-case translation benchmarking. + const VALIDATE_FUEL_PER_BYTE: u64 = 2; + /// The amount of fuel required to validate and compile a function body per byte. + const VALIDATE_AND_COMPILE_FUEL_PER_BYTE: u64 = + VALIDATE_FUEL_PER_BYTE + COMPILE_FUEL_PER_BYTE; + let func_idx = self.func_index; let bytes = mem::take(&mut self.bytes); let needs_validation = self.validation.is_some(); let compilation_fuel = |_costs: &FuelCosts| { let len_bytes = bytes.as_slice().len() as u64; - let compile_factor = match needs_validation { - false => 7, - true => 9, + let fuel_per_byte = match needs_validation { + false => COMPILE_FUEL_PER_BYTE, + true => VALIDATE_AND_COMPILE_FUEL_PER_BYTE, }; - len_bytes.saturating_mul(compile_factor) + len_bytes.saturating_mul(fuel_per_byte) }; if let Some(fuel) = fuel { match fuel.consume_fuel(compilation_fuel) { diff --git a/crates/wasmi/src/engine/executor/instrs/return_.rs b/crates/wasmi/src/engine/executor/instrs/return_.rs index 5d2b798d83..c409e200c9 100644 --- a/crates/wasmi/src/engine/executor/instrs/return_.rs +++ b/crates/wasmi/src/engine/executor/instrs/return_.rs @@ -246,7 +246,7 @@ impl Executor<'_> { } } - /// Execute an [`Instruction::Return`]. + /// Execute an [`Instruction::ReturnNez`]. pub fn execute_return_nez(&mut self, store: &mut StoreInner, condition: Reg) -> ControlFlow { self.execute_return_nez_impl(store, condition, (), |this, store, _| { this.execute_return(store) @@ -263,7 +263,7 @@ impl Executor<'_> { self.execute_return_nez_impl(store, condition, value, Self::execute_return_reg) } - /// Execute an [`Instruction::ReturnNezReg`] returning a single [`Reg`] value. + /// Execute an [`Instruction::ReturnNezReg2`] returning two [`Reg`] values. pub fn execute_return_nez_reg2( &mut self, store: &mut StoreInner, @@ -273,7 +273,7 @@ impl Executor<'_> { self.execute_return_nez_impl(store, condition, value, Self::execute_return_reg2) } - /// Execute an [`Instruction::ReturnNezImm32`] returning a single 32-bit constant value. + /// Execute an [`Instruction::ReturnNezImm32`] returning a single 32-bit immediate value. pub fn execute_return_nez_imm32( &mut self, store: &mut StoreInner, @@ -283,7 +283,7 @@ impl Executor<'_> { self.execute_return_nez_impl(store, condition, value, Self::execute_return_imm32) } - /// Execute an [`Instruction::ReturnNezI64Imm32`] returning a single 32-bit encoded constant `i64` value. + /// Execute an [`Instruction::ReturnNezI64Imm32`] returning a single 32-bit encoded immediate `i64` value. pub fn execute_return_nez_i64imm32( &mut self, store: &mut StoreInner, @@ -293,7 +293,7 @@ impl Executor<'_> { self.execute_return_nez_impl(store, condition, value, Self::execute_return_i64imm32) } - /// Execute an [`Instruction::ReturnNezF64Imm32`] returning a single 32-bit encoded constant `f64` value. + /// Execute an [`Instruction::ReturnNezF64Imm32`] returning a single 32-bit encoded immediate `f64` value. pub fn execute_return_nez_f64imm32( &mut self, store: &mut StoreInner, diff --git a/crates/wasmi/src/memory/buffer.rs b/crates/wasmi/src/memory/buffer.rs index eff885903d..bb255c0655 100644 --- a/crates/wasmi/src/memory/buffer.rs +++ b/crates/wasmi/src/memory/buffer.rs @@ -36,6 +36,12 @@ pub struct ByteBuffer { // Both of them are `Send` so this is sound. unsafe impl Send for ByteBuffer {} +// # Safety +// +// `ByteBuffer` is essentially an `enum`` of `Vec` or `&'static mut [u8]`. +// Both of them are `Sync` so this is sound. +unsafe impl Sync for ByteBuffer {} + /// Decomposes the `Vec` into its raw components. /// /// Returns the raw pointer to the underlying data, the length of