Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[experimental] Optimize memory consumption (take 2) #718

Merged
merged 52 commits into from
May 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
e67b89d
add intx dependency
Robbepop May 16, 2023
e0e45b6
add new translation error kinds
Robbepop May 16, 2023
41aa2aa
re-export TranslationErrorInner
Robbepop May 16, 2023
0f4ba7f
make bytecode::FuncIdx use U24 internally
Robbepop May 16, 2023
add28ce
rename FuncIdx::into_inner to to_u32
Robbepop May 16, 2023
ebb02d4
fix tests after FuncIdx refactoring
Robbepop May 16, 2023
138b8ed
refactor bytecode::TableIdx to use 3 bytes only
Robbepop May 17, 2023
fc75c00
correct error
Robbepop May 17, 2023
12d0a19
make SignatureIdx use 3 bytes only
Robbepop May 17, 2023
891ffe0
make LocalDepth use 3 bytes only
Robbepop May 17, 2023
e4ee65c
make GlobalIdx use 3 bytes only
Robbepop May 17, 2023
659fc03
rename GlobalIdx::into_inner -> to_u32
Robbepop May 17, 2023
04c4d38
make data and element segment indices 24-bit sized
Robbepop May 17, 2023
35a2fb0
add missing commits (oups)
Robbepop May 17, 2023
dc5906e
move DropKeep def to the bottom of the file
Robbepop May 17, 2023
2847a4d
update docs
Robbepop May 17, 2023
a78f2da
make BranchOffset use I24 instead of i32 internally
Robbepop May 18, 2023
818146a
apply clippy suggestions
Robbepop May 18, 2023
1b703c1
improve Debug impl for FuncType
Robbepop May 19, 2023
37371f6
make DropKeep use only 3 bytes
Robbepop May 19, 2023
fb03bb7
make br_table targets use U24 internally
Robbepop May 19, 2023
cb78426
make Local{Get,Set,Tee} tuple struct variants
Robbepop May 19, 2023
3f2d183
remove dbg println
Robbepop May 19, 2023
658773b
fix minor bug
Robbepop May 19, 2023
e6f1fa1
fix bug in DropKeep with big endian systems
Robbepop May 19, 2023
14f2c4f
fix internal doc link
Robbepop May 19, 2023
b28058e
make ConsumeFuel instr use 24-bit encoding
Robbepop May 19, 2023
03959e3
remove superflous error kind
Robbepop May 19, 2023
e200196
extend wasmi bytecode utils tests
Robbepop May 19, 2023
4445181
convert more Instruction variants into tuple structs
Robbepop May 20, 2023
8267923
apply new encoding to wasmi branch instructions
Robbepop May 20, 2023
216e325
fix and improve internal docs
Robbepop May 20, 2023
6753dbf
make ReturnCall instruction use 3 bytes only
Robbepop May 22, 2023
81d3f9e
make ReturnCallIndirect use only 3 bytes for encoding
Robbepop May 22, 2023
b0fa320
make CallIndirect use 3 bytes encoding
Robbepop May 22, 2023
5b57b91
make TableCopy encode in 3 bytes only
Robbepop May 22, 2023
91abfaa
make TableInit use 3 byte encoding
Robbepop May 22, 2023
98c8ac9
fix internal docs
Robbepop May 22, 2023
235c62f
add ConstPool type
Robbepop May 22, 2023
ce12a7c
introduce I{32,64}Const24 instructions
Robbepop May 22, 2023
35df49c
introduce new ConstRef instruction
Robbepop May 22, 2023
ed202c3
introduce Const32 instruction
Robbepop May 23, 2023
2d1f579
change I64Const24 -> I64Const32
Robbepop May 23, 2023
5c8a52e
make bytecode utils base on u32 and i32 again
Robbepop May 23, 2023
d46928c
remove unused translation error kinds
Robbepop May 23, 2023
8332a07
rename Offset to AddressOffset
Robbepop May 23, 2023
28920b8
make BranchOffset encoding 4 bytes
Robbepop May 23, 2023
65061a4
make BranchTableTargets encoding 4 bytes
Robbepop May 23, 2023
142d5dc
make BlockFuel encoding 4 bytes
Robbepop May 23, 2023
03222e9
make DropKeep encoding 4 bytes
Robbepop May 23, 2023
4b3df45
implement non-adjusting branches
Robbepop May 23, 2023
3b83ca1
apply clippy suggestions
Robbepop May 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/wasmi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ spin = { version = "0.9", default-features = false, features = [
"rwlock",
] }
smallvec = { version = "1.10.0", features = ["union"] }
intx = "0.1.0"

