1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andres Noetzli, Morgan Deters, Mathias Preiner |
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 |
|
* A builder interface for Nodes. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "expr/node_builder.h" |
17 |
|
|
18 |
|
#include <memory> |
19 |
|
|
20 |
|
namespace cvc5 { |
21 |
|
|
22 |
40727286 |
NodeBuilder::NodeBuilder() |
23 |
40727286 |
: d_nv(&d_inlineNv), |
24 |
40727286 |
d_nm(NodeManager::currentNM()), |
25 |
122181858 |
d_nvMaxChildren(default_nchild_thresh) |
26 |
|
{ |
27 |
40727286 |
d_inlineNv.d_id = 0; |
28 |
40727286 |
d_inlineNv.d_rc = 0; |
29 |
40727286 |
d_inlineNv.d_kind = expr::NodeValue::kindToDKind(kind::UNDEFINED_KIND); |
30 |
40727286 |
d_inlineNv.d_nchildren = 0; |
31 |
40727286 |
} |
32 |
|
|
33 |
33918387 |
NodeBuilder::NodeBuilder(Kind k) |
34 |
33918387 |
: d_nv(&d_inlineNv), |
35 |
33918387 |
d_nm(NodeManager::currentNM()), |
36 |
101755161 |
d_nvMaxChildren(default_nchild_thresh) |
37 |
|
{ |
38 |
33918387 |
Assert(k != kind::NULL_EXPR && k != kind::UNDEFINED_KIND) |
39 |
|
<< "illegal Node-building kind"; |
40 |
|
|
41 |
33918387 |
d_inlineNv.d_id = 1; // have a kind already |
42 |
33918387 |
d_inlineNv.d_rc = 0; |
43 |
33918387 |
d_inlineNv.d_kind = expr::NodeValue::kindToDKind(k); |
44 |
33918387 |
d_inlineNv.d_nchildren = 0; |
45 |
33918387 |
} |
46 |
|
|
47 |
2 |
NodeBuilder::NodeBuilder(NodeManager* nm) |
48 |
2 |
: d_nv(&d_inlineNv), d_nm(nm), d_nvMaxChildren(default_nchild_thresh) |
49 |
|
{ |
50 |
2 |
d_inlineNv.d_id = 0; |
51 |
2 |
d_inlineNv.d_rc = 0; |
52 |
2 |
d_inlineNv.d_kind = expr::NodeValue::kindToDKind(kind::UNDEFINED_KIND); |
53 |
2 |
d_inlineNv.d_nchildren = 0; |
54 |
2 |
} |
55 |
|
|
56 |
183426821 |
NodeBuilder::NodeBuilder(NodeManager* nm, Kind k) |
57 |
183426821 |
: d_nv(&d_inlineNv), d_nm(nm), d_nvMaxChildren(default_nchild_thresh) |
58 |
|
{ |
59 |
183426821 |
Assert(k != kind::NULL_EXPR && k != kind::UNDEFINED_KIND) |
60 |
|
<< "illegal Node-building kind"; |
61 |
|
|
62 |
183426821 |
d_inlineNv.d_id = 1; // have a kind already |
63 |
183426821 |
d_inlineNv.d_rc = 0; |
64 |
183426821 |
d_inlineNv.d_kind = expr::NodeValue::kindToDKind(k); |
65 |
183426821 |
d_inlineNv.d_nchildren = 0; |
66 |
183426821 |
} |
67 |
|
|
68 |
47037344 |
NodeBuilder::NodeBuilder(const NodeBuilder& nb) |
69 |
47037344 |
: d_nv(&d_inlineNv), d_nm(nb.d_nm), d_nvMaxChildren(default_nchild_thresh) |
70 |
|
{ |
71 |
47037344 |
d_inlineNv.d_id = nb.d_nv->d_id; |
72 |
47037344 |
d_inlineNv.d_rc = 0; |
73 |
47037344 |
d_inlineNv.d_kind = nb.d_nv->d_kind; |
74 |
47037344 |
d_inlineNv.d_nchildren = 0; |
75 |
|
|
76 |
47037344 |
internalCopy(nb); |
77 |
47037344 |
} |
78 |
|
|
79 |
610219674 |
NodeBuilder::~NodeBuilder() |
80 |
|
{ |
81 |
305109837 |
if (CVC5_PREDICT_FALSE(nvIsAllocated())) |
82 |
|
{ |
83 |
7834 |
dealloc(); |
84 |
|
} |
85 |
305102003 |
else if (CVC5_PREDICT_FALSE(!isUsed())) |
86 |
|
{ |
87 |
78952180 |
decrRefCounts(); |
88 |
|
} |
89 |
305109837 |
} |
90 |
|
|
91 |
1831923768 |
Kind NodeBuilder::getKind() const |
92 |
|
{ |
93 |
1831923768 |
Assert(!isUsed()) << "NodeBuilder is one-shot only; " |
94 |
|
"attempt to access it after conversion"; |
95 |
1831923768 |
return d_nv->getKind(); |
96 |
|
} |
97 |
|
|
98 |
676192363 |
kind::MetaKind NodeBuilder::getMetaKind() const |
99 |
|
{ |
100 |
676192363 |
Assert(!isUsed()) << "NodeBuilder is one-shot only; " |
101 |
|
"attempt to access it after conversion"; |
102 |
676192363 |
Assert(getKind() != kind::UNDEFINED_KIND) |
103 |
|
<< "The metakind of a NodeBuilder is undefined " |
104 |
|
"until a Kind is set"; |
105 |
676192363 |
return d_nv->getMetaKind(); |
106 |
|
} |
107 |
|
|
108 |
453528617 |
unsigned NodeBuilder::getNumChildren() const |
109 |
|
{ |
110 |
453528617 |
Assert(!isUsed()) << "NodeBuilder is one-shot only; " |
111 |
|
"attempt to access it after conversion"; |
112 |
453528617 |
Assert(getKind() != kind::UNDEFINED_KIND) |
113 |
|
<< "The number of children of a NodeBuilder is undefined " |
114 |
|
"until a Kind is set"; |
115 |
453528617 |
return d_nv->getNumChildren(); |
116 |
|
} |
117 |
|
|
118 |
|
Node NodeBuilder::getOperator() const |
119 |
|
{ |
120 |
|
Assert(!isUsed()) << "NodeBuilder is one-shot only; " |
121 |
|
"attempt to access it after conversion"; |
122 |
|
Assert(getKind() != kind::UNDEFINED_KIND) |
123 |
|
<< "NodeBuilder operator access is not permitted " |
124 |
|
"until a Kind is set"; |
125 |
|
Assert(getMetaKind() == kind::metakind::PARAMETERIZED) |
126 |
|
<< "NodeBuilder operator access is only permitted " |
127 |
|
"on parameterized kinds, not `" |
128 |
|
<< getKind() << "'"; |
129 |
|
return Node(d_nv->getOperator()); |
130 |
|
} |
131 |
|
|
132 |
2703541 |
Node NodeBuilder::getChild(int i) const |
133 |
|
{ |
134 |
2703541 |
Assert(!isUsed()) << "NodeBuilder is one-shot only; " |
135 |
|
"attempt to access it after conversion"; |
136 |
2703541 |
Assert(getKind() != kind::UNDEFINED_KIND) |
137 |
|
<< "NodeBuilder child access is not permitted " |
138 |
|
"until a Kind is set"; |
139 |
2703541 |
Assert(i >= 0 && unsigned(i) < d_nv->getNumChildren()) |
140 |
|
<< "index out of range for NodeBuilder::getChild()"; |
141 |
2703541 |
return Node(d_nv->getChild(i)); |
142 |
|
} |
143 |
|
|
144 |
2696158 |
Node NodeBuilder::operator[](int i) const { return getChild(i); } |
145 |
|
|
146 |
105109 |
void NodeBuilder::clear(Kind k) |
147 |
|
{ |
148 |
105109 |
Assert(k != kind::NULL_EXPR) << "illegal Node-building clear kind"; |
149 |
|
|
150 |
105109 |
if (CVC5_PREDICT_FALSE(nvIsAllocated())) |
151 |
|
{ |
152 |
6 |
dealloc(); |
153 |
|
} |
154 |
105103 |
else if (CVC5_PREDICT_FALSE(!isUsed())) |
155 |
|
{ |
156 |
105097 |
decrRefCounts(); |
157 |
|
} |
158 |
|
else |
159 |
|
{ |
160 |
6 |
setUnused(); |
161 |
|
} |
162 |
|
|
163 |
105109 |
d_inlineNv.d_kind = expr::NodeValue::kindToDKind(k); |
164 |
105109 |
for (expr::NodeValue::nv_iterator i = d_inlineNv.nv_begin(); |
165 |
105109 |
i != d_inlineNv.nv_end(); |
166 |
|
++i) |
167 |
|
{ |
168 |
|
(*i)->dec(); |
169 |
|
} |
170 |
105109 |
d_inlineNv.d_nchildren = 0; |
171 |
|
// keep track of whether or not we hvae a kind already |
172 |
105109 |
d_inlineNv.d_id = (k == kind::UNDEFINED_KIND) ? 0 : 1; |
173 |
105109 |
} |
174 |
|
|
175 |
11629026 |
NodeBuilder& NodeBuilder::operator<<(const Kind& k) |
176 |
|
{ |
177 |
11629026 |
Assert(!isUsed()) << "NodeBuilder is one-shot only; " |
178 |
|
"attempt to access it after conversion"; |
179 |
11629026 |
Assert(getKind() == kind::UNDEFINED_KIND || d_nv->d_id == 0) |
180 |
|
<< "can't redefine the Kind of a NodeBuilder"; |
181 |
11629026 |
Assert(d_nv->d_id == 0) |
182 |
|
<< "internal inconsistency with NodeBuilder: d_id != 0"; |
183 |
11629026 |
AssertArgument( |
184 |
|
k != kind::UNDEFINED_KIND && k != kind::NULL_EXPR && k < kind::LAST_KIND, |
185 |
|
k, |
186 |
|
"illegal node-building kind"); |
187 |
|
// This test means: we didn't have a Kind at the beginning (on |
188 |
|
// NodeBuilder construction or at the last clear()), but we do |
189 |
|
// now. That means we appended a Kind with operator<<(Kind), |
190 |
|
// which now (lazily) we'll collapse. |
191 |
11629026 |
if (CVC5_PREDICT_FALSE(d_nv->d_id == 0 && getKind() != kind::UNDEFINED_KIND)) |
192 |
|
{ |
193 |
|
Node n2 = operator Node(); |
194 |
|
clear(); |
195 |
|
append(n2); |
196 |
|
} |
197 |
11629026 |
else if (d_nv->d_nchildren == 0) |
198 |
|
{ |
199 |
11628998 |
d_nv->d_id = 1; // remember that we had a kind from the start |
200 |
|
} |
201 |
11629026 |
d_nv->d_kind = expr::NodeValue::kindToDKind(k); |
202 |
11629026 |
return *this; |
203 |
|
} |
204 |
|
|
205 |
415333456 |
NodeBuilder& NodeBuilder::operator<<(TNode n) |
206 |
|
{ |
207 |
415333456 |
Assert(!isUsed()) << "NodeBuilder is one-shot only; " |
208 |
|
"attempt to access it after conversion"; |
209 |
|
// This test means: we didn't have a Kind at the beginning (on |
210 |
|
// NodeBuilder construction or at the last clear()), but we do |
211 |
|
// now. That means we appended a Kind with operator<<(Kind), |
212 |
|
// which now (lazily) we'll collapse. |
213 |
415333456 |
if (CVC5_PREDICT_FALSE(d_nv->d_id == 0 && getKind() != kind::UNDEFINED_KIND)) |
214 |
|
{ |
215 |
12 |
Node n2 = operator Node(); |
216 |
6 |
clear(); |
217 |
6 |
append(n2); |
218 |
|
} |
219 |
415333456 |
return append(n); |
220 |
|
} |
221 |
|
|
222 |
134415 |
NodeBuilder& NodeBuilder::operator<<(TypeNode n) |
223 |
|
{ |
224 |
134415 |
Assert(!isUsed()) << "NodeBuilder is one-shot only; " |
225 |
|
"attempt to access it after conversion"; |
226 |
|
// This test means: we didn't have a Kind at the beginning (on |
227 |
|
// NodeBuilder construction or at the last clear()), but we do |
228 |
|
// now. That means we appended a Kind with operator<<(Kind), |
229 |
|
// which now (lazily) we'll collapse. |
230 |
134415 |
if (CVC5_PREDICT_FALSE(d_nv->d_id == 0 && getKind() != kind::UNDEFINED_KIND)) |
231 |
|
{ |
232 |
|
Node n2 = operator Node(); |
233 |
|
clear(); |
234 |
|
append(n2); |
235 |
|
} |
236 |
134415 |
return append(n); |
237 |
|
} |
238 |
|
|
239 |
74116 |
NodeBuilder& NodeBuilder::append(const std::vector<TypeNode>& children) |
240 |
|
{ |
241 |
74116 |
Assert(!isUsed()) << "NodeBuilder is one-shot only; " |
242 |
|
"attempt to access it after conversion"; |
243 |
74116 |
return append(children.begin(), children.end()); |
244 |
|
} |
245 |
|
|
246 |
486926489 |
NodeBuilder& NodeBuilder::append(TNode n) |
247 |
|
{ |
248 |
486926489 |
Assert(!isUsed()) << "NodeBuilder is one-shot only; " |
249 |
|
"attempt to access it after conversion"; |
250 |
486926489 |
Assert(!n.isNull()) << "Cannot use NULL Node as a child of a Node"; |
251 |
486926489 |
if (n.getKind() == kind::BUILTIN) |
252 |
|
{ |
253 |
|
return *this << NodeManager::operatorToKind(n); |
254 |
|
} |
255 |
486926489 |
allocateNvIfNecessaryForAppend(); |
256 |
486926489 |
expr::NodeValue* nv = n.d_nv; |
257 |
486926489 |
nv->inc(); |
258 |
486926489 |
d_nv->d_children[d_nv->d_nchildren++] = nv; |
259 |
486926489 |
Assert(d_nv->d_nchildren <= d_nvMaxChildren); |
260 |
486926489 |
return *this; |
261 |
|
} |
262 |
|
|
263 |
375623 |
NodeBuilder& NodeBuilder::append(const TypeNode& typeNode) |
264 |
|
{ |
265 |
375623 |
Assert(!isUsed()) << "NodeBuilder is one-shot only; " |
266 |
|
"attempt to access it after conversion"; |
267 |
375623 |
Assert(!typeNode.isNull()) << "Cannot use NULL Node as a child of a Node"; |
268 |
375623 |
allocateNvIfNecessaryForAppend(); |
269 |
375623 |
expr::NodeValue* nv = typeNode.d_nv; |
270 |
375623 |
nv->inc(); |
271 |
375623 |
d_nv->d_children[d_nv->d_nchildren++] = nv; |
272 |
375623 |
Assert(d_nv->d_nchildren <= d_nvMaxChildren); |
273 |
375623 |
return *this; |
274 |
|
} |
275 |
|
|
276 |
2601300 |
void NodeBuilder::realloc(size_t toSize) |
277 |
|
{ |
278 |
2601300 |
AlwaysAssert(toSize > d_nvMaxChildren) |
279 |
|
<< "attempt to realloc() a NodeBuilder to a smaller/equal size!"; |
280 |
2601300 |
Assert(toSize < (static_cast<size_t>(1) << expr::NodeValue::NBITS_NCHILDREN)) |
281 |
|
<< "attempt to realloc() a NodeBuilder to size " << toSize |
282 |
|
<< " (beyond hard limit of " << expr::NodeValue::MAX_CHILDREN << ")"; |
283 |
|
|
284 |
2601300 |
if (CVC5_PREDICT_FALSE(nvIsAllocated())) |
285 |
|
{ |
286 |
|
// Ensure d_nv is not modified on allocation failure |
287 |
990998 |
expr::NodeValue* newBlock = (expr::NodeValue*)std::realloc( |
288 |
990998 |
d_nv, sizeof(expr::NodeValue) + (sizeof(expr::NodeValue*) * toSize)); |
289 |
990998 |
if (newBlock == nullptr) |
290 |
|
{ |
291 |
|
// In this case, d_nv was NOT freed. If we throw, the |
292 |
|
// deallocation should occur on destruction of the NodeBuilder. |
293 |
|
throw std::bad_alloc(); |
294 |
|
} |
295 |
990998 |
d_nvMaxChildren = toSize; |
296 |
990998 |
Assert(d_nvMaxChildren == toSize); // overflow check |
297 |
|
// Here, the copy (between two heap-allocated buffers) has already |
298 |
|
// been done for us by the std::realloc(). |
299 |
990998 |
d_nv = newBlock; |
300 |
|
} |
301 |
|
else |
302 |
|
{ |
303 |
|
// Ensure d_nv is not modified on allocation failure |
304 |
1610302 |
expr::NodeValue* newBlock = (expr::NodeValue*)std::malloc( |
305 |
1610302 |
sizeof(expr::NodeValue) + (sizeof(expr::NodeValue*) * toSize)); |
306 |
1610302 |
if (newBlock == nullptr) |
307 |
|
{ |
308 |
|
throw std::bad_alloc(); |
309 |
|
} |
310 |
1610302 |
d_nvMaxChildren = toSize; |
311 |
1610302 |
Assert(d_nvMaxChildren == toSize); // overflow check |
312 |
|
|
313 |
1610302 |
d_nv = newBlock; |
314 |
1610302 |
d_nv->d_id = d_inlineNv.d_id; |
315 |
1610302 |
d_nv->d_rc = 0; |
316 |
1610302 |
d_nv->d_kind = d_inlineNv.d_kind; |
317 |
1610302 |
d_nv->d_nchildren = d_inlineNv.d_nchildren; |
318 |
|
|
319 |
3220604 |
std::copy(d_inlineNv.d_children, |
320 |
1610302 |
d_inlineNv.d_children + d_inlineNv.d_nchildren, |
321 |
1610302 |
d_nv->d_children); |
322 |
|
|
323 |
|
// ensure "inline" children don't get decremented in dtor |
324 |
1610302 |
d_inlineNv.d_nchildren = 0; |
325 |
|
} |
326 |
2601300 |
} |
327 |
|
|
328 |
1082486 |
void NodeBuilder::dealloc() |
329 |
|
{ |
330 |
1082486 |
Assert(nvIsAllocated()) |
331 |
|
<< "Internal error: NodeBuilder: dealloc() called without a " |
332 |
|
"private NodeBuilder-allocated buffer"; |
333 |
|
|
334 |
41056197 |
for (expr::NodeValue::nv_iterator i = d_nv->nv_begin(); i != d_nv->nv_end(); |
335 |
|
++i) |
336 |
|
{ |
337 |
39973711 |
(*i)->dec(); |
338 |
|
} |
339 |
|
|
340 |
1082486 |
free(d_nv); |
341 |
1082486 |
d_nv = &d_inlineNv; |
342 |
1082486 |
d_nvMaxChildren = default_nchild_thresh; |
343 |
1082486 |
} |
344 |
|
|
345 |
276107655 |
void NodeBuilder::decrRefCounts() |
346 |
|
{ |
347 |
276107655 |
Assert(!nvIsAllocated()) |
348 |
|
<< "Internal error: NodeBuilder: decrRefCounts() called with a " |
349 |
|
"private NodeBuilder-allocated buffer"; |
350 |
|
|
351 |
646908741 |
for (expr::NodeValue::nv_iterator i = d_inlineNv.nv_begin(); |
352 |
646908741 |
i != d_inlineNv.nv_end(); |
353 |
|
++i) |
354 |
|
{ |
355 |
370801086 |
(*i)->dec(); |
356 |
|
} |
357 |
|
|
358 |
276107655 |
d_inlineNv.d_nchildren = 0; |
359 |
276107655 |
} |
360 |
|
|
361 |
163066 |
TypeNode NodeBuilder::constructTypeNode() { return TypeNode(constructNV()); } |
362 |
|
|
363 |
225986763 |
Node NodeBuilder::constructNode() |
364 |
|
{ |
365 |
225986763 |
Node n(constructNV()); |
366 |
225987221 |
maybeCheckType(n); |
367 |
225986305 |
return n; |
368 |
|
} |
369 |
|
|
370 |
|
Node* NodeBuilder::constructNodePtr() |
371 |
|
{ |
372 |
|
std::unique_ptr<Node> np(new Node(constructNV())); |
373 |
|
maybeCheckType(*np.get()); |
374 |
|
return np.release(); |
375 |
|
} |
376 |
|
|
377 |
43562162 |
NodeBuilder::operator Node() { return constructNode(); } |
378 |
|
|
379 |
|
NodeBuilder::operator TypeNode() { return constructTypeNode(); } |
380 |
|
|
381 |
226149829 |
expr::NodeValue* NodeBuilder::constructNV() |
382 |
|
{ |
383 |
226149829 |
Assert(!isUsed()) << "NodeBuilder is one-shot only; " |
384 |
|
"attempt to access it after conversion"; |
385 |
226149829 |
Assert(getKind() != kind::UNDEFINED_KIND) |
386 |
|
<< "Can't make an expression of an undefined kind!"; |
387 |
|
|
388 |
|
// NOTE: The comments in this function refer to the cases in the |
389 |
|
// file comments at the top of this file. |
390 |
|
|
391 |
|
// Case 0: If a VARIABLE |
392 |
452299658 |
if (getMetaKind() == kind::metakind::VARIABLE |
393 |
226149829 |
|| getMetaKind() == kind::metakind::NULLARY_OPERATOR) |
394 |
|
{ |
395 |
|
/* 0. If a VARIABLE, treat similarly to 1(b), except that we know |
396 |
|
* there are no children (no reference counts to reason about), |
397 |
|
* and we don't keep VARIABLE-kinded Nodes in the NodeManager |
398 |
|
* pool. */ |
399 |
|
|
400 |
1131067 |
Assert(!nvIsAllocated()) |
401 |
|
<< "internal NodeBuilder error: " |
402 |
|
"VARIABLE-kinded NodeBuilder is heap-allocated !?"; |
403 |
1131067 |
Assert(d_inlineNv.d_nchildren == 0) |
404 |
|
<< "improperly-formed VARIABLE-kinded NodeBuilder: " |
405 |
|
"no children permitted"; |
406 |
|
|
407 |
|
// we have to copy the inline NodeValue out |
408 |
|
expr::NodeValue* nv = |
409 |
1131067 |
(expr::NodeValue*)std::malloc(sizeof(expr::NodeValue)); |
410 |
1131067 |
if (nv == nullptr) |
411 |
|
{ |
412 |
|
throw std::bad_alloc(); |
413 |
|
} |
414 |
|
// there are no children, so we don't have to worry about |
415 |
|
// reference counts in this case. |
416 |
1131067 |
nv->d_nchildren = 0; |
417 |
1131067 |
nv->d_kind = d_nv->d_kind; |
418 |
1131067 |
nv->d_id = d_nm->next_id++; // FIXME multithreading |
419 |
1131067 |
nv->d_rc = 0; |
420 |
1131067 |
setUsed(); |
421 |
1131067 |
if (Debug.isOn("gc")) |
422 |
|
{ |
423 |
|
Debug("gc") << "creating node value " << nv << " [" << nv->d_id << "]: "; |
424 |
|
nv->printAst(Debug("gc")); |
425 |
|
Debug("gc") << std::endl; |
426 |
|
} |
427 |
1131067 |
return nv; |
428 |
|
} |
429 |
|
|
430 |
|
// check that there are the right # of children for this kind |
431 |
225018762 |
Assert(getMetaKind() != kind::metakind::CONSTANT) |
432 |
|
<< "Cannot make Nodes with NodeBuilder that have CONSTANT-kinded kinds"; |
433 |
225018762 |
Assert(getNumChildren() >= kind::metakind::getMinArityForKind(getKind())) |
434 |
|
<< "Nodes with kind " << getKind() << " must have at least " |
435 |
|
<< kind::metakind::getMinArityForKind(getKind()) |
436 |
|
<< " children (the one under " |
437 |
|
"construction has " |
438 |
|
<< getNumChildren() << ")"; |
439 |
225018762 |
Assert(getNumChildren() <= kind::metakind::getMaxArityForKind(getKind())) |
440 |
|
<< "Nodes with kind " << getKind() << " must have at most " |
441 |
|
<< kind::metakind::getMaxArityForKind(getKind()) |
442 |
|
<< " children (the one under " |
443 |
|
"construction has " |
444 |
|
<< getNumChildren() << ")"; |
445 |
|
|
446 |
|
// Implementation differs depending on whether the NodeValue was |
447 |
|
// malloc'ed or not and whether or not it's in the already-been-seen |
448 |
|
// NodeManager pool of Nodes. See implementation notes at the top |
449 |
|
// of this file. |
450 |
|
|
451 |
225018762 |
if (CVC5_PREDICT_TRUE(!nvIsAllocated())) |
452 |
|
{ |
453 |
|
/** Case 1. d_nv points to d_inlineNv: it is the backing store |
454 |
|
** allocated "inline" in this NodeBuilder. **/ |
455 |
|
|
456 |
|
// Lookup the expression value in the pool we already have |
457 |
223416300 |
expr::NodeValue* poolNv = d_nm->poolLookup(&d_inlineNv); |
458 |
|
// If something else is there, we reuse it |
459 |
223416300 |
if (poolNv != nullptr) |
460 |
|
{ |
461 |
|
/* Subcase (a): The Node under construction already exists in |
462 |
|
* the NodeManager's pool. */ |
463 |
|
|
464 |
|
/* 1(a). Reference-counts for all children in d_inlineNv must be |
465 |
|
* decremented, and the NodeBuilder must be marked as "used" and |
466 |
|
* the number of children set to zero so that we don't decrement |
467 |
|
* them again on destruction. The existing NodeManager pool |
468 |
|
* entry is returned. |
469 |
|
*/ |
470 |
197050378 |
decrRefCounts(); |
471 |
197050378 |
d_inlineNv.d_nchildren = 0; |
472 |
197050378 |
setUsed(); |
473 |
197050378 |
return poolNv; |
474 |
|
} |
475 |
|
else |
476 |
|
{ |
477 |
|
/* Subcase (b): The Node under construction is NOT already in |
478 |
|
* the NodeManager's pool. */ |
479 |
|
|
480 |
|
/* 1(b). A new heap-allocated NodeValue must be constructed and |
481 |
|
* all settings and children from d_inlineNv copied into it. |
482 |
|
* This new NodeValue is put into the NodeManager's pool. The |
483 |
|
* NodeBuilder is marked as "used" and the number of children in |
484 |
|
* d_inlineNv set to zero so that we don't decrement child |
485 |
|
* reference counts on destruction (the child reference counts |
486 |
|
* have been "taken over" by the new NodeValue). We return a |
487 |
|
* Node wrapper for this new NodeValue, which increments its |
488 |
|
* reference count. */ |
489 |
|
|
490 |
|
// create the canonical expression value for this node |
491 |
26365922 |
expr::NodeValue* nv = (expr::NodeValue*)std::malloc( |
492 |
|
sizeof(expr::NodeValue) |
493 |
52731844 |
+ (sizeof(expr::NodeValue*) * d_inlineNv.d_nchildren)); |
494 |
26365922 |
if (nv == nullptr) |
495 |
|
{ |
496 |
|
throw std::bad_alloc(); |
497 |
|
} |
498 |
26365922 |
nv->d_nchildren = d_inlineNv.d_nchildren; |
499 |
26365922 |
nv->d_kind = d_inlineNv.d_kind; |
500 |
26365922 |
nv->d_id = d_nm->next_id++; // FIXME multithreading |
501 |
26365922 |
nv->d_rc = 0; |
502 |
|
|
503 |
52731844 |
std::copy(d_inlineNv.d_children, |
504 |
26365922 |
d_inlineNv.d_children + d_inlineNv.d_nchildren, |
505 |
26365922 |
nv->d_children); |
506 |
|
|
507 |
26365922 |
d_inlineNv.d_nchildren = 0; |
508 |
26365922 |
setUsed(); |
509 |
|
|
510 |
|
// poolNv = nv; |
511 |
26365922 |
d_nm->poolInsert(nv); |
512 |
26365922 |
if (Debug.isOn("gc")) |
513 |
|
{ |
514 |
|
Debug("gc") << "creating node value " << nv << " [" << nv->d_id |
515 |
|
<< "]: "; |
516 |
|
nv->printAst(Debug("gc")); |
517 |
|
Debug("gc") << std::endl; |
518 |
|
} |
519 |
26365922 |
return nv; |
520 |
|
} |
521 |
|
} |
522 |
|
else |
523 |
|
{ |
524 |
|
/** Case 2. d_nv does NOT point to d_inlineNv: it is a new, larger |
525 |
|
** buffer that was heap-allocated by this NodeBuilder. **/ |
526 |
|
|
527 |
|
// Lookup the expression value in the pool we already have (with insert) |
528 |
1602462 |
expr::NodeValue* poolNv = d_nm->poolLookup(d_nv); |
529 |
|
// If something else is there, we reuse it |
530 |
1602462 |
if (poolNv != nullptr) |
531 |
|
{ |
532 |
|
/* Subcase (a): The Node under construction already exists in |
533 |
|
* the NodeManager's pool. */ |
534 |
|
|
535 |
|
/* 2(a). Reference-counts for all children in d_nv must be |
536 |
|
* decremented. The NodeBuilder is marked as "used" and the |
537 |
|
* heap-allocated d_nv deleted. d_nv is repointed to d_inlineNv |
538 |
|
* so that destruction of the NodeBuilder doesn't cause any |
539 |
|
* problems. The existing NodeManager pool entry is |
540 |
|
* returned. */ |
541 |
|
|
542 |
1074646 |
dealloc(); |
543 |
1074646 |
setUsed(); |
544 |
1074646 |
return poolNv; |
545 |
|
} |
546 |
|
else |
547 |
|
{ |
548 |
|
/* Subcase (b) The Node under construction is NOT already in the |
549 |
|
* NodeManager's pool. */ |
550 |
|
|
551 |
|
/* 2(b). The heap-allocated d_nv is "cropped" to the correct |
552 |
|
* size (based on the number of children it _actually_ has). |
553 |
|
* d_nv is repointed to d_inlineNv so that destruction of the |
554 |
|
* NodeBuilder doesn't cause any problems, and the (old) value |
555 |
|
* it had is placed into the NodeManager's pool and returned in |
556 |
|
* a Node wrapper. */ |
557 |
|
|
558 |
527816 |
crop(); |
559 |
527816 |
expr::NodeValue* nv = d_nv; |
560 |
527816 |
nv->d_id = d_nm->next_id++; // FIXME multithreading |
561 |
527816 |
d_nv = &d_inlineNv; |
562 |
527816 |
d_nvMaxChildren = default_nchild_thresh; |
563 |
527816 |
setUsed(); |
564 |
|
|
565 |
|
// poolNv = nv; |
566 |
527816 |
d_nm->poolInsert(nv); |
567 |
1055632 |
Debug("gc") << "creating node value " << nv << " [" << nv->d_id |
568 |
527816 |
<< "]: " << *nv << "\n"; |
569 |
527816 |
return nv; |
570 |
|
} |
571 |
|
} |
572 |
|
} |
573 |
|
|
574 |
47037344 |
void NodeBuilder::internalCopy(const NodeBuilder& nb) |
575 |
|
{ |
576 |
47037344 |
if (nb.isUsed()) |
577 |
|
{ |
578 |
|
setUsed(); |
579 |
|
return; |
580 |
|
} |
581 |
|
|
582 |
47037344 |
bool realloced CVC5_UNUSED = false; |
583 |
47037344 |
if (nb.d_nvMaxChildren > d_nvMaxChildren) |
584 |
|
{ |
585 |
7828 |
realloced = true; |
586 |
7828 |
realloc(nb.d_nvMaxChildren); |
587 |
|
} |
588 |
|
|
589 |
47037344 |
Assert(nb.d_nvMaxChildren <= d_nvMaxChildren); |
590 |
47037344 |
Assert(nb.d_nv->nv_end() >= nb.d_nv->nv_begin()); |
591 |
47037344 |
Assert((size_t)(nb.d_nv->nv_end() - nb.d_nv->nv_begin()) <= d_nvMaxChildren) |
592 |
|
<< "realloced:" << (realloced ? "true" : "false") |
593 |
|
<< ", d_nvMax:" << d_nvMaxChildren |
594 |
|
<< ", size:" << nb.d_nv->nv_end() - nb.d_nv->nv_begin() |
595 |
|
<< ", nc:" << nb.d_nv->getNumChildren(); |
596 |
47037344 |
std::copy(nb.d_nv->nv_begin(), nb.d_nv->nv_end(), d_nv->nv_begin()); |
597 |
|
|
598 |
47037344 |
d_nv->d_nchildren = nb.d_nv->d_nchildren; |
599 |
|
|
600 |
49251631 |
for (expr::NodeValue::nv_iterator i = d_nv->nv_begin(); i != d_nv->nv_end(); |
601 |
|
++i) |
602 |
|
{ |
603 |
2214287 |
(*i)->inc(); |
604 |
|
} |
605 |
|
} |
606 |
|
|
607 |
|
#ifdef CVC5_DEBUG |
608 |
225986763 |
inline void NodeBuilder::maybeCheckType(const TNode n) const |
609 |
|
{ |
610 |
|
/* force an immediate type check, if early type checking is |
611 |
|
enabled and the current node isn't a variable or constant */ |
612 |
225986763 |
kind::MetaKind mk = n.getMetaKind(); |
613 |
225986763 |
if (mk != kind::metakind::VARIABLE && mk != kind::metakind::NULLARY_OPERATOR |
614 |
224855696 |
&& mk != kind::metakind::CONSTANT) |
615 |
|
{ |
616 |
224856154 |
d_nm->getType(n, true); |
617 |
|
} |
618 |
225986305 |
} |
619 |
|
#endif /* CVC5_DEBUG */ |
620 |
|
|
621 |
4714968694 |
bool NodeBuilder::isUsed() const { return CVC5_PREDICT_FALSE(d_nv == nullptr); } |
622 |
|
|
623 |
226149829 |
void NodeBuilder::setUsed() |
624 |
|
{ |
625 |
226149829 |
Assert(!isUsed()) << "Internal error: bad `used' state in NodeBuilder!"; |
626 |
226149829 |
Assert(d_inlineNv.d_nchildren == 0 |
627 |
|
&& d_nvMaxChildren == default_nchild_thresh) |
628 |
|
<< "Internal error: bad `inline' state in NodeBuilder!"; |
629 |
226149829 |
d_nv = nullptr; |
630 |
226149829 |
} |
631 |
|
|
632 |
6 |
void NodeBuilder::setUnused() |
633 |
|
{ |
634 |
6 |
Assert(isUsed()) << "Internal error: bad `used' state in NodeBuilder!"; |
635 |
6 |
Assert(d_inlineNv.d_nchildren == 0 |
636 |
|
&& d_nvMaxChildren == default_nchild_thresh) |
637 |
|
<< "Internal error: bad `inline' state in NodeBuilder!"; |
638 |
6 |
d_nv = &d_inlineNv; |
639 |
6 |
} |
640 |
|
|
641 |
811684032 |
bool NodeBuilder::nvIsAllocated() const |
642 |
|
{ |
643 |
811684032 |
return CVC5_PREDICT_FALSE(d_nv != &d_inlineNv) |
644 |
811684032 |
&& CVC5_PREDICT_TRUE(d_nv != nullptr); |
645 |
|
} |
646 |
|
|
647 |
487302112 |
bool NodeBuilder::nvNeedsToBeAllocated() const |
648 |
|
{ |
649 |
487302112 |
return CVC5_PREDICT_FALSE(d_nv->d_nchildren == d_nvMaxChildren); |
650 |
|
} |
651 |
|
|
652 |
2593452 |
void NodeBuilder::realloc() |
653 |
|
{ |
654 |
2593452 |
size_t newSize = 2 * size_t(d_nvMaxChildren); |
655 |
2593452 |
size_t hardLimit = expr::NodeValue::MAX_CHILDREN; |
656 |
2593452 |
realloc(CVC5_PREDICT_FALSE(newSize > hardLimit) ? hardLimit : newSize); |
657 |
2593452 |
} |
658 |
|
|
659 |
487302112 |
void NodeBuilder::allocateNvIfNecessaryForAppend() |
660 |
|
{ |
661 |
487302112 |
if (CVC5_PREDICT_FALSE(nvNeedsToBeAllocated())) |
662 |
|
{ |
663 |
2593452 |
realloc(); |
664 |
|
} |
665 |
487302112 |
} |
666 |
|
|
667 |
527816 |
void NodeBuilder::crop() |
668 |
|
{ |
669 |
1055632 |
if (CVC5_PREDICT_FALSE(nvIsAllocated()) |
670 |
527816 |
&& CVC5_PREDICT_TRUE(d_nvMaxChildren > d_nv->d_nchildren)) |
671 |
|
{ |
672 |
|
// Ensure d_nv is not modified on allocation failure |
673 |
511790 |
expr::NodeValue* newBlock = (expr::NodeValue*)std::realloc( |
674 |
511790 |
d_nv, |
675 |
|
sizeof(expr::NodeValue) |
676 |
1023580 |
+ (sizeof(expr::NodeValue*) * d_nv->d_nchildren)); |
677 |
511790 |
if (newBlock == nullptr) |
678 |
|
{ |
679 |
|
// In this case, d_nv was NOT freed. If we throw, the |
680 |
|
// deallocation should occur on destruction of the |
681 |
|
// NodeBuilder. |
682 |
|
throw std::bad_alloc(); |
683 |
|
} |
684 |
511790 |
d_nv = newBlock; |
685 |
511790 |
d_nvMaxChildren = d_nv->d_nchildren; |
686 |
|
} |
687 |
527816 |
} |
688 |
|
|
689 |
|
NodeBuilder& NodeBuilder::collapseTo(Kind k) |
690 |
|
{ |
691 |
|
AssertArgument( |
692 |
|
k != kind::UNDEFINED_KIND && k != kind::NULL_EXPR && k < kind::LAST_KIND, |
693 |
|
k, |
694 |
|
"illegal collapsing kind"); |
695 |
|
|
696 |
|
if (getKind() != k) |
697 |
|
{ |
698 |
|
Node n = operator Node(); |
699 |
|
clear(); |
700 |
|
d_nv->d_kind = expr::NodeValue::kindToDKind(k); |
701 |
|
d_nv->d_id = 1; // have a kind already |
702 |
|
return append(n); |
703 |
|
} |
704 |
|
return *this; |
705 |
|
} |
706 |
|
|
707 |
|
std::ostream& operator<<(std::ostream& out, const NodeBuilder& nb) |
708 |
|
{ |
709 |
|
return out << *nb.d_nv; |
710 |
|
} |
711 |
|
|
712 |
29574 |
} // namespace cvc5 |