Skip to content

Commit df04e54

Browse files
authored
fix(avm)!: unconstrained reads in debuglog (#17061)
Debuglog is not connected to the memory trace, so we can't generate events. The objective of this PR is to enable that while trying not to break the MemoryInterface. I'm using type-checked casting `dynamic_cast` and adding an unconstrained read to the subclass `Memory`. Still evil, but a bit less evil than changing the interface itself. In the circuit: I re-enabled the memory selectors check, and check circuit passes with `-d` (which should trigger debug logs). Closes #16759.
1 parent d8ec40b commit df04e54

File tree

8 files changed

+128
-55
lines changed

8 files changed

+128
-55
lines changed

barretenberg/cpp/pil/vm2/memory.pil

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,6 @@ sel_to_radix_write * (1 - sel_to_radix_write) = 0;
135135
// Permutation consistency.
136136
// TODO(#16759): Enable the following relation ensuring that each memory entry is associated with a valid permutation selector.
137137
// DebugLog opcode is generating some memory entries which should not be triggered.
138-
/*
139138
#[ACTIVE_ROW_NEEDS_PERM_SELECTOR]
140139
sel = // Addressing.
141140
sel_addressing_base
@@ -165,7 +164,6 @@ sel = // Addressing.
165164
+ sel_ecc_write[0] + sel_ecc_write[1] + sel_ecc_write[2]
166165
// To Radix.
167166
+ sel_to_radix_write;
168-
*/
169167

170168
// Other boolean constraints.
171169
sel * (1 - sel) = 0; // Ensure mutual exclusivity of the permutation selectors.

barretenberg/cpp/src/barretenberg/vm2/constraining/relations/memory.test.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ TEST(MemoryConstrainingTest, MultipleEventsWithTraceGen)
140140
range_check_trace_builder.process(range_check_events, trace);
141141
memory_trace_builder.process(mem_events, trace);
142142

143+
// For the selector consistency, we need to make the read/write come from some trace.
144+
trace.visit_column(Column::memory_sel,
145+
[&](uint32_t row, const FF&) { trace.set(Column::memory_sel_register_op_0_, row, 1); });
146+
143147
check_relation<memory>(trace);
144148
check_all_interactions<MemoryTraceBuilder>(trace);
145149
}

