Skip to content

Commit d6a30af

Browse files
authored
Merge pull request #469 from AztecProtocol/jean/precomputed-granular
feat(avm): granular destination selectors for precomputed subtraces
2 parents bbab809 + cf0fc39 commit d6a30af

85 files changed

Lines changed: 8622 additions & 9324 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

barretenberg/cpp/pil/vm2/bitwise.pil

Lines changed: 353 additions & 211 deletions
Large diffs are not rendered by default.

barretenberg/cpp/pil/vm2/constants_gen.pil

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,8 @@ namespace constants;
6060
pol AVM_DYN_GAS_ID_CALLDATACOPY = 1;
6161
pol AVM_DYN_GAS_ID_RETURNDATACOPY = 2;
6262
pol AVM_DYN_GAS_ID_TORADIX = 4;
63-
pol AVM_DYN_GAS_ID_BITWISE = 8;
64-
pol AVM_DYN_GAS_ID_EMITPUBLICLOG = 16;
65-
pol AVM_DYN_GAS_ID_SSTORE = 32;
63+
pol AVM_DYN_GAS_ID_EMITPUBLICLOG = 8;
64+
pol AVM_DYN_GAS_ID_SSTORE = 16;
6665
pol AVM_EXEC_OP_ID_GETENVVAR = 1;
6766
pol AVM_EXEC_OP_ID_MOV = 2;
6867
pol AVM_EXEC_OP_ID_JUMP = 4;

barretenberg/cpp/pil/vm2/execution.pil

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,6 @@ include "opcodes/sstore.pil";
131131
* - #[EXEC_SPEC_READ] -> precomputed: read execution spec (gas costs, register info, subtrace dispatch).
132132
*
133133
* Gas (dynamic):
134-
* - #[DYN_L2_FACTOR_BITWISE] -> precomputed: tag-based byte length for bitwise gas.
135134
* - #[CHECK_RADIX_GT_256] -> gt: compare radix > 256.
136135
* - #[GET_P_LIMBS] -> precomputed: lookup num_p_limbs for to_radix gas.
137136
* - #[GET_MAX_LIMBS] -> gt: compare num_limbs > num_p_limbs.
@@ -586,38 +585,26 @@ pol commit dyn_gas_id; // from precomputed.pil (#[EXEC_SPEC_READ])
586585
pol commit sel_gas_calldata_copy; // @boolean
587586
pol commit sel_gas_returndata_copy; // @boolean
588587
pol commit sel_gas_to_radix; // @boolean
589-
pol commit sel_gas_bitwise; // @boolean
590588
pol commit sel_gas_emit_public_log; // @boolean
591589
pol commit sel_gas_sstore; // @boolean
592590

593591
// Boolean constraints
594592
sel_gas_calldata_copy * (1 - sel_gas_calldata_copy) = 0;
595593
sel_gas_returndata_copy * (1 - sel_gas_returndata_copy) = 0;
596594
sel_gas_to_radix * (1 - sel_gas_to_radix) = 0;
597-
sel_gas_bitwise * (1 - sel_gas_bitwise) = 0;
598595
sel_gas_emit_public_log * (1 - sel_gas_emit_public_log) = 0;
599596
sel_gas_sstore * (1 - sel_gas_sstore) = 0;
600597

601598
#[DYN_GAS_ID_DECOMPOSITION]
602599
sel_gas_calldata_copy * constants.AVM_DYN_GAS_ID_CALLDATACOPY +
603600
sel_gas_returndata_copy * constants.AVM_DYN_GAS_ID_RETURNDATACOPY +
604601
sel_gas_to_radix * constants.AVM_DYN_GAS_ID_TORADIX +
605-
sel_gas_bitwise * constants.AVM_DYN_GAS_ID_BITWISE +
606602
sel_gas_emit_public_log * constants.AVM_DYN_GAS_ID_EMITPUBLICLOG +
607603
sel_gas_sstore * constants.AVM_DYN_GAS_ID_SSTORE
608604
// We force the selectors to be 0 if we failed before gas checks
609605
// or when we are not in the execution subtrace.
610606
= sel_check_gas * dyn_gas_id;
611607

