@@ -134,7 +134,7 @@ namespace euf {
134
134
//
135
135
// ------------------------------------
136
136
typedef enum {
137
- INIT1=0 , INIT2, INIT3, INIT4, INIT5, INIT6, INITN,
137
+ INIT1=0 , INIT2, INIT3, INIT4, INIT5, INIT6, INITN, INITAC,
138
138
BIND1, BIND2, BIND3, BIND4, BIND5, BIND6, BINDN,
139
139
YIELD1, YIELD2, YIELD3, YIELD4, YIELD5, YIELD6, YIELDN,
140
140
COMPARE, CHECK, FILTER, CFILTER, PFILTER, CHOOSE, NOOP, CONTINUE,
@@ -150,7 +150,7 @@ namespace euf {
150
150
unsigned m_counter; // how often it was executed
151
151
#endif
152
152
bool is_init () const {
153
- return m_opcode >= INIT1 && m_opcode <= INITN ;
153
+ return m_opcode >= INIT1 && m_opcode <= INITAC ;
154
154
}
155
155
};
156
156
@@ -332,12 +332,14 @@ namespace euf {
332
332
333
333
std::ostream & operator <<(std::ostream & out, const instruction & instr) {
334
334
switch (instr.m_opcode ) {
335
- case INIT1: case INIT2: case INIT3: case INIT4: case INIT5: case INIT6: case INITN:
335
+ case INIT1: case INIT2: case INIT3: case INIT4: case INIT5: case INIT6: case INITN: case INITAC:
336
336
out << " (INIT" ;
337
337
if (instr.m_opcode <= INIT6)
338
338
out << (instr.m_opcode - INIT1 + 1 );
339
- else
339
+ else if (instr. m_opcode == INITN)
340
340
out << " N" ;
341
+ else
342
+ out << " AC" ;
341
343
out << " )" ;
342
344
break ;
343
345
case BIND1: case BIND2: case BIND3: case BIND4: case BIND5: case BIND6: case BINDN:
@@ -519,6 +521,10 @@ namespace euf {
519
521
}
520
522
#endif
521
523
524
+ bool arg_compatible (app* f) const {
525
+ return expected_num_args () == f->get_num_args ();
526
+ }
527
+
522
528
unsigned expected_num_args () const {
523
529
return m_num_args;
524
530
}
@@ -626,24 +632,34 @@ namespace euf {
626
632
// ------------------------------------
627
633
628
634
class code_tree_manager {
629
- euf::mam_solver & ctx;
630
- label_hasher & m_lbl_hasher;
631
- region & m_region;
635
+ euf::mam_solver& ctx;
636
+ label_hasher& m_lbl_hasher;
637
+ region& m_region;
632
638
633
639
template <typename OP>
634
- OP * mk_instr (opcode op, unsigned size) {
635
- void * mem = m_region.allocate (size);
636
- OP * r = new (mem) OP;
640
+ OP* mk_instr (opcode op, unsigned size) {
641
+ void * mem = m_region.allocate (size);
642
+ OP* r = new (mem) OP;
637
643
r->m_opcode = op;
638
- r->m_next = nullptr ;
644
+ r->m_next = nullptr ;
639
645
#ifdef _PROFILE_MAM
640
646
r->m_counter = 0 ;
641
647
#endif
642
648
return r;
643
649
}
644
-
645
- instruction * mk_init (unsigned n) {
650
+
651
+ bool is_ac (func_decl* f) const {
652
+ return false && f->is_associative () && f->is_commutative ();
653
+ }
654
+
655
+ instruction * mk_init (func_decl* f, unsigned n) {
646
656
SASSERT (n >= 1 );
657
+ if (is_ac (f)) {
658
+ auto * r = mk_instr<initn>(INITAC, sizeof (initn));
659
+ r->m_num_args = n;
660
+ return r;
661
+ }
662
+
647
663
opcode op = n <= 6 ? static_cast <opcode>(INIT1 + n - 1 ) : INITN;
648
664
if (op == INITN) {
649
665
// We store the actual number of arguments for INITN.
@@ -668,7 +684,7 @@ namespace euf {
668
684
669
685
code_tree * mk_code_tree (func_decl * lbl, unsigned short num_args, bool filter_candidates) {
670
686
code_tree * r = alloc (code_tree,m_lbl_hasher, lbl, num_args, filter_candidates);
671
- r->m_root = mk_init (num_args);
687
+ r->m_root = mk_init (lbl, num_args);
672
688
return r;
673
689
}
674
690
@@ -1238,7 +1254,7 @@ namespace euf {
1238
1254
m_matched_exprs.reset ();
1239
1255
while (!m_todo.empty ())
1240
1256
linearise_core ();
1241
-
1257
+
1242
1258
if (m_mp->get_num_args () > 1 ) {
1243
1259
m_mp_already_processed.reset ();
1244
1260
m_mp_already_processed.resize (m_mp->get_num_args ());
@@ -1786,7 +1802,7 @@ namespace euf {
1786
1802
- is_tmp_tree: trail for update operations is created if is_tmp_tree = false.
1787
1803
*/
1788
1804
void insert (code_tree * tree, quantifier * qa, app * mp, unsigned first_idx, bool is_tmp_tree) {
1789
- if (tree->expected_num_args () != to_app (mp->get_arg (first_idx))-> get_num_args ( )) {
1805
+ if (! tree->arg_compatible ( to_app (mp->get_arg (first_idx)))) {
1790
1806
// We have to check the number of arguments because of nary + and * operators.
1791
1807
// The E-matching engine that was built when all + and * applications were binary.
1792
1808
// We ignore the pattern if it does not have the expected number of arguments.
@@ -1845,6 +1861,9 @@ namespace euf {
1845
1861
unsigned m_old_max_generation;
1846
1862
union {
1847
1863
enode * m_curr;
1864
+ struct {
1865
+ unsigned m_next_pattern;
1866
+ };
1848
1867
struct {
1849
1868
enode_vector * m_to_recycle;
1850
1869
enode * const * m_it;
@@ -1883,6 +1902,9 @@ namespace euf {
1883
1902
unsigned_vector m_min_top_generation, m_max_top_generation;
1884
1903
1885
1904
pool<enode_vector> m_pool;
1905
+ ptr_buffer<enode> m_acargs;
1906
+ bool_vector m_acbitset;
1907
+ unsigned_vector m_acpatarg;
1886
1908
1887
1909
enode_vector * mk_enode_vector () {
1888
1910
enode_vector * r = m_pool.mk ();
@@ -1987,6 +2009,8 @@ namespace euf {
1987
2009
1988
2010
void display_pc_info (std::ostream & out);
1989
2011
2012
+ bool match_ac (initn const * pc);
2013
+
1990
2014
#define INIT_ARGS_SIZE 16
1991
2015
1992
2016
public:
@@ -2219,7 +2243,7 @@ namespace euf {
2219
2243
2220
2244
void interpreter::display_instr_input_reg (std::ostream & out, const instruction * instr) {
2221
2245
switch (instr->m_opcode ) {
2222
- case INIT1: case INIT2: case INIT3: case INIT4: case INIT5: case INIT6: case INITN:
2246
+ case INIT1: case INIT2: case INIT3: case INIT4: case INIT5: case INIT6: case INITN: case INITAC:
2223
2247
display_reg (out, 0 );
2224
2248
break ;
2225
2249
case BIND1: case BIND2: case BIND3: case BIND4: case BIND5: case BIND6: case BINDN:
@@ -2254,6 +2278,25 @@ namespace euf {
2254
2278
display_instr_input_reg (out, m_pc);
2255
2279
}
2256
2280
2281
+ //
2282
+ // plan:
2283
+ // - bit-set of matched elements in m_acargs (m_acbitset)
2284
+ // - for each pattern index an index into m_acargs that it matches (m_acpatarg)
2285
+ // when backtracking, take previous pattern index and clear bit-set at position
2286
+ // of pattern_index: try binding the next available position not in the bit-index
2287
+ //
2288
+ // If pattern argument is a variable it can bind to multiple m_acargs
2289
+ // Initially: simply punt. Dont consider these as matches
2290
+ // Naive: iterate over all subsets not in current bitset and use a sequence binding.
2291
+ // Established: use Diophantine equations to capture matchability.
2292
+ //
2293
+
2294
+ bool interpreter::match_ac (initn const * pc) {
2295
+ unsigned f_args = pc->m_num_args ;
2296
+ SASSERT (f_args <= m_acargs.size ());
2297
+ return false ;
2298
+ }
2299
+
2257
2300
bool interpreter::execute_core (code_tree * t, enode * n) {
2258
2301
TRACE (trigger_bug, tout << " interpreter::execute_core\n " ; t->display (tout); tout << " \n enode\n " << mk_ismt2_pp (n->get_expr (), m) << " \n " ;);
2259
2302
unsigned since_last_check = 0 ;
@@ -2364,6 +2407,37 @@ namespace euf {
2364
2407
m_pc = m_pc->m_next ;
2365
2408
goto main_loop;
2366
2409
2410
+ case INITAC: {
2411
+ m_app = m_registers[0 ];
2412
+ m_acargs.reset ();
2413
+ m_acargs.push_back (m_app);
2414
+ auto * f = m_app->get_decl ();
2415
+ for (unsigned i = 0 ; i < m_acargs.size (); ++i) {
2416
+ auto * arg = m_acargs[i];
2417
+ if (is_app (arg->get_expr ()) && f == arg->get_decl ()) {
2418
+ m_acargs.append (arg->num_args (), arg->args ());
2419
+ m_acargs[i] = m_acargs.back ();
2420
+ m_acargs.pop_back ();
2421
+ --i;
2422
+ }
2423
+ }
2424
+ if (static_cast <const initn*>(m_pc)->m_num_args > m_acargs.size ())
2425
+ goto backtrack;
2426
+ m_acbitset.reset ();
2427
+ m_acbitset.reserve (m_acargs.size (), false );
2428
+ m_acpatarg.reset ();
2429
+ m_acpatarg.reserve (m_acargs.size (), 0 );
2430
+ m_backtrack_stack[m_top].m_instr = m_pc;
2431
+ m_backtrack_stack[m_top].m_old_max_generation = m_curr_max_generation;
2432
+ m_backtrack_stack[m_top].m_next_pattern = 0 ;
2433
+ ++m_top;
2434
+ // perform the match relative index
2435
+ if (!match_ac (static_cast <initn const *>(m_pc)))
2436
+ goto backtrack;
2437
+ m_pc = m_pc->m_next ;
2438
+ goto main_loop;
2439
+ }
2440
+
2367
2441
case COMPARE:
2368
2442
m_n1 = m_registers[static_cast <const compare *>(m_pc)->m_reg1 ];
2369
2443
m_n2 = m_registers[static_cast <const compare *>(m_pc)->m_reg2 ];
@@ -2756,6 +2830,11 @@ namespace euf {
2756
2830
m_pc = m_b->m_next ;
2757
2831
goto main_loop;
2758
2832
2833
+ case INITAC:
2834
+ // this is a backtracking point.
2835
+ NOT_IMPLEMENTED_YET ();
2836
+ goto main_loop;
2837
+
2759
2838
case CONTINUE:
2760
2839
++bp.m_it ;
2761
2840
for (; bp.m_it != bp.m_end ; ++bp.m_it ) {
@@ -2867,7 +2946,7 @@ namespace euf {
2867
2946
m_trees.reserve (lbl_id+1 , nullptr );
2868
2947
if (m_trees[lbl_id] == nullptr ) {
2869
2948
m_trees[lbl_id] = m_compiler.mk_tree (qa, mp, first_idx, false );
2870
- SASSERT (m_trees[lbl_id]->expected_num_args () == p-> get_num_args ( ));
2949
+ SASSERT (m_trees[lbl_id]->arg_compatible (p ));
2871
2950
DEBUG_CODE (m_trees[lbl_id]->set_egraph (m_egraph););
2872
2951
ctx.get_trail ().push (mk_tree_trail (m_trees, lbl_id));
2873
2952
}
@@ -2877,7 +2956,7 @@ namespace euf {
2877
2956
// The E-matching engine that was built when all + and * applications were binary.
2878
2957
// We ignore the pattern if it does not have the expected number of arguments.
2879
2958
// This is not the ideal solution, but it avoids possible crashes.
2880
- if (tree->expected_num_args () == p-> get_num_args ( ))
2959
+ if (tree->arg_compatible (p ))
2881
2960
m_compiler.insert (tree, qa, mp, first_idx, false );
2882
2961
}
2883
2962
DEBUG_CODE (if (first_idx == 0 ) {
0 commit comments