Skip to content

Commit aa069d3

Browse files
authored
Merge pull request #5568 from jezhiggins/expression-transform
Expression transform
2 parents 7ff70ee + 16300bd commit aa069d3

File tree

5 files changed

+231
-0
lines changed

5 files changed

+231
-0
lines changed

src/analyses/variable-sensitivity/value_set_abstract_value.cpp

+90
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "value_set_abstract_value.h"
1010

1111
#include <ansi-c/expr2c.h>
12+
#include <util/simplify_expr.h>
1213

1314
value_set_abstract_valuet::value_set_abstract_valuet(
1415
const typet &type,
@@ -125,3 +126,92 @@ void value_set_abstract_valuet::output(
125126
}
126127
out << "}";
127128
}
129+
130+
static void merge_all_possible_results(
131+
std::shared_ptr<const value_set_abstract_valuet> &out_value,
132+
const exprt &expr,
133+
const std::vector<const value_set_abstract_valuet *> &operand_value_sets,
134+
const namespacet &ns,
135+
std::vector<exprt> &operands_so_far)
136+
{
137+
if(expr.operands().size() == operands_so_far.size())
138+
{
139+
exprt expr_with_evaluated_operands_filled_in = expr;
140+
expr_with_evaluated_operands_filled_in.operands() = operands_so_far;
141+
simplify(expr_with_evaluated_operands_filled_in, ns);
142+
if(expr_with_evaluated_operands_filled_in.is_constant())
143+
{
144+
bool out_modifications;
145+
auto post_merge = abstract_objectt::merge(
146+
out_value,
147+
std::make_shared<value_set_abstract_valuet>(
148+
expr.type(),
149+
value_set_abstract_valuet::valuest{
150+
expr_with_evaluated_operands_filled_in}),
151+
out_modifications);
152+
if(
153+
auto post_merge_casted =
154+
std::dynamic_pointer_cast<const value_set_abstract_valuet>(
155+
post_merge))
156+
{
157+
out_value = post_merge_casted;
158+
return;
159+
}
160+
}
161+
out_value = std::make_shared<value_set_abstract_valuet>(expr.type());
162+
}
163+
else
164+
{
165+
for(auto const &operand_value :
166+
operand_value_sets[operands_so_far.size()]->get_values())
167+
{
168+
operands_so_far.push_back(operand_value);
169+
merge_all_possible_results(
170+
out_value, expr, operand_value_sets, ns, operands_so_far);
171+
operands_so_far.pop_back();
172+
}
173+
}
174+
}
175+
176+
static std::shared_ptr<const value_set_abstract_valuet>
177+
merge_all_possible_results(
178+
const exprt &expr,
179+
const std::vector<const value_set_abstract_valuet *> &operand_value_sets,
180+
const namespacet &ns)
181+
{
182+
const bool is_top = false;
183+
const bool is_bottom = true;
184+
auto result_abstract_value =
185+
std::make_shared<const value_set_abstract_valuet>(
186+
expr.type(), is_top, is_bottom);
187+
auto operands_so_far = std::vector<exprt>{};
188+
merge_all_possible_results(
189+
result_abstract_value, expr, operand_value_sets, ns, operands_so_far);
190+
return result_abstract_value;
191+
}
192+
193+
abstract_object_pointert value_set_abstract_valuet::expression_transform(
194+
const exprt &expr,
195+
const std::vector<abstract_object_pointert> &operands,
196+
const abstract_environmentt &environment,
197+
const namespacet &ns) const
198+
{
199+
// TODO possible special case handling for things like
200+
// __CPROVER_rounding_mode where we know what valid values look like
201+
// which we could make use of in place of TOP
202+
auto operand_value_sets = std::vector<const value_set_abstract_valuet *>{};
203+
for(auto const &possible_value_set : operands)
204+
{
205+
PRECONDITION(possible_value_set != nullptr);
206+
const auto as_value_set =
207+
dynamic_cast<const value_set_abstract_valuet *>(possible_value_set.get());
208+
if(
209+
as_value_set == nullptr || as_value_set->is_top() ||
210+
as_value_set->is_bottom())
211+
{
212+
return std::make_shared<value_set_abstract_valuet>(expr.type());
213+
}
214+
operand_value_sets.push_back(as_value_set);
215+
}
216+
return merge_all_possible_results(expr, operand_value_sets, ns);
217+
}

src/analyses/variable-sensitivity/value_set_abstract_value.h

+6
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ class value_set_abstract_valuet : public abstract_valuet
4444

4545
exprt to_constant() const override;
4646

