From d2dfb90c71f35c10287db866ac1a8c6bfa56d942 Mon Sep 17 00:00:00 2001 From: OLUWAMUYIWA Date: Tue, 6 Sep 2022 17:06:15 +0100 Subject: [PATCH 1/2] added new assembly instructions: assertz, u32wrapping_madd, u32wrapping_add3 --- assembly/src/parsers/field_ops.rs | 27 ++++++ assembly/src/parsers/mod.rs | 3 + assembly/src/parsers/u32_ops.rs | 85 +++++++++++++++++++ assembly/src/tests.rs | 27 ++++++ .../user_docs/assembly/field_operations.md | 1 + docs/src/user_docs/assembly/u32_operations.md | 3 + 6 files changed, 146 insertions(+) diff --git a/assembly/src/parsers/field_ops.rs b/assembly/src/parsers/field_ops.rs index b2ea24b016..3b4721507b 100644 --- a/assembly/src/parsers/field_ops.rs +++ b/assembly/src/parsers/field_ops.rs @@ -30,6 +30,19 @@ pub(super) fn parse_assert_eq( Ok(()) } +/// Appends EQZ ASSERT operation sequence to the span block. +pub(super) fn parse_assert_eqz( + span_ops: &mut Vec, + 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 // ================================================================================================ @@ -696,6 +709,20 @@ mod tests { ); } + #[test] + fn assertz() { + let mut span_ops: Vec = 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_assert_eqz(&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 diff --git a/assembly/src/parsers/mod.rs b/assembly/src/parsers/mod.rs index 175710dd8f..b2c2b18219 100644 --- a/assembly/src/parsers/mod.rs +++ b/assembly/src/parsers/mod.rs @@ -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_assert_eqz(span_ops, op), "add" => field_ops::parse_add(span_ops, op), "sub" => field_ops::parse_sub(span_ops, op), @@ -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), @@ -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), diff --git a/assembly/src/parsers/u32_ops.rs b/assembly/src/parsers/u32_ops.rs index 2bd20e1f95..69368726c6 100644 --- a/assembly/src/parsers/u32_ops.rs +++ b/assembly/src/parsers/u32_ops.rs @@ -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, + 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 @@ -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, + 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: @@ -1058,3 +1104,42 @@ 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 = 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 = 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 + ); + } +} diff --git a/assembly/src/tests.rs b/assembly/src/tests.rs index 3146682ec0..bf67729c19 100644 --- a/assembly/src/tests.rs +++ b/assembly/src/tests.rs @@ -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() { diff --git a/docs/src/user_docs/assembly/field_operations.md b/docs/src/user_docs/assembly/field_operations.md index 50da5a82a2..292946270a 100644 --- a/docs/src/user_docs/assembly/field_operations.md +++ b/docs/src/user_docs/assembly/field_operations.md @@ -11,6 +11,7 @@ For instructions where one or more operands can be provided as immediate paramet | ---------------- | ----------- | ------------- | ----------------------------- | | assert
- *(1 cycle)* | [a, ...] | [...] | If $a = 1$, removes it from the stack.
Fails if $a \ne 1$ | | assert_eq
- *(2 cycles)* | [b, a, ...] | [...] | If $a = b$, removes them from the stack.
Fails if $a \ne b$ | +| assertz
- *(2 cycles)* | [ a, ...] | [...] | if $a = 0$, removes it from the stack,
Fails if $a \ne b$ | ### Arithmetic and Boolean operations diff --git a/docs/src/user_docs/assembly/u32_operations.md b/docs/src/user_docs/assembly/u32_operations.md index b707ad82df..1a4e7eead6 100644 --- a/docs/src/user_docs/assembly/u32_operations.md +++ b/docs/src/user_docs/assembly/u32_operations.md @@ -21,6 +21,7 @@ In all the table below, the number of cycles it takes for the VM to execute each | u32cast
- *(2 cycles)* | [a, ...] | [b, ...] | $b \leftarrow a \mod 2^{32}$ | | u32split
- *(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 | @@ -29,6 +30,7 @@ In all the table below, the number of cycles it takes for the VM to execute each | u32overflowing_add
- *(1 cycle)*
u32overflowing_add.*b*
- *(2-3 cycles)* | [b, a, ...] | [d, c, ...] | $c \leftarrow (a + b) \mod 2^{32}$
$d \leftarrow \begin{cases} 1, & \text{if}\ (a + b) \ge 2^{32} \\ 0, & \text{otherwise}\ \end{cases}$
Undefined if $max(a, b) \ge 2^{32}$ | | u32wrapping_add
- *(2 cycles)*
u32wrapping_add.*b*
- *(3-4 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow (a + b) \mod 2^{32}$
Undefined if $max(a, b) \ge 2^{32}$ | | u32overflowing_add3
- *(1 cycle)* | [c, b, a, ...] | [e, d, ...] | $d \leftarrow (a + b + c) \mod 2^{32}$,
$e \leftarrow \lfloor (a + b + c) / 2^{32}\rfloor$
Undefined if $max(a, b, c) \ge 2^{32}$
| +| u32wrapping_add3
- *(2 cycles)* | [c, b, a, ...] | [d, ...] | $d \leftarrow (a + b + c) \mod 2^{32}$,
Undefined if $max(a, b, c) \ge 2^{32}$
| | u32checked_sub
- *(4 cycles)*
u32checked_sub.*b*
- *(5-6 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow (a - b)$
Fails if $max(a, b) \ge 2^{32}$ or $a < b$ | | u32overflowing_sub
- *(1 cycle)*
u32overflowing_sub.*b*
- *(2-3 cycles)* | [b, a, ...] | [d, c, ...] | $c \leftarrow (a - b) \mod 2^{32}$
$d \leftarrow \begin{cases} 1, & \text{if}\ a < b \\ 0, & \text{otherwise}\ \end{cases}$
Undefined if $max(a, b) \ge 2^{32}$ | | u32wrapping_sub
- *(2 cycles)*
u32wrapping_sub.*b*
- *(3-4 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow (a - b) \mod 2^{32}$
Undefined if $max(a, b) \ge 2^{32}$ | @@ -36,6 +38,7 @@ In all the table below, the number of cycles it takes for the VM to execute each | u32overflowing_mul
- *(1 cycle)*
u32overflowing_mul.*b*
- *(2-3 cycles)* | [b, a, ...] | [d, c, ...] | $c \leftarrow (a \cdot b) \mod 2^{32}$
$d \leftarrow \lfloor(a \cdot b) / 2^{32}\rfloor$
Undefined if $max(a, b) \ge 2^{32}$ | | u32wrapping_mul
- *(2 cycles)*
u32wrapping_mul.*b*
- *(3-4 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow (a \cdot b) \mod 2^{32}$
Undefined if $max(a, b) \ge 2^{32}$ | | u32overflowing_madd
- *(1 cycle)* | [b, a, c, ...] | [e, d, ...] | $d \leftarrow (a \cdot b + c) \mod 2^{32}$
$e \leftarrow \lfloor(a \cdot b + c) / 2^{32}\rfloor$
Undefined if $max(a, b, c) \ge 2^{32}$ | +| u32wrapping_madd
- *(2 cycles)* | [b, a, c, ...] | [d, ...] | $d \leftarrow (a \cdot b + c) \mod 2^{32}$
Undefined if $max(a, b, c) \ge 2^{32}$ | | u32checked_div
- *(3 cycles)*
u32checked_div.*b*
- *(4-5 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \lfloor a / b\rfloor$
Fails if $max(a, b) \ge 2^{32}$ or $b = 0$ | | u32unchecked_div
- *(2 cycles)*
u32unchecked_div.*b*
- *(3-4 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \lfloor a / b\rfloor$
Fails if $b = 0$
Undefined if $max(a, b) \ge 2^{32}$ | | u32checked_mod
- *(4 cycles)*
u32checked_mod.*b*
- *(5-6 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow a \mod b$
Fails if $max(a, b) \ge 2^{32}$ or $b = 0$ | From 1c3fd0b23b4068215e34983f0bc7c561a4106e8a Mon Sep 17 00:00:00 2001 From: OLUWAMUYIWA Date: Wed, 7 Sep 2022 10:58:12 +0100 Subject: [PATCH 2/2] fixed requested changes to the earlier new assembly instruction commit --- assembly/src/parsers/field_ops.rs | 4 ++-- assembly/src/parsers/mod.rs | 2 +- assembly/src/parsers/u32_ops.rs | 1 + docs/src/user_docs/assembly/field_operations.md | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/assembly/src/parsers/field_ops.rs b/assembly/src/parsers/field_ops.rs index 3b4721507b..9ba5a74595 100644 --- a/assembly/src/parsers/field_ops.rs +++ b/assembly/src/parsers/field_ops.rs @@ -31,7 +31,7 @@ pub(super) fn parse_assert_eq( } /// Appends EQZ ASSERT operation sequence to the span block. -pub(super) fn parse_assert_eqz( +pub(super) fn parse_assertz( span_ops: &mut Vec, op: &Token, ) -> Result<(), AssemblyError> { @@ -718,7 +718,7 @@ mod tests { let expected_err = AssemblyError::extra_param(&op_err); assert_eq!( - parse_assert_eqz(&mut span_ops, &op_err).unwrap_err(), + parse_assertz(&mut span_ops, &op_err).unwrap_err(), expected_err ); } diff --git a/assembly/src/parsers/mod.rs b/assembly/src/parsers/mod.rs index b2c2b18219..5b7a4e21ac 100644 --- a/assembly/src/parsers/mod.rs +++ b/assembly/src/parsers/mod.rs @@ -42,7 +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_assert_eqz(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), diff --git a/assembly/src/parsers/u32_ops.rs b/assembly/src/parsers/u32_ops.rs index 69368726c6..92bc51e903 100644 --- a/assembly/src/parsers/u32_ops.rs +++ b/assembly/src/parsers/u32_ops.rs @@ -1115,6 +1115,7 @@ mod tests { tokens::Token, AssemblyError, }; + #[test] fn test_parse_u32wrapping_madd() { let mut span_ops: Vec = Vec::new(); diff --git a/docs/src/user_docs/assembly/field_operations.md b/docs/src/user_docs/assembly/field_operations.md index 292946270a..c88bb89f50 100644 --- a/docs/src/user_docs/assembly/field_operations.md +++ b/docs/src/user_docs/assembly/field_operations.md @@ -10,8 +10,9 @@ For instructions where one or more operands can be provided as immediate paramet | Instruction | Stack_input | Stack_output | Notes | | ---------------- | ----------- | ------------- | ----------------------------- | | assert
- *(1 cycle)* | [a, ...] | [...] | If $a = 1$, removes it from the stack.
Fails if $a \ne 1$ | -| assert_eq
- *(2 cycles)* | [b, a, ...] | [...] | If $a = b$, removes them from the stack.
Fails if $a \ne b$ | | assertz
- *(2 cycles)* | [ a, ...] | [...] | if $a = 0$, removes it from the stack,
Fails if $a \ne b$ | +| assert_eq
- *(2 cycles)* | [b, a, ...] | [...] | If $a = b$, removes them from the stack.
Fails if $a \ne b$ | + ### Arithmetic and Boolean operations