Skip to content

Commit 7e0f829

Browse files
committed
Simplify disjunctions over equalities to intervals
Disjunctions over equalities of integer constants can be rewritten as closed intervals. This will most commonly appear when expanding enum_is_in_range. This also surfaced a bug in to_integer, which used the wrong type reference.
1 parent 8e39116 commit 7e0f829

File tree

5 files changed

+84
-5
lines changed

5 files changed

+84
-5
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
enum_test3.c
3+
--show-goto-functions
4+
0 ≤ cast\(main::1::ev1, (un)?signedbv\[\d+\]\) ∧ cast\(main::1::ev1, (un)?signedbv\[\d+\]\) ≤ 4$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
main::1::ev1 = 0
9+
--
10+
This test is to demonstrate simplification of enumerated ranges of integers.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
['integer-assignments1', 'integer-typecheck.desc'],
2626
['destructors', 'compound_literal.desc'],
2727
['destructors', 'enter_lexical_block.desc'],
28+
['enum_is_in_range', 'enum_test3-simplified.desc'],
2829
['enum_is_in_range', 'format.desc'],
2930
['r_w_ok9', 'simplify.desc'],
3031
['reachability-slice-interproc2', 'test.desc'],

src/goto-programs/builtin_functions.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,7 @@ void goto_convertt::do_enum_is_in_range(
636636
disjuncts.push_back(equal_exprt(enum_expr, std::move(val)));
637637
}
638638

639-
code_assignt assignment(lhs, disjunction(disjuncts));
639+
code_assignt assignment(lhs, simplify_expr(disjunction(disjuncts), ns));
640640
assignment.add_source_location() = function.source_location();
641641
assignment.add_source_location().add_pragma("disable:enum-range-check");
642642
copy(assignment, ASSIGN, dest);

src/util/arith_tools.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,13 @@ bool to_integer(const constant_exprt &expr, mp_integer &int_value)
5959
const typet &underlying_type = to_c_enum_type(type).underlying_type();
6060
if(underlying_type.id() == ID_signedbv)
6161
{
62-
const auto width = to_signedbv_type(type).get_width();
62+
const auto width = to_signedbv_type(underlying_type).get_width();
6363
int_value = bvrep2integer(value, width, true);
6464
return false;
6565
}
6666
else if(underlying_type.id() == ID_unsignedbv)
6767
{
68-
const auto width = to_unsignedbv_type(type).get_width();
68+
const auto width = to_unsignedbv_type(underlying_type).get_width();
6969
int_value = bvrep2integer(value, width, false);
7070
return false;
7171
}

src/util/simplify_expr_boolean.cpp

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@ Author: Daniel Kroening, [email protected]
88

99
#include "simplify_expr_class.h"
1010

11-
#include <unordered_set>
12-
11+
#include "arith_tools.h"
12+
#include "c_types.h"
1313
#include "expr_util.h"
1414
#include "mathematical_expr.h"
15+
#include "namespace.h"
1516
#include "std_expr.h"
1617

18+
#include <unordered_set>
19+
1720
simplify_exprt::resultt<> simplify_exprt::simplify_boolean(const exprt &expr)
1821
{
1922
if(!expr.has_operands())
@@ -96,6 +99,8 @@ simplify_exprt::resultt<> simplify_exprt::simplify_boolean(const exprt &expr)
9699
std::unordered_set<exprt, irep_hash> expr_set;
97100

98101
bool no_change = true;
102+
bool may_be_reducible_to_interval =
103+
expr.id() == ID_or && expr.operands().size() > 2;
99104

100105
exprt::operandst new_operands = expr.operands();
101106

@@ -127,7 +132,70 @@ simplify_exprt::resultt<> simplify_exprt::simplify_boolean(const exprt &expr)
127132
no_change = false;
128133
}
129134
else
135+
{
136+
if(may_be_reducible_to_interval)
137+
may_be_reducible_to_interval = it->id() == ID_equal;
130138
it++;
139+
}
140+
}
141+
142+
if(may_be_reducible_to_interval)
143+
{
144+
optionalt<symbol_exprt> symbol_opt;
145+
std::set<mp_integer> values;
146+
for(const exprt &op : new_operands)
147+
{
148+
equal_exprt eq = to_equal_expr(op);
149+
if(eq.lhs().is_constant())
150+
std::swap(eq.lhs(), eq.rhs());
151+
if(auto s = expr_try_dynamic_cast<symbol_exprt>(eq.lhs()))
152+
{
153+
if(!symbol_opt.has_value())
154+
symbol_opt = *s;
155+
156+
if(*s == *symbol_opt)
157+
{
158+
if(auto c = expr_try_dynamic_cast<constant_exprt>(eq.rhs()))
159+
{
160+
constant_exprt c_tmp = *c;
161+
if(c_tmp.type().id() == ID_c_enum_tag)
162+
c_tmp.type() = ns.follow_tag(to_c_enum_tag_type(c_tmp.type()));
163+
if(auto int_opt = numeric_cast<mp_integer>(c_tmp))
164+
{
165+
values.insert(*int_opt);
166+
continue;
167+
}
168+
}
169+
}
170+
}
171+
172+
symbol_opt.reset();
173+
break;
174+
}
175+
176+
if(symbol_opt.has_value() && values.size() >= 3)
177+
{
178+
mp_integer lower = *values.begin();
179+
mp_integer upper = *std::prev(values.end());
180+
if(upper - lower + 1 == mp_integer{values.size()})
181+
{
182+
typet type = symbol_opt->type();
183+
if(symbol_opt->type().id() == ID_c_enum_tag)
184+
{
185+
type = ns.follow_tag(to_c_enum_tag_type(symbol_opt->type()))
186+
.underlying_type();
187+
}
188+
189+
less_than_or_equal_exprt lb{
190+
from_integer(lower, type),
191+
typecast_exprt::conditional_cast(*symbol_opt, type)};
192+
less_than_or_equal_exprt ub{
193+
typecast_exprt::conditional_cast(*symbol_opt, type),
194+
from_integer(upper, type)};
195+
196+
return and_exprt{lb, ub};
197+
}
198+
}
131199
}
132200

133201
// search for a and !a

0 commit comments

Comments
 (0)