Skip to content

Commit 5239c68

Browse files
authored
Merge pull request #5873 from tautschnig/simplify-byte-extract
Simplify byte-extract from struct or union expressions
2 parents e9b5f5c + 6544f7c commit 5239c68

File tree

6 files changed

+153
-1
lines changed

6 files changed

+153
-1
lines changed

regression/cbmc/simplify-union/main.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
struct O
5+
{
6+
char *ptr;
7+
unsigned len;
8+
};
9+
10+
struct E
11+
{
12+
int e;
13+
};
14+
15+
union U {
16+
struct O ok;
17+
struct E err;
18+
};
19+
20+
struct S
21+
{
22+
union U u;
23+
_Bool success;
24+
};
25+
26+
void alloc(struct S *s, unsigned size)
27+
{
28+
char *p = malloc(sizeof(char) * size);
29+
if(p != 0)
30+
{
31+
s->u.ok = (struct O){p, size};
32+
s->success = 1;
33+
}
34+
else
35+
{
36+
s->success = 0;
37+
}
38+
}
39+
40+
int main()
41+
{
42+
struct S s;
43+
alloc(&s, 2);
44+
if(s.success)
45+
{
46+
assert(s.u.ok.len == 2);
47+
s.u.ok.ptr = "a";
48+
}
49+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
main.c
3+
4+
^Generated 1 VCC\(s\), 0 remaining after simplification$
5+
^VERIFICATION SUCCESSFUL$
6+
^EXIT=0$
7+
^SIGNAL=0$
8+
--
9+
^warning: ignoring
10+
--
11+
The len member should constant propagate with the help of simplification.

scripts/expected_doxygen_warnings.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ warning: Included by graph for 'invariant.h' not generated, too many nodes (172)
77
warning: Included by graph for 'irep.h' not generated, too many nodes (80), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
88
warning: Included by graph for 'message.h' not generated, too many nodes (97), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
99
warning: Included by graph for 'namespace.h' not generated, too many nodes (88), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
10-
warning: Included by graph for 'pointer_expr.h' not generated, too many nodes (121), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
10+
warning: Included by graph for 'pointer_expr.h' not generated, too many nodes (122), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
1111
warning: Included by graph for 'prefix.h' not generated, too many nodes (62), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
1212
warning: Included by graph for 'simplify_expr.h' not generated, too many nodes (67), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
1313
warning: Included by graph for 'std_code.h' not generated, too many nodes (71), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.

src/pointer-analysis/value_set.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,11 @@ void value_sett::get_value_set_rec(
784784
get_value_set_rec(*it, dest, suffix, original_type, ns);
785785
}
786786
}
787+
else if(expr.id() == ID_union)
788+
{
789+
get_value_set_rec(
790+
to_union_expr(expr).op(), dest, suffix, original_type, ns);
791+
}
787792
else if(expr.id()==ID_with)
788793
{
789794
const with_exprt &with_expr = to_with_expr(expr);

src/util/pointer_offset_size.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Author: Daniel Kroening, [email protected]
1616
#include "c_types.h"
1717
#include "invariant.h"
1818
#include "namespace.h"
19+
#include "pointer_expr.h"
1920
#include "simplify_expr.h"
2021
#include "ssa_expr.h"
2122
#include "std_expr.h"
@@ -681,6 +682,27 @@ optionalt<exprt> get_subexpression_at_offset(
681682
}
682683
}
683684
}
685+
else if(
686+
object_descriptor_exprt(expr).root_object().id() == ID_union &&
687+
source_type.id() == ID_union)
688+
{
689+
const union_typet &union_type = to_union_type(source_type);
690+
691+
for(const auto &component : union_type.components())
692+
{
693+
const auto m_size_bits = pointer_offset_bits(component.type(), ns);
694+
if(!m_size_bits.has_value())
695+
continue;
696+
697+
// if this member completely contains the target, recurse into it
698+
if(offset_bytes * 8 + *target_size_bits <= *m_size_bits)
699+
{
700+
const member_exprt member(expr, component.get_name(), component.type());
701+
return get_subexpression_at_offset(
702+
member, offset_bytes, target_type_raw, ns);
703+
}
704+
}
705+
}
684706

685707
return make_byte_extract(
686708
expr, from_integer(offset_bytes, index_type()), target_type_raw);

src/util/simplify_expr.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1765,6 +1765,71 @@ simplify_exprt::simplify_byte_extract(const byte_extract_exprt &expr)
17651765
return std::move(*tmp);
17661766
}
17671767

1768+
// push byte extracts into struct or union expressions, just like
1769+
// lower_byte_extract does (this is the same code, except recursive calls use
1770+
// simplify rather than lower_byte_extract)
1771+
if(expr.op().id() == ID_struct || expr.op().id() == ID_union)
1772+
{
1773+
if(expr.type().id() == ID_struct || expr.type().id() == ID_struct_tag)
1774+
{
1775+
const struct_typet &struct_type = to_struct_type(ns.follow(expr.type()));
1776+
const struct_typet::componentst &components = struct_type.components();
1777+
1778+
bool failed = false;
1779+
struct_exprt s({}, expr.type());
1780+
1781+
for(const auto &comp : components)
1782+
{
1783+
auto component_bits = pointer_offset_bits(comp.type(), ns);
1784+
1785+
// the next member would be misaligned, abort
1786+
if(
1787+
!component_bits.has_value() || *component_bits == 0 ||
1788+
*component_bits % 8 != 0)
1789+
{
1790+
failed = true;
1791+
break;
1792+
}
1793+
1794+
auto member_offset_opt =
1795+
member_offset_expr(struct_type, comp.get_name(), ns);
1796+
1797+
if(!member_offset_opt.has_value())
1798+
{
1799+
failed = true;
1800+
break;
1801+
}
1802+
1803+
exprt new_offset = simplify_rec(
1804+
plus_exprt{expr.offset(),
1805+
typecast_exprt::conditional_cast(
1806+
member_offset_opt.value(), expr.offset().type())});
1807+
1808+
byte_extract_exprt tmp = expr;
1809+
tmp.type() = comp.type();
1810+
tmp.offset() = new_offset;
1811+
1812+
s.add_to_operands(simplify_byte_extract(tmp));
1813+
}
1814+
1815+
if(!failed)
1816+
return s;
1817+
}
1818+
else if(expr.type().id() == ID_union || expr.type().id() == ID_union_tag)
1819+
{
1820+
const union_typet &union_type = to_union_type(ns.follow(expr.type()));
1821+
auto widest_member_opt = union_type.find_widest_union_component(ns);
1822+
if(widest_member_opt.has_value())
1823+
{
1824+
byte_extract_exprt be = expr;
1825+
be.type() = widest_member_opt->first.type();
1826+
return union_exprt{widest_member_opt->first.get_name(),
1827+
simplify_byte_extract(be),
1828+
expr.type()};
1829+
}
1830+
}
1831+
}
1832+
17681833
// try to refine it down to extracting from a member or an index in an array
17691834
auto subexpr =
17701835
get_subexpression_at_offset(expr.op(), *offset, expr.type(), ns);

0 commit comments

Comments
 (0)