Skip to content

Commit c5008bd

Browse files
authored
feat(avm)!: memory multi permutations (#16586)
# Multi permutation builder (tracegen) This component solves the following problem. Suppose you have `n` permutations (P1, ..., Pn) such that * The destination tuple columns (and therefore table) are the same for all of them (e.g., mem.address, mem.value). * There is a "global" destination table selector (mem.sel) * The source selectors are (potentially) different, and **already set** (e.g., execution.sel_addressing_base, execution.sel_addressing_indirect_0..., sha256.mem_op_1...) * The destination selectors are different, but UNSET. E.g., mem.sel_addressing_base, mem.sel_addressing_indirect_0...) You want to find, for each source row of each permutation, a row in the destination table and set the right destination selector. For the next source row or permutation, you should not be able to consider the "already used" destination rows, because this is a permutation. ## Solution The multipermutation builder is templated over a list of permutation settings. It builds an index of the destination table, and basically applies the algorithm mentioned above. ## Example in this PR For this PR, I made the required changes for Addressing, Keccak(mem) and Sha256. Note, especially for sha, that we didn't need separate SOURCE selectors because these are separate permutation instances. ## Downside All of this gets currently done in one thread, and is also centralized in the memory trace builder (as opposed to the sources). This might be possible to change later.
2 parents 33aaffd + 57dbfe1 commit c5008bd

25 files changed

+957
-725
lines changed

barretenberg/cpp/pil/vm2/execution/addressing.pil

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,9 @@ pol NUM_RELATIVE_E = 1 - sel_do_base_check;
128128
#[NUM_RELATIVE_INV_CHECK]
129129
NUM_RELATIVE_X * (NUM_RELATIVE_E * (1 - NUM_RELATIVE_Y) + NUM_RELATIVE_Y) - 1 + NUM_RELATIVE_E = 0;
130130

131-
// FIXME: this should eventually be a permutation.
132131
#[BASE_ADDRESS_FROM_MEMORY]
133132
sel_do_base_check { precomputed.clk, context_id, /*address=*/precomputed.zero, /*value=*/base_address_val, /*tag=*/base_address_tag, /*rw=*/precomputed.zero/*(read)*/ }
134-
in
135-
memory.sel { memory.clk, memory.space_id, memory.address, memory.value, memory.tag, memory.rw };
133+
is memory.sel_addressing_base { memory.clk, memory.space_id, memory.address, memory.value, memory.tag, memory.rw };
136134

137135
// This error will be true iff the base address is not valid AND we did actually check it.
138136
pol commit sel_base_address_failure;
@@ -259,30 +257,28 @@ sel_should_apply_indirection[6] = SEL_OP_IS_INDIRECT_EFFECTIVE_6_ * (1 - sel_rel
259257
pol commit rop_tag[7];
260258

261259
// If indirection is applied, we need to lookup the value from memory.
262-
// If sel_should_apply_indirection is 1, then we know the address is valid therefore we can make the lookups.
263-
// TODO: complete these lookups once we get memory done. In particular, clk and space id.
264-
// FIXME: these should eventually be permutations.
260+
// If sel_should_apply_indirection is 1, then we know the address is valid therefore we can make the permutations.
265261
#[INDIRECT_FROM_MEMORY_0]
266262
sel_should_apply_indirection[0] { precomputed.clk, context_id, /*address=*/op_after_relative[0], /*value=*/rop[0], /*tag=*/rop_tag[0], /*rw=*/precomputed.zero/*(read)*/ }
267-
in memory.sel { memory.clk, memory.space_id, memory.address, memory.value, memory.tag, memory.rw };
263+
is memory.sel_addressing_indirect[0] { memory.clk, memory.space_id, memory.address, memory.value, memory.tag, memory.rw };
268264
#[INDIRECT_FROM_MEMORY_1]
269265
sel_should_apply_indirection[1] { precomputed.clk, context_id, /*address=*/op_after_relative[1], /*value=*/rop[1], /*tag=*/rop_tag[1], /*rw=*/precomputed.zero/*(read)*/ }
270-
in memory.sel { memory.clk, memory.space_id, memory.address, memory.value, memory.tag, memory.rw };
266+
is memory.sel_addressing_indirect[1] { memory.clk, memory.space_id, memory.address, memory.value, memory.tag, memory.rw };
271267
#[INDIRECT_FROM_MEMORY_2]
272268
sel_should_apply_indirection[2] { precomputed.clk, context_id, /*address=*/op_after_relative[2], /*value=*/rop[2], /*tag=*/rop_tag[2], /*rw=*/precomputed.zero/*(read)*/ }
273-
in memory.sel { memory.clk, memory.space_id, memory.address, memory.value, memory.tag, memory.rw };
269+
is memory.sel_addressing_indirect[2] { memory.clk, memory.space_id, memory.address, memory.value, memory.tag, memory.rw };
274270
#[INDIRECT_FROM_MEMORY_3]
275271
sel_should_apply_indirection[3] { precomputed.clk, context_id, /*address=*/op_after_relative[3], /*value=*/rop[3], /*tag=*/rop_tag[3], /*rw=*/precomputed.zero/*(read)*/ }
276-
in memory.sel { memory.clk, memory.space_id, memory.address, memory.value, memory.tag, memory.rw };
272+
is memory.sel_addressing_indirect[3] { memory.clk, memory.space_id, memory.address, memory.value, memory.tag, memory.rw };
277273
#[INDIRECT_FROM_MEMORY_4]
278274
sel_should_apply_indirection[4] { precomputed.clk, context_id, /*address=*/op_after_relative[4], /*value=*/rop[4], /*tag=*/rop_tag[4], /*rw=*/precomputed.zero/*(read)*/ }
279-
in memory.sel { memory.clk, memory.space_id, memory.address, memory.value, memory.tag, memory.rw };
275+
is memory.sel_addressing_indirect[4] { memory.clk, memory.space_id, memory.address, memory.value, memory.tag, memory.rw };
280276
#[INDIRECT_FROM_MEMORY_5]
281277
sel_should_apply_indirection[5] { precomputed.clk, context_id, /*address=*/op_after_relative[5], /*value=*/rop[5], /*tag=*/rop_tag[5], /*rw=*/precomputed.zero/*(read)*/ }
282-
in memory.sel { memory.clk, memory.space_id, memory.address, memory.value, memory.tag, memory.rw };
278+
is memory.sel_addressing_indirect[5] { memory.clk, memory.space_id, memory.address, memory.value, memory.tag, memory.rw };
283279
#[INDIRECT_FROM_MEMORY_6]
284280
sel_should_apply_indirection[6] { precomputed.clk, context_id, /*address=*/op_after_relative[6], /*value=*/rop[6], /*tag=*/rop_tag[6], /*rw=*/precomputed.zero/*(read)*/ }
285-
in memory.sel { memory.clk, memory.space_id, memory.address, memory.value, memory.tag, memory.rw };
281+
is memory.sel_addressing_indirect[6] { memory.clk, memory.space_id, memory.address, memory.value, memory.tag, memory.rw };
286282

287283
// Otherwise, if indirection is not applied , we propagate the operands from the previous step.
288284
#[INDIRECT_PROPAGATION_0]

barretenberg/cpp/pil/vm2/keccak_memory.pil

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -237,13 +237,9 @@ val43 = (1 - last) * val42';
237237
#[VAL44]
238238
val44 = (1 - last) * val43';
239239

240-
// Memory permutation
241-
// TODO: Proper memory permutation (not a lookup), and introduce a specific
242-
// selector for keccak in memory sub-trace.
243240
#[SLICE_TO_MEM]
244-
sel {clk, addr, val00, tag, rw, space_id}
245-
in // TODO: replace with `is` (permutation)
246-
memory.sel { memory.clk, memory.address, memory.value, memory.tag, memory.rw, memory.space_id };
241+
sel { clk, space_id, addr, val00, tag, rw }
242+
is memory.sel_keccak { memory.clk, memory.space_id, memory.address, memory.value, memory.tag, memory.rw };
247243

248244
// Used to constrain the number of rounds in keccakf1600.pil through the slice_write lookup.
249245
pol commit num_rounds;

barretenberg/cpp/pil/vm2/memory.pil

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,49 @@ pol commit tag;
1010
pol commit rw;
1111
pol commit space_id;
1212

13+
// Permutation selectors (addressing.pil).
14+
pol commit sel_addressing_base;
15+
pol commit sel_addressing_indirect[7];
16+
sel_addressing_base * (1 - sel_addressing_base) = 0;
17+
sel_addressing_indirect[0] * (1 - sel_addressing_indirect[0]) = 0;
18+
sel_addressing_indirect[1] * (1 - sel_addressing_indirect[1]) = 0;
19+
sel_addressing_indirect[2] * (1 - sel_addressing_indirect[2]) = 0;
20+
sel_addressing_indirect[3] * (1 - sel_addressing_indirect[3]) = 0;
21+
sel_addressing_indirect[4] * (1 - sel_addressing_indirect[4]) = 0;
22+
sel_addressing_indirect[5] * (1 - sel_addressing_indirect[5]) = 0;
23+
sel_addressing_indirect[6] * (1 - sel_addressing_indirect[6]) = 0;
24+
25+
// Permutation selectors (keccak_memory.pil).
26+
pol commit sel_keccak;
27+
sel_keccak * (1 - sel_keccak) = 0;
28+
29+
// Permutation selectors (sha256_mem.pil).
30+
pol commit sel_sha256_read;
31+
pol commit sel_sha256_op[8];
32+
sel_sha256_read * (1 - sel_sha256_read) = 0;
33+
sel_sha256_op[0] * (1 - sel_sha256_op[0]) = 0;
34+
sel_sha256_op[1] * (1 - sel_sha256_op[1]) = 0;
35+
sel_sha256_op[2] * (1 - sel_sha256_op[2]) = 0;
36+
sel_sha256_op[3] * (1 - sel_sha256_op[3]) = 0;
37+
sel_sha256_op[4] * (1 - sel_sha256_op[4]) = 0;
38+
sel_sha256_op[5] * (1 - sel_sha256_op[5]) = 0;
39+
sel_sha256_op[6] * (1 - sel_sha256_op[6]) = 0;
40+
sel_sha256_op[7] * (1 - sel_sha256_op[7]) = 0;
41+
42+
// Permutation consistency.
43+
/*
44+
sel = // Addressing.
45+
sel_addressing_base
46+
+ sel_addressing_indirect[0] + sel_addressing_indirect[1] + sel_addressing_indirect[2] + sel_addressing_indirect[3]
47+
+ sel_addressing_indirect[4] + sel_addressing_indirect[5] + sel_addressing_indirect[6]
48+
// Keccak.
49+
+ sel_keccak
50+
// Sha256.
51+
+ sel_sha256_read
52+
+ sel_sha256_op[0] + sel_sha256_op[1] + sel_sha256_op[2] + sel_sha256_op[3]
53+
+ sel_sha256_op[4] + sel_sha256_op[5] + sel_sha256_op[6] + sel_sha256_op[7];
54+
*/
55+
1356
#[skippable_if]
1457
sel = 0;
1558

barretenberg/cpp/pil/vm2/sha256_mem.pil

Lines changed: 89 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ include "execution.pil";
1515
* sha256.start { sha256.execution_clk, sha256.space_id, sha256.output_addr, sha256.state_addr, sha256.input_addr, sha256.err };
1616
*
1717
* This trace reads and writes the following:
18-
* (1) Read State: From { state_addr, state_addr + 1, ..., state_addr + 7 }
18+
* (1) Read State: From { state_addr, state_addr + 1, ..., state_addr + 7 }
1919
* (2) Read Input: From { input_addr, input_addr + 1, ..., input_addr + 15 }
2020
* (3) Write Output: From { output_addr, output_addr + 1, ..., output_addr + 7 }
2121
* All values operated on in this subtrace are validated to be U32.
2222
* This subtrace has a mix of a vertical and horizontal memory accesses.
23-
* The State and Outputs are written horizontally (i.e. there are 8 columns), this means that
23+
* The State and Outputs are written horizontally (i.e. there are 8 columns), this means that
2424
* they are loaded in "parallel" and therefore they form their own temporality groups (for the purpose of any errors)
2525
* The Input is "mixed" into the hash at each round (from round 1 to 16), this means the inputs are loaded in a single column and the i-th input
2626
* is loaded in the i-th row of computation. Therefore, a single load is a temporality group (for the purpose of errors).
@@ -90,7 +90,7 @@ include "execution.pil";
9090
*/
9191

9292
namespace sha256;
93-
93+
9494
pol commit sel;
9595
sel * (1 - sel) = 0;
9696

@@ -117,7 +117,7 @@ namespace sha256;
117117
#[LATCH_HAS_SEL_ON] // sel = 1 when latch = 1, sel = 0 at first row because of shift relations
118118
latch * (1 - sel) = 0;
119119
// This is now guaranteed to be mututally exclusive because of the above condition and since sel = 0 at row = 0 (since it has shifts)
120-
pol LATCH_CONDITION = latch + precomputed.first_row;
120+
pol LATCH_CONDITION = latch + precomputed.first_row;
121121

122122
#[START_AFTER_LAST]
123123
sel' * (start' - LATCH_CONDITION) = 0;
@@ -177,7 +177,7 @@ namespace sha256;
177177
// Memory Operations: State and Output
178178
////////////////////////////////////////////////////////
179179
// Since reading the hash state and writing the hash outputs have similar "shapes" (i.e. 8 columns)
180-
// and occur at different "times" (the start and end (latch) respectively), we define a set of
180+
// and occur at different "times" (the start and end (latch) respectively), we define a set of
181181
// shared columns that we can re-use between the two. This saves us ~24 columns and 8 permutations.
182182

183183
pol commit memory_address[8]; // Addresses to read from or write to
@@ -230,101 +230,100 @@ namespace sha256;
230230
// flag that accounts for this
231231
pol commit rw;
232232
rw = OUTPUT_WRITE_CONDITION;
233-
// TODO: These need to be changed to permutations once we have the custom permutation selectors impl
234233
#[MEM_OP_0]
235-
sel_mem_state_or_output {
236-
execution_clk, memory_address[0],
237-
memory_register[0], memory_tag[0],
238-
space_id, /*rw=*/ rw
239-
} in
240-
memory.sel {
241-
memory.clk, memory.address,
242-
memory.value, memory.tag,
243-
memory.space_id, memory.rw
234+
sel_mem_state_or_output {
235+
execution_clk, space_id,
236+
memory_address[0], memory_register[0],
237+
memory_tag[0], /*rw=*/ rw
238+
} is
239+
memory.sel_sha256_op[0] {
240+
memory.clk, memory.space_id,
241+
memory.address, memory.value,
242+
memory.tag, memory.rw
244243
};
245244

246245
#[MEM_OP_1]
247-
sel_mem_state_or_output {
248-
execution_clk, memory_address[1],
249-
memory_register[1], memory_tag[1],
250-
space_id, /*rw=*/ rw
251-
} in
252-
memory.sel {
253-
memory.clk, memory.address,
254-
memory.value, memory.tag,
255-
memory.space_id, memory.rw
246+
sel_mem_state_or_output {
247+
execution_clk, space_id,
248+
memory_address[1], memory_register[1],
249+
memory_tag[1], /*rw=*/ rw
250+
} is
251+
memory.sel_sha256_op[1] {
252+
memory.clk, memory.space_id,
253+
memory.address, memory.value,
254+
memory.tag, memory.rw
256255
};
257256

258257
#[MEM_OP_2]
259-
sel_mem_state_or_output {
260-
execution_clk, memory_address[2],
261-
memory_register[2], memory_tag[2],
262-
space_id, /*rw=*/ rw
263-
} in
264-
memory.sel {
265-
memory.clk, memory.address,
266-
memory.value, memory.tag,
267-
memory.space_id, memory.rw
258+
sel_mem_state_or_output {
259+
execution_clk, space_id,
260+
memory_address[2], memory_register[2],
261+
memory_tag[2], /*rw=*/ rw
262+
} is
263+
memory.sel_sha256_op[2] {
264+
memory.clk, memory.space_id,
265+
memory.address, memory.value,
266+
memory.tag, memory.rw
268267
};
269268

270269
#[MEM_OP_3]
271-
sel_mem_state_or_output {
272-
execution_clk, memory_address[3],
273-
memory_register[3], memory_tag[3],
274-
space_id, /*rw=*/ rw
275-
} in
276-
memory.sel {
277-
memory.clk, memory.address,
278-
memory.value, memory.tag,
279-
memory.space_id, memory.rw
270+
sel_mem_state_or_output {
271+
execution_clk, space_id,
272+
memory_address[3], memory_register[3],
273+
memory_tag[3], /*rw=*/ rw
274+
} is
275+
memory.sel_sha256_op[3] {
276+
memory.clk, memory.space_id,
277+
memory.address, memory.value,
278+
memory.tag, memory.rw
280279
};
281280

282281
#[MEM_OP_4]
283-
sel_mem_state_or_output {
284-
execution_clk, memory_address[4],
285-
memory_register[4], memory_tag[4],
286-
space_id, /*rw=*/ rw
287-
} in
288-
memory.sel {
289-
memory.clk, memory.address,
290-
memory.value, memory.tag,
291-
memory.space_id, memory.rw
282+
sel_mem_state_or_output {
283+
execution_clk, space_id,
284+
memory_address[4], memory_register[4],
285+
memory_tag[4], /*rw=*/ rw
286+
} is
287+
memory.sel_sha256_op[4] {
288+
memory.clk, memory.space_id,
289+
memory.address, memory.value,
290+
memory.tag, memory.rw
292291
};
293292

294293
#[MEM_OP_5]
295-
sel_mem_state_or_output {
296-
execution_clk, memory_address[5],
297-
memory_register[5], memory_tag[5],
298-
space_id, /*rw=*/ rw
299-
} in
300-
memory.sel {
301-
memory.clk, memory.address,
302-
memory.value, memory.tag,
303-
memory.space_id, memory.rw
294+
sel_mem_state_or_output {
295+
execution_clk, space_id,
296+
memory_address[5], memory_register[5],
297+
memory_tag[5], /*rw=*/ rw
298+
} is
299+
memory.sel_sha256_op[5] {
300+
memory.clk, memory.space_id,
301+
memory.address, memory.value,
302+
memory.tag, memory.rw
304303
};
305304

306305
#[MEM_OP_6]
307-
sel_mem_state_or_output {
308-
execution_clk, memory_address[6],
309-
memory_register[6], memory_tag[6],
310-
space_id, /*rw=*/ rw
311-
} in
312-
memory.sel {
313-
memory.clk, memory.address,
314-
memory.value, memory.tag,
315-
memory.space_id, memory.rw
306+
sel_mem_state_or_output {
307+
execution_clk, space_id,
308+
memory_address[6], memory_register[6],
309+
memory_tag[6], /*rw=*/ rw
310+
} is
311+
memory.sel_sha256_op[6] {
312+
memory.clk, memory.space_id,
313+
memory.address, memory.value,
314+
memory.tag, memory.rw
316315
};
317316

318317
#[MEM_OP_7]
319-
sel_mem_state_or_output {
320-
execution_clk, memory_address[7],
321-
memory_register[7], memory_tag[7],
322-
space_id, /*rw=*/ rw
323-
} in
324-
memory.sel {
325-
memory.clk, memory.address,
326-
memory.value, memory.tag,
327-
memory.space_id, memory.rw
318+
sel_mem_state_or_output {
319+
execution_clk, space_id,
320+
memory_address[7], memory_register[7],
321+
memory_tag[7], /*rw=*/ rw
322+
} is
323+
memory.sel_sha256_op[7] {
324+
memory.clk, memory.space_id,
325+
memory.address, memory.value,
326+
memory.tag, memory.rw
328327
};
329328

330329
////////////////////////////////////////////////
@@ -345,7 +344,7 @@ namespace sha256;
345344
pol BATCHED_TAG_CHECK = 2**0 * STATE_TAG_DIFF_0 + 2**3 * STATE_TAG_DIFF_1
346345
+ 2**6 * STATE_TAG_DIFF_2 + 2**9 * STATE_TAG_DIFF_3
347346
+ 2**12 * STATE_TAG_DIFF_4 + 2**15 * STATE_TAG_DIFF_5
348-
+ 2**18 * STATE_TAG_DIFF_6 + 2**21 * STATE_TAG_DIFF_7;
347+
+ 2**18 * STATE_TAG_DIFF_6 + 2**21 * STATE_TAG_DIFF_7;
349348

350349
pol commit batch_tag_inv;
351350
#[BATCH_ZERO_CHECK_READ]
@@ -385,7 +384,7 @@ namespace sha256;
385384
sel * (1 - LATCH_CONDITION) * (input_rounds_rem' - (input_rounds_rem - sel_is_input_round)) = 0;
386385

387386
// Read input if we have not had an error from the previous temporality groups and this is an input round
388-
pol commit sel_read_input_from_memory;
387+
pol commit sel_read_input_from_memory;
389388
sel_read_input_from_memory = sel * (1 - mem_out_of_range_err) * sel_is_input_round * (1 - sel_invalid_state_tag_err);
390389

391390
// Put the result of the input loads in to the corresponding w value
@@ -396,15 +395,15 @@ namespace sha256;
396395
pol commit input_tag;
397396

398397
#[MEM_INPUT_READ]
399-
sel_read_input_from_memory {
400-
execution_clk, input_addr,
401-
input, input_tag,
402-
space_id, /*rw=*/ precomputed.zero
403-
} in
404-
memory.sel {
405-
memory.clk, memory.address,
406-
memory.value, memory.tag,
407-
memory.space_id, memory.rw
398+
sel_read_input_from_memory {
399+
execution_clk, space_id,
400+
input_addr, input,
401+
input_tag, /*rw=*/ precomputed.zero
402+
} is
403+
memory.sel_sha256_read {
404+
memory.clk, memory.space_id,
405+
memory.address, memory.value,
406+
memory.tag, memory.rw
408407
};
409408

410409
////////////////////////////////////////////////
@@ -418,7 +417,7 @@ namespace sha256;
418417
pol commit sel_invalid_input_row_tag_err;
419418
sel_invalid_input_row_tag_err * (1 - sel_invalid_input_row_tag_err) = 0;
420419

421-
pol INPUT_TAG_DIFF = sel_read_input_from_memory * (input_tag - constants.MEM_TAG_U32);
420+
pol INPUT_TAG_DIFF = sel_read_input_from_memory * (input_tag - constants.MEM_TAG_U32);
422421
pol commit input_tag_diff_inv;
423422
// Iff INPUT_TAG_DIFF != 0, sel_invalid_input_row_tag_err = 1
424423
#[INPUT_TAG_DIFF_CHECK]

0 commit comments

Comments
 (0)