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

Implement u32assert_lt instruction #1157

Closed
wants to merge 3 commits into from
Closed
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- Introduced the `procref.<proc_name>` assembly instruction (#1113).
- Added the ability to use constants as counters in `repeat` loops (#1124).
- All `checked` versions of the u32 instructions were removed. All `unchecked` versions were renamed: this mode specification was removed from their titles (#1115).
- Introduced the `u32assert_lt` assembly instruction (#1157).

#### Stdlib
- Introduced `std::utils` module with `is_empty_word` procedure. Refactored `std::collections::smt`
Expand Down
2 changes: 2 additions & 0 deletions assembly/src/assembler/instruction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ impl Assembler {
Instruction::U32AssertWWithError(err_code) => {
u32_ops::u32assertw(span, Felt::from(*err_code))
}
Instruction::U32AssertLt => u32_ops::u32assert_lt(span, None),
Instruction::U32AssertLtImm(v) => u32_ops::u32assert_lt(span, Some(*v)),

Instruction::U32Cast => span.add_ops([U32split, Drop]),
Instruction::U32Split => span.add_op(U32split),
Expand Down
27 changes: 21 additions & 6 deletions assembly/src/assembler/instruction/u32_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,22 @@ pub fn u32assertw(
span.add_ops(ops)
}

/// Translates u32assert_lt assembly instructions to VM operations.
///
/// This takes 6 VM cycles.
pub fn u32assert_lt(
span: &mut SpanBuilder,
imm: Option<u32>,
) -> Result<Option<CodeBlock>, AssemblyError> {
if let Some(imm) = imm {
span.push_ops([Dup0, Push(Felt::from(imm))]);
} else {
span.push_ops([Dup1, Dup1]);
}
u32lt(span)?;
span.add_op(Assert(ZERO))
Comment on lines +76 to +82
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would the following be more efficient:

For the case with immediate values:

Dup0 Push(imm) U32sub Eqz Assert Drop

And for the case with no immediate values:

Dup1 Dup1 U32Sub Eqz Assert Drop

Assuming I didn't make a mistake, both of these are 6 cycles.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this set will check greater than instead of less than: Eqz checks whether underflow flag is eqal zero, so the next Assert check will succeed if it equals 0, so underflow flag is zero.

I think we can leave it as it is, your u32lt optimization works great, I'm ashamed that I didn't notice it earlier, so u32assert_lt will take 6 cycles anyway.

}

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

Expand Down Expand Up @@ -384,7 +400,7 @@ fn prepare_bitwise<const MAX_VALUE: u8>(

/// Translates u32lt assembly instructions to VM operations.
///
/// This operation takes 5 cycles.
/// This operation takes 3 cycles.
pub fn u32lt(span: &mut SpanBuilder) -> Result<Option<CodeBlock>, AssemblyError> {
compute_lt(span);

Expand All @@ -393,7 +409,7 @@ pub fn u32lt(span: &mut SpanBuilder) -> Result<Option<CodeBlock>, AssemblyError>

/// Translates u32lte assembly instructions to VM operations.
///
/// This operation takes 7 cycles.
/// This operation takes 5 cycles.
pub fn u32lte(span: &mut SpanBuilder) -> Result<Option<CodeBlock>, AssemblyError> {
// Compute the lt with reversed number to get a gt check
span.push_op(Swap);
Expand All @@ -405,7 +421,7 @@ pub fn u32lte(span: &mut SpanBuilder) -> Result<Option<CodeBlock>, AssemblyError

/// Translates u32gt assembly instructions to VM operations.
///
/// This operation takes 6 cycles.
/// This operation takes 4 cycles.
pub fn u32gt(span: &mut SpanBuilder) -> Result<Option<CodeBlock>, AssemblyError> {
// Reverse the numbers so we can get a gt check.
span.push_op(Swap);
Expand All @@ -417,7 +433,7 @@ pub fn u32gt(span: &mut SpanBuilder) -> Result<Option<CodeBlock>, AssemblyError>

/// Translates u32gte assembly instructions to VM operations.
///
/// This operation takes 6 cycles.
/// This operation takes 4 cycles.
pub fn u32gte(span: &mut SpanBuilder) -> Result<Option<CodeBlock>, AssemblyError> {
compute_lt(span);

Expand Down Expand Up @@ -457,11 +473,10 @@ pub fn u32max(span: &mut SpanBuilder) -> Result<Option<CodeBlock>, AssemblyError
// ================================================================================================

/// Inserts the VM operations to check if the second element is less than
/// the top element. This takes 5 cycles.
/// the top element. This takes 3 cycles.
fn compute_lt(span: &mut SpanBuilder) {
span.push_ops([
U32sub, Swap, Drop, // Perform the operations
Eqz, Not, // Check the underflow flag
])
}

Expand Down
4 changes: 4 additions & 0 deletions assembly/src/ast/nodes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ pub enum Instruction {
U32Assert2WithError(ErrorCode),
U32AssertW,
U32AssertWWithError(ErrorCode),
U32AssertLt,
U32AssertLtImm(u32),
U32Split,
U32Cast,
U32WrappingAdd,
Expand Down Expand Up @@ -354,6 +356,8 @@ impl fmt::Display for Instruction {
Self::U32Assert2WithError(err_code) => write!(f, "u32assert2.err={err_code}"),
Self::U32AssertW => write!(f, "u32assertw"),
Self::U32AssertWWithError(err_code) => write!(f, "u32assertw.err={err_code}"),
Self::U32AssertLt => write!(f, "u32assert_lt"),
Self::U32AssertLtImm(value) => write!(f, "u32assert_lt.{value}"),
Self::U32Split => write!(f, "u32split"),
Self::U32Cast => write!(f, "u32cast"),
Self::U32WrappingAdd => write!(f, "u32wrapping_add"),
Expand Down
2 changes: 2 additions & 0 deletions assembly/src/ast/nodes/serde/deserialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ impl Deserializable for Instruction {
OpCode::U32Assert2WithError => Ok(Instruction::U32Assert2WithError(source.read_u32()?)),
OpCode::U32AssertW => Ok(Instruction::U32AssertW),
OpCode::U32AssertWWithError => Ok(Instruction::U32AssertWWithError(source.read_u32()?)),
OpCode::U32AssertLt => Ok(Instruction::U32AssertLt),
OpCode::U32AssertLtImm => Ok(Instruction::U32AssertLtImm(source.read_u32()?)),
OpCode::U32Split => Ok(Instruction::U32Split),
OpCode::U32Cast => Ok(Instruction::U32Cast),
OpCode::U32WrappingAdd => Ok(Instruction::U32WrappingAdd),
Expand Down
Loading
Loading