Skip to content

Commit

Permalink
feat: select t* instruction validation/execution
Browse files Browse the repository at this point in the history
Signed-off-by: Cem Onem <cem.oenem@dlr.de>
  • Loading branch information
Cem Onem committed Dec 17, 2024
1 parent 93de1ce commit 643f6c9
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 0 deletions.
8 changes: 8 additions & 0 deletions src/core/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ pub enum Error {
ValidationCtrlStackEmpty,
ElseWithoutMatchingIf,
IfWithoutMatchingElse,
TypeUnificationMismatch,
InvalidSelectTypeVector,
}

impl Display for Error {
Expand Down Expand Up @@ -230,6 +232,12 @@ impl Display for Error {
Error::IfWithoutMatchingElse => {
f.write_str("read 'end' without matching 'else' instruction to 'if' instruction")
}
Error::TypeUnificationMismatch => {
f.write_str("cannot unify types")
}
Error::InvalidSelectTypeVector => {
f.write_str("SELECT T* (0x1C) instruction must have exactly one type in the subsequent type vector")
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/core/reader/types/opcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ pub const BR_TABLE: u8 = 0x0E;
pub const RETURN: u8 = 0x0F;
pub const CALL: u8 = 0x10;
pub const DROP: u8 = 0x1A;
pub const SELECT: u8 = 0x1B;
pub const SELECT_T: u8 = 0x1C;
pub const CALL_INDIRECT: u8 = 0x11;
pub const LOCAL_GET: u8 = 0x20;
pub const LOCAL_SET: u8 = 0x21;
Expand Down
21 changes: 21 additions & 0 deletions src/execution/interpreter_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,27 @@ pub(super) fn run<H: HookSet>(
DROP => {
stack.drop_value();
}
SELECT => {
let test_val: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
let val2 = stack.pop_value_with_unknown_type();
let val1 = stack.pop_value_with_unknown_type();
if test_val != 0 {
stack.push_value(val1);
} else {
stack.push_value(val2);
}
}
SELECT_T => {
let type_vec = wasm.read_vec(ValType::read).unwrap_validated();
let test_val: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
let val2 = stack.pop_value(type_vec[0]);
let val1 = stack.pop_value(type_vec[0]);
if test_val != 0 {
stack.push_value(val1);
} else {
stack.push_value(val2);
}
}
LOCAL_GET => {
let local_idx = wasm.read_var_u32().unwrap_validated() as LocalIdx;
stack.get_local(local_idx);
Expand Down
5 changes: 5 additions & 0 deletions src/execution/value_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ impl Stack {
}
}

//unfortunately required for polymorphic select
pub fn pop_value_with_unknown_type(&mut self) -> Value {
self.values.pop().unwrap_validated()
}

/// Copy a value of the given [ValType] from the value stack without removing it
pub fn peek_value(&self, ty: ValType) -> Value {
let value = self.values.last().unwrap_validated();
Expand Down
13 changes: 13 additions & 0 deletions src/validation/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,19 @@ fn read_instructions(
DROP => {
stack.drop_val()?;
}
SELECT => {
stack.validate_polymorphic_select()?;
}
SELECT_T => {
let type_vec = wasm.read_vec(ValType::read)?;
if type_vec.len() != 1 {
return Err(Error::InvalidSelectTypeVector);
}
stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
stack.assert_pop_val_type(type_vec[0])?;
stack.assert_pop_val_type(type_vec[0])?;
stack.push_valtype(type_vec[0]);
}
// local.get: [] -> [t]
LOCAL_GET => {
let local_idx = wasm.read_var_u32()? as LocalIdx;
Expand Down
51 changes: 51 additions & 0 deletions src/validation/validation_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,30 @@ impl ValidationStack {
last_ctrl_stack_entry.block_ty,
))
}

pub fn validate_polymorphic_select(&mut self) -> Result<()> {
//SELECT instruction has the type signature
//[t t i32] -> [t] where t is a Num or Vec Type

// TODO write this more efficiently
self.assert_pop_val_type(ValType::NumType(crate::NumType::I32))?;

let unified = self
.pop_valtype()?
.unify(&self.pop_valtype()?)
.map_err(|_| Error::InvalidValidationStackValType(None))?;

match unified {
ValidationStackEntry::UnspecifiedValTypes => {
//if unified is a bottom type only way to satisfy validation of SELECT is to unify it to NumOrVec
self.stack.push(ValidationStackEntry::NumOrVecType);
}
_ => {
self.stack.push(unified);
}
}
Ok(())
}
}

#[derive(Clone, Debug, PartialEq, Eq)]
Expand All @@ -326,6 +350,33 @@ pub enum ValidationStackEntry {
// TODO change this name to BottomType
UnspecifiedValTypes,
}

impl ValidationStackEntry {
// TODO better way to write this?
fn unify(&self, other: &ValidationStackEntry) -> Result<Self> {
match self {
ValidationStackEntry::Val(s) => match other {
Self::Val(o) => (o == s)
.then_some(self.clone())
.ok_or(Error::TypeUnificationMismatch),
Self::NumOrVecType => self.unify_to_num_or_vec_type(),
Self::UnspecifiedValTypes => Ok(self.clone()),
},
ValidationStackEntry::NumOrVecType => other.unify_to_num_or_vec_type(),
ValidationStackEntry::UnspecifiedValTypes => Ok(other.clone()),
}
}

fn unify_to_num_or_vec_type(&self) -> Result<Self> {
match self {
ValidationStackEntry::Val(ValType::NumType(_)) => Ok(self.clone()),
ValidationStackEntry::Val(ValType::VecType) => Ok(self.clone()),
ValidationStackEntry::NumOrVecType => Ok(self.clone()),
_ => Err(Error::TypeUnificationMismatch),
}
}
}

// TODO hide implementation
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CtrlStackEntry {
Expand Down

0 comments on commit 643f6c9

Please sign in to comment.