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
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;
112119pol constant sel_range_8; // 1 in the first 2^8 rows [0, 2^8)
113120pol 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.
137149pol constant bitwise_output_or; // output = a OR b.
138150pol 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+
356380pol constant addressing_gas;
357381
358382// ===== Section 11: Phase table =====
@@ -444,3 +468,7 @@ pol constant is_deployer;
444468pol constant is_class_id;
445469pol constant is_init_hash;
446470pol 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;
0 commit comments