sp1_core_machine/memory/instructions/
air.rs1use std::borrow::Borrow;
2
3use p3_air::{Air, AirBuilder};
4use p3_field::AbstractField;
5use p3_matrix::Matrix;
6use sp1_stark::{air::SP1AirBuilder, Word};
7
8use crate::{
9 air::{SP1CoreAirBuilder, WordAirBuilder},
10 memory::MemoryCols,
11 operations::{BabyBearWordRangeChecker, IsZeroOperation},
12};
13use sp1_core_executor::{
14 events::MemoryAccessPosition, ByteOpcode, Opcode, DEFAULT_PC_INC, UNUSED_PC,
15};
16
17use super::{columns::MemoryInstructionsColumns, MemoryInstructionsChip};
18
19impl<AB> Air<AB> for MemoryInstructionsChip
20where
21 AB: SP1AirBuilder,
22 AB::Var: Sized,
23{
24 #[inline(never)]
25 fn eval(&self, builder: &mut AB) {
26 let main = builder.main();
27 let local = main.row_slice(0);
28 let local: &MemoryInstructionsColumns<AB::Var> = (*local).borrow();
29
30 let is_real = local.is_lb +
36 local.is_lbu +
37 local.is_lh +
38 local.is_lhu +
39 local.is_lw +
40 local.is_sb +
41 local.is_sh +
42 local.is_sw;
43
44 builder.assert_bool(local.is_lb);
45 builder.assert_bool(local.is_lbu);
46 builder.assert_bool(local.is_lh);
47 builder.assert_bool(local.is_lhu);
48 builder.assert_bool(local.is_lw);
49 builder.assert_bool(local.is_sb);
50 builder.assert_bool(local.is_sh);
51 builder.assert_bool(local.is_sw);
52 builder.assert_bool(is_real.clone());
53 builder.assert_bool(local.op_a_0);
54
55 self.eval_memory_address_and_access::<AB>(builder, local, is_real.clone());
56 self.eval_memory_load::<AB>(builder, local);
57 self.eval_memory_store::<AB>(builder, local);
58
59 let opcode = self.compute_opcode::<AB>(local);
60
61 builder.receive_instruction(
72 local.shard,
73 local.clk,
74 local.pc,
75 local.pc + AB::Expr::from_canonical_u32(DEFAULT_PC_INC),
76 AB::Expr::zero(),
77 opcode,
78 local.op_a_value,
79 local.op_b_value,
80 local.op_c_value,
81 local.op_a_0,
82 local.is_sb + local.is_sh + local.is_sw,
83 AB::Expr::one(),
84 AB::Expr::zero(),
85 AB::Expr::zero(),
86 is_real,
87 );
88 }
89}
90
91impl MemoryInstructionsChip {
92 pub(crate) fn compute_opcode<AB: SP1AirBuilder>(
94 &self,
95 local: &MemoryInstructionsColumns<AB::Var>,
96 ) -> AB::Expr {
97 local.is_lb * Opcode::LB.as_field::<AB::F>() +
98 local.is_lbu * Opcode::LBU.as_field::<AB::F>() +
99 local.is_lh * Opcode::LH.as_field::<AB::F>() +
100 local.is_lhu * Opcode::LHU.as_field::<AB::F>() +
101 local.is_lw * Opcode::LW.as_field::<AB::F>() +
102 local.is_sb * Opcode::SB.as_field::<AB::F>() +
103 local.is_sh * Opcode::SH.as_field::<AB::F>() +
104 local.is_sw * Opcode::SW.as_field::<AB::F>()
105 }
106
107 pub(crate) fn eval_memory_address_and_access<AB: SP1CoreAirBuilder>(
115 &self,
116 builder: &mut AB,
117 local: &MemoryInstructionsColumns<AB::Var>,
118 is_real: AB::Expr,
119 ) {
120 builder.send_instruction(
122 AB::Expr::zero(),
123 AB::Expr::zero(),
124 AB::Expr::from_canonical_u32(UNUSED_PC),
125 AB::Expr::from_canonical_u32(UNUSED_PC + DEFAULT_PC_INC),
126 AB::Expr::zero(),
127 AB::Expr::from_canonical_u32(Opcode::ADD as u32),
128 local.addr_word,
129 local.op_b_value,
130 local.op_c_value,
131 AB::Expr::zero(),
132 AB::Expr::zero(),
133 AB::Expr::zero(),
134 AB::Expr::zero(),
135 AB::Expr::zero(),
136 is_real.clone(),
137 );
138
139 BabyBearWordRangeChecker::<AB::F>::range_check(
142 builder,
143 local.addr_word,
144 local.addr_word_range_checker,
145 is_real.clone(),
146 );
147
148 builder.slice_range_check_u8(&local.addr_word.0[1..3], is_real.clone());
151
152 builder.send_byte(
156 ByteOpcode::LTU.as_field::<AB::F>(),
157 AB::Expr::one(),
158 AB::Expr::from_canonical_u8(31),
159 local.addr_word[0],
160 local.most_sig_bytes_zero.result,
161 );
162
163 builder.when(local.most_sig_bytes_zero.result).assert_one(is_real.clone());
168
169 IsZeroOperation::<AB::F>::eval(
173 builder,
174 local.addr_word[1] + local.addr_word[2] + local.addr_word[3],
175 local.most_sig_bytes_zero,
176 is_real.clone(),
177 );
178
179 self.eval_offset_value_flags(builder, local);
181
182 builder.when(is_real.clone()).assert_eq::<AB::Expr, AB::Expr>(
184 local.addr_aligned + local.addr_ls_two_bits,
185 local.addr_word.reduce::<AB>(),
186 );
187
188 builder.send_byte(
191 ByteOpcode::AND.as_field::<AB::F>(),
192 local.addr_ls_two_bits,
193 local.addr_word[0],
194 AB::Expr::from_canonical_u8(0b11),
195 is_real.clone(),
196 );
197
198 builder.eval_memory_access(
201 local.shard,
202 local.clk + AB::F::from_canonical_u32(MemoryAccessPosition::Memory as u32),
203 local.addr_aligned,
204 &local.memory_access,
205 is_real.clone(),
206 );
207
208 builder
210 .when(local.is_lb + local.is_lbu + local.is_lh + local.is_lhu + local.is_lw)
211 .assert_word_eq(*local.memory_access.value(), *local.memory_access.prev_value());
212 }
213
214 pub(crate) fn eval_memory_load<AB: SP1AirBuilder>(
216 &self,
217 builder: &mut AB,
218 local: &MemoryInstructionsColumns<AB::Var>,
219 ) {
220 self.eval_unsigned_mem_value(builder, local);
222
223 builder.assert_eq(
230 local.mem_value_is_neg_not_x0,
231 (local.is_lb + local.is_lh) * local.most_sig_bit * (AB::Expr::one() - local.op_a_0),
232 );
233
234 builder.send_byte(
237 ByteOpcode::MSB.as_field::<AB::F>(),
238 local.most_sig_bit,
239 local.most_sig_byte,
240 AB::Expr::zero(),
241 local.is_lb + local.is_lh,
242 );
243 builder.assert_eq(
244 local.most_sig_byte,
245 local.is_lb * local.unsigned_mem_val[0] + local.is_lh * local.unsigned_mem_val[1],
246 );
247
248 let signed_value = Word([
251 AB::Expr::zero(),
252 AB::Expr::one() * local.is_lb,
253 AB::Expr::one() * local.is_lh,
254 AB::Expr::zero(),
255 ]);
256
257 builder.send_instruction(
260 AB::Expr::zero(),
261 AB::Expr::zero(),
262 AB::Expr::from_canonical_u32(UNUSED_PC),
263 AB::Expr::from_canonical_u32(UNUSED_PC + DEFAULT_PC_INC),
264 AB::Expr::zero(),
265 Opcode::SUB.as_field::<AB::F>(),
266 local.op_a_value,
267 local.unsigned_mem_val,
268 signed_value,
269 AB::Expr::zero(),
270 AB::Expr::zero(),
271 AB::Expr::zero(),
272 AB::Expr::zero(),
273 AB::Expr::zero(),
274 local.mem_value_is_neg_not_x0,
275 );
276
277 let mem_value_is_pos = (local.is_lb + local.is_lh) * (AB::Expr::one() - local.most_sig_bit) +
283 local.is_lbu +
284 local.is_lhu +
285 local.is_lw;
286 builder.assert_eq(
287 local.mem_value_is_pos_not_x0,
288 mem_value_is_pos * (AB::Expr::one() - local.op_a_0),
289 );
290
291 builder
294 .when(local.mem_value_is_pos_not_x0)
295 .assert_word_eq(local.unsigned_mem_val, local.op_a_value);
296
297 }
301
302 pub(crate) fn eval_memory_store<AB: SP1AirBuilder>(
304 &self,
305 builder: &mut AB,
306 local: &MemoryInstructionsColumns<AB::Var>,
307 ) {
308 self.eval_offset_value_flags(builder, local);
310 let offset_is_zero =
314 AB::Expr::one() - local.ls_bits_is_one - local.ls_bits_is_two - local.ls_bits_is_three;
315
316 let one = AB::Expr::one();
318 let a_val = local.op_a_value;
319 let mem_val = *local.memory_access.value();
320 let prev_mem_val = *local.memory_access.prev_value();
321 let sb_expected_stored_value = Word([
322 a_val[0] * offset_is_zero.clone() +
323 (one.clone() - offset_is_zero.clone()) * prev_mem_val[0],
324 a_val[0] * local.ls_bits_is_one +
325 (one.clone() - local.ls_bits_is_one) * prev_mem_val[1],
326 a_val[0] * local.ls_bits_is_two +
327 (one.clone() - local.ls_bits_is_two) * prev_mem_val[2],
328 a_val[0] * local.ls_bits_is_three +
329 (one.clone() - local.ls_bits_is_three) * prev_mem_val[3],
330 ]);
331 builder
332 .when(local.is_sb)
333 .assert_word_eq(mem_val.map(|x| x.into()), sb_expected_stored_value);
334
335 builder.when(local.is_sh).assert_zero(local.ls_bits_is_one + local.ls_bits_is_three);
337
338 builder.when(local.is_sw).assert_one(offset_is_zero.clone());
340
341 let a_is_lower_half = offset_is_zero;
343 let a_is_upper_half = local.ls_bits_is_two;
344 let sh_expected_stored_value = Word([
345 a_val[0] * a_is_lower_half.clone() +
346 (one.clone() - a_is_lower_half.clone()) * prev_mem_val[0],
347 a_val[1] * a_is_lower_half.clone() + (one.clone() - a_is_lower_half) * prev_mem_val[1],
348 a_val[0] * a_is_upper_half + (one.clone() - a_is_upper_half) * prev_mem_val[2],
349 a_val[1] * a_is_upper_half + (one.clone() - a_is_upper_half) * prev_mem_val[3],
350 ]);
351 builder
352 .when(local.is_sh)
353 .assert_word_eq(mem_val.map(|x| x.into()), sh_expected_stored_value);
354
355 builder
357 .when(local.is_sw)
358 .assert_word_eq(mem_val.map(|x| x.into()), a_val.map(|x| x.into()));
359 }
360
361 pub(crate) fn eval_unsigned_mem_value<AB: SP1AirBuilder>(
364 &self,
365 builder: &mut AB,
366 local: &MemoryInstructionsColumns<AB::Var>,
367 ) {
368 let mem_val = *local.memory_access.value();
369
370 let offset_is_zero =
374 AB::Expr::one() - local.ls_bits_is_one - local.ls_bits_is_two - local.ls_bits_is_three;
375
376 let mem_byte = mem_val[0] * offset_is_zero.clone() +
378 mem_val[1] * local.ls_bits_is_one +
379 mem_val[2] * local.ls_bits_is_two +
380 mem_val[3] * local.ls_bits_is_three;
381 let byte_value = Word::extend_expr::<AB>(mem_byte.clone());
382
383 builder
385 .when(local.is_lb + local.is_lbu)
386 .assert_word_eq(byte_value, local.unsigned_mem_val.map(|x| x.into()));
387
388 builder
390 .when(local.is_lh + local.is_lhu)
391 .assert_zero(local.ls_bits_is_one + local.ls_bits_is_three);
392
393 builder.when(local.is_lw).assert_one(offset_is_zero.clone());
395
396 let use_lower_half = offset_is_zero;
397 let use_upper_half = local.ls_bits_is_two;
398 let half_value = Word([
399 use_lower_half.clone() * mem_val[0] + use_upper_half * mem_val[2],
400 use_lower_half * mem_val[1] + use_upper_half * mem_val[3],
401 AB::Expr::zero(),
402 AB::Expr::zero(),
403 ]);
404 builder
405 .when(local.is_lh + local.is_lhu)
406 .assert_word_eq(half_value, local.unsigned_mem_val.map(|x| x.into()));
407
408 builder.when(local.is_lw).assert_word_eq(mem_val, local.unsigned_mem_val);
410 }
411
412 pub(crate) fn eval_offset_value_flags<AB: SP1AirBuilder>(
414 &self,
415 builder: &mut AB,
416 local: &MemoryInstructionsColumns<AB::Var>,
417 ) {
418 let offset_is_zero =
419 AB::Expr::one() - local.ls_bits_is_one - local.ls_bits_is_two - local.ls_bits_is_three;
420
421 builder.assert_bool(local.ls_bits_is_one);
423 builder.assert_bool(local.ls_bits_is_two);
424 builder.assert_bool(local.ls_bits_is_three);
425
426 builder.assert_one(
428 offset_is_zero.clone() +
429 local.ls_bits_is_one +
430 local.ls_bits_is_two +
431 local.ls_bits_is_three,
432 );
433
434 builder.when(offset_is_zero).assert_zero(local.addr_ls_two_bits);
439 builder.when(local.ls_bits_is_one).assert_one(local.addr_ls_two_bits);
440 builder.when(local.ls_bits_is_two).assert_eq(local.addr_ls_two_bits, AB::Expr::two());
441 builder
442 .when(local.ls_bits_is_three)
443 .assert_eq(local.addr_ls_two_bits, AB::Expr::from_canonical_u8(3));
444 }
445}