CBMC
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
trace_automaton.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module: Loop Acceleration
4
5Author: Matt Lewis
6
7\*******************************************************************/
8
11
12#include "trace_automaton.h"
13
14#include <utility>
15#include <iostream>
16#include <limits>
17
18#include <util/invariant.h>
19
20#include "path.h"
21
22const statet automatont::no_state=std::numeric_limits<statet>::max();
23
25{
26#ifdef DEBUG
27 std::cout << "NTA:\n";
28 nta.output(std::cout);
29#endif
30
32 // minimise();
33
34#ifdef DEBUG
35 std::cout << "DTA:\n";
36 dta.output(std::cout);
37#endif
38}
39
40/*
41 * Build the trace automaton alphabet.
42 *
43 * The alphabet is the set of distinguishing points, i.e. the
44 * successors of any location with multiple successors.
45 */
47{
49 {
50 const auto succs=program.get_successors(it);
51 if(succs.size()>1)
52 {
53 alphabet.insert(succs.begin(), succs.end());
54 }
55 }
56}
57
65
66/*
67 * Add a path to the trace automaton.
68 */
70{
71 statet state;
72
73 state=nta.add_state();
75
76#ifdef DEBUG
77 std::cout << "Adding path: ";
78#endif
79
80 for(const auto &step : path)
81 {
82 goto_programt::targett l=step.loc;
83
84#ifdef DEBUG
85 std::cout << ", " << l->location_number << ':'
86 << l->source_location().as_string();
87#endif
88
89 if(in_alphabet(l))
90 {
91#ifdef DEBUG
92 std::cout << "(*) ";
93#endif
94
96 nta.add_trans(state, l, new_state);
97 state=new_state;
98 }
99 }
100
101#ifdef DEBUG
102 std::cout << '\n';
103
104 std::cout << "Final state: " << state << '\n';
105#endif
106
107 nta.set_accepting(state);
108}
109
110/*
111 * Use the subset construction (cf. the Dragon book)
112 * to convert a nondeterministic trace automaton (NTA) to
113 * a deterministic one (DTA).
114 */
116{
117#ifdef DEBUG
118 std::cout << "Determinising automaton with " << nta.num_states
119 << " states and " << nta.accept_states.size()
120 << " accept states and " << nta.count_transitions()
121 << " transitions\n";
122#endif
123
124 dstates.clear();
125 unmarked_dstates.clear();
126 dta.clear();
127
129 init_states.insert(nta.init_state);
131
132#ifdef DEBUG
133 std::cout << "There are " << init_states.size() << " init states\n";
134#endif
135
137
138 while(!unmarked_dstates.empty())
139 {
140 state_sett t;
142 INVARIANT(
144 "Removed state has actually been removed");
145
146
147 // For each symbol a such that there is a transition
148 //
149 // s -a-> s'
150 //
151 // for some s in t, find the states that are reachable
152 // using a-transitions and add them as a new state.
153 for(alphabett::iterator it=alphabet.begin();
154 it!=alphabet.end();
155 ++it)
156 {
157 if(*it==epsilon)
158 {
159 continue;
160 }
161
163
164 nta.move(t, *it, u);
166
167 add_dstate(u);
168 add_dtrans(t, *it, u);
169 }
170 }
171
172 dta.trim();
173
174#ifdef DEBUG
175 std::cout << "Produced DTA with " << dta.num_states << " states and "
176 << dta.accept_states.size() << " accept states and "
177 << dta.count_transitions() << " transitions\n";
178#endif
179}
180
186
187/*
188 * Calculate the epsilon closure of a set of states in a NTA.
189 */
191{
192 std::vector<statet> queue(states.size());
193
194 // Initialise -- fill queue with states.
195 for(state_sett::iterator it=states.begin();
196 it!=states.end();
197 ++it)
198 {
199 queue.push_back(*it);
200 }
201
202 // Take epsilon transitions until we can take no more.
203 while(!queue.empty())
204 {
205 statet state=queue.back();
207
208 queue.pop_back();
209
210 nta.move(state, epsilon, next_states);
211
212 // Check if we've arrived at any fresh states. If so, recurse on them.
213 for(state_sett::iterator it=next_states.begin();
214 it!=next_states.end();
215 ++it)
216 {
217 if(states.find(*it)==states.end())
218 {
219 // This is a new state. Add it to the state set & enqueue it.
220 states.insert(*it);
221 queue.push_back(*it);
222 }
223 }
224 }
225}
226
227/*
228 * Create a new (unmarked) state in the DTA if the state has not been added
229 * before.
230 */
232{
234
236 {
237 // We've added this state before. Don't need to do it again.
238 return state_num;
239 }
240
243 unmarked_dstates.push_back(s);
244 INVARIANT(
245 dstates.find(s)!=dstates.end(),
246 "Added state has actually been added");
247
248 for(state_sett::iterator it=s.begin();
249 it!=s.end();
250 ++it)
251 {
252 if(nta.is_accepting(*it))
253 {
254#ifdef DEBUG
255 std::cout << "NTA state " << *it
256 << " is accepting, so accepting DTA state "
257 << state_num << '\n';
258#endif
259
261 break;
262 }
263 }
264
265 return state_num;
266}
267
269{
270 state_mapt::iterator it=dstates.find(s);
271
272 if(it==dstates.end())
273 {
275 }
276 else
277 {
278 return it->second;
279 }
280}
281
282/*
283 * Add a new state.
284 */
286{
287 transitionst trans;
288 transitions.push_back(trans);
289
290 return num_states++;
291}
292
293/*
294 * Add the transition s -a-> t.
295 */
297{
298 PRECONDITION(s<transitions.size());
299 transitionst &trans=transitions[s];
300
301 trans.insert(std::make_pair(a, t));
302}
303
304/*
305 * Add a transition to the DTA.
306 */
320
322{
323 PRECONDITION(s<transitions.size());
324
325 transitionst &trans=transitions[s];
326
327 transition_ranget range=trans.equal_range(a);
328
329 for(transitionst::iterator it=range.first;
330 it!=range.second;
331 ++it)
332 {
333 t.insert(it->second);
334 }
335}
336
338 state_sett &s,
340 state_sett &t)
341{
342 for(const auto &state : s)
343 move(state, a, t);
344}
345
347{
349
350 for(std::size_t i=0; i<dtrans.size(); ++i)
351 {
353
354 for(const auto &trans : dta_transitions)
355 {
356 goto_programt::targett l=trans.first;
357 unsigned int j=trans.second;
358
359 // We have a transition: i -l-> j.
361 transitions.insert(std::make_pair(l, state_pair));
362 }
363 }
364}
365
367{
370
372
373 for(std::size_t i=0; i<old_table.size(); i++)
374 {
375 transitions.push_back(transitionst());
376 }
377
378 if(accept_states.empty())
379 {
380 num_states=0;
381 return;
382 }
383 else if(accept_states.size()==1)
384 {
385 new_init=*(accept_states.begin());
386 }
387 else
388 {
389 // More than one accept state. Introduce a new state with
390 // epsilon transitions to each accept state.
392
393 for(const auto &s : accept_states)
394 add_trans(new_init, epsilon, s);
395 }
396
397 std::cout << "Reversing automaton, old init=" << init_state
398 << ", new init=" << new_init << ", old accept="
399 << *(accept_states.begin()) << '/' << accept_states.size()
400 << " new accept=" << init_state << '\n';
401
404
406
407 for(std::size_t i=0; i<old_table.size(); i++)
408 {
409 transitionst &trans=old_table[i];
410
411 for(const auto &t : trans)
412 {
413 goto_programt::targett l=t.first;
414 unsigned int j=t.second;
415
416 // There was a transition i -l-> j, so add a transition
417 // j -l-> i.
418 add_trans(j, l, i);
419 }
420 }
421}
422
424{
425 state_sett reachable;
427
428 reachable.insert(init_state);
429 new_states.insert(init_state);
430
431 do
432 {
434
435 for(const auto &s : new_states)
436 {
437 transitionst &trans=transitions[s];
438
439 for(const auto &t : trans)
440 {
441 unsigned int j=t.second;
442
443 if(reachable.find(j)==reachable.end())
444 {
445 reachable.insert(j);
446 tmp.insert(j);
447 }
448 }
449 }
450
451 tmp.swap(new_states);
452 }
453 while(!new_states.empty());
454
455 for(std::size_t i=0; i<num_states; i++)
456 {
457 if(reachable.find(i)==reachable.end())
458 {
460 accept_states.erase(i);
461 }
462 }
463}
464
465// Produce a minimal DTA using Brzozowski's algorithm.
467{
469 determinise();
470
471 nta.swap(dta);
473 determinise();
474}
475
476void automatont::output(std::ostream &str) const
477{
478 str << "Init: " << init_state << '\n';
479
480 str << "Accept states: ";
481
482 for(const auto &state : accept_states)
483 str << state << ' ';
484
485 str << '\n';
486
487 for(unsigned int i=0; i<transitions.size(); ++i)
488 {
489 for(const auto &trans : transitions[i])
490 {
491 goto_programt::targett l=trans.first;
492 statet j=trans.second;
493
494 str << i << " -- " << l->location_number << " --> " << j << '\n';
495 }
496 }
497}
498
500{
501 std::size_t ret=0;
502
503 for(const auto &trans : transitions)
504 ret+=trans.size();
505
506 return ret;
507}
virtual void clear()
Reset the abstract state.
Definition ai.h:265
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:562
bool is_accepting(statet s)
unsigned num_states
std::vector< transitionst > transition_tablet
static const statet no_state
void set_accepting(statet s)
transition_tablet transitions
std::pair< transitionst::iterator, transitionst::iterator > transition_ranget
void output(std::ostream &str) const
void move(statet s, goto_programt::targett a, state_sett &t)
void swap(automatont &that)
void reverse(goto_programt::targett epsilon)
statet init_state
void add_trans(statet s, goto_programt::targett a, statet t)
state_sett accept_states
std::size_t count_transitions()
std::multimap< goto_programt::targett, statet, goto_programt::target_less_than > transitionst
A generic container class for the GOTO intermediate representation of one function.
instructionst::iterator targett
std::list< Target > get_successors(Target target) const
Get control-flow successors of a given instruction.
bool in_alphabet(goto_programt::targett t)
goto_programt::targett epsilon
void get_transitions(sym_mapt &transitions)
statet find_dstate(state_sett &s)
void add_dtrans(state_sett &s, goto_programt::targett a, state_sett &t)
statet add_dstate(state_sett &s)
void build_alphabet(goto_programt &program)
std::multimap< goto_programt::targett, state_pairt, goto_programt::target_less_than > sym_mapt
std::vector< state_sett > unmarked_dstates
std::pair< statet, statet > state_pairt
void add_path(patht &path)
void epsilon_closure(state_sett &s)
void pop_unmarked_dstate(state_sett &s)
#define Forall_goto_program_instructions(it, program)
Loop Acceleration.
std::list< path_nodet > patht
Definition path.h:44
#define CHECK_RETURN(CONDITION)
Definition invariant.h:495
#define PRECONDITION(CONDITION)
Definition invariant.h:463
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
Definition invariant.h:423
Loop Acceleration.
std::set< statet > state_sett
unsigned int statet