Skip to content

Commit

Permalink
Merge pull request #387 from OLUWAMUYIWA/new-assembly-instructn
Browse files Browse the repository at this point in the history
New assembly instructions
  • Loading branch information
bobbinth authored Sep 7, 2022
2 parents 66a7a62 + 1c3fd0b commit e658813
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 0 deletions.
27 changes: 27 additions & 0 deletions assembly/src/parsers/field_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ pub(super) fn parse_assert_eq(
Ok(())
}

/// Appends EQZ ASSERT operation sequence to the span block.
pub(super) fn parse_assertz(
span_ops: &mut Vec<Operation>,
op: &Token,
) -> Result<(), AssemblyError> {
if op.num_parts() > 1 {
return Err(AssemblyError::extra_param(op));
}
span_ops.push(Operation::Eqz);
span_ops.push(Operation::Assert);
Ok(())
}

// ARITHMETIC OPERATIONS
// ================================================================================================

Expand Down Expand Up @@ -696,6 +709,20 @@ mod tests {
);
}

#[test]
fn assertz() {
let mut span_ops: Vec<Operation> = Vec::new();
let op_pos = 0;

let op_err = Token::new("assertz.2", op_pos);
let expected_err = AssemblyError::extra_param(&op_err);

assert_eq!(
parse_assertz(&mut span_ops, &op_err).unwrap_err(),
expected_err
);
}

