1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Morgan Deters, Tim King, Andres Noetzli |
4 |
|
* |
5 |
|
* This file is part of the cvc5 project. |
6 |
|
* |
7 |
|
* Copyright (c) 2009-2021 by the authors listed in the file AUTHORS |
8 |
|
* in the top-level source directory and their institutional affiliations. |
9 |
|
* All rights reserved. See the file COPYING in the top-level source |
10 |
|
* directory for licensing information. |
11 |
|
* **************************************************************************** |
12 |
|
* |
13 |
|
* Node attributes' internals. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "cvc5_private.h" |
17 |
|
|
18 |
|
#ifndef CVC5_ATTRIBUTE_H__INCLUDING__ATTRIBUTE_INTERNALS_H |
19 |
|
# error expr/attribute_internals.h should only be included by expr/attribute.h |
20 |
|
#endif /* CVC5_ATTRIBUTE_H__INCLUDING__ATTRIBUTE_INTERNALS_H */ |
21 |
|
|
22 |
|
#ifndef CVC5__EXPR__ATTRIBUTE_INTERNALS_H |
23 |
|
#define CVC5__EXPR__ATTRIBUTE_INTERNALS_H |
24 |
|
|
25 |
|
#include <unordered_map> |
26 |
|
|
27 |
|
namespace cvc5 { |
28 |
|
namespace expr { |
29 |
|
|
30 |
|
// ATTRIBUTE HASH FUNCTIONS ==================================================== |
31 |
|
|
32 |
|
namespace attr { |
33 |
|
|
34 |
|
/** |
35 |
|
* A hash function for attribute table keys. Attribute table keys are |
36 |
|
* pairs, (unique-attribute-id, Node). |
37 |
|
*/ |
38 |
|
struct AttrHashFunction { |
39 |
|
enum { LARGE_PRIME = 32452843ul }; |
40 |
4260129308 |
std::size_t operator()(const std::pair<uint64_t, NodeValue*>& p) const { |
41 |
4260129308 |
return p.first * LARGE_PRIME + p.second->getId(); |
42 |
|
} |
43 |
|
};/* struct AttrHashFunction */ |
44 |
|
|
45 |
|
/** |
46 |
|
* A hash function for boolean-valued attribute table keys; here we |
47 |
|
* don't have to store a pair as the key, because we use a known bit |
48 |
|
* in [0..63] for each attribute |
49 |
|
*/ |
50 |
|
struct AttrBoolHashFunction { |
51 |
880041605 |
std::size_t operator()(NodeValue* nv) const { |
52 |
880041605 |
return (size_t)nv->getId(); |
53 |
|
} |
54 |
|
};/* struct AttrBoolHashFunction */ |
55 |
|
|
56 |
|
} // namespace attr |
57 |
|
|
58 |
|
// ATTRIBUTE TYPE MAPPINGS ===================================================== |
59 |
|
|
60 |
|
namespace attr { |
61 |
|
|
62 |
|
/** |
63 |
|
* KindValueToTableValueMapping is a compile-time-only mechanism to |
64 |
|
* convert "attribute value types" into "table value types" and back |
65 |
|
* again. |
66 |
|
* |
67 |
|
* Each instantiation < T > is expected to have three members: |
68 |
|
* |
69 |
|
* typename table_value_type |
70 |
|
* |
71 |
|
* A type representing the underlying table's value_type for |
72 |
|
* attribute value type (T). It may be different from T, e.g. T |
73 |
|
* could be a pointer-to-Foo, but the underlying table value_type |
74 |
|
* might be pointer-to-void. |
75 |
|
* |
76 |
|
* static [convertible-to-table_value_type] convert([convertible-from-T]) |
77 |
|
* |
78 |
|
* Converts a T into a table_value_type. Used to convert an |
79 |
|
* attribute value on setting it (and assigning it into the |
80 |
|
* underlying table). See notes on specializations of |
81 |
|
* KindValueToTableValueMapping, below. |
82 |
|
* |
83 |
|
* static [convertible-to-T] convertBack([convertible-from-table_value_type]) |
84 |
|
* |
85 |
|
* Converts a table_value_type back into a T. Used to convert an |
86 |
|
* underlying table value back into the attribute's expected type |
87 |
|
* when retrieving it from the table. See notes on |
88 |
|
* specializations of KindValueToTableValueMapping, below. |
89 |
|
* |
90 |
|
* This general (non-specialized) implementation of the template does |
91 |
|
* nothing. |
92 |
|
* |
93 |
|
* The `Enable` template parameter is used to instantiate the template |
94 |
|
* conditionally: If the template substitution of Enable fails (e.g. when using |
95 |
|
* `std::enable_if` in the template parameter and the condition is false), the |
96 |
|
* instantiation is ignored due to the SFINAE rule. |
97 |
|
*/ |
98 |
|
template <class T, class Enable = void> |
99 |
|
struct KindValueToTableValueMapping |
100 |
|
{ |
101 |
|
/** Simple case: T == table_value_type */ |
102 |
|
typedef T table_value_type; |
103 |
|
/** No conversion necessary */ |
104 |
54817666 |
inline static T convert(const T& t) { return t; } |
105 |
|
/** No conversion necessary */ |
106 |
823922142 |
inline static T convertBack(const T& t) { return t; } |
107 |
|
}; |
108 |
|
|
109 |
|
/** |
110 |
|
* This converts arbitrary unsigned integers (up to 64-bit) to and from 64-bit |
111 |
|
* integers s.t. they can be stored in the hash table for integral-valued |
112 |
|
* attributes. |
113 |
|
*/ |
114 |
|
template <class T> |
115 |
|
struct KindValueToTableValueMapping< |
116 |
|
T, |
117 |
|
// Use this specialization only for unsigned integers |
118 |
|
typename std::enable_if<std::is_unsigned<T>::value>::type> |
119 |
|
{ |
120 |
|
typedef uint64_t table_value_type; |
121 |
|
/** Convert from unsigned integer to uint64_t */ |
122 |
48094850 |
static uint64_t convert(const T& t) |
123 |
|
{ |
124 |
|
static_assert(sizeof(T) <= sizeof(uint64_t), |
125 |
|
"Cannot store integer attributes of a bit-width that is " |
126 |
|
"greater than 64-bits"); |
127 |
48094850 |
return static_cast<uint64_t>(t); |
128 |
|
} |
129 |
|
/** Convert from uint64_t to unsigned integer */ |
130 |
783279625 |
static T convertBack(const uint64_t& t) { return static_cast<T>(t); } |
131 |
|
}; |
132 |
|
|
133 |
|
} // namespace attr |
134 |
|
|
135 |
|
// ATTRIBUTE HASH TABLES ======================================================= |
136 |
|
|
137 |
|
namespace attr { |
138 |
|
|
139 |
|
// Returns a 64 bit integer with a single `bit` set when `bit` < 64. |
140 |
|
// Avoids problems in (1 << x) when sizeof(x) <= sizeof(uint64_t). |
141 |
820625000 |
inline uint64_t GetBitSet(uint64_t bit) |
142 |
|
{ |
143 |
820625000 |
constexpr uint64_t kOne = 1; |
144 |
820625000 |
return kOne << bit; |
145 |
|
} |
146 |
|
|
147 |
|
/** |
148 |
|
* An "AttrHash<value_type>"---the hash table underlying |
149 |
|
* attributes---is simply a mapping of pair<unique-attribute-id, Node> |
150 |
|
* to value_type using our specialized hash function for these pairs. |
151 |
|
*/ |
152 |
|
template <class value_type> |
153 |
79570 |
class AttrHash : |
154 |
|
public std::unordered_map<std::pair<uint64_t, NodeValue*>, |
155 |
|
value_type, |
156 |
|
AttrHashFunction> { |
157 |
|
};/* class AttrHash<> */ |
158 |
|
|
159 |
|
/** |
160 |
|
* In the case of Boolean-valued attributes we have a special |
161 |
|
* "AttrHash<bool>" to pack bits together in words. |
162 |
|
*/ |
163 |
|
template <> |
164 |
15914 |
class AttrHash<bool> : |
165 |
|
protected std::unordered_map<NodeValue*, |
166 |
|
uint64_t, |
167 |
|
AttrBoolHashFunction> { |
168 |
|
|
169 |
|
/** A "super" type, like in Java, for easy reference below. */ |
170 |
|
typedef std::unordered_map<NodeValue*, uint64_t, AttrBoolHashFunction> super; |
171 |
|
|
172 |
|
/** |
173 |
|
* BitAccessor allows us to return a bit "by reference." Of course, |
174 |
|
* we don't require bit-addressibility supported by the system, we |
175 |
|
* do it with a complex type. |
176 |
|
*/ |
177 |
|
class BitAccessor { |
178 |
|
|
179 |
|
uint64_t& d_word; |
180 |
|
|
181 |
|
uint64_t d_bit; |
182 |
|
|
183 |
|
public: |
184 |
47888704 |
BitAccessor(uint64_t& word, uint64_t bit) : d_word(word), d_bit(bit) {} |
185 |
|
|
186 |
47888704 |
BitAccessor& operator=(bool b) { |
187 |
47888704 |
if(b) { |
188 |
|
// set the bit |
189 |
39219760 |
d_word |= GetBitSet(d_bit); |
190 |
|
} else { |
191 |
|
// clear the bit |
192 |
8668944 |
d_word &= ~GetBitSet(d_bit); |
193 |
|
} |
194 |
|
|
195 |
47888704 |
return *this; |
196 |
|
} |
197 |
|
|
198 |
|
operator bool() const { return (d_word & GetBitSet(d_bit)) ? true : false; } |
199 |
|
};/* class AttrHash<bool>::BitAccessor */ |
200 |
|
|
201 |
|
/** |
202 |
|
* A (somewhat degenerate) iterator over boolean-valued attributes. |
203 |
|
* This iterator doesn't support anything except comparison and |
204 |
|
* dereference. It's intended just for the result of find() on the |
205 |
|
* table. |
206 |
|
*/ |
207 |
|
class BitIterator { |
208 |
|
|
209 |
|
std::pair<NodeValue* const, uint64_t>* d_entry; |
210 |
|
|
211 |
|
uint64_t d_bit; |
212 |
|
|
213 |
|
public: |
214 |
|
|
215 |
|
BitIterator() : |
216 |
|
d_entry(NULL), |
217 |
|
d_bit(0) { |
218 |
|
} |
219 |
|
|
220 |
|
BitIterator(std::pair<NodeValue* const, uint64_t>& entry, uint64_t bit) |
221 |
|
: d_entry(&entry), d_bit(bit) |
222 |
|
{ |
223 |
|
} |
224 |
|
|
225 |
|
std::pair<NodeValue* const, BitAccessor> operator*() { |
226 |
|
return std::make_pair(d_entry->first, |
227 |
|
BitAccessor(d_entry->second, d_bit)); |
228 |
|
} |
229 |
|
|
230 |
|
bool operator==(const BitIterator& b) { |
231 |
|
return d_entry == b.d_entry && d_bit == b.d_bit; |
232 |
|
} |
233 |
|
};/* class AttrHash<bool>::BitIterator */ |
234 |
|
|
235 |
|
/** |
236 |
|
* A (somewhat degenerate) const_iterator over boolean-valued |
237 |
|
* attributes. This const_iterator doesn't support anything except |
238 |
|
* comparison and dereference. It's intended just for the result of |
239 |
|
* find() on the table. |
240 |
|
*/ |
241 |
|
class ConstBitIterator { |
242 |
|
|
243 |
|
const std::pair<NodeValue* const, uint64_t>* d_entry; |
244 |
|
|
245 |
|
uint64_t d_bit; |
246 |
|
|
247 |
|
public: |
248 |
|
|
249 |
828761514 |
ConstBitIterator() : |
250 |
|
d_entry(NULL), |
251 |
828761514 |
d_bit(0) { |
252 |
828761514 |
} |
253 |
|
|
254 |
772736296 |
ConstBitIterator(const std::pair<NodeValue* const, uint64_t>& entry, |
255 |
|
uint64_t bit) |
256 |
772736296 |
: d_entry(&entry), d_bit(bit) |
257 |
|
{ |
258 |
772736296 |
} |
259 |
|
|
260 |
772736296 |
std::pair<NodeValue* const, bool> operator*() |
261 |
|
{ |
262 |
1545472592 |
return std::make_pair( |
263 |
2318208888 |
d_entry->first, (d_entry->second & GetBitSet(d_bit)) ? true : false); |
264 |
|
} |
265 |
|
|
266 |
800748905 |
bool operator==(const ConstBitIterator& b) { |
267 |
800748905 |
return d_entry == b.d_entry && d_bit == b.d_bit; |
268 |
|
} |
269 |
|
};/* class AttrHash<bool>::ConstBitIterator */ |
270 |
|
|
271 |
|
public: |
272 |
|
|
273 |
|
typedef std::pair<uint64_t, NodeValue*> key_type; |
274 |
|
typedef bool data_type; |
275 |
|
typedef std::pair<const key_type, data_type> value_type; |
276 |
|
|
277 |
|
/** an iterator type; see above for limitations */ |
278 |
|
typedef BitIterator iterator; |
279 |
|
/** a const_iterator type; see above for limitations */ |
280 |
|
typedef ConstBitIterator const_iterator; |
281 |
|
|
282 |
|
/** |
283 |
|
* Find the boolean value in the hash table. Returns something == |
284 |
|
* end() if not found. |
285 |
|
*/ |
286 |
|
BitIterator find(const std::pair<uint64_t, NodeValue*>& k) { |
287 |
|
super::iterator i = super::find(k.second); |
288 |
|
if(i == super::end()) { |
289 |
|
return BitIterator(); |
290 |
|
} |
291 |
|
/* |
292 |
|
Debug.printf("boolattr", |
293 |
|
"underlying word at 0x%p looks like 0x%016llx, bit is %u\n", |
294 |
|
&(*i).second, |
295 |
|
(uint64_t)((*i).second), |
296 |
|
uint64_t(k.first)); |
297 |
|
*/ |
298 |
|
return BitIterator(*i, k.first); |
299 |
|
} |
300 |
|
|
301 |
|
/** The "off the end" iterator */ |
302 |
|
BitIterator end() { |
303 |
|
return BitIterator(); |
304 |
|
} |
305 |
|
|
306 |
|
/** |
307 |
|
* Find the boolean value in the hash table. Returns something == |
308 |
|
* end() if not found. |
309 |
|
*/ |
310 |
800748905 |
ConstBitIterator find(const std::pair<uint64_t, NodeValue*>& k) const { |
311 |
800748905 |
super::const_iterator i = super::find(k.second); |
312 |
800748905 |
if(i == super::end()) { |
313 |
28012609 |
return ConstBitIterator(); |
314 |
|
} |
315 |
|
/* |
316 |
|
Debug.printf("boolattr", |
317 |
|
"underlying word at 0x%p looks like 0x%016llx, bit is %u\n", |
318 |
|
&(*i).second, |
319 |
|
(uint64_t)((*i).second), |
320 |
|
uint64_t(k.first)); |
321 |
|
*/ |
322 |
772736296 |
return ConstBitIterator(*i, k.first); |
323 |
|
} |
324 |
|
|
325 |
|
/** The "off the end" const_iterator */ |
326 |
800748905 |
ConstBitIterator end() const { |
327 |
800748905 |
return ConstBitIterator(); |
328 |
|
} |
329 |
|
|
330 |
|
/** |
331 |
|
* Access the hash table using the underlying operator[]. Inserts |
332 |
|
* the key into the table (associated to default value) if it's not |
333 |
|
* already there. |
334 |
|
*/ |
335 |
47888704 |
BitAccessor operator[](const std::pair<uint64_t, NodeValue*>& k) { |
336 |
47888704 |
uint64_t& word = super::operator[](k.second); |
337 |
47888704 |
return BitAccessor(word, k.first); |
338 |
|
} |
339 |
|
|
340 |
|
/** |
341 |
|
* Delete all flags from the given node. |
342 |
|
*/ |
343 |
31403996 |
void erase(NodeValue* nv) { |
344 |
31403996 |
super::erase(nv); |
345 |
31403996 |
} |
346 |
|
|
347 |
|
/** |
348 |
|
* Clear the hash table. |
349 |
|
*/ |
350 |
7957 |
void clear() { |
351 |
7957 |
super::clear(); |
352 |
7957 |
} |
353 |
|
|
354 |
|
/** Is the hash table empty? */ |
355 |
|
bool empty() const { |
356 |
|
return super::empty(); |
357 |
|
} |
358 |
|
|
359 |
|
/** This is currently very misleading! */ |
360 |
|
size_t size() const { |
361 |
|
return super::size(); |
362 |
|
} |
363 |
|
};/* class AttrHash<bool> */ |
364 |
|
|
365 |
|
} // namespace attr |
366 |
|
|
367 |
|
// ATTRIBUTE IDENTIFIER ASSIGNMENT TEMPLATE ==================================== |
368 |
|
|
369 |
|
namespace attr { |
370 |
|
|
371 |
|
/** |
372 |
|
* This is the last-attribute-assigner. IDs are not globally |
373 |
|
* unique; rather, they are unique for each table_value_type. |
374 |
|
*/ |
375 |
|
template <class T> |
376 |
|
struct LastAttributeId |
377 |
|
{ |
378 |
|
public: |
379 |
1054337 |
static uint64_t getNextId() { |
380 |
1054337 |
uint64_t* id = raw_id(); |
381 |
1054337 |
const uint64_t next_id = *id; |
382 |
1054337 |
++(*id); |
383 |
1054337 |
return next_id; |
384 |
|
} |
385 |
157019986 |
static uint64_t getId() { |
386 |
157019986 |
return *raw_id(); |
387 |
|
} |
388 |
|
private: |
389 |
158074323 |
static uint64_t* raw_id() |
390 |
|
{ |
391 |
|
static uint64_t s_id = 0; |
392 |
158074323 |
return &s_id; |
393 |
|
} |
394 |
|
}; |
395 |
|
|
396 |
|
} // namespace attr |
397 |
|
|
398 |
|
// ATTRIBUTE DEFINITION ======================================================== |
399 |
|
|
400 |
|
/** |
401 |
|
* An "attribute type" structure. |
402 |
|
* |
403 |
|
* @param T the tag for the attribute kind. |
404 |
|
* |
405 |
|
* @param value_t the underlying value_type for the attribute kind |
406 |
|
*/ |
407 |
|
template <class T, class value_t> |
408 |
|
class Attribute |
409 |
|
{ |
410 |
|
/** |
411 |
|
* The unique ID associated to this attribute. Assigned statically, |
412 |
|
* at load time. |
413 |
|
*/ |
414 |
|
static const uint64_t s_id; |
415 |
|
|
416 |
|
public: |
417 |
|
|
418 |
|
/** The value type for this attribute. */ |
419 |
|
typedef value_t value_type; |
420 |
|
|
421 |
|
/** Get the unique ID associated to this attribute. */ |
422 |
1842012738 |
static inline uint64_t getId() { return s_id; } |
423 |
|
|
424 |
|
/** |
425 |
|
* This attribute does not have a default value: calling |
426 |
|
* hasAttribute() for a Node that hasn't had this attribute set will |
427 |
|
* return false, and getAttribute() for the Node will return a |
428 |
|
* default-constructed value_type. |
429 |
|
*/ |
430 |
|
static const bool has_default_value = false; |
431 |
|
|
432 |
|
/** |
433 |
|
* Register this attribute kind and check that the ID is a valid ID |
434 |
|
* for bool-valued attributes. Fail an assert if not. Otherwise |
435 |
|
* return the id. |
436 |
|
*/ |
437 |
751695 |
static inline uint64_t registerAttribute() { |
438 |
|
typedef typename attr::KindValueToTableValueMapping<value_t>:: |
439 |
|
table_value_type table_value_type; |
440 |
751695 |
return attr::LastAttributeId<table_value_type>::getNextId(); |
441 |
|
} |
442 |
|
};/* class Attribute<> */ |
443 |
|
|
444 |
|
/** |
445 |
|
* An "attribute type" structure for boolean flags (special). |
446 |
|
*/ |
447 |
|
template <class T> |
448 |
|
class Attribute<T, bool> |
449 |
|
{ |
450 |
|
/** IDs for bool-valued attributes are actually bit assignments. */ |
451 |
|
static const uint64_t s_id; |
452 |
|
|
453 |
|
public: |
454 |
|
|
455 |
|
/** The value type for this attribute; here, bool. */ |
456 |
|
typedef bool value_type; |
457 |
|
|
458 |
|
/** Get the unique ID associated to this attribute. */ |
459 |
848637609 |
static inline uint64_t getId() { return s_id; } |
460 |
|
|
461 |
|
/** |
462 |
|
* Such bool-valued attributes ("flags") have a default value: they |
463 |
|
* are false for all nodes on entry. Calling hasAttribute() for a |
464 |
|
* Node that hasn't had this attribute set will return true, and |
465 |
|
* getAttribute() for the Node will return the default_value below. |
466 |
|
*/ |
467 |
|
static const bool has_default_value = true; |
468 |
|
|
469 |
|
/** |
470 |
|
* Default value of the attribute for Nodes without one explicitly |
471 |
|
* set. |
472 |
|
*/ |
473 |
|
static const bool default_value = false; |
474 |
|
|
475 |
|
/** |
476 |
|
* Register this attribute kind and check that the ID is a valid ID |
477 |
|
* for bool-valued attributes. Fail an assert if not. Otherwise |
478 |
|
* return the id. |
479 |
|
*/ |
480 |
302642 |
static inline uint64_t registerAttribute() { |
481 |
302642 |
const uint64_t id = attr::LastAttributeId<bool>::getNextId(); |
482 |
302642 |
AlwaysAssert(id <= 63) << "Too many boolean node attributes registered " |
483 |
|
"during initialization !"; |
484 |
302642 |
return id; |
485 |
|
} |
486 |
|
};/* class Attribute<..., bool, ...> */ |
487 |
|
|
488 |
|
// ATTRIBUTE IDENTIFIER ASSIGNMENT ============================================= |
489 |
|
|
490 |
|
/** Assign unique IDs to attributes at load time. */ |
491 |
|
template <class T, class value_t> |
492 |
1347206 |
const uint64_t Attribute<T, value_t>::s_id = |
493 |
751695 |
Attribute<T, value_t>::registerAttribute(); |
494 |
|
|
495 |
|
/** Assign unique IDs to attributes at load time. */ |
496 |
|
template <class T> |
497 |
634563 |
const uint64_t Attribute<T, bool>::s_id = |
498 |
302642 |
Attribute<T, bool>::registerAttribute(); |
499 |
|
|
500 |
|
} // namespace expr |
501 |
|
} // namespace cvc5 |
502 |
|
|
503 |
|
#endif /* CVC5__EXPR__ATTRIBUTE_INTERNALS_H */ |