Skip to content

Commit f46faa4

Browse files
committed
Skip Ackermann constraints for derived arrays (weak equivalence)
Skip generating Ackermann constraints for arrays that are derived from other arrays via with (store), if, array_of, array constants, array_comprehension, typecast, or let expressions. For a derived array such as x = y with [k := v], the Ackermann constraint i1 = i2 => x[i1] = x[i2] is already implied by: (1) the with constraint: k != j => x[j] = y[j], and (2) the Ackermann constraint on the base array y. This is the read-over-weakeq optimisation from the theory of weakly equivalent arrays (Christ & Hoenicke, 2014). The same reasoning applies to if, array_of, and other derived array expressions, all of which already have constraints connecting them element-wise to their constituent arrays. With 5 stores to the same unbounded array the Ackermann constraint count drops from 110 to 60; with 40 stores it drops from 63180 to 31980 (approximately 50% reduction in all cases). Co-authored-by: Kiro
1 parent 15eb10a commit f46faa4

File tree

3 files changed

+53
-0
lines changed

3 files changed

+53
-0
lines changed

regression/cbmc/Array_UF23/main.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/// \file
2+
/// Test that Ackermann constraints are reduced by the weak equivalence
3+
/// optimisation: derived arrays (with, if, etc.) do not need Ackermann
4+
/// constraints because they are implied by the with/if constraints plus
5+
/// Ackermann on base arrays.
6+
#include <stdlib.h>
7+
int main()
8+
{
9+
size_t array_size;
10+
int a[array_size];
11+
int i0, i1, i2, i3, i4;
12+
13+
a[i0] = 0;
14+
a[i1] = 1;
15+
a[i2] = 2;
16+
a[i3] = 3;
17+
a[i4] = 4;
18+
19+
__CPROVER_assert(a[i0] >= 0, "a[i0] >= 0");
20+
return 0;
21+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
main.c
3+
--arrays-uf-always --unwind 1 --show-array-constraints --json-ui
4+
"arrayAckermann": 60
5+
^EXIT=10$
6+
^SIGNAL=0$
7+
--
8+
"arrayAckermann": 110
9+
--
10+
Checks that the weak equivalence optimisation reduces Ackermann constraints.
11+
With 5 stores to the same unbounded array, the old code produced 110 Ackermann
12+
constraints (one per pair of indices per array term). The optimised code skips
13+
derived arrays (with, if, etc.) and produces only 60.

src/solvers/flattening/arrays.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,25 @@ void arrayst::add_array_Ackermann_constraints()
335335
// iterate over arrays
336336
for(std::size_t i=0; i<arrays.size(); i++)
337337
{
338+
// Skip arrays that are derived from other arrays via with, if, etc.
339+
// Their Ackermann constraints are implied by the combination of:
340+
// (1) the with/if/array_of/etc. constraints already generated, and
341+
// (2) the Ackermann constraints on the underlying base arrays.
342+
// This is the "weak equivalence" optimisation: arrays connected by
343+
// store chains are weakly equivalent, and read-over-weakeq follows
344+
// from the read-over-write constraints plus Ackermann on base arrays.
345+
const exprt &arr = arrays[i];
346+
if(
347+
arr.id() == ID_with || arr.id() == ID_update || arr.id() == ID_if ||
348+
arr.id() == ID_array_of || arr.id() == ID_array ||
349+
arr.id() == ID_array_comprehension || arr.id() == ID_typecast ||
350+
arr.id() == ID_string_constant || arr.is_constant())
351+
{
352+
continue;
353+
}
354+
if(expr_try_dynamic_cast<let_exprt>(arr))
355+
continue;
356+
338357
const index_sett &index_set=index_map[arrays.find_number(i)];
339358

340359
#ifdef DEBUG

0 commit comments

Comments
 (0)