Skip to content

Commit

Permalink
refactor(globals): move global implementation around and document it
Browse files Browse the repository at this point in the history
Signed-off-by: George Cosma <[email protected]>
  • Loading branch information
george-cosma authored and wucke13 committed Sep 11, 2024
1 parent df76086 commit 0e25525
Show file tree
Hide file tree
Showing 8 changed files with 309 additions and 226 deletions.
6 changes: 3 additions & 3 deletions src/core/reader/types/global.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use alloc::vec;

use crate::code::{read_constant_instructions, validate_value_stack};
use crate::core::reader::span::Span;
use crate::core::reader::types::{ResultType, ValType};
use crate::core::reader::{WasmReadable, WasmReader};
use crate::execution::assert_validated::UnwrapValidatedExt;
use crate::{unreachable_validated, Error, Result};
use crate::globals::read_constant_instructions;
use crate::{unreachable_validated, validate_value_stack, Error, Result};

#[derive(Debug, Copy, Clone)]
pub struct Global {
Expand Down Expand Up @@ -36,7 +36,7 @@ impl WasmReadable for Global {
// Error. If an Error is returned it is pushed up the call stack.
Ok(Self {
ty,
init_expr: init_expr.unwrap_validated(),
init_expr: init_expr.expect("expected gobal init expression to be initialized on successful value stack validation"),
})
}

Expand Down
101 changes: 101 additions & 0 deletions src/execution/const_interpreter_loop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use crate::{
assert_validated::UnwrapValidatedExt, core::reader::WasmReader, value_stack::Stack, NumType,
ValType,
};

/// Execute a previosly-validated constant expression. These type of expressions are used for initializing global
/// variables.
///
/// # Arguments
/// - `wasm` - a [WasmReader] whose [program counter](WasmReader::pc) is set at the beginning of the constant
/// expression. Reader will be consumed.
/// - `stack` - a [Stack]. It is preferrable for it to be clean, but that is not required. As long as the executed code
/// is validated, the values on this stack will remain the same except for the addition of the return value of this
/// code sequence. A global's final value can be popped off the top of the stack.
/// - `imported_globals` (TODO) - instances of all imported globals. They are required as local globals can reference
/// imported globals in their initialization.
///
/// # Safety
/// This function assumes that the expression has been validated. Passing unvalidated code will likely result in a
/// panic, or undefined behaviour.
///
/// # Note
/// The following instructions are not yet supported:
/// - `ref.null`
/// - `ref.func`
/// - `global.get`
pub(crate) fn run_const(
mut wasm: WasmReader,
stack: &mut Stack,
_imported_globals: (), /*todo!*/
) {
use crate::core::reader::types::opcode::*;
loop {
let first_instr_byte = wasm.read_u8().unwrap_validated();

match first_instr_byte {
END => {
break;
}
I32_CONST => {
let constant = wasm.read_var_i32().unwrap_validated();
trace!("Constant instruction: i32.const [] -> [{constant}]");
stack.push_value(constant.into());
}
I32_ADD => {
let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
let res = v1.wrapping_add(v2);

trace!("Constant instruction: i32.add [{v1} {v2}] -> [{res}]");
stack.push_value(res.into());
}
I32_SUB => {
let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
let res = v1.wrapping_sub(v2);

trace!("Constant instruction: i32.sub [{v1} {v2}] -> [{res}]");
stack.push_value(res.into());
}
I32_MUL => {
let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
let res = v1.wrapping_mul(v2);

trace!("Constant instruction: i32.mul [{v1} {v2}] -> [{res}]");
stack.push_value(res.into());
}
I64_CONST => {
let constant = wasm.read_var_i64().unwrap_validated();
trace!("Constant instruction: i64.const [] -> [{constant}]");
stack.push_value(constant.into());
}
I64_ADD => {
let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
let res = v1.wrapping_add(v2);

trace!("Constant instruction: i64.add [{v1} {v2}] -> [{res}]");
stack.push_value(res.into());
}
I64_SUB => {
let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
let res = v1.wrapping_sub(v2);

trace!("Constant instruction: i64.sub [{v1} {v2}] -> [{res}]");
stack.push_value(res.into());
}
I64_MUL => {
let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
let res = v1.wrapping_mul(v2);

trace!("Constant instruction: i64.mul [{v1} {v2}] -> [{res}]");
stack.push_value(res.into());
}
_ => panic!(\\_(ツ)_/¯"),
}
}
}
73 changes: 0 additions & 73 deletions src/execution/interpreter_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1410,76 +1410,3 @@ pub(super) fn run<H: HookSet>(
}
Ok(())
}

pub fn run_const(mut wasm: WasmReader, stack: &mut Stack, _imported_globals: () /*todo!*/) {
use crate::core::reader::types::opcode::*;
loop {
let first_instr_byte = wasm.read_u8().unwrap_validated();

match first_instr_byte {
// Missing: ref.null, ref.func, global.get
END => {
break;
}
I32_CONST => {
let constant = wasm.read_var_i32().unwrap_validated();
trace!("Constant instruction: i32.const [] -> [{constant}]");
stack.push_value(constant.into());
}
I32_ADD => {
let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
let res = v1.wrapping_add(v2);

trace!("Constant instruction: i32.add [{v1} {v2}] -> [{res}]");
stack.push_value(res.into());
}
I32_SUB => {
let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
let res = v1.wrapping_sub(v2);

trace!("Constant instruction: i32.sub [{v1} {v2}] -> [{res}]");
stack.push_value(res.into());
}
I32_MUL => {
let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
let res = v1.wrapping_mul(v2);

trace!("Constant instruction: i32.mul [{v1} {v2}] -> [{res}]");
stack.push_value(res.into());
}
I64_CONST => {
let constant = wasm.read_var_i64().unwrap_validated();
trace!("Constant instruction: i64.const [] -> [{constant}]");
stack.push_value(constant.into());
}
I64_ADD => {
let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
let res = v1.wrapping_add(v2);

trace!("Constant instruction: i64.add [{v1} {v2}] -> [{res}]");
stack.push_value(res.into());
}
I64_SUB => {
let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
let res = v1.wrapping_sub(v2);

trace!("Constant instruction: i64.sub [{v1} {v2}] -> [{res}]");
stack.push_value(res.into());
}
I64_MUL => {
let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
let res = v1.wrapping_mul(v2);

trace!("Constant instruction: i64.mul [{v1} {v2}] -> [{res}]");
stack.push_value(res.into());
}
_ => panic!(\\_(ツ)_/¯"),
}
}
}
29 changes: 17 additions & 12 deletions src/execution/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use alloc::vec::Vec;

use interpreter_loop::{run, run_const};
use const_interpreter_loop::run_const;
use interpreter_loop::run;
use locals::Locals;
use value_stack::Stack;

Expand All @@ -18,6 +19,7 @@ use crate::{RuntimeError, ValidationInfo};

// TODO
pub(crate) mod assert_validated;
mod const_interpreter_loop;
pub mod hooks;
mod interpreter_loop;
pub(crate) mod locals;
Expand Down Expand Up @@ -240,18 +242,21 @@ where
let global_instances: Vec<GlobalInst> = validation_info
.globals
.iter()
.map(|global| {
let mut wasm = WasmReader::new(validation_info.wasm);
// The place we are moving the start to should, by all means, be inside the wasm bytecode.
wasm.move_start_to(global.init_expr).unwrap_validated();
.map({
let mut stack = Stack::new();

run_const(wasm, &mut stack, ());
let value = stack.pop_value(global.ty.ty);

GlobalInst {
global: *global,
value,
move |global| {
let mut wasm = WasmReader::new(validation_info.wasm);
// The place we are moving the start to should, by all means, be inside the wasm bytecode.
wasm.move_start_to(global.init_expr).unwrap_validated();
// We shouldn't need to clear the stack. If validation is correct, it will remain empty after execution.

run_const(wasm, &mut stack, ());
let value = stack.pop_value(global.ty.ty);

GlobalInst {
global: *global,
value,
}
}
})
.collect();
Expand Down
Loading

0 comments on commit 0e25525

Please sign in to comment.