barretenberg/cpp/src/barretenberg/vm2/generated/flavor_variables.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@ struct AvmFlavorVariables {
148148
// Need to be templated for recursive verifier
149149
template <typename FF_>
150150
using MainRelations_ = flat_tuple::tuple<
151-
152151
// Optimized Relations
153152
avm2::optimized_poseidon2_perm<FF_>,
154153
// Relations

barretenberg/cpp/src/barretenberg/vm2/generated/relations/memory.hpp

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ template <typename FF_> class memoryImpl {
1414
public:
1515
using FF = FF_;
1616

17-
static constexpr std::array<size_t, 59> SUBRELATION_PARTIAL_LENGTHS = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
18-
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
19-
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
20-
3, 4, 3, 2, 2, 5, 5, 2, 4, 4, 4, 4, 5, 3 };
17+
static constexpr std::array<size_t, 60> SUBRELATION_PARTIAL_LENGTHS = {
18+
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
19+
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 4, 3, 2, 2, 5, 5, 2, 4, 4, 4, 4, 5, 3
20+
};
2121

2222
template <typename AllEntities> inline static bool skip(const AllEntities& in)
2323
{
@@ -38,23 +38,26 @@ template <typename FF> class memory : public Relation<memoryImpl<FF>> {
3838
static constexpr const std::string_view NAME = "memory";
3939

4040
// Subrelation indices constants, to be used in tests.
41-
static constexpr size_t SR_MEM_CONTIGUOUS = 46;
42-
static constexpr size_t SR_SEL_RNG_CHK = 47;
43-
static constexpr size_t SR_GLOBAL_ADDR = 48;
44-
static constexpr size_t SR_TIMESTAMP = 49;
45-
static constexpr size_t SR_LAST_ACCESS = 50;
46-
static constexpr size_t SR_DIFF = 51;
47-
static constexpr size_t SR_DIFF_DECOMP = 52;
48-
static constexpr size_t SR_MEMORY_INIT_VALUE = 53;
49-
static constexpr size_t SR_MEMORY_INIT_TAG = 54;
50-
static constexpr size_t SR_READ_WRITE_CONSISTENCY_VALUE = 55;
51-
static constexpr size_t SR_READ_WRITE_CONSISTENCY_TAG = 56;
52-
static constexpr size_t SR_TAG_IS_FF = 57;
53-
static constexpr size_t SR_SEL_RNG_WRITE = 58;
41+
static constexpr size_t SR_ACTIVE_ROW_NEEDS_PERM_SELECTOR = 42;
42+
static constexpr size_t SR_MEM_CONTIGUOUS = 47;
43+
static constexpr size_t SR_SEL_RNG_CHK = 48;
44+
static constexpr size_t SR_GLOBAL_ADDR = 49;
45+
static constexpr size_t SR_TIMESTAMP = 50;
46+
static constexpr size_t SR_LAST_ACCESS = 51;
47+
static constexpr size_t SR_DIFF = 52;
48+
static constexpr size_t SR_DIFF_DECOMP = 53;
49+
static constexpr size_t SR_MEMORY_INIT_VALUE = 54;
50+
static constexpr size_t SR_MEMORY_INIT_TAG = 55;
51+
static constexpr size_t SR_READ_WRITE_CONSISTENCY_VALUE = 56;
52+
static constexpr size_t SR_READ_WRITE_CONSISTENCY_TAG = 57;
53+
static constexpr size_t SR_TAG_IS_FF = 58;
54+
static constexpr size_t SR_SEL_RNG_WRITE = 59;
5455

5556
static std::string get_subrelation_label(size_t index)
5657
{
5758
switch (index) {
59+
case SR_ACTIVE_ROW_NEEDS_PERM_SELECTOR:
60+
return "ACTIVE_ROW_NEEDS_PERM_SELECTOR";
5861
case SR_MEM_CONTIGUOUS:
5962
return "MEM_CONTIGUOUS";
6063
case SR_SEL_RNG_CHK:

barretenberg/cpp/src/barretenberg/vm2/generated/relations/memory_impl.hpp

Lines changed: 79 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -273,65 +273,112 @@ void memoryImpl<FF_>::accumulate(ContainerOverSubrelations& evals,
273273
(FF(1) - static_cast<View>(in.get(C::memory_sel_to_radix_write)));
274274
std::get<41>(evals) += (tmp * scaling_factor);
275275
}
276-
{
276+
{ // ACTIVE_ROW_NEEDS_PERM_SELECTOR
277277
using View = typename std::tuple_element_t<42, ContainerOverSubrelations>::View;
278-
auto tmp = static_cast<View>(in.get(C::memory_sel)) * (FF(1) - static_cast<View>(in.get(C::memory_sel)));
278+
auto tmp =
279+
(static_cast<View>(in.get(C::memory_sel)) -
280+
(static_cast<View>(in.get(C::memory_sel_addressing_base)) +
281+
static_cast<View>(in.get(C::memory_sel_addressing_indirect_0_)) +
282+
static_cast<View>(in.get(C::memory_sel_addressing_indirect_1_)) +
283+
static_cast<View>(in.get(C::memory_sel_addressing_indirect_2_)) +
284+
static_cast<View>(in.get(C::memory_sel_addressing_indirect_3_)) +
285+
static_cast<View>(in.get(C::memory_sel_addressing_indirect_4_)) +
286+
static_cast<View>(in.get(C::memory_sel_addressing_indirect_5_)) +
287+
static_cast<View>(in.get(C::memory_sel_addressing_indirect_6_)) +
288+
static_cast<View>(in.get(C::memory_sel_register_op_0_)) +
289+
static_cast<View>(in.get(C::memory_sel_register_op_1_)) +
290+
static_cast<View>(in.get(C::memory_sel_register_op_2_)) +
291+
static_cast<View>(in.get(C::memory_sel_register_op_3_)) +
292+
static_cast<View>(in.get(C::memory_sel_register_op_4_)) +
293+
static_cast<View>(in.get(C::memory_sel_register_op_5_)) +
294+
static_cast<View>(in.get(C::memory_sel_register_op_6_)) +
295+
static_cast<View>(in.get(C::memory_sel_data_copy_read)) +
296+
static_cast<View>(in.get(C::memory_sel_data_copy_write)) +
297+
static_cast<View>(in.get(C::memory_sel_get_contract_instance_exists_write)) +
298+
static_cast<View>(in.get(C::memory_sel_get_contract_instance_member_write)) +
299+
static_cast<View>(in.get(C::memory_sel_unencrypted_log_read)) +
300+
static_cast<View>(in.get(C::memory_sel_poseidon2_read_0_)) +
301+
static_cast<View>(in.get(C::memory_sel_poseidon2_read_1_)) +
302+
static_cast<View>(in.get(C::memory_sel_poseidon2_read_2_)) +
303+
static_cast<View>(in.get(C::memory_sel_poseidon2_read_3_)) +
304+
static_cast<View>(in.get(C::memory_sel_poseidon2_write_0_)) +
305+
static_cast<View>(in.get(C::memory_sel_poseidon2_write_1_)) +
306+
static_cast<View>(in.get(C::memory_sel_poseidon2_write_2_)) +
307+
static_cast<View>(in.get(C::memory_sel_poseidon2_write_3_)) +
308+
static_cast<View>(in.get(C::memory_sel_keccak)) + static_cast<View>(in.get(C::memory_sel_sha256_read)) +
309+
static_cast<View>(in.get(C::memory_sel_sha256_op_0_)) +
310+
static_cast<View>(in.get(C::memory_sel_sha256_op_1_)) +
311+
static_cast<View>(in.get(C::memory_sel_sha256_op_2_)) +
312+
static_cast<View>(in.get(C::memory_sel_sha256_op_3_)) +
313+
static_cast<View>(in.get(C::memory_sel_sha256_op_4_)) +
314+
static_cast<View>(in.get(C::memory_sel_sha256_op_5_)) +
315+
static_cast<View>(in.get(C::memory_sel_sha256_op_6_)) +
316+
static_cast<View>(in.get(C::memory_sel_sha256_op_7_)) +
317+
static_cast<View>(in.get(C::memory_sel_ecc_write_0_)) +
318+
static_cast<View>(in.get(C::memory_sel_ecc_write_1_)) +
319+
static_cast<View>(in.get(C::memory_sel_ecc_write_2_)) +
320+
static_cast<View>(in.get(C::memory_sel_to_radix_write))));
279321
std::get<42>(evals) += (tmp * scaling_factor);
280322
}
281323
{
282324
using View = typename std::tuple_element_t<43, ContainerOverSubrelations>::View;
283-
auto tmp = static_cast<View>(in.get(C::memory_last_access)) *
284-
(FF(1) - static_cast<View>(in.get(C::memory_last_access)));
325+
auto tmp = static_cast<View>(in.get(C::memory_sel)) * (FF(1) - static_cast<View>(in.get(C::memory_sel)));
285326
std::get<43>(evals) += (tmp * scaling_factor);
286327
}
287328
{
288329
using View = typename std::tuple_element_t<44, ContainerOverSubrelations>::View;
289-
auto tmp = static_cast<View>(in.get(C::memory_rw)) * (FF(1) - static_cast<View>(in.get(C::memory_rw)));
330+
auto tmp = static_cast<View>(in.get(C::memory_last_access)) *
331+
(FF(1) - static_cast<View>(in.get(C::memory_last_access)));
290332
std::get<44>(evals) += (tmp * scaling_factor);
291333
}
292334
{
293335
using View = typename std::tuple_element_t<45, ContainerOverSubrelations>::View;
336+
auto tmp = static_cast<View>(in.get(C::memory_rw)) * (FF(1) - static_cast<View>(in.get(C::memory_rw)));
337+
std::get<45>(evals) += (tmp * scaling_factor);
338+
}
339+
{
340+
using View = typename std::tuple_element_t<46, ContainerOverSubrelations>::View;
294341
auto tmp = static_cast<View>(in.get(C::memory_sel_tag_is_ff)) *
295342
(FF(1) - static_cast<View>(in.get(C::memory_sel_tag_is_ff)));
296-
std::get<45>(evals) += (tmp * scaling_factor);
343+
std::get<46>(evals) += (tmp * scaling_factor);
297344
}
298345
{ // MEM_CONTIGUOUS
299-
using View = typename std::tuple_element_t<46, ContainerOverSubrelations>::View;
346+
using View = typename std::tuple_element_t<47, ContainerOverSubrelations>::View;
300347
auto tmp = (FF(1) - static_cast<View>(in.get(C::precomputed_first_row))) *
301348
(FF(1) - static_cast<View>(in.get(C::memory_sel))) * static_cast<View>(in.get(C::memory_sel_shift));
302-
std::get<46>(evals) += (tmp * scaling_factor);
349+
std::get<47>(evals) += (tmp * scaling_factor);
303350
}
304351
{ // SEL_RNG_CHK
305-
using View = typename std::tuple_element_t<47, ContainerOverSubrelations>::View;
352+
using View = typename std::tuple_element_t<48, ContainerOverSubrelations>::View;
306353
auto tmp = (static_cast<View>(in.get(C::memory_sel_rng_chk)) -
307354
static_cast<View>(in.get(C::memory_sel)) * static_cast<View>(in.get(C::memory_sel_shift)));
308-
std::get<47>(evals) += (tmp * scaling_factor);
355+
std::get<48>(evals) += (tmp * scaling_factor);
309356
}
310357
{ // GLOBAL_ADDR
311-
using View = typename std::tuple_element_t<48, ContainerOverSubrelations>::View;
358+
using View = typename std::tuple_element_t<49, ContainerOverSubrelations>::View;
312359
auto tmp = (static_cast<View>(in.get(C::memory_global_addr)) -
313360
(static_cast<View>(in.get(C::memory_space_id)) * FF(4294967296UL) +
314361
static_cast<View>(in.get(C::memory_address))));
315-
std::get<48>(evals) += (tmp * scaling_factor);
362+
std::get<49>(evals) += (tmp * scaling_factor);
316363
}
317364
{ // TIMESTAMP
318-
using View = typename std::tuple_element_t<49, ContainerOverSubrelations>::View;
365+
using View = typename std::tuple_element_t<50, ContainerOverSubrelations>::View;
319366
auto tmp = (static_cast<View>(in.get(C::memory_timestamp)) -
320367
(FF(2) * static_cast<View>(in.get(C::memory_clk)) + static_cast<View>(in.get(C::memory_rw))));
321-
std::get<49>(evals) += (tmp * scaling_factor);
368+
std::get<50>(evals) += (tmp * scaling_factor);
322369
}
323370
{ // LAST_ACCESS
324-
using View = typename std::tuple_element_t<50, ContainerOverSubrelations>::View;
371+
using View = typename std::tuple_element_t<51, ContainerOverSubrelations>::View;
325372
auto tmp =
326373
static_cast<View>(in.get(C::memory_sel_rng_chk)) *
327374
(CView(memory_GLOB_ADDR_DIFF) * ((FF(1) - static_cast<View>(in.get(C::memory_last_access))) *
328375
(FF(1) - static_cast<View>(in.get(C::memory_glob_addr_diff_inv))) +
329376
static_cast<View>(in.get(C::memory_glob_addr_diff_inv))) -
330377
static_cast<View>(in.get(C::memory_last_access)));
331-
std::get<50>(evals) += (tmp * scaling_factor);
378+
std::get<51>(evals) += (tmp * scaling_factor);
332379
}
333380
{ // DIFF
334-
using View = typename std::tuple_element_t<51, ContainerOverSubrelations>::View;
381+
using View = typename std::tuple_element_t<52, ContainerOverSubrelations>::View;
335382
auto tmp =
336383
(static_cast<View>(in.get(C::memory_diff)) -
337384
static_cast<View>(in.get(C::memory_sel_rng_chk)) *
@@ -340,61 +387,61 @@ void memoryImpl<FF_>::accumulate(ContainerOverSubrelations& evals,
340387
((static_cast<View>(in.get(C::memory_timestamp_shift)) -
341388
static_cast<View>(in.get(C::memory_timestamp))) -
342389
static_cast<View>(in.get(C::memory_rw_shift)) * static_cast<View>(in.get(C::memory_rw)))));
343-
std::get<51>(evals) += (tmp * scaling_factor);
390+
std::get<52>(evals) += (tmp * scaling_factor);
344391
}
345392
{ // DIFF_DECOMP
346-
using View = typename std::tuple_element_t<52, ContainerOverSubrelations>::View;
393+
using View = typename std::tuple_element_t<53, ContainerOverSubrelations>::View;
347394
auto tmp =
348395
(static_cast<View>(in.get(C::memory_diff)) -
349396
(static_cast<View>(in.get(C::memory_limb_0_)) + static_cast<View>(in.get(C::memory_limb_1_)) * FF(65536) +
350397
static_cast<View>(in.get(C::memory_limb_2_)) * FF(4294967296UL)));
351-
std::get<52>(evals) += (tmp * scaling_factor);
398+
std::get<53>(evals) += (tmp * scaling_factor);
352399
}
353400
{ // MEMORY_INIT_VALUE
354-
using View = typename std::tuple_element_t<53, ContainerOverSubrelations>::View;
401+
using View = typename std::tuple_element_t<54, ContainerOverSubrelations>::View;
355402
auto tmp =
356403
(static_cast<View>(in.get(C::memory_last_access)) + static_cast<View>(in.get(C::precomputed_first_row))) *
357404
(FF(1) - static_cast<View>(in.get(C::memory_rw_shift))) * static_cast<View>(in.get(C::memory_value_shift));
358-
std::get<53>(evals) += (tmp * scaling_factor);
405+
std::get<54>(evals) += (tmp * scaling_factor);
359406
}
360407
{ // MEMORY_INIT_TAG
361-
using View = typename std::tuple_element_t<54, ContainerOverSubrelations>::View;
408+
using View = typename std::tuple_element_t<55, ContainerOverSubrelations>::View;
362409
auto tmp =
363410
(static_cast<View>(in.get(C::memory_last_access)) + static_cast<View>(in.get(C::precomputed_first_row))) *
364411
(FF(1) - static_cast<View>(in.get(C::memory_rw_shift))) *
365412
(static_cast<View>(in.get(C::memory_tag_shift)) - CView(constants_MEM_TAG_FF));
366-
std::get<54>(evals) += (tmp * scaling_factor);
413+
std::get<55>(evals) += (tmp * scaling_factor);
367414
}
368415
{ // READ_WRITE_CONSISTENCY_VALUE
369-
using View = typename std::tuple_element_t<55, ContainerOverSubrelations>::View;
416+
using View = typename std::tuple_element_t<56, ContainerOverSubrelations>::View;
370417
auto tmp = (FF(1) - static_cast<View>(in.get(C::memory_last_access))) *
371418
(FF(1) - static_cast<View>(in.get(C::memory_rw_shift))) *
372419
(static_cast<View>(in.get(C::memory_value_shift)) - static_cast<View>(in.get(C::memory_value)));
373-
std::get<55>(evals) += (tmp * scaling_factor);
420+
std::get<56>(evals) += (tmp * scaling_factor);
374421
}
375422
{ // READ_WRITE_CONSISTENCY_TAG
376-
using View = typename std::tuple_element_t<56, ContainerOverSubrelations>::View;
423+
using View = typename std::tuple_element_t<57, ContainerOverSubrelations>::View;
377424
auto tmp = (FF(1) - static_cast<View>(in.get(C::memory_last_access))) *
378425
(FF(1) - static_cast<View>(in.get(C::memory_rw_shift))) *
379426
(static_cast<View>(in.get(C::memory_tag_shift)) - static_cast<View>(in.get(C::memory_tag)));
380-
std::get<56>(evals) += (tmp * scaling_factor);
427+
std::get<57>(evals) += (tmp * scaling_factor);
381428
}
382429
{ // TAG_IS_FF
383-
using View = typename std::tuple_element_t<57, ContainerOverSubrelations>::View;
430+
using View = typename std::tuple_element_t<58, ContainerOverSubrelations>::View;
384431
auto tmp = static_cast<View>(in.get(C::memory_sel)) *
385432
((CView(memory_TAG_FF_DIFF) * (static_cast<View>(in.get(C::memory_sel_tag_is_ff)) *
386433
(FF(1) - static_cast<View>(in.get(C::memory_tag_ff_diff_inv))) +
387434
static_cast<View>(in.get(C::memory_tag_ff_diff_inv))) +
388435
static_cast<View>(in.get(C::memory_sel_tag_is_ff))) -
389436
FF(1));
390-
std::get<57>(evals) += (tmp * scaling_factor);
437+
std::get<58>(evals) += (tmp * scaling_factor);
391438
}
392439
{ // SEL_RNG_WRITE
393-
using View = typename std::tuple_element_t<58, ContainerOverSubrelations>::View;
440+
using View = typename std::tuple_element_t<59, ContainerOverSubrelations>::View;
394441
auto tmp =
395442
(static_cast<View>(in.get(C::memory_sel_rng_write)) -
396443
static_cast<View>(in.get(C::memory_rw)) * (FF(1) - static_cast<View>(in.get(C::memory_sel_tag_is_ff))));
397-
std::get<58>(evals) += (tmp * scaling_factor);
444+
std::get<59>(evals) += (tmp * scaling_factor);
398445
}
399446
}
400447

barretenberg/cpp/src/barretenberg/vm2/simulation/execution.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -584,22 +584,34 @@ void Execution::debug_log(ContextInterface& context,
584584
try {
585585
auto& memory = context.get_memory();
586586

587+
// This is a workaround. Do not copy or use in other places.
588+
auto unconstrained_read = [&memory](MemoryAddress offset) {
589+
Memory* memory_ptr = dynamic_cast<Memory*>(&memory);
590+
if (memory_ptr) {
591+
// This means that we are using the event generating memory.
592+
return memory_ptr->unconstrained_get(offset);
593+
} else {
594+
// This assumes that any other type will not generate events.
595+
return memory.get(offset);
596+
}
597+
};
598+
587599
// Get the fields size and validate its tag
588-
const auto fields_size_value = memory.get(fields_size_offset);
600+
const auto fields_size_value = unconstrained_read(fields_size_offset);
589601
const uint32_t fields_size = fields_size_value.as<uint32_t>();
590602

591603
// Read message and fields from memory
592604
std::string message_as_str;
593605
uint16_t truncated_message_size = std::min<uint16_t>(message_size, 100);
594606
for (uint32_t i = 0; i < truncated_message_size; ++i) {
595-
const auto message_field = memory.get(message_offset + i);
607+
const auto message_field = unconstrained_read(message_offset + i);
596608
message_as_str += static_cast<char>(static_cast<uint8_t>(message_field.as_ff()));
597609
}
598610
message_as_str += ": [";
599611

600612
// Read fields
601613
for (uint32_t i = 0; i < fields_size; ++i) {
602-
const auto field = memory.get(fields_offset + i);
614+
const auto field = unconstrained_read(fields_offset + i);
603615
message_as_str += field_to_string(field);
604616
if (i < fields_size - 1) {
605617
message_as_str += ", ";

barretenberg/cpp/src/barretenberg/vm2/simulation/memory.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ const MemoryValue& Memory::get(MemoryAddress index) const
4040
return vt;
4141
}
4242

43+
const MemoryValue& Memory::unconstrained_get(MemoryAddress index) const
44+
{
45+
static const auto default_value = MemoryValue::from<FF>(0);
46+
auto it = memory.find(index);
47+
return it != memory.end() ? it->second : default_value;
48+
}
49+
4350
// Sadly this is circuit leaking. In simulation we know the tag-value is consistent.
4451
// But the circuit does need to force a range check.
4552
void Memory::validate_tag(const MemoryValue& value) const

0 commit comments

Comments
 (0)