Skip to content

Commit 10177c6

Browse files
committed
Record constraints of indexed access to array constants
When even constant arrays are left to be handled by the array theory, expressions such as { 3, 4 }[1] were actually unconstrained as we never recorded the fact that this expression evaluates to 4. To reduce the number of constraints to be generated for non-constant indices, ranges of equal values just yield a single constraint, optimising the case of large zero-initialised arrays.
1 parent 09a5ad9 commit 10177c6

File tree

4 files changed

+129
-2
lines changed

4 files changed

+129
-2
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#include <assert.h>
2+
3+
// C semantics are that these will be zero
4+
int uninitializedGlobalArray1[2];
5+
int uninitializedGlobalArray2[2];
6+
7+
int main(void)
8+
{
9+
// Variable access
10+
long directUseReadLocation; // Non-det
11+
12+
long x = directUseReadLocation;
13+
if(0 <= directUseReadLocation && directUseReadLocation < 2)
14+
assert(uninitializedGlobalArray1[directUseReadLocation] == 0);
15+
16+
/*** Variable non-redundant update ***/
17+
// No obvious simplifications to writes
18+
19+
long nonRedundantWriteLocation;
20+
21+
if(
22+
0 <= nonRedundantWriteLocation && nonRedundantWriteLocation < 2 &&
23+
nonRedundantWriteLocation != 1)
24+
{
25+
uninitializedGlobalArray2[nonRedundantWriteLocation] = 1;
26+
}
27+
28+
// Constant access
29+
// Again, constant index into a fully known but non-constant array
30+
assert(uninitializedGlobalArray2[1] == 0);
31+
32+
return 0;
33+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
uf-always.c
3+
--arrays-uf-always
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
9+
--
10+
This test is extracted from main.c to showcase a bug in the array encoding.

src/solvers/flattening/arrays.cpp

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,11 +436,12 @@ void arrayst::add_array_constraints(
436436
return add_array_constraints_if(index_set, to_if_expr(expr));
437437
else if(expr.id()==ID_array_of)
438438
return add_array_constraints_array_of(index_set, to_array_of_expr(expr));
439+
else if(expr.id() == ID_array)
440+
return add_array_constraints_array_constant(index_set, to_array_expr(expr));
439441
else if(expr.id()==ID_symbol ||
440442
expr.id()==ID_nondet_symbol ||
441443
expr.id()==ID_constant ||
442444
expr.id()=="zero_string" ||
443-
expr.id()==ID_array ||
444445
expr.id()==ID_string_constant)
445446
{
446447
}
@@ -647,6 +648,85 @@ void arrayst::add_array_constraints_array_of(
647648
}
648649
}
649650

651+
void arrayst::add_array_constraints_array_constant(
652+
const index_sett &index_set,
653+
const array_exprt &expr)
654+
{
655+
// we got x = { v, ... } - add constraint x[i] = v
656+
const exprt::operandst &operands = expr.operands();
657+
658+
for(const auto &index : index_set)
659+
{
660+
const typet &subtype = expr.type().subtype();
661+
const index_exprt index_expr{expr, index, subtype};
662+
663+
if(index.is_constant())
664+
{
665+
// We have a constant index - just pick the element at that index from the
666+
// array constant.
667+
668+
const std::size_t i =
669+
numeric_cast_v<std::size_t>(to_constant_expr(index));
670+
// if the access is out of bounds, we leave it unconstrained
671+
if(i >= operands.size())
672+
continue;
673+
674+
const exprt v = operands[i];
675+
DATA_INVARIANT(
676+
index_expr.type() == v.type(),
677+
"array operand type should match array element type");
678+
679+
// add constraint
680+
lazy_constraintt lazy{lazy_typet::ARRAY_CONSTANT,
681+
equal_exprt{index_expr, v}};
682+
add_array_constraint(lazy, false); // added immediately
683+
}
684+
else
685+
{
686+
// We have a non-constant index into an array constant. We need to build a
687+
// case statement testing the index against all possible values. Whenever
688+
// neighbouring array elements are the same, we can test the index against
689+
// the range rather than individual elements. This should be particularly
690+
// helpful when we have arrays of zeros, as is the case for initializers.
691+
692+
std::vector<std::pair<std::size_t, std::size_t>> ranges;
693+
694+
for(std::size_t i = 0; i < operands.size(); ++i)
695+
{
696+
if(ranges.empty() || operands[i] != operands[ranges.back().first])
697+
ranges.emplace_back(i, i);
698+
else
699+
ranges.back().second = i;
700+
}
701+
702+
for(const auto &range : ranges)
703+
{
704+
exprt index_constraint;
705+
706+
if(range.first == range.second)
707+
{
708+
index_constraint =
709+
equal_exprt{index, from_integer(range.first, index.type())};
710+
}
711+
else
712+
{
713+
index_constraint = and_exprt{
714+
binary_predicate_exprt{
715+
from_integer(range.first, index.type()), ID_le, index},
716+
binary_predicate_exprt{
717+
index, ID_le, from_integer(range.second, index.type())}};
718+
}
719+
720+
lazy_constraintt lazy{
721+
lazy_typet::ARRAY_CONSTANT,
722+
implies_exprt{index_constraint,
723+
equal_exprt{index_expr, operands[range.first]}}};
724+
add_array_constraint(lazy, true); // added lazily
725+
}
726+
}
727+
}
728+
}
729+
650730
void arrayst::add_array_constraints_if(
651731
const index_sett &index_set,
652732
const if_exprt &expr)

src/solvers/flattening/arrays.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ class arrayst:public equalityt
8383
ARRAY_WITH,
8484
ARRAY_IF,
8585
ARRAY_OF,
86-
ARRAY_TYPECAST
86+
ARRAY_TYPECAST,
87+
ARRAY_CONSTANT
8788
};
8889

8990
struct lazy_constraintt
@@ -119,6 +120,9 @@ class arrayst:public equalityt
119120
const index_sett &index_set, const update_exprt &expr);
120121
void add_array_constraints_array_of(
121122
const index_sett &index_set, const array_of_exprt &exprt);
123+
void add_array_constraints_array_constant(
124+
const index_sett &index_set,
125+
const array_exprt &exprt);
122126

123127
void update_index_map(bool update_all);
124128
void update_index_map(std::size_t i);

0 commit comments

Comments
 (0)