Skip to content

Commit fa9e204

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 4205dcd commit fa9e204

File tree

4 files changed

+120
-2
lines changed

4 files changed

+120
-2
lines changed
+33
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 uninitialisedGlobalArray1[2];
5+
int uninitialisedGlobalArray2[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(uninitialisedGlobalArray1[directUseReadLocation] == 0);
15+
16+
/*** Variable non-redundant update ***/
17+
// No obvious simplifications to writes
18+
19+
long int nonRedundantWriteLocation;
20+
21+
if(
22+
0 <= nonRedundantWriteLocation && nonRedundantWriteLocation < 2 &&
23+
nonRedundantWriteLocation != 1)
24+
{
25+
uninitialisedGlobalArray2[nonRedundantWriteLocation] = 1;
26+
}
27+
28+
// Constant access
29+
// Again, constant index into a fully known but non-constant array
30+
assert(uninitialisedGlobalArray2[1] == 0);
31+
32+
return 0;
33+
}
+10
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

+72-1
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,76 @@ 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+
const std::size_t i =
666+
numeric_cast_v<std::size_t>(to_constant_expr(index));
667+
// if the access is out of bounds, we leave it unconstrained
668+
if(i >= operands.size())
669+
continue;
670+
671+
const exprt v = operands[i];
672+
DATA_INVARIANT(
673+
index_expr.type() == v.type(),
674+
"array operand type should match array element type");
675+
676+
// add constraint
677+
lazy_constraintt lazy{lazy_typet::ARRAY_CONSTANT,
678+
equal_exprt{index_expr, v}};
679+
add_array_constraint(lazy, false); // added immediately
680+
}
681+
else
682+
{
683+
std::vector<std::pair<std::size_t, std::size_t>> ranges;
684+
685+
for(std::size_t i = 0; i < operands.size(); ++i)
686+
{
687+
if(ranges.empty() || operands[i] != operands[ranges.back().first])
688+
ranges.emplace_back(i, i);
689+
else
690+
ranges.back().second = i;
691+
}
692+
693+
for(const auto &range : ranges)
694+
{
695+
exprt index_constraint;
696+
697+
if(range.first == range.second)
698+
{
699+
index_constraint =
700+
equal_exprt{index, from_integer(range.first, index.type())};
701+
}
702+
else
703+
{
704+
index_constraint = and_exprt{
705+
binary_predicate_exprt{
706+
from_integer(range.first, index.type()), ID_le, index},
707+
binary_predicate_exprt{
708+
index, ID_le, from_integer(range.second, index.type())}};
709+
}
710+
711+
lazy_constraintt lazy{
712+
lazy_typet::ARRAY_CONSTANT,
713+
implies_exprt{index_constraint,
714+
equal_exprt{index_expr, operands[range.first]}}};
715+
add_array_constraint(lazy, true); // added lazily
716+
}
717+
}
718+
}
719+
}
720+
650721
void arrayst::add_array_constraints_if(
651722
const index_sett &index_set,
652723
const if_exprt &expr)

src/solvers/flattening/arrays.h

+5-1
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)