Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New assembly instructions #387

Merged
merged 2 commits into from
Sep 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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]
OLUWAMUYIWA marked this conversation as resolved.
Show resolved Hide resolved
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$ |
OLUWAMUYIWA marked this conversation as resolved.
Show resolved Hide resolved
| 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