Skip to content

Commit 2b27a2d

Browse files
authored
Merge pull request diffblue#2622 from martin-cs/feature/context-sensitive-ait-merge-2
Feature/context sensitive ait merge 2
2 parents 0f5a057 + 28ba192 commit 2b27a2d

8 files changed

+93
-103
lines changed

src/analyses/ai.cpp

Lines changed: 25 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ Author: Daniel Kroening, [email protected]
1515
#include <memory>
1616
#include <sstream>
1717

18-
#include <util/std_expr.h>
18+
#include <util/invariant.h>
1919
#include <util/std_code.h>
20+
#include <util/std_expr.h>
2021

2122
#include "is_threaded.h"
2223

@@ -50,7 +51,7 @@ void ai_baset::output(
5051
out << "**** " << i_it->location_number << " "
5152
<< i_it->source_location << "\n";
5253

53-
find_state(i_it).output(out, *this, ns);
54+
abstract_state_before(i_it)->output(out, *this, ns);
5455
out << "\n";
5556
#if 1
5657
goto_program.output_instruction(ns, identifier, out, *i_it);
@@ -101,7 +102,8 @@ jsont ai_baset::output_json(
101102
json_numbert(std::to_string(i_it->location_number));
102103
location["sourceLocation"]=
103104
json_stringt(i_it->source_location.as_string());
104-
location["abstractState"]=find_state(i_it).output_json(*this, ns);
105+
location["abstractState"] =
106+
abstract_state_before(i_it)->output_json(*this, ns);
105107

106108
// Ideally we need output_instruction_json
107109
std::ostringstream out;
@@ -162,7 +164,7 @@ xmlt ai_baset::output_xml(
162164
"source_location",
163165
i_it->source_location.as_string());
164166

165-
location.new_element(find_state(i_it).output_xml(*this, ns));
167+
location.new_element(abstract_state_before(i_it)->output_xml(*this, ns));
166168

167169
// Ideally we need output_instruction_xml
168170
std::ostringstream out;
@@ -219,7 +221,7 @@ void ai_baset::finalize()
219221
ai_baset::locationt ai_baset::get_next(
220222
working_sett &working_set)
221223
{
222-
assert(!working_set.empty());
224+
PRECONDITION(!working_set.empty());
223225

224226
working_sett::iterator i=working_set.begin();
225227
locationt l=i->second;
@@ -247,6 +249,7 @@ bool ai_baset::fixedpoint(
247249
{
248250
locationt l=get_next(working_set);
249251

252+
// goto_program is really only needed for iterator manipulation
250253
if(visit(l, working_set, goto_program, goto_functions, ns))
251254
new_data=true;
252255
}
@@ -322,6 +325,8 @@ bool ai_baset::do_function_call(
322325
// initialize state, if necessary
323326
get_state(l_return);
324327

328+
PRECONDITION(l_call->is_function_call());
329+
325330
const goto_functionst::goto_functiont &goto_function=
326331
f_it->second;
327332

@@ -387,69 +392,27 @@ bool ai_baset::do_function_call_rec(
387392
const goto_functionst &goto_functions,
388393
const namespacet &ns)
389394
{
390-
assert(!goto_functions.function_map.empty());
395+
PRECONDITION(!goto_functions.function_map.empty());
396+
397+
// This is quite a strong assumption on the well-formedness of the program.
398+
// It means function pointers must be removed before use.
399+
DATA_INVARIANT(
400+
function.id() == ID_symbol,
401+
"Function pointers and indirect calls must be removed before analysis.");
391402

392403
bool new_data=false;
393404

394-
if(function.id()==ID_symbol)
395-
{
396-
const irep_idt &identifier = to_symbol_expr(function).get_identifier();
405+
const irep_idt &identifier = to_symbol_expr(function).get_identifier();
397406

398-
goto_functionst::function_mapt::const_iterator it=
399-
goto_functions.function_map.find(identifier);
407+
goto_functionst::function_mapt::const_iterator it =
408+
goto_functions.function_map.find(identifier);
400409

401-
if(it==goto_functions.function_map.end())
402-
throw "failed to find function "+id2string(identifier);
410+
DATA_INVARIANT(
411+
it != goto_functions.function_map.end(),
412+
"Function " + id2string(identifier) + "not in function map");
403413

404-
new_data=do_function_call(
405-
l_call, l_return,
406-
goto_functions,
407-
it,
408-
arguments,
409-
ns);
410-
}
411-
else if(function.id()==ID_if)
412-
{
413-
if(function.operands().size()!=3)
414-
throw "if has three operands";
415-
416-
bool new_data1=
417-
do_function_call_rec(
418-
l_call, l_return,
419-
function.op1(),
420-
arguments,
421-
goto_functions,
422-
ns);
423-
424-
bool new_data2=
425-
do_function_call_rec(
426-
l_call, l_return,
427-
function.op2(),
428-
arguments,
429-
goto_functions,
430-
ns);
431-
432-
if(new_data1 || new_data2)
433-
new_data=true;
434-
}
435-
else if(function.id()==ID_dereference)
436-
{
437-
// We can't really do this here -- we rely on
438-
// these being removed by some previous analysis.
439-
}
440-
else if(function.id() == ID_null_object)
441-
{
442-
// ignore, can't be a function
443-
}
444-
else if(function.id()==ID_member || function.id()==ID_index)
445-
{
446-
// ignore, can't be a function
447-
}
448-
else
449-
{
450-
throw "unexpected function_call argument: "+
451-
function.id_string();
452-
}
414+
new_data =
415+
do_function_call(l_call, l_return, goto_functions, it, arguments, ns);
453416

454417
return new_data;
455418
}

src/analyses/ai.h

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ Author: Daniel Kroening, [email protected]
2525

2626
#include "ai_domain.h"
2727

28+
/// The basic interface of an abstract interpreter. This should be enough
29+
/// to create, run and query an abstract interpreter.
2830
// don't use me -- I am just a base class
2931
// use ait instead
3032
class ai_baset
@@ -41,6 +43,7 @@ class ai_baset
4143
{
4244
}
4345

46+
/// Running the interpreter
4447
void operator()(
4548
const goto_programt &goto_program,
4649
const namespacet &ns)
@@ -82,17 +85,26 @@ class ai_baset
8285
finalize();
8386
}
8487

88+
/// Accessing individual domains at particular locations
89+
/// (without needing to know what kind of domain or history is used)
90+
/// A pointer to a copy as the method should be const and
91+
/// there are some non-trivial cases including merging domains, etc.
92+
/// Intended for users of the abstract interpreter; don't use internally.
93+
8594
/// Returns the abstract state before the given instruction
86-
virtual const ai_domain_baset & abstract_state_before(
87-
goto_programt::const_targett t) const = 0;
95+
/// PRECONDITION(l is dereferenceable)
96+
virtual std::unique_ptr<statet> abstract_state_before(locationt l) const = 0;
8897

8998
/// Returns the abstract state after the given instruction
90-
virtual const ai_domain_baset & abstract_state_after(
91-
goto_programt::const_targett t) const
99+
virtual std::unique_ptr<statet> abstract_state_after(locationt l) const
92100
{
93-
return abstract_state_before(std::next(t));
101+
/// PRECONDITION(l is dereferenceable && std::next(l) is dereferenceable)
102+
/// Check relies on a DATA_INVARIANT of goto_programs
103+
INVARIANT(!l->is_end_function(), "No state after the last instruction");
104+
return abstract_state_before(std::next(l));
94105
}
95106

107+
/// Resets the domain
96108
virtual void clear()
97109
{
98110
}
@@ -237,6 +249,9 @@ class ai_baset
237249
const goto_functionst &goto_functions,
238250
const namespacet &ns);
239251

252+
// Visit performs one step of abstract interpretation from location l
253+
// Depending on the instruction type it may compute a number of "edges"
254+
// or applications of the abstract transformer
240255
// true = found something new
241256
bool visit(
242257
locationt l,
@@ -304,10 +319,17 @@ class ait:public ai_baset
304319
return it->second;
305320
}
306321

307-
const ai_domain_baset & abstract_state_before(
308-
goto_programt::const_targett t) const override
322+
std::unique_ptr<statet> abstract_state_before(locationt t) const override
309323
{
310-
return (*this)[t];
324+
typename state_mapt::const_iterator it = state_map.find(t);
325+
if(it == state_map.end())
326+
{
327+
std::unique_ptr<statet> d = util_make_unique<domainT>();
328+
CHECK_RETURN(d->is_bottom());
329+
return d;
330+
}
331+
332+
return util_make_unique<domainT>(it->second);
311333
}
312334

313335
void clear() override

src/analyses/constant_propagator.cpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -592,43 +592,45 @@ void constant_propagator_ait::replace(
592592
{
593593
Forall_goto_program_instructions(it, goto_function.body)
594594
{
595-
state_mapt::iterator s_it=state_map.find(it);
595+
// Works because this is a location (but not history) sensitive domain
596+
const constant_propagator_domaint &d = (*this)[it];
596597

597-
if(s_it==state_map.end())
598+
if(d.is_bottom())
598599
continue;
599600

600-
replace_types_rec(s_it->second.values.replace_const, it->code);
601-
replace_types_rec(s_it->second.values.replace_const, it->guard);
601+
replace_types_rec(d.values.replace_const, it->code);
602+
replace_types_rec(d.values.replace_const, it->guard);
602603

603604
if(it->is_goto() || it->is_assume() || it->is_assert())
604605
{
605-
s_it->second.partial_evaluate(it->guard, ns);
606+
d.partial_evaluate(it->guard, ns);
606607
}
607608
else if(it->is_assign())
608609
{
609610
exprt &rhs=to_code_assign(it->code).rhs();
610-
s_it->second.partial_evaluate(rhs, ns);
611+
d.partial_evaluate(rhs, ns);
612+
611613
if(rhs.id()==ID_constant)
612614
rhs.add_source_location()=it->code.op0().source_location();
613615
}
614616
else if(it->is_function_call())
615617
{
616618
exprt &function = to_code_function_call(it->code).function();
617-
s_it->second.partial_evaluate(function, ns);
619+
d.partial_evaluate(function, ns);
618620

619621
exprt::operandst &args=
620622
to_code_function_call(it->code).arguments();
621623

622624
for(auto &arg : args)
623625
{
624-
s_it->second.partial_evaluate(arg, ns);
626+
d.partial_evaluate(arg, ns);
625627
}
626628
}
627629
else if(it->is_other())
628630
{
629631
if(it->code.get_statement()==ID_expression)
630632
{
631-
s_it->second.partial_evaluate(it->code, ns);
633+
d.partial_evaluate(it->code, ns);
632634
}
633635
}
634636
}

src/analyses/dependence_graph.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -307,9 +307,9 @@ void dependence_grapht::add_dep(
307307
goto_programt::const_targett from,
308308
goto_programt::const_targett to)
309309
{
310-
const node_indext n_from=state_map[from].get_node_id();
310+
const node_indext n_from = (*this)[from].get_node_id();
311311
assert(n_from<size());
312-
const node_indext n_to=state_map[to].get_node_id();
312+
const node_indext n_to = (*this)[to].get_node_id();
313313
assert(n_to<size());
314314

315315
// add_edge is redundant as the subsequent operations also insert

src/analyses/invariant_propagation.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ void invariant_propagationt::initialize(const goto_programt &goto_program)
206206

207207
forall_goto_program_instructions(it, goto_program)
208208
{
209-
invariant_sett &s=state_map[it].invariant_set;
209+
invariant_sett &s = (*this)[it].invariant_set;
210210

211211
if(it==goto_program.instructions.begin())
212212
s.make_true();
@@ -243,11 +243,11 @@ void invariant_propagationt::simplify(goto_programt &goto_program)
243243
continue;
244244

245245
// find invariant set
246-
state_mapt::const_iterator s_it=state_map.find(i_it);
247-
if(s_it==state_map.end())
246+
const auto &d = (*this)[i_it];
247+
if(d.is_bottom())
248248
continue;
249249

250-
const invariant_sett &invariant_set=s_it->second.invariant_set;
250+
const invariant_sett &invariant_set = d.invariant_set;
251251

252252
exprt simplified_guard(i_it->guard);
253253

src/goto-analyzer/static_simplifier.cpp

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ bool static_simplifier(
6565

6666
if(i_it->is_assert())
6767
{
68-
bool unchanged=
69-
ai.abstract_state_before(i_it).ai_simplify(i_it->guard, ns);
68+
bool unchanged =
69+
ai.abstract_state_before(i_it)->ai_simplify(i_it->guard, ns);
7070

7171
if(unchanged)
7272
unmodified.asserts++;
@@ -75,8 +75,8 @@ bool static_simplifier(
7575
}
7676
else if(i_it->is_assume())
7777
{
78-
bool unchanged=
79-
ai.abstract_state_before(i_it).ai_simplify(i_it->guard, ns);
78+
bool unchanged =
79+
ai.abstract_state_before(i_it)->ai_simplify(i_it->guard, ns);
8080

8181
if(unchanged)
8282
unmodified.assumes++;
@@ -85,8 +85,8 @@ bool static_simplifier(
8585
}
8686
else if(i_it->is_goto())
8787
{
88-
bool unchanged=
89-
ai.abstract_state_before(i_it).ai_simplify(i_it->guard, ns);
88+
bool unchanged =
89+
ai.abstract_state_before(i_it)->ai_simplify(i_it->guard, ns);
9090

9191
if(unchanged)
9292
unmodified.gotos++;
@@ -102,11 +102,11 @@ bool static_simplifier(
102102
// <i=0, j=1> i=j
103103
// should simplify to i=1, not to 0=1.
104104

105-
bool unchanged_lhs=
106-
ai.abstract_state_before(i_it).ai_simplify_lhs(assign.lhs(), ns);
105+
bool unchanged_lhs =
106+
ai.abstract_state_before(i_it)->ai_simplify_lhs(assign.lhs(), ns);
107107

108-
bool unchanged_rhs=
109-
ai.abstract_state_before(i_it).ai_simplify(assign.rhs(), ns);
108+
bool unchanged_rhs =
109+
ai.abstract_state_before(i_it)->ai_simplify(assign.rhs(), ns);
110110

111111
if(unchanged_lhs && unchanged_rhs)
112112
unmodified.assigns++;
@@ -117,13 +117,13 @@ bool static_simplifier(
117117
{
118118
code_function_callt &fcall=to_code_function_call(i_it->code);
119119

120-
bool unchanged=
121-
ai.abstract_state_before(i_it).ai_simplify(fcall.function(), ns);
120+
bool unchanged =
121+
ai.abstract_state_before(i_it)->ai_simplify(fcall.function(), ns);
122122

123123
exprt::operandst &args=fcall.arguments();
124124

125125
for(auto &o : args)
126-
unchanged&=ai.abstract_state_before(i_it).ai_simplify(o, ns);
126+
unchanged &= ai.abstract_state_before(i_it)->ai_simplify(o, ns);
127127

128128
if(unchanged)
129129
unmodified.function_calls++;

0 commit comments

Comments
 (0)