@@ -30,6 +30,7 @@ namespace avm_main(256);
3030
3131 //===== MEMORY OPCODES ==========================================================
3232 pol commit sel_mov;
33+ pol commit sel_cmov;
3334
3435 //===== TABLE SUBOP-TR ========================================================
3536 // Boolean selectors for (sub-)operations. Only one operation is activated at
@@ -75,40 +76,45 @@ namespace avm_main(256);
7576 // A helper witness being the inverse of some value
7677 // to show a non-zero equality
7778 pol commit inv;
79+ pol commit id_zero; // Boolean telling whether id is zero (cmov opcode)
7880
7981 // Intermediate register values
8082 pol commit ia;
8183 pol commit ib;
8284 pol commit ic;
85+ pol commit id;
8386
8487 // Memory operation selector per intermediate register
8588 pol commit mem_op_a;
8689 pol commit mem_op_b;
8790 pol commit mem_op_c;
91+ pol commit mem_op_d;
8892
8993 // Read-write flag per intermediate register: Read = 0, Write = 1
9094 pol commit rwa;
9195 pol commit rwb;
9296 pol commit rwc;
97+ pol commit rwd;
9398
9499 // Indirect register values
95100 pol commit ind_a;
96101 pol commit ind_b;
97102 pol commit ind_c;
103+ pol commit ind_d;
98104
99105 // Indirect memory operation selector per indirect register
100106 pol commit ind_op_a;
101107 pol commit ind_op_b;
102108 pol commit ind_op_c;
103-
109+ pol commit ind_op_d;
104110
105111 // Memory index involved into a memory operation per pertaining intermediate register
106112 // We should range constrain it to 32 bits ultimately. For first version of the AVM,
107113 // we will assume that these columns are of the right type.
108114 pol commit mem_idx_a;
109115 pol commit mem_idx_b;
110116 pol commit mem_idx_c;
111-
117+ pol commit mem_idx_d;
112118
113119 // Track the last line of the execution trace. It does NOT correspond to the last row of the whole table
114120 // of size N. As this depends on the supplied bytecode, this polynomial cannot be constant.
@@ -135,27 +141,32 @@ namespace avm_main(256);
135141
136142 // Might be removed if derived from opcode based on a lookup of constants
137143 sel_mov * ( 1 - sel_mov) = 0;
144+ sel_cmov * ( 1 - sel_cmov) = 0;
138145
139146 op_err * (1 - op_err) = 0;
140147 tag_err * (1 - tag_err) = 0; // Potential optimization (boolean constraint derivation from equivalence check to avm_mem)?
148+ id_zero * (1 - id_zero) = 0;
141149
142150 // Might be removed if derived from opcode based on a lookup of constants
143151 mem_op_a * (1 - mem_op_a) = 0;
144152 mem_op_b * (1 - mem_op_b) = 0;
145153 mem_op_c * (1 - mem_op_c) = 0;
154+ mem_op_d * (1 - mem_op_d) = 0;
146155
147156 rwa * (1 - rwa) = 0;
148157 rwb * (1 - rwb) = 0;
149158 rwc * (1 - rwc) = 0;
159+ rwd * (1 - rwd) = 0;
150160
151161 // Might be removed if derived from opcode based on a lookup of constants
152162 ind_op_a * (1 - ind_op_a) = 0;
153163 ind_op_b * (1 - ind_op_b) = 0;
154164 ind_op_c * (1 - ind_op_c) = 0;
165+ ind_op_d * (1 - ind_op_d) = 0;
155166
156167 // TODO - Constraints:
157- // - mem_idx_a, mem_idx_b, mem_idx_c to u32 type
158- // - ind_a, ind_b, ind_c to u32 type
168+ // - mem_idx_a, mem_idx_b, mem_idx_c, mem_idx_d to u32 type
169+ // - ind_a, ind_b, ind_c, ind_d to u32 type
159170 // - 0 <= r_in_tag, w_in_tag <= 6 // Maybe not needed as probably derived by the operation decomposition.
160171
161172 //====== COMPARATOR OPCODES CONSTRAINTS =====================================
@@ -245,10 +256,36 @@ namespace avm_main(256);
245256 // TODO: we want to set an initial number for the reserved memory of the jump pointer
246257
247258 //====== MEMORY OPCODES CONSTRAINTS =========================================
248- #[MOV_SAME_VALUE]
249- sel_mov * (ia - ic) = 0; // Ensure that the correct value is moved/copied.
259+
260+ // TODO: consolidate with zero division error handling
261+
262+ // When sel_cmov == 1, we need id == 0 <==> id_zero == 0
263+ // This can be achieved with the 2 following relations.
264+ // inv is an extra witness to show that we can invert id, i.e., inv = id^(-1)
265+ // If id == 0, we have to set inv = 1 to satisfy the second relation,
266+ // because id_zero == 1 from the first relation.
267+ #[CMOV_CONDITION_RES_1]
268+ sel_cmov * (id * inv - 1 + id_zero) = 0;
269+ #[CMOV_CONDITION_RES_2]
270+ sel_cmov * id_zero * (1 - inv) = 0;
271+
272+ // Boolean selectors telling whether we move ia to ic or ib to ic.
273+ // Boolean constraints and mutual exclusivity are derived from their
274+ // respective definitions based on sel_mov, sel_cmov, and id_zero.
275+ pol commit sel_mov_a;
276+ pol commit sel_mov_b;
277+
278+ // For MOV, we copy ia to ic.
279+ // For CMOV, we copy ia to ic if id is NOT zero, otherwise we copy ib to ic.
280+ sel_mov_a = sel_mov + sel_cmov * (1 - id_zero);
281+ sel_mov_b = sel_cmov * id_zero;
282+
283+ #[MOV_SAME_VALUE_A]
284+ sel_mov_a * (ia - ic) = 0; // Ensure that the correct value is moved/copied.
285+ #[MOV_SAME_VALUE_B]
286+ sel_mov_b * (ib - ic) = 0; // Ensure that the correct value is moved/copied.
250287 #[MOV_MAIN_SAME_TAG]
251- sel_mov * (r_in_tag - w_in_tag) = 0;
288+ ( sel_mov + sel_cmov) * (r_in_tag - w_in_tag) = 0;
252289
253290 //====== Inter-table Constraints ============================================
254291 #[INCL_MAIN_TAG_ERR]
@@ -288,20 +325,31 @@ namespace avm_main(256);
288325 avm_binary.start {avm_binary.clk, avm_binary.acc_ia, avm_binary.acc_ib, avm_binary.acc_ic, avm_binary.op_id, avm_binary.in_tag};
289326
290327 #[PERM_MAIN_MEM_A]
291- mem_op_a {clk, mem_idx_a, ia, rwa, r_in_tag, w_in_tag, sel_mov}
328+ mem_op_a {clk, mem_idx_a, ia, rwa
329+ , r_in_tag, w_in_tag, sel_mov_a, sel_cmov}
292330 is
293- avm_mem.op_a {avm_mem.clk, avm_mem.addr, avm_mem.val, avm_mem.rw, avm_mem.r_in_tag, avm_mem.w_in_tag, avm_mem.sel_mov};
331+ avm_mem.op_a {avm_mem.clk, avm_mem.addr, avm_mem.val, avm_mem.rw
332+ , avm_mem.r_in_tag, avm_mem.w_in_tag, avm_mem.sel_mov_a, avm_mem.sel_cmov};
294333
295334 #[PERM_MAIN_MEM_B]
296- mem_op_b {clk, mem_idx_b, ib, rwb, r_in_tag, w_in_tag}
335+ mem_op_b {clk, mem_idx_b, ib, rwb
336+ , r_in_tag, w_in_tag, sel_mov_b, sel_cmov}
297337 is
298- avm_mem.op_b {avm_mem.clk, avm_mem.addr, avm_mem.val, avm_mem.rw, avm_mem.r_in_tag, avm_mem.w_in_tag};
338+ avm_mem.op_b {avm_mem.clk, avm_mem.addr, avm_mem.val, avm_mem.rw
339+ , avm_mem.r_in_tag, avm_mem.w_in_tag, avm_mem.sel_mov_b, avm_mem.sel_cmov};
299340
300341 #[PERM_MAIN_MEM_C]
301342 mem_op_c {clk, mem_idx_c, ic, rwc, r_in_tag, w_in_tag}
302343 is
303344 avm_mem.op_c {avm_mem.clk, avm_mem.addr, avm_mem.val, avm_mem.rw, avm_mem.r_in_tag, avm_mem.w_in_tag};
304345
346+ #[PERM_MAIN_MEM_D]
347+ mem_op_d {clk, mem_idx_d, id, rwd
348+ , r_in_tag, w_in_tag, sel_cmov}
349+ is
350+ avm_mem.op_d {avm_mem.clk, avm_mem.addr, avm_mem.val, avm_mem.rw
351+ , avm_mem.r_in_tag, avm_mem.w_in_tag, avm_mem.sel_cmov};
352+
305353 #[PERM_MAIN_MEM_IND_A]
306354 ind_op_a {clk, ind_a, mem_idx_a} is avm_mem.ind_op_a {avm_mem.clk, avm_mem.addr, avm_mem.val};
307355
@@ -311,6 +359,9 @@ namespace avm_main(256);
311359 #[PERM_MAIN_MEM_IND_C]
312360 ind_op_c {clk, ind_c, mem_idx_c} is avm_mem.ind_op_c {avm_mem.clk, avm_mem.addr, avm_mem.val};
313361
362+ #[PERM_MAIN_MEM_IND_D]
363+ ind_op_d {clk, ind_d, mem_idx_d} is avm_mem.ind_op_d {avm_mem.clk, avm_mem.addr, avm_mem.val};
364+
314365 //====== Inter-table Constraints (Range Checks) ============================================
315366 // TODO: Investigate optimising these range checks. Handling non-FF elements should require less range checks.
316367 #[LOOKUP_U8_0]
0 commit comments