From 8566cc0b1c973996cdd581fb40d9280575948c33 Mon Sep 17 00:00:00 2001 From: Andrey Date: Sat, 22 Jun 2024 02:06:53 +0300 Subject: [PATCH 1/7] feat: implement immediate values for u32 binary instructions, implement binary parser for lexer --- assembly/src/assembler/instruction/mod.rs | 12 ++- assembly/src/assembler/instruction/u32_ops.rs | 27 ++++++- assembly/src/ast/instruction/deserialize.rs | 4 + assembly/src/ast/instruction/mod.rs | 4 + assembly/src/ast/instruction/opcode.rs | 4 + assembly/src/ast/instruction/print.rs | 4 + assembly/src/ast/instruction/serialize.rs | 16 ++++ assembly/src/ast/visit.rs | 8 ++ assembly/src/parser/error.rs | 7 ++ assembly/src/parser/grammar.lalrpop | 47 ++++++++++-- assembly/src/parser/lexer.rs | 75 ++++++++++++++++++- assembly/src/parser/mod.rs | 2 +- assembly/src/parser/token.rs | 22 ++++++ miden/src/examples/fibonacci.rs | 4 +- miden/src/repl/mod.rs | 2 +- miden/src/tools/mod.rs | 2 +- 16 files changed, 220 insertions(+), 20 deletions(-) diff --git a/assembly/src/assembler/instruction/mod.rs b/assembly/src/assembler/instruction/mod.rs index 71619bbecd..56a183159e 100644 --- a/assembly/src/assembler/instruction/mod.rs +++ b/assembly/src/assembler/instruction/mod.rs @@ -184,10 +184,14 @@ impl Assembler { Instruction::U32DivModImm(v) => { u32_ops::u32divmod(span_builder, ctx, Some(v.expect_spanned_value()))? } - Instruction::U32And => span_builder.push_op(U32and), - Instruction::U32Or => span_builder.push_ops([Dup1, Dup1, U32and, Neg, Add, Add]), - Instruction::U32Xor => span_builder.push_op(U32xor), - Instruction::U32Not => u32_ops::u32not(span_builder), + Instruction::U32And => u32_ops::u32and(span_builder, None), + Instruction::U32AndImm(v) => u32_ops::u32and(span_builder, Some(v.expect_value())), + Instruction::U32Or => u32_ops::u32or(span_builder, None), + Instruction::U32OrImm(v) => u32_ops::u32or(span_builder, Some(v.expect_value())), + Instruction::U32Xor => u32_ops::u32xor(span_builder, None), + Instruction::U32XorImm(v) => u32_ops::u32xor(span_builder, Some(v.expect_value())), + Instruction::U32Not => u32_ops::u32not(span_builder, None), + Instruction::U32NotImm(v) => u32_ops::u32not(span_builder, Some(v.expect_value())), Instruction::U32Shl => u32_ops::u32shl(span_builder, None)?, Instruction::U32ShlImm(v) => u32_ops::u32shl(span_builder, Some(v.expect_value()))?, Instruction::U32Shr => u32_ops::u32shr(span_builder, None)?, diff --git a/assembly/src/assembler/instruction/u32_ops.rs b/assembly/src/assembler/instruction/u32_ops.rs index e5b5575728..78420e0513 100644 --- a/assembly/src/assembler/instruction/u32_ops.rs +++ b/assembly/src/assembler/instruction/u32_ops.rs @@ -160,13 +160,38 @@ pub fn u32divmod( // BITWISE OPERATIONS // ================================================================================================ +pub fn u32and(span_builder: &mut BasicBlockBuilder, imm: Option) { + if let Some(imm) = imm { + span_builder.push_op(Push(Felt::from(imm))); + } + span_builder.push_op(U32and); +} + +pub fn u32or(span_builder: &mut BasicBlockBuilder, imm: Option) { + if let Some(imm) = imm { + span_builder.push_op(Push(Felt::from(imm))); + } + span_builder.push_ops([Dup1, Dup1, U32and, Neg, Add, Add]); +} + +pub fn u32xor(span_builder: &mut BasicBlockBuilder, imm: Option) { + if let Some(imm) = imm { + span_builder.push_op(Push(Felt::from(imm))); + } + span_builder.push_op(U32xor); +} + /// Translates u32not assembly instruction to VM operations. /// /// The reason this method works is because 2^32 -1 provides a bit mask of ones, which after /// subtracting the element, flips the bits of the original value to perform a bitwise NOT. /// /// This takes 5 VM cycles. -pub fn u32not(span_builder: &mut BasicBlockBuilder) { +pub fn u32not(span_builder: &mut BasicBlockBuilder, imm: Option) { + if let Some(imm) = imm { + span_builder.push_op(Push(Felt::from(imm))); + } + #[rustfmt::skip] let ops = [ // Perform the operation diff --git a/assembly/src/ast/instruction/deserialize.rs b/assembly/src/ast/instruction/deserialize.rs index abdbefa7a4..58a0c54f38 100644 --- a/assembly/src/ast/instruction/deserialize.rs +++ b/assembly/src/ast/instruction/deserialize.rs @@ -100,9 +100,13 @@ impl Deserializable for Instruction { OpCode::U32DivMod => Ok(Self::U32DivMod), OpCode::U32DivModImm => Ok(Self::U32DivModImm(source.read_u32()?.into())), OpCode::U32And => Ok(Self::U32And), + OpCode::U32AndImm => Ok(Self::U32AndImm(source.read_u32()?.into())), OpCode::U32Or => Ok(Self::U32Or), + OpCode::U32OrImm => Ok(Self::U32OrImm(source.read_u32()?.into())), OpCode::U32Xor => Ok(Self::U32Xor), + OpCode::U32XorImm => Ok(Self::U32XorImm(source.read_u32()?.into())), OpCode::U32Not => Ok(Self::U32Not), + OpCode::U32NotImm => Ok(Self::U32NotImm(source.read_u32()?.into())), OpCode::U32Shr => Ok(Self::U32Shr), OpCode::U32ShrImm => Ok(Self::U32ShrImm(source.read_u8()?.into())), OpCode::U32Shl => Ok(Self::U32Shl), diff --git a/assembly/src/ast/instruction/mod.rs b/assembly/src/ast/instruction/mod.rs index 9cb7fda2eb..c2b355ba21 100644 --- a/assembly/src/ast/instruction/mod.rs +++ b/assembly/src/ast/instruction/mod.rs @@ -107,9 +107,13 @@ pub enum Instruction { U32DivMod, U32DivModImm(ImmU32), U32And, + U32AndImm(ImmU32), U32Or, + U32OrImm(ImmU32), U32Xor, + U32XorImm(ImmU32), U32Not, + U32NotImm(ImmU32), U32Shr, U32ShrImm(ImmU8), U32Shl, diff --git a/assembly/src/ast/instruction/opcode.rs b/assembly/src/ast/instruction/opcode.rs index 4698997f7b..1cb6a67eca 100644 --- a/assembly/src/ast/instruction/opcode.rs +++ b/assembly/src/ast/instruction/opcode.rs @@ -98,9 +98,13 @@ pub enum OpCode { U32DivMod, U32DivModImm, U32And, + U32AndImm, U32Or, + U32OrImm, U32Xor, + U32XorImm, U32Not, + U32NotImm, U32Shr, U32ShrImm, U32Shl, diff --git a/assembly/src/ast/instruction/print.rs b/assembly/src/ast/instruction/print.rs index 88837e3706..6cd32df21d 100644 --- a/assembly/src/ast/instruction/print.rs +++ b/assembly/src/ast/instruction/print.rs @@ -111,9 +111,13 @@ impl PrettyPrint for Instruction { Self::U32DivMod => const_text("u32divmod"), Self::U32DivModImm(value) => inst_with_imm("u32divmod", value), Self::U32And => const_text("u32and"), + Self::U32AndImm(value) => inst_with_imm("u32and", value), Self::U32Or => const_text("u32or"), + Self::U32OrImm(value) => inst_with_imm("u32or", value), Self::U32Xor => const_text("u32xor"), + Self::U32XorImm(value) => inst_with_imm("u32xor", value), Self::U32Not => const_text("u32not"), + Self::U32NotImm(value) => inst_with_imm("u32not", value), Self::U32Shr => const_text("u32shr"), Self::U32ShrImm(value) => inst_with_imm("u32shr", value), Self::U32Shl => const_text("u32shl"), diff --git a/assembly/src/ast/instruction/serialize.rs b/assembly/src/ast/instruction/serialize.rs index 4f3b14adb0..39b1dbf01c 100644 --- a/assembly/src/ast/instruction/serialize.rs +++ b/assembly/src/ast/instruction/serialize.rs @@ -176,9 +176,25 @@ impl Serializable for Instruction { target.write_u32(v.expect_value()); } Self::U32And => OpCode::U32And.write_into(target), + Self::U32AndImm(v) => { + OpCode::U32AndImm.write_into(target); + target.write_u32(v.expect_value()); + } Self::U32Or => OpCode::U32Or.write_into(target), + Self::U32OrImm(v) => { + OpCode::U32OrImm.write_into(target); + target.write_u32(v.expect_value()); + } Self::U32Xor => OpCode::U32Xor.write_into(target), + Self::U32XorImm(v) => { + OpCode::U32XorImm.write_into(target); + target.write_u32(v.expect_value()); + } Self::U32Not => OpCode::U32Not.write_into(target), + Self::U32NotImm(v) => { + OpCode::U32NotImm.write_into(target); + target.write_u32(v.expect_value()); + } Self::U32Shr => OpCode::U32Shr.write_into(target), Self::U32ShrImm(v) => { OpCode::U32ShrImm.write_into(target); diff --git a/assembly/src/ast/visit.rs b/assembly/src/ast/visit.rs index 12b6245249..d52fe3fd25 100644 --- a/assembly/src/ast/visit.rs +++ b/assembly/src/ast/visit.rs @@ -314,6 +314,10 @@ where | U32GteImm(ref imm) | U32MinImm(ref imm) | U32MaxImm(ref imm) + | U32AndImm(ref imm) + | U32OrImm(ref imm) + | U32XorImm(ref imm) + | U32NotImm(ref imm) | MemLoadImm(ref imm) | MemLoadWImm(ref imm) | MemStoreImm(ref imm) @@ -763,6 +767,10 @@ where | U32GteImm(ref mut imm) | U32MinImm(ref mut imm) | U32MaxImm(ref mut imm) + | U32AndImm(ref mut imm) + | U32OrImm(ref mut imm) + | U32XorImm(ref mut imm) + | U32NotImm(ref mut imm) | MemLoadImm(ref mut imm) | MemLoadWImm(ref mut imm) | MemStoreImm(ref mut imm) diff --git a/assembly/src/parser/error.rs b/assembly/src/parser/error.rs index 9c8c3ec9fe..5dd979635b 100644 --- a/assembly/src/parser/error.rs +++ b/assembly/src/parser/error.rs @@ -162,6 +162,12 @@ pub enum ParsingError { span: SourceSpan, kind: HexErrorKind, }, + #[error("Binary value overflowed the field modulus")] + #[diagnostic()] + InvalidBinaryLiteral { + #[label] + span: SourceSpan, + }, #[error("invalid MAST root literal")] InvalidMastRoot { #[label] @@ -335,6 +341,7 @@ fn simplify_expected_tokens(expected: Vec) -> Vec { "quoted_ident" => return Some("quoted identifier".to_string()), "doc_comment" => return Some("doc comment".to_string()), "hex_value" => return Some("hex-encoded literal".to_string()), + "bin_value" => return Some("bin-encoded literal".to_string()), "uint" => return Some("integer literal".to_string()), "EOF" => return Some("end of file".to_string()), other => other[1..].strip_suffix('"').and_then(|t| Token::parse(t).ok()), diff --git a/assembly/src/parser/grammar.lalrpop b/assembly/src/parser/grammar.lalrpop index 6cd00c8b01..0537ce0114 100644 --- a/assembly/src/parser/grammar.lalrpop +++ b/assembly/src/parser/grammar.lalrpop @@ -12,7 +12,7 @@ use vm_core::{Felt, FieldElement, StarkField, crypto::hash::RpoDigest}; use crate::{LibraryPath, LibraryNamespace, ast::*, diagnostics::SourceFile, SourceSpan}; use super::{ - HexEncodedValue, Token, ParseError, ParsingError, + BinEncodedValue, HexEncodedValue, Token, ParseError, ParsingError, LiteralErrorKind, HexErrorKind, Span, Spanned, DocumentationType }; @@ -35,6 +35,7 @@ extern { const_ident => Token::ConstantIdent(<&'input str>), quoted_ident => Token::QuotedIdent(<&'input str>), hex_value => Token::HexValue(), + bin_value => Token::BinValue(), doc_comment => Token::DocComment(), comment => Token::Comment, uint => Token::Int(), @@ -480,10 +481,7 @@ Inst: Instruction = { "rcomb_base" => Instruction::RCombBase, "sdepth" => Instruction::Sdepth, "swapdw" => Instruction::SwapDw, - "u32and" => Instruction::U32And, "u32cast" => Instruction::U32Cast, - "u32not" => Instruction::U32Not, - "u32or" => Instruction::U32Or, "u32overflowing_add3" => Instruction::U32OverflowingAdd3, "u32overflowing_madd" => Instruction::U32OverflowingMadd, "u32popcnt" => Instruction::U32Popcnt, @@ -496,7 +494,6 @@ Inst: Instruction = { "u32testw" => Instruction::U32TestW, "u32wrapping_add3" => Instruction::U32WrappingAdd3, "u32wrapping_madd" => Instruction::U32WrappingMadd, - "u32xor" => Instruction::U32Xor, "xor" => Instruction::Xor, } @@ -715,6 +712,37 @@ FoldableInstWithU32Immediate: SmallOpsVec = { None => Ok(smallvec![Op::Inst(Span::new(span, Instruction::U32Mod))]), } }, + "u32and" > => { + let span = span!(l, r); + match imm { + Some(imm) if imm == 0 => smallvec![Op::Inst(Span::new(span, Instruction::Drop)), Op::Inst(Span::new(span, Instruction::PushU8(0)))], + Some(imm) => smallvec![Op::Inst(Span::new(span, Instruction::U32AndImm(imm)))], + None => smallvec![Op::Inst(Span::new(span, Instruction::U32And))], + } + }, + "u32or" > => { + let span = span!(l, r); + match imm { + Some(imm) if imm == 0 => smallvec![], + Some(imm) => smallvec![Op::Inst(Span::new(span, Instruction::U32OrImm(imm)))], + None => smallvec![Op::Inst(Span::new(span, Instruction::U32Or))], + } + }, + "u32xor" > => { + let span = span!(l, r); + match imm { + Some(imm) if imm == 0 => smallvec![], + Some(imm) => smallvec![Op::Inst(Span::new(span, Instruction::U32XorImm(imm)))], + None => smallvec![Op::Inst(Span::new(span, Instruction::U32Xor))], + } + }, + "u32not" > => { + let span = span!(l, r); + match imm { + Some(imm) => smallvec![Op::Inst(Span::new(span, Instruction::U32NotImm(imm)))], + None => smallvec![Op::Inst(Span::new(span, Instruction::U32Not))], + } + }, "u32wrapping_add" > => { let span = span!(l, r); match imm { @@ -1083,6 +1111,15 @@ U32: u32 = { HexEncodedValue::U32(v) => Ok(v), _ => Err(ParseError::User { error: ParsingError::InvalidLiteral { span: span!(l, r), kind: LiteralErrorKind::U32Overflow } }), } + }, + + =>? { + match value { + BinEncodedValue::U8(v) => Ok(v as u32), + BinEncodedValue::U16(v) => Ok(v as u32), + BinEncodedValue::U32(v) => Ok(v), + _ => Err(ParseError::User { error: ParsingError::InvalidLiteral { span: span!(l, r), kind: LiteralErrorKind::U32Overflow } }), + } } } diff --git a/assembly/src/parser/lexer.rs b/assembly/src/parser/lexer.rs index be09a785d9..a2b32bf368 100644 --- a/assembly/src/parser/lexer.rs +++ b/assembly/src/parser/lexer.rs @@ -3,8 +3,8 @@ use alloc::string::String; use core::{num::IntErrorKind, ops::Range}; use super::{ - DocumentationType, HexEncodedValue, HexErrorKind, LiteralErrorKind, ParsingError, Scanner, - SourceSpan, Token, + BinEncodedValue, DocumentationType, HexEncodedValue, HexErrorKind, LiteralErrorKind, + ParsingError, Scanner, SourceSpan, Token, }; /// The value produced by the [Lexer] when iterated @@ -293,6 +293,11 @@ impl<'input> Lexer<'input> { self.skip(); self.lex_hex() } + 'b' => { + self.skip(); + self.skip(); + self.lex_bin() + } '0'..='9' => self.lex_number(), _ => pop!(self, Token::Int(0)), }, @@ -524,6 +529,28 @@ impl<'input> Lexer<'input> { let value = parse_hex(span, self.slice_span(digit_start..end))?; Ok(Token::HexValue(value)) } + + fn lex_bin(&mut self) -> Result, ParsingError> { + // Expect the first character to be a valid binary digit + debug_assert!(is_ascii_binary(self.read())); + + loop { + // If we hit a non-binary digit, we're done + let c1 = self.read(); + if !c1.is_ascii_hexdigit() { + break; + } + self.skip(); + } + + let span = self.span(); + let start = span.start() as u32; + let digit_start = start + 2; + let end = span.end() as u32; + let span = SourceSpan::from(start..end); + let value = parse_bin(span, self.slice_span(digit_start..end))?; + Ok(Token::BinValue(value)) + } } impl<'input> Iterator for Lexer<'input> { @@ -561,7 +588,7 @@ fn parse_hex(span: SourceSpan, hex_digits: &str) -> Result { @@ -609,8 +636,35 @@ fn parse_hex(span: SourceSpan, hex_digits: &str) -> Result Result { + use vm_core::StarkField; + if bin_digits.len() <= 64 { + let value = + u64::from_str_radix(bin_digits, 2).map_err(|error| ParsingError::InvalidLiteral { + span, + kind: int_error_kind_to_literal_error_kind( + error.kind(), + LiteralErrorKind::FeltOverflow, + ), + })?; + if value > Felt::MODULUS { + return Err(ParsingError::InvalidLiteral { + span, + kind: LiteralErrorKind::FeltOverflow, + }); + } + Ok(shrink_u64_bin(value)) + } else { + Err(ParsingError::InvalidBinaryLiteral { span }) + } +} + +fn is_ascii_binary(c: char) -> bool { + matches!(c, '0'..='1') +} + #[inline] -fn shrink_u64(n: u64) -> HexEncodedValue { +fn shrink_u64_hex(n: u64) -> HexEncodedValue { if n <= (u8::MAX as u64) { HexEncodedValue::U8(n as u8) } else if n <= (u16::MAX as u64) { @@ -622,6 +676,19 @@ fn shrink_u64(n: u64) -> HexEncodedValue { } } +#[inline] +fn shrink_u64_bin(n: u64) -> BinEncodedValue { + if n <= (u8::MAX as u64) { + BinEncodedValue::U8(n as u8) + } else if n <= (u16::MAX as u64) { + BinEncodedValue::U16(n as u16) + } else if n <= (u32::MAX as u64) { + BinEncodedValue::U32(n as u32) + } else { + BinEncodedValue::Felt(Felt::new(n)) + } +} + #[inline] fn int_error_kind_to_literal_error_kind( kind: &IntErrorKind, diff --git a/assembly/src/parser/mod.rs b/assembly/src/parser/mod.rs index e939c2fcab..e4dedd9ac7 100644 --- a/assembly/src/parser/mod.rs +++ b/assembly/src/parser/mod.rs @@ -26,7 +26,7 @@ pub use self::lexer::Lexer; pub use self::location::SourceLocation; pub use self::scanner::Scanner; pub use self::span::{SourceSpan, Span, Spanned}; -pub use self::token::{DocumentationType, HexEncodedValue, Token}; +pub use self::token::{BinEncodedValue, DocumentationType, HexEncodedValue, Token}; use crate::{ ast, diff --git a/assembly/src/parser/token.rs b/assembly/src/parser/token.rs index 49f990cf3d..675ad48b1b 100644 --- a/assembly/src/parser/token.rs +++ b/assembly/src/parser/token.rs @@ -51,6 +51,25 @@ pub enum HexEncodedValue { Word([Felt; 4]), } +// BINARY ENCODED VALUE +// ================================================================================================ + +/// Represents one of the various types of values that have a hex-encoded representation in Miden +/// Assembly source files. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum BinEncodedValue { + /// A tiny value + U8(u8), + /// A small value + U16(u16), + /// A u32 constant, typically represents a memory address + U32(u32), + /// A single field element, 8 bytes, encoded as 64 binary digits + Felt(Felt), + // /// A set of 4 field elements, 32 bytes, encoded as a contiguous string of 256 binary digits + // Word([Felt; 4]), +} + // TOKEN // ================================================================================================ @@ -224,6 +243,7 @@ pub enum Token<'input> { Rstab, DocComment(DocumentationType), HexValue(HexEncodedValue), + BinValue(BinEncodedValue), Int(u64), Ident(&'input str), ConstantIdent(&'input str), @@ -403,6 +423,7 @@ impl<'input> fmt::Display for Token<'input> { Token::DocComment(DocumentationType::Module(_)) => f.write_str("module doc"), Token::DocComment(DocumentationType::Form(_)) => f.write_str("doc comment"), Token::HexValue(_) => f.write_str("hex-encoded value"), + Token::BinValue(_) => f.write_str("bin-encoded value"), Token::Int(_) => f.write_str("integer"), Token::Ident(_) => f.write_str("identifier"), Token::ConstantIdent(_) => f.write_str("constant identifier"), @@ -804,6 +825,7 @@ impl<'input> Token<'input> { "doc comment" => Ok(Token::DocComment(DocumentationType::Form(String::new()))), "comment" => Ok(Token::Comment), "hex-encoded value" => Ok(Token::HexValue(HexEncodedValue::U8(0))), + "bin-encoded value" => Ok(Token::BinValue(BinEncodedValue::U8(0))), "integer" => Ok(Token::Int(0)), "identifier" => Ok(Token::Ident("")), "constant identifier" => Ok(Token::ConstantIdent("")), diff --git a/miden/src/examples/fibonacci.rs b/miden/src/examples/fibonacci.rs index 4629f960c2..306e6ebe68 100644 --- a/miden/src/examples/fibonacci.rs +++ b/miden/src/examples/fibonacci.rs @@ -1,7 +1,5 @@ use super::{Example, ONE, ZERO}; -use miden_vm::{ - math::Felt, Assembler, DefaultHost, MemAdviceProvider, Program, ProvingOptions, StackInputs, -}; +use miden_vm::{math::Felt, Assembler, DefaultHost, MemAdviceProvider, Program, StackInputs}; // EXAMPLE BUILDER // ================================================================================================ diff --git a/miden/src/repl/mod.rs b/miden/src/repl/mod.rs index aea32e62ea..074b6f7e25 100644 --- a/miden/src/repl/mod.rs +++ b/miden/src/repl/mod.rs @@ -1,5 +1,5 @@ use assembly::{Assembler, Library, MaslLibrary}; -use miden_vm::{math::Felt, DefaultHost, Program, StackInputs, Word}; +use miden_vm::{math::Felt, DefaultHost, StackInputs, Word}; use processor::ContextId; use rustyline::{error::ReadlineError, DefaultEditor}; use std::{collections::BTreeSet, path::PathBuf}; diff --git a/miden/src/tools/mod.rs b/miden/src/tools/mod.rs index fce3d34dc8..60e299d00d 100644 --- a/miden/src/tools/mod.rs +++ b/miden/src/tools/mod.rs @@ -2,7 +2,7 @@ use super::cli::InputFile; use assembly::diagnostics::{IntoDiagnostic, Report, WrapErr}; use clap::Parser; use core::fmt; -use miden_vm::{Assembler, DefaultHost, Host, Operation, Program, StackInputs}; +use miden_vm::{Assembler, DefaultHost, Host, Operation, StackInputs}; use processor::{AsmOpInfo, TraceLenSummary}; use std::{fs, path::PathBuf}; use stdlib::StdLibrary; From c0ce2a2ff8013197ebbb2d4eb6171742ddd101f4 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 25 Jun 2024 23:27:26 +0300 Subject: [PATCH 2/7] test: add for u32 binary imms --- .../operations/u32_ops/bitwise_ops.rs | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/miden/tests/integration/operations/u32_ops/bitwise_ops.rs b/miden/tests/integration/operations/u32_ops/bitwise_ops.rs index 2338e176a6..0b20bfba13 100644 --- a/miden/tests/integration/operations/u32_ops/bitwise_ops.rs +++ b/miden/tests/integration/operations/u32_ops/bitwise_ops.rs @@ -40,6 +40,38 @@ fn u32and() { test.expect_stack(&[(a & b) as u64, d as u64, c as u64]); } +#[test] +fn u32and_b() { + let build_asm_op = |param: u32| format!("u32and.{param}"); + + // --- simple cases --------------------------------------------------------------------------- + let test = build_op_test!(build_asm_op(1), &[1]); + test.expect_stack(&[1]); + + let test = build_op_test!(build_asm_op(1), &[0]); + test.expect_stack(&[0]); + + let test = build_op_test!(build_asm_op(0), &[1]); + test.expect_stack(&[0]); + + let test = build_op_test!(build_asm_op(0), &[0]); + test.expect_stack(&[0]); + + // --- random u32 values ---------------------------------------------------------------------- + let a = rand_value::(); + let b = rand_value::(); + + let test = build_op_test!(build_asm_op(b), &[a as u64]); + test.expect_stack(&[(a & b) as u64]); + + // --- test that the rest of the stack isn't affected ----------------------------------------- + let c = rand_value::(); + let d = rand_value::(); + + let test = build_op_test!(build_asm_op(b), &[c as u64, d as u64, a as u64]); + test.expect_stack(&[(a & b) as u64, d as u64, c as u64]); +} + #[test] fn u32and_fail() { let asm_op = "u32and"; @@ -83,6 +115,38 @@ fn u32or() { test.expect_stack(&[(a | b) as u64, d as u64, c as u64]); } +#[test] +fn u32or_b() { + let build_asm_op = |param: u32| format!("u32or.{param}"); + + // --- simple cases --------------------------------------------------------------------------- + let test = build_op_test!(build_asm_op(1), &[1]); + test.expect_stack(&[1]); + + let test = build_op_test!(build_asm_op(1), &[0]); + test.expect_stack(&[1]); + + let test = build_op_test!(build_asm_op(0), &[1]); + test.expect_stack(&[1]); + + let test = build_op_test!(build_asm_op(0), &[0]); + test.expect_stack(&[0]); + + // --- random u32 values ---------------------------------------------------------------------- + let a = rand_value::(); + let b = rand_value::(); + + let test = build_op_test!(build_asm_op(b), &[a as u64]); + test.expect_stack(&[(a | b) as u64]); + + // --- test that the rest of the stack isn't affected ----------------------------------------- + let c = rand_value::(); + let d = rand_value::(); + + let test = build_op_test!(build_asm_op(b), &[c as u64, d as u64, a as u64]); + test.expect_stack(&[(a | b) as u64, d as u64, c as u64]); +} + #[test] fn u32or_fail() { let asm_op = "u32or"; @@ -125,6 +189,38 @@ fn u32xor() { test.expect_stack(&[(a ^ b) as u64, d as u64, c as u64]); } +#[test] +fn u32xor_b() { + let build_asm_op = |param: u32| format!("u32xor.{param}"); + + // --- simple cases --------------------------------------------------------------------------- + let test = build_op_test!(build_asm_op(1), &[1]); + test.expect_stack(&[0]); + + let test = build_op_test!(build_asm_op(1), &[0]); + test.expect_stack(&[1]); + + let test = build_op_test!(build_asm_op(0), &[1]); + test.expect_stack(&[1]); + + let test = build_op_test!(build_asm_op(0), &[0]); + test.expect_stack(&[0]); + + // --- random u32 values ---------------------------------------------------------------------- + let a = rand_value::(); + let b = rand_value::(); + + let test = build_op_test!(build_asm_op(b), &[a as u64]); + test.expect_stack(&[(a ^ b) as u64]); + + // --- test that the rest of the stack isn't affected ----------------------------------------- + let c = rand_value::(); + let d = rand_value::(); + + let test = build_op_test!(build_asm_op(b), &[c as u64, d as u64, a as u64]); + test.expect_stack(&[(a ^ b) as u64, d as u64, c as u64]); +} + #[test] fn u32xor_fail() { let asm_op = "u32xor"; @@ -160,6 +256,30 @@ fn u32not() { test.expect_stack(&[!a as u64, b as u64]); } +#[test] +fn u32not_b() { + let build_asm_op = |param: u64| format!("u32not.{param}"); + + // --- simple cases --------------------------------------------------------------------------- + let test = build_op_test!(build_asm_op(U32_BOUND - 1), &[]); + test.expect_stack(&[0]); + + let test = build_op_test!(build_asm_op(0), &[]); + test.expect_stack(&[U32_BOUND - 1]); + + // --- random u32 values ---------------------------------------------------------------------- + let a = rand_value::(); + + let test = build_op_test!(build_asm_op(a as u64), &[]); + test.expect_stack(&[(!a) as u64]); + + // --- test that the rest of the stack isn't affected ----------------------------------------- + let b = rand_value::(); + + let test = build_op_test!(build_asm_op(a as u64), &[b as u64]); + test.expect_stack(&[!a as u64, b as u64]); +} + #[test] fn u32not_fail() { let asm_op = "u32not"; From 32dac16e79793e0c054baa077c61f48bab640f4b Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 25 Jun 2024 23:55:19 +0300 Subject: [PATCH 3/7] chore: fix cargo check --- miden/src/examples/fibonacci.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/miden/src/examples/fibonacci.rs b/miden/src/examples/fibonacci.rs index 306e6ebe68..76c827f710 100644 --- a/miden/src/examples/fibonacci.rs +++ b/miden/src/examples/fibonacci.rs @@ -71,6 +71,8 @@ fn test_fib_example_fail() { #[test] fn test_fib_example_rpo() { + use miden_vm::ProvingOptions; + let example = get_example(16); super::test_example_with_options(example, false, ProvingOptions::with_96_bit_security(true)); } From 9d3a1c0d292b9804f3b86112baeb944db8820331 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 26 Jun 2024 16:42:17 +0300 Subject: [PATCH 4/7] refactor: emulate immediate value on the parsing stage --- assembly/src/assembler/instruction/mod.rs | 12 +++----- assembly/src/assembler/instruction/u32_ops.rs | 27 +----------------- assembly/src/ast/instruction/deserialize.rs | 4 --- assembly/src/ast/instruction/mod.rs | 4 --- assembly/src/ast/instruction/opcode.rs | 4 --- assembly/src/ast/instruction/print.rs | 4 --- assembly/src/ast/instruction/serialize.rs | 16 ----------- assembly/src/ast/visit.rs | 8 ------ assembly/src/parser/grammar.lalrpop | 28 ++++++++++++++++--- 9 files changed, 29 insertions(+), 78 deletions(-) diff --git a/assembly/src/assembler/instruction/mod.rs b/assembly/src/assembler/instruction/mod.rs index 56a183159e..71619bbecd 100644 --- a/assembly/src/assembler/instruction/mod.rs +++ b/assembly/src/assembler/instruction/mod.rs @@ -184,14 +184,10 @@ impl Assembler { Instruction::U32DivModImm(v) => { u32_ops::u32divmod(span_builder, ctx, Some(v.expect_spanned_value()))? } - Instruction::U32And => u32_ops::u32and(span_builder, None), - Instruction::U32AndImm(v) => u32_ops::u32and(span_builder, Some(v.expect_value())), - Instruction::U32Or => u32_ops::u32or(span_builder, None), - Instruction::U32OrImm(v) => u32_ops::u32or(span_builder, Some(v.expect_value())), - Instruction::U32Xor => u32_ops::u32xor(span_builder, None), - Instruction::U32XorImm(v) => u32_ops::u32xor(span_builder, Some(v.expect_value())), - Instruction::U32Not => u32_ops::u32not(span_builder, None), - Instruction::U32NotImm(v) => u32_ops::u32not(span_builder, Some(v.expect_value())), + Instruction::U32And => span_builder.push_op(U32and), + Instruction::U32Or => span_builder.push_ops([Dup1, Dup1, U32and, Neg, Add, Add]), + Instruction::U32Xor => span_builder.push_op(U32xor), + Instruction::U32Not => u32_ops::u32not(span_builder), Instruction::U32Shl => u32_ops::u32shl(span_builder, None)?, Instruction::U32ShlImm(v) => u32_ops::u32shl(span_builder, Some(v.expect_value()))?, Instruction::U32Shr => u32_ops::u32shr(span_builder, None)?, diff --git a/assembly/src/assembler/instruction/u32_ops.rs b/assembly/src/assembler/instruction/u32_ops.rs index 78420e0513..e5b5575728 100644 --- a/assembly/src/assembler/instruction/u32_ops.rs +++ b/assembly/src/assembler/instruction/u32_ops.rs @@ -160,38 +160,13 @@ pub fn u32divmod( // BITWISE OPERATIONS // ================================================================================================ -pub fn u32and(span_builder: &mut BasicBlockBuilder, imm: Option) { - if let Some(imm) = imm { - span_builder.push_op(Push(Felt::from(imm))); - } - span_builder.push_op(U32and); -} - -pub fn u32or(span_builder: &mut BasicBlockBuilder, imm: Option) { - if let Some(imm) = imm { - span_builder.push_op(Push(Felt::from(imm))); - } - span_builder.push_ops([Dup1, Dup1, U32and, Neg, Add, Add]); -} - -pub fn u32xor(span_builder: &mut BasicBlockBuilder, imm: Option) { - if let Some(imm) = imm { - span_builder.push_op(Push(Felt::from(imm))); - } - span_builder.push_op(U32xor); -} - /// Translates u32not assembly instruction to VM operations. /// /// The reason this method works is because 2^32 -1 provides a bit mask of ones, which after /// subtracting the element, flips the bits of the original value to perform a bitwise NOT. /// /// This takes 5 VM cycles. -pub fn u32not(span_builder: &mut BasicBlockBuilder, imm: Option) { - if let Some(imm) = imm { - span_builder.push_op(Push(Felt::from(imm))); - } - +pub fn u32not(span_builder: &mut BasicBlockBuilder) { #[rustfmt::skip] let ops = [ // Perform the operation diff --git a/assembly/src/ast/instruction/deserialize.rs b/assembly/src/ast/instruction/deserialize.rs index 58a0c54f38..abdbefa7a4 100644 --- a/assembly/src/ast/instruction/deserialize.rs +++ b/assembly/src/ast/instruction/deserialize.rs @@ -100,13 +100,9 @@ impl Deserializable for Instruction { OpCode::U32DivMod => Ok(Self::U32DivMod), OpCode::U32DivModImm => Ok(Self::U32DivModImm(source.read_u32()?.into())), OpCode::U32And => Ok(Self::U32And), - OpCode::U32AndImm => Ok(Self::U32AndImm(source.read_u32()?.into())), OpCode::U32Or => Ok(Self::U32Or), - OpCode::U32OrImm => Ok(Self::U32OrImm(source.read_u32()?.into())), OpCode::U32Xor => Ok(Self::U32Xor), - OpCode::U32XorImm => Ok(Self::U32XorImm(source.read_u32()?.into())), OpCode::U32Not => Ok(Self::U32Not), - OpCode::U32NotImm => Ok(Self::U32NotImm(source.read_u32()?.into())), OpCode::U32Shr => Ok(Self::U32Shr), OpCode::U32ShrImm => Ok(Self::U32ShrImm(source.read_u8()?.into())), OpCode::U32Shl => Ok(Self::U32Shl), diff --git a/assembly/src/ast/instruction/mod.rs b/assembly/src/ast/instruction/mod.rs index c2b355ba21..9cb7fda2eb 100644 --- a/assembly/src/ast/instruction/mod.rs +++ b/assembly/src/ast/instruction/mod.rs @@ -107,13 +107,9 @@ pub enum Instruction { U32DivMod, U32DivModImm(ImmU32), U32And, - U32AndImm(ImmU32), U32Or, - U32OrImm(ImmU32), U32Xor, - U32XorImm(ImmU32), U32Not, - U32NotImm(ImmU32), U32Shr, U32ShrImm(ImmU8), U32Shl, diff --git a/assembly/src/ast/instruction/opcode.rs b/assembly/src/ast/instruction/opcode.rs index 1cb6a67eca..4698997f7b 100644 --- a/assembly/src/ast/instruction/opcode.rs +++ b/assembly/src/ast/instruction/opcode.rs @@ -98,13 +98,9 @@ pub enum OpCode { U32DivMod, U32DivModImm, U32And, - U32AndImm, U32Or, - U32OrImm, U32Xor, - U32XorImm, U32Not, - U32NotImm, U32Shr, U32ShrImm, U32Shl, diff --git a/assembly/src/ast/instruction/print.rs b/assembly/src/ast/instruction/print.rs index 6cd32df21d..88837e3706 100644 --- a/assembly/src/ast/instruction/print.rs +++ b/assembly/src/ast/instruction/print.rs @@ -111,13 +111,9 @@ impl PrettyPrint for Instruction { Self::U32DivMod => const_text("u32divmod"), Self::U32DivModImm(value) => inst_with_imm("u32divmod", value), Self::U32And => const_text("u32and"), - Self::U32AndImm(value) => inst_with_imm("u32and", value), Self::U32Or => const_text("u32or"), - Self::U32OrImm(value) => inst_with_imm("u32or", value), Self::U32Xor => const_text("u32xor"), - Self::U32XorImm(value) => inst_with_imm("u32xor", value), Self::U32Not => const_text("u32not"), - Self::U32NotImm(value) => inst_with_imm("u32not", value), Self::U32Shr => const_text("u32shr"), Self::U32ShrImm(value) => inst_with_imm("u32shr", value), Self::U32Shl => const_text("u32shl"), diff --git a/assembly/src/ast/instruction/serialize.rs b/assembly/src/ast/instruction/serialize.rs index 39b1dbf01c..4f3b14adb0 100644 --- a/assembly/src/ast/instruction/serialize.rs +++ b/assembly/src/ast/instruction/serialize.rs @@ -176,25 +176,9 @@ impl Serializable for Instruction { target.write_u32(v.expect_value()); } Self::U32And => OpCode::U32And.write_into(target), - Self::U32AndImm(v) => { - OpCode::U32AndImm.write_into(target); - target.write_u32(v.expect_value()); - } Self::U32Or => OpCode::U32Or.write_into(target), - Self::U32OrImm(v) => { - OpCode::U32OrImm.write_into(target); - target.write_u32(v.expect_value()); - } Self::U32Xor => OpCode::U32Xor.write_into(target), - Self::U32XorImm(v) => { - OpCode::U32XorImm.write_into(target); - target.write_u32(v.expect_value()); - } Self::U32Not => OpCode::U32Not.write_into(target), - Self::U32NotImm(v) => { - OpCode::U32NotImm.write_into(target); - target.write_u32(v.expect_value()); - } Self::U32Shr => OpCode::U32Shr.write_into(target), Self::U32ShrImm(v) => { OpCode::U32ShrImm.write_into(target); diff --git a/assembly/src/ast/visit.rs b/assembly/src/ast/visit.rs index d52fe3fd25..12b6245249 100644 --- a/assembly/src/ast/visit.rs +++ b/assembly/src/ast/visit.rs @@ -314,10 +314,6 @@ where | U32GteImm(ref imm) | U32MinImm(ref imm) | U32MaxImm(ref imm) - | U32AndImm(ref imm) - | U32OrImm(ref imm) - | U32XorImm(ref imm) - | U32NotImm(ref imm) | MemLoadImm(ref imm) | MemLoadWImm(ref imm) | MemStoreImm(ref imm) @@ -767,10 +763,6 @@ where | U32GteImm(ref mut imm) | U32MinImm(ref mut imm) | U32MaxImm(ref mut imm) - | U32AndImm(ref mut imm) - | U32OrImm(ref mut imm) - | U32XorImm(ref mut imm) - | U32NotImm(ref mut imm) | MemLoadImm(ref mut imm) | MemLoadWImm(ref mut imm) | MemStoreImm(ref mut imm) diff --git a/assembly/src/parser/grammar.lalrpop b/assembly/src/parser/grammar.lalrpop index 0537ce0114..f04bb37098 100644 --- a/assembly/src/parser/grammar.lalrpop +++ b/assembly/src/parser/grammar.lalrpop @@ -716,7 +716,12 @@ FoldableInstWithU32Immediate: SmallOpsVec = { let span = span!(l, r); match imm { Some(imm) if imm == 0 => smallvec![Op::Inst(Span::new(span, Instruction::Drop)), Op::Inst(Span::new(span, Instruction::PushU8(0)))], - Some(imm) => smallvec![Op::Inst(Span::new(span, Instruction::U32AndImm(imm)))], + Some(imm) => { + match imm { + Immediate::Constant(name) => smallvec![Op::Inst(Span::new(span, Instruction::Push(Immediate::Constant(name)))), Op::Inst(Span::new(span, Instruction::U32And))], + Immediate::Value(value) => smallvec![Op::Inst(Span::new(span, Instruction::PushU32(value.into_inner()))), Op::Inst(Span::new(span, Instruction::U32And))], + } + } None => smallvec![Op::Inst(Span::new(span, Instruction::U32And))], } }, @@ -724,7 +729,12 @@ FoldableInstWithU32Immediate: SmallOpsVec = { let span = span!(l, r); match imm { Some(imm) if imm == 0 => smallvec![], - Some(imm) => smallvec![Op::Inst(Span::new(span, Instruction::U32OrImm(imm)))], + Some(imm) => { + match imm { + Immediate::Constant(name) => smallvec![Op::Inst(Span::new(span, Instruction::Push(Immediate::Constant(name)))), Op::Inst(Span::new(span, Instruction::U32Or))], + Immediate::Value(value) => smallvec![Op::Inst(Span::new(span, Instruction::PushU32(value.into_inner()))), Op::Inst(Span::new(span, Instruction::U32Or))], + } + } None => smallvec![Op::Inst(Span::new(span, Instruction::U32Or))], } }, @@ -732,14 +742,24 @@ FoldableInstWithU32Immediate: SmallOpsVec = { let span = span!(l, r); match imm { Some(imm) if imm == 0 => smallvec![], - Some(imm) => smallvec![Op::Inst(Span::new(span, Instruction::U32XorImm(imm)))], + Some(imm) => { + match imm { + Immediate::Constant(name) => smallvec![Op::Inst(Span::new(span, Instruction::Push(Immediate::Constant(name)))), Op::Inst(Span::new(span, Instruction::U32Xor))], + Immediate::Value(value) => smallvec![Op::Inst(Span::new(span, Instruction::PushU32(value.into_inner()))), Op::Inst(Span::new(span, Instruction::U32Xor))], + } + } None => smallvec![Op::Inst(Span::new(span, Instruction::U32Xor))], } }, "u32not" > => { let span = span!(l, r); match imm { - Some(imm) => smallvec![Op::Inst(Span::new(span, Instruction::U32NotImm(imm)))], + Some(imm) => { + match imm { + Immediate::Constant(name) => smallvec![Op::Inst(Span::new(span, Instruction::Push(Immediate::Constant(name)))), Op::Inst(Span::new(span, Instruction::U32Not))], + Immediate::Value(value) => smallvec![Op::Inst(Span::new(span, Instruction::PushU32(value.into_inner()))), Op::Inst(Span::new(span, Instruction::U32Not))], + } + } None => smallvec![Op::Inst(Span::new(span, Instruction::U32Not))], } }, From 392de7895a06bfbb0d65c9c1a3d223346b93a22a Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 2 Jul 2024 16:41:47 +0300 Subject: [PATCH 5/7] refactor: remove binary encoding for Felt, update CHANGELOG and docs --- CHANGELOG.md | 1 + assembly/src/parser/error.rs | 22 ++++++++++++- assembly/src/parser/grammar.lalrpop | 1 - assembly/src/parser/lexer.rs | 32 ++++++++----------- assembly/src/parser/mod.rs | 2 +- assembly/src/parser/token.rs | 4 --- docs/src/user_docs/assembly/u32_operations.md | 8 ++--- 7 files changed, 40 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbdab46c18..23c149e88c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Added support for the `nop` instruction, which corresponds to the VM opcode of the same name, and has the same semantics. This is implemented for use by compilers primarily. - Added support for the `if.false` instruction, which can be used in the same manner as `if.true` - Relaxed the parser to allow one branch of an `if.(true|false)` to be empty +- Added support for immediate values for `u32and`, `u32or`, `u32xor` and `u32not` bitwise instructions (#1362). #### Changed diff --git a/assembly/src/parser/error.rs b/assembly/src/parser/error.rs index 5dd979635b..e4a57858d3 100644 --- a/assembly/src/parser/error.rs +++ b/assembly/src/parser/error.rs @@ -69,6 +69,25 @@ impl fmt::Display for HexErrorKind { } } +// BINARY ERROR KIND +// ================================================================================================ + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum BinErrorKind { + /// Occurs when the bin-encoded value is > 32 digits + TooLong, +} + +impl fmt::Display for BinErrorKind { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::TooLong => f.write_str( + "value has too many digits, binary string can contain no more than 32 digits", + ), + } + } +} + // PARSING ERROR // ================================================================================================ @@ -162,11 +181,12 @@ pub enum ParsingError { span: SourceSpan, kind: HexErrorKind, }, - #[error("Binary value overflowed the field modulus")] + #[error("invalid literal: {}", kind)] #[diagnostic()] InvalidBinaryLiteral { #[label] span: SourceSpan, + kind: BinErrorKind, }, #[error("invalid MAST root literal")] InvalidMastRoot { diff --git a/assembly/src/parser/grammar.lalrpop b/assembly/src/parser/grammar.lalrpop index f04bb37098..4f937cc4ef 100644 --- a/assembly/src/parser/grammar.lalrpop +++ b/assembly/src/parser/grammar.lalrpop @@ -1138,7 +1138,6 @@ U32: u32 = { BinEncodedValue::U8(v) => Ok(v as u32), BinEncodedValue::U16(v) => Ok(v as u32), BinEncodedValue::U32(v) => Ok(v), - _ => Err(ParseError::User { error: ParsingError::InvalidLiteral { span: span!(l, r), kind: LiteralErrorKind::U32Overflow } }), } } } diff --git a/assembly/src/parser/lexer.rs b/assembly/src/parser/lexer.rs index a2b32bf368..9deec6355d 100644 --- a/assembly/src/parser/lexer.rs +++ b/assembly/src/parser/lexer.rs @@ -3,8 +3,8 @@ use alloc::string::String; use core::{num::IntErrorKind, ops::Range}; use super::{ - BinEncodedValue, DocumentationType, HexEncodedValue, HexErrorKind, LiteralErrorKind, - ParsingError, Scanner, SourceSpan, Token, + BinEncodedValue, BinErrorKind, DocumentationType, HexEncodedValue, HexErrorKind, + LiteralErrorKind, ParsingError, Scanner, SourceSpan, Token, }; /// The value produced by the [Lexer] when iterated @@ -637,25 +637,21 @@ fn parse_hex(span: SourceSpan, hex_digits: &str) -> Result Result { - use vm_core::StarkField; - if bin_digits.len() <= 64 { + if bin_digits.len() <= 32 { let value = - u64::from_str_radix(bin_digits, 2).map_err(|error| ParsingError::InvalidLiteral { + u32::from_str_radix(bin_digits, 2).map_err(|error| ParsingError::InvalidLiteral { span, kind: int_error_kind_to_literal_error_kind( error.kind(), LiteralErrorKind::FeltOverflow, ), })?; - if value > Felt::MODULUS { - return Err(ParsingError::InvalidLiteral { - span, - kind: LiteralErrorKind::FeltOverflow, - }); - } - Ok(shrink_u64_bin(value)) + Ok(shrink_u32_bin(value)) } else { - Err(ParsingError::InvalidBinaryLiteral { span }) + Err(ParsingError::InvalidBinaryLiteral { + span, + kind: BinErrorKind::TooLong, + }) } } @@ -677,15 +673,13 @@ fn shrink_u64_hex(n: u64) -> HexEncodedValue { } #[inline] -fn shrink_u64_bin(n: u64) -> BinEncodedValue { - if n <= (u8::MAX as u64) { +fn shrink_u32_bin(n: u32) -> BinEncodedValue { + if n <= (u8::MAX as u32) { BinEncodedValue::U8(n as u8) - } else if n <= (u16::MAX as u64) { + } else if n <= (u16::MAX as u32) { BinEncodedValue::U16(n as u16) - } else if n <= (u32::MAX as u64) { - BinEncodedValue::U32(n as u32) } else { - BinEncodedValue::Felt(Felt::new(n)) + BinEncodedValue::U32(n) } } diff --git a/assembly/src/parser/mod.rs b/assembly/src/parser/mod.rs index e4dedd9ac7..761a759392 100644 --- a/assembly/src/parser/mod.rs +++ b/assembly/src/parser/mod.rs @@ -21,7 +21,7 @@ mod scanner; mod span; mod token; -pub use self::error::{HexErrorKind, LiteralErrorKind, ParsingError}; +pub use self::error::{BinErrorKind, HexErrorKind, LiteralErrorKind, ParsingError}; pub use self::lexer::Lexer; pub use self::location::SourceLocation; pub use self::scanner::Scanner; diff --git a/assembly/src/parser/token.rs b/assembly/src/parser/token.rs index 675ad48b1b..ba235f1542 100644 --- a/assembly/src/parser/token.rs +++ b/assembly/src/parser/token.rs @@ -64,10 +64,6 @@ pub enum BinEncodedValue { U16(u16), /// A u32 constant, typically represents a memory address U32(u32), - /// A single field element, 8 bytes, encoded as 64 binary digits - Felt(Felt), - // /// A set of 4 field elements, 32 bytes, encoded as a contiguous string of 256 binary digits - // Word([Felt; 4]), } // TOKEN diff --git a/docs/src/user_docs/assembly/u32_operations.md b/docs/src/user_docs/assembly/u32_operations.md index 7d88ab27b6..ea07055a60 100644 --- a/docs/src/user_docs/assembly/u32_operations.md +++ b/docs/src/user_docs/assembly/u32_operations.md @@ -46,10 +46,10 @@ If the error code is omitted, the default value of $0$ is assumed. | Instruction | Stack input | Stack output | Notes | | ------------------------------------------------------------------------------------- | -------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------ | -| u32and
- *(1 cycle)* | [b, a, ...] | [c, ...] | Computes $c$ as a bitwise `AND` of binary representations of $a$ and $b$.
Fails if $max(a,b) \ge 2^{32}$ | -| u32or
- *(6 cycle)s* | [b, a, ...] | [c, ...] | Computes $c$ as a bitwise `OR` of binary representations of $a$ and $b$.
Fails if $max(a,b) \ge 2^{32}$ | -| u32xor
- *(1 cycle)* | [b, a, ...] | [c, ...] | Computes $c$ as a bitwise `XOR` of binary representations of $a$ and $b$.
Fails if $max(a,b) \ge 2^{32}$ | -| u32not
- *(5 cycles)* | [a, ...] | [b, ...] | Computes $b$ as a bitwise `NOT` of binary representation of $a$.
Fails if $a \ge 2^{32}$ | +| u32and
- *(1 cycle)*
u32and.*b*
- *(2 cycles)* | [b, a, ...] | [c, ...] | Computes $c$ as a bitwise `AND` of binary representations of $a$ and $b$.
Fails if $max(a,b) \ge 2^{32}$ | +| u32or
- *(6 cycle)s*
u32or.*b*
- *(7 cycles)* | [b, a, ...] | [c, ...] | Computes $c$ as a bitwise `OR` of binary representations of $a$ and $b$.
Fails if $max(a,b) \ge 2^{32}$ | +| u32xor
- *(1 cycle)*
u32xor.*b*
- *(2 cycles)* | [b, a, ...] | [c, ...] | Computes $c$ as a bitwise `XOR` of binary representations of $a$ and $b$.
Fails if $max(a,b) \ge 2^{32}$ | +| u32not
- *(5 cycles)*
u32not.*a*
- *(6 cycles)* | [a, ...] | [b, ...] | Computes $b$ as a bitwise `NOT` of binary representation of $a$.
Fails if $a \ge 2^{32}$ | | u32shl
- *(18 cycles)*
u32shl.*b*
- *(3 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow (a \cdot 2^b) \mod 2^{32}$
Undefined if $a \ge 2^{32}$ or $b > 31$ | | u32shr
- *(18 cycles)*
u32shr.*b*
- *(3 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \lfloor a/2^b \rfloor$
Undefined if $a \ge 2^{32}$ or $b > 31$ | | u32rotl
- *(18 cycles)*
u32rotl.*b*
- *(3 cycles)* | [b, a, ...] | [c, ...] | Computes $c$ by rotating a 32-bit representation of $a$ to the left by $b$ bits.
Undefined if $a \ge 2^{32}$ or $b > 31$ | From b3812e066077719bfc69a71dcfb16ba75147c945 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 2 Jul 2024 20:35:30 +0300 Subject: [PATCH 6/7] fix: fix bug in lex_bin, add inlining for is_ascii_binary --- assembly/src/parser/lexer.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assembly/src/parser/lexer.rs b/assembly/src/parser/lexer.rs index 9deec6355d..e6a0243283 100644 --- a/assembly/src/parser/lexer.rs +++ b/assembly/src/parser/lexer.rs @@ -537,7 +537,7 @@ impl<'input> Lexer<'input> { loop { // If we hit a non-binary digit, we're done let c1 = self.read(); - if !c1.is_ascii_hexdigit() { + if !is_ascii_binary(c1) { break; } self.skip(); @@ -655,6 +655,7 @@ fn parse_bin(span: SourceSpan, bin_digits: &str) -> Result bool { matches!(c, '0'..='1') } From 93d139d79722fa378b64c70d9305c0971f58e5b6 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 3 Jul 2024 02:19:58 +0300 Subject: [PATCH 7/7] refactor: fix inlining, fix the bug in error kind --- assembly/src/parser/lexer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assembly/src/parser/lexer.rs b/assembly/src/parser/lexer.rs index e6a0243283..fc511f849a 100644 --- a/assembly/src/parser/lexer.rs +++ b/assembly/src/parser/lexer.rs @@ -643,7 +643,7 @@ fn parse_bin(span: SourceSpan, bin_digits: &str) -> Result Result bool { matches!(c, '0'..='1') }