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}