612-
//////////////////////////////////////////
613-
// Bitwise, Dynamic L2 Gas Calculation
614-
//////////////////////////////////////////
615-
// For bitwise, the tag determines the dynamic_l2_gas_factor
616-
#[DYN_L2_FACTOR_BITWISE]
617-
sel_gas_bitwise { mem_tag_reg[0], dynamic_l2_gas_factor }
618-
in
619-
precomputed.sel_tag_parameters { precomputed.idx, precomputed.tag_byte_length };
620-
621608
//////////////////////////////////////////
622609
// ToRadixBE, Dynamic L2 Gas Calculation
623610
//////////////////////////////////////////
@@ -734,9 +721,9 @@ sel_gas_emit_public_log * (/*log_size=*/ register[0] - dynamic_da_gas_factor) =
734721
#[DYN_DA_GAS_IS_ZERO]
735722
(1 - sel_gas_sstore - sel_gas_emit_public_log) * dynamic_da_gas_factor = 0;
736723

737-
// Except BITWISE, TORADIX, EMITPUBLICLOG, CALLDATACOPY, RETURNDATACOPY, all opcodes have a zero dynamic l2 gas.
724+
// Except TORADIX, EMITPUBLICLOG, CALLDATACOPY, RETURNDATACOPY, all opcodes have a zero dynamic l2 gas.
738725
#[DYN_L2_GAS_IS_ZERO]
739-
(1 - sel_gas_bitwise - sel_gas_to_radix - sel_gas_emit_public_log - sel_gas_calldata_copy - sel_gas_returndata_copy) * dynamic_l2_gas_factor = 0;
726+
(1 - sel_gas_to_radix - sel_gas_emit_public_log - sel_gas_calldata_copy - sel_gas_returndata_copy) * dynamic_l2_gas_factor = 0;
740727

