@@ -69,18 +69,15 @@ The formal properties of saturation have to be established.
69
69
- Saturation does not complete with respect to associativity.
70
70
Instead the claim is along the lines that the resulting E-graph can be used as a canonizer.
71
71
If given a set of equations E that are saturated, and terms t1, t2 that are
72
- both simplified with respect to left-associativity of concatenation , and t1, t2 belong to the E-graph,
72
+ both simplified with respect to left-associativity of concatentation , and t1, t2 belong to the E-graph,
73
73
then t1 = t2 iff t1 ~ t2 in the E-graph.
74
74
75
75
TODO: Is saturation for (7) overkill for the purpose of canonization?
76
76
77
- TODO: revisit re-entrancy during register_node. It can be called when creating internal extract terms.
78
- Instead of allowing re-entrancy we can accumulate nodes that are registered during recursive calls
79
- and have the main call perform recursive slicing.
80
-
81
77
--*/
82
78
83
79
80
+ #include " ast/ast_pp.h"
84
81
#include " ast/euf/euf_bv_plugin.h"
85
82
#include " ast/euf/euf_egraph.h"
86
83
@@ -91,19 +88,22 @@ namespace euf {
91
88
bv (g.get_manager())
92
89
{}
93
90
94
- enode* bv_plugin::mk_value_concat (enode* a , enode* b ) {
95
- auto v1 = get_value (a );
96
- auto v2 = get_value (b );
97
- auto v3 = v1 + v2 * power ( rational ( 2 ), width (a ));
98
- return mk_value (v3, width (a ) + width (b ));
91
+ enode* bv_plugin::mk_value_concat (enode* hi , enode* lo ) {
92
+ auto v1 = get_value (hi );
93
+ auto v2 = get_value (lo );
94
+ auto v3 = v2 + v1 * rational::power_of_two ( width (lo ));
95
+ return mk_value (v3, width (lo ) + width (hi ));
99
96
}
100
97
101
98
enode* bv_plugin::mk_value (rational const & v, unsigned sz) {
102
99
auto e = bv.mk_numeral (v, sz);
103
- return mk (e, 0 , nullptr );
100
+ auto n = mk (e, 0 , nullptr );
101
+ if (m_ensure_th_var)
102
+ m_ensure_th_var (n);
103
+ return n;
104
104
}
105
105
106
- void bv_plugin::merge_eh (enode* x, enode* y) {
106
+ void bv_plugin::propagate_merge (enode* x, enode* y) {
107
107
if (!bv.is_bv (x->get_expr ()))
108
108
return ;
109
109
@@ -128,7 +128,36 @@ namespace euf {
128
128
propagate_extract (n);
129
129
}
130
130
131
- // enforce concat(v1, v2) = v2*2^|v1| + v1
131
+ void bv_plugin::register_node (enode* n) {
132
+ m_queue.push_back (n);
133
+ m_trail.push_back (new (get_region ()) push_back_vector (m_queue));
134
+ push_plugin_undo (bv.get_family_id ());
135
+ }
136
+
137
+ void bv_plugin::merge_eh (enode* n1, enode* n2) {
138
+ m_queue.push_back (enode_pair (n1, n2));
139
+ m_trail.push_back (new (get_region ()) push_back_vector (m_queue));
140
+ push_plugin_undo (bv.get_family_id ());
141
+ }
142
+
143
+ void bv_plugin::propagate () {
144
+ if (m_qhead == m_queue.size ())
145
+ return ;
146
+ m_trail.push_back (new (get_region ()) value_trail (m_qhead));
147
+ push_plugin_undo (bv.get_family_id ());
148
+ for (; m_qhead < m_queue.size (); ++m_qhead) {
149
+ if (std::holds_alternative<enode*>(m_queue[m_qhead])) {
150
+ auto n = *std::get_if<enode*>(&m_queue[m_qhead]);
151
+ propagate_register_node (n);
152
+ }
153
+ else {
154
+ auto [a, b] = *std::get_if<enode_pair>(&m_queue[m_qhead]);
155
+ propagate_merge (a, b);
156
+ }
157
+ }
158
+ }
159
+
160
+ // enforce concat(v1, v2) = v1*2^|v2| + v2
132
161
void bv_plugin::propagate_values (enode* x) {
133
162
if (!is_value (x))
134
163
return ;
@@ -142,9 +171,9 @@ namespace euf {
142
171
if (is_concat (sib, a, b)) {
143
172
if (!is_value (a) || !is_value (b)) {
144
173
auto val = get_value (x);
145
- auto v1 = mod2k (val, width (a ));
146
- auto v2 = machine_div2k (val, width (a ));
147
- push_merge (mk_concat (mk_value (v1 , width (a)), mk_value (v2 , width (b))), x->get_interpreted ());
174
+ auto val_a = machine_div2k (val, width (b ));
175
+ auto val_b = mod2k (val, width (b ));
176
+ push_merge (mk_concat (mk_value (val_a , width (a)), mk_value (val_b , width (b))), x->get_interpreted ());
148
177
}
149
178
}
150
179
}
@@ -176,18 +205,18 @@ namespace euf {
176
205
if (is_extract (p1, lo_, hi_) && lo_ == lo && hi_ == hi && p1->get_arg (0 )->get_root () == arg_r)
177
206
return ;
178
207
// add the axiom instead of merge(p, mk_extract(arg, lo, hi)), which would require tracking justifications
179
- push_merge (mk_concat (mk_extract (arg, lo, mid ), mk_extract (arg, mid + 1 , hi )), mk_extract (arg, lo, hi));
208
+ push_merge (mk_concat (mk_extract (arg, mid + 1 , hi ), mk_extract (arg, lo, mid )), mk_extract (arg, lo, hi));
180
209
};
181
210
182
- auto propagate_left = [&](enode* b) {
183
- TRACE (" bv" , tout << " propagate-left " << g.bpp (b) << " \n " );
211
+ auto propagate_above = [&](enode* b) {
212
+ TRACE (" bv" , tout << " propagate-above " << g.bpp (b) << " \n " );
184
213
for (enode* sib : enode_class (b))
185
214
if (is_extract (sib, lo2, hi2) && sib->get_arg (0 )->get_root () == arg_r && hi1 + 1 == lo2)
186
215
ensure_concat (lo1, hi1, hi2);
187
216
};
188
217
189
- auto propagate_right = [&](enode* a) {
190
- TRACE (" bv" , tout << " propagate-right " << g.bpp (a) << " \n " );
218
+ auto propagate_below = [&](enode* a) {
219
+ TRACE (" bv" , tout << " propagate-below " << g.bpp (a) << " \n " );
191
220
for (enode* sib : enode_class (a))
192
221
if (is_extract (sib, lo2, hi2) && sib->get_arg (0 )->get_root () == arg_r && hi2 + 1 == lo1)
193
222
ensure_concat (lo2, hi2, hi1);
@@ -196,46 +225,65 @@ namespace euf {
196
225
for (enode* p : enode_parents (n)) {
197
226
if (is_concat (p, a, b)) {
198
227
if (a->get_root () == n_r)
199
- propagate_left (b);
228
+ propagate_below (b);
200
229
if (b->get_root () == n_r)
201
- propagate_right (a);
230
+ propagate_above (a);
202
231
}
203
232
}
204
233
}
205
234
206
- void bv_plugin::push_undo_split (enode* n) {
207
- m_undo_split.push_back (n);
235
+ class bv_plugin ::undo_split : public trail {
236
+ bv_plugin& p;
237
+ enode* n;
238
+ public:
239
+ undo_split (bv_plugin& p, enode* n): p(p), n(n) {}
240
+ void undo () override {
241
+ auto & i = p.info (n);
242
+ i.value = nullptr ;
243
+ i.lo = nullptr ;
244
+ i.hi = nullptr ;
245
+ i.cut = null_cut;
246
+ }
247
+ };
248
+
249
+ void bv_plugin::push_undo_split (enode* n) {
250
+ m_trail.push_back (new (get_region ()) undo_split (*this , n));
208
251
push_plugin_undo (bv.get_family_id ());
209
252
}
210
253
211
254
void bv_plugin::undo () {
212
- enode* n = m_undo_split.back ();
213
- m_undo_split.pop_back ();
214
- auto & i = info (n);
215
- i.lo = nullptr ;
216
- i.hi = nullptr ;
217
- i.cut = null_cut;
255
+ m_trail.back ()->undo ();
256
+ m_trail.pop_back ();
218
257
}
258
+
219
259
220
- void bv_plugin::register_node (enode* n) {
260
+ void bv_plugin::propagate_register_node (enode* n) {
221
261
TRACE (" bv" , tout << " register " << g.bpp (n) << " \n " );
222
- auto & i = info (n);
223
- i.value = n;
224
262
enode* a, * b;
263
+ unsigned lo, hi;
225
264
if (is_concat (n, a, b)) {
226
- i.lo = a;
227
- i.hi = b;
228
- i.cut = width (a);
265
+ auto & i = info (n);
266
+ i.value = n;
267
+ i.hi = a;
268
+ i.lo = b;
269
+ i.cut = width (b);
229
270
push_undo_split (n);
230
271
}
231
- unsigned lo, hi;
232
- if (is_extract (n, lo, hi) && (lo != 0 || hi + 1 != width (n->get_arg (0 )))) {
272
+ else if (is_concat (n) && n->num_args () != 2 ) {
273
+ SASSERT (n->num_args () != 0 );
274
+ auto last = n->get_arg (n->num_args () - 1 );
275
+ for (unsigned i = n->num_args () - 1 ; i-- > 0 ;)
276
+ last = mk_concat (n->get_arg (i), last);
277
+ push_merge (last, n);
278
+ }
279
+ else if (is_extract (n, lo, hi) && (lo != 0 || hi + 1 != width (n->get_arg (0 )))) {
233
280
enode* arg = n->get_arg (0 );
234
281
unsigned w = width (arg);
235
282
if (all_of (enode_parents (arg), [&](enode* p) { unsigned _lo, _hi; return !is_extract (p, _lo, _hi) || _lo != 0 || _hi + 1 != w; }))
236
283
push_merge (mk_extract (arg, 0 , w - 1 ), arg);
237
284
ensure_slice (arg, lo, hi);
238
285
}
286
+ TRACE (" bv" , tout << " done register " << g.bpp (n) << " \n " );
239
287
}
240
288
241
289
//
@@ -250,7 +298,8 @@ namespace euf {
250
298
SASSERT (ub - lb + 1 == width (r));
251
299
if (lb == lo && ub == hi)
252
300
return ;
253
- slice_info& i = info (r);
301
+ slice_info const & i = info (r);
302
+
254
303
if (!i.lo ) {
255
304
if (lo > lb) {
256
305
split (r, lo - lb);
@@ -287,19 +336,28 @@ namespace euf {
287
336
hi += lo1;
288
337
n = n->get_arg (0 );
289
338
}
339
+ if (n->interpreted ()) {
340
+ auto v = get_value (n);
341
+ if (lo > 0 )
342
+ v = div (v, rational::power_of_two (lo));
343
+ if (hi + 1 != width (n))
344
+ v = mod (v, rational::power_of_two (hi + 1 ));
345
+ return mk_value (v, hi - lo + 1 );
346
+ }
290
347
return mk (bv.mk_extract (hi, lo, n->get_expr ()), 1 , &n);
291
348
}
292
349
293
- enode* bv_plugin::mk_concat (enode* lo , enode* hi ) {
294
- enode* args[2 ] = { lo, hi };
295
- return mk (bv.mk_concat (lo ->get_expr (), hi ->get_expr ()), 2 , args);
350
+ enode* bv_plugin::mk_concat (enode* hi , enode* lo ) {
351
+ enode* args[2 ] = { hi, lo };
352
+ return mk (bv.mk_concat (hi ->get_expr (), lo ->get_expr ()), 2 , args);
296
353
}
297
354
298
355
void bv_plugin::merge (enode_vector& xs, enode_vector& ys, justification dep) {
299
356
while (!xs.empty ()) {
300
357
SASSERT (!ys.empty ());
301
358
auto x = xs.back ();
302
359
auto y = ys.back ();
360
+ TRACE (" bv" , tout << " merge " << g.bpp (x) << " " << g.bpp (y) << " \n " );
303
361
if (unfold_sub (x, xs))
304
362
continue ;
305
363
else if (unfold_sub (y, ys))
@@ -342,14 +400,13 @@ namespace euf {
342
400
SASSERT (0 < cut && cut < w);
343
401
enode* hi = mk_extract (n, cut, w - 1 );
344
402
enode* lo = mk_extract (n, 0 , cut - 1 );
345
- auto & i = info (n);
346
- if (!i.value )
347
- i.value = n;
403
+ auto & i = info (n);
404
+ i.value = n;
348
405
i.hi = hi;
349
406
i.lo = lo;
350
407
i.cut = cut;
351
408
push_undo_split (n);
352
- push_merge (mk_concat (lo, hi ), n);
409
+ push_merge (mk_concat (hi, lo ), n);
353
410
}
354
411
355
412
void bv_plugin::sub_slices (enode* n, std::function<bool (enode*, unsigned )>& consumer) {
@@ -442,9 +499,12 @@ namespace euf {
442
499
continue ;
443
500
offsets.push_back (offs);
444
501
if (n->get_root () == b->get_root () && offs == offset) {
502
+ if (n != b)
503
+ consumer (n, b);
445
504
while (j != UINT_MAX) {
446
505
auto [x, y, j2] = just[j];
447
- consumer (x, y);
506
+ if (x != y)
507
+ consumer (x, y);
448
508
j = j2;
449
509
}
450
510
for (auto const & [n, offset, j] : m_jtodo) {
@@ -487,10 +547,10 @@ namespace euf {
487
547
}
488
548
489
549
std::ostream& bv_plugin::display (std::ostream& out) const {
490
- out << " bv\n " ;
550
+ out << " bv\n " ;
491
551
for (auto const & i : m_info)
492
- if (i.lo )
493
- out << g.bpp (i.value ) << " cut " << i.cut << " lo " << g.bpp (i.lo ) << " hi " << g.bpp (i.hi ) << " \n " ;
552
+ if (i.lo )
553
+ out << g.bpp (i.value ) << " cut " << i.cut << " lo " << g.bpp (i.lo ) << " hi " << g.bpp (i.hi ) << " \n " ;
494
554
return out;
495
555
}
496
556
}
0 commit comments