[dev-dependencies]
wat = "1"
Expand Down
280 changes: 170 additions & 110 deletions crates/wasmi/src/engine/bytecode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,23 @@ mod utils;
mod tests;

pub use self::utils::{
AddressOffset,
BlockFuel,
BranchOffset,
BranchParams,
BranchTableTargets,
DataSegmentIdx,
DropKeep,
DropKeepError,
ElementSegmentIdx,
FuncIdx,
GlobalIdx,
LocalDepth,
Offset,
SignatureIdx,
TableIdx,
};
use super::{const_pool::ConstRef, TranslationError};
use core::fmt::Debug;
use wasmi_core::UntypedValue;
use wasmi_core::F32;

/// The internal `wasmi` bytecode that is stored for Wasm functions.
///
Expand All @@ -32,102 +34,155 @@ use wasmi_core::UntypedValue;
/// each representing either the `BrTable` head or one of its branching targets.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Instruction {
LocalGet {
local_depth: LocalDepth,
},
LocalSet {
local_depth: LocalDepth,
},
LocalTee {
local_depth: LocalDepth,
},
Br(BranchParams),
BrIfEqz(BranchParams),
BrIfNez(BranchParams),
BrTable {
len_targets: usize,
},
LocalGet(LocalDepth),
LocalSet(LocalDepth),
LocalTee(LocalDepth),
/// An unconditional branch.
Br(BranchOffset),
/// Branches if the top-most stack value is equal to zero.
BrIfEqz(BranchOffset),
/// Branches if the top-most stack value is _not_ equal to zero.
BrIfNez(BranchOffset),
/// An unconditional branch.
///
/// This operation also adjust the underlying value stack if necessary.
///
/// # Encoding
///
/// This [`Instruction`] must be followed by a [`Instruction::Return`]
/// which stores information about the [`DropKeep`] behavior of the
/// [`Instruction::Br`]. The [`Instruction::Return`] will never be executed
/// and only acts as parameter storage for this instruction.
BrAdjust(BranchOffset),
/// Branches if the top-most stack value is _not_ equal to zero.
///
/// This operation also adjust the underlying value stack if necessary.
///
/// # Encoding
///
/// This [`Instruction`] must be followed by a [`Instruction::Return`]
/// which stores information about the [`DropKeep`] behavior of the
/// [`Instruction::BrIfNez`]. The [`Instruction::Return`] will never be executed
/// and only acts as parameter storage for this instruction.
BrAdjustIfNez(BranchOffset),
/// Branch table with a set number of branching targets.
///
/// # Encoding
///
/// This [`Instruction`] must be followed by exactly as many unconditional
/// branch instructions as determined by [`BranchTableTargets`]. Branch
/// instructions that may follow are [`Instruction::Br] and [`Instruction::Return`].
BrTable(BranchTableTargets),
Unreachable,
ConsumeFuel {
amount: u64,
},
ConsumeFuel(BlockFuel),
Return(DropKeep),
ReturnIfNez(DropKeep),
ReturnCall {
drop_keep: DropKeep,
func: FuncIdx,
},
ReturnCallIndirect {
drop_keep: DropKeep,
table: TableIdx,
func_type: SignatureIdx,
},
/// Tail calling `func`.
///
/// # Encoding
///
/// This [`Instruction`] must be followed by an [`Instruction::Return`] that
/// encodes the [`DropKeep`] parameter. Note that the [`Instruction::Return`]
/// only acts as a storage for the parameter of the [`Instruction::ReturnCall`]
/// and will never be executed by itself.
ReturnCall(FuncIdx),
/// Tail calling a function indirectly.
///
/// # Encoding
///
/// This [`Instruction`] must be followed by an [`Instruction::Return`] that
/// encodes the [`DropKeep`] parameter as well as an [`Instruction::TableGet`]
/// that encodes the [`TableIdx`] parameter. Note that both, [`Instruction::Return`]
/// and [`Instruction::TableGet`] only act as a storage for parameters to the
/// [`Instruction::ReturnCallIndirect`] and will never be executed by themselves.
ReturnCallIndirect(SignatureIdx),
/// Calls the function.
Call(FuncIdx),
CallIndirect {
table: TableIdx,
func_type: SignatureIdx,
},
/// Calling a function indirectly.
///
/// # Encoding
///
/// This [`Instruction`] must be followed by an [`Instruction::TableGet`]
/// that encodes the [`TableIdx`] parameter. Note that the [`Instruction::TableGet`]
/// only acts as a storage for the parameter of the [`Instruction::CallIndirect`]
/// and will never be executed by itself.
CallIndirect(SignatureIdx),
Drop,
Select,
GlobalGet(GlobalIdx),
GlobalSet(GlobalIdx),
I32Load(Offset),
I64Load(Offset),
F32Load(Offset),
F64Load(Offset),
I32Load8S(Offset),
I32Load8U(Offset),
I32Load16S(Offset),
I32Load16U(Offset),
I64Load8S(Offset),
I64Load8U(Offset),
I64Load16S(Offset),
I64Load16U(Offset),
I64Load32S(Offset),
I64Load32U(Offset),
I32Store(Offset),
I64Store(Offset),
F32Store(Offset),
F64Store(Offset),
I32Store8(Offset),
I32Store16(Offset),
I64Store8(Offset),
I64Store16(Offset),
I64Store32(Offset),
I32Load(AddressOffset),
I64Load(AddressOffset),
F32Load(AddressOffset),
F64Load(AddressOffset),
I32Load8S(AddressOffset),
I32Load8U(AddressOffset),
I32Load16S(AddressOffset),
I32Load16U(AddressOffset),
I64Load8S(AddressOffset),
I64Load8U(AddressOffset),
I64Load16S(AddressOffset),
I64Load16U(AddressOffset),
I64Load32S(AddressOffset),
I64Load32U(AddressOffset),
I32Store(AddressOffset),
I64Store(AddressOffset),
F32Store(AddressOffset),
F64Store(AddressOffset),
I32Store8(AddressOffset),
I32Store16(AddressOffset),
I64Store8(AddressOffset),
I64Store16(AddressOffset),
I64Store32(AddressOffset),
MemorySize,
MemoryGrow,
MemoryFill,
MemoryCopy,
MemoryInit(DataSegmentIdx),
DataDrop(DataSegmentIdx),
TableSize {
table: TableIdx,
},
TableGrow {
table: TableIdx,
},
TableFill {
table: TableIdx,
},
TableGet {
table: TableIdx,
},
TableSet {
table: TableIdx,
},
TableCopy {
dst: TableIdx,
src: TableIdx,
},
TableInit {
table: TableIdx,
elem: ElementSegmentIdx,
},
TableSize(TableIdx),
TableGrow(TableIdx),
TableFill(TableIdx),
TableGet(TableIdx),
TableSet(TableIdx),
/// Copies elements from one table to another.
///
/// # Note
///
/// It is also possible to copy elements within the same table.
///
/// # Encoding
///
/// The [`TableIdx`] referred to by the [`Instruction::TableCopy`]
/// represents the `dst` (destination) table. The [`Instruction::TableCopy`]
/// must be followed by an [`Instruction::TableGet`] which stores a
/// [`TableIdx`] that refers to the `src` (source) table.
TableCopy(TableIdx),
/// Initializes a table given an [`ElementSegmentIdx`].
///
/// # Encoding
///
/// The [`Instruction::TableInit`] must be followed by an
/// [`Instruction::TableGet`] which stores a [`TableIdx`]
/// that refers to the table to be initialized.
TableInit(ElementSegmentIdx),
ElemDrop(ElementSegmentIdx),
RefFunc {
func_index: FuncIdx,
},
Const(UntypedValue),
RefFunc(FuncIdx),
/// A 32-bit constant value.
Const32([u8; 4]),
/// A 64-bit integer value losslessly encoded as 32-bit integer.
///
/// Upon execution the 32-bit integer is sign-extended to the 64-bit integer.
///
/// # Note
///
/// This is a space-optimized variant of [`Instruction::ConstRef`] but can
/// only used for small integer values that fit into a 24-bit integer value.
I64Const32(i32),
/// Pushes a constant value onto the stack.
///
/// The constant value is referred to indirectly by the [`ConstRef`].
ConstRef(ConstRef),
I32Eqz,
I32Eq,
I32Ne,
Expand Down Expand Up @@ -263,38 +318,47 @@ pub enum Instruction {
}

