Skip to main content

zkevm_assembly/assembly/operand/
mod.rs

1//!
2//! The zkEVM register.
3//!
4//!
5use nom::{
6    self,
7    bytes::complete::{tag, take_until, take_while1, take_while_m_n},
8    multi::many_m_n,
9    AsChar,
10};
11
12use nom::error::{Error, ErrorKind};
13
14use std::{convert::TryFrom, num::ParseIntError};
15
16use crate::error::InstructionReadError;
17
18use zkevm_opcode_defs::{ImmMemHandlerFlags, RegOrImmFlags};
19
20#[derive(Debug, Clone, PartialEq, Eq, Hash)]
21pub enum FullOperand {
22    /// Full including all memory modifiers
23    Full(GenericOperand),
24    /// Use value at register
25    Register(RegisterOperand),
26    /// Constant and potentially offset
27    Constant(ConstantOperand),
28    /// Global variable on the stack
29    GlobalVariable(GlobalVariable),
30}
31
32///
33/// Structure representing address of the constant.
34///
35#[derive(Debug, Clone, PartialEq, Eq, Hash)]
36pub struct ConstantOperand {
37    /// Label of the constant
38    pub label: String,
39    /// Register to use it offset computation
40    pub register: RegisterOperand,
41    /// Offset to the constant label
42    pub immediate: u64,
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, Hash)]
46pub struct GlobalVariable {
47    /// Label of the constant
48    pub label: String,
49    /// Register to use it offset computation
50    pub register: RegisterOperand,
51    /// Offset to the constant label
52    pub immediate: u64,
53}
54
55///
56/// Structure representing address in memory.
57///
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
59pub struct GenericOperand {
60    pub r#type: ImmMemHandlerFlags,
61    /// Offset to apply to sp for Stack memory and memory address for other types of memory
62    pub immediate: u64,
63    /// Additional offset to apply
64    pub register: RegisterOperand,
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
68pub enum RegisterOperand {
69    /// The special null/void register.
70    Null,
71    /// The general purpose register. 1-based.
72    Register(u8),
73}
74
75///
76/// Structure representing non-memory operand
77///
78#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
79pub struct NonMemoryOperand {
80    pub r#type: RegOrImmFlags,
81    /// Offset to apply to sp for Stack memory and memory address for other types of memory
82    pub immediate: u64,
83    /// Additional offset to apply
84    pub register: RegisterOperand,
85}
86
87impl NonMemoryOperand {
88    pub fn as_register_operand(
89        self,
90        index: usize,
91    ) -> Result<RegisterOperand, InstructionReadError> {
92        match self.r#type {
93            RegOrImmFlags::UseRegOnly => Ok(self.register),
94            RegOrImmFlags::UseImm16Only => {
95                Err(InstructionReadError::InvalidRegImmInPlaceOfReg { index, found: self })
96            }
97        }
98    }
99}
100
101fn is_imm(input: &str) -> (&str, bool) {
102    let t = tag("#")(input);
103    match t {
104        Ok((input, _)) => (input, true),
105        Err(nom::Err::Error(Error {
106            input,
107            code: ErrorKind::Tag,
108        })) => (input, false),
109        _ => {
110            unreachable!()
111        }
112    }
113}
114
115fn is_register(input: &str) -> (&str, bool, &str) {
116    let mut reg_parser = nom::sequence::tuple::<_, _, nom::error::Error<_>, _>((
117        nom::character::complete::space0,
118        tag("r"),
119        take_while_m_n(1, 2, |el: char| el.is_ascii_digit()),
120    ));
121
122    match reg_parser(input) {
123        Ok((rest, result)) => {
124            let reg_raw = result.2;
125
126            (rest, true, reg_raw)
127        }
128        _ => (input, false, ""),
129    }
130}
131
132fn is_label(input: &str) -> (&str, bool, &str) {
133    let label_name_parser =
134        take_while1(|c: char| c.is_alphanum() || (c.is_ascii() && (c as u8) == b"_"[0]));
135    let mut label_parser = nom::sequence::tuple::<_, _, nom::error::Error<_>, _>((
136        nom::character::complete::space0,
137        tag("."),
138        label_name_parser,
139    ));
140
141    let label_name_parser =
142        take_while1(|c: char| c.is_alphanum() || (c.is_ascii() && (c as u8) == b"_"[0]));
143    let mut pseudo_label_parser = nom::sequence::tuple::<_, _, nom::error::Error<_>, _>((
144        nom::character::complete::space0,
145        tag("."),
146        label_name_parser,
147        nom::sequence::pair(tag("["), take_until("]")),
148    ));
149
150    // we need to parse .label, but not .label[] that will be handled by other cases
151
152    match pseudo_label_parser(input) {
153        Ok(_) => (input, false, ""),
154        _ => match label_parser(input) {
155            Ok((rest, result)) => {
156                let label_raw = result.2;
157
158                (rest, true, label_raw)
159            }
160            _ => (input, false, ""),
161        },
162    }
163}
164
165fn has_tag<'a>(input: &'a str, tag: &str) -> (bool, &'a str) {
166    match nom::bytes::complete::tag::<_, _, nom::error::Error<_>>(tag)(input) {
167        Ok((rest, _)) => (true, rest),
168        Err(nom::Err::Error(Error {
169            input,
170            code: ErrorKind::Tag,
171        })) => (false, input),
172        _ => {
173            unreachable!()
174        }
175    }
176}
177
178fn try_memory_offset<'a>(
179    input: &'a str,
180) -> Result<(&'a str, Vec<&'a str>, Vec<&'a str>, Vec<&'a str>), ()> {
181    let reg_parser = nom::sequence::tuple::<_, _, nom::error::Error<_>, _>((
182        nom::character::complete::space0,
183        tag("r"),
184        take_while_m_n(1, 2, |el: char| el.is_ascii_digit()),
185    ));
186
187    let sign_parser = nom::sequence::tuple((
188        nom::character::complete::space0,
189        many_m_n(0, 1, nom::branch::alt((tag("+"), tag("-")))),
190        nom::character::complete::space0,
191    ));
192
193    let mut parser = nom::sequence::tuple((
194        many_m_n(0, 1, reg_parser),
195        nom::character::complete::space0,
196        sign_parser,
197    ));
198
199    match parser(input) {
200        Ok((rest, result)) => {
201            let (reg_parsing, _, sign_parsing) = result;
202            let reg_may_be: Vec<_> = reg_parsing.into_iter().map(|el| el.2).collect();
203            let sign_may_be = sign_parsing.1;
204
205            if rest.is_empty() {
206                return Ok((rest, reg_may_be, sign_may_be, vec![]));
207            }
208
209            // otherwise try to parse imm
210            match many_m_n::<_, _, nom::error::Error<_>, _>(
211                0,
212                1,
213                nom::character::complete::alphanumeric0,
214            )(rest)
215            {
216                Ok((rest, result)) => Ok((rest, reg_may_be, sign_may_be, result)),
217                Err(nom::Err::Error(Error { input, code })) => {
218                    dbg!(input);
219                    dbg!(code);
220                    Err(())
221                }
222                _ => Err(()),
223            }
224        }
225        _ => Err(()),
226    }
227}
228
229fn try_immediate<'a>(input: &'a str) -> Result<(&'a str, u64), ParseIntError> {
230    let (has_prefix, input) = has_tag(input, "0x");
231    if has_prefix {
232        match u64::from_str_radix(input, 16) {
233            Ok(imm) => Ok(("", imm)),
234            Err(e) => Err(e),
235        }
236    } else {
237        let (has_prefix, input) = has_tag(input, "0b");
238        if has_prefix {
239            match u64::from_str_radix(input, 2) {
240                Ok(imm) => Ok(("", imm)),
241                Err(e) => Err(e),
242            }
243        } else {
244            match u64::from_str_radix(input, 10) {
245                Ok(imm) => Ok(("", imm)),
246                Err(e) => Err(e),
247            }
248        }
249    }
250}
251
252impl FullOperand {
253    pub fn as_register_operand(
254        self,
255        index: usize,
256    ) -> Result<RegisterOperand, InstructionReadError> {
257        match self {
258            FullOperand::Register(reg) => Ok(reg),
259            other => Err(InstructionReadError::InvalidOperandForRegLocation {
260                index,
261                found: other,
262            }),
263        }
264    }
265
266    pub fn as_non_memory_operand(
267        self,
268        index: usize,
269    ) -> Result<NonMemoryOperand, InstructionReadError> {
270        match self {
271            FullOperand::Full(operand) => Ok(operand.as_non_memory_operand()),
272            other => Err(InstructionReadError::InvalidOperandForRegImmLocation {
273                index,
274                found: other,
275            }),
276        }
277    }
278
279    pub fn as_generic_operand(self, index: usize) -> Result<GenericOperand, InstructionReadError> {
280        match self {
281            FullOperand::Full(operand) => Ok(operand),
282            other => Err(InstructionReadError::InvalidOperandForGenericLocation {
283                index,
284                found: other,
285            }),
286        }
287    }
288
289    pub fn as_constant_operand(
290        self,
291        index: usize,
292    ) -> Result<ConstantOperand, InstructionReadError> {
293        match self {
294            FullOperand::Constant(operand) => Ok(operand),
295            other => Err(InstructionReadError::InvalidOperandForLabelLocation {
296                index,
297                found: other,
298            }),
299        }
300    }
301
302    pub fn as_global_variable(self, index: usize) -> Result<GlobalVariable, InstructionReadError> {
303        match self {
304            FullOperand::GlobalVariable(operand) => Ok(operand),
305            other => Err(InstructionReadError::InvalidOperandForLabelLocation {
306                index,
307                found: other,
308            }),
309        }
310    }
311
312    pub fn as_immediate(self, index: usize) -> Result<u64, InstructionReadError> {
313        match self {
314            FullOperand::Full(GenericOperand {
315                r#type: ImmMemHandlerFlags::UseImm16Only,
316                immediate: imm,
317                register: RegisterOperand::Null,
318            }) => Ok(imm),
319            other => Err(InstructionReadError::InvalidArgument {
320                index,
321                expected: "label or immediate",
322                found: format!("{:?}", other),
323            }),
324        }
325    }
326}
327
328impl GenericOperand {
329    pub fn as_word_offset_into_code_page(self) -> u64 {
330        let GenericOperand {
331            r#type,
332            immediate,
333            register,
334        } = self;
335        match register {
336            RegisterOperand::Null => {}
337            _ => {
338                panic!("Invalid linking result")
339            }
340        }
341        match r#type {
342            ImmMemHandlerFlags::UseCodePage => immediate,
343            _ => {
344                panic!("Invalid linking result")
345            }
346        }
347    }
348
349    pub fn as_pc_offset(self) -> u64 {
350        let GenericOperand {
351            r#type,
352            immediate,
353            register,
354        } = self;
355        match register {
356            RegisterOperand::Null => {}
357            _ => {
358                panic!("Invalid linking result")
359            }
360        }
361        match r#type {
362            ImmMemHandlerFlags::UseImm16Only => immediate,
363            _ => {
364                panic!("Invalid linking result")
365            }
366        }
367    }
368
369    pub fn as_non_memory_operand(self) -> NonMemoryOperand {
370        match self.r#type {
371            ImmMemHandlerFlags::UseRegOnly => {
372                assert!(self.immediate == 0);
373                NonMemoryOperand {
374                    register: self.register,
375                    r#type: RegOrImmFlags::UseRegOnly,
376                    immediate: 0,
377                }
378            }
379            ImmMemHandlerFlags::UseImm16Only => {
380                match self.register {
381                    RegisterOperand::Null => {}
382                    _ => {
383                        panic!("Register must be zero")
384                    }
385                }
386                NonMemoryOperand {
387                    register: self.register,
388                    r#type: RegOrImmFlags::UseImm16Only,
389                    immediate: self.immediate,
390                }
391            }
392            _ => {
393                panic!("Invalid reg/imm operand")
394            }
395        }
396    }
397}
398
399impl RegisterOperand {
400    ///
401    /// Whether the register is the null (`r0`) one.
402    ///
403    pub fn is_void(&self) -> bool {
404        matches!(self, Self::Null)
405    }
406}
407
408impl TryFrom<&str> for RegisterOperand {
409    type Error = InstructionReadError;
410
411    fn try_from(input: &str) -> Result<Self, Self::Error> {
412        let index: u8 = input
413            .parse()
414            .map_err(|e| InstructionReadError::InvalidNumber(input.to_owned(), e))?;
415        if index == 0 {
416            return Ok(Self::Null);
417        }
418        if index as usize > zkevm_opcode_defs::REGISTERS_COUNT {
419            return Err(InstructionReadError::UnknownRegister(input.to_owned()));
420        }
421
422        Ok(Self::Register(index))
423    }
424}