CBMC
irep.h
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module:
4 
5 Author: Daniel Kroening, kroening@kroening.com
6 
7 \*******************************************************************/
8 
9 
10 #ifndef CPROVER_UTIL_IREP_H
11 #define CPROVER_UTIL_IREP_H
12 
13 #include <string>
14 #include <vector>
15 
16 #include "invariant.h"
17 #include "irep_ids.h"
18 
19 #define SHARING
20 #ifndef HASH_CODE
21 # define HASH_CODE 1
22 #endif
23 // use forward_list by default, unless _GLIBCXX_DEBUG is set as the debug
24 // overhead is noticeably higher with the regression test suite taking four
25 // times as long.
26 #if !defined(NAMED_SUB_IS_FORWARD_LIST) && !defined(_GLIBCXX_DEBUG)
27 # define NAMED_SUB_IS_FORWARD_LIST 1
28 #endif
29 
30 #if NAMED_SUB_IS_FORWARD_LIST
31 # include "forward_list_as_map.h"
32 #else
33 #include <map>
34 #endif
35 
37 // NOLINTNEXTLINE(readability/identifiers)
39 
40 #if defined(__GNUC__) && __GNUC__ >= 14
41 [[gnu::no_dangling]]
42 #endif
43 inline const std::string &
45 {
46  return as_string(d);
47 }
48 
49 #ifdef IREP_DEBUG
50 #include <iostream>
51 #endif
52 
53 class irept;
54 const irept &get_nil_irep();
55 
59 template <bool enabled>
61 {
62 };
63 
64 template <>
65 struct ref_count_ift<true>
66 {
67  unsigned ref_count = 1;
68 };
69 
88 template <typename treet, typename named_subtreest, bool sharing = true>
89 class tree_nodet : public ref_count_ift<sharing>
90 {
91 public:
92  // These are not stable.
93  typedef std::vector<treet> subt;
94 
95  // named_subt has to provide stable references; we can
96  // use std::forward_list or std::vector< unique_ptr<T> > to save
97  // memory and increase efficiency.
98  using named_subt = named_subtreest;
99 
100  friend treet;
101 
104 
107 
108 #if HASH_CODE
109  mutable std::size_t hash_code = 0;
110 #endif
111 
112  void clear()
113  {
114  data.clear();
115  sub.clear();
116  named_sub.clear();
117 #if HASH_CODE
118  hash_code = 0;
119 #endif
120  }
121 
122  void swap(tree_nodet &d)
123  {
124  d.data.swap(data);
125  d.sub.swap(sub);
126  d.named_sub.swap(named_sub);
127 #if HASH_CODE
128  std::swap(d.hash_code, hash_code);
129 #endif
130  }
131 
132  tree_nodet() = default;
133 
134  explicit tree_nodet(irep_idt _data) : data(std::move(_data))
135  {
136  }
137 
138  tree_nodet(irep_idt _data, named_subt _named_sub, subt _sub)
139  : data(std::move(_data)),
140  named_sub(std::move(_named_sub)),
141  sub(std::move(_sub))
142  {
143  }
144 };
145 
147 template <typename derivedt, typename named_subtreest>
149 {
150 public:
152  using subt = typename dt::subt;
153  using named_subt = typename dt::named_subt;
154 
157 
158  explicit sharing_treet(irep_idt _id) : data(new dt(std::move(_id)))
159  {
160  }
161 
162  sharing_treet(irep_idt _id, named_subt _named_sub, subt _sub)
163  : data(new dt(std::move(_id), std::move(_named_sub), std::move(_sub)))
164  {
165  }
166 
167  // constructor for blank irep
169  {
170  }
171 
172  // copy constructor
173  sharing_treet(const sharing_treet &irep) : data(irep.data)
174  {
175  if(data!=&empty_d)
176  {
177  PRECONDITION(data->ref_count != 0);
178  data->ref_count++;
179 #ifdef IREP_DEBUG
180  std::cout << "COPY " << data << " " << data->ref_count << '\n';
181 #endif
182  }
183  }
184 
185  // Copy from rvalue reference.
186  // Note that this does avoid a branch compared to the
187  // standard copy constructor above.
189  {
190 #ifdef IREP_DEBUG
191  std::cout << "COPY MOVE\n";
192 #endif
193  irep.data=&empty_d;
194  }
195 
197  {
198 #ifdef IREP_DEBUG
199  std::cout << "ASSIGN\n";
200 #endif
201 
202  // Ordering is very important here!
203  // Consider self-assignment, which may destroy 'irep'
204  dt *irep_data=irep.data;
205  if(irep_data!=&empty_d)
206  irep_data->ref_count++;
207 
208  remove_ref(data); // this may kill 'irep'
209  data=irep_data;
210 
211  return *this;
212  }
213 
214  // Note that the move assignment operator does avoid
215  // three branches compared to standard operator above.
217  {
218 #ifdef IREP_DEBUG
219  std::cout << "ASSIGN MOVE\n";
220 #endif
221  // we simply swap two pointers
222  std::swap(data, irep.data);
223  return *this;
224  }
225 
227  {
228  remove_ref(data);
229  }
230 
231 protected:
233  static dt empty_d;
234 
235  static void remove_ref(dt *old_data);
236  static void nonrecursive_destructor(dt *old_data);
237  void detach();
238 
239 public:
240  const dt &read() const
241  {
242  return *data;
243  }
244 
246  {
247  detach();
248 #if HASH_CODE
249  data->hash_code = 0;
250 #endif
251  return *data;
252  }
253 };
254 
255 // Static field initialization
256 template <typename derivedt, typename named_subtreest>
259 
261 template <typename derivedt, typename named_subtreest>
263 {
264 public:
266  using subt = typename dt::subt;
267  using named_subt = typename dt::named_subt;
268 
271 
272  explicit non_sharing_treet(irep_idt _id) : data(std::move(_id))
273  {
274  }
275 
276  non_sharing_treet(irep_idt _id, named_subt _named_sub, subt _sub)
277  : data(std::move(_id), std::move(_named_sub), std::move(_sub))
278  {
279  }
280 
281  non_sharing_treet() = default;
282 
283  const dt &read() const
284  {
285  return data;
286  }
287 
289  {
290 #if HASH_CODE
291  data.hash_code = 0;
292 #endif
293  return data;
294  }
295 
296 protected:
298 };
299 
351 class irept
352 #ifdef SHARING
353  : public sharing_treet<
354  irept,
355 #else
356  : public non_sharing_treet<
357  irept,
358 #endif
359 #if NAMED_SUB_IS_FORWARD_LIST
360  forward_list_as_mapt<irep_idt, irept>>
361 #else
362  std::map<irep_idt, irept>>
363 #endif
364 {
365 public:
367 
368  bool is_nil() const
369  {
370  return id() == ID_nil;
371  }
372  bool is_not_nil() const
373  {
374  return id() != ID_nil;
375  }
376 
377  explicit irept(const irep_idt &_id) : baset(_id)
378  {
379  }
380 
381  irept(const irep_idt &_id, const named_subt &_named_sub, const subt &_sub)
382  : baset(_id, _named_sub, _sub)
383  {
384  }
385 
386  irept() = default;
387 
388  const irep_idt &id() const
389  { return read().data; }
390 
391  const std::string &id_string() const
392  { return id2string(read().data); }
393 
394  void id(const irep_idt &_data)
395  { write().data=_data; }
396 
397  const irept &find(const irep_idt &name) const;
398  irept &add(const irep_idt &name);
399  irept &add(const irep_idt &name, irept irep);
400 
401  const std::string &get_string(const irep_idt &name) const
402  {
403  return id2string(get(name));
404  }
405 
406  const irep_idt &get(const irep_idt &name) const;
407  bool get_bool(const irep_idt &name) const;
408  signed int get_int(const irep_idt &name) const;
409  std::size_t get_size_t(const irep_idt &name) const;
410  long long get_long_long(const irep_idt &name) const;
411 
412  void set(const irep_idt &name, const irep_idt &value)
413  {
414  add(name, irept(value));
415  }
416  void set(const irep_idt &name, irept irep)
417  {
418  add(name, std::move(irep));
419  }
420  void set(const irep_idt &name, const long long value);
421  void set_size_t(const irep_idt &name, const std::size_t value);
422 
423  void remove(const irep_idt &name);
424  void move_to_sub(irept &irep);
425  void move_to_named_sub(const irep_idt &name, irept &irep);
426 
427  bool operator==(const irept &other) const;
428 
429  bool operator!=(const irept &other) const
430  {
431  return !(*this==other);
432  }
433 
434  void swap(irept &irep)
435  {
436  std::swap(irep.data, data);
437  }
438 
439  bool operator<(const irept &other) const;
440  bool ordering(const irept &other) const;
441 
442  int compare(const irept &i) const;
443 
444  void clear() { *this=irept(); }
445 
446  void make_nil() { *this=get_nil_irep(); }
447 
448  subt &get_sub() { return write().sub; } // DANGEROUS
449  const subt &get_sub() const { return read().sub; }
450  named_subt &get_named_sub() { return write().named_sub; } // DANGEROUS
451  const named_subt &get_named_sub() const { return read().named_sub; }
452 
453  std::size_t hash() const;
454  std::size_t full_hash() const;
455 
456  bool full_eq(const irept &other) const;
457 
458  std::string pretty(unsigned indent=0, unsigned max_indent=0) const;
459 
460  static bool is_comment(const irep_idt &name)
461  { return !name.empty() && name[0]=='#'; }
462 
464  static std::size_t number_of_non_comments(const named_subt &);
465 };
466 
467 // NOLINTNEXTLINE(readability/identifiers)
468 struct irep_hash
469 {
470  std::size_t operator()(const irept &irep) const
471  {
472  return irep.hash();
473  }
474 };
475 
476 // NOLINTNEXTLINE(readability/identifiers)
478 {
479  std::size_t operator()(const irept &irep) const
480  {
481  return irep.full_hash();
482  }
483 };
484 
485 // NOLINTNEXTLINE(readability/identifiers)
487 {
488  bool operator()(const irept &i1, const irept &i2) const
489  {
490  return i1.full_eq(i2);
491  }
492 };
493 
495 {
496  const irept &irep;
497  explicit irep_pretty_diagnosticst(const irept &irep);
498 };
499 
500 template <>
502 {
503  static std::string diagnostics_as_string(const irep_pretty_diagnosticst &irep)
504  {
505  return irep.irep.pretty();
506  }
507 };
508 
509 template <typename derivedt, typename named_subtreest>
511 {
512 #ifdef IREP_DEBUG
513  std::cout << "DETACH1: " << data << '\n';
514 #endif
515 
516  if(data == &empty_d)
517  {
518  data = new dt;
519 
520 #ifdef IREP_DEBUG
521  std::cout << "ALLOCATED " << data << '\n';
522 #endif
523  }
524  else if(data->ref_count > 1)
525  {
526  dt *old_data(data);
527  data = new dt(*old_data);
528 
529 #ifdef IREP_DEBUG
530  std::cout << "ALLOCATED " << data << '\n';
531 #endif
532 
533  data->ref_count = 1;
534  remove_ref(old_data);
535  }
536 
537  POSTCONDITION(data->ref_count == 1);
538 
539 #ifdef IREP_DEBUG
540  std::cout << "DETACH2: " << data << '\n';
541 #endif
542 }
543 
544 template <typename derivedt, typename named_subtreest>
546 {
547  if(old_data == &empty_d)
548  return;
549 
550 #if 0
551  nonrecursive_destructor(old_data);
552 #else
553 
554  PRECONDITION(old_data->ref_count != 0);
555 
556 #ifdef IREP_DEBUG
557  std::cout << "R: " << old_data << " " << old_data->ref_count << '\n';
558 #endif
559 
560  old_data->ref_count--;
561  if(old_data->ref_count == 0)
562  {
563 #ifdef IREP_DEBUG
564  std::cout << "D: " << pretty() << '\n';
565  std::cout << "DELETING " << old_data->data << " " << old_data << '\n';
566  old_data->clear();
567  std::cout << "DEALLOCATING " << old_data << "\n";
568 #endif
569 
570  // may cause recursive call
571  delete old_data;
572 
573 #ifdef IREP_DEBUG
574  std::cout << "DONE\n";
575 #endif
576  }
577 #endif
578 }
579 
582 template <typename derivedt, typename named_subtreest>
584  dt *old_data)
585 {
586  std::vector<dt *> stack(1, old_data);
587 
588  while(!stack.empty())
589  {
590  dt *d = stack.back();
591  stack.erase(--stack.end());
592  if(d == &empty_d)
593  continue;
594 
595  INVARIANT(d->ref_count != 0, "All contents of the stack must be in use");
596  d->ref_count--;
597 
598  if(d->ref_count == 0)
599  {
600  stack.reserve(
601  stack.size() + std::distance(d->named_sub.begin(), d->named_sub.end()) +
602  d->sub.size());
603 
604  for(typename named_subt::iterator it = d->named_sub.begin();
605  it != d->named_sub.end();
606  it++)
607  {
608  stack.push_back(it->second.data);
609  it->second.data = &empty_d;
610  }
611 
612  for(typename subt::iterator it = d->sub.begin(); it != d->sub.end(); it++)
613  {
614  stack.push_back(it->data);
615  it->data = &empty_d;
616  }
617 
618  // now delete, won't do recursion
619  delete d;
620  }
621  }
622 }
623 
624 #endif // CPROVER_UTIL_IREP_H
dstringt has one field, an unsigned integer no which is an index into a static table of strings.
Definition: dstring.h:38
void swap(dstringt &b)
Definition: dstring.h:162
bool empty() const
Definition: dstring.h:89
void clear()
Definition: dstring.h:159
There are a large number of kinds of tree structured or tree-like data in CPROVER.
Definition: irep.h:364
static std::size_t number_of_non_comments(const named_subt &)
count the number of named_sub elements that are not comments
Definition: irep.cpp:404
bool get_bool(const irep_idt &name) const
Definition: irep.cpp:57
irept()=default
bool operator==(const irept &other) const
Definition: irep.cpp:133
subt & get_sub()
Definition: irep.h:448
void set(const irep_idt &name, irept irep)
Definition: irep.h:416
std::string pretty(unsigned indent=0, unsigned max_indent=0) const
Definition: irep.cpp:482
void move_to_named_sub(const irep_idt &name, irept &irep)
Definition: irep.cpp:26
std::size_t get_size_t(const irep_idt &name) const
Definition: irep.cpp:67
bool operator!=(const irept &other) const
Definition: irep.h:429
const irept & find(const irep_idt &name) const
Definition: irep.cpp:93
bool ordering(const irept &other) const
defines ordering on the internal representation
Definition: irep.cpp:232
const subt & get_sub() const
Definition: irep.h:449
const irep_idt & get(const irep_idt &name) const
Definition: irep.cpp:44
named_subt & get_named_sub()
Definition: irep.h:450
irept(const irep_idt &_id)
Definition: irep.h:377
const std::string & id_string() const
Definition: irep.h:391
void remove(const irep_idt &name)
Definition: irep.cpp:87
void set(const irep_idt &name, const irep_idt &value)
Definition: irep.h:412
const named_subt & get_named_sub() const
Definition: irep.h:451
void set_size_t(const irep_idt &name, const std::size_t value)
Definition: irep.cpp:82
irept(const irep_idt &_id, const named_subt &_named_sub, const subt &_sub)
Definition: irep.h:381
std::size_t hash() const
Definition: irep.cpp:415
int compare(const irept &i) const
defines ordering on the internal representation comments are ignored
Definition: irep.cpp:301
void clear()
Definition: irep.h:444
bool is_not_nil() const
Definition: irep.h:372
bool operator<(const irept &other) const
defines ordering on the internal representation
Definition: irep.cpp:395
const irep_idt & id() const
Definition: irep.h:388
bool full_eq(const irept &other) const
Definition: irep.cpp:192
long long get_long_long(const irep_idt &name) const
Definition: irep.cpp:72
std::size_t full_hash() const
Definition: irep.cpp:453
void make_nil()
Definition: irep.h:446
signed int get_int(const irep_idt &name) const
Definition: irep.cpp:62
static bool is_comment(const irep_idt &name)
Definition: irep.h:460
void move_to_sub(irept &irep)
Definition: irep.cpp:35
void swap(irept &irep)
Definition: irep.h:434
void id(const irep_idt &_data)
Definition: irep.h:394
const std::string & get_string(const irep_idt &name) const
Definition: irep.h:401
irept & add(const irep_idt &name)
Definition: irep.cpp:103
bool is_nil() const
Definition: irep.h:368
Base class for tree-like data structures without sharing.
Definition: irep.h:263
non_sharing_treet(irep_idt _id, named_subt _named_sub, subt _sub)
Definition: irep.h:276
non_sharing_treet()=default
typename dt::subt subt
Definition: irep.h:266
non_sharing_treet(irep_idt _id)
Definition: irep.h:272
typename dt::named_subt named_subt
Definition: irep.h:267
const dt & read() const
Definition: irep.h:283
dt & write()
Definition: irep.h:288
Base class for tree-like data structures with sharing.
Definition: irep.h:149
sharing_treet(irep_idt _id, named_subt _named_sub, subt _sub)
Definition: irep.h:162
dt & write()
Definition: irep.h:245
dt * data
Definition: irep.h:232
sharing_treet(irep_idt _id)
Definition: irep.h:158
sharing_treet & operator=(sharing_treet &&irep)
Definition: irep.h:216
static void remove_ref(dt *old_data)
Definition: irep.h:545
typename dt::named_subt named_subt
Definition: irep.h:153
typename dt::subt subt
Definition: irep.h:152
sharing_treet tree_implementationt
Used to refer to this class from derived classes.
Definition: irep.h:156
sharing_treet(const sharing_treet &irep)
Definition: irep.h:173
static void nonrecursive_destructor(dt *old_data)
Does the same as remove_ref, but using an explicit stack instead of recursion.
Definition: irep.h:583
static dt empty_d
Definition: irep.h:233
sharing_treet & operator=(const sharing_treet &irep)
Definition: irep.h:196
const dt & read() const
Definition: irep.h:240
sharing_treet()
Definition: irep.h:168
sharing_treet(sharing_treet &&irep)
Definition: irep.h:188
~sharing_treet()
Definition: irep.h:226
void detach()
Definition: irep.h:510
A node with data in a tree, it contains:
Definition: irep.h:90
named_subt named_sub
Definition: irep.h:105
named_subtreest named_subt
Definition: irep.h:98
friend treet
Definition: irep.h:100
tree_nodet()=default
void swap(tree_nodet &d)
Definition: irep.h:122
tree_nodet(irep_idt _data, named_subt _named_sub, subt _sub)
Definition: irep.h:138
std::vector< treet > subt
Definition: irep.h:93
void clear()
Definition: irep.h:112
tree_nodet(irep_idt _data)
Definition: irep.h:134
std::size_t hash_code
Definition: irep.h:109
subt sub
Definition: irep.h:106
irep_idt data
This irep_idt is the only place to store data in an tree node.
Definition: irep.h:103
dstring_hash irep_id_hash
Definition: irep.h:38
dstringt irep_idt
Definition: irep.h:36
const irept & get_nil_irep()
Definition: irep.cpp:19
#define SHARING
Definition: irep.h:19
const std::string & id2string(const irep_idt &d)
Definition: irep.h:44
#define PRECONDITION(CONDITION)
Definition: invariant.h:463
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
Definition: invariant.h:423
#define POSTCONDITION(CONDITION)
Definition: invariant.h:479
std::string as_string(const ai_verifier_statust &status)
Makes a status message string from a status.
static std::string diagnostics_as_string(const irep_pretty_diagnosticst &irep)
Definition: irep.h:503
Helper to give us some diagnostic in a usable form on assertion failure.
Definition: invariant.h:299
bool operator()(const irept &i1, const irept &i2) const
Definition: irep.h:488
std::size_t operator()(const irept &irep) const
Definition: irep.h:479
std::size_t operator()(const irept &irep) const
Definition: irep.h:470
irep_pretty_diagnosticst(const irept &irep)
Definition: irep.cpp:523
const irept & irep
Definition: irep.h:496
unsigned ref_count
Definition: irep.h:67
Used in tree_nodet for activating or not reference counting.
Definition: irep.h:61