Skip to content

Commit

Permalink
refactor: magic bytes to constant op codes
Browse files Browse the repository at this point in the history
  • Loading branch information
nerodesu017 committed Jul 8, 2024
1 parent b4021bb commit 1d2cd50
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 77 deletions.
2 changes: 1 addition & 1 deletion src/core/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub enum Error {
InvalidExportDesc(u8),
InvalidImportDesc(u8),
ExprMissingEnd,
InvalidInstr(u8),
InvalidInstr(u16),
EndInvalidValueStack,
InvalidLocalIdx,
InvalidValueStackType(Option<ValType>),
Expand Down
1 change: 1 addition & 0 deletions src/core/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod error;

pub mod indices;
pub mod opcodes;
pub mod reader;
21 changes: 21 additions & 0 deletions src/core/opcodes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
pub const NOP: u8 = 0x01;
pub const END: u8 = 0x0B;
pub const LOCAL_GET: u8 = 0x20;
pub const LOCAL_SET: u8 = 0x21;
pub const GLOBAL_GET: u8 = 0x23;
pub const GLOBAL_SET: u8 = 0x24;
pub const I32_LOAD: u8 = 0x28;
pub const I32_STORE: u8 = 0x36;
pub const I32_CONST: u8 = 0x41;
pub const I32_ADD: u8 = 0x6A;
pub const I32_MUL: u8 = 0x6C;
pub const I32_DIV_S: u8 = 0x6D;
pub const I32_DIV_U: u8 = 0x6E;
pub const FB_INSTRUCTIONS: u8 = 0xFB;
pub const FC_INSTRUCTIONS: u8 = 0xFC;
pub const FD_INSTRUCTIONS: u8 = 0xFD;
pub const FE_INSTRUCTIONS: u8 = 0xFE;

pub mod fc_opcodes {
pub const I32_TRUNC_SAT_F32S: u8 = 0x00;
}
30 changes: 18 additions & 12 deletions src/core/reader/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,47 +8,51 @@ pub mod types;
/// Its purpose is mostly to abstract parsing basic WASM values from the bytecode.
pub struct WasmReader<'a> {
pub(crate) full_contents: &'a [u8],
pub(crate) current: &'a [u8],
pub(crate) pc: usize,
}

