Skip to content

Commit ec9dfdf

Browse files
authored
refactor(avm)!: remove CMOV opcode (#9030)
It's not emitted by Noir, and it needs special casing in the AVM circuit. We discussed with Alvaro that we'd remove it.
1 parent 4873c7b commit ec9dfdf

36 files changed

+711
-1520
lines changed

avm-transpiler/src/opcodes.rs

-2
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ pub enum AvmOpcode {
5151
SET_FF,
5252
MOV_8,
5353
MOV_16,
54-
CMOV,
5554
// World state
5655
SLOAD,
5756
SSTORE,
@@ -144,7 +143,6 @@ impl AvmOpcode {
144143
AvmOpcode::SET_FF => "SET_FF",
145144
AvmOpcode::MOV_8 => "MOV_8",
146145
AvmOpcode::MOV_16 => "MOV_16",
147-
AvmOpcode::CMOV => "CMOV",
148146

149147
// World State
150148
AvmOpcode::SLOAD => "SLOAD", // Public Storage

avm-transpiler/src/transpile.rs

-13
Original file line numberDiff line numberDiff line change
@@ -249,19 +249,6 @@ pub fn brillig_to_avm(
249249
destination.to_usize() as u32,
250250
));
251251
}
252-
BrilligOpcode::ConditionalMov { source_a, source_b, condition, destination } => {
253-
avm_instrs.push(AvmInstruction {
254-
opcode: AvmOpcode::CMOV,
255-
indirect: Some(AvmOperand::U8 { value: ALL_DIRECT }),
256-
operands: vec![
257-
AvmOperand::U32 { value: source_a.to_usize() as u32 },
258-
AvmOperand::U32 { value: source_b.to_usize() as u32 },
259-
AvmOperand::U32 { value: condition.to_usize() as u32 },
260-
AvmOperand::U32 { value: destination.to_usize() as u32 },
261-
],
262-
..Default::default()
263-
});
264-
}
265252
BrilligOpcode::Load { destination, source_pointer } => {
266253
avm_instrs.push(generate_mov_instruction(
267254
Some(AvmOperand::U8 { value: ZEROTH_OPERAND_INDIRECT }),

barretenberg/cpp/pil/avm/main.pil

+16-21
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,6 @@ namespace main(256);
120120
//===== MEMORY OPCODES ==========================================================
121121
pol commit sel_op_set;
122122
pol commit sel_op_mov;
123-
pol commit sel_op_cmov;
124123

125124
//===== TABLE SUBOP-TR ========================================================
126125
// Boolean selectors for (sub-)operations. Only one operation is activated at
@@ -160,7 +159,7 @@ namespace main(256);
160159
// A helper witness being the inverse of some value
161160
// to show a non-zero equality
162161
pol commit inv;
163-
pol commit id_zero; // Boolean telling whether id is zero (cmov opcode)
162+
pol commit id_zero; // Boolean telling whether id is zero (jmp opcode)
164163

165164
//===== MEMORY MODEL ========================================================
166165
// ind_addr_a -> (gets resolved to)
@@ -285,7 +284,6 @@ namespace main(256);
285284
// Might be removed if derived from opcode based on a lookup of constants
286285
sel_op_set * (1 - sel_op_set) = 0;
287286
sel_op_mov * (1 - sel_op_mov) = 0;
288-
sel_op_cmov * (1 - sel_op_cmov) = 0;
289287

290288
op_err * (1 - op_err) = 0;
291289
tag_err * (1 - tag_err) = 0; // Potential optimization (boolean constraint derivation from equivalence check to mem)?
@@ -424,7 +422,7 @@ namespace main(256);
424422
pol SEL_ALL_BINARY = sel_op_and + sel_op_or + sel_op_xor;
425423
pol SEL_ALL_GADGET = sel_op_radix_le + sel_op_sha256 + sel_op_poseidon2 + sel_op_keccak + sel_op_pedersen
426424
+ sel_op_ecadd + sel_op_pedersen_commit + sel_op_msm;
427-
pol SEL_ALL_MEMORY = sel_op_cmov + sel_op_mov + sel_op_set;
425+
pol SEL_ALL_MEMORY = sel_op_mov + sel_op_set;
428426
pol OPCODE_SELECTORS = sel_op_fdiv + sel_op_calldata_copy + sel_op_get_contract_instance
429427
+ SEL_ALL_ALU + SEL_ALL_BINARY + SEL_ALL_MEMORY + SEL_ALL_GADGET
430428
+ KERNEL_INPUT_SELECTORS + KERNEL_OUTPUT_SELECTORS + SEL_ALL_LEFTGAS
@@ -457,35 +455,32 @@ namespace main(256);
457455
//====== MEMORY OPCODES CONSTRAINTS =========================================
458456

459457
// TODO: consolidate with zero division error handling
460-
// TODO: Ensure that operation decompostion will ensure mutual exclusivity of sel_op_cmov and sel_op_jumpi
461458

462-
// When sel_op_cmov or sel_op_jumpi == 1, we need id == 0 <==> id_zero == 0
459+
// When sel_op_jumpi == 1, we need id == 0 <==> id_zero == 0
463460
// This can be achieved with the 2 following relations.
464461
// inv is an extra witness to show that we can invert id, i.e., inv = id^(-1)
465462
// If id == 0, we have to set inv = 1 to satisfy the second relation,
466463
// because id_zero == 1 from the first relation.
467-
#[CMOV_CONDITION_RES_1]
468-
(sel_op_cmov + sel_op_jumpi) * (id * inv - 1 + id_zero) = 0;
469-
#[CMOV_CONDITION_RES_2]
470-
(sel_op_cmov + sel_op_jumpi) * id_zero * (1 - inv) = 0;
464+
#[JMP_CONDITION_RES_1]
465+
sel_op_jumpi * (id * inv - 1 + id_zero) = 0;
466+
#[JMP_CONDITION_RES_2]
467+
sel_op_jumpi * id_zero * (1 - inv) = 0;
471468

472469
// Boolean selectors telling whether we move ia to ic or ib to ic.
473470
// Boolean constraints and mutual exclusivity are derived from their
474-
// respective definitions based on sel_op_mov, sel_op_cmov, and id_zero.
471+
// respective definitions based on sel_op_mov, and id_zero.
475472
pol commit sel_mov_ia_to_ic;
476473
pol commit sel_mov_ib_to_ic;
477474

478475
// For MOV, we copy ia to ic.
479-
// For CMOV, we copy ia to ic if id is NOT zero, otherwise we copy ib to ic.
480-
sel_mov_ia_to_ic = sel_op_mov + sel_op_cmov * (1 - id_zero);
481-
sel_mov_ib_to_ic = sel_op_cmov * id_zero;
476+
sel_mov_ia_to_ic = sel_op_mov * (1 - id_zero);
482477

483478
#[MOV_SAME_VALUE_A]
484479
sel_mov_ia_to_ic * (ia - ic) = 0; // Ensure that the correct value is moved/copied.
485480
#[MOV_SAME_VALUE_B]
486481
sel_mov_ib_to_ic * (ib - ic) = 0; // Ensure that the correct value is moved/copied.
487482
#[MOV_MAIN_SAME_TAG]
488-
(sel_op_mov + sel_op_cmov) * (r_in_tag - w_in_tag) = 0;
483+
sel_op_mov * (r_in_tag - w_in_tag) = 0;
489484

490485
// Predicate to activate the copy of intermediate registers to ALU table. If tag_err == 1,
491486
// the operation is not copied to the ALU table.
@@ -580,27 +575,27 @@ namespace main(256);
580575
slice.sel_start {slice.clk, slice.space_id, slice.col_offset, slice.cnt, slice.addr, slice.sel_cd_cpy, slice.sel_return};
581576

582577
#[PERM_MAIN_MEM_A]
583-
sel_mem_op_a {clk, space_id, mem_addr_a, ia, rwa, r_in_tag, w_in_tag, sel_mov_ia_to_ic, sel_op_cmov}
578+
sel_mem_op_a {clk, space_id, mem_addr_a, ia, rwa, r_in_tag, w_in_tag, sel_mov_ia_to_ic}
584579
is
585580
mem.sel_op_a {mem.clk, mem.space_id, mem.addr, mem.val, mem.rw
586-
, mem.r_in_tag, mem.w_in_tag, mem.sel_mov_ia_to_ic, mem.sel_op_cmov};
581+
, mem.r_in_tag, mem.w_in_tag, mem.sel_mov_ia_to_ic};
587582

588583
#[PERM_MAIN_MEM_B]
589-
sel_mem_op_b {clk, space_id, mem_addr_b, ib, rwb, r_in_tag, w_in_tag, sel_mov_ib_to_ic, sel_op_cmov}
584+
sel_mem_op_b {clk, space_id, mem_addr_b, ib, rwb, r_in_tag, w_in_tag, sel_mov_ib_to_ic}
590585
is
591586
mem.sel_op_b {mem.clk, mem.space_id, mem.addr, mem.val, mem.rw
592-
, mem.r_in_tag, mem.w_in_tag, mem.sel_mov_ib_to_ic, mem.sel_op_cmov};
587+
, mem.r_in_tag, mem.w_in_tag, mem.sel_mov_ib_to_ic};
593588

594589
#[PERM_MAIN_MEM_C]
595590
sel_mem_op_c {clk, space_id, mem_addr_c, ic, rwc, r_in_tag, w_in_tag}
596591
is
597592
mem.sel_op_c {mem.clk, mem.space_id, mem.addr, mem.val, mem.rw, mem.r_in_tag, mem.w_in_tag};
598593

599594
#[PERM_MAIN_MEM_D]
600-
sel_mem_op_d {clk, space_id, mem_addr_d, id, rwd, r_in_tag, w_in_tag, sel_op_cmov}
595+
sel_mem_op_d {clk, space_id, mem_addr_d, id, rwd, r_in_tag, w_in_tag}
601596
is
602597
mem.sel_op_d {mem.clk, mem.space_id, mem.addr, mem.val, mem.rw
603-
, mem.r_in_tag, mem.w_in_tag, mem.sel_op_cmov};
598+
, mem.r_in_tag, mem.w_in_tag};
604599

605600
#[PERM_MAIN_MEM_IND_ADDR_A]
606601
sel_resolve_ind_addr_a {clk, space_id, ind_addr_a, mem_addr_a}

barretenberg/cpp/pil/avm/mem.pil

+11-12
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace mem(256);
2020
pol commit r_in_tag; // Instruction memory tag ("foreign key" pointing to main.r_in_tag)
2121
pol commit w_in_tag; // Instruction memory tag ("foreign key" pointing to main.w_in_tag)
2222
pol commit skip_check_tag; // A boolean value which relaxes the consistency check in memory
23-
// trace between tag and r_in_tag. Required for CMOV opcode.
23+
// trace between tag and r_in_tag. Required for JMPI opcode.
2424

2525
// Indicator of the intermediate register pertaining to the memory operation (foreign key to main.sel_mem_op_XXX)
2626
pol commit sel_op_a;
@@ -56,11 +56,10 @@ namespace mem(256);
5656
// Selector for calldata_copy/return memory operations triggered from memory slice gadget.
5757
pol commit sel_op_slice;
5858

59-
// Selectors related to MOV/CMOV opcodes (copied from main trace for loading operation on intermediated register ia/ib)
59+
// Selectors related to MOV opcodes (copied from main trace for loading operation on intermediated register ia/ib)
6060
// Boolean constraint is performed in main trace.
6161
pol commit sel_mov_ia_to_ic;
6262
pol commit sel_mov_ib_to_ic;
63-
pol commit sel_op_cmov;
6463

6564
// Error columns
6665
pol commit tag_err; // Boolean (1 if r_in_tag != tag is detected)
@@ -91,7 +90,7 @@ namespace mem(256);
9190
sel_mem = SEL_DIRECT_MEM_OP_A + SEL_DIRECT_MEM_OP_B + SEL_DIRECT_MEM_OP_C + SEL_DIRECT_MEM_OP_D +
9291
sel_resolve_ind_addr_a + sel_resolve_ind_addr_b + sel_resolve_ind_addr_c + sel_resolve_ind_addr_d
9392
+ sel_op_slice;
94-
93+
9594
// Maximum one memory operation enabled per row
9695
sel_mem * (sel_mem - 1) = 0; // TODO: might be infered by the main trace
9796

@@ -145,11 +144,11 @@ namespace mem(256);
145144
// Optimization: We removed the term (1 - main.sel_first)
146145
#[MEM_LAST_ACCESS_DELIMITER]
147146
(1 - lastAccess) * (glob_addr' - glob_addr) = 0;
148-
147+
149148
// We need: lastAccess == 1 ==> glob_addr' > glob_addr
150149
// The above implies: glob_addr' == glob_addr ==> lastAccess == 0
151150
// This condition does not apply on the last row.
152-
151+
153152
// In addition, we need glob_addr' == glob_addr ==> tsp' > tsp
154153
// For all rows pertaining to the memory trace (sel_mem == 1) except the last one,
155154
// i.e., when sel_rng_chk == 1, we compute the difference:
@@ -166,11 +165,11 @@ namespace mem(256);
166165
// This condition does not apply on the last row.
167166
// Note: in barretenberg, a shifted polynomial will be 0 on the last row (shift is not cyclic)
168167
// Note2: in barretenberg, if a poynomial is shifted, its non-shifted equivalent must be 0 on the first row
169-
168+
170169
// Optimization: We removed the term (1 - main.sel_first) and (1 - last)
171170
#[MEM_READ_WRITE_VAL_CONSISTENCY]
172171
(1 - lastAccess) * (1 - rw') * (val' - val) = 0;
173-
172+
174173
// lastAccess == 0 && rw' == 0 ==> tag == tag'
175174
// Optimization: We removed the term (1 - main.sel_first) and (1 - last)
176175
#[MEM_READ_WRITE_TAG_CONSISTENCY]
@@ -183,9 +182,9 @@ namespace mem(256);
183182
lastAccess * (1 - rw') * val' = 0;
184183

185184
// TODO: Verfiy that skip_check_tag cannot be enabled maliciously by the prover.
186-
// Skip check tag enabled for some MOV/CMOV opcodes and RETURN opcode (sel_op_slice)
185+
// Skip check tag enabled for some MOV opcodes and RETURN opcode (sel_op_slice)
187186
#[SKIP_CHECK_TAG]
188-
skip_check_tag = sel_op_cmov * (sel_op_d + sel_op_a * (1-sel_mov_ia_to_ic) + sel_op_b * (1-sel_mov_ib_to_ic)) + sel_op_slice;
187+
skip_check_tag = sel_op_slice;
189188

190189
// Memory tag consistency check for load operations, i.e., rw == 0.
191190
// We want to prove that r_in_tag == tag <==> tag_err == 0
@@ -200,7 +199,7 @@ namespace mem(256);
200199
// instead of (r_in_tag - tag)^(-1) as this allows to store zero by default (i.e., when tag_err == 0).
201200
// The new column one_min_inv is set to 1 - (r_in_tag - tag)^(-1) when tag_err == 1
202201
// but must be set to 0 when tags are matching and tag_err = 0
203-
// Relaxation: This relation is relaxed when skip_check_tag is enabled or for
202+
// Relaxation: This relation is relaxed when skip_check_tag is enabled or for
204203
// uninitialized memory, i.e. tag == 0.
205204
#[MEM_IN_TAG_CONSISTENCY_1]
206205
tag * (1 - skip_check_tag) * (1 - rw) * ((r_in_tag - tag) * (1 - one_min_inv) - tag_err) = 0;
@@ -251,7 +250,7 @@ namespace mem(256);
251250
sel_op_poseidon_write_c * (r_in_tag - constants.MEM_TAG_FF) = 0; // Only write elements of type FF
252251
sel_op_poseidon_write_d * (r_in_tag - constants.MEM_TAG_FF) = 0; // Only write elements of type FF
253252

254-
//====== MOV/CMOV Opcode Tag Constraint =====================================
253+
//====== MOV Opcode Tag Constraint =====================================
255254
// The following constraint ensures that the r_in_tag is set to tag for
256255
// the load operation pertaining to Ia resp. Ib.
257256
// The permutation check #[PERM_MAIN_MEM_A/B] guarantees that the r_in_tag

barretenberg/cpp/src/barretenberg/vm/avm/generated/circuit_builder.cpp

-2
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,6 @@ AvmCircuitBuilder::ProverPolynomials AvmCircuitBuilder::compute_polynomials() co
239239
polys.main_sel_op_calldata_copy.set_if_valid_index(i, rows[i].main_sel_op_calldata_copy);
240240
polys.main_sel_op_cast.set_if_valid_index(i, rows[i].main_sel_op_cast);
241241
polys.main_sel_op_chain_id.set_if_valid_index(i, rows[i].main_sel_op_chain_id);
242-
polys.main_sel_op_cmov.set_if_valid_index(i, rows[i].main_sel_op_cmov);
243242
polys.main_sel_op_dagasleft.set_if_valid_index(i, rows[i].main_sel_op_dagasleft);
244243
polys.main_sel_op_div.set_if_valid_index(i, rows[i].main_sel_op_div);
245244
polys.main_sel_op_ecadd.set_if_valid_index(i, rows[i].main_sel_op_ecadd);
@@ -323,7 +322,6 @@ AvmCircuitBuilder::ProverPolynomials AvmCircuitBuilder::compute_polynomials() co
323322
polys.mem_sel_op_a.set_if_valid_index(i, rows[i].mem_sel_op_a);
324323
polys.mem_sel_op_b.set_if_valid_index(i, rows[i].mem_sel_op_b);
325324
polys.mem_sel_op_c.set_if_valid_index(i, rows[i].mem_sel_op_c);
326-
polys.mem_sel_op_cmov.set_if_valid_index(i, rows[i].mem_sel_op_cmov);
327325
polys.mem_sel_op_d.set_if_valid_index(i, rows[i].mem_sel_op_d);
328326
polys.mem_sel_op_poseidon_read_a.set_if_valid_index(i, rows[i].mem_sel_op_poseidon_read_a);
329327
polys.mem_sel_op_poseidon_read_b.set_if_valid_index(i, rows[i].mem_sel_op_poseidon_read_b);

0 commit comments

Comments
 (0)