47+
abstract_object_pointert expression_transform(
48+
const exprt &expr,
49+
const std::vector<abstract_object_pointert> &operands,
50+
const abstract_environmentt &environment,
51+
const namespacet &ns) const override;
52+
4753
void output(std::ostream &out, const class ai_baset &ai, const namespacet &ns)
4854
const override;
4955

src/analyses/variable-sensitivity/variable_sensitivity_object_factory.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "variable_sensitivity_object_factory.h"
99
#include "interval_array_abstract_object.h"
1010
#include "util/namespace.h"
11+
#include "value_set_abstract_value.h"
1112

1213
variable_sensitivity_object_factoryt
1314
variable_sensitivity_object_factoryt::s_instance;
@@ -24,10 +25,18 @@ variable_sensitivity_object_factoryt::get_abstract_object_type(const typet type)
2425
{
2526
abstract_object_type =
2627
configuration.advanced_sensitivities.intervals ? INTERVAL : CONSTANT;
28+
if(configuration.advanced_sensitivities.new_value_set)
29+
{
30+
abstract_object_type = VALUE_SET;
31+
}
2732
}
2833
else if(type.id() == ID_floatbv)
2934
{
3035
abstract_object_type = CONSTANT;
36+
if(configuration.advanced_sensitivities.new_value_set)
37+
{
38+
abstract_object_type = VALUE_SET;
39+
}
3140
}
3241
else if(type.id() == ID_array)
3342
{
@@ -121,6 +130,11 @@ variable_sensitivity_object_factoryt::get_abstract_object(
121130
return initialize_abstract_object<abstract_objectt>(
122131
followed_type, top, bottom, e, environment, ns);
123132
case VALUE_SET:
133+
if(configuration.advanced_sensitivities.new_value_set)
134+
{
135+
return initialize_abstract_object<value_set_abstract_valuet>(
136+
followed_type, top, bottom, e, environment, ns);
137+
}
124138
return initialize_abstract_object<value_set_abstract_objectt>(
125139
followed_type, top, bottom, e, environment, ns);
126140
default:

src/analyses/variable-sensitivity/variable_sensitivity_object_factory.h

+3
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ struct vsd_configt
5050
{
5151
bool intervals;
5252
bool value_set;
53+
bool new_value_set;
5354
} advanced_sensitivities;
5455

5556
static vsd_configt from_options(const optionst &options)
@@ -83,6 +84,8 @@ struct vsd_configt
8384
options.get_bool_option("interval");
8485
config.advanced_sensitivities.value_set =
8586
options.get_bool_option("value-set");
87+
config.advanced_sensitivities.new_value_set =
88+
options.get_bool_option("new-value-set");
8689

8790
return config;
8891
}

unit/analyses/variable-sensitivity/value_set/abstract_value.cpp

+118
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Author: Diffblue Ltd.
1212
#include <analyses/variable-sensitivity/variable_sensitivity_domain.h>
1313
#include <ansi-c/expr2c.h>
1414

15+
#include <analyses/variable-sensitivity/variable_sensitivity_object_factory.h>
1516
#include <sstream>
1617
#include <string>
1718
#include <util/arith_tools.h>
@@ -57,6 +58,7 @@ class ContainsAllOf
5758
{
5859
oss << ", ";
5960
}
61+
first = false;
6062
oss << expr2c(value, ns);
6163
}
6264
oss << " }";
@@ -359,3 +361,119 @@ TEST_CASE(
359361

360362
REQUIRE(value_set.to_constant() == value);
361363
}
364+
365+
static abstract_environmentt get_value_set_abstract_environment()
366+
{
367+
vsd_configt config;
368+
config.advanced_sensitivities.new_value_set = true;
369+
config.context_tracking.data_dependency_context = false;
370+
config.context_tracking.last_write_context = false;
371+
variable_sensitivity_object_factoryt::instance().set_options(config);
372+
auto environment = abstract_environmentt{};
373+
environment.make_top();
374+
return environment;
375+
}
376+
377+
TEST_CASE("Eval on an constant gives us that constant", VALUE_SET_TEST_TAGS)
378+
{
379+
const auto environment = get_value_set_abstract_environment();
380+
namespacet ns{symbol_tablet{}};
381+
const auto zero = from_integer(0, signedbv_typet{32});
382+
const auto eval_result = environment.eval(zero, ns);
383+
REQUIRE(eval_result != nullptr);
384+
REQUIRE(!eval_result->is_top());
385+
REQUIRE(!eval_result->is_bottom());
386+
const auto eval_result_as_value_set =
387+
std::dynamic_pointer_cast<const value_set_abstract_valuet>(
388+
eval_result->unwrap_context());
389+
REQUIRE(eval_result_as_value_set != nullptr);
390+
REQUIRE(eval_result_as_value_set->get_values().size() == 1);
391+
REQUIRE_THAT(eval_result_as_value_set->get_values(), ContainsAllOf{zero});
392+
}
393+
394+
TEST_CASE(
395+
"Eval on a plus expression with constant operands gives us the result of "
396+
"that plus",
397+
VALUE_SET_TEST_TAGS)
398+
{
399+
const auto environment = get_value_set_abstract_environment();
400+
namespacet ns{symbol_tablet{}};
401+
const auto s32_type = signedbv_typet{32};
402+
const auto three = from_integer(3, s32_type);
403+
const auto five = from_integer(5, s32_type);
404+
const auto three_plus_five = plus_exprt{three, five};
405+
406+
auto eval_result = environment.eval(three_plus_five, ns);
407+
REQUIRE(eval_result != nullptr);
408+
REQUIRE(!eval_result->is_top());
409+
REQUIRE(!eval_result->is_bottom());
410+
411+
const auto eval_result_as_value_set =
412+
std::dynamic_pointer_cast<const value_set_abstract_valuet>(
413+
eval_result->unwrap_context());
414+
REQUIRE(eval_result_as_value_set != nullptr);
415+
const auto eight = from_integer(8, s32_type);
416+
REQUIRE(eval_result_as_value_set->get_values().size() == 1);
417+
REQUIRE_THAT(eval_result_as_value_set->get_values(), ContainsAllOf{eight});
418+
}
419+
420+
TEST_CASE(
421+
"Eval on a multiply expression on symbols gives us the results of that "
422+
"multiplication",
423+
VALUE_SET_TEST_TAGS)
424+
{
425+
auto environment = get_value_set_abstract_environment();
426+
symbol_tablet symbol_table;
427+
428+
const auto s32_type = signedbv_typet{32};
429+
430+
symbolt a_symbol{};
431+
a_symbol.name = a_symbol.pretty_name = a_symbol.base_name = "a";
432+
a_symbol.is_lvalue = true;
433+
a_symbol.type = s32_type;
434+
symbol_table.add(a_symbol);
435+
436+
symbolt b_symbol{};
437+
b_symbol.name = b_symbol.pretty_name = b_symbol.base_name = "b";
438+
b_symbol.is_lvalue = true;
439+
b_symbol.type = s32_type;
440+
symbol_table.add(b_symbol);
441+
symbol_table.add(b_symbol);
442+
443+
const namespacet ns{symbol_table};
444+
445+
const auto three = from_integer(3, s32_type);
446+
const auto four = from_integer(4, s32_type);
447+
const auto five = from_integer(5, s32_type);
448+
const auto six = from_integer(6, s32_type);
449+
450+
const auto three_or_five = std::make_shared<const value_set_abstract_valuet>(
451+
s32_type, value_set_abstract_valuet::valuest{three, five});
452+
environment.assign(a_symbol.symbol_expr(), three_or_five, ns);
453+
454+
const auto four_or_six = std::make_shared<const value_set_abstract_valuet>(
455+
s32_type, value_set_abstract_valuet::valuest{four, six});
456+
environment.assign(b_symbol.symbol_expr(), four_or_six, ns);
457+
458+
const auto a_times_b =
459+
mult_exprt{a_symbol.symbol_expr(), b_symbol.symbol_expr()};
460+
461+
const auto eval_result = environment.eval(a_times_b, ns);
462+
REQUIRE(eval_result != nullptr);
463+
REQUIRE(!eval_result->is_top());
464+
REQUIRE(!eval_result->is_bottom());
465+
466+
const auto eval_result_as_value_set =
467+
std::dynamic_pointer_cast<const value_set_abstract_valuet>(
468+
eval_result->unwrap_context());
469+
REQUIRE(eval_result_as_value_set != nullptr);
470+
REQUIRE(eval_result_as_value_set->get_values().size() == 4);
471+
472+
const auto twelve = from_integer(12, s32_type);
473+
const auto eighteen = from_integer(18, s32_type);
474+
const auto twenty = from_integer(20, s32_type);
475+
const auto twentyfour = from_integer(30, s32_type);
476+
REQUIRE_THAT(
477+
eval_result_as_value_set->get_values(),
478+
ContainsAllOf(twelve, eighteen, twenty, twentyfour));
479+
}

0 commit comments

Comments
 (0)