Skip to content

Commit aa683aa

Browse files
committed
Generalise get_subexpression_at_offset to extract parts of members
This enables re-use in the simplifier and other places. Adding a unit test of get_subexpression_at_offset.
1 parent 992bd3a commit aa683aa

File tree

5 files changed

+178
-41
lines changed

5 files changed

+178
-41
lines changed

src/pointer-analysis/value_set_dereference.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,9 +468,10 @@ value_set_dereferencet::valuet value_set_dereferencet::build_reference_to(
468468
}
469469
else
470470
{
471+
// try to build a member/index expression - do not use byte_extract
471472
exprt subexpr = get_subexpression_at_offset(
472473
root_object_subexpression, o.offset(), dereference_type, ns);
473-
if(subexpr.is_not_nil())
474+
if(subexpr.is_not_nil() && subexpr.id() != byte_extract_id())
474475
{
475476
// Successfully found a member, array index, or combination thereof
476477
// that matches the desired type and offset:

src/util/pointer_offset_size.cpp

Lines changed: 54 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ Author: Daniel Kroening, [email protected]
1212
#include "pointer_offset_size.h"
1313

1414
#include "arith_tools.h"
15+
#include "base_type.h"
16+
#include "byte_operators.h"
1517
#include "c_types.h"
1618
#include "invariant.h"
1719
#include "namespace.h"
@@ -676,67 +678,80 @@ exprt build_sizeof_expr(
676678

677679
exprt get_subexpression_at_offset(
678680
const exprt &expr,
679-
mp_integer offset,
681+
const mp_integer &offset,
680682
const typet &target_type_raw,
681683
const namespacet &ns)
682684
{
683-
exprt result = expr;
684-
const typet &source_type=ns.follow(result.type());
685-
const typet &target_type=ns.follow(target_type_raw);
685+
if(offset == 0 && base_type_eq(expr.type(), target_type_raw, ns))
686+
{
687+
exprt result = expr;
688+
689+
if(expr.type() != target_type_raw)
690+
result.type() = target_type_raw;
686691

687-
if(offset==0 && source_type==target_type)
688692
return result;
693+
}
694+
695+
const typet &source_type = ns.follow(expr.type());
696+
const auto target_size = pointer_offset_bits(target_type_raw, ns);
697+
if(!target_size.has_value())
698+
return nil_exprt();
689699

690700
if(source_type.id()==ID_struct)
691701
{
692-
const auto &st=to_struct_type(source_type);
693-
const struct_typet::componentst &components=st.components();
694-
member_offset_iterator offsets(st, ns);
695-
while(offsets->first<components.size() &&
696-
offsets->second!=-1 &&
697-
offsets->second<=offset)
702+
const struct_typet &struct_type = to_struct_type(source_type);
703+
704+
mp_integer m_offset_bits = 0;
705+
for(const auto &component : struct_type.components())
698706
{
699-
auto nextit=offsets;
700-
++nextit;
701-
if((offsets->first+1)==components.size() || offset<nextit->second)
707+
const auto m_size = pointer_offset_bits(component.type(), ns);
708+
if(!m_size.has_value())
709+
return nil_exprt();
710+
711+
if(
712+
offset * 8 >= m_offset_bits && m_offset_bits % 8 == 0 &&
713+
offset * 8 + *target_size <= m_offset_bits + *m_size)
702714
{
703-
// This field might be, or might contain, the answer.
704-
result=
705-
member_exprt(
706-
result,
707-
components[offsets->first].get_name(),
708-
components[offsets->first].type());
709-
return
710-
get_subexpression_at_offset(
711-
result, offset-offsets->second, target_type, ns);
715+
const member_exprt member(expr, component.get_name(), component.type());
716+
return get_subexpression_at_offset(
717+
member, offset - m_offset_bits / 8, target_type_raw, ns);
712718
}
713-
++offsets;
719+
720+
m_offset_bits += *m_size;
714721
}
715-
return nil_exprt();
716722
}
717723
else if(source_type.id()==ID_array)
718724
{
719-
// A cell of the array might be, or contain, the subexpression
720-
// we're looking for.
721-
const auto &at=to_array_type(source_type);
722-
723-
auto elem_size = pointer_offset_size(at.subtype(), ns);
724-
725-
if(!elem_size.has_value())
726-
return nil_exprt();
725+
const array_typet &array_type = to_array_type(source_type);
727726

728-
mp_integer cellidx = offset / (*elem_size);
727+
const auto elem_size = pointer_offset_bits(array_type.subtype(), ns);
729728

730-
if(cellidx < 0 || !cellidx.is_long())
729+
// no arrays of non-byte, zero-, or unknown-sized objects
730+
if(!elem_size.has_value() || *elem_size == 0 || *elem_size % 8 != 0)
731731
return nil_exprt();
732732

733-
offset = offset % (*elem_size);
733+
if(*target_size <= *elem_size)
734+
return get_subexpression_at_offset(
735+
index_exprt(
736+
expr, from_integer(offset / (*elem_size / 8), index_type())),
737+
offset % (*elem_size / 8),
738+
target_type_raw,
739+
ns);
740+
}
734741

735-
result=index_exprt(result, from_integer(cellidx, unsignedbv_typet(64)));
736-
return get_subexpression_at_offset(result, offset, target_type, ns);
742+
const auto source_size = pointer_offset_bits(source_type, ns);
743+
if(
744+
offset == 0 && source_size.has_value() && *source_size == *target_size &&
745+
source_type.id() == ID_pointer && target_type_raw.id() == ID_pointer)
746+
{
747+
return typecast_exprt(expr, target_type_raw);
737748
}
738749
else
739-
return nil_exprt();
750+
return byte_extract_exprt(
751+
byte_extract_id(),
752+
expr,
753+
from_integer(offset, index_type()),
754+
target_type_raw);
740755
}
741756

742757
exprt get_subexpression_at_offset(

src/util/pointer_offset_size.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ exprt build_sizeof_expr(
8181

8282
exprt get_subexpression_at_offset(
8383
const exprt &expr,
84-
mp_integer offset,
84+
const mp_integer &offset,
8585
const typet &target_type,
8686
const namespacet &ns);
8787

unit/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ SRC += analyses/ai/ai.cpp \
4040
util/irep_sharing.cpp \
4141
util/message.cpp \
4242
util/optional.cpp \
43+
util/pointer_offset_size.cpp \
4344
util/replace_symbol.cpp \
4445
util/sharing_map.cpp \
4546
util/sharing_node.cpp \

unit/util/pointer_offset_size.cpp

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*******************************************************************\
2+
3+
Module: Unit tests of expression size/offset computation
4+
5+
Author: Michael Tautschnig
6+
7+
\*******************************************************************/
8+
9+
#include <testing-utils/catch.hpp>
10+
11+
#include <util/arith_tools.h>
12+
#include <util/byte_operators.h>
13+
#include <util/c_types.h>
14+
#include <util/cmdline.h>
15+
#include <util/config.h>
16+
#include <util/namespace.h>
17+
#include <util/pointer_offset_size.h>
18+
#include <util/std_expr.h>
19+
#include <util/symbol_table.h>
20+
21+
TEST_CASE("Build subexpression to access element at offset into array")
22+
{
23+
// this test does require a proper architecture to be set so that byte extract
24+
// uses adequate endianness
25+
cmdlinet cmdline;
26+
config.set(cmdline);
27+
28+
symbol_tablet symbol_table;
29+
namespacet ns(symbol_table);
30+
31+
const signedbv_typet t(32);
32+
33+
array_typet array_type(t, from_integer(2, size_type()));
34+
symbol_exprt a("array", array_type);
35+
36+
{
37+
const exprt result = get_subexpression_at_offset(a, 0, t, ns);
38+
REQUIRE(result == index_exprt(a, from_integer(0, index_type())));
39+
}
40+
41+
{
42+
const exprt result = get_subexpression_at_offset(a, 32 / 8, t, ns);
43+
REQUIRE(result == index_exprt(a, from_integer(1, index_type())));
44+
}
45+
46+
{
47+
const exprt result =
48+
get_subexpression_at_offset(a, from_integer(0, size_type()), t, ns);
49+
REQUIRE(result == index_exprt(a, from_integer(0, index_type())));
50+
}
51+
52+
{
53+
const exprt result =
54+
get_subexpression_at_offset(a, size_of_expr(t, ns), t, ns);
55+
REQUIRE(result == index_exprt(a, from_integer(1, index_type())));
56+
}
57+
58+
{
59+
const signedbv_typet small_t(8);
60+
const exprt result = get_subexpression_at_offset(a, 1, small_t, ns);
61+
REQUIRE(
62+
result == byte_extract_exprt(
63+
byte_extract_id(),
64+
index_exprt(a, from_integer(0, index_type())),
65+
from_integer(1, index_type()),
66+
small_t));
67+
}
68+
}
69+
70+
TEST_CASE("Build subexpression to access element at offset into struct")
71+
{
72+
// this test does require a proper architecture to be set so that byte extract
73+
// uses adequate endianness
74+
cmdlinet cmdline;
75+
config.set(cmdline);
76+
77+
symbol_tablet symbol_table;
78+
namespacet ns(symbol_table);
79+
80+
const signedbv_typet t(32);
81+
82+
struct_typet st;
83+
st.components().emplace_back("foo", t);
84+
st.components().emplace_back("bar", t);
85+
86+
symbol_exprt s("struct", st);
87+
88+
{
89+
const exprt result = get_subexpression_at_offset(s, 0, t, ns);
90+
REQUIRE(result == member_exprt(s, "foo", t));
91+
}
92+
93+
{
94+
const exprt result = get_subexpression_at_offset(s, 32 / 8, t, ns);
95+
REQUIRE(result == member_exprt(s, "bar", t));
96+
}
97+
98+
{
99+
const exprt result =
100+
get_subexpression_at_offset(s, from_integer(0, size_type()), t, ns);
101+
REQUIRE(result == member_exprt(s, "foo", t));
102+
}
103+
104+
{
105+
const exprt result =
106+
get_subexpression_at_offset(s, size_of_expr(t, ns), t, ns);
107+
REQUIRE(result == member_exprt(s, "bar", t));
108+
}
109+
110+
{
111+
const signedbv_typet small_t(8);
112+
const exprt result = get_subexpression_at_offset(s, 1, small_t, ns);
113+
REQUIRE(
114+
result == byte_extract_exprt(
115+
byte_extract_id(),
116+
member_exprt(s, "foo", t),
117+
from_integer(1, index_type()),
118+
small_t));
119+
}
120+
}

0 commit comments

Comments
 (0)