impl<'a> WasmReader<'a> {
pub fn new(wasm: &'a [u8]) -> Self {
Self {
full_contents: wasm,
current: wasm,
pc: 0,
}
}
// TODO this is not very intuitive but we cannot shorten `self.current`'s end
// because some methods rely on the property that `self.current`'s and
// `self.full_contents`'s last element are equal.
pub fn move_start_to(&mut self, span: Span) {
self.current =
&self.full_contents[span.from../* normally we would have the end of the span here*/];
self.pc = span.from;
}

pub fn remaining_bytes(&self) -> &[u8] {
self.current
&self.full_contents[self.pc..]
}

pub fn current_idx(&self) -> usize {
self.full_contents.len() - self.current.len()
self.pc
}
pub fn make_span(&self, len: usize) -> Span {
Span::new(self.current_idx(), len)
}

pub fn strip_bytes<const N: usize>(&mut self) -> Result<[u8; N]> {
if N > self.current.len() {
if N > (self.full_contents.len() - self.pc) {
return Err(Error::Eof);
}

let (bytes, rest) = self.current.split_at(N);
self.current = rest;
let (bytes, _) = self.full_contents[self.pc..].split_at(N);

self.pc += N;

Ok(bytes.try_into().expect("the slice length to be exactly N"))
}
pub fn peek_u8(&self) -> Result<u8> {
self.current.first().copied().ok_or(Error::Eof)
if self.pc >= self.full_contents.len() {
Err(Error::Eof)
} else {
Ok(self.full_contents[self.pc])
}
}

pub fn measure_num_read_bytes<T>(
Expand All @@ -63,10 +67,12 @@ impl<'a> WasmReader<'a> {
}

pub fn skip(&mut self, num_bytes: usize) -> Result<()> {
if self.current.len() < num_bytes {
if self.full_contents.len() - self.pc < num_bytes {
return Err(Error::Eof);
}
self.current = &self.current[num_bytes..];

self.pc += num_bytes;

Ok(())
}
pub fn into_inner(self) -> &'a [u8] {
Expand Down
61 changes: 52 additions & 9 deletions src/core/reader/types/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,26 @@
use alloc::vec::Vec;
use core::mem;

use crate::core::opcodes::{FB_INSTRUCTIONS, FC_INSTRUCTIONS, FD_INSTRUCTIONS, FE_INSTRUCTIONS};
use crate::core::reader::WasmReader;
use crate::{Error, Result};

impl WasmReader<'_> {
/// Note: If `Err`, the [Wasm] object is no longer guaranteed to be in a valid state
pub fn read_u8(&mut self) -> Result<u8> {
let value = *self.current.first().ok_or(Error::Eof)?;
if self.full_contents.len() <= self.pc {
return Err(Error::Eof);
}

let byte = self.full_contents[self.pc];

self.current = self
.current
.get(1..)
.expect("slice to contain at least 1 element");
self.pc += 1;

if self.full_contents.len() - self.pc == 0 {
panic!("slice to contain at least 1 element")
}

Ok(value)
Ok(byte)
}

/// Parses a variable-length `u32` as specified by [LEB128](https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128).
Expand Down Expand Up @@ -66,11 +72,13 @@ impl WasmReader<'_> {
pub fn read_name(&mut self) -> Result<&str> {
let len = self.read_var_u32()? as usize;

if len > self.current.len() {
if len > (self.full_contents.len() - self.pc) {
return Err(Error::Eof);
}
let (utf8_str, rest) = self.current.split_at(len); // Cannot panic because check is done above
self.current = rest;

let (utf8_str, _) = self.full_contents[self.pc..].split_at(len);

self.pc += utf8_str.len();

core::str::from_utf8(utf8_str).map_err(Error::MalformedUtf8String)
}
Expand All @@ -95,6 +103,41 @@ impl WasmReader<'_> {
let len = self.read_var_u32()?;
(0..len).map(|_| read_element(self)).collect()
}

pub fn read_instruction(&mut self) -> Result<&[u8]> {
// if self.pc.is_empty() {
// return Err(Error::Eof);
// }
if self.full_contents.len() - self.pc == 0 {
return Err(Error::Eof);
}

match self.full_contents[self.pc..] {
// check if we are at a multibyte (2 byte) instruction
[FB_INSTRUCTIONS, _, ..]
| [FC_INSTRUCTIONS, _, ..]
| [FD_INSTRUCTIONS, _, ..]
| [FE_INSTRUCTIONS, _, ..] => {
let bytes = &self.full_contents[self.pc..self.pc + 2];
trace!("Multibyte instruction: 0x{:2X} 0x{:2X}", bytes[0], bytes[1]);
self.strip_bytes::<2>()?;
Ok(bytes)
}
// if we aren't at a multibyte instruction, we are at a 1 byte instruction
[_, ..] => {
let bytes = &self.full_contents[self.pc..self.pc + 1];
trace!("Single instruction: 0x{:2X}", bytes[0]);
self.strip_bytes::<1>()?;
Ok(bytes)
}
_ => {
unreachable!()
}
}

// TODO: implement
// Ok(&[1])
}
}

#[cfg(test)]
Expand Down
56 changes: 41 additions & 15 deletions src/execution/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use alloc::vec::Vec;
use value_stack::Stack;

use crate::core::indices::{FuncIdx, GlobalIdx, LocalIdx};
use crate::core::opcodes::*;
use crate::core::reader::types::memarg::MemArg;
use crate::core::reader::types::{FuncType, NumType, ValType};
use crate::core::reader::{WasmReadable, WasmReader};
Expand All @@ -12,7 +13,7 @@ use crate::execution::store::{FuncInst, GlobalInst, MemInst, Store};
use crate::execution::value::Value;
use crate::validation::code::read_declared_locals;
use crate::value::InteropValueList;
use crate::Error::RuntimeError;
use crate::Error::{self, RuntimeError};
use crate::RuntimeError::{DivideBy0, UnrepresentableResult};
use crate::{Result, ValidationInfo};

Expand Down Expand Up @@ -111,42 +112,48 @@ impl<'b> RuntimeInstance<'b> {
wasm.move_start_to(inst.code_expr);

loop {
match wasm.read_u8().unwrap_validated() {
let instr = &wasm.full_contents[wasm.pc..];
if instr.len() == 0 {
return Err(Error::Eof);
}
wasm.strip_bytes::<1>()?;
trace!("Read instruction byte {:#x?}", instr.get(0).unwrap());
match instr {
// end
0x0B => {
[NOP, ..] | [END, ..] => {
break;
}
// local.get: [] -> [t]
0x20 => {
[LOCAL_GET, ..] => {
let local_idx = wasm.read_var_u32().unwrap_validated() as LocalIdx;
let local = locals.get(local_idx);
trace!("Instruction: local.get [] -> [{local:?}]");
stack.push_value(local.clone());
}
// local.set [t] -> []
0x21 => {
[LOCAL_SET, ..] => {
let local_idx = wasm.read_var_u32().unwrap_validated() as LocalIdx;
let local = locals.get_mut(local_idx);
let value = stack.pop_value(local.to_ty());
trace!("Instruction: local.set [{local:?}] -> []");
*local = value;
}
// global.get [] -> [t]
0x23 => {
[GLOBAL_GET, ..] => {
let global_idx = wasm.read_var_u32().unwrap_validated() as GlobalIdx;
let global = self.store.globals.get(global_idx).unwrap_validated();

stack.push_value(global.value.clone());
}
// global.set [t] -> []
0x24 => {
[GLOBAL_SET, ..] => {
let global_idx = wasm.read_var_u32().unwrap_validated() as GlobalIdx;
let global = self.store.globals.get_mut(global_idx).unwrap_validated();

global.value = stack.pop_value(global.global.ty.ty)
}
// i32.load [i32] -> [i32]
0x28 => {
[I32_LOAD, ..] => {
let memarg = MemArg::read_unvalidated(&mut wasm);
let relative_address: u32 =
stack.pop_value(ValType::NumType(NumType::I32)).into();
Expand Down Expand Up @@ -174,7 +181,7 @@ impl<'b> RuntimeInstance<'b> {
trace!("Instruction: i32.load [{relative_address}] -> [{data}]");
}
// i32.store [i32] -> [i32]
0x36 => {
[I32_STORE, ..] => {
let memarg = MemArg::read_unvalidated(&mut wasm);

let data_to_store: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
Expand All @@ -197,13 +204,13 @@ impl<'b> RuntimeInstance<'b> {
trace!("Instruction: i32.store [{relative_address} {data_to_store}] -> []");
}
// i32.const: [] -> [i32]
0x41 => {
[I32_CONST, ..] => {
let constant = wasm.read_var_i32().unwrap_validated();
trace!("Instruction: i32.const [] -> [{constant}]");
stack.push_value(constant.into());
}
// i32.add: [i32 i32] -> [i32]
0x6A => {
[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);
Expand All @@ -212,7 +219,7 @@ impl<'b> RuntimeInstance<'b> {
stack.push_value(res.into());
}
// i32.mul: [i32 i32] -> [i32]
0x6C => {
[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);
Expand All @@ -221,7 +228,7 @@ impl<'b> RuntimeInstance<'b> {
stack.push_value(res.into());
}
// i32.div_s: [i32 i32] -> [i32]
0x6D => {
[I32_DIV_S, ..] => {
let dividend: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
let divisor: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();

Expand All @@ -238,7 +245,7 @@ impl<'b> RuntimeInstance<'b> {
stack.push_value(res.into());
}
// i32.div_u: [i32 i32] -> [i32]
0x6E => {
[I32_DIV_U, ..] => {
let dividend: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
let divisor: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();

Expand All @@ -254,9 +261,28 @@ impl<'b> RuntimeInstance<'b> {
trace!("Instruction: i32.div_u [{divisor} {dividend}] -> [{res}]");
stack.push_value(res.into());
}
other => {
[FB_INSTRUCTIONS, _, ..] => {
wasm.strip_bytes::<1>()?;
unimplemented!()
}
[FC_INSTRUCTIONS, _, ..] => {
wasm.strip_bytes::<1>()?;
unimplemented!()
}
[FD_INSTRUCTIONS, _, ..] => {
wasm.strip_bytes::<1>()?;
unimplemented!()
}
[FE_INSTRUCTIONS, _, ..] => {
wasm.strip_bytes::<1>()?;
unimplemented!()
}
[other, ..] => {
trace!("Unknown instruction {other:#x}, skipping..");
}
&[] => {
unreachable!()
}
}
}
Ok(())
Expand Down
Loading

0 comments on commit 1d2cd50

Please sign in to comment.