#[test]
fn lt() {
// parse_lt should return an error if called with an invalid or incorrect operation
Expand Down
3 changes: 3 additions & 0 deletions assembly/src/parsers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ fn parse_op_token(
// ----- field operations -----------------------------------------------------------------
"assert" => field_ops::parse_assert(span_ops, op),
"assert_eq" => field_ops::parse_assert_eq(span_ops, op),
"assertz" => field_ops::parse_assertz(span_ops, op),

"add" => field_ops::parse_add(span_ops, op),
"sub" => field_ops::parse_sub(span_ops, op),
Expand Down Expand Up @@ -79,6 +80,7 @@ fn parse_op_token(
"u32overflowing_add" => u32_ops::parse_u32add(span_ops, op, U32OpMode::Overflowing),

"u32overflowing_add3" => u32_ops::parse_u32add3(span_ops, op, U32OpMode::Overflowing),
"u32wrapping_add3" => u32_ops::parse_u32wrapping_add3(span_ops, op, U32OpMode::Wrapping),

"u32checked_sub" => u32_ops::parse_u32sub(span_ops, op, U32OpMode::Checked),
"u32wrapping_sub" => u32_ops::parse_u32sub(span_ops, op, U32OpMode::Wrapping),
Expand All @@ -89,6 +91,7 @@ fn parse_op_token(
"u32overflowing_mul" => u32_ops::parse_u32mul(span_ops, op, U32OpMode::Overflowing),

"u32overflowing_madd" => u32_ops::parse_u32madd(span_ops, op, U32OpMode::Overflowing),
"u32wrapping_madd" => u32_ops::parse_u32wrapping_madd(span_ops, op, U32OpMode::Wrapping),

"u32checked_div" => u32_ops::parse_u32div(span_ops, op, U32OpMode::Checked),
"u32unchecked_div" => u32_ops::parse_u32div(span_ops, op, U32OpMode::Unchecked),
Expand Down
86 changes: 86 additions & 0 deletions assembly/src/parsers/u32_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,29 @@ pub fn parse_u32add3(
Ok(())
}

/// Translates u32wrapping_add3 assembly instruction directly to a sequence of `U32ADD3` and `Drop` operations.
///
/// This operation takes 2 VM cycles
pub fn parse_u32wrapping_add3(
span_ops: &mut Vec<Operation>,
op: &Token,
op_mode: U32OpMode,
) -> Result<(), AssemblyError> {
match op.num_parts() {
0 => return Err(AssemblyError::missing_param(op)),
1 => {
if op_mode != U32OpMode::Wrapping {
return Err(AssemblyError::invalid_op(op));
}
span_ops.push(Operation::U32add3);
span_ops.push(Operation::Drop);
}
_ => return Err(AssemblyError::extra_param(op)),
}

Ok(())
}

/// Translates u32sub assembly instructions to VM operations.
///
/// The base operation is `U32SUB`, but depending on the mode, additional operations may be
Expand Down Expand Up @@ -273,6 +296,29 @@ pub fn parse_u32madd(
Ok(())
}

/// Translates u32wrapping_madd assembly instruction directly to a sequence of `U32MADD` and `Drop` operations.
///
/// This operation takes 2 VM cycles
pub fn parse_u32wrapping_madd(
span_ops: &mut Vec<Operation>,
op: &Token,
op_mode: U32OpMode,
) -> Result<(), AssemblyError> {
match op.num_parts() {
0 => return Err(AssemblyError::missing_param(op)),
1 => {
if op_mode != U32OpMode::Wrapping {
return Err(AssemblyError::invalid_op(op));
}
span_ops.push(Operation::U32madd);
span_ops.push(Operation::Drop);
}
_ => return Err(AssemblyError::extra_param(op)),
}

Ok(())
}

/// Translates u32div assembly instructions to VM operations.
///
/// VM cycles per mode:
Expand Down Expand Up @@ -1058,3 +1104,43 @@ fn handle_division(

Ok(())
}

#[cfg(test)]
mod tests {
use vm_core::Operation;

use crate::parsers::u32_ops::U32OpMode;
use crate::{
parsers::u32_ops::{parse_u32wrapping_add3, parse_u32wrapping_madd},
tokens::Token,
AssemblyError,
};

#[test]
fn test_parse_u32wrapping_madd() {
let mut span_ops: Vec<Operation> = Vec::new();
let op_pos = 0;

let op_err = Token::new("u32wrapping_madd.1", op_pos);
let expected_err = AssemblyError::extra_param(&op_err);

assert_eq!(
parse_u32wrapping_madd(&mut span_ops, &op_err, U32OpMode::Wrapping).unwrap_err(),
expected_err
);
}

#[test]
fn test_parse_u32wrapping_add3() {
let mut span_ops: Vec<Operation> = Vec::new();
let op_pos = 0;

let op_err = Token::new("u32wrapping_add3.2", op_pos);
let expected_err = AssemblyError::extra_param(&op_err);

assert_eq!(
parse_u32wrapping_add3(&mut span_ops, &op_err, U32OpMode::Wrapping).unwrap_err(),
expected_err
);
}
}
27 changes: 27 additions & 0 deletions assembly/src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
// SIMPLE PROGRAMS
// ================================================================================================
#[test]
fn simple_new_instrctns() {
let assembler = super::Assembler::default();
let source = "begin push.0 assertz end";
let program = assembler.compile(source).unwrap();
let expected = "\
begin \
span pad eqz assert end \
end";
assert_eq!(expected, format!("{program}"));

let source = "begin push.10 push.50 push.2 u32wrapping_madd end";
let program = assembler.compile(source).unwrap();
let expected = "\
begin \
span push(10) push(50) push(2) u32madd drop end \
end";
assert_eq!(expected, format!("{program}"));

let source = "begin push.10 push.50 push.2 u32wrapping_add3 end";
let program = assembler.compile(source).unwrap();
let expected = "\
begin \
span push(10) push(50) push(2) u32add3 drop end \
end";
assert_eq!(expected, format!("{program}"));
}

#[test]
fn single_span() {
Expand Down
2 changes: 2 additions & 0 deletions docs/src/user_docs/assembly/field_operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ For instructions where one or more operands can be provided as immediate paramet
| Instruction | Stack_input | Stack_output | Notes |
| ---------------- | ----------- | ------------- | ----------------------------- |
| assert <br> - *(1 cycle)* | [a, ...] | [...] | If $a = 1$, removes it from the stack. <br> Fails if $a \ne 1$ |
| assertz <br> - *(2 cycles)* | [ a, ...] | [...] | if $a = 0$, removes it from the stack, <br> Fails if $a \ne b$ |
| assert_eq <br> - *(2 cycles)* | [b, a, ...] | [...] | If $a = b$, removes them from the stack. <br> Fails if $a \ne b$ |


### Arithmetic and Boolean operations

| Instruction | Stack_input | Stack_output | Notes |
Expand Down
3 changes: 3 additions & 0 deletions docs/src/user_docs/assembly/u32_operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ In all the table below, the number of cycles it takes for the VM to execute each
| u32cast <br> - *(2 cycles)* | [a, ...] | [b, ...] | $b \leftarrow a \mod 2^{32}$ |
| u32split <br> - *(1 cycle)* | [a, ...] | [c, b, ...] | $b \leftarrow a \mod 2^{32}$, $c \leftarrow \lfloor{a / 2^{32}}\rfloor$ |


### Arithmetic operations

| Instruction | Stack input | Stack output | Notes |
Expand All @@ -29,13 +30,15 @@ In all the table below, the number of cycles it takes for the VM to execute each
| u32overflowing_add <br> - *(1 cycle)* <br> u32overflowing_add.*b* <br> - *(2-3 cycles)* | [b, a, ...] | [d, c, ...] | $c \leftarrow (a + b) \mod 2^{32}$ <br> $d \leftarrow \begin{cases} 1, & \text{if}\ (a + b) \ge 2^{32} \\ 0, & \text{otherwise}\ \end{cases}$ <br> Undefined if $max(a, b) \ge 2^{32}$ |
| u32wrapping_add <br> - *(2 cycles)* <br> u32wrapping_add.*b* <br> - *(3-4 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow (a + b) \mod 2^{32}$ <br> Undefined if $max(a, b) \ge 2^{32}$ |
| u32overflowing_add3 <br> - *(1 cycle)* | [c, b, a, ...] | [e, d, ...] | $d \leftarrow (a + b + c) \mod 2^{32}$, <br> $e \leftarrow \lfloor (a + b + c) / 2^{32}\rfloor$ <br> Undefined if $max(a, b, c) \ge 2^{32}$ <br> |
| u32wrapping_add3 <br> - *(2 cycles)* | [c, b, a, ...] | [d, ...] | $d \leftarrow (a + b + c) \mod 2^{32}$, <br> Undefined if $max(a, b, c) \ge 2^{32}$ <br> |
| u32checked_sub <br> - *(4 cycles)* <br> u32checked_sub.*b* <br> - *(5-6 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow (a - b)$ <br> Fails if $max(a, b) \ge 2^{32}$ or $a < b$ |
| u32overflowing_sub <br> - *(1 cycle)* <br> u32overflowing_sub.*b* <br> - *(2-3 cycles)* | [b, a, ...] | [d, c, ...] | $c \leftarrow (a - b) \mod 2^{32}$ <br> $d \leftarrow \begin{cases} 1, & \text{if}\ a < b \\ 0, & \text{otherwise}\ \end{cases}$ <br> Undefined if $max(a, b) \ge 2^{32}$ |
| u32wrapping_sub <br> - *(2 cycles)* <br> u32wrapping_sub.*b* <br> - *(3-4 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow (a - b) \mod 2^{32}$ <br> Undefined if $max(a, b) \ge 2^{32}$ |
| u32checked_mul <br> - *(4 cycles)* <br> u32checked_mul.*b* <br> - *(5-6 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow a \cdot b$ <br> Fails if $max(a, b, c) \ge 2^{32}$ |
| u32overflowing_mul <br> - *(1 cycle)* <br> u32overflowing_mul.*b* <br> - *(2-3 cycles)* | [b, a, ...] | [d, c, ...] | $c \leftarrow (a \cdot b) \mod 2^{32}$ <br> $d \leftarrow \lfloor(a \cdot b) / 2^{32}\rfloor$ <br> Undefined if $max(a, b) \ge 2^{32}$ |
| u32wrapping_mul <br> - *(2 cycles)* <br> u32wrapping_mul.*b* <br> - *(3-4 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow (a \cdot b) \mod 2^{32}$ <br> Undefined if $max(a, b) \ge 2^{32}$ |
| u32overflowing_madd <br> - *(1 cycle)* | [b, a, c, ...] | [e, d, ...] | $d \leftarrow (a \cdot b + c) \mod 2^{32}$ <br> $e \leftarrow \lfloor(a \cdot b + c) / 2^{32}\rfloor$ <br> Undefined if $max(a, b, c) \ge 2^{32}$ |
| u32wrapping_madd <br> - *(2 cycles)* | [b, a, c, ...] | [d, ...] | $d \leftarrow (a \cdot b + c) \mod 2^{32}$ <br> Undefined if $max(a, b, c) \ge 2^{32}$ |
| u32checked_div <br> - *(3 cycles)* <br> u32checked_div.*b* <br> - *(4-5 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \lfloor a / b\rfloor$ <br> Fails if $max(a, b) \ge 2^{32}$ or $b = 0$ |
| u32unchecked_div <br> - *(2 cycles)* <br> u32unchecked_div.*b* <br> - *(3-4 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \lfloor a / b\rfloor$ <br> Fails if $b = 0$ <br> Undefined if $max(a, b) \ge 2^{32}$ |
| u32checked_mod <br> - *(4 cycles)* <br> u32checked_mod.*b* <br> - *(5-6 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow a \mod b$ <br> Fails if $max(a, b) \ge 2^{32}$ or $b = 0$ |
Expand Down

0 comments on commit e658813

Please sign in to comment.