Skip to content

Commit 551ac6e

Browse files
authored
Merge pull request #5914 from TGWDB/optimize-expr-size
Refactor and optimise size to have approximate bound
2 parents f899754 + ef2f3e1 commit 551ac6e

File tree

5 files changed

+60
-20
lines changed

5 files changed

+60
-20
lines changed

src/goto-symex/complexity_limiter.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ complexity_limitert::check_complexity(goto_symex_statet &state)
136136
if(!complexity_limits_active() || !state.reachable)
137137
return complexity_violationt::NONE;
138138

139-
std::size_t complexity = state.complexity();
140-
if(complexity == 0)
139+
std::size_t complexity = state.guard.as_expr().bounded_size(max_complexity);
140+
if(complexity == 1)
141141
return complexity_violationt::NONE;
142142

143143
auto &current_call_stack = state.call_stack();

src/goto-symex/goto_state.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,6 @@ class goto_statet
7171
/// Threads
7272
unsigned atomic_section_id = 0;
7373

74-
/// Get the complexity for this state. Used to short-circuit states if they
75-
/// have become too computationally complex.
76-
inline std::size_t complexity()
77-
{
78-
// TODO: This isn't too efficent for BDDs, try using a different size
79-
// like DAG size.
80-
return guard.as_expr().size();
81-
}
82-
8374
/// Constructors
8475
goto_statet() = delete;
8576
goto_statet &operator=(const goto_statet &other) = delete;

src/util/expr.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,28 @@ Author: Daniel Kroening, [email protected]
2424

2525
#include <stack>
2626

27-
std::size_t exprt::size() const
27+
/// Returns the size of the exprt added to count without searching significantly
28+
/// beyond the supplied limit.
29+
std::size_t exprt::bounded_size(std::size_t count, std::size_t limit) const
2830
{
29-
// Initial size of 1 to count self.
30-
std::size_t size = 1;
31-
for(const auto &op : operands())
31+
const auto &ops = operands();
32+
count += ops.size();
33+
for(const auto &op : ops)
3234
{
33-
size += op.size();
35+
if(count >= limit)
36+
{
37+
return count;
38+
}
39+
count = op.bounded_size(count, limit);
3440
}
41+
return count;
42+
}
3543

36-
return size;
44+
/// Returns the size of the exprt without significantly searching beyond the
45+
/// supplied limit.
46+
std::size_t exprt::bounded_size(std::size_t limit) const
47+
{
48+
return bounded_size(1, limit);
3749
}
3850

3951
/// Return whether the expression is a constant.

src/util/expr.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,9 @@ class exprt:public irept
8585
return static_cast<const typet &>(find(ID_type));
8686
}
8787

88-
/// Amount of nodes this expression tree contains. This is the size of the
89-
/// actual tree, ignoring memory/sub-tree sharing.
90-
std::size_t size() const;
88+
/// Amount of nodes in this expression tree approximately bounded by limit.
89+
/// This is the size of the actual tree, ignoring memory/sub-tree sharing.
90+
std::size_t bounded_size(std::size_t limit) const;
9191

9292
/// Return true if there is at least one operand.
9393
bool has_operands() const
@@ -124,6 +124,10 @@ class exprt:public irept
124124
const exprt &op3() const
125125
{ return operands()[3]; }
126126

127+
/// Amount of nodes this expression tree contains, with a bound on how far
128+
/// to search. Starts with an existing count.
129+
std::size_t bounded_size(std::size_t count, std::size_t limit) const;
130+
127131
public:
128132
void reserve_operands(operandst::size_type n)
129133
{ operands().reserve(n) ; }

unit/util/expr.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,36 @@ SCENARIO("bitfield-expr-is-zero", "[core][util][expr]")
3636
}
3737
}
3838
}
39+
40+
TEST_CASE("Bounded size is computer correctly", "[core][util][expr]")
41+
{
42+
// Helper types and functions for constructing test expressions.
43+
const typet type = signedbv_typet(32);
44+
std::function<exprt(size_t)> make_expression;
45+
make_expression = [&](size_t size) -> exprt {
46+
PRECONDITION(size != 0);
47+
if(size <= 1)
48+
return from_integer(1, type);
49+
if(size == 2)
50+
return unary_minus_exprt(from_integer(1, type));
51+
return plus_exprt(from_integer(1, type), make_expression(size - 2));
52+
};
53+
// Actual test cases.
54+
REQUIRE(make_expression(1).bounded_size(10) == 1);
55+
REQUIRE(make_expression(2).bounded_size(10) == 2);
56+
REQUIRE(make_expression(3).bounded_size(10) == 3);
57+
58+
REQUIRE(make_expression(30).bounded_size(10) < 13);
59+
REQUIRE(make_expression(30).bounded_size(10) >= 10);
60+
}
61+
62+
TEST_CASE("Bounded size versus pathological example", "[core][util][expr]")
63+
{
64+
const typet type = signedbv_typet(32);
65+
exprt pathology = plus_exprt(from_integer(1, type), from_integer(1, type));
66+
// Create an infinite exprt by self reference!
67+
pathology.add_to_operands(pathology);
68+
// If bounded size is not checking the bound this will never terminate.
69+
REQUIRE(pathology.bounded_size(10) < 13);
70+
REQUIRE(pathology.bounded_size(10) >= 10);
71+
}

0 commit comments

Comments
 (0)