turshi/
word.rs

1//! The Cairo language works natively for field elements in the finite field with
2//! modulus 0x800000000000011000000000000000000000000000000000000000000000001
3//! This is the hexadecimal value for 2 ^ 251 + 17 * 2 ^ 192 + 1
4//! Our Pallas curves have 255 bits, so Cairo native instructions will fit.
5//! This means that our Cairo implementation can admit a larger domain for
6//! immediate values than theirs.
7
8use crate::{flags::*, helper::CairoFieldHelpers};
9use ark_ff::Field;
10use o1_utils::field_helpers::FieldHelpers;
11
12/// A Cairo word for the runner. Some words are instructions (which fit inside a
13/// `u64`). Others are immediate values (any `F` element).
14#[derive(Clone, Copy)]
15pub struct CairoWord<F>(F);
16
17/// Returns an offset of 16 bits to its biased representation in the interval
18/// `[-2^15,2^15)` as a field element
19pub fn bias<F: Field>(offset: F) -> F {
20    offset - F::from(2u16.pow(15u32)) // -2^15 + sum_(i=0..15) b_i * 2^i
21}
22
23impl<F: Field> CairoWord<F> {
24    /// Creates a [CairoWord] from a field element
25    pub fn new(word: F) -> CairoWord<F> {
26        CairoWord(word)
27    }
28
29    /// Returns the content of the word as a field element
30    pub fn word(&self) -> F {
31        self.0
32    }
33
34    /// Returns i-th bit-flag
35    fn flag_at(&self, pos: usize) -> F {
36        self.word().to_bits()[POS_FLAGS + pos].into()
37    }
38}
39
40/// This trait contains methods to obtain the offset decomposition of a
41/// [CairoWord]
42pub trait Offsets<F> {
43    /// Returns the destination offset in biased representation
44    fn off_dst(&self) -> F;
45
46    /// Returns the first operand offset in biased representation
47    fn off_op0(&self) -> F;
48
49    /// Returns the second operand offset in biased representation
50    fn off_op1(&self) -> F;
51}
52
53/// This trait contains methods that decompose a field element into [CairoWord]
54/// flagbits
55pub trait FlagBits<F> {
56    /// Returns bit-flag for destination register as `F`
57    fn f_dst_fp(&self) -> F;
58
59    /// Returns bit-flag for first operand register as `F`
60    fn f_op0_fp(&self) -> F;
61
62    /// Returns bit-flag for immediate value for second register as `F`
63    fn f_op1_val(&self) -> F;
64
65    /// Returns bit-flag for frame pointer for second register as `F`
66    fn f_op1_fp(&self) -> F;
67
68    /// Returns bit-flag for allocation pointer for second regsiter as `F`
69    fn f_op1_ap(&self) -> F;
70
71    /// Returns bit-flag for addition operation in right side as `F`
72    fn f_res_add(&self) -> F;
73
74    /// Returns bit-flag for multiplication operation in right side as `F`
75    fn f_res_mul(&self) -> F;
76
77    /// Returns bit-flag for program counter update being absolute jump as `F`
78    fn f_pc_abs(&self) -> F;
79
80    /// Returns bit-flag for program counter update being relative jump as `F`
81    fn f_pc_rel(&self) -> F;
82
83    /// Returns bit-flag for program counter update being conditional jump as
84    /// `F`
85    fn f_pc_jnz(&self) -> F;
86
87    /// Returns bit-flag for allocation counter update being a manual addition
88    /// as `F`
89    fn f_ap_add(&self) -> F;
90
91    /// Returns bit-flag for allocation counter update being a self increment as
92    /// `F`
93    fn f_ap_one(&self) -> F;
94
95    /// Returns bit-flag for operation being a call as `F`
96    fn f_opc_call(&self) -> F;
97
98    /// Returns bit-flag for operation being a return as `F`
99    fn f_opc_ret(&self) -> F;
100
101    /// Returns bit-flag for operation being an assert-equal as `F`
102    fn f_opc_aeq(&self) -> F;
103
104    /// Returns bit-flag for 16th position
105    fn f15(&self) -> F;
106}
107
108/// This trait contains methods that decompose a field element into [CairoWord]
109/// flagsets
110pub trait FlagSets<F> {
111    /// Returns flagset for destination register
112    fn dst_reg(&self) -> u8;
113
114    /// Returns flagset for first operand register
115    fn op0_reg(&self) -> u8;
116
117    /// Returns flagset for second operand register
118    fn op1_src(&self) -> u8;
119
120    /// Returns flagset for result logics
121    fn res_log(&self) -> u8;
122
123    /// Returns flagset for program counter update
124    fn pc_up(&self) -> u8;
125
126    /// Returns flagset for allocation pointer update
127    fn ap_up(&self) -> u8;
128
129    /// Returns flagset for operation code
130    fn opcode(&self) -> u8;
131}
132
133impl<F: Field> Offsets<F> for CairoWord<F> {
134    fn off_dst(&self) -> F {
135        // The least significant 16 bits
136        bias(self.word().u16_chunk(POS_DST))
137    }
138
139    fn off_op0(&self) -> F {
140        // From the 32nd bit to the 17th
141        bias(self.word().u16_chunk(POS_OP0))
142    }
143
144    fn off_op1(&self) -> F {
145        // From the 48th bit to the 33rd
146        bias(self.word().u16_chunk(POS_OP1))
147    }
148}
149
150impl<F: Field> FlagBits<F> for CairoWord<F> {
151    fn f_dst_fp(&self) -> F {
152        self.flag_at(0)
153    }
154
155    fn f_op0_fp(&self) -> F {
156        self.flag_at(1)
157    }
158
159    fn f_op1_val(&self) -> F {
160        self.flag_at(2)
161    }
162
163    fn f_op1_fp(&self) -> F {
164        self.flag_at(3)
165    }
166
167    fn f_op1_ap(&self) -> F {
168        self.flag_at(4)
169    }
170
171    fn f_res_add(&self) -> F {
172        self.flag_at(5)
173    }
174
175    fn f_res_mul(&self) -> F {
176        self.flag_at(6)
177    }
178
179    fn f_pc_abs(&self) -> F {
180        self.flag_at(7)
181    }
182
183    fn f_pc_rel(&self) -> F {
184        self.flag_at(8)
185    }
186
187    fn f_pc_jnz(&self) -> F {
188        self.flag_at(9)
189    }
190
191    fn f_ap_add(&self) -> F {
192        self.flag_at(10)
193    }
194
195    fn f_ap_one(&self) -> F {
196        self.flag_at(11)
197    }
198
199    fn f_opc_call(&self) -> F {
200        self.flag_at(12)
201    }
202
203    fn f_opc_ret(&self) -> F {
204        self.flag_at(13)
205    }
206
207    fn f_opc_aeq(&self) -> F {
208        self.flag_at(14)
209    }
210
211    fn f15(&self) -> F {
212        self.flag_at(15)
213    }
214}
215
216impl<F: Field> FlagSets<F> for CairoWord<F> {
217    fn dst_reg(&self) -> u8 {
218        // dst_reg = fDST_REG
219        self.f_dst_fp().lsb()
220    }
221
222    fn op0_reg(&self) -> u8 {
223        // op0_reg = fOP0_REG
224        self.f_op0_fp().lsb()
225    }
226
227    fn op1_src(&self) -> u8 {
228        // op1_src = 4*fOP1_AP + 2*fOP1_FP + fOP1_VAL
229        2 * (2 * self.f_op1_ap().lsb() + self.f_op1_fp().lsb()) + self.f_op1_val().lsb()
230    }
231
232    fn res_log(&self) -> u8 {
233        // res_log = 2*fRES_MUL + fRES_ADD
234        2 * self.f_res_mul().lsb() + self.f_res_add().lsb()
235    }
236
237    fn pc_up(&self) -> u8 {
238        // pc_up = 4*fPC_JNZ + 2*fPC_REL + fPC_ABS
239        2 * (2 * self.f_pc_jnz().lsb() + self.f_pc_rel().lsb()) + self.f_pc_abs().lsb()
240    }
241
242    fn ap_up(&self) -> u8 {
243        // ap_up = 2*fAP_ONE + fAP_ADD
244        2 * self.f_ap_one().lsb() + self.f_ap_add().lsb()
245    }
246
247    fn opcode(&self) -> u8 {
248        // opcode = 4*fOPC_AEQ + 2*fOPC_RET + fOPC_CALL
249        2 * (2 * self.f_opc_aeq().lsb() + self.f_opc_ret().lsb()) + self.f_opc_call().lsb()
250    }
251}