741728
/**************************************************************************************************
742729
* Temporality group 5: Opcode execution
@@ -1087,11 +1074,11 @@ sel_exec_dispatch_bitwise {
10871074
register[0], mem_tag_reg[0],
10881075
register[1], mem_tag_reg[1],
10891076
register[2], mem_tag_reg[2]
1090-
} in bitwise.start {
1077+
} in bitwise.sel_bitwise_opcode {
10911078
bitwise.op_id, bitwise.err,
1092-
bitwise.acc_ia, bitwise.tag_a,
1093-
bitwise.acc_ib, bitwise.tag_b,
1094-
bitwise.acc_ic, bitwise.tag_c
1079+
bitwise.ia, bitwise.tag_a,
1080+
bitwise.ib, bitwise.tag_b,
1081+
bitwise.ic, bitwise.tag_c
10951082
};
10961083

10971084
// CAST DISPATCHING

barretenberg/cpp/pil/vm2/keccakf1600.pil

Lines changed: 282 additions & 474 deletions
Large diffs are not rendered by default.

barretenberg/cpp/pil/vm2/memory.pil

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -283,11 +283,11 @@ diff = limb[0] + limb[1] * 2**16 + limb[2] * 2**32;
283283
// Range check each limb to 16 bits, proving diff is in [0, 2^48).
284284
// Only checked on active non-last rows (sel_rng_chk gating).
285285
#[RANGE_CHECK_LIMB_0]
286-
sel_rng_chk { limb[0] } in precomputed.sel_range_16 { precomputed.idx };
286+
sel_rng_chk { limb[0] } in precomputed.sel_range_16_active { precomputed.idx };
287287
#[RANGE_CHECK_LIMB_1]
288-
sel_rng_chk { limb[1] } in precomputed.sel_range_16 { precomputed.idx };
288+
sel_rng_chk { limb[1] } in precomputed.sel_range_16_active { precomputed.idx };
289289
#[RANGE_CHECK_LIMB_2]
290-
sel_rng_chk { limb[2] } in precomputed.sel_range_16 { precomputed.idx };
290+
sel_rng_chk { limb[2] } in precomputed.sel_range_16_active { precomputed.idx };
291291

292292
////////////////////////////////////////////////
293293
// Memory Initialization

barretenberg/cpp/pil/vm2/precomputed.pil

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
*
2424
* Valid selectors determining the row ranges:
2525
* - sel_range_8: rows [0, 2^8) — 8-bit range checks and power-of-2 lookups
26-
* - sel_range_16: rows [0, 2^16) — 16-bit range checks
26+
* - sel_range_16: rows [0, 2^16) — 16-bit range checks (used also for bitwise and addressing gas lookups)
2727
* - sel_sha256_compression: SHA-256 round constants
2828
* - sel_tag_parameters: memory tag metadata (byte length, max bits, max value)
2929
* - sel_to_radix_p_limb_counts: safe limb counts for radix decomposition
@@ -33,6 +33,13 @@
3333
* - sel_phase: transaction execution phase properties
3434
* - sel_keccak: keccak round constants
3535
*
36+
* The above selectors are fixed (precomputed) and do not change per transaction/program. For optimization reasons,
37+
* we introduce committed granular destination selectors for:
38+
* - 16-bit range check: sel_range_16_active
39+
* - bitwise: sel_bitwise
40+
* - addressing gas: sel_addressing_gas
41+
* Therefore, the lookup's log-derivative inverse is materialised over the used entries instead of the full 2^16-row table.
42+
*
3643
* The columns are organized into several logical groups:
3744
*
3845
* 1. General-purpose columns: row index, zero column, first-row selector.
@@ -112,6 +119,11 @@ pol constant first_row;
112119
pol constant sel_range_8; // 1 in the first 2^8 rows [0, 2^8)
113120
pol constant sel_range_16; // 1 in the first 2^16 rows [0, 2^16)
114121

122+
// Granular destination selector for the 16-bit range-check lookups.
123+
pol commit sel_range_16_active; // @boolean
124+
sel_range_16_active * (1 - sel_range_16_active) = 0;
125+
// Relation #[GRANULAR_SELECTORS_ON_RANGE_16] ensures that `sel_range_16_active == 1 ==> sel_range_16 == 1`.
126+
115127
// ===== Section 3: Bitwise lookup tables =====
116128
// Precomputed AND/OR/XOR results for all pairs of 8-bit inputs (256x256 = 65536 rows).
117129
// Row index = (input_a << 8) | input_b, so all 65536 pairs are covered.
@@ -137,6 +149,11 @@ pol constant bitwise_output_and; // output = a AND b.
137149
pol constant bitwise_output_or; // output = a OR b.
138150
pol constant bitwise_output_xor; // output = a XOR b.
139151

152+
// Granular destination selector for the byte-operations lookup (#[BYTE_OPERATIONS]).
153+
pol commit sel_bitwise; // @boolean
154+
sel_bitwise * (1 - sel_bitwise) = 0;
155+
// Relation #[GRANULAR_SELECTORS_ON_RANGE_16] ensures that `sel_bitwise == 1 ==> sel_range_16 == 1`.
156+
140157
// ===== Section 4: Power of 2 table =====
141158
// Lookup table for 2^idx, populated for idx in [0, 255]. Note the [0, 255] property is guaranteed by the
142159
// use of sel_range_8 as the dst selector (since idx actually goes from [0, 65536]).
@@ -342,7 +359,7 @@ pol constant sel_op_is_address[5];
342359
// => 1 indirect + 2 relative => gas = 3 + 1*3 + 2*3 = 12
343360
//
344361
// Example trace:
345-
// idx | sel_addressing_gas | addressing_gas
362+
// idx | sel_range_16 | addressing_gas
346363
// ------+--------------------+---------------
347364
// 0 | 1 | 0 (no indirect/relative operands)
348365
// 1 | 1 | 3 (operand 0 indirect)
@@ -352,7 +369,14 @@ pol constant sel_op_is_address[5];
352369
// ... | 1 | ...
353370
// 65535 | 1 | 33 (all bits above operand 4 are ignored, see addressing.pil for why this table still needs to be 16-bits)
354371

355-
pol constant sel_addressing_gas;
372+
// Granular destination selector for the addressing-gas lookup (#[ADDRESSING_GAS_READ]). This is sound
373+
// because the addressing_gas value column is populated over the entire
374+
// [0, 2^16) range (every 16-bit bitmask), so any row toggled within sel_range_16 carries a valid gas value;
375+
// a static assertion in precomputed_trace.cpp guarantees the table spans 2^16 rows.
376+
pol commit sel_addressing_gas; // @boolean
377+
sel_addressing_gas * (1 - sel_addressing_gas) = 0;
378+
// Relation #[GRANULAR_SELECTORS_ON_RANGE_16] ensures that `sel_addressing_gas == 1 ==> sel_range_16 == 1`.
379+
356380
pol constant addressing_gas;
357381

358382
// ===== Section 11: Phase table =====
@@ -444,3 +468,7 @@ pol constant is_deployer;
444468
pol constant is_class_id;
445469
pol constant is_init_hash;
446470
pol constant is_immutables_hash;
471+
472+
// Granular destination selectors must be on sel_range_16 range.
473+
#[GRANULAR_SELECTORS_ON_RANGE_16]
474+
(sel_range_16_active + sel_bitwise + sel_addressing_gas) * (1 - sel_range_16) = 0;

barretenberg/cpp/pil/vm2/range_check.pil

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,18 @@ include "precomputed.pil";
5959
*
6060
* Interactions (this gadget as source -- lookups into precomputed tables):
6161
*
62-
* | Lookup Name | Selector | LHS Tuple | RHS (precomputed) |
63-
* |----------------------|---------------------------|-----------------------------------------|---------------------------------|
64-
* | #[DYN_RNG_CHK_POW_2] | sel | { dyn_rng_chk_bits, dyn_rng_chk_pow_2 } | sel_range_8 { idx, power_of_2 } |
65-
* | #[DYN_DIFF_IS_U16] | sel | { dyn_diff } | sel_range_16 { idx } |
66-
* | #[R0_IS_U16] | sel_r0_16_bit_rng_lookup | { u16_r0 } | sel_range_16 { idx } |
67-
* | #[R1_IS_U16] | sel_r1_16_bit_rng_lookup | { u16_r1 } | sel_range_16 { idx } |
68-
* | #[R2_IS_U16] | sel_r2_16_bit_rng_lookup | { u16_r2 } | sel_range_16 { idx } |
69-
* | #[R3_IS_U16] | sel_r3_16_bit_rng_lookup | { u16_r3 } | sel_range_16 { idx } |
70-
* | #[R4_IS_U16] | sel_r4_16_bit_rng_lookup | { u16_r4 } | sel_range_16 { idx } |
71-
* | #[R5_IS_U16] | sel_r5_16_bit_rng_lookup | { u16_r5 } | sel_range_16 { idx } |
72-
* | #[R6_IS_U16] | sel_r6_16_bit_rng_lookup | { u16_r6 } | sel_range_16 { idx } |
73-
* | #[R7_IS_U16] | sel | { u16_r7 } | sel_range_16 { idx } |
62+
* | Lookup Name | Selector | LHS Tuple | RHS (precomputed) |
63+
* |----------------------|---------------------------|-----------------------------------------|-----------------------------------|
64+
* | #[DYN_RNG_CHK_POW_2] | sel | { dyn_rng_chk_bits, dyn_rng_chk_pow_2 } | sel_range_8 { idx, power_of_2 } |
65+
* | #[DYN_DIFF_IS_U16] | sel | { dyn_diff } | sel_range_16_active { idx } |
66+
* | #[R0_IS_U16] | sel_r0_16_bit_rng_lookup | { u16_r0 } | sel_range_16_active { idx } |
67+
* | #[R1_IS_U16] | sel_r1_16_bit_rng_lookup | { u16_r1 } | sel_range_16_active { idx } |
68+
* | #[R2_IS_U16] | sel_r2_16_bit_rng_lookup | { u16_r2 } | sel_range_16_active { idx } |
69+
* | #[R3_IS_U16] | sel_r3_16_bit_rng_lookup | { u16_r3 } | sel_range_16_active { idx } |
70+
* | #[R4_IS_U16] | sel_r4_16_bit_rng_lookup | { u16_r4 } | sel_range_16_active { idx } |
71+
* | #[R5_IS_U16] | sel_r5_16_bit_rng_lookup | { u16_r5 } | sel_range_16_active { idx } |
72+
* | #[R6_IS_U16] | sel_r6_16_bit_rng_lookup | { u16_r6 } | sel_range_16_active { idx } |
73+
* | #[R7_IS_U16] | sel | { u16_r7 } | sel_range_16_active { idx } |
7474
*
7575
* Soundness argument summary:
7676
* 1) #[CHECK_RECOMPOSITION] forces value = sum of (u16_r_i * 2^(16*i)) + u16_r7 * 2^(16*k),
@@ -274,7 +274,7 @@ namespace range_check;
274274
// The value of dyn_diff has to be between [0, 2^16)
275275
// To check we did not underflow we just range check it
276276
#[DYN_DIFF_IS_U16]
277-
sel { dyn_diff } in precomputed.sel_range_16 { precomputed.idx };
277+
sel { dyn_diff } in precomputed.sel_range_16_active { precomputed.idx };
278278

279279
////////////////////////////////////////////////
280280
// Cumulative Lookup Selectors and 16-bit Range Check Lookups
@@ -312,18 +312,18 @@ namespace range_check;
312312
sel_r6_16_bit_rng_lookup - CUM_LTE_128 = 0;
313313

314314
#[R0_IS_U16]
315-
sel_r0_16_bit_rng_lookup { u16_r0 } in precomputed.sel_range_16 { precomputed.idx };
315+
sel_r0_16_bit_rng_lookup { u16_r0 } in precomputed.sel_range_16_active { precomputed.idx };
316316
#[R1_IS_U16]
317-
sel_r1_16_bit_rng_lookup { u16_r1 } in precomputed.sel_range_16 { precomputed.idx };
317+
sel_r1_16_bit_rng_lookup { u16_r1 } in precomputed.sel_range_16_active { precomputed.idx };
318318
#[R2_IS_U16]
319-
sel_r2_16_bit_rng_lookup { u16_r2 } in precomputed.sel_range_16 { precomputed.idx };
319+
sel_r2_16_bit_rng_lookup { u16_r2 } in precomputed.sel_range_16_active { precomputed.idx };
320320
#[R3_IS_U16]
321-
sel_r3_16_bit_rng_lookup { u16_r3 } in precomputed.sel_range_16 { precomputed.idx };
321+
sel_r3_16_bit_rng_lookup { u16_r3 } in precomputed.sel_range_16_active { precomputed.idx };
322322
#[R4_IS_U16]
323-
sel_r4_16_bit_rng_lookup { u16_r4 } in precomputed.sel_range_16 { precomputed.idx };
323+
sel_r4_16_bit_rng_lookup { u16_r4 } in precomputed.sel_range_16_active { precomputed.idx };
324324
#[R5_IS_U16]
325-
sel_r5_16_bit_rng_lookup { u16_r5 } in precomputed.sel_range_16 { precomputed.idx };
325+
sel_r5_16_bit_rng_lookup { u16_r5 } in precomputed.sel_range_16_active { precomputed.idx };
326326
#[R6_IS_U16]
327-
sel_r6_16_bit_rng_lookup { u16_r6 } in precomputed.sel_range_16 { precomputed.idx };
327+
sel_r6_16_bit_rng_lookup { u16_r6 } in precomputed.sel_range_16_active { precomputed.idx };
328328
#[R7_IS_U16]
329-
sel { u16_r7 } in precomputed.sel_range_16 { precomputed.idx };
329+
sel { u16_r7 } in precomputed.sel_range_16_active { precomputed.idx };

0 commit comments

Comments
 (0)