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

Make branch instructions offset program counter by delta (PR) #478

Merged
merged 15 commits into from
Sep 27, 2022
Merged
16 changes: 9 additions & 7 deletions crates/wasmi/src/engine/bytecode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ mod utils;
mod tests;

pub use self::utils::{
BranchOffset,
BranchParams,
DropKeep,
DropKeepError,
FuncIdx,
GlobalIdx,
LocalDepth,
Offset,
SignatureIdx,
Target,
};
use core::fmt::Debug;
use wasmi_core::UntypedValue;

/// The internal `wasmi` bytecode that is stored for Wasm functions.
Expand All @@ -25,14 +27,14 @@ use wasmi_core::UntypedValue;
///
/// For example the `BrTable` instruction is unrolled into separate instructions
/// each representing either the `BrTable` head or one of its branching targets.
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Instruction {
LocalGet { local_depth: LocalDepth },
LocalSet { local_depth: LocalDepth },
LocalTee { local_depth: LocalDepth },
Br(Target),
BrIfEqz(Target),
BrIfNez(Target),
Br(BranchParams),
BrIfEqz(BranchParams),
BrIfNez(BranchParams),
ReturnIfNez(DropKeep),
BrTable { len_targets: usize },
Unreachable,
Expand Down Expand Up @@ -209,9 +211,9 @@ pub enum Instruction {

impl Instruction {
/// Creates a new `Const` instruction from the given value.
pub fn constant<T>(value: T) -> Self
pub fn constant<C>(value: C) -> Self
where
T: Into<UntypedValue>,
C: Into<UntypedValue>,
{
Self::Const(value.into())
}
Expand Down
3 changes: 2 additions & 1 deletion crates/wasmi/src/engine/bytecode/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ use super::*;
fn size_of_instruction() {
assert_eq!(core::mem::size_of::<Instruction>(), 16);
assert_eq!(core::mem::size_of::<DropKeep>(), 4);
assert_eq!(core::mem::size_of::<Target>(), 8);
assert_eq!(core::mem::size_of::<BranchParams>(), 8);
assert_eq!(core::mem::size_of::<BranchOffset>(), 4);
}
134 changes: 87 additions & 47 deletions crates/wasmi/src/engine/bytecode/utils.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::super::super::engine::InstructionIdx;
use crate::engine::Instr;
use core::fmt::Display;

/// Defines how many stack values are going to be dropped and kept after branching.
Expand Down Expand Up @@ -64,52 +64,6 @@ impl DropKeep {
}
}

/// A branching target.
///
/// This also specifies how many values on the stack
/// need to be dropped and kept in order to maintain
/// value stack integrity.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Target {
/// The destination program counter.
dst_pc: InstructionIdx,
/// How many values on the stack need to be dropped and kept.
drop_keep: DropKeep,
}

impl Target {
/// Creates a new `wasmi` branching target.
pub fn new(dst_pc: InstructionIdx, drop_keep: DropKeep) -> Self {
Self { dst_pc, drop_keep }
}

/// Returns the destination program counter (as index).
pub fn destination_pc(self) -> InstructionIdx {
self.dst_pc
}

/// Updates the destination program counter (as index).
///
/// # Panics
///
/// If the old destination program counter was not [`InstructionIdx::INVALID`].
pub fn update_destination_pc(&mut self, new_destination_pc: InstructionIdx) {
assert_eq!(
self.destination_pc(),
InstructionIdx::INVALID,
"can only update the destination pc of a target with an invalid \
destination pc but found a valid one: {:?}",
self.destination_pc(),
);
self.dst_pc = new_destination_pc;
}

/// Returns the amount of stack values to drop and keep upon taking the branch.
pub fn drop_keep(self) -> DropKeep {
self.drop_keep
}
}

/// A function index.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(transparent)]
Expand Down Expand Up @@ -215,3 +169,89 @@ impl Offset {
self.0
}
}

/// A branching target.
///
/// This also specifies how many values on the stack
/// need to be dropped and kept in order to maintain
/// value stack integrity.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct BranchParams {
/// The branching offset.
///
/// How much instruction pointer is offset upon taking the branch.
offset: BranchOffset,
/// How many values on the stack need to be dropped and kept.
drop_keep: DropKeep,
}