impl Instruction {
/// Creates a new `Const` instruction from the given value.
pub fn constant<C>(value: C) -> Self
where
C: Into<UntypedValue>,
{
Self::Const(value.into())
/// Creates an [`Instruction::Const32`] from the given `i32` constant value.
pub fn i32_const(value: i32) -> Self {
Self::Const32(value.to_ne_bytes())
}

/// Creates an [`Instruction::Const32`] from the given `f32` constant value.
pub fn f32_const(value: F32) -> Self {
Self::Const32(value.to_bits().to_ne_bytes())
}

/// Creates a new `local.get` instruction from the given local depth.
pub fn local_get(local_depth: usize) -> Self {
Self::LocalGet {
local_depth: LocalDepth::from(local_depth),
}
///
/// # Errors
///
/// If the `local_depth` is out of bounds as local depth index.
pub fn local_get(local_depth: u32) -> Result<Self, TranslationError> {
Ok(Self::LocalGet(LocalDepth::from(local_depth)))
}

/// Creates a new `local.set` instruction from the given local depth.
pub fn local_set(local_depth: usize) -> Self {
Self::LocalSet {
local_depth: LocalDepth::from(local_depth),
}
///
/// # Errors
///
/// If the `local_depth` is out of bounds as local depth index.
pub fn local_set(local_depth: u32) -> Result<Self, TranslationError> {
Ok(Self::LocalSet(LocalDepth::from(local_depth)))
}

/// Creates a new `local.tee` instruction from the given local depth.
pub fn local_tee(local_depth: usize) -> Self {
Self::LocalTee {
local_depth: LocalDepth::from(local_depth),
}
///
/// # Errors
///
/// If the `local_depth` is out of bounds as local depth index.
pub fn local_tee(local_depth: u32) -> Result<Self, TranslationError> {
Ok(Self::LocalTee(LocalDepth::from(local_depth)))
}

/// Convenience method to create a new `ConsumeFuel` instruction.
pub fn consume_fuel(amount: u64) -> Self {
Self::ConsumeFuel { amount }
pub fn consume_fuel(amount: u64) -> Result<Self, TranslationError> {
let block_fuel = BlockFuel::try_from(amount)?;
Ok(Self::ConsumeFuel(block_fuel))
}

/// Increases the fuel consumption of the [`ConsumeFuel`] instruction by `delta`.
Expand All @@ -305,13 +369,9 @@ impl Instruction {
/// - If the new fuel consumption overflows the internal `u64` value.
///
/// [`ConsumeFuel`]: Instruction::ConsumeFuel
pub fn bump_fuel_consumption(&mut self, delta: u64) {
pub fn bump_fuel_consumption(&mut self, delta: u64) -> Result<(), TranslationError> {
match self {
Self::ConsumeFuel { amount } => {
*amount = amount.checked_add(delta).unwrap_or_else(|| {
panic!("overflowed fuel consumption. current = {amount}, delta = {delta}",)
})
}
Self::ConsumeFuel(block_fuel) => block_fuel.bump_by(delta),
instr => panic!("expected Instruction::ConsumeFuel but found: {instr:?}"),
}
}
Expand Down
12 changes: 10 additions & 2 deletions crates/wasmi/src/engine/bytecode/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,16 @@ use core::mem::size_of;

#[test]
fn size_of_instruction() {
assert_eq!(size_of::<Instruction>(), 16);
assert_eq!(size_of::<Instruction>(), 8);
assert_eq!(size_of::<DropKeep>(), 4);
assert_eq!(size_of::<BranchParams>(), 8);
assert_eq!(size_of::<BranchOffset>(), 4);
assert_eq!(size_of::<BlockFuel>(), 4);
assert_eq!(size_of::<BranchTableTargets>(), 4);
assert_eq!(size_of::<DataSegmentIdx>(), 4);
assert_eq!(size_of::<ElementSegmentIdx>(), 4);
assert_eq!(size_of::<FuncIdx>(), 4);
assert_eq!(size_of::<GlobalIdx>(), 4);
assert_eq!(size_of::<TableIdx>(), 4);
assert_eq!(size_of::<SignatureIdx>(), 4);
assert_eq!(size_of::<LocalDepth>(), 4);
}
Loading