Skip to content

Commit d1e5b2c

Browse files
authored
Merge pull request diffblue#5418 from smowton/smowton/draft/variable-array-derefs
Generate index expressions for deref(p + offset), where p points to an array in a struct
2 parents 0246e3d + 2709787 commit d1e5b2c

File tree

5 files changed

+91
-0
lines changed

5 files changed

+91
-0
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
struct st
2+
{
3+
char c;
4+
int n[4];
5+
};
6+
7+
int main()
8+
{
9+
unsigned i;
10+
int k;
11+
i %= 4;
12+
struct st s;
13+
int *p = s.n;
14+
p[i] = k;
15+
return 0;
16+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE paths-lifo-expected-failure
2+
access.c
3+
--program-only
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
s!0@1#1\.\.n == \{ s!0@1#1\.\.n\[\[0\]\], s!0@1#1\.\.n\[\[1\]\], s!0@1#1\.\.n\[\[2\]\], s!0@1#1\.\.n\[\[3\]\] } WITH \[\(.*\)i!0@1#2:=k!0@1#1\]
7+
--
8+
byte_update
9+
--
10+
This tests applying field-sensitivity to a pointer to an array that is part of a struct. See cbmc issue #5397 and PR #5418 for more detail.
11+
Disabled for paths-lifo mode, which does not support --program-only.

regression/validate-trace-xml-schema/check.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
# program-only instead of trace
2929
['vla1', 'program-only.desc'],
3030
['Quantifiers-simplify', 'simplify_not_forall.desc'],
31+
['array-cell-sensitivity15', 'test.desc'],
3132
# these test for invalid command line handling
3233
['bad_option', 'test_multiple.desc'],
3334
['bad_option', 'test.desc'],

src/pointer-analysis/value_set_dereference.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,36 @@ static json_objectt value_set_dereference_stats_to_json(
9999
return json_result;
100100
}
101101

102+
optionalt<exprt> value_set_dereferencet::try_add_offset_to_indices(
103+
const exprt &expr,
104+
const exprt &offset_elements)
105+
{
106+
if(const auto *index_expr = expr_try_dynamic_cast<index_exprt>(expr))
107+
{
108+
return index_exprt{
109+
index_expr->array(),
110+
plus_exprt{index_expr->index(),
111+
typecast_exprt::conditional_cast(
112+
offset_elements, index_expr->index().type())}};
113+
}
114+
else if(const auto *if_expr = expr_try_dynamic_cast<if_exprt>(expr))
115+
{
116+
const auto true_case =
117+
try_add_offset_to_indices(if_expr->true_case(), offset_elements);
118+
if(!true_case)
119+
return {};
120+
const auto false_case =
121+
try_add_offset_to_indices(if_expr->false_case(), offset_elements);
122+
if(!false_case)
123+
return {};
124+
return if_exprt{if_expr->cond(), *true_case, *false_case};
125+
}
126+
else
127+
{
128+
return {};
129+
}
130+
}
131+
102132
exprt value_set_dereferencet::dereference(
103133
const exprt &pointer,
104134
bool display_points_to_sets)
@@ -140,6 +170,33 @@ exprt value_set_dereferencet::dereference(
140170
display_points_to_sets));
141171
}
142172
}
173+
else if(pointer.id() == ID_plus && pointer.operands().size() == 2)
174+
{
175+
// Try to improve results for *(p + i) where p points to known offsets but
176+
// i is non-constant-- if `p` points to known positions in arrays or array-members
177+
// of structs then we can add the non-constant expression `i` to the index
178+
// instead of using a byte-extract expression.
179+
180+
exprt pointer_expr = to_plus_expr(pointer).op0();
181+
exprt offset_expr = to_plus_expr(pointer).op1();
182+
183+
if(can_cast_type<pointer_typet>(offset_expr.type()))
184+
std::swap(pointer_expr, offset_expr);
185+
186+
if(
187+
can_cast_type<pointer_typet>(pointer_expr.type()) &&
188+
!can_cast_type<pointer_typet>(offset_expr.type()) &&
189+
!can_cast_expr<constant_exprt>(offset_expr))
190+
{
191+
exprt derefd_pointer = dereference(pointer_expr);
192+
if(
193+
auto derefd_with_offset =
194+
try_add_offset_to_indices(derefd_pointer, offset_expr))
195+
return *derefd_with_offset;
196+
197+
// If any of this fails, fall through to use the normal byte-extract path.
198+
}
199+
}
143200

144201
return handle_dereference_base_case(pointer, display_points_to_sets);
145202
}

src/pointer-analysis/value_set_dereference.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ class value_set_dereferencet final
5959
/// \param display_points_to_sets: Display size and contents of points to sets
6060
exprt dereference(const exprt &pointer, bool display_points_to_sets = false);
6161

62+
/// If `expr` is of the form (c1 ? e1[o1] : c2 ? e2[o2] : c3 ? ...)
63+
/// then return `c1 ? e1[o1 + offset] : e2[o2 + offset] : c3 ? ...`
64+
/// otherwise return an empty optionalt.
65+
optionalt<exprt>
66+
try_add_offset_to_indices(const exprt &expr, const exprt &offset);
67+
6268
/// Return value for `build_reference_to`; see that method for documentation.
6369
class valuet
6470
{

0 commit comments

Comments
 (0)