impl BranchParams {
/// Creates new [`BranchParams`].
pub fn new(offset: BranchOffset, drop_keep: DropKeep) -> Self {
Self { offset, drop_keep }
}

/// Returns `true` if the [`BranchParams`] have been initialized already.
fn is_init(&self) -> bool {
self.offset.is_init()
}

/// Initializes the [`BranchParams`] with a proper [`BranchOffset`].
///
/// # Panics
///
/// - If the [`BranchParams`] have already been initialized.
/// - If the given [`BranchOffset`] is not properly initialized.
pub fn init(&mut self, offset: BranchOffset) {
assert!(offset.is_init());
assert!(!self.is_init());
self.offset = offset;
}

/// Returns the branching offset.
pub fn offset(self) -> BranchOffset {
self.offset
}

/// Returns the amount of stack values to drop and keep upon taking the branch.
pub fn drop_keep(self) -> DropKeep {
self.drop_keep
}
}

/// The branching offset.
///
/// This defines how much the instruction pointer is offset
/// upon taking the respective branch.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct BranchOffset(i32);

impl BranchOffset {
/// Creates a [`BranchOffset`] from the given raw `i32` value.
#[cfg(test)]
pub fn from_i32(value: i32) -> Self {
Self(value)
}

/// Creates an uninitalized [`BranchOffset`].
pub fn uninit() -> Self {
Self(0)
}

/// Creates an initialized [`BranchOffset`] from `src` to `dst`.
pub fn init(src: Instr, dst: Instr) -> Self {
let src = src.into_u32() as i32;
let dst = dst.into_u32() as i32;
Self(dst - src)
}

/// Returns `true` if the [`BranchOffset`] has been initialized.
pub fn is_init(self) -> bool {
self.0 != 0
}

/// Returns the `i32` representation of the [`BranchOffset`].
pub fn into_i32(self) -> i32 {
self.0
}
}
13 changes: 6 additions & 7 deletions crates/wasmi/src/engine/executor.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use super::{
super::{Memory, Table},
bytecode::{FuncIdx, GlobalIdx, Instruction, LocalDepth, Offset, SignatureIdx},
bytecode::{BranchParams, FuncIdx, GlobalIdx, Instruction, LocalDepth, Offset, SignatureIdx},
cache::InstanceCache,
code_map::Instructions,
stack::ValueStackRef,
AsContextMut,
CallOutcome,
DropKeep,
FuncFrame,
Target,
ValueStack,
};
use crate::{
Expand Down Expand Up @@ -487,9 +486,9 @@ impl<'ctx, 'engine, 'func, HostData> Executor<'ctx, 'engine, 'func, HostData> {
self.pc += 1;
}

fn branch_to(&mut self, target: Target) {
fn branch_to(&mut self, target: BranchParams) {
self.value_stack.drop_keep(target.drop_keep());
self.pc = target.destination_pc().into_usize();
self.pc = (self.pc as isize + target.offset().into_i32() as isize) as usize;
}

fn sync_stack_ptr(&mut self) {
Expand Down Expand Up @@ -520,11 +519,11 @@ impl<'ctx, 'engine, 'func, HostData> Executor<'ctx, 'engine, 'func, HostData> {
Err(TrapCode::Unreachable).map_err(Into::into)
}

fn visit_br(&mut self, target: Target) {
fn visit_br(&mut self, target: BranchParams) {
self.branch_to(target)
}

fn visit_br_if_eqz(&mut self, target: Target) {
fn visit_br_if_eqz(&mut self, target: BranchParams) {
let condition = self.value_stack.pop_as();
if condition {
self.next_instr()
Expand All @@ -533,7 +532,7 @@ impl<'ctx, 'engine, 'func, HostData> Executor<'ctx, 'engine, 'func, HostData> {
}
}

fn visit_br_if_nez(&mut self, target: Target) {
fn visit_br_if_nez(&mut self, target: BranchParams) {
let condition = self.value_stack.pop_as();
if condition {
self.branch_to(target)
Expand Down
35 changes: 18 additions & 17 deletions crates/wasmi/src/engine/func_builder/control_frame.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{engine::LabelIdx, module::BlockType};
use super::labels::LabelRef;
use crate::module::BlockType;

/// A Wasm `block` control flow frame.
#[derive(Debug, Copy, Clone)]
Expand All @@ -8,12 +9,12 @@ pub struct BlockControlFrame {
/// The value stack height upon entering the [`BlockControlFrame`].
stack_height: u32,
/// Label representing the end of the [`BlockControlFrame`].
end_label: LabelIdx,
end_label: LabelRef,
}

impl BlockControlFrame {
/// Creates a new [`BlockControlFrame`].
pub fn new(block_type: BlockType, end_label: LabelIdx, stack_height: u32) -> Self {
pub fn new(block_type: BlockType, end_label: LabelRef, stack_height: u32) -> Self {
Self {
block_type,
stack_height,
Expand All @@ -26,12 +27,12 @@ impl BlockControlFrame {
/// # Note
///
/// Branches to [`BlockControlFrame`] jump to the end of the frame.
pub fn branch_destination(&self) -> LabelIdx {
pub fn branch_destination(&self) -> LabelRef {
self.end_label
}

/// Returns the label to the end of the [`BlockControlFrame`].
pub fn end_label(&self) -> LabelIdx {
pub fn end_label(&self) -> LabelRef {
self.end_label
}

Expand All @@ -54,12 +55,12 @@ pub struct LoopControlFrame {
/// The value stack height upon entering the [`LoopControlFrame`].
stack_height: u32,
/// Label representing the head of the [`LoopControlFrame`].
head_label: LabelIdx,
head_label: LabelRef,
}

impl LoopControlFrame {
/// Creates a new [`LoopControlFrame`].
pub fn new(block_type: BlockType, head_label: LabelIdx, stack_height: u32) -> Self {
pub fn new(block_type: BlockType, head_label: LabelRef, stack_height: u32) -> Self {
Self {
block_type,
stack_height,
Expand All @@ -72,7 +73,7 @@ impl LoopControlFrame {
/// # Note
///
/// Branches to [`LoopControlFrame`] jump to the head of the loop.
pub fn branch_destination(&self) -> LabelIdx {
pub fn branch_destination(&self) -> LabelRef {
self.head_label
}

Expand All @@ -95,9 +96,9 @@ pub struct IfControlFrame {
/// The value stack height upon entering the [`IfControlFrame`].
stack_height: u32,
/// Label representing the end of the [`IfControlFrame`].
end_label: LabelIdx,
end_label: LabelRef,
/// Label representing the optional `else` branch of the [`IfControlFrame`].
else_label: LabelIdx,
else_label: LabelRef,
/// End of `then` branch is reachable.
///
/// # Note
Expand All @@ -116,8 +117,8 @@ impl IfControlFrame {
/// Creates a new [`IfControlFrame`].
pub fn new(
block_type: BlockType,
end_label: LabelIdx,
else_label: LabelIdx,
end_label: LabelRef,
else_label: LabelRef,
stack_height: u32,
) -> Self {
assert_ne!(
Expand All @@ -138,17 +139,17 @@ impl IfControlFrame {
/// # Note
///
/// Branches to [`IfControlFrame`] jump to the end of the if and else frame.
pub fn branch_destination(&self) -> LabelIdx {
pub fn branch_destination(&self) -> LabelRef {
self.end_label
}

/// Returns the label to the end of the [`IfControlFrame`].
pub fn end_label(&self) -> LabelIdx {
pub fn end_label(&self) -> LabelRef {
self.end_label
}

/// Returns the label to the optional `else` of the [`IfControlFrame`].
pub fn else_label(&self) -> LabelIdx {
pub fn else_label(&self) -> LabelRef {
self.else_label
}

Expand Down Expand Up @@ -259,7 +260,7 @@ impl ControlFrame {
}

/// Returns the label for the branch destination of the [`ControlFrame`].
pub fn branch_destination(&self) -> LabelIdx {
pub fn branch_destination(&self) -> LabelRef {
match self {
Self::Block(frame) => frame.branch_destination(),
Self::Loop(frame) => frame.branch_destination(),
Expand All @@ -276,7 +277,7 @@ impl ControlFrame {
/// All [`ControlFrame`] kinds have it except [`ControlFrame::Loop`].
/// In order to a [`ControlFrame::Loop`] to branch outside it is required
/// to be wrapped in another control frame such as [`ControlFrame::Block`].
pub fn end_label(&self) -> LabelIdx {
pub fn end_label(&self) -> LabelRef {
match self {
Self::Block(frame) => frame.end_label(),
Self::If(frame) => frame.end_label(),
Expand Down
Loading