Skip to content

Commit

Permalink
wip: making the runner work
Browse files Browse the repository at this point in the history
  • Loading branch information
nerodesu017 committed Dec 16, 2024
1 parent f1fd142 commit aa23a77
Show file tree
Hide file tree
Showing 11 changed files with 660 additions and 34 deletions.
31 changes: 31 additions & 0 deletions src/core/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,34 @@ pub enum RuntimeError {
UninitializedElement,
SignatureMismatch,
ExpectedAValueOnTheStack,
UndefinedTableIndex,
// "undefined element" <- as-call_indirect-last
// "unreachable"
}

impl RuntimeError {
#[cfg(debug_assertions)]
pub fn to_wasm_testsuite_string(&self) -> alloc::string::String {
match self {
Self::DivideBy0 => "integer divide by zero",
Self::UnrepresentableResult => "integer overflow",
Self::FunctionNotFound => todo!(),
Self::StackSmash => todo!(),
Self::BadConversionToInteger => "invalid conversion to integer",

Self::MemoryAccessOutOfBounds => "out of bounds memory access",
Self::TableAccessOutOfBounds => "out of bounds table access",
Self::ElementAccessOutOfBounds => todo!(),

Self::UninitializedElement => "uninitialized element",
Self::SignatureMismatch => "indirect call type mismatch",
Self::ExpectedAValueOnTheStack => todo!(),

Self::UndefinedTableIndex => "undefined element",
// _ => "",
}
.to_string()
}
}

#[derive(Debug, PartialEq, Eq, Clone)]
Expand Down Expand Up @@ -234,6 +262,9 @@ impl Display for RuntimeError {
RuntimeError::ExpectedAValueOnTheStack => {
f.write_str("Expected a value on the stack, but None was found")
}
RuntimeError::UndefinedTableIndex => {
f.write_str("Indirect call: table index out of bounds")
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ pub mod error;

pub mod indices;
pub mod reader;
pub mod utils;
184 changes: 184 additions & 0 deletions src/core/reader/types/opcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,187 @@ pub mod fc_extensions {
pub const TABLE_SIZE: u8 = 0x10;
pub const TABLE_FILL: u8 = 0x11;
}

#[cfg(debug_assertions)]
pub fn opcode_byte_to_str(byte: u8) -> alloc::string::String {
use alloc::borrow::ToOwned;
match byte {
UNREACHABLE => "UNREACHABLE",
NOP => "NOP",
// BLOCK => "BLOCK",
// LOOP => "LOOP",
// IF => "IF",
// ELSE => "ELSE",
END => "END",
// BR => "BR",
// BR_IF => "BR_IF",
// BR_TABLE => "BR_TABLE",
RETURN => "RETURN",
CALL => "CALL",
// CALL_INDIRECT => "CALL_INDIRECT",
DROP => "DROP",
// SELECT => "SELECT",
LOCAL_GET => "LOCAL_GET",
LOCAL_SET => "LOCAL_SET",
LOCAL_TEE => "LOCAL_TEE",
GLOBAL_GET => "GLOBAL_GET",
GLOBAL_SET => "GLOBAL_SET",
I32_LOAD => "I32_LOAD",
I64_LOAD => "I64_LOAD",
F32_LOAD => "F32_LOAD",
F64_LOAD => "F64_LOAD",
I32_LOAD8_S => "I32_LOAD8_S",
I32_LOAD8_U => "I32_LOAD8_U",
I32_LOAD16_S => "I32_LOAD16_S",
I32_LOAD16_U => "I32_LOAD16_U",
I64_LOAD8_S => "I64_LOAD8_S",
I64_LOAD8_U => "I64_LOAD8_U",
I64_LOAD16_S => "I64_LOAD16_S",
I64_LOAD16_U => "I64_LOAD16_U",
I64_LOAD32_S => "I64_LOAD32_S",
I64_LOAD32_U => "I64_LOAD32_U",
I32_STORE => "I32_STORE",
I64_STORE => "I64_STORE",
F32_STORE => "F32_STORE",
F64_STORE => "F64_STORE",
I32_STORE8 => "I32_STORE8",
I32_STORE16 => "I32_STORE16",
I64_STORE8 => "I64_STORE8",
I64_STORE16 => "I64_STORE16",
I64_STORE32 => "I64_STORE32",
MEMORY_SIZE => "MEMORY_SIZE",
MEMORY_GROW => "MEMORY_GROW",
I32_CONST => "I32_CONST",
I64_CONST => "I64_CONST",
F32_CONST => "F32_CONST",
F64_CONST => "F64_CONST",
I32_EQZ => "I32_EQZ",
I32_EQ => "I32_EQ",
I32_NE => "I32_NE",
I32_LT_S => "I32_LT_S",
I32_LT_U => "I32_LT_U",
I32_GT_S => "I32_GT_S",
I32_GT_U => "I32_GT_U",
I32_LE_S => "I32_LE_S",
I32_LE_U => "I32_LE_U",
I32_GE_S => "I32_GE_S",
I32_GE_U => "I32_GE_U",
I64_EQZ => "I64_EQZ",
I64_EQ => "I64_EQ",
I64_NE => "I64_NE",
I64_LT_S => "I64_LT_S",
I64_LT_U => "I64_LT_U",
I64_GT_S => "I64_GT_S",
I64_GT_U => "I64_GT_U",
I64_LE_S => "I64_LE_S",
I64_LE_U => "I64_LE_U",
I64_GE_S => "I64_GE_S",
I64_GE_U => "I64_GE_U",
F32_EQ => "F32_EQ",
F32_NE => "F32_NE",
F32_LT => "F32_LT",
F32_GT => "F32_GT",
F32_LE => "F32_LE",
F32_GE => "F32_GE",
F64_EQ => "F64_EQ",
F64_NE => "F64_NE",
F64_LT => "F64_LT",
F64_GT => "F64_GT",
F64_LE => "F64_LE",
F64_GE => "F64_GE",
I32_CLZ => "I32_CLZ",
I32_CTZ => "I32_CTZ",
I32_POPCNT => "I32_POPCNT",
I32_ADD => "I32_ADD",
I32_SUB => "I32_SUB",
I32_MUL => "I32_MUL",
I32_DIV_S => "I32_DIV_S",
I32_DIV_U => "I32_DIV_U",
I32_REM_S => "I32_REM_S",
I32_REM_U => "I32_REM_U",
I32_AND => "I32_AND",
I32_OR => "I32_OR",
I32_XOR => "I32_XOR",
I32_SHL => "I32_SHL",
I32_SHR_S => "I32_SHR_S",
I32_SHR_U => "I32_SHR_U",
I32_ROTL => "I32_ROTL",
I32_ROTR => "I32_ROTR",
I64_CLZ => "I64_CLZ",
I64_CTZ => "I64_CTZ",
I64_POPCNT => "I64_POPCNT",
I64_ADD => "I64_ADD",
I64_SUB => "I64_SUB",
I64_MUL => "I64_MUL",
I64_DIV_S => "I64_DIV_S",
I64_DIV_U => "I64_DIV_U",
I64_REM_S => "I64_REM_S",
I64_REM_U => "I64_REM_U",
I64_AND => "I64_AND",
I64_OR => "I64_OR",
I64_XOR => "I64_XOR",
I64_SHL => "I64_SHL",
I64_SHR_S => "I64_SHR_S",
I64_SHR_U => "I64_SHR_U",
I64_ROTL => "I64_ROTL",
I64_ROTR => "I64_ROTR",
F32_ABS => "F32_ABS",
F32_NEG => "F32_NEG",
F32_CEIL => "F32_CEIL",
F32_FLOOR => "F32_FLOOR",
F32_TRUNC => "F32_TRUNC",
F32_NEAREST => "F32_NEAREST",
F32_SQRT => "F32_SQRT",
F32_ADD => "F32_ADD",
F32_SUB => "F32_SUB",
F32_MUL => "F32_MUL",
F32_DIV => "F32_DIV",
F32_MIN => "F32_MIN",
F32_MAX => "F32_MAX",
F32_COPYSIGN => "F32_COPYSIGN",
F64_ABS => "F64_ABS",
F64_NEG => "F64_NEG",
F64_CEIL => "F64_CEIL",
F64_FLOOR => "F64_FLOOR",
F64_TRUNC => "F64_TRUNC",
F64_NEAREST => "F64_NEAREST",
F64_SQRT => "F64_SQRT",
F64_ADD => "F64_ADD",
F64_SUB => "F64_SUB",
F64_MUL => "F64_MUL",
F64_DIV => "F64_DIV",
F64_MIN => "F64_MIN",
F64_MAX => "F64_MAX",
F64_COPYSIGN => "F64_COPYSIGN",
I32_WRAP_I64 => "I32_WRAP_I64",
I32_TRUNC_F32_S => "I32_TRUNC_F32_S",
I32_TRUNC_F32_U => "I32_TRUNC_F32_U",
I32_TRUNC_F64_S => "I32_TRUNC_F64_S",
I32_TRUNC_F64_U => "I32_TRUNC_F64_U",
I64_EXTEND_I32_S => "I64_EXTEND_I32_S",
I64_EXTEND_I32_U => "I64_EXTEND_I32_U",
I64_TRUNC_F32_S => "I64_TRUNC_F32_S",
I64_TRUNC_F32_U => "I64_TRUNC_F32_U",
I64_TRUNC_F64_S => "I64_TRUNC_F64_S",
I64_TRUNC_F64_U => "I64_TRUNC_F64_U",
F32_CONVERT_I32_S => "F32_CONVERT_I32_S",
F32_CONVERT_I32_U => "F32_CONVERT_I32_U",
F32_CONVERT_I64_S => "F32_CONVERT_I64_S",
F32_CONVERT_I64_U => "F32_CONVERT_I64_U",
F32_DEMOTE_F64 => "F32_DEMOTE_F64",
F64_CONVERT_I32_S => "F64_CONVERT_I32_S",
F64_CONVERT_I32_U => "F64_CONVERT_I32_U",
F64_CONVERT_I64_S => "F64_CONVERT_I64_S",
F64_CONVERT_I64_U => "F64_CONVERT_I64_U",
F64_PROMOTE_F32 => "F64_PROMOTE_F32",
I32_REINTERPRET_F32 => "I32_REINTERPRET_F32",
I64_REINTERPRET_F64 => "I64_REINTERPRET_F64",
F32_REINTERPRET_I32 => "F32_REINTERPRET_I32",
F64_REINTERPRET_I64 => "F64_REINTERPRET_I64",
REF_NULL => "REF_NULL",
REF_FUNC => "REF_FUNC",
FC_EXTENSIONS => "FC_EXTENSIONS",
_ => "UNKNOWN",
}
.to_owned()
}
10 changes: 10 additions & 0 deletions src/core/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#[cfg(debug_assertions)]
pub fn print_beautiful_instruction_name_1_byte(first_byte: u8, pc: usize) {
use crate::core::reader::types::opcode::opcode_byte_to_str;

trace!(
"Read instruction {} at wasm_binary[{}]",
opcode_byte_to_str(first_byte),
pc
);
}
2 changes: 2 additions & 0 deletions src/execution/function_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ impl FunctionRef {
) -> Result<Vec<Value>, RuntimeError> {
runtime.invoke_dynamic(self, params, ret_types)
}

// pub fn get_return_types(&self) -> Vec<Value
}
23 changes: 14 additions & 9 deletions src/execution/interpreter_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pub(super) fn run<H: HookSet>(
.get(stack.current_stackframe().func_idx)
.unwrap_validated();

#[cfg(debug_assertions)]
// Start reading the function's instructions
let mut wasm = WasmReader::new(wasm_bytecode);

Expand All @@ -59,6 +60,12 @@ pub(super) fn run<H: HookSet>(

let first_instr_byte = wasm.read_u8().unwrap_validated();

#[cfg(debug_assertions)]
trace!(
"Executing instruction {}",
opcode_byte_to_str(first_instr_byte)
);

match first_instr_byte {
NOP => {
trace!("Instruction: NOP");
Expand Down Expand Up @@ -128,7 +135,7 @@ pub(super) fn run<H: HookSet>(
let r = tab
.elem
.get(i as usize)
.ok_or(RuntimeError::TableAccessOutOfBounds)
.ok_or(RuntimeError::UndefinedTableIndex)
.and_then(|r| {
if r.is_null() {
trace!("table_idx ({table_idx}) --- element index in table ({i})");
Expand All @@ -141,12 +148,10 @@ pub(super) fn run<H: HookSet>(
let func_addr = match *r {
Ref::Func(func_addr) => func_addr.addr,
Ref::Extern(_) => unreachable!(),
};
}
.unwrap_validated();

let func_to_call_inst = store
.funcs
.get(func_addr.unwrap_validated())
.unwrap_validated();
let func_to_call_inst = store.funcs.get(func_addr).unwrap_validated();

let func_ty_actual_index = func_to_call_inst.ty;

Expand All @@ -159,7 +164,7 @@ pub(super) fn run<H: HookSet>(

trace!("Instruction: call_indirect [{func_addr:?}]");
let locals = Locals::new(params, remaining_locals);
stack.push_stackframe(func_addr.unwrap_validated(), func_ty, locals, wasm.pc);
stack.push_stackframe(func_addr, func_ty, locals, wasm.pc);

wasm.move_start_to(func_to_call_inst.code_expr)
.unwrap_validated();
Expand Down Expand Up @@ -628,7 +633,7 @@ pub(super) fn run<H: HookSet>(
I64_STORE => {
let memarg = MemArg::read_unvalidated(&mut wasm);

let data_to_store: u32 = stack.pop_value(ValType::NumType(NumType::I64)).into();
let data_to_store: u64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();

let mem = store.mems.get_mut(0).unwrap_validated(); // there is only one memory allowed as of now
Expand Down Expand Up @@ -681,7 +686,7 @@ pub(super) fn run<H: HookSet>(
let memory_location = address
.and_then(|address| {
let address = address as usize;
mem.data.get_mut(address..(address + 4))
mem.data.get_mut(address..(address + 8))
})
.ok_or(RuntimeError::MemoryAccessOutOfBounds)?;

Expand Down
52 changes: 52 additions & 0 deletions src/execution/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,58 @@ where
Ok(ret)
}

#[cfg(debug_assertions)]
pub fn invoke_dynamic_unchecked_return_ty(
&mut self,
function_ref: &FunctionRef,
params: Vec<Value>,
) -> Result<Vec<Value>, RuntimeError> {
// First, verify that the function reference is valid
let (_module_idx, func_idx) = self.verify_function_ref(function_ref)?;

// -=-= Verification =-=-
let func_inst = self.store.funcs.get(func_idx).expect("valid FuncIdx");
let func_ty = self.types.get(func_inst.ty).unwrap_validated();

// Verify that the given parameters match the function parameters
let param_types = params.iter().map(|v| v.to_ty()).collect::<Vec<_>>();

if func_ty.params.valtypes != param_types {
panic!("Invalid parameters for function");
}

// Prepare a new stack with the locals for the entry function
let mut stack = Stack::new();
let locals = Locals::new(params.into_iter(), func_inst.locals.iter().cloned());
stack.push_stackframe(func_idx, func_ty, locals, 0);

// Run the interpreter
run(
self.wasm_bytecode,
&self.types,
&mut self.store,
&mut stack,
EmptyHookSet,
)?;

let func_inst = self.store.funcs.get(func_idx).expect("valid FuncIdx");
let func_ty = self.types.get(func_inst.ty).unwrap_validated();

// Pop return values from stack
let return_values = func_ty
.returns
.valtypes
.iter()
.map(|ty| stack.pop_value(*ty))
.collect::<Vec<Value>>();

// Values are reversed because they were popped from stack one-by-one. Now reverse them back
let reversed_values = return_values.into_iter().rev();
let ret = reversed_values.collect();
debug!("Successfully invoked function");
Ok(ret)
}

// TODO: replace this with the lookup table when implmenting the linker
fn get_indicies(
&self,
Expand Down
3 changes: 2 additions & 1 deletion src/execution/value_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,9 @@ impl Stack {
/// Copy value from top of the value stack to the given local
pub fn tee_local(&mut self, idx: LocalIdx) {
let local_ty = self.current_stackframe().locals.get_ty(idx);

let stack_value = self.peek_value(local_ty);

trace!("Instruction: local.tee [{stack_value:?}] -> []");
*self.current_stackframe_mut().locals.get_mut(idx) = stack_value;
}

Expand Down
Loading

0 comments on commit aa23a77

Please sign in to comment.