1 /*++
2 Copyright (c) 2006 Microsoft Corporation
3 
4 Module Name:
5 
6     euf_mam.cpp
7 
8 Abstract:
9 
10     Matching Abstract Machine
11 
12 Author:
13 
14     Leonardo de Moura (leonardo) 2007-02-13.
15     Nikolaj Bjorner (nbjorner) 2021-01-22.
16 
17 Revision History:
18 
19     Ported to self-contained egraph
20 
21 --*/
22 #include <algorithm>
23 
24 #include "util/pool.h"
25 #include "util/trail.h"
26 #include "util/stopwatch.h"
27 #include "util/approx_set.h"
28 #include "ast/ast_pp.h"
29 #include "ast/ast_ll_pp.h"
30 #include "ast/ast_smt2_pp.h"
31 #include "ast/euf/euf_enode.h"
32 #include "ast/euf/euf_egraph.h"
33 #include "sat/smt/q_mam.h"
34 #include "sat/smt/q_ematch.h"
35 #include "sat/smt/euf_solver.h"
36 
37 
38 
39 // #define _PROFILE_MAM
40 
41 // -----------------------------------------
42 // Flags for _PROFILE_MAM
43 //
44 // send profiling information to stdout
45 #define _PROFILE_MAM_TO_STDOUT
46 // threshold in secs for being considered expensive
47 #define _PROFILE_MAM_THRESHOLD 30.0
48 // dump expensive (> _PROFILE_MAM_THRESHOLD) code trees whenever execute_core is executed.
49 #define _PROFILE_MAM_EXPENSIVE
50 //
51 #define _PROFILE_MAM_EXPENSIVE_FREQ 100000
52 //
53 // -----------------------------------------
54 
55 // #define _PROFILE_PATH_TREE
56 // -----------------------------------------
57 // Flags for _PROFILE_PATH_TREE
58 //
59 #define _PROFILE_PATH_TREE_THRESHOLD 20000
60 //
61 // -----------------------------------------
62 
63 #define IS_CGR_SUPPORT true
64 
65 namespace q {
66     // ------------------------------------
67     //
68     // Trail
69     //
70     // ------------------------------------
71 
72     class mam_impl;
73 
74 
75     template<typename T>
76     class mam_value_trail : public value_trail<T> {
77     public:
mam_value_trail(T & value)78         mam_value_trail(T & value):value_trail<T>(value) {}
79     };
80 
get_max_generation(unsigned n,enode * const * nodes)81     unsigned get_max_generation(unsigned n, enode* const* nodes) {
82         unsigned g = 0;
83         for (unsigned i = 0; i < n; ++i)
84             g = std::max(g, nodes[i]->generation());
85         return g;
86     }
87 
88 
89     // ------------------------------------
90     //
91     // Auxiliary
92     //
93     // ------------------------------------
94     class label_hasher {
95         svector<signed char>             m_lbl2hash;        // cache: lbl_id -> hash
96 
mk_lbl_hash(unsigned lbl_id)97         void mk_lbl_hash(unsigned lbl_id) {
98             unsigned a = 17;
99             unsigned b = 3;
100             unsigned c = lbl_id;
101             mix(a, b, c);
102             m_lbl2hash[lbl_id] = c & (APPROX_SET_CAPACITY - 1);
103         }
104 
105     public:
operator ()(func_decl * lbl)106         unsigned char operator()(func_decl * lbl) {
107             unsigned lbl_id = lbl->get_decl_id();
108             if (lbl_id >= m_lbl2hash.size())
109                 m_lbl2hash.resize(lbl_id + 1, -1);
110             if (m_lbl2hash[lbl_id] == -1) {
111                 mk_lbl_hash(lbl_id);
112             }
113             SASSERT(m_lbl2hash[lbl_id] >= 0);
114             return m_lbl2hash[lbl_id];
115         }
116 
display(std::ostream & out) const117         void display(std::ostream & out) const {
118             out << "lbl-hasher:\n";
119             bool first = true;
120             for (unsigned i = 0; i < m_lbl2hash.size(); i++) {
121                 if (m_lbl2hash[i] != -1) {
122                     if (first)
123                         first = false;
124                     else
125                         out << ", ";
126                     out << i << " -> " << static_cast<int>(m_lbl2hash[i]);
127                 }
128             }
129             out << "\n";
130         }
131     };
132 
133     // ------------------------------------
134     //
135     // Instructions
136     //
137     // ------------------------------------
138     typedef enum {
139         INIT1=0, INIT2,  INIT3,  INIT4,  INIT5,  INIT6,  INITN,
140         BIND1,   BIND2,  BIND3,  BIND4,  BIND5,  BIND6,  BINDN,
141         YIELD1,  YIELD2, YIELD3, YIELD4, YIELD5, YIELD6, YIELDN,
142         COMPARE, CHECK, FILTER, CFILTER, PFILTER, CHOOSE, NOOP, CONTINUE,
143         GET_ENODE,
144         GET_CGR1, GET_CGR2, GET_CGR3, GET_CGR4, GET_CGR5, GET_CGR6, GET_CGRN,
145         IS_CGR
146     } opcode;
147 
148     struct instruction {
149         opcode         m_opcode;
150         instruction *  m_next;
151 #ifdef _PROFILE_MAM
152         unsigned       m_counter; // how often it was executed
153 #endif
is_initq::instruction154         bool is_init() const {
155             return m_opcode >= INIT1 && m_opcode <= INITN;
156         }
157     };
158 
159     struct initn : public instruction {
160         // We need that because starting at Z3 3.0, some associative
161         // operators (e.g., + and *) are represented using n-ary
162         // applications.
163         // We do not need the extra field for INIT1, ..., INIT6.
164         unsigned       m_num_args;
165     };
166 
167     struct compare : public instruction {
168         unsigned       m_reg1;
169         unsigned       m_reg2;
170     };
171 
172     struct check : public instruction {
173         unsigned       m_reg;
174         enode *        m_enode;
175     };
176 
177     struct filter : public instruction {
178         unsigned       m_reg;
179         approx_set     m_lbl_set;
180     };
181 
182     struct pcheck : public instruction {
183         enode *        m_enode;
184         approx_set     m_lbl_set;
185     };
186 
187     /**
188        \brief Copy m_enode to register m_oreg
189     */
190     struct get_enode_instr : public instruction {
191         unsigned  m_oreg;
192         enode *   m_enode;
193     };
194 
195     struct choose: public instruction {
196         choose *       m_alt;
197     };
198 
199     /**
200        \brief A depth-2 joint. It is used in CONTINUE instruction.
201        There are 3 forms of joints
202        1) Variables:   (f ... X ...)
203        2) Ground terms: (f ... t ...)
204        3) depth 2 joint: (f ... (g ... X ...) ...)
205           Joint2 stores the declaration g, and the position of variable X, and its idx.
206 
207        \remark Z3 has no support for depth 3 joints (f ... (g ... (h ... X ...) ...) ....)
208     */
209     struct joint2 {
210         func_decl * m_decl;
211         unsigned    m_arg_pos;
212         unsigned    m_reg;    // register that contains the variable
joint2q::joint2213         joint2(func_decl * f, unsigned pos, unsigned r):m_decl(f), m_arg_pos(pos), m_reg(r) {}
214     };
215 
216 #define NULL_TAG        0
217 #define GROUND_TERM_TAG 1
218 #define VAR_TAG         2
219 #define NESTED_VAR_TAG  3
220 
221     struct cont: public instruction {
222         func_decl *     m_label;
223         unsigned short  m_num_args;
224         unsigned        m_oreg;
225         approx_set      m_lbl_set; // singleton set containing m_label
226         /*
227           The following field is an array of tagged pointers.
228           Each position contains:
229           1- null (no joint), NULL_TAG
230           2- a boxed integer (i.e., register that contains the variable bind) VAR_TAG
231           3- an enode pointer (ground term)  GROUND_TERM_TAG
232           4- or, a joint2 pointer.    NESTED_VAR_TAG
233 
234           The size of the array is m_num_args.
235         */
236         enode *         m_joints[0];
237     };
238 
239     struct bind : public instruction {
240         func_decl *    m_label;
241         unsigned short m_num_args;
242         unsigned       m_ireg;
243         unsigned       m_oreg;
244     };
245 
246     struct get_cgr : public instruction {
247         func_decl *    m_label;
248         approx_set     m_lbl_set;
249         unsigned short m_num_args;
250         unsigned       m_oreg;
251         unsigned       m_iregs[0];
252     };
253 
254     struct yield : public instruction {
255         quantifier *      m_qa;
256         app *             m_pat;
257         unsigned short    m_num_bindings;
258         unsigned          m_bindings[0];
259     };
260 
261     struct is_cgr : public instruction {
262         unsigned       m_ireg;
263         func_decl *    m_label;
264         unsigned short m_num_args;
265         unsigned       m_iregs[0];
266     };
267 
display_num_args(std::ostream & out,unsigned num_args)268     void display_num_args(std::ostream & out, unsigned num_args) {
269         if (num_args <= 6) {
270             out << num_args;
271         }
272         else {
273             out << "N";
274         }
275     }
276 
display_bind(std::ostream & out,const bind & b)277     void display_bind(std::ostream & out, const bind & b) {
278         out << "(BIND";
279         display_num_args(out, b.m_num_args);
280         out << " " << b.m_label->get_name() << " " << b.m_ireg << " " << b.m_oreg << ")";
281     }
282 
display_get_cgr(std::ostream & out,const get_cgr & c)283     void display_get_cgr(std::ostream & out, const get_cgr & c) {
284         out << "(GET_CGR";
285         display_num_args(out, c.m_num_args);
286         out << " " << c.m_label->get_name() << " " << c.m_oreg;
287         for (unsigned i = 0; i < c.m_num_args; i++)
288             out << " " << c.m_iregs[i];
289         out << ")";
290     }
291 
display_is_cgr(std::ostream & out,const is_cgr & c)292     void display_is_cgr(std::ostream & out, const is_cgr & c) {
293         out << "(IS_CGR " << c.m_label->get_name() << " " << c.m_ireg;
294         for (unsigned i = 0; i < c.m_num_args; i++)
295             out << " " << c.m_iregs[i];
296         out << ")";
297     }
298 
display_yield(std::ostream & out,const yield & y)299     void display_yield(std::ostream & out, const yield & y) {
300         out << "(YIELD";
301         display_num_args(out, y.m_num_bindings);
302         out << " #" << y.m_qa->get_id();
303         for (unsigned i = 0; i < y.m_num_bindings; i++) {
304             out << " " << y.m_bindings[i];
305         }
306         out << ")";
307     }
308 
display_joints(std::ostream & out,unsigned num_joints,enode * const * joints)309     void display_joints(std::ostream & out, unsigned num_joints, enode * const * joints) {
310         for (unsigned i = 0; i < num_joints; i++) {
311             if (i > 0)
312                 out << " ";
313             enode * bare = joints[i];
314             switch (GET_TAG(bare)) {
315             case NULL_TAG: out << "nil"; break;
316             case GROUND_TERM_TAG: out << "#" << UNTAG(enode*, bare)->get_expr_id(); break;
317             case VAR_TAG: out << UNBOXINT(bare); break;
318             case NESTED_VAR_TAG: out << "(" << UNTAG(joint2*, bare)->m_decl->get_name() << " " << UNTAG(joint2*, bare)->m_arg_pos << " " << UNTAG(joint2*, bare)->m_reg << ")"; break;
319             }
320         }
321     }
322 
display_continue(std::ostream & out,const cont & c)323     void display_continue(std::ostream & out, const cont & c) {
324         out << "(CONTINUE " << c.m_label->get_name() << " " << c.m_num_args << " " << c.m_oreg << " "
325             << c.m_lbl_set << " (";
326         display_joints(out, c.m_num_args, c.m_joints);
327         out << "))";
328     }
329 
display_filter(std::ostream & out,char const * op,filter const & instr)330     void display_filter(std::ostream & out, char const * op, filter const & instr) {
331         out << "(" << op << " " << instr.m_reg
332             << " " << instr.m_lbl_set << ")";
333     }
334 
operator <<(std::ostream & out,const instruction & instr)335     std::ostream & operator<<(std::ostream & out, const instruction & instr) {
336         switch (instr.m_opcode) {
337         case INIT1: case INIT2: case INIT3: case INIT4: case INIT5: case INIT6: case INITN:
338             out << "(INIT";
339             if (instr.m_opcode <= INIT6)
340                 out << (instr.m_opcode - INIT1 + 1);
341             else
342                 out << "N";
343             out << ")";
344             break;
345         case BIND1: case BIND2: case BIND3: case BIND4: case BIND5: case BIND6: case BINDN:
346             display_bind(out, static_cast<const bind &>(instr));
347             break;
348         case GET_CGR1: case GET_CGR2: case GET_CGR3: case GET_CGR4: case GET_CGR5: case GET_CGR6: case GET_CGRN:
349             display_get_cgr(out, static_cast<const get_cgr &>(instr));
350             break;
351         case IS_CGR:
352             display_is_cgr(out, static_cast<const is_cgr &>(instr));
353             break;
354         case YIELD1: case YIELD2: case YIELD3: case YIELD4: case YIELD5: case YIELD6: case YIELDN:
355             display_yield(out, static_cast<const yield &>(instr));
356             break;
357         case CONTINUE:
358             display_continue(out, static_cast<const cont &>(instr));
359             break;
360         case COMPARE:
361             out << "(COMPARE " << static_cast<const compare &>(instr).m_reg1 << " "
362                 << static_cast<const compare &>(instr).m_reg2 << ")";
363             break;
364         case CHECK:
365             out << "(CHECK " << static_cast<const check &>(instr).m_reg
366                 << " #" << static_cast<const check &>(instr).m_enode->get_expr_id() << ")";
367             break;
368         case FILTER:
369             display_filter(out, "FILTER", static_cast<const filter &>(instr));
370             break;
371         case CFILTER:
372             display_filter(out, "CFILTER", static_cast<const filter &>(instr));
373             break;
374         case PFILTER:
375             display_filter(out, "PFILTER", static_cast<const filter &>(instr));
376             break;
377         case GET_ENODE:
378             out << "(GET_ENODE " << static_cast<const get_enode_instr &>(instr).m_oreg << " #" <<
379                 static_cast<const get_enode_instr &>(instr).m_enode->get_expr_id() << ")";
380             break;
381         case CHOOSE:
382             out << "(CHOOSE)";
383             break;
384         case NOOP:
385             out << "(NOOP)";
386             break;
387         }
388 #ifdef _PROFILE_MAM
389         out << "[" << instr.m_counter << "]";
390 #endif
391         return out;
392     }
393 
394     // ------------------------------------
395     //
396     // Code Tree
397     //
398     // ------------------------------------
399 
mk_enode(egraph & egraph,quantifier * qa,app * n)400     inline enode * mk_enode(egraph & egraph, quantifier * qa, app * n) {
401         enode * e = egraph.find(n);
402         SASSERT(e);
403         return e;
404     }
405 
406     class code_tree {
407         label_hasher &             m_lbl_hasher;
408         func_decl *                m_root_lbl;
409         unsigned                   m_num_args; //!< we need this information to avoid the nary *,+ crash bug
410         bool                       m_filter_candidates;
411         unsigned                   m_num_regs;
412         unsigned                   m_num_choices = 0;
413         instruction *              m_root = nullptr;
414         enode_vector               m_candidates;
415         unsigned                   m_qhead = 0;
416 #ifdef Z3DEBUG
417         egraph *                   m_egraph = nullptr;
418         svector<std::pair<quantifier*, app*>>            m_patterns;
419 #endif
420 #ifdef _PROFILE_MAM
421         stopwatch                  m_watch;
422         unsigned                   m_counter = 0;
423 #endif
424         friend class compiler;
425         friend class code_tree_manager;
426 
display_seq(std::ostream & out,instruction * head,unsigned indent) const427         void display_seq(std::ostream & out, instruction * head, unsigned indent) const {
428             for (unsigned i = 0; i < indent; i++) {
429                 out << "    ";
430             }
431             instruction * curr = head;
432             out << *curr;
433             curr = curr->m_next;
434             while (curr != nullptr && curr->m_opcode != CHOOSE && curr->m_opcode != NOOP) {
435                 out << "\n";
436                 out << *curr;
437                 curr = curr->m_next;
438             }
439             out << "\n";
440             if (curr != nullptr) {
441                 display_children(out, static_cast<choose*>(curr), indent + 1);
442             }
443         }
444 
display_children(std::ostream & out,choose * first_child,unsigned indent) const445         void display_children(std::ostream & out, choose * first_child, unsigned indent) const {
446             choose * curr = first_child;
447             while (curr != nullptr) {
448                 display_seq(out, curr, indent);
449                 curr = curr->m_alt;
450             }
451         }
452 
453 #ifdef Z3DEBUG
get_enode(egraph & ctx,app * n) const454         inline enode * get_enode(egraph & ctx, app * n) const {
455             enode * e = ctx.find(n);
456             SASSERT(e);
457             return e;
458         }
459 
display_label_hashes_core(std::ostream & out,app * p) const460         void display_label_hashes_core(std::ostream & out, app * p) const {
461             if (is_ground(p)) {
462                 enode * e = get_enode(*m_egraph, p);
463                 SASSERT(e->has_lbl_hash());
464                 out << "#" << e->get_expr_id() << ":" << e->get_lbl_hash() << " ";
465             }
466             else {
467                 out << p->get_decl()->get_name() << ":" << m_lbl_hasher(p->get_decl()) << " ";
468                 for (expr* arg : *p)
469                     if (is_app(arg))
470                         display_label_hashes(out, to_app(arg));
471             }
472         }
473 
display_label_hashes(std::ostream & out,app * p) const474         void display_label_hashes(std::ostream & out, app * p) const {
475             ast_manager & m = m_egraph->get_manager();
476             if (m.is_pattern(p)) {
477                 for (expr* arg : *p)
478                     if (is_app(arg)) {
479                         display_label_hashes_core(out, to_app(arg));
480                         out << "\n";
481                     }
482             }
483             else {
484                 display_label_hashes_core(out, p);
485                 out << "\n";
486             }
487         }
488 #endif
489 
490     public:
code_tree(label_hasher & h,func_decl * lbl,unsigned short num_args,bool filter_candidates)491         code_tree(label_hasher & h, func_decl * lbl, unsigned short num_args, bool filter_candidates):
492             m_lbl_hasher(h),
493             m_root_lbl(lbl),
494             m_num_args(num_args),
495             m_filter_candidates(filter_candidates),
496             m_num_regs(num_args + 1) {
497             (void)m_lbl_hasher;
498         }
499 
500 #ifdef _PROFILE_MAM
~code_tree()501         ~code_tree() {
502 #ifdef _PROFILE_MAM_TO_STDOUT
503             std::cout << "killing code tree for: " << m_root_lbl->get_name() << " " << static_cast<unsigned long long>(m_watch.get_seconds() * 1000) << "\n"; display(std::cout);
504 #endif
505         }
506 
get_watch()507         stopwatch & get_watch() {
508             return m_watch;
509         }
510 
inc_counter()511         void inc_counter() {
512             m_counter++;
513         }
514 
get_counter() const515         unsigned get_counter() const {
516             return m_counter;
517         }
518 #endif
519 
expected_num_args() const520         unsigned expected_num_args() const {
521             return m_num_args;
522         }
523 
get_num_regs() const524         unsigned get_num_regs() const {
525             return m_num_regs;
526         }
527 
get_num_choices() const528         unsigned get_num_choices() const {
529             return m_num_choices;
530         }
531 
get_root_lbl() const532         func_decl * get_root_lbl() const {
533             return m_root_lbl;
534         }
535 
filter_candidates() const536         bool filter_candidates() const {
537             return m_filter_candidates;
538         }
539 
get_root() const540         const instruction * get_root() const {
541             return m_root;
542         }
543 
add_candidate(euf::solver & ctx,enode * n)544         void add_candidate(euf::solver& ctx, enode * n) {
545             m_candidates.push_back(n);
546             ctx.push(push_back_trail<enode*, false>(m_candidates));
547         }
548 
unmark(unsigned head)549         void unmark(unsigned head) {
550             for (unsigned i = m_candidates.size(); i-- > head; ) {
551                 enode* app = m_candidates[i];
552                 if (app->is_marked1())
553                     app->unmark1();
554             }
555         }
556 
557         struct scoped_unmark {
558             unsigned   m_qhead;
559             code_tree* t;
scoped_unmarkq::code_tree::scoped_unmark560             scoped_unmark(code_tree* t) : m_qhead(t->m_qhead), t(t) {}
~scoped_unmarkq::code_tree::scoped_unmark561             ~scoped_unmark() { t->unmark(m_qhead); }
562         };
563 
564 
has_candidates() const565         bool has_candidates() const {
566             return m_qhead < m_candidates.size();
567         }
568 
save_qhead(euf::solver & ctx)569         void save_qhead(euf::solver& ctx) {
570             ctx.push(value_trail<unsigned>(m_qhead));
571         }
572 
next_candidate()573         enode* next_candidate() {
574             if (m_qhead < m_candidates.size())
575                 return m_candidates[m_qhead++];
576             else
577                 return nullptr;
578         }
579 
get_candidates() const580         enode_vector const & get_candidates() const {
581             return m_candidates;
582         }
583 
584 #ifdef Z3DEBUG
set_egraph(egraph * ctx)585         void set_egraph(egraph * ctx) {
586             SASSERT(m_egraph == nullptr);
587             m_egraph = ctx;
588         }
589 
get_patterns()590         svector<std::pair<quantifier*, app*>> & get_patterns() {
591             return m_patterns;
592         }
593 #endif
594 
display(std::ostream & out) const595         void display(std::ostream & out) const {
596 #ifdef Z3DEBUG
597             if (m_egraph) {
598                 ast_manager & m = m_egraph->get_manager();
599                 out << "patterns:\n";
600                 for (auto [q, a] : m_patterns)
601                     out << mk_pp(q, m) << " " << mk_pp(a, m) << "\n";
602             }
603 #endif
604             out << "function: " << m_root_lbl->get_name();
605 #ifdef _PROFILE_MAM
606             out << " " << m_watch.get_seconds() << " secs, [" << m_counter << "]";
607 #endif
608             out << "\n";
609             out << "num. regs:    " << m_num_regs << "\n"
610                 << "num. choices: " << m_num_choices << "\n";
611             display_seq(out, m_root, 0);
612         }
613     };
614 
operator <<(std::ostream & out,code_tree const & tree)615     std::ostream & operator<<(std::ostream & out, code_tree const & tree) {
616         tree.display(out);
617         return out;
618     }
619 
620     // ------------------------------------
621     //
622     // Code Tree Manager
623     //
624     // ------------------------------------
625 
626     class code_tree_manager {
627         euf::solver &     ctx;
628         label_hasher &    m_lbl_hasher;
629         region &          m_region;
630 
631         template<typename OP>
mk_instr(opcode op,unsigned size)632         OP * mk_instr(opcode op, unsigned size) {
633             void * mem = m_region.allocate(size);
634             OP * r = new (mem) OP;
635             r->m_opcode = op;
636             r->m_next   = nullptr;
637 #ifdef _PROFILE_MAM
638             r->m_counter = 0;
639 #endif
640             return r;
641         }
642 
mk_init(unsigned n)643         instruction * mk_init(unsigned n) {
644             SASSERT(n >= 1);
645             opcode op = n <= 6 ? static_cast<opcode>(INIT1 + n - 1) : INITN;
646             if (op == INITN) {
647                 // We store the actual number of arguments for INITN.
648                 // Starting at Z3 3.0, some associative operators
649                 // (e.g., + and *) are represented using n-ary
650                 // applications.
651                 initn * r = mk_instr<initn>(op, sizeof(initn));
652                 r->m_num_args = n;
653                 return r;
654             }
655             else {
656                 return mk_instr<instruction>(op, sizeof(instruction));
657             }
658         }
659 
660     public:
code_tree_manager(label_hasher & h,euf::solver & ctx)661         code_tree_manager(label_hasher & h, euf::solver& ctx):
662             ctx(ctx),
663             m_lbl_hasher(h),
664             m_region(ctx.get_region()) {
665         }
666 
mk_code_tree(func_decl * lbl,unsigned short num_args,bool filter_candidates)667         code_tree * mk_code_tree(func_decl * lbl, unsigned short num_args, bool filter_candidates) {
668             code_tree * r = alloc(code_tree,m_lbl_hasher, lbl, num_args, filter_candidates);
669             r->m_root     = mk_init(num_args);
670             return r;
671         }
672 
mk_joint2(func_decl * f,unsigned pos,unsigned reg)673         joint2 * mk_joint2(func_decl * f, unsigned pos, unsigned reg) {
674             return new (m_region) joint2(f, pos, reg);
675         }
676 
mk_compare(unsigned reg1,unsigned reg2)677         compare * mk_compare(unsigned reg1, unsigned reg2) {
678             compare * r = mk_instr<compare>(COMPARE, sizeof(compare));
679             r->m_reg1 = reg1;
680             r->m_reg2 = reg2;
681             return r;
682         }
683 
mk_check(unsigned reg,enode * n)684         check * mk_check(unsigned reg, enode * n) {
685             check * r = mk_instr<check>(CHECK, sizeof(check));
686             r->m_reg   = reg;
687             r->m_enode = n;
688             return r;
689         }
690 
mk_filter_core(opcode op,unsigned reg,approx_set s)691         filter * mk_filter_core(opcode op, unsigned reg, approx_set s) {
692             filter * r = mk_instr<filter>(op, sizeof(filter));
693             r->m_reg      = reg;
694             r->m_lbl_set  = s;
695             return r;
696         }
697 
mk_filter(unsigned reg,approx_set s)698         filter * mk_filter(unsigned reg, approx_set s) {
699             return mk_filter_core(FILTER, reg, s);
700         }
701 
mk_pfilter(unsigned reg,approx_set s)702         filter * mk_pfilter(unsigned reg, approx_set s) {
703             return mk_filter_core(PFILTER, reg, s);
704         }
705 
mk_cfilter(unsigned reg,approx_set s)706         filter * mk_cfilter(unsigned reg, approx_set s) {
707             return mk_filter_core(CFILTER, reg, s);
708         }
709 
mk_get_enode(unsigned reg,enode * n)710         get_enode_instr * mk_get_enode(unsigned reg, enode * n) {
711             get_enode_instr * s = mk_instr<get_enode_instr>(GET_ENODE, sizeof(get_enode_instr));
712             s->m_oreg  = reg;
713             s->m_enode = n;
714             return s;
715         }
716 
mk_choose(choose * alt)717         choose * mk_choose(choose * alt) {
718             choose * r  = mk_instr<choose>(CHOOSE, sizeof(choose));
719             r->m_alt = alt;
720             return r;
721         }
722 
mk_noop()723         choose * mk_noop() {
724             choose * r  = mk_instr<choose>(NOOP, sizeof(choose));
725             r->m_alt = nullptr;
726             return r;
727         }
728 
mk_bind(func_decl * lbl,unsigned short num_args,unsigned ireg,unsigned oreg)729         bind * mk_bind(func_decl * lbl, unsigned short num_args, unsigned ireg, unsigned oreg) {
730             SASSERT(num_args >= 1);
731             opcode op = num_args <= 6 ? static_cast<opcode>(BIND1 + num_args - 1) : BINDN;
732             bind * r = mk_instr<bind>(op, sizeof(bind));
733             r->m_label    = lbl;
734             r->m_num_args = num_args;
735             r->m_ireg     = ireg;
736             r->m_oreg     = oreg;
737             return r;
738         }
739 
mk_get_cgr(func_decl * lbl,unsigned oreg,unsigned num_args,unsigned const * iregs)740         get_cgr * mk_get_cgr(func_decl * lbl, unsigned oreg, unsigned num_args, unsigned const * iregs) {
741             SASSERT(num_args >= 1);
742             opcode op = num_args <= 6 ? static_cast<opcode>(GET_CGR1 + num_args - 1) : GET_CGRN;
743             get_cgr * r   = mk_instr<get_cgr>(op, sizeof(get_cgr) + num_args * sizeof(unsigned));
744             r->m_label    = lbl;
745             r->m_lbl_set.insert(m_lbl_hasher(lbl));
746             r->m_oreg     = oreg;
747             r->m_num_args = num_args;
748             memcpy(r->m_iregs, iregs, sizeof(unsigned) * num_args);
749             return r;
750         }
751 
mk_is_cgr(func_decl * lbl,unsigned ireg,unsigned num_args,unsigned const * iregs)752         is_cgr * mk_is_cgr(func_decl * lbl, unsigned ireg, unsigned num_args, unsigned const * iregs) {
753             SASSERT(num_args >= 1);
754             is_cgr * r   = mk_instr<is_cgr>(IS_CGR, sizeof(is_cgr) + num_args * sizeof(unsigned));
755             r->m_label    = lbl;
756             r->m_ireg     = ireg;
757             r->m_num_args = num_args;
758             memcpy(r->m_iregs, iregs, sizeof(unsigned) * num_args);
759             return r;
760         }
761 
mk_yield(quantifier * qa,app * pat,unsigned num_bindings,unsigned * bindings)762         yield * mk_yield(quantifier * qa, app * pat, unsigned num_bindings, unsigned * bindings) {
763             SASSERT(num_bindings >= 1);
764             opcode op         = num_bindings <= 6 ? static_cast<opcode>(YIELD1 + num_bindings - 1) : YIELDN;
765             yield * y         = mk_instr<yield>(op, sizeof(yield) + num_bindings * sizeof(unsigned));
766             y->m_qa           = qa;
767             y->m_pat          = pat;
768             y->m_num_bindings = num_bindings;
769             memcpy(y->m_bindings, bindings, sizeof(unsigned) * num_bindings);
770             return y;
771         }
772 
mk_cont(func_decl * lbl,unsigned short num_args,unsigned oreg,approx_set const & s,enode * const * joints)773         cont * mk_cont(func_decl * lbl, unsigned short num_args, unsigned oreg,
774                        approx_set const & s, enode * const * joints) {
775             SASSERT(num_args >= 1);
776             cont * r        = mk_instr<cont>(CONTINUE, sizeof(cont) + num_args * sizeof(enode*));
777             r->m_label      = lbl;
778             r->m_num_args   = num_args;
779             r->m_oreg       = oreg;
780             r->m_lbl_set    = s;
781             memcpy(r->m_joints, joints, num_args * sizeof(enode *));
782             return r;
783         }
784 
set_next(instruction * instr,instruction * new_next)785         void set_next(instruction * instr, instruction * new_next) {
786             ctx.push(mam_value_trail<instruction*>(instr->m_next));
787             instr->m_next = new_next;
788         }
789 
save_num_regs(code_tree * tree)790         void save_num_regs(code_tree * tree) {
791             ctx.push(mam_value_trail<unsigned>(tree->m_num_regs));
792         }
793 
save_num_choices(code_tree * tree)794         void save_num_choices(code_tree * tree) {
795             ctx.push(mam_value_trail<unsigned>(tree->m_num_choices));
796         }
797 
insert_new_lbl_hash(filter * instr,unsigned h)798         void insert_new_lbl_hash(filter * instr, unsigned h) {
799             ctx.push(mam_value_trail<approx_set>(instr->m_lbl_set));
800             instr->m_lbl_set.insert(h);
801         }
802     };
803 
804     // ------------------------------------
805     //
806     // Compiler: Pattern ---> Code Tree
807     //
808     // ------------------------------------
809 
810     class compiler {
811         egraph &                m_egraph;
812         ast_manager &           m;
813         code_tree_manager &     m_ct_manager;
814         label_hasher &          m_lbl_hasher;
815         bool                    m_use_filters;
816         ptr_vector<expr>        m_registers;
817         unsigned_vector         m_todo; // list of registers that have patterns to be processed.
818         unsigned_vector         m_aux;
819         int_vector              m_vars; // -1 the variable is unbound, >= 0 is the register that contains the variable
820         quantifier *            m_qa;
821         app *                   m_mp;
822         code_tree *             m_tree;
823         unsigned                m_num_choices;
824         bool                    m_is_tmp_tree;
825         bool_vector             m_mp_already_processed;
826         obj_map<expr, unsigned> m_matched_exprs;
827 
828         struct pcheck_checked {
829             func_decl * m_label;
830             enode *     m_enode;
831         };
832 
833         typedef enum { NOT_CHECKED,
834                        CHECK_SET,
835                        CHECK_SINGLETON } check_mark;
836 
837         svector<check_mark>     m_mark;
838         unsigned_vector         m_to_reset;
839         ptr_vector<instruction> m_compatible;
840         ptr_vector<instruction> m_incompatible;
841         ptr_vector<instruction> m_seq;
842 
set_register(unsigned reg,expr * p)843         void set_register(unsigned reg, expr * p) {
844             m_registers.setx(reg, p, nullptr);
845         }
846 
get_check_mark(unsigned reg) const847         check_mark get_check_mark(unsigned reg) const {
848             return m_mark.get(reg, NOT_CHECKED);
849         }
850 
set_check_mark(unsigned reg,check_mark cm)851         void set_check_mark(unsigned reg, check_mark cm) {
852             m_mark.setx(reg, cm, NOT_CHECKED);
853         }
854 
init(code_tree * t,quantifier * qa,app * mp,unsigned first_idx)855         void init(code_tree * t, quantifier * qa, app * mp, unsigned first_idx) {
856             SASSERT(m.is_pattern(mp));
857 #ifdef Z3DEBUG
858             for (auto cm : m_mark) {
859                 SASSERT(cm == NOT_CHECKED);
860             }
861 #endif
862             m_tree        = t;
863             m_qa          = qa;
864             m_mp          = mp;
865             m_num_choices = 0;
866             m_todo.reset();
867             m_registers.fill(0);
868 
869             app * p = to_app(mp->get_arg(first_idx));
870             SASSERT(t->get_root_lbl() == p->get_decl());
871             unsigned num_args = p->get_num_args();
872             for (unsigned i = 0; i < num_args; i++) {
873                 set_register(i+1, p->get_arg(i));
874                 m_todo.push_back(i+1);
875             }
876             unsigned num_decls = m_qa->get_num_decls();
877             if (num_decls > m_vars.size()) {
878                 m_vars.resize(num_decls, -1);
879             }
880             for (unsigned j = 0; j < num_decls; j++) {
881                 m_vars[j] = -1;
882             }
883         }
884 
885         /**
886            \brief Return true if all arguments of n are bound variables.
887            That is, during execution time, the variables will be already bound
888          */
all_args_are_bound_vars(app * n)889         bool all_args_are_bound_vars(app * n) {
890             for (expr* arg : *n) {
891                 if (!is_var(arg))
892                     return false;
893                 if (m_vars[to_var(arg)->get_idx()] == -1)
894                     return false;
895             }
896             return true;
897         }
898 
899         /**
900            \see get_stats
901         */
get_stats_core(app * n,unsigned & sz,unsigned & num_unbound_vars)902         void get_stats_core(app * n, unsigned & sz, unsigned & num_unbound_vars) {
903             sz++;
904             if (n->is_ground()) {
905                 return;
906             }
907             for (expr* arg : *n) {
908                 if (is_var(arg)) {
909                     sz++;
910                     unsigned var_id = to_var(arg)->get_idx();
911                     if (m_vars[var_id] == -1)
912                         num_unbound_vars++;
913                 }
914                 else if (is_app(arg)) {
915                     get_stats_core(to_app(arg), sz, num_unbound_vars);
916                 }
917             }
918         }
919 
920         /**
921            \brief Return statistics for the given pattern
922            \remark Patterns are small. So, it doesn't hurt to use a recursive function.
923         */
get_stats(app * n,unsigned & sz,unsigned & num_unbound_vars)924         void get_stats(app * n, unsigned & sz, unsigned & num_unbound_vars) {
925             sz = 0;
926             num_unbound_vars = 0;
927             get_stats_core(n, sz, num_unbound_vars);
928         }
929 
930         /**
931            \brief Process registers in m_todo. The registers in m_todo
932            that produce non-BIND operations are processed first. Then,
933            a single BIND operation b is produced.
934 
935            After executing this method m_todo will contain the
936            registers in m_todo that produce BIND operations and were
937            not processed, and the registers generated when the
938            operation b was produced.
939 
940            \remark The new operations are appended to m_seq.
941         */
linearise_core()942         void linearise_core() {
943             m_aux.reset();
944             app *         first_app = nullptr;
945             unsigned      first_app_reg;
946             unsigned      first_app_sz;
947             unsigned      first_app_num_unbound_vars;
948             // generate first the non-BIND operations
949             for (unsigned reg : m_todo) {
950                 expr *  p    = m_registers[reg];
951                 SASSERT(!is_quantifier(p));
952                 TRACE("mam", tout << "lin: " << reg << " " << get_check_mark(reg) << " " << is_var(p) << "\n";);
953                 if (is_var(p)) {
954                     unsigned var_id = to_var(p)->get_idx();
955                     if (m_vars[var_id] != -1)
956                         m_seq.push_back(m_ct_manager.mk_compare(m_vars[var_id], reg));
957                     else
958                         m_vars[var_id] = reg;
959                     continue;
960                 }
961 
962 
963                 SASSERT(is_app(p));
964 
965                 if (to_app(p)->is_ground()) {
966                     // ground applications are viewed as constants, and eagerly
967                     // converted into enodes.
968                     enode * e = m_egraph.find(p);
969                     m_seq.push_back(m_ct_manager.mk_check(reg, e));
970                     set_check_mark(reg, NOT_CHECKED); // reset mark, register was fully processed.
971                     continue;
972                 }
973 
974                 unsigned matched_reg;
975                 if (m_matched_exprs.find(p, matched_reg) && reg != matched_reg) {
976                     m_seq.push_back(m_ct_manager.mk_compare(matched_reg, reg));
977                     set_check_mark(reg, NOT_CHECKED); // reset mark, register was fully processed.
978                     continue;
979                 }
980                 m_matched_exprs.insert(p, reg);
981 
982                 if (m_use_filters && get_check_mark(reg) != CHECK_SINGLETON) {
983                     func_decl * lbl = to_app(p)->get_decl();
984                     approx_set s(m_lbl_hasher(lbl));
985                     m_seq.push_back(m_ct_manager.mk_filter(reg, s));
986                     set_check_mark(reg, CHECK_SINGLETON);
987                 }
988 
989                 if (first_app) {
990                     // Try to select the best first_app
991                     if (first_app_num_unbound_vars == 0) {
992                         // first_app doesn't have free vars... so it is the best choice...
993                         m_aux.push_back(reg);
994                     }
995                     else {
996                         unsigned sz;
997                         unsigned num_unbound_vars;
998                         get_stats(to_app(p), sz, num_unbound_vars);
999                         if (num_unbound_vars == 0 ||
1000                             sz > first_app_sz ||
1001                             (sz == first_app_sz && num_unbound_vars < first_app_num_unbound_vars)) {
1002                             // change the first_app
1003                             m_aux.push_back(first_app_reg);
1004                             first_app     = to_app(p);
1005                             first_app_reg = reg;
1006                             first_app_sz  = sz;
1007                             first_app_num_unbound_vars = num_unbound_vars;
1008                         }
1009                         else {
1010                             m_aux.push_back(reg);
1011                         }
1012                     }
1013                 }
1014                 else {
1015                     first_app     = to_app(p);
1016                     first_app_reg = reg;
1017                     get_stats(first_app, first_app_sz, first_app_num_unbound_vars);
1018                 }
1019             }
1020 
1021             if (first_app) {
1022                 // m_todo contains at least one (non-ground) application.
1023                 func_decl * lbl         = first_app->get_decl();
1024                 unsigned short num_args = first_app->get_num_args();
1025                 if (IS_CGR_SUPPORT && all_args_are_bound_vars(first_app)) {
1026                     // use IS_CGR instead of BIND
1027                     sbuffer<unsigned> iregs;
1028                     for (unsigned i = 0; i < num_args; i++) {
1029                         expr * arg = to_app(first_app)->get_arg(i);
1030                         SASSERT(is_var(arg));
1031                         SASSERT(m_vars[to_var(arg)->get_idx()] != -1);
1032                         iregs.push_back(m_vars[to_var(arg)->get_idx()]);
1033                     }
1034                     m_seq.push_back(m_ct_manager.mk_is_cgr(lbl, first_app_reg, num_args, iregs.data()));
1035                 }
1036                 else {
1037                     // Generate a BIND operation for this application.
1038                     unsigned oreg           = m_tree->m_num_regs;
1039                     m_tree->m_num_regs     += num_args;
1040                     for (unsigned j = 0; j < num_args; j++) {
1041                         set_register(oreg + j, first_app->get_arg(j));
1042                         m_aux.push_back(oreg + j);
1043                     }
1044                     m_seq.push_back(m_ct_manager.mk_bind(lbl, num_args, first_app_reg, oreg));
1045                     m_num_choices++;
1046                 }
1047                 set_check_mark(first_app_reg, NOT_CHECKED); // reset mark, register was fully processed.
1048             }
1049 
1050             // make m_aux the new todo list.
1051             m_todo.swap(m_aux);
1052         }
1053 
1054         /**
1055            \brief Return the number of already bound vars in n.
1056 
1057            \remark Patterns are small. So, it doesn't hurt to use a recursive function.
1058         */
get_num_bound_vars_core(app * n,bool & has_unbound_vars)1059         unsigned get_num_bound_vars_core(app * n, bool & has_unbound_vars) {
1060             unsigned r = 0;
1061             if (n->is_ground()) {
1062                 return 0;
1063             }
1064             for (expr * arg : *n) {
1065                 if (is_var(arg)) {
1066                     unsigned var_id = to_var(arg)->get_idx();
1067                     if (m_vars[var_id] != -1)
1068                         r++;
1069                     else
1070                         has_unbound_vars = true;
1071                 }
1072                 else if (is_app(arg)) {
1073                     r += get_num_bound_vars_core(to_app(arg), has_unbound_vars);
1074                 }
1075             }
1076             return r;
1077         }
1078 
get_num_bound_vars(app * n,bool & has_unbound_vars)1079         unsigned get_num_bound_vars(app * n, bool & has_unbound_vars) {
1080             has_unbound_vars = false;
1081             return get_num_bound_vars_core(n, has_unbound_vars);
1082         }
1083 
1084         /**
1085            \brief Compile a pattern where all free variables are already bound.
1086            Return the register where the enode congruent to f will be stored.
1087         */
gen_mp_filter(app * n)1088         unsigned gen_mp_filter(app * n) {
1089             if (is_ground(n)) {
1090                 unsigned oreg        = m_tree->m_num_regs;
1091                 m_tree->m_num_regs  += 1;
1092                 enode * e = m_egraph.find(n);
1093                 m_seq.push_back(m_ct_manager.mk_get_enode(oreg, e));
1094                 return oreg;
1095             }
1096 
1097             sbuffer<unsigned> iregs;
1098             for (expr* arg : *n) {
1099                 if (is_var(arg)) {
1100                     SASSERT(m_vars[to_var(arg)->get_idx()] != -1);
1101                     if (m_vars[to_var(arg)->get_idx()] == -1)
1102                         verbose_stream() << "BUG.....\n";
1103                     iregs.push_back(m_vars[to_var(arg)->get_idx()]);
1104                 }
1105                 else {
1106                     iregs.push_back(gen_mp_filter(to_app(arg)));
1107                 }
1108             }
1109             unsigned oreg        = m_tree->m_num_regs;
1110             m_tree->m_num_regs  += 1;
1111             m_seq.push_back(m_ct_manager.mk_get_cgr(n->get_decl(), oreg, n->get_num_args(), iregs.data()));
1112             return oreg;
1113         }
1114 
1115         /**
1116            \brief Process the rest of a multi-pattern. That is the patterns different from first_idx
1117         */
linearise_multi_pattern(unsigned first_idx)1118         void linearise_multi_pattern(unsigned first_idx) {
1119             unsigned num_args = m_mp->get_num_args();
1120             // multi_pattern support
1121             for (unsigned i = 1; i < num_args; i++) {
1122                 // select the pattern with the biggest number of bound variables
1123                 app *    best  = nullptr;
1124                 unsigned best_num_bvars = 0;
1125                 unsigned best_j = 0;
1126                 bool     found_bounded_mp = false;
1127                 for (unsigned j = 0; j < m_mp->get_num_args(); j++) {
1128                     if (m_mp_already_processed[j])
1129                         continue;
1130                     app * p            = to_app(m_mp->get_arg(j));
1131                     bool has_unbound_vars = false;
1132                     unsigned num_bvars = get_num_bound_vars(p, has_unbound_vars);
1133                     if (!has_unbound_vars) {
1134                         best             = p;
1135                         best_j           = j;
1136                         found_bounded_mp = true;
1137                         break;
1138                     }
1139                     if (best == nullptr || (num_bvars > best_num_bvars)) {
1140                         best           = p;
1141                         best_num_bvars = num_bvars;
1142                         best_j         = j;
1143                     }
1144                 }
1145                 m_mp_already_processed[best_j] = true;
1146                 SASSERT(best != 0);
1147                 app * p                 = best;
1148                 func_decl * lbl         = p->get_decl();
1149                 unsigned short num_args = p->get_num_args();
1150                 approx_set s;
1151                 if (m_use_filters)
1152                     s.insert(m_lbl_hasher(lbl));
1153 
1154                 if (found_bounded_mp) {
1155                     gen_mp_filter(p);
1156                 }
1157                 else {
1158                     // USE CONTINUE
1159                     unsigned oreg           = m_tree->m_num_regs;
1160                     m_tree->m_num_regs     += num_args;
1161                     ptr_buffer<enode>       joints;
1162                     bool has_depth1_joint   = false; // VAR_TAG or GROUND_TERM_TAG
1163                     for (unsigned j = 0; j < num_args; j++) {
1164                         expr * curr = p->get_arg(j);
1165                         SASSERT(!is_quantifier(curr));
1166                         set_register(oreg + j, curr);
1167                         m_todo.push_back(oreg + j);
1168 
1169                         if ((is_var(curr) && m_vars[to_var(curr)->get_idx()] >= 0)
1170                             ||
1171                             (is_app(curr) && (to_app(curr)->is_ground())))
1172                             has_depth1_joint = true;
1173                     }
1174 
1175                     if (has_depth1_joint) {
1176                         for (expr * curr : *p) {
1177 
1178                             if (is_var(curr)) {
1179                                 unsigned var_id = to_var(curr)->get_idx();
1180                                 if (m_vars[var_id] >= 0)
1181                                     joints.push_back(BOXTAGINT(enode *, m_vars[var_id], VAR_TAG));
1182                                 else
1183                                     joints.push_back(NULL_TAG);
1184                                 continue;
1185                             }
1186 
1187                             SASSERT(is_app(curr));
1188 
1189                             if (is_ground(curr)) {
1190                                 enode * e = m_egraph.find(curr);
1191                                 joints.push_back(TAG(enode *, e, GROUND_TERM_TAG));
1192                                 continue;
1193                             }
1194 
1195                             joints.push_back(0);
1196                         }
1197                     }
1198                     else {
1199                         // Only try to use depth2 joints if there is no depth1 joint.
1200                         for (expr * curr : *p) {
1201                             if (!is_app(curr)) {
1202                                 joints.push_back(0);
1203                                 continue;
1204                             }
1205                             unsigned num_args2 = to_app(curr)->get_num_args();
1206                             unsigned k = 0;
1207                             for (; k < num_args2; k++) {
1208                                 expr * arg = to_app(curr)->get_arg(k);
1209                                 if (!is_var(arg))
1210                                     continue;
1211                                 unsigned var_id = to_var(arg)->get_idx();
1212                                 if (m_vars[var_id] < 0)
1213                                     continue;
1214                                 joint2 * new_joint = m_ct_manager.mk_joint2(to_app(curr)->get_decl(), k, m_vars[var_id]);
1215                                 joints.push_back(TAG(enode *, new_joint, NESTED_VAR_TAG));
1216                                 break; // found a new joint
1217                             }
1218                             if (k == num_args2)
1219                                 joints.push_back(0); // didn't find joint
1220                         }
1221                     }
1222                     SASSERT(joints.size() == num_args);
1223                     m_seq.push_back(m_ct_manager.mk_cont(lbl, num_args, oreg, s, joints.data()));
1224                     m_num_choices++;
1225                     while (!m_todo.empty())
1226                         linearise_core();
1227                 }
1228             }
1229         }
1230 
1231         /**
1232            \brief Produce the operations for the registers in m_todo.
1233         */
linearise(instruction * head,unsigned first_idx)1234         void linearise(instruction * head, unsigned first_idx) {
1235             m_seq.reset();
1236             m_matched_exprs.reset();
1237             while (!m_todo.empty())
1238                 linearise_core();
1239 
1240             if (m_mp->get_num_args() > 1) {
1241                 m_mp_already_processed.reset();
1242                 m_mp_already_processed.resize(m_mp->get_num_args());
1243                 m_mp_already_processed[first_idx] = true;
1244                 linearise_multi_pattern(first_idx);
1245             }
1246             for (unsigned i = 0; i < m_qa->get_num_decls(); i++)
1247                 if (m_vars[i] == -1)
1248                     return;
1249 
1250             SASSERT(head->m_next == 0);
1251             m_seq.push_back(m_ct_manager.mk_yield(m_qa, m_mp, m_qa->get_num_decls(), reinterpret_cast<unsigned*>(m_vars.begin())));
1252 
1253             for (instruction * curr : m_seq) {
1254                 head->m_next = curr;
1255                 head = curr;
1256             }
1257         }
1258 
set_next(instruction * instr,instruction * new_next)1259         void set_next(instruction * instr, instruction * new_next) {
1260             if (m_is_tmp_tree)
1261                 instr->m_next = new_next;
1262             else
1263                 m_ct_manager.set_next(instr, new_next);
1264         }
1265 
1266         /*
1267           The nodes in the bottom of the code-tree can have a lot of children in big examples.
1268           Example:
1269             parent-node:
1270               (CHOOSE) (CHECK #1 10) (YIELD ...)
1271               (CHOOSE) (CHECK #2 10) (YIELD ...)
1272               (CHOOSE) (CHECK #3 10) (YIELD ...)
1273               (CHOOSE) (CHECK #4 10) (YIELD ...)
1274               (CHOOSE) (CHECK #5 10) (YIELD ...)
1275               (CHOOSE) (CHECK #6 10) (YIELD ...)
1276               (CHOOSE) (CHECK #7 10) (YIELD ...)
1277               (CHOOSE) (CHECK #8 10) (YIELD ...)
1278               ...
1279           The method find_best_child will traverse this big list, and usually will not find
1280           a compatible child. So, I limit the number of simple code sequences that can be
1281           traversed.
1282         */
1283 #define FIND_BEST_CHILD_THRESHOLD 64
1284 
find_best_child(choose * first_child)1285         choose * find_best_child(choose * first_child) {
1286             unsigned num_too_simple    = 0;
1287             choose * best_child        = nullptr;
1288             unsigned max_compatibility = 0;
1289             choose * curr_child        = first_child;
1290             while (curr_child != nullptr) {
1291                 bool simple = false;
1292                 unsigned curr_compatibility = get_compatibility_measure(curr_child, simple);
1293                 if (simple) {
1294                     num_too_simple++;
1295                     if (num_too_simple > FIND_BEST_CHILD_THRESHOLD)
1296                         return nullptr; // it is unlikely we will find a compatible node
1297                 }
1298                 if (curr_compatibility > max_compatibility) {
1299                     TRACE("mam", tout << "better child " << best_child << " -> " << curr_child << "\n";);
1300                     best_child         = curr_child;
1301                     max_compatibility  = curr_compatibility;
1302                 }
1303                 curr_child = curr_child->m_alt;
1304             }
1305             return best_child;
1306         }
1307 
is_compatible(bind * instr) const1308         bool is_compatible(bind * instr) const {
1309             unsigned ireg = instr->m_ireg;
1310             expr * n      = m_registers[ireg];
1311             return
1312                 n != nullptr &&
1313                 is_app(n) &&
1314                 // It is wasteful to use a bind of a ground term.
1315                 // Actually, in the rest of the code I assume that.
1316                 !is_ground(n) &&
1317                 to_app(n)->get_decl() == instr->m_label &&
1318                 to_app(n)->get_num_args() == instr->m_num_args;
1319         }
1320 
is_compatible(compare * instr) const1321         bool is_compatible(compare * instr) const {
1322             unsigned reg1 = instr->m_reg1;
1323             unsigned reg2 = instr->m_reg2;
1324             return
1325                 m_registers[reg1] != nullptr &&
1326                 m_registers[reg1] == m_registers[reg2];
1327         }
1328 
is_compatible(check * instr) const1329         bool is_compatible(check * instr) const {
1330             unsigned reg  = instr->m_reg;
1331             enode *  n    = instr->m_enode;
1332             if (!m_registers[reg])
1333                 return false;
1334             if (!is_app(m_registers[reg]))
1335                 return false;
1336             if (!to_app(m_registers[reg])->is_ground())
1337                 return false;
1338             enode * n_prime = m_egraph.find(m_registers[reg]);
1339             // it is safe to compare the roots because the modifications
1340             // on the code tree are chronological.
1341             return n->get_root() == n_prime->get_root();
1342         }
1343 
1344         /**
1345            \brief Get the label hash of the pattern stored at register reg.
1346 
1347            If the pattern is a ground application, then it is viewed as a
1348            constant. In this case, we use the field get_lbl_hash() in the enode
1349            associated with it.
1350         */
get_pat_lbl_hash(unsigned reg) const1351         unsigned get_pat_lbl_hash(unsigned reg) const {
1352             SASSERT(m_registers[reg] != 0);
1353             SASSERT(is_app(m_registers[reg]));
1354             app * p = to_app(m_registers[reg]);
1355             if (p->is_ground()) {
1356                 enode * e = m_egraph.find(p);
1357                 if (!e->has_lbl_hash())
1358                     m_egraph.set_lbl_hash(e);
1359                 return e->get_lbl_hash();
1360             }
1361             else {
1362                 func_decl * lbl = p->get_decl();
1363                 return m_lbl_hasher(lbl);
1364             }
1365         }
1366 
1367         /**
1368            \brief We say a check operation is semi compatible if
1369            it access a register that was not yet processed,
1370            and given reg = instr->m_reg
1371              1- is_ground(m_registers[reg])
1372              2- get_pat_lbl_hash(reg) == m_enode->get_lbl_hash()
1373 
1374            If that is the case, then a CFILTER is created
1375         */
is_semi_compatible(check * instr) const1376         bool is_semi_compatible(check * instr) const {
1377             unsigned reg  = instr->m_reg;
1378             if (instr->m_enode && !instr->m_enode->has_lbl_hash())
1379                 m_egraph.set_lbl_hash(instr->m_enode);
1380             return
1381                 m_registers[reg] != 0 &&
1382                 // if the register was already checked by another filter, then it doesn't make sense
1383                 // to check it again.
1384                 get_check_mark(reg) == NOT_CHECKED &&
1385                 is_ground(m_registers[reg]) &&
1386                 get_pat_lbl_hash(reg) == instr->m_enode->get_lbl_hash();
1387         }
1388 
1389         /**
1390            \brief FILTER is not compatible with ground terms anymore.
1391            See CFILTER is the filter used for ground terms.
1392         */
is_compatible(filter * instr) const1393         bool is_compatible(filter * instr) const {
1394             unsigned reg = instr->m_reg;
1395             if (m_registers[reg] != 0 && is_app(m_registers[reg]) && !is_ground(m_registers[reg])) {
1396                 // FILTER is fully compatible if it already contains
1397                 // the label hash of the pattern stored at reg.
1398                 unsigned elem = get_pat_lbl_hash(reg);
1399                 return instr->m_lbl_set.may_contain(elem);
1400             }
1401             return false;
1402         }
1403 
is_cfilter_compatible(filter * instr) const1404         bool is_cfilter_compatible(filter * instr) const {
1405             unsigned reg = instr->m_reg;
1406             // only ground terms are considered in CFILTERS
1407             if (m_registers[reg] != 0 && is_ground(m_registers[reg])) {
1408                 // FILTER is fully compatible if it already contains
1409                 // the label hash of the pattern stored at reg.
1410                 unsigned elem = get_pat_lbl_hash(reg);
1411                 return instr->m_lbl_set.may_contain(elem);
1412             }
1413             return false;
1414         }
1415 
1416         /**
1417            \brief See comments at is_semi_compatible(check * instr) and is_compatible(filter * instr).
1418            Remark: FILTER is not compatible with ground terms anymore
1419         */
is_semi_compatible(filter * instr) const1420         bool is_semi_compatible(filter * instr) const {
1421             unsigned reg = instr->m_reg;
1422             return
1423                 m_registers[reg] != nullptr &&
1424                 get_check_mark(reg) == NOT_CHECKED &&
1425                 is_app(m_registers[reg]) &&
1426                 !is_ground(m_registers[reg]);
1427         }
1428 
is_compatible(cont * instr) const1429         bool is_compatible(cont * instr) const {
1430             unsigned oreg = instr->m_oreg;
1431             for (unsigned i = 0; i < instr->m_num_args; i++)
1432                 if (m_registers[oreg + i] != 0)
1433                     return false;
1434             return true;
1435         }
1436 
1437         // Threshold for a code sequence (in number of instructions) to be considered simple.
1438 #define SIMPLE_SEQ_THRESHOLD 4
1439 
1440         /**
1441            \brief Return a "compatibility measure" that quantifies how
1442            many operations in the branch starting at child are compatible
1443            with the patterns in the registers stored in m_todo.
1444 
1445            Set simple to true, if the tree starting at child is too simple
1446            (no branching and less than SIMPLE_SEQ_THRESHOLD instructions)
1447         */
get_compatibility_measure(choose * child,bool & simple)1448         unsigned get_compatibility_measure(choose * child, bool & simple) {
1449             simple = true;
1450             m_to_reset.reset();
1451             unsigned weight    = 0;
1452             unsigned num_instr = 0;
1453             instruction * curr = child->m_next;
1454             while (curr != nullptr && curr->m_opcode != CHOOSE && curr->m_opcode != NOOP) {
1455                 num_instr++;
1456                 switch (curr->m_opcode) {
1457                 case BIND1: case BIND2: case BIND3: case BIND4: case BIND5: case BIND6: case BINDN:
1458                     if (is_compatible(static_cast<bind*>(curr))) {
1459                         weight += 4; // the weight of BIND is bigger than COMPARE and CHECK
1460                         unsigned ireg     = static_cast<bind*>(curr)->m_ireg;
1461                         app * n           = to_app(m_registers[ireg]);
1462                         unsigned oreg     = static_cast<bind*>(curr)->m_oreg;
1463                         unsigned num_args = static_cast<bind*>(curr)->m_num_args;
1464                         SASSERT(n->get_num_args() == num_args);
1465                         for (unsigned i = 0; i < num_args; i++) {
1466                             set_register(oreg + i, n->get_arg(i));
1467                             m_to_reset.push_back(oreg + i);
1468                         }
1469                     }
1470                     break;
1471                 case COMPARE:
1472                     if (is_compatible(static_cast<compare*>(curr)))
1473                         weight += 2;
1474                     break;
1475                 case CHECK:
1476                     if (is_compatible(static_cast<check*>(curr)))
1477                         weight += 2;
1478                     else if (m_use_filters && is_semi_compatible(static_cast<check*>(curr)))
1479                         weight += 1;
1480                     break;
1481                 case CFILTER:
1482                     if (is_cfilter_compatible(static_cast<filter*>(curr)))
1483                         weight += 2;
1484                     break;
1485                 case FILTER:
1486                     if (is_compatible(static_cast<filter*>(curr)))
1487                         weight += 2;
1488                     else if (is_semi_compatible(static_cast<filter*>(curr)))
1489                         weight += 1;
1490                     break;
1491                 // TODO: Try to reuse IS_CGR instruction
1492                 default:
1493                     break;
1494                 }
1495                 curr = curr->m_next;
1496             }
1497             if (num_instr > SIMPLE_SEQ_THRESHOLD || (curr != nullptr && curr->m_opcode == CHOOSE))
1498                 simple = false;
1499             for (unsigned r : m_to_reset)
1500                 m_registers[r] = 0;
1501             return weight;
1502         }
1503 
insert(instruction * head,unsigned first_mp_idx)1504         void insert(instruction * head, unsigned first_mp_idx) {
1505             for (;;) {
1506                 m_compatible.reset();
1507                 m_incompatible.reset();
1508                 TRACE("mam_compiler_detail", tout << "processing head: " << *head << "\n";);
1509                 instruction * curr = head->m_next;
1510                 instruction * last = head;
1511                 while (curr != nullptr && curr->m_opcode != CHOOSE && curr->m_opcode != NOOP) {
1512                     TRACE("mam_compiler_detail", tout << "processing instr: " << *curr << "\n";);
1513                     switch (curr->m_opcode) {
1514                     case BIND1: case BIND2: case BIND3: case BIND4: case BIND5: case BIND6: case BINDN: {
1515                         bind* bnd = static_cast<bind*>(curr);
1516                         if (is_compatible(bnd)) {
1517                             TRACE("mam_compiler_detail", tout << "compatible\n";);
1518                             unsigned ireg     = bnd->m_ireg;
1519                             SASSERT(m_todo.contains(ireg));
1520                             m_todo.erase(ireg);
1521                             set_check_mark(ireg, NOT_CHECKED);
1522                             m_compatible.push_back(curr);
1523                             app * app         = to_app(m_registers[ireg]);
1524                             unsigned oreg     = bnd->m_oreg;
1525                             unsigned num_args = bnd->m_num_args;
1526                             for (unsigned i = 0; i < num_args; i++) {
1527                                 set_register(oreg + i, app->get_arg(i));
1528                                 m_todo.push_back(oreg + i);
1529                             }
1530                         }
1531                         else {
1532                             TRACE("mam_compiler_detail", tout << "incompatible\n";);
1533                             m_incompatible.push_back(curr);
1534                         }
1535                         break;
1536                     }
1537                     case CHECK: {
1538                         check* chk = static_cast<check*>(curr);
1539                         if (is_compatible(chk)) {
1540                             TRACE("mam_compiler_detail", tout << "compatible\n";);
1541                             unsigned reg = chk->m_reg;
1542                             SASSERT(m_todo.contains(reg));
1543                             m_todo.erase(reg);
1544                             set_check_mark(reg, NOT_CHECKED);
1545                             m_compatible.push_back(curr);
1546                         }
1547                         else if (m_use_filters && is_semi_compatible(chk)) {
1548                             TRACE("mam_compiler_detail", tout << "semi compatible\n";);
1549                             unsigned reg = chk->m_reg;
1550                             enode *   n1 = chk->m_enode;
1551                             // n1->has_lbl_hash may be false, even
1552                             // when update_filters is invoked before
1553                             // executing this method.
1554                             //
1555                             // Reason: n1 is a ground subterm of a ground term T.
1556                             // I incorrectly assumed n1->has_lbl_hash() was true because
1557                             // update_filters executes set_lbl_hash for all maximal ground terms.
1558                             // And, I also incorrectly assumed that all arguments of check were
1559                             // maximal ground terms. This is not true. For example, assume
1560                             // the code_tree already has the pattern
1561                             // (f (g x) z)
1562                             // So, when the pattern (f (g b) x) is compiled a check instruction
1563                             // is created for a ground subterm b of the maximal ground term (g b).
1564                             if (!n1->has_lbl_hash())
1565                                 m_egraph.set_lbl_hash(n1);
1566                             unsigned  h1 = n1->get_lbl_hash();
1567                             unsigned  h2 = get_pat_lbl_hash(reg);
1568                             approx_set s(h1);
1569                             s.insert(h2);
1570                             filter * new_instr = m_ct_manager.mk_cfilter(reg, s);
1571                             set_check_mark(reg, CHECK_SET);
1572                             m_compatible.push_back(new_instr);
1573                             m_incompatible.push_back(curr);
1574                         }
1575                         else {
1576                             TRACE("mam_compiler_detail", tout << "incompatible " << chk->m_reg << "\n";);
1577                             m_incompatible.push_back(curr);
1578                         }
1579                         break;
1580                     }
1581                     case COMPARE:
1582                         if (is_compatible(static_cast<compare*>(curr))) {
1583                             TRACE("mam_compiler_detail", tout << "compatible\n";);
1584                             unsigned reg1   = static_cast<compare*>(curr)->m_reg1;
1585                             unsigned reg2   = static_cast<compare*>(curr)->m_reg2;
1586                             SASSERT(m_todo.contains(reg2));
1587                             m_todo.erase(reg2);
1588                             set_check_mark(reg2, NOT_CHECKED);
1589                             if (is_var(m_registers[reg1])) {
1590                                 m_todo.erase(reg1);
1591                                 set_check_mark(reg1, NOT_CHECKED);
1592                                 unsigned var_id = to_var(m_registers[reg1])->get_idx();
1593                                 if (m_vars[var_id] == -1)
1594                                     m_vars[var_id] = reg1;
1595                             }
1596                             m_compatible.push_back(curr);
1597                         }
1598                         else {
1599                             TRACE("mam_compiler_detail", tout << "incompatible\n";);
1600                             m_incompatible.push_back(curr);
1601                         }
1602                         break;
1603                     case CFILTER:
1604                         SASSERT(m_use_filters);
1605                         if (is_cfilter_compatible(static_cast<filter*>(curr))) {
1606                             unsigned reg = static_cast<filter*>(curr)->m_reg;
1607                             SASSERT(static_cast<filter*>(curr)->m_lbl_set.size() == 1);
1608                             set_check_mark(reg, CHECK_SINGLETON);
1609                             m_compatible.push_back(curr);
1610                         }
1611                         else {
1612                             m_incompatible.push_back(curr);
1613                         }
1614                         break;
1615                     case FILTER: {
1616                         filter *flt = static_cast<filter*>(curr);
1617                         SASSERT(m_use_filters);
1618                         if (is_compatible(flt)) {
1619                             unsigned reg = flt->m_reg;
1620                             TRACE("mam_compiler_detail", tout << "compatible " << reg << "\n";);
1621                             CTRACE("mam_compiler_bug", !m_todo.contains(reg), {
1622                                     for (unsigned t : m_todo) { tout << t << " "; }
1623                                     tout << "\nregisters:\n";
1624                                     unsigned i = 0;
1625                                     for (expr* r : m_registers) { if (r) { tout << i++ << ":\n" << mk_pp(r, m) << "\n"; } }
1626                                     tout << "quantifier:\n" << mk_pp(m_qa, m) << "\n";
1627                                     tout << "pattern:\n" << mk_pp(m_mp, m) << "\n";
1628                                 });
1629                             SASSERT(m_todo.contains(reg));
1630                             if (flt->m_lbl_set.size() == 1)
1631                                 set_check_mark(reg, CHECK_SINGLETON);
1632                             else
1633                                 set_check_mark(reg, CHECK_SET);
1634                             m_compatible.push_back(curr);
1635                         }
1636                         else if (is_semi_compatible(flt)) {
1637                             unsigned reg = flt->m_reg;
1638                             TRACE("mam_compiler_detail", tout << "semi compatible " << reg << "\n";);
1639                             CTRACE("mam_compiler_bug", !m_todo.contains(reg), {
1640                                     for (unsigned t : m_todo) { tout << t << " "; }
1641                                     tout << "\nregisters:\n";
1642                                     unsigned i = 0;
1643                                     for (expr* r : m_registers) { if (r) { tout << i++ << ":\n" << mk_pp(r, m) << "\n"; } }
1644                                     tout << "quantifier:\n" << mk_pp(m_qa, m) << "\n";
1645                                     tout << "pattern:\n" << mk_pp(m_mp, m) << "\n";
1646                                 });
1647                             SASSERT(m_todo.contains(reg));
1648                             unsigned  h  = get_pat_lbl_hash(reg);
1649                             TRACE("mam_lbl_bug",
1650                                   tout << "curr_set: " << flt->m_lbl_set << "\n";
1651                                   tout << "new hash: " << h << "\n";);
1652                             set_check_mark(reg, CHECK_SET);
1653                             approx_set const & s = flt->m_lbl_set;
1654                             if (s.size() > 1) {
1655                                 m_ct_manager.insert_new_lbl_hash(flt, h);
1656                                 m_compatible.push_back(curr);
1657                             }
1658                             else {
1659                                 SASSERT(s.size() == 1);
1660                                 approx_set new_s(s);
1661                                 new_s.insert(h);
1662                                 filter * new_instr = m_ct_manager.mk_filter(reg, new_s);
1663                                 m_compatible.push_back(new_instr);
1664                                 m_incompatible.push_back(curr);
1665                             }
1666                         }
1667                         else {
1668                             TRACE("mam_compiler_detail", tout << "incompatible\n";);
1669                             m_incompatible.push_back(curr);
1670                         }
1671                         break;
1672                     }
1673                     default:
1674                         TRACE("mam_compiler_detail", tout << "incompatible\n";);
1675                         m_incompatible.push_back(curr);
1676                         break;
1677                     }
1678                     last = curr;
1679                     curr = curr->m_next;
1680                 }
1681 
1682                 TRACE("mam_compiler", tout << *head << " " << head << "\n";
1683                       tout << "m_compatible.size(): " << m_compatible.size() << "\n";
1684                       tout << "m_incompatible.size(): " << m_incompatible.size() << "\n";);
1685 
1686                 if (m_incompatible.empty()) {
1687                     // sequence starting at head is fully compatible
1688                     SASSERT(curr != 0);
1689                     SASSERT(curr->m_opcode == CHOOSE);
1690                     choose * first_child = static_cast<choose *>(curr);
1691                     choose * best_child = find_best_child(first_child);
1692                     TRACE("mam", tout << "best child " << best_child << "\n";);
1693                     if (best_child == nullptr) {
1694                         // There is no compatible child
1695                         // Suppose the sequence is:
1696                         //   head -> c1 -> ... -> (cn == last) -> first_child;
1697                         // Then we should add
1698                         //   head -> c1 -> ... -> (cn == last) -> new_child
1699                         //   new_child: CHOOSE(first_child) -> linearise
1700                         choose * new_child = m_ct_manager.mk_choose(first_child);
1701                         m_num_choices++;
1702                         set_next(last, new_child);
1703                         linearise(new_child, first_mp_idx);
1704                         // DONE
1705                         return;
1706                     }
1707                     else {
1708                         head = best_child;
1709                         // CONTINUE from best_child
1710                     }
1711                 }
1712                 else {
1713                     SASSERT(head->is_init() || !m_compatible.empty());
1714                     SASSERT(!m_incompatible.empty());
1715                     // Suppose the sequence is:
1716                     // head -> c1 -> i1 -> c2 -> c3 -> i2 -> first_child_head
1717                     //    where c_j are the compatible instructions, and i_j are the incompatible ones
1718                     // Then the sequence starting at head should become
1719                     // head -> c1 -> c2 -> c3 -> new_child_head1
1720                     // new_child_head1:CHOOSE(new_child_head2) -> i1 -> i2 -> first_child_head
1721                     // new_child_head2:NOOP -> linearise()
1722                     instruction * first_child_head = curr;
1723                     choose * new_child_head2 = m_ct_manager.mk_noop();
1724                     SASSERT(new_child_head2->m_alt == 0);
1725                     choose * new_child_head1 = m_ct_manager.mk_choose(new_child_head2);
1726                     m_num_choices++;
1727                     // set: head -> c1 -> c2 -> c3 -> new_child_head1
1728                     curr = head;
1729                     for (instruction* instr : m_compatible) {
1730                         set_next(curr, instr);
1731                         curr = instr;
1732                     }
1733                     set_next(curr, new_child_head1);
1734                     // set: new_child_head1:CHOOSE(new_child_head2) -> i1 -> i2 -> first_child_head
1735                     curr = new_child_head1;
1736                     for (instruction* inc : m_incompatible) {
1737                         if (curr == new_child_head1)
1738                             curr->m_next = inc; // new_child_head1 is a new node, I don't need to save trail
1739                         else
1740                             set_next(curr, inc);
1741                         curr = inc;
1742                     }
1743                     set_next(curr, first_child_head);
1744                     // build new_child_head2:NOOP -> linearise()
1745                     linearise(new_child_head2, first_mp_idx);
1746                     // DONE
1747                     return;
1748                 }
1749             }
1750         }
1751 
1752 
1753     public:
compiler(egraph & ctx,code_tree_manager & ct_mg,label_hasher & h,bool use_filters=true)1754         compiler(egraph & ctx, code_tree_manager & ct_mg, label_hasher & h, bool use_filters = true):
1755             m_egraph(ctx),
1756             m(ctx.get_manager()),
1757             m_ct_manager(ct_mg),
1758             m_lbl_hasher(h),
1759             m_use_filters(use_filters) {
1760         }
1761 
1762         /**
1763            \brief Create a new code tree for the given quantifier.
1764 
1765            - mp: is a pattern of qa that will be used to create the code tree
1766 
1767            - first_idx: index of mp that will be the "head" (first to be processed) of the multi-pattern.
1768         */
mk_tree(quantifier * qa,app * mp,unsigned first_idx,bool filter_candidates)1769         code_tree * mk_tree(quantifier * qa, app * mp, unsigned first_idx, bool filter_candidates) {
1770             SASSERT(m.is_pattern(mp));
1771             app * p = to_app(mp->get_arg(first_idx));
1772             unsigned num_args = p->get_num_args();
1773             code_tree * r     = m_ct_manager.mk_code_tree(p->get_decl(), num_args, filter_candidates);
1774             init(r, qa, mp, first_idx);
1775             linearise(r->m_root, first_idx);
1776             r->m_num_choices  = m_num_choices;
1777             TRACE("mam_compiler", tout << "new tree for:\n" << mk_pp(mp, m) << "\n" << *r;);
1778             return r;
1779         }
1780 
1781         /**
1782            \brief Insert a pattern into the code tree.
1783 
1784            - is_tmp_tree: trail for update operations is created if is_tmp_tree = false.
1785         */
insert(code_tree * tree,quantifier * qa,app * mp,unsigned first_idx,bool is_tmp_tree)1786         void insert(code_tree * tree, quantifier * qa, app * mp, unsigned first_idx, bool is_tmp_tree) {
1787             if (tree->expected_num_args() != to_app(mp->get_arg(first_idx))->get_num_args()) {
1788                 // We have to check the number of arguments because of nary + and * operators.
1789                 // The E-matching engine that was built when all + and * applications were binary.
1790                 // We ignore the pattern if it does not have the expected number of arguments.
1791                 // This is not the ideal solution, but it avoids possible crashes.
1792                 return;
1793             }
1794             m_is_tmp_tree = is_tmp_tree;
1795             TRACE("mam_compiler", tout << "updating tree with:\n" << mk_pp(mp, m) << "\n";);
1796             TRACE("mam_bug", tout << "before insertion\n" << *tree << "\n";);
1797             if (!is_tmp_tree)
1798                 m_ct_manager.save_num_regs(tree);
1799             init(tree, qa, mp, first_idx);
1800             m_num_choices = tree->m_num_choices;
1801             insert(tree->m_root, first_idx);
1802             TRACE("mam_bug",
1803                   tout << "m_num_choices: " << m_num_choices << "\n";);
1804             if (m_num_choices > tree->m_num_choices) {
1805                 if (!is_tmp_tree)
1806                     m_ct_manager.save_num_choices(tree);
1807                 tree->m_num_choices = m_num_choices;
1808             }
1809             TRACE("mam_bug",
1810                   tout << "m_num_choices: " << m_num_choices << "\n";
1811                   tout << "new tree:\n" << *tree;
1812                   tout << "todo ";
1813                   for (auto t : m_todo) tout << t << " ";
1814                   tout << "\n";);
1815         }
1816     };
1817 
1818 #if 0
1819     bool check_lbls(enode * n) {
1820         approx_set  lbls;
1821         approx_set plbls;
1822         enode * first = n;
1823         do {
1824             lbls  |= n->get_lbls();
1825             plbls |= n->get_plbls();
1826             n = n->get_next();
1827         }
1828         while (first != n);
1829         SASSERT(n->get_root()->get_lbls()  == lbls);
1830         SASSERT(n->get_root()->get_plbls() == plbls);
1831         return true;
1832     }
1833 #endif
1834 
1835     // ------------------------------------
1836     //
1837     // Code Tree Interpreter
1838     //
1839     // ------------------------------------
1840 
1841     struct backtrack_point {
1842         const instruction *  m_instr;
1843         unsigned             m_old_max_generation;
1844         union {
1845             enode *  m_curr;
1846             struct {
1847                 enode_vector *  m_to_recycle;
1848                 enode * const * m_it;
1849                 enode * const * m_end;
1850             };
1851         };
1852     };
1853 
1854     typedef svector<backtrack_point> backtrack_stack;
1855 
1856     class interpreter {
1857         euf::solver&        ctx;
1858         ast_manager &       m;
1859         mam &               m_mam;
1860         bool                m_use_filters;
1861         enode_vector        m_registers;
1862         enode_vector        m_bindings;
1863         enode_vector        m_args;
1864         backtrack_stack     m_backtrack_stack;
1865         unsigned            m_top;
1866         const instruction * m_pc;
1867 
1868         // auxiliary temporary variables
1869         unsigned            m_max_generation;  // the maximum generation of an app enode processed.
1870         unsigned            m_curr_max_generation;  // temporary var used to store a copy of m_max_generation
1871         unsigned            m_num_args;
1872         unsigned            m_oreg;
1873         enode *             m_n1;
1874         enode *             m_n2;
1875         enode *             m_app;
1876         const bind *        m_b;
1877 
1878         // equalities used for pattern match. The first element of the tuple gives the argument (or null) of some term that was matched against some higher level
1879         // structure of the trigger, the second element gives the term that argument is replaced with in order to match the trigger. Used for logging purposes only.
1880         ptr_vector<enode>   m_pattern_instances; // collect the pattern instances... used for computing min_top_generation and max_top_generation
1881         unsigned_vector     m_min_top_generation, m_max_top_generation;
1882 
1883         pool<enode_vector>  m_pool;
1884 
mk_enode_vector()1885         enode_vector * mk_enode_vector() {
1886             enode_vector * r = m_pool.mk();
1887             r->reset();
1888             return r;
1889         }
1890 
recycle_enode_vector(enode_vector * v)1891         void recycle_enode_vector(enode_vector * v) {
1892             m_pool.recycle(v);
1893         }
1894 
update_max_generation(enode * n,enode * prev)1895         void update_max_generation(enode * n, enode * prev) {
1896             m_max_generation = std::max(m_max_generation, n->generation());
1897         }
1898 
1899         // We have to provide the number of expected arguments because we have flat-assoc applications such as +.
1900         // Flat-assoc applications may have arbitrary number of arguments.
get_first_f_app(func_decl * lbl,unsigned num_expected_args,enode * first)1901         enode * get_first_f_app(func_decl * lbl, unsigned num_expected_args, enode * first) {
1902             for (enode* curr : euf::enode_class(first)) {
1903                 if (curr->get_decl() == lbl && curr->is_cgr() && curr->num_args() == num_expected_args) {
1904                     update_max_generation(curr, first);
1905                     return curr;
1906                 }
1907             }
1908             return nullptr;
1909         }
1910 
get_next_f_app(func_decl * lbl,unsigned num_expected_args,enode * first,enode * curr)1911         enode * get_next_f_app(func_decl * lbl, unsigned num_expected_args, enode * first, enode * curr) {
1912             curr = curr->get_next();
1913             while (curr != first) {
1914                 if (curr->get_decl() == lbl && curr->is_cgr() && curr->num_args() == num_expected_args) {
1915                     update_max_generation(curr, first);
1916                     return curr;
1917                 }
1918                 curr = curr->get_next();
1919             }
1920             return nullptr;
1921         }
1922 
1923         /**
1924            \brief Execute the is_cgr instruction.
1925            Return true if succeeded, and false if backtracking is needed.
1926         */
exec_is_cgr(is_cgr const * pc)1927         bool exec_is_cgr(is_cgr const * pc) {
1928             unsigned num_args = pc->m_num_args;
1929             enode * r         = m_registers[pc->m_ireg];
1930             func_decl * f     = pc->m_label;
1931             switch (num_args) {
1932             case 1:
1933                 m_args[0] = m_registers[pc->m_iregs[0]]->get_root();
1934                 for (enode* n : euf::enode_class(r)) {
1935                     if (n->get_decl() == f &&
1936                         n->get_arg(0)->get_root() == m_args[0]) {
1937                         update_max_generation(n, r);
1938                         return true;
1939                     }
1940                 }
1941                 return false;
1942             case 2:
1943                 m_args[0] = m_registers[pc->m_iregs[0]]->get_root();
1944                 m_args[1] = m_registers[pc->m_iregs[1]]->get_root();
1945                 for (enode* n : euf::enode_class(r)) {
1946                     if (n->get_decl() == f &&
1947                         n->get_arg(0)->get_root() == m_args[0] &&
1948                         n->get_arg(1)->get_root() == m_args[1]) {
1949                         update_max_generation(n, r);
1950                         return true;
1951                     }
1952                 }
1953                 return false;
1954             default: {
1955                 m_args.reserve(num_args+1, 0);
1956                 for (unsigned i = 0; i < num_args; i++)
1957                     m_args[i] = m_registers[pc->m_iregs[i]]->get_root();
1958                 for (enode* n : euf::enode_class(r)) {
1959                     if (n->get_decl() == f) {
1960                         unsigned i = 0;
1961                         for (; i < num_args; i++) {
1962                             if (n->get_arg(i)->get_root() != m_args[i])
1963                                 break;
1964                         }
1965                         if (i == num_args) {
1966                             update_max_generation(n, r);
1967                             return true;
1968                         }
1969                     }
1970                 }
1971                 return false;
1972             } }
1973         }
1974 
1975         enode_vector * mk_depth1_vector(enode * n, func_decl * f, unsigned i);
1976 
1977         enode_vector * mk_depth2_vector(joint2 * j2, func_decl * f, unsigned i);
1978 
1979         enode * init_continue(cont const * c, unsigned expected_num_args);
1980 
1981         void display_reg(std::ostream & out, unsigned reg);
1982 
1983         void display_instr_input_reg(std::ostream & out, instruction const * instr);
1984 
1985         void display_pc_info(std::ostream & out);
1986 
1987 #define INIT_ARGS_SIZE 16
1988 
1989     public:
interpreter(euf::solver & ctx,mam & ma,bool use_filters)1990         interpreter(euf::solver& ctx, mam & ma, bool use_filters):
1991             ctx(ctx),
1992             m(ctx.get_manager()),
1993             m_mam(ma),
1994             m_use_filters(use_filters) {
1995             m_args.resize(INIT_ARGS_SIZE);
1996         }
1997 
~interpreter()1998         ~interpreter() {
1999         }
2000 
init(code_tree * t)2001         void init(code_tree * t) {
2002             TRACE("mam_bug", tout << "preparing to match tree:\n" << *t << "\n";);
2003             m_registers.reserve(t->get_num_regs(), nullptr);
2004             m_bindings.reserve(t->get_num_regs(),  nullptr);
2005             if (m_backtrack_stack.size() < t->get_num_choices())
2006                 m_backtrack_stack.resize(t->get_num_choices());
2007         }
2008 
2009 
execute(code_tree * t)2010         void execute(code_tree * t) {
2011             if (!t->has_candidates())
2012                 return;
2013             TRACE("trigger_bug", tout << "execute for code tree:\n"; t->display(tout););
2014             init(t);
2015             t->save_qhead(ctx);
2016             enode* app;
2017             if (t->filter_candidates()) {
2018                 code_tree::scoped_unmark _unmark(t);
2019                 while ((app = t->next_candidate()) && !ctx.resource_limits_exceeded()) {
2020                     TRACE("trigger_bug", tout << "candidate\n" << ctx.bpp(app) << "\n";);
2021                     if (!app->is_marked1() && app->is_cgr()) {
2022                         execute_core(t, app);
2023                         app->mark1();
2024                     }
2025                 }
2026             }
2027             else {
2028                 while ((app = t->next_candidate()) && !ctx.resource_limits_exceeded()) {
2029                     TRACE("trigger_bug", tout << "candidate\n" << ctx.bpp(app) << "\n";);
2030                     if (app->is_cgr())
2031                         execute_core(t, app);
2032                 }
2033             }
2034         }
2035 
2036         // init(t) must be invoked before execute_core
2037         bool execute_core(code_tree * t, enode * n);
2038 
2039         // Return the min, max generation of the enodes in m_pattern_instances.
2040 
get_min_max_top_generation(unsigned & min,unsigned & max)2041         void get_min_max_top_generation(unsigned& min, unsigned& max) {
2042             SASSERT(!m_pattern_instances.empty());
2043             if (m_min_top_generation.empty()) {
2044                 min = max = m_pattern_instances[0]->generation();
2045                 m_min_top_generation.push_back(min);
2046                 m_max_top_generation.push_back(max);
2047             }
2048             else {
2049                 min = m_min_top_generation.back();
2050                 max = m_max_top_generation.back();
2051             }
2052             for (unsigned i = m_min_top_generation.size(); i < m_pattern_instances.size(); ++i) {
2053                 unsigned curr = m_pattern_instances[i]->generation();
2054                 min = std::min(min, curr);
2055                 m_min_top_generation.push_back(min);
2056                 max = std::max(max, curr);
2057                 m_max_top_generation.push_back(max);
2058             }
2059         }
2060     };
2061 
2062     /**
2063        \brief Return a vector with the relevant f-parents of n such that n is the i-th argument.
2064     */
mk_depth1_vector(enode * n,func_decl * f,unsigned i)2065     enode_vector * interpreter::mk_depth1_vector(enode * n, func_decl * f, unsigned i) {
2066         enode_vector * v = mk_enode_vector();
2067         n = n->get_root();
2068         for (enode* p : euf::enode_parents(n)) {
2069             if (p->get_decl() == f  &&
2070                 i < p->num_args() &&
2071                 ctx.is_relevant(p)  &&
2072                 p->is_cgr() &&
2073                 p->get_arg(i)->get_root() == n)
2074                 v->push_back(p);
2075         }
2076         return v;
2077     }
2078 
2079     /**
2080        \brief Return a vector with the relevant f-parents of each p in joint2 where n is the i-th argument.
2081        We say a p is in joint2 if p is the g-parent of m_registers[j2->m_reg] where g is j2->m_decl,
2082        and m_registers[j2->m_reg] is the argument j2->m_arg_pos.
2083     */
mk_depth2_vector(joint2 * j2,func_decl * f,unsigned i)2084     enode_vector * interpreter::mk_depth2_vector(joint2 * j2, func_decl * f, unsigned i) {
2085         enode * n = m_registers[j2->m_reg]->get_root();
2086         if (n->num_parents() == 0)
2087             return nullptr;
2088         enode_vector * v  = mk_enode_vector();
2089         for (enode* p : euf::enode_parents(n)) {
2090             if (p->get_decl() == j2->m_decl &&
2091                 ctx.is_relevant(p) &&
2092                 p->num_args() > j2->m_arg_pos &&
2093                 p->is_cgr() &&
2094                 p->get_arg(j2->m_arg_pos)->get_root() == n) {
2095                 // p is in joint2
2096                 p = p->get_root();
2097                 for (enode* p2 : euf::enode_parents(p)) {
2098                     if (p2->get_decl() == f &&
2099                         ctx.is_relevant(p2) &&
2100                         p2->is_cgr() &&
2101                         i < p2->num_args() &&
2102                         p2->get_arg(i)->get_root() == p) {
2103                         v->push_back(p2);
2104                     }
2105                 }
2106             }
2107         }
2108         return v;
2109     }
2110 
init_continue(cont const * c,unsigned expected_num_args)2111     enode * interpreter::init_continue(cont const * c, unsigned expected_num_args) {
2112         func_decl * lbl         = c->m_label;
2113         unsigned min_sz         = ctx.get_egraph().enodes_of(lbl).size();
2114         unsigned short num_args = c->m_num_args;
2115         enode * r;
2116         // quick filter... check if any of the joint points have zero parents...
2117         for (unsigned i = 0; i < num_args; i++) {
2118             void * bare = c->m_joints[i];
2119             enode * n   = nullptr;
2120             switch (GET_TAG(bare)) {
2121             case NULL_TAG:
2122                 goto non_depth1;
2123             case GROUND_TERM_TAG:
2124                 n = UNTAG(enode *, bare);
2125                 break;
2126             case VAR_TAG:
2127                 n = m_registers[UNBOXINT(bare)];
2128                 break;
2129             case NESTED_VAR_TAG:
2130                 goto non_depth1;
2131             }
2132             r = n->get_root();
2133             if (m_use_filters && r->get_plbls().empty_intersection(c->m_lbl_set))
2134                 return nullptr;
2135             if (r->num_parents() == 0)
2136                 return nullptr;
2137         non_depth1:
2138             ;
2139         }
2140         // traverse each joint and select the best one.
2141         enode_vector * best_v   = nullptr;
2142         for (unsigned i = 0; i < num_args; i++) {
2143             enode * bare          = c->m_joints[i];
2144             enode_vector * curr_v = nullptr;
2145             switch (GET_TAG(bare)) {
2146             case NULL_TAG:
2147                 curr_v = nullptr;
2148                 break;
2149             case GROUND_TERM_TAG:
2150                 curr_v = mk_depth1_vector(UNTAG(enode *, bare), lbl, i);
2151                 break;
2152             case VAR_TAG:
2153                 curr_v = mk_depth1_vector(m_registers[UNBOXINT(bare)], lbl, i);
2154                 break;
2155             case NESTED_VAR_TAG:
2156                 curr_v = mk_depth2_vector(UNTAG(joint2 *, bare), lbl, i);
2157                 break;
2158             }
2159             if (curr_v != nullptr) {
2160                 if (curr_v->size() < min_sz && (best_v == nullptr || curr_v->size() < best_v->size())) {
2161                     if (best_v)
2162                         recycle_enode_vector(best_v);
2163                     best_v  = curr_v;
2164                     if (best_v->empty()) {
2165                         recycle_enode_vector(best_v);
2166                         return nullptr;
2167                     }
2168                 }
2169                 else {
2170                     recycle_enode_vector(curr_v);
2171                 }
2172             }
2173         }
2174         backtrack_point & bp = m_backtrack_stack[m_top];
2175         bp.m_instr                = c;
2176         bp.m_old_max_generation   = m_max_generation;
2177         if (best_v == nullptr) {
2178             TRACE("mam_bug", tout << "m_top: " << m_top << ", m_backtrack_stack.size(): " << m_backtrack_stack.size() << "\n";
2179                   tout << *c << "\n";);
2180             bp.m_to_recycle           = nullptr;
2181             bp.m_it                   = ctx.get_egraph().enodes_of(lbl).begin();
2182             bp.m_end                  = ctx.get_egraph().enodes_of(lbl).end();
2183         }
2184         else {
2185             SASSERT(!best_v->empty());
2186             bp.m_to_recycle           = best_v;
2187             bp.m_it                   = best_v->begin();
2188             bp.m_end                  = best_v->end();
2189         }
2190         // find application with the right number of arguments
2191         for (; bp.m_it != bp.m_end; ++bp.m_it) {
2192             enode * curr = *bp.m_it;
2193             if (curr->num_args() == expected_num_args && ctx.is_relevant(curr))
2194                 break;
2195         }
2196         if (bp.m_it == bp.m_end)
2197             return nullptr;
2198         m_top++;
2199         update_max_generation(*(bp.m_it), nullptr);
2200         return *(bp.m_it);
2201     }
2202 
display_reg(std::ostream & out,unsigned reg)2203     void interpreter::display_reg(std::ostream & out, unsigned reg) {
2204         out << "reg[" << reg << "]: ";
2205         enode * n = m_registers[reg];
2206         if (!n) {
2207             out << "nil\n";
2208         }
2209         else {
2210             out << "#" << n->get_expr_id() << ", root: " << n->get_root()->get_expr_id();
2211             if (m_use_filters)
2212                 out << ", lbls: " << n->get_root()->get_lbls() << " ";
2213             out << "\n";
2214             out << mk_pp(n->get_expr(), m) << "\n";
2215         }
2216     }
2217 
display_instr_input_reg(std::ostream & out,const instruction * instr)2218     void interpreter::display_instr_input_reg(std::ostream & out, const instruction * instr) {
2219         switch (instr->m_opcode) {
2220         case INIT1: case INIT2: case INIT3: case INIT4: case INIT5: case INIT6: case INITN:
2221             display_reg(out, 0);
2222             break;
2223         case BIND1: case BIND2: case BIND3: case BIND4: case BIND5: case BIND6: case BINDN:
2224             display_reg(out, static_cast<const bind *>(instr)->m_ireg);
2225             break;
2226         case COMPARE:
2227             display_reg(out, static_cast<const compare *>(instr)->m_reg1);
2228             display_reg(out, static_cast<const compare *>(instr)->m_reg2);
2229             break;
2230         case CHECK:
2231             display_reg(out, static_cast<const check *>(instr)->m_reg);
2232             break;
2233         case FILTER:
2234             display_reg(out, static_cast<const filter *>(instr)->m_reg);
2235             break;
2236         case YIELD1: case YIELD2: case YIELD3: case YIELD4: case YIELD5: case YIELD6: case YIELDN:
2237             for (unsigned i = 0; i < static_cast<const yield *>(instr)->m_num_bindings; i++) {
2238                 display_reg(out, static_cast<const yield *>(instr)->m_bindings[i]);
2239             }
2240             break;
2241         default:
2242             break;
2243         }
2244     }
2245 
display_pc_info(std::ostream & out)2246     void interpreter::display_pc_info(std::ostream & out) {
2247         out << "executing: " << *m_pc << "\n";
2248         out << "m_pc: " << m_pc << ", next: " << m_pc->m_next;
2249         if (m_pc->m_opcode == CHOOSE)
2250             out << ", alt: " << static_cast<const choose *>(m_pc)->m_alt;
2251         out << "\n";
2252         display_instr_input_reg(out, m_pc);
2253     }
2254 
execute_core(code_tree * t,enode * n)2255     bool interpreter::execute_core(code_tree * t, enode * n) {
2256         TRACE("trigger_bug", tout << "interpreter::execute_core\n"; t->display(tout); tout << "\nenode\n" << mk_ismt2_pp(n->get_expr(), m) << "\n";);
2257         unsigned since_last_check = 0;
2258 
2259 #ifdef _PROFILE_MAM
2260 #ifdef _PROFILE_MAM_EXPENSIVE
2261         if (t->get_watch().get_seconds() > _PROFILE_MAM_THRESHOLD && t->get_counter() % _PROFILE_MAM_EXPENSIVE_FREQ == 0) {
2262             std::cout << "EXPENSIVE\n";
2263             t->display(std::cout);
2264         }
2265 #endif
2266         t->get_watch().start();
2267         t->inc_counter();
2268 #endif
2269         // It doesn't make sense to process an irrelevant enode.
2270         TRACE("mam_execute_core", tout << "EXEC " << t->get_root_lbl()->get_name() << "\n";);
2271         if (!ctx.is_relevant(n))
2272             return false;
2273         SASSERT(ctx.is_relevant(n));
2274         m_pattern_instances.reset();
2275         m_min_top_generation.reset();
2276         m_max_top_generation.reset();
2277         m_pattern_instances.push_back(n);
2278         m_max_generation = n->generation();
2279 
2280         m_pc             = t->get_root();
2281         m_registers[0]   = n;
2282         m_top            = 0;
2283 
2284 
2285     main_loop:
2286 
2287         TRACE("mam_int", display_pc_info(tout););
2288 #ifdef _PROFILE_MAM
2289         const_cast<instruction*>(m_pc)->m_counter++;
2290 #endif
2291         switch (m_pc->m_opcode) {
2292         case INIT1:
2293             m_app          = m_registers[0];
2294             if (m_app->num_args() != 1)
2295                 goto backtrack;
2296             m_registers[1] = m_app->get_arg(0);
2297             m_pc = m_pc->m_next;
2298             goto main_loop;
2299 
2300         case INIT2:
2301             m_app          = m_registers[0];
2302             if (m_app->num_args() != 2)
2303                 goto backtrack;
2304             m_registers[1] = m_app->get_arg(0);
2305             m_registers[2] = m_app->get_arg(1);
2306             m_pc = m_pc->m_next;
2307             goto main_loop;
2308 
2309         case INIT3:
2310             m_app          = m_registers[0];
2311             if (m_app->num_args() != 3)
2312                 goto backtrack;
2313             m_registers[1] = m_app->get_arg(0);
2314             m_registers[2] = m_app->get_arg(1);
2315             m_registers[3] = m_app->get_arg(2);
2316             m_pc = m_pc->m_next;
2317             goto main_loop;
2318 
2319         case INIT4:
2320             m_app          = m_registers[0];
2321             if (m_app->num_args() != 4)
2322                 goto backtrack;
2323             m_registers[1] = m_app->get_arg(0);
2324             m_registers[2] = m_app->get_arg(1);
2325             m_registers[3] = m_app->get_arg(2);
2326             m_registers[4] = m_app->get_arg(3);
2327             m_pc = m_pc->m_next;
2328             goto main_loop;
2329 
2330         case INIT5:
2331             m_app          = m_registers[0];
2332             if (m_app->num_args() != 5)
2333                 goto backtrack;
2334             m_registers[1] = m_app->get_arg(0);
2335             m_registers[2] = m_app->get_arg(1);
2336             m_registers[3] = m_app->get_arg(2);
2337             m_registers[4] = m_app->get_arg(3);
2338             m_registers[5] = m_app->get_arg(4);
2339             m_pc = m_pc->m_next;
2340             goto main_loop;
2341 
2342         case INIT6:
2343             m_app          = m_registers[0];
2344             if (m_app->num_args() != 6)
2345                 goto backtrack;
2346             m_registers[1] = m_app->get_arg(0);
2347             m_registers[2] = m_app->get_arg(1);
2348             m_registers[3] = m_app->get_arg(2);
2349             m_registers[4] = m_app->get_arg(3);
2350             m_registers[5] = m_app->get_arg(4);
2351             m_registers[6] = m_app->get_arg(5);
2352             m_pc = m_pc->m_next;
2353             goto main_loop;
2354 
2355         case INITN:
2356             m_app      = m_registers[0];
2357             m_num_args = m_app->num_args();
2358             if (m_num_args != static_cast<const initn *>(m_pc)->m_num_args)
2359                 goto backtrack;
2360             for (unsigned i = 0; i < m_num_args; i++)
2361                 m_registers[i+1] = m_app->get_arg(i);
2362             m_pc = m_pc->m_next;
2363             goto main_loop;
2364 
2365         case COMPARE:
2366             m_n1 = m_registers[static_cast<const compare *>(m_pc)->m_reg1];
2367             m_n2 = m_registers[static_cast<const compare *>(m_pc)->m_reg2];
2368             SASSERT(m_n1 != 0);
2369             SASSERT(m_n2 != 0);
2370             if (m_n1->get_root() != m_n2->get_root())
2371                 goto backtrack;
2372 
2373             m_pc = m_pc->m_next;
2374             goto main_loop;
2375 
2376         case CHECK:
2377             m_n1 = m_registers[static_cast<const check *>(m_pc)->m_reg];
2378             m_n2 = static_cast<const check *>(m_pc)->m_enode;
2379             SASSERT(m_n1 != 0);
2380             SASSERT(m_n2 != 0);
2381             if (m_n1->get_root() != m_n2->get_root())
2382                 goto backtrack;
2383 
2384             m_pc = m_pc->m_next;
2385             goto main_loop;
2386 
2387             /* CFILTER AND FILTER are handled differently by the compiler
2388                The compiler will never merge two CFILTERs with different m_lbl_set fields.
2389                Essentially, CFILTER is used to combine CHECK statements, and FILTER for BIND
2390             */
2391         case CFILTER:
2392         case FILTER:
2393             m_n1 = m_registers[static_cast<const filter *>(m_pc)->m_reg]->get_root();
2394             if (static_cast<const filter *>(m_pc)->m_lbl_set.empty_intersection(m_n1->get_lbls()))
2395                 goto backtrack;
2396             m_pc = m_pc->m_next;
2397             goto main_loop;
2398 
2399         case PFILTER:
2400             m_n1 = m_registers[static_cast<const filter *>(m_pc)->m_reg]->get_root();
2401             if (static_cast<const filter *>(m_pc)->m_lbl_set.empty_intersection(m_n1->get_plbls()))
2402                 goto backtrack;
2403             m_pc = m_pc->m_next;
2404             goto main_loop;
2405 
2406         case CHOOSE:
2407             m_backtrack_stack[m_top].m_instr                = m_pc;
2408             m_backtrack_stack[m_top].m_old_max_generation   = m_max_generation;
2409             m_top++;
2410             m_pc = m_pc->m_next;
2411             goto main_loop;
2412         case NOOP:
2413             SASSERT(static_cast<const choose *>(m_pc)->m_alt == 0);
2414             m_pc = m_pc->m_next;
2415             goto main_loop;
2416 
2417         case BIND1:
2418 #define BIND_COMMON()                                                                                                   \
2419                  m_n1   = m_registers[static_cast<const bind *>(m_pc)->m_ireg];                                         \
2420                  SASSERT(m_n1 != 0);                                                                                    \
2421                  m_oreg = static_cast<const bind *>(m_pc)->m_oreg;                                                      \
2422                  m_curr_max_generation = m_max_generation;                                                              \
2423                  m_app  = get_first_f_app(static_cast<const bind *>(m_pc)->m_label, static_cast<const bind *>(m_pc)->m_num_args, m_n1); \
2424                  if (!m_app)                                                                                            \
2425                      goto backtrack;                                                                                    \
2426                  TRACE("mam_int", tout << "bind candidate: " << mk_pp(m_app->get_expr(), m) << "\n";);     \
2427                  m_backtrack_stack[m_top].m_instr              = m_pc;                                                  \
2428                  m_backtrack_stack[m_top].m_old_max_generation = m_curr_max_generation;                                 \
2429                  m_backtrack_stack[m_top].m_curr               = m_app;                                                 \
2430                  m_top++;
2431 
2432             BIND_COMMON();
2433             m_registers[m_oreg] = m_app->get_arg(0);
2434             m_pc = m_pc->m_next;
2435             goto main_loop;
2436 
2437         case BIND2:
2438             BIND_COMMON();
2439             m_registers[m_oreg]   = m_app->get_arg(0);
2440             m_registers[m_oreg+1] = m_app->get_arg(1);
2441             m_pc = m_pc->m_next;
2442             goto main_loop;
2443 
2444         case BIND3:
2445             BIND_COMMON();
2446             m_registers[m_oreg]   = m_app->get_arg(0);
2447             m_registers[m_oreg+1] = m_app->get_arg(1);
2448             m_registers[m_oreg+2] = m_app->get_arg(2);
2449             m_pc = m_pc->m_next;
2450             goto main_loop;
2451 
2452         case BIND4:
2453             BIND_COMMON();
2454             m_registers[m_oreg]   = m_app->get_arg(0);
2455             m_registers[m_oreg+1] = m_app->get_arg(1);
2456             m_registers[m_oreg+2] = m_app->get_arg(2);
2457             m_registers[m_oreg+3] = m_app->get_arg(3);
2458             m_pc = m_pc->m_next;
2459             goto main_loop;
2460 
2461         case BIND5:
2462             BIND_COMMON();
2463             m_registers[m_oreg]   = m_app->get_arg(0);
2464             m_registers[m_oreg+1] = m_app->get_arg(1);
2465             m_registers[m_oreg+2] = m_app->get_arg(2);
2466             m_registers[m_oreg+3] = m_app->get_arg(3);
2467             m_registers[m_oreg+4] = m_app->get_arg(4);
2468             m_pc = m_pc->m_next;
2469             goto main_loop;
2470 
2471         case BIND6:
2472             BIND_COMMON();
2473             m_registers[m_oreg]   = m_app->get_arg(0);
2474             m_registers[m_oreg+1] = m_app->get_arg(1);
2475             m_registers[m_oreg+2] = m_app->get_arg(2);
2476             m_registers[m_oreg+3] = m_app->get_arg(3);
2477             m_registers[m_oreg+4] = m_app->get_arg(4);
2478             m_registers[m_oreg+5] = m_app->get_arg(5);
2479             m_pc = m_pc->m_next;
2480             goto main_loop;
2481 
2482         case BINDN:
2483             BIND_COMMON();
2484             m_num_args = static_cast<const bind *>(m_pc)->m_num_args;
2485             for (unsigned i = 0; i < m_num_args; i++)
2486                 m_registers[m_oreg+i] = m_app->get_arg(i);
2487             m_pc = m_pc->m_next;
2488             goto main_loop;
2489 
2490         case YIELD1:
2491             m_bindings[0] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[0]];
2492 #define ON_MATCH(NUM)                                                                                   \
2493             m_max_generation = std::max(m_max_generation, get_max_generation(NUM, m_bindings.begin())); \
2494             if (!m.inc())                                                                               \
2495                 return false;                                                                           \
2496                                                                                                         \
2497             m_mam.on_match(static_cast<const yield *>(m_pc)->m_qa,                                      \
2498                            static_cast<const yield *>(m_pc)->m_pat,                                     \
2499                            NUM,                                                                         \
2500                            m_bindings.begin(),                                                          \
2501                            m_max_generation)
2502 
2503             SASSERT(static_cast<const yield *>(m_pc)->m_qa->get_decl_sort(0) == m_bindings[0]->get_expr()->get_sort());
2504             ON_MATCH(1);
2505             goto backtrack;
2506 
2507         case YIELD2:
2508             m_bindings[0] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[1]];
2509             m_bindings[1] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[0]];
2510             ON_MATCH(2);
2511             goto backtrack;
2512 
2513         case YIELD3:
2514             m_bindings[0] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[2]];
2515             m_bindings[1] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[1]];
2516             m_bindings[2] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[0]];
2517             ON_MATCH(3);
2518             goto backtrack;
2519 
2520         case YIELD4:
2521             m_bindings[0] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[3]];
2522             m_bindings[1] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[2]];
2523             m_bindings[2] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[1]];
2524             m_bindings[3] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[0]];
2525             ON_MATCH(4);
2526             goto backtrack;
2527 
2528         case YIELD5:
2529             m_bindings[0] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[4]];
2530             m_bindings[1] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[3]];
2531             m_bindings[2] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[2]];
2532             m_bindings[3] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[1]];
2533             m_bindings[4] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[0]];
2534             ON_MATCH(5);
2535             goto backtrack;
2536 
2537         case YIELD6:
2538             m_bindings[0] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[5]];
2539             m_bindings[1] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[4]];
2540             m_bindings[2] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[3]];
2541             m_bindings[3] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[2]];
2542             m_bindings[4] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[1]];
2543             m_bindings[5] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[0]];
2544             ON_MATCH(6);
2545             goto backtrack;
2546 
2547         case YIELDN:
2548             m_num_args = static_cast<const yield *>(m_pc)->m_num_bindings;
2549             for (unsigned i = 0; i < m_num_args; i++)
2550                 m_bindings[i] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[m_num_args - i - 1]];
2551             ON_MATCH(m_num_args);
2552             goto backtrack;
2553 
2554         case GET_ENODE:
2555             m_registers[static_cast<const get_enode_instr *>(m_pc)->m_oreg] = static_cast<const get_enode_instr *>(m_pc)->m_enode;
2556             m_pc = m_pc->m_next;
2557             goto main_loop;
2558 
2559         case GET_CGR1:
2560 
2561 #define SET_VAR(IDX)                                                                                                                         \
2562             m_args[IDX] = m_registers[static_cast<const get_cgr *>(m_pc)->m_iregs[IDX]];                                                     \
2563             if (m_use_filters && static_cast<const get_cgr *>(m_pc)->m_lbl_set.empty_intersection(m_args[IDX]->get_root()->get_plbls())) {   \
2564                 goto backtrack;                                                                                                              \
2565             }
2566 
2567             SET_VAR(0);
2568             goto cgr_common;
2569 
2570         case GET_CGR2:
2571             SET_VAR(0);
2572             SET_VAR(1);
2573             goto cgr_common;
2574 
2575         case GET_CGR3:
2576             SET_VAR(0);
2577             SET_VAR(1);
2578             SET_VAR(2);
2579             goto cgr_common;
2580 
2581         case GET_CGR4:
2582             SET_VAR(0);
2583             SET_VAR(1);
2584             SET_VAR(2);
2585             SET_VAR(3);
2586             goto cgr_common;
2587 
2588         case GET_CGR5:
2589             SET_VAR(0);
2590             SET_VAR(1);
2591             SET_VAR(2);
2592             SET_VAR(3);
2593             SET_VAR(4);
2594             goto cgr_common;
2595 
2596         case GET_CGR6:
2597             SET_VAR(0);
2598             SET_VAR(1);
2599             SET_VAR(2);
2600             SET_VAR(3);
2601             SET_VAR(4);
2602             SET_VAR(5);
2603             goto cgr_common;
2604 
2605         case GET_CGRN:
2606             m_num_args = static_cast<const get_cgr *>(m_pc)->m_num_args;
2607             m_args.reserve(m_num_args, 0);
2608             for (unsigned i = 0; i < m_num_args; i++)
2609                 m_args[i] = m_registers[static_cast<const get_cgr *>(m_pc)->m_iregs[i]];
2610             goto cgr_common;
2611 
2612         case IS_CGR:
2613             if (!exec_is_cgr(static_cast<const is_cgr *>(m_pc)))
2614                 goto backtrack;
2615             m_pc = m_pc->m_next;
2616             goto main_loop;
2617 
2618         case CONTINUE:
2619             m_num_args = static_cast<const cont *>(m_pc)->m_num_args;
2620             m_oreg     = static_cast<const cont *>(m_pc)->m_oreg;
2621             m_app = init_continue(static_cast<const cont *>(m_pc), m_num_args);
2622             if (m_app == nullptr)
2623                 goto backtrack;
2624             m_pattern_instances.push_back(m_app);
2625             TRACE("mam_int", tout << "continue candidate:\n" << mk_ll_pp(m_app->get_expr(), m););
2626             for (unsigned i = 0; i < m_num_args; i++)
2627                 m_registers[m_oreg+i] = m_app->get_arg(i);
2628             m_pc = m_pc->m_next;
2629             goto main_loop;
2630         }
2631 
2632         goto backtrack;
2633 
2634     cgr_common:
2635         m_n1 = ctx.get_egraph().get_enode_eq_to(static_cast<const get_cgr *>(m_pc)->m_label, static_cast<const get_cgr *>(m_pc)->m_num_args, m_args.data());
2636         if (!m_n1 || !ctx.is_relevant(m_n1))
2637             goto backtrack;
2638         update_max_generation(m_n1, nullptr);
2639         m_registers[static_cast<const get_cgr *>(m_pc)->m_oreg] = m_n1;
2640         m_pc = m_pc->m_next;
2641         goto main_loop;
2642 
2643     backtrack:
2644         TRACE("mam_int", tout << "backtracking.\n";);
2645         if (m_top == 0) {
2646             TRACE("mam_int", tout << "no more alternatives.\n";);
2647 #ifdef _PROFILE_MAM
2648             t->get_watch().stop();
2649 #endif
2650             return true; // no more alternatives
2651         }
2652         backtrack_point & bp = m_backtrack_stack[m_top - 1];
2653         m_max_generation     = bp.m_old_max_generation;
2654 
2655 
2656         TRACE("mam_int", tout << "backtrack top: " << bp.m_instr << " " << *(bp.m_instr) << "\n";);
2657 #ifdef _PROFILE_MAM
2658         if (bp.m_instr->m_opcode != CHOOSE) // CHOOSE has a different status. It is a control flow backtracking.
2659             const_cast<instruction*>(bp.m_instr)->m_counter++;
2660 #endif
2661 
2662         if (since_last_check++ > 100) {
2663             since_last_check = 0;
2664             if (ctx.resource_limits_exceeded()) {
2665                 // Soft timeout...
2666                 // Cleanup before exiting
2667                 while (m_top != 0) {
2668                     backtrack_point & bp = m_backtrack_stack[m_top - 1];
2669                     if (bp.m_instr->m_opcode == CONTINUE && bp.m_to_recycle)
2670                         recycle_enode_vector(bp.m_to_recycle);
2671                     m_top--;
2672                 }
2673 #ifdef _PROFILE_MAM
2674                t->get_watch().stop();
2675 #endif
2676                 return false;
2677             }
2678         }
2679 
2680         switch (bp.m_instr->m_opcode) {
2681         case CHOOSE:
2682             m_pc = static_cast<const choose*>(bp.m_instr)->m_alt;
2683             TRACE("mam_int", tout << "alt: " << m_pc << "\n";);
2684             SASSERT(m_pc != 0);
2685             m_top--;
2686             goto main_loop;
2687         case BIND1:
2688 #define BBIND_COMMON() m_b   = static_cast<const bind*>(bp.m_instr);                                                            \
2689                        m_n1  = m_registers[m_b->m_ireg];                                                                        \
2690                        m_app = get_next_f_app(m_b->m_label, m_b->m_num_args, m_n1, bp.m_curr); \
2691                        if (m_app == 0) {                                                                                        \
2692                            m_top--;                                                                                             \
2693                            goto backtrack;                                                                                      \
2694                        }                                                                                                        \
2695                        bp.m_curr = m_app;                                                                                       \
2696                        TRACE("mam_int", tout << "bind next candidate:\n" << mk_ll_pp(m_app->get_expr(), m););      \
2697                        m_oreg    = m_b->m_oreg
2698 
2699             BBIND_COMMON();
2700             m_registers[m_oreg] = m_app->get_arg(0);
2701             m_pc = m_b->m_next;
2702             goto main_loop;
2703 
2704         case BIND2:
2705             BBIND_COMMON();
2706             m_registers[m_oreg]   = m_app->get_arg(0);
2707             m_registers[m_oreg+1] = m_app->get_arg(1);
2708             m_pc = m_b->m_next;
2709                 goto main_loop;
2710 
2711         case BIND3:
2712             BBIND_COMMON();
2713             m_registers[m_oreg]   = m_app->get_arg(0);
2714             m_registers[m_oreg+1] = m_app->get_arg(1);
2715             m_registers[m_oreg+2] = m_app->get_arg(2);
2716             m_pc = m_b->m_next;
2717             goto main_loop;
2718 
2719         case BIND4:
2720             BBIND_COMMON();
2721             m_registers[m_oreg]   = m_app->get_arg(0);
2722             m_registers[m_oreg+1] = m_app->get_arg(1);
2723             m_registers[m_oreg+2] = m_app->get_arg(2);
2724             m_registers[m_oreg+3] = m_app->get_arg(3);
2725             m_pc = m_b->m_next;
2726             goto main_loop;
2727 
2728         case BIND5:
2729             BBIND_COMMON();
2730             m_registers[m_oreg]   = m_app->get_arg(0);
2731             m_registers[m_oreg+1] = m_app->get_arg(1);
2732             m_registers[m_oreg+2] = m_app->get_arg(2);
2733             m_registers[m_oreg+3] = m_app->get_arg(3);
2734             m_registers[m_oreg+4] = m_app->get_arg(4);
2735             m_pc = m_b->m_next;
2736             goto main_loop;
2737 
2738         case BIND6:
2739             BBIND_COMMON();
2740             m_registers[m_oreg]   = m_app->get_arg(0);
2741             m_registers[m_oreg+1] = m_app->get_arg(1);
2742             m_registers[m_oreg+2] = m_app->get_arg(2);
2743             m_registers[m_oreg+3] = m_app->get_arg(3);
2744             m_registers[m_oreg+4] = m_app->get_arg(4);
2745             m_registers[m_oreg+5] = m_app->get_arg(5);
2746             m_pc = m_b->m_next;
2747             goto main_loop;
2748 
2749         case BINDN:
2750             BBIND_COMMON();
2751             m_num_args = m_b->m_num_args;
2752             for (unsigned i = 0; i < m_num_args; i++)
2753                 m_registers[m_oreg+i] = m_app->get_arg(i);
2754             m_pc = m_b->m_next;
2755             goto main_loop;
2756 
2757         case CONTINUE:
2758             ++bp.m_it;
2759             for (; bp.m_it != bp.m_end; ++bp.m_it) {
2760                 m_app = *bp.m_it;
2761                 const cont * c = static_cast<const cont*>(bp.m_instr);
2762                 // bp.m_it may reference an enode in [begin_enodes_of(lbl), end_enodes_of(lbl))
2763                 // This enodes are not necessarily relevant.
2764                 // So, we must check whether ctx.is_relevant(m_app) is true or not.
2765                 if (m_app->num_args() == c->m_num_args && ctx.is_relevant(m_app)) {
2766                     // update the pattern instance
2767                     SASSERT(!m_pattern_instances.empty());
2768                     if (m_pattern_instances.size() == m_max_top_generation.size()) {
2769                         m_max_top_generation.pop_back();
2770                         m_min_top_generation.pop_back();
2771                     }
2772                     m_pattern_instances.pop_back();
2773                     m_pattern_instances.push_back(m_app);
2774                     // continue succeeded
2775                     update_max_generation(m_app, nullptr); // null indicates a top-level match
2776                     TRACE("mam_int", tout << "continue next candidate:\n" << mk_ll_pp(m_app->get_expr(), m););
2777                     m_num_args = c->m_num_args;
2778                     m_oreg     = c->m_oreg;
2779                     for (unsigned i = 0; i < m_num_args; i++)
2780                         m_registers[m_oreg+i] = m_app->get_arg(i);
2781                     m_pc = c->m_next;
2782                     goto main_loop;
2783                 }
2784             }
2785             // continue failed
2786             if (bp.m_to_recycle)
2787                 recycle_enode_vector(bp.m_to_recycle);
2788             m_top--;
2789             goto backtrack;
2790 
2791         default:
2792             UNREACHABLE();
2793         }
2794         return false;
2795     } // end of execute_core
2796 
2797 #if 0
2798     void display_trees(std::ostream & out, const ptr_vector<code_tree> & trees) {
2799         unsigned lbl = 0;
2800         for (code_tree * tree : trees) {
2801             if (tree) {
2802                 out << "tree for f" << lbl << "\n";
2803                 out << *tree;
2804             }
2805             ++lbl;
2806         }
2807     }
2808 #endif
2809 
2810     // ------------------------------------
2811     //
2812     // A mapping from func_label -> code tree.
2813     //
2814     // ------------------------------------
2815     class code_tree_map {
2816         ast_manager &               m;
2817         compiler &                  m_compiler;
2818         ptr_vector<code_tree>       m_trees;       // mapping: func_label -> tree
2819         euf::solver&                ctx;
2820 #ifdef Z3DEBUG
2821         egraph *                   m_egraph;
2822 #endif
2823 
2824         class mk_tree_trail : public trail {
2825             ptr_vector<code_tree> & m_trees;
2826             unsigned                m_lbl_id;
2827         public:
mk_tree_trail(ptr_vector<code_tree> & t,unsigned id)2828             mk_tree_trail(ptr_vector<code_tree> & t, unsigned id):m_trees(t), m_lbl_id(id) {}
undo()2829             void undo() override {
2830                 dealloc(m_trees[m_lbl_id]);
2831                 m_trees[m_lbl_id] = nullptr;
2832             }
2833         };
2834 
2835     public:
code_tree_map(ast_manager & m,compiler & c,euf::solver & ctx)2836         code_tree_map(ast_manager & m, compiler & c, euf::solver& ctx):
2837             m(m),
2838             m_compiler(c),
2839             ctx(ctx) {
2840         }
2841 
2842 #ifdef Z3DEBUG
set_egraph(egraph * c)2843         void set_egraph(egraph* c) { m_egraph = c; }
2844 
2845 #endif
2846 
~code_tree_map()2847         ~code_tree_map() {
2848             std::for_each(m_trees.begin(), m_trees.end(), delete_proc<code_tree>());
2849         }
2850 
2851         /**
2852            \brief Add a pattern to the code tree map.
2853 
2854            - mp: is used a pattern for qa.
2855 
2856            - first_idx: index to be used as head of the multi-pattern mp
2857         */
add_pattern(quantifier * qa,app * mp,unsigned first_idx)2858         void add_pattern(quantifier * qa, app * mp, unsigned first_idx) {
2859             (void)m;
2860             SASSERT(m.is_pattern(mp));
2861             SASSERT(first_idx < mp->get_num_args());
2862             app * p           = to_app(mp->get_arg(first_idx));
2863             func_decl * lbl   = p->get_decl();
2864             unsigned lbl_id   = lbl->get_decl_id();
2865             m_trees.reserve(lbl_id+1, nullptr);
2866             if (m_trees[lbl_id] == nullptr) {
2867                 m_trees[lbl_id] = m_compiler.mk_tree(qa, mp, first_idx, false);
2868                 SASSERT(m_trees[lbl_id]->expected_num_args() == p->get_num_args());
2869                 DEBUG_CODE(m_trees[lbl_id]->set_egraph(m_egraph););
2870                 ctx.push(mk_tree_trail(m_trees, lbl_id));
2871             }
2872             else {
2873                 code_tree * tree = m_trees[lbl_id];
2874                 // We have to check the number of arguments because of nary + and * operators.
2875                 // The E-matching engine that was built when all + and * applications were binary.
2876                 // We ignore the pattern if it does not have the expected number of arguments.
2877                 // This is not the ideal solution, but it avoids possible crashes.
2878                 if (tree->expected_num_args() == p->get_num_args())
2879                     m_compiler.insert(tree, qa, mp, first_idx, false);
2880             }
2881             DEBUG_CODE(m_trees[lbl_id]->get_patterns().push_back(std::make_pair(qa, mp));
2882                        ctx.push(push_back_trail<std::pair<quantifier*,app*>, false>(m_trees[lbl_id]->get_patterns())););
2883             TRACE("trigger_bug", tout << "after add_pattern, first_idx: " << first_idx << "\n"; m_trees[lbl_id]->display(tout););
2884         }
2885 
reset()2886         void reset() {
2887             std::for_each(m_trees.begin(), m_trees.end(), delete_proc<code_tree>());
2888             m_trees.reset();
2889         }
2890 
get_code_tree_for(func_decl * lbl) const2891         code_tree * get_code_tree_for(func_decl * lbl) const {
2892             unsigned lbl_id = lbl->get_decl_id();
2893             if (lbl_id < m_trees.size())
2894                 return m_trees[lbl_id];
2895             else
2896                 return nullptr;
2897         }
2898 
begin()2899         ptr_vector<code_tree>::iterator begin() { return m_trees.begin(); }
end()2900         ptr_vector<code_tree>::iterator end() { return m_trees.end(); }
2901     };
2902 
2903     // ------------------------------------
2904     //
2905     // Path trees AKA inverted path index
2906     //
2907     // ------------------------------------
2908 
2909     /**
2910        \brief Temporary object used to encode a path of the form:
2911 
2912        f.1 -> g.2 -> h.0
2913 
2914        These objects are used to update the inverse path index data structure.
2915        For example, in the path above, given an enode n, I want to follow the
2916        parents p_0 of n that are f-applications, and n is the second argument,
2917        then for each such p_0, I want to follow the parents p_1 of p_0 that
2918        are g-applications, and p_0 is the third argument. Finally, I want to
2919        follow the p_2 parents of p_1 that are h-applications and p_1 is the
2920        first argument of p_2.
2921 
2922        To improve the filtering power of the inverse path index, I'm also
2923        storing a ground argument (when possible) in the inverted path index.
2924        the idea is to have paths of the form
2925 
2926        f.1:t.2 -> g.2 -> h.0:s.1
2927 
2928        The extra pairs t.2 and s.1 are an extra filter on the parents.
2929        The idea is that I want only the f-parents s.t. the third argument is equal to t.
2930     */
2931     struct path {
2932         func_decl *    m_label;
2933         unsigned short m_arg_idx;
2934         unsigned short m_ground_arg_idx;
2935         enode *        m_ground_arg;
2936         unsigned       m_pattern_idx;
2937         path *         m_child;
pathq::path2938         path (func_decl * lbl, unsigned short arg_idx, unsigned short ground_arg_idx, enode * ground_arg, unsigned pat_idx, path * child):
2939             m_label(lbl),
2940             m_arg_idx(arg_idx),
2941             m_ground_arg_idx(ground_arg_idx),
2942             m_ground_arg(ground_arg),
2943             m_pattern_idx(pat_idx),
2944             m_child(child) {
2945             SASSERT(ground_arg != nullptr || ground_arg_idx == 0);
2946         }
2947     };
2948 
is_equal(path const * p1,path const * p2)2949     bool is_equal(path const * p1, path const * p2) {
2950         while (true) {
2951 
2952             if (p1->m_label        != p2->m_label ||
2953                 p1->m_arg_idx      != p2->m_arg_idx ||
2954                 p1->m_pattern_idx  != p2->m_pattern_idx ||
2955                 (p1->m_child == nullptr) != (p2->m_child == nullptr)) {
2956                 return false;
2957             }
2958 
2959             if (p1->m_child == nullptr && p2->m_child == nullptr)
2960                 return true;
2961 
2962             p1 = p1->m_child;
2963             p2 = p2->m_child;
2964         }
2965     }
2966 
2967     typedef ptr_vector<path> paths;
2968 
2969     /**
2970        \brief Inverted path index data structure. See comments at struct path.
2971     */
2972     struct path_tree {
2973         func_decl *    m_label;
2974         unsigned short m_arg_idx;
2975         unsigned short m_ground_arg_idx;
2976         enode *        m_ground_arg;
2977         code_tree *    m_code;
2978         approx_set     m_filter;
2979         path_tree *    m_sibling;
2980         path_tree *    m_first_child;
2981         enode_vector * m_todo; // temporary field used to collect candidates
2982 #ifdef _PROFILE_PATH_TREE
2983         stopwatch      m_watch;
2984         unsigned       m_counter;
2985         unsigned       m_num_eq_visited;
2986         unsigned       m_num_neq_visited;
2987         bool           m_already_displayed; //!< true if the path_tree was already displayed after reaching _PROFILE_PATH_TREE_THRESHOLD
2988 #endif
2989 
path_treeq::path_tree2990         path_tree(path * p, label_hasher & h):
2991             m_label(p->m_label),
2992             m_arg_idx(p->m_arg_idx),
2993             m_ground_arg_idx(p->m_ground_arg_idx),
2994             m_ground_arg(p->m_ground_arg),
2995             m_code(nullptr),
2996             m_filter(h(p->m_label)),
2997             m_sibling(nullptr),
2998             m_first_child(nullptr),
2999             m_todo(nullptr) {
3000 #ifdef _PROFILE_PATH_TREE
3001             m_counter = 0;
3002             m_num_eq_visited = 0;
3003             m_num_neq_visited = 0;
3004             m_already_displayed = false;
3005 #endif
3006         }
3007 
displayq::path_tree3008         void display(std::ostream & out, unsigned indent) {
3009             path_tree * curr = this;
3010             while (curr != nullptr) {
3011                 for (unsigned i = 0; i < indent; i++) out << "  ";
3012                 out << curr->m_label->get_name() << ":" << curr->m_arg_idx;
3013                 if (curr->m_ground_arg)
3014                     out << ":#" << curr->m_ground_arg->get_expr_id() << ":" << curr->m_ground_arg_idx;
3015                 out << "  " << m_filter << " " << m_code;
3016 #ifdef _PROFILE_PATH_TREE
3017                 out << ", counter: " << m_counter << ", num_eq_visited: " << m_num_eq_visited << ", num_neq_visited: " << m_num_neq_visited
3018                     << ", avg. : " << static_cast<double>(m_num_neq_visited)/static_cast<double>(m_num_neq_visited+m_num_eq_visited);
3019 #endif
3020                 out << "\n";
3021                 curr->m_first_child->display(out, indent+1);
3022                 curr = curr->m_sibling;
3023             }
3024         }
3025     };
3026 
3027     typedef std::pair<path_tree *, path_tree *> path_tree_pair;
3028 
3029     // ------------------------------------
3030     //
3031     // Matching Abstract Machine Implementation
3032     //
3033     // ------------------------------------
3034     class mam_impl : public mam {
3035         euf::solver&                ctx;
3036         egraph &                    m_egraph;
3037         ematch &                    m_ematch;
3038         ast_manager &               m;
3039         bool                        m_use_filters;
3040         label_hasher                m_lbl_hasher;
3041         code_tree_manager           m_ct_manager;
3042         compiler                    m_compiler;
3043         interpreter                 m_interpreter;
3044         code_tree_map               m_trees;
3045 
3046         ptr_vector<code_tree>       m_tmp_trees;
3047         ptr_vector<func_decl>       m_tmp_trees_to_delete;
3048         ptr_vector<code_tree>       m_to_match;
3049         unsigned                    m_to_match_head = 0;
3050         typedef std::pair<quantifier *, app *> qp_pair;
3051         svector<qp_pair>            m_new_patterns; // recently added patterns
3052 
3053         // m_is_plbl[f] is true, then when f(c_1, ..., c_n) becomes relevant,
3054         //  for each c_i. c_i->get_root()->lbls().insert(lbl_hash(f))
3055         bool_vector               m_is_plbl;
3056         // m_is_clbl[f] is true, then when n=f(c_1, ..., c_n) becomes relevant,
3057         //  n->get_root()->lbls().insert(lbl_hash(f))
3058         bool_vector               m_is_clbl;    // children labels
3059 
3060         // auxiliary field used to update data-structures...
3061         typedef ptr_vector<func_decl> func_decls;
3062         vector<func_decls>          m_var_parent_labels;
3063 
3064         region &                    m_region;
3065         region                      m_tmp_region;
3066         path_tree_pair              m_pp[APPROX_SET_CAPACITY][APPROX_SET_CAPACITY];
3067         path_tree *                 m_pc[APPROX_SET_CAPACITY][APPROX_SET_CAPACITY];
3068         pool<enode_vector>          m_pool;
3069 
3070         // temporary field used to update path trees.
3071         vector<paths>               m_var_paths;
3072         // temporary field used to collect candidates
3073         ptr_vector<path_tree>       m_todo;
3074 
3075         enode *                     m_root = nullptr;  // temp field
3076         enode *                     m_other = nullptr; // temp field
3077         bool                        m_check_missing_instances = false;
3078 
mk_tmp_vector()3079         enode_vector * mk_tmp_vector() {
3080             enode_vector * r = m_pool.mk();
3081             r->reset();
3082             return r;
3083         }
3084 
recycle(enode_vector * v)3085         void recycle(enode_vector * v) {
3086             m_pool.recycle(v);
3087         }
3088 
add_candidate(code_tree * t,enode * app)3089         void add_candidate(code_tree * t, enode * app) {
3090             if (!t)
3091                 return;
3092             TRACE("q", tout << "candidate " << t << " " << ctx.bpp(app) << "\n";);
3093             if (!t->has_candidates()) {
3094                 ctx.push(push_back_trail<code_tree*, false>(m_to_match));
3095                 m_to_match.push_back(t);
3096             }
3097             t->add_candidate(ctx, app);
3098         }
3099 
add_candidate(enode * app)3100         void add_candidate(enode * app) {
3101             func_decl * lbl = app->get_decl();
3102             add_candidate(m_trees.get_code_tree_for(lbl), app);
3103         }
3104 
is_plbl(func_decl * lbl) const3105         bool is_plbl(func_decl * lbl) const {
3106             unsigned lbl_id = lbl->get_decl_id();
3107             return lbl_id < m_is_plbl.size() && m_is_plbl[lbl_id];
3108         }
is_clbl(func_decl * lbl) const3109         bool is_clbl(func_decl * lbl) const {
3110             unsigned lbl_id = lbl->get_decl_id();
3111             return lbl_id < m_is_clbl.size() && m_is_clbl[lbl_id];
3112         }
3113 
update_lbls(enode * n,unsigned elem)3114         void update_lbls(enode * n, unsigned elem) {
3115             approx_set & r_lbls = n->get_root()->get_lbls();
3116             if (!r_lbls.may_contain(elem)) {
3117                 ctx.push(mam_value_trail<approx_set>(r_lbls));
3118                 r_lbls.insert(elem);
3119             }
3120         }
3121 
update_clbls(func_decl * lbl)3122         void update_clbls(func_decl * lbl) {
3123             unsigned lbl_id = lbl->get_decl_id();
3124             m_is_clbl.reserve(lbl_id+1, false);
3125             TRACE("trigger_bug", tout << "update_clbls: " << lbl->get_name() << " is already clbl: " << m_is_clbl[lbl_id] << "\n";);
3126             TRACE("mam_bug", tout << "update_clbls: " << lbl->get_name() << " is already clbl: " << m_is_clbl[lbl_id] << "\n";);
3127             if (m_is_clbl[lbl_id])
3128                 return;
3129             ctx.push(set_bitvector_trail(m_is_clbl, lbl_id));
3130             SASSERT(m_is_clbl[lbl_id]);
3131             unsigned h = m_lbl_hasher(lbl);
3132             for (enode* app : m_egraph.enodes_of(lbl)) {
3133                 if (ctx.is_relevant(app)) {
3134                     update_lbls(app, h);
3135                     TRACE("mam_bug", tout << "updating labels of: #" << app->get_expr_id() << "\n";
3136                           tout << "new_elem: " << h << "\n";
3137                           tout << "lbls:     " << app->get_lbls() << "\n";
3138                           tout << "r.lbls:   " << app->get_root()->get_lbls() << "\n";);
3139                 }
3140             }
3141         }
3142 
update_children_plbls(enode * app,unsigned char elem)3143         void update_children_plbls(enode * app, unsigned char elem) {
3144             unsigned num_args = app->num_args();
3145             for (unsigned i = 0; i < num_args; i++) {
3146                 enode * c            = app->get_arg(i);
3147                 approx_set & r_plbls = c->get_root()->get_plbls();
3148                 if (!r_plbls.may_contain(elem)) {
3149                     ctx.push(mam_value_trail<approx_set>(r_plbls));
3150                     r_plbls.insert(elem);
3151                     TRACE("trigger_bug", tout << "updating plabels of:\n" << mk_ismt2_pp(c->get_root()->get_expr(), m) << "\n";
3152                           tout << "new_elem: " << static_cast<unsigned>(elem) << "\n";
3153                           tout << "plbls:    " << c->get_root()->get_plbls() << "\n";);
3154                     TRACE("mam_bug", tout << "updating plabels of: #" << c->get_root()->get_expr_id() << "\n";
3155                           tout << "new_elem: " << static_cast<unsigned>(elem) << "\n";
3156                           tout << "plbls:    " << c->get_root()->get_plbls() << "\n";);
3157 
3158                 }
3159             }
3160         }
3161 
update_plbls(func_decl * lbl)3162         void update_plbls(func_decl * lbl) {
3163             unsigned lbl_id = lbl->get_decl_id();
3164             m_is_plbl.reserve(lbl_id+1, false);
3165             TRACE("trigger_bug", tout << "update_plbls: " << lbl->get_name() << " is already plbl: " << m_is_plbl[lbl_id] << ", lbl_id: " << lbl_id << "\n";
3166                   tout << "mam: " << this << "\n";);
3167             TRACE("mam_bug", tout << "update_plbls: " << lbl->get_name() << " is already plbl: " << m_is_plbl[lbl_id] << "\n";);
3168             if (m_is_plbl[lbl_id])
3169                 return;
3170             ctx.push(set_bitvector_trail(m_is_plbl, lbl_id));
3171             SASSERT(m_is_plbl[lbl_id]);
3172             SASSERT(is_plbl(lbl));
3173             unsigned h = m_lbl_hasher(lbl);
3174             for (enode * app : m_egraph.enodes_of(lbl)) {
3175                 if (ctx.is_relevant(app))
3176                     update_children_plbls(app, h);
3177             }
3178         }
3179 
reset_pp_pc()3180         void reset_pp_pc() {
3181             for (unsigned i = 0; i < APPROX_SET_CAPACITY; i++) {
3182                 for (unsigned j = 0; j < APPROX_SET_CAPACITY; j++) {
3183                     m_pp[i][j].first  = 0;
3184                     m_pp[i][j].second = 0;
3185                     m_pc[i][j]        = nullptr;
3186                 }
3187             }
3188         }
3189 
mk_code(quantifier * qa,app * mp,unsigned pat_idx)3190         code_tree * mk_code(quantifier * qa, app * mp, unsigned pat_idx) {
3191             SASSERT(m.is_pattern(mp));
3192             return m_compiler.mk_tree(qa, mp, pat_idx, true);
3193         }
3194 
insert_code(path_tree * t,quantifier * qa,app * mp,unsigned pat_idx)3195         void insert_code(path_tree * t, quantifier * qa, app * mp, unsigned pat_idx) {
3196             SASSERT(m.is_pattern(mp));
3197             m_compiler.insert(t->m_code, qa, mp, pat_idx, false);
3198         }
3199 
mk_path_tree(path * p,quantifier * qa,app * mp)3200         path_tree * mk_path_tree(path * p, quantifier * qa, app * mp) {
3201             SASSERT(m.is_pattern(mp));
3202             SASSERT(p != nullptr);
3203             unsigned pat_idx = p->m_pattern_idx;
3204             path_tree * head = nullptr;
3205             path_tree * curr = nullptr;
3206             path_tree * prev = nullptr;
3207             while (p != nullptr) {
3208                 curr = new (m_region) path_tree(p, m_lbl_hasher);
3209                 if (prev)
3210                     prev->m_first_child = curr;
3211                 if (!head)
3212                     head = curr;
3213                 prev = curr;
3214                 p = p->m_child;
3215             }
3216             curr->m_code = mk_code(qa, mp, pat_idx);
3217             ctx.push(new_obj_trail<code_tree>(curr->m_code));
3218             return head;
3219         }
3220 
insert(path_tree * t,path * p,quantifier * qa,app * mp)3221         void insert(path_tree * t, path * p, quantifier * qa, app * mp) {
3222             SASSERT(m.is_pattern(mp));
3223             path_tree * head = t;
3224             path_tree * prev_sibling = nullptr;
3225             bool found_label = false;
3226             while (t != nullptr) {
3227                 if (t->m_label == p->m_label) {
3228                     found_label = true;
3229                     if (t->m_arg_idx == p->m_arg_idx &&
3230                         t->m_ground_arg == p->m_ground_arg &&
3231                         t->m_ground_arg_idx == p->m_ground_arg_idx
3232                         ) {
3233                         // found compatible node
3234                         if (t->m_first_child == nullptr) {
3235                             if (p->m_child == nullptr) {
3236                                 SASSERT(t->m_code != 0);
3237                                 insert_code(t, qa, mp, p->m_pattern_idx);
3238                             }
3239                             else {
3240                                 ctx.push(set_ptr_trail<path_tree>(t->m_first_child));
3241                                 t->m_first_child = mk_path_tree(p->m_child, qa, mp);
3242                             }
3243                         }
3244                         else {
3245                             if (p->m_child == nullptr) {
3246                                 if (t->m_code) {
3247                                     insert_code(t, qa, mp, p->m_pattern_idx);
3248                                 }
3249                                 else {
3250                                     ctx.push(set_ptr_trail<code_tree>(t->m_code));
3251                                     t->m_code = mk_code(qa, mp, p->m_pattern_idx);
3252                                     ctx.push(new_obj_trail<code_tree>(t->m_code));
3253                                 }
3254                             }
3255                             else {
3256                                 insert(t->m_first_child, p->m_child, qa, mp);
3257                             }
3258                         }
3259                         return;
3260                     }
3261                 }
3262                 prev_sibling = t;
3263                 t = t->m_sibling;
3264             }
3265             ctx.push(set_ptr_trail<path_tree>(prev_sibling->m_sibling));
3266             prev_sibling->m_sibling = mk_path_tree(p, qa, mp);
3267             if (!found_label) {
3268                 ctx.push(value_trail<approx_set>(head->m_filter));
3269                 head->m_filter.insert(m_lbl_hasher(p->m_label));
3270             }
3271         }
3272 
update_pc(unsigned char h1,unsigned char h2,path * p,quantifier * qa,app * mp)3273         void update_pc(unsigned char h1, unsigned char h2, path * p, quantifier * qa, app * mp) {
3274             if (m_pc[h1][h2]) {
3275                 insert(m_pc[h1][h2], p, qa, mp);
3276             }
3277             else {
3278                 ctx.push(set_ptr_trail<path_tree>(m_pc[h1][h2]));
3279                 m_pc[h1][h2] = mk_path_tree(p, qa, mp);
3280             }
3281             TRACE("mam_path_tree_updt",
3282                   tout << "updated path tree:\n";
3283                   m_pc[h1][h2]->display(tout, 2););
3284         }
3285 
update_pp(unsigned char h1,unsigned char h2,path * p1,path * p2,quantifier * qa,app * mp)3286         void update_pp(unsigned char h1, unsigned char h2, path * p1, path * p2, quantifier * qa, app * mp) {
3287             if (h1 == h2) {
3288                 SASSERT(m_pp[h1][h2].second == 0);
3289                 if (m_pp[h1][h2].first) {
3290                     insert(m_pp[h1][h2].first, p1, qa, mp);
3291                     if (!is_equal(p1, p2))
3292                         insert(m_pp[h1][h2].first, p2, qa, mp);
3293                 }
3294                 else {
3295                     ctx.push(set_ptr_trail<path_tree>(m_pp[h1][h2].first));
3296                     m_pp[h1][h2].first = mk_path_tree(p1, qa, mp);
3297                     insert(m_pp[h1][h2].first, p2, qa, mp);
3298                 }
3299             }
3300             else {
3301                 if (h1 > h2) {
3302                     std::swap(h1, h2);
3303                     std::swap(p1, p2);
3304                 }
3305 
3306                 if (m_pp[h1][h2].first) {
3307                     SASSERT(m_pp[h1][h2].second);
3308                     insert(m_pp[h1][h2].first,  p1, qa, mp);
3309                     insert(m_pp[h1][h2].second, p2, qa, mp);
3310                 }
3311                 else {
3312                     SASSERT(m_pp[h1][h2].second == nullptr);
3313                     ctx.push(set_ptr_trail<path_tree>(m_pp[h1][h2].first));
3314                     ctx.push(set_ptr_trail<path_tree>(m_pp[h1][h2].second));
3315                     m_pp[h1][h2].first  = mk_path_tree(p1, qa, mp);
3316                     m_pp[h1][h2].second = mk_path_tree(p2, qa, mp);
3317                 }
3318             }
3319             TRACE("mam_path_tree_updt",
3320                   tout << "updated path tree:\n";
3321                   SASSERT(h1 <= h2);
3322                   m_pp[h1][h2].first->display(tout, 2);
3323                   if (h1 != h2) {
3324                       m_pp[h1][h2].second->display(tout, 2);
3325                   });
3326         }
3327 
update_vars(unsigned short var_id,path * p,quantifier * qa,app * mp)3328         void update_vars(unsigned short var_id, path * p, quantifier * qa, app * mp) {
3329             if (var_id >= qa->get_num_decls())
3330                 return;
3331             paths & var_paths = m_var_paths[var_id];
3332             bool found = false;
3333             for (path* curr_path : var_paths) {
3334                 if (is_equal(p, curr_path))
3335                     found = true;
3336                 func_decl * lbl1 = curr_path->m_label;
3337                 func_decl * lbl2 = p->m_label;
3338                 update_plbls(lbl1);
3339                 update_plbls(lbl2);
3340                 update_pp(m_lbl_hasher(lbl1), m_lbl_hasher(lbl2), curr_path, p, qa, mp);
3341             }
3342             if (!found)
3343                 var_paths.push_back(p);
3344         }
3345 
get_ground_arg(app * pat,quantifier * qa,unsigned & pos)3346         enode * get_ground_arg(app * pat, quantifier * qa, unsigned & pos) {
3347             pos = 0;
3348             unsigned num_args = pat->get_num_args();
3349             for (unsigned i = 0; i < num_args; i++) {
3350                 expr * arg = pat->get_arg(i);
3351                 if (is_ground(arg)) {
3352                     pos = i;
3353                     return m_egraph.find(arg);
3354                 }
3355             }
3356             return nullptr;
3357         }
3358 
3359         /**
3360            \brief Update inverted path index with respect to pattern pat in the egraph of path p.
3361            pat is a sub-expression of mp->get_arg(pat_idx). mp is a multi-pattern of qa.
3362            If p == 0, then mp->get_arg(pat_idx) == pat.
3363         */
update_filters(app * pat,path * p,quantifier * qa,app * mp,unsigned pat_idx)3364         void update_filters(app * pat, path * p, quantifier * qa, app * mp, unsigned pat_idx) {
3365             unsigned short num_args = pat->get_num_args();
3366             unsigned ground_arg_pos = 0;
3367             enode * ground_arg      = get_ground_arg(pat, qa, ground_arg_pos);
3368             func_decl * plbl        = pat->get_decl();
3369             for (unsigned short i = 0; i < num_args; i++) {
3370                 expr * child = pat->get_arg(i);
3371                 path * new_path = new (m_tmp_region) path(plbl, i, ground_arg_pos, ground_arg, pat_idx, p);
3372 
3373                 if (is_var(child)) {
3374                     update_vars(to_var(child)->get_idx(), new_path, qa, mp);
3375                     continue;
3376                 }
3377 
3378                 SASSERT(is_app(child));
3379 
3380                 if (to_app(child)->is_ground()) {
3381                     enode * n = m_egraph.find(child);
3382                     update_plbls(plbl);
3383                     if (!n->has_lbl_hash())
3384                         m_egraph.set_lbl_hash(n);
3385                     TRACE("mam_bug",
3386                           tout << "updating pc labels " << plbl->get_name() << " " <<
3387                           static_cast<unsigned>(n->get_lbl_hash()) << "\n";
3388                           tout << "#" << n->get_expr_id() << " " << n->get_root()->get_lbls() << "\n";
3389                           tout << "relevant: " << ctx.is_relevant(n) << "\n";);
3390                     update_pc(m_lbl_hasher(plbl), n->get_lbl_hash(), new_path, qa, mp);
3391                     continue;
3392                 }
3393 
3394                 func_decl * clbl = to_app(child)->get_decl();
3395                 TRACE("mam_bug", tout << "updating pc labels " << plbl->get_name() << " " << clbl->get_name() << "\n";);
3396                 update_plbls(plbl);
3397                 update_clbls(clbl);
3398                 update_pc(m_lbl_hasher(plbl), m_lbl_hasher(clbl), new_path, qa, mp);
3399                 update_filters(to_app(child), new_path, qa, mp, pat_idx);
3400             }
3401         }
3402 
3403         /**
3404            \brief Update inverted path index.
3405         */
update_filters(quantifier * qa,app * mp)3406         void update_filters(quantifier * qa, app * mp) {
3407             TRACE("mam_bug", tout << "updating filters using:\n" << mk_pp(mp, m) << "\n";);
3408             unsigned num_vars = qa->get_num_decls();
3409             if (num_vars >= m_var_paths.size())
3410                 m_var_paths.resize(num_vars+1);
3411             for (unsigned i = 0; i <= num_vars; i++)
3412                 m_var_paths[i].reset();
3413             m_tmp_region.reset();
3414             // Given a multi-pattern (p_1, ..., p_n)
3415             // We need to update the filters using patterns:
3416             //  (p_1, p_2, ..., p_n)
3417             //  (p_2, p_1, ..., p_n)
3418             //  ...
3419             //  (p_n, p_2, ..., p_1)
3420             unsigned num_patterns = mp->get_num_args();
3421             for (unsigned i = 0; i < num_patterns; i++) {
3422                 app * pat = to_app(mp->get_arg(i));
3423                 update_filters(pat, nullptr, qa, mp, i);
3424             }
3425         }
3426 
display_filter_info(std::ostream & out)3427         void display_filter_info(std::ostream & out) {
3428             for (unsigned i = 0; i < APPROX_SET_CAPACITY; i++) {
3429                 for (unsigned j = 0; j < APPROX_SET_CAPACITY; j++) {
3430                     if (m_pp[i][j].first) {
3431                         out << "pp[" << i << "][" << j << "]:\n";
3432                         m_pp[i][j].first->display(out, 1);
3433                         if (i != j) {
3434                             m_pp[i][j].second->display(out, 1);
3435                         }
3436                     }
3437                     if (m_pc[i][j]) {
3438                         out << "pc[" << i << "][" << j << "]:\n";
3439                         m_pc[i][j]->display(out, 1);
3440                     }
3441                 }
3442             }
3443         }
3444 
3445         /**
3446            \brief Check equality modulo the equality m_r1 = m_r2
3447         */
is_eq(enode * n1,enode * n2)3448         bool is_eq(enode * n1, enode * n2) {
3449             return
3450                 n1->get_root() == n2->get_root() ||
3451                 (n1->get_root() == m_other && n2->get_root() == m_root) ||
3452                 (n2->get_root() == m_other && n1->get_root() == m_root);
3453         }
3454 
3455         /**
3456            \brief Collect new E-matching candidates using the inverted path index t.
3457         */
collect_parents(enode * r,path_tree * t)3458         void collect_parents(enode * r, path_tree * t) {
3459             TRACE("mam", tout << ctx.bpp(r) << " " << t << "\n";);
3460             if (t == nullptr)
3461                 return;
3462 #ifdef _PROFILE_PATH_TREE
3463             t->m_watch.start();
3464 #endif
3465 
3466             m_todo.reset();
3467             enode_vector * to_unmark  = mk_tmp_vector();
3468             enode_vector * to_unmark2 = mk_tmp_vector();
3469             SASSERT(to_unmark->empty());
3470             SASSERT(to_unmark2->empty());
3471             t->m_todo = mk_tmp_vector();
3472             t->m_todo->push_back(r);
3473             m_todo.push_back(t);
3474             unsigned head = 0;
3475             while (head < m_todo.size()) {
3476                 path_tree    * t    = m_todo[head];
3477 #ifdef _PROFILE_PATH_TREE
3478                 t->m_counter++;
3479 #endif
3480                 TRACE("mam_path_tree",
3481                       tout << "processing:\n";
3482                       t->display(tout, 2););
3483                 enode_vector * v    = t->m_todo;
3484                 approx_set & filter = t->m_filter;
3485                 head++;
3486 
3487 #ifdef _PROFILE_PATH_TREE
3488                 static unsigned counter  = 0;
3489                 static unsigned total_sz = 0;
3490                 static unsigned max_sz   = 0;
3491                 counter++;
3492                 total_sz += v->size();
3493                 if (v->size() > max_sz)
3494                     max_sz = v->size();
3495                 if (counter % 100000 == 0)
3496                     std::cout << "Avg. " << static_cast<double>(total_sz)/static_cast<double>(counter) << ", Max. " << max_sz << "\n";
3497 #endif
3498 
3499                 for (enode* n : *v) {
3500                     // Two different kinds of mark are used:
3501                     // - enode mark field:  it is used to mark the already processed parents.
3502                     // - enode mark2 field: it is used to mark the roots already added to be processed in the next level.
3503                     //
3504                     // In a previous version of Z3, the "enode mark field" was used to mark both cases. This is incorrect,
3505                     // and Z3 may fail to find potential new matches.
3506                     //
3507                     // The file regression\acu.sx exposed this problem.
3508                     enode * curr_child = n->get_root();
3509 
3510                     if (m_use_filters && curr_child->get_plbls().empty_intersection(filter))
3511                         continue;
3512 
3513 #ifdef _PROFILE_PATH_TREE
3514                     static unsigned counter2  = 0;
3515                     static unsigned total_sz2 = 0;
3516                     static unsigned max_sz2   = 0;
3517                     counter2++;
3518                     total_sz2 += curr_child->get_num_parents();
3519                     if (curr_child->get_num_parents() > max_sz2)
3520                         max_sz2 = curr_child->get_num_parents();
3521                     if (counter2 % 100000 == 0)
3522                         std::cout << "Avg2. " << static_cast<double>(total_sz2)/static_cast<double>(counter2) << ", Max2. " << max_sz2 << "\n";
3523 #endif
3524 
3525                     TRACE("mam_path_tree", tout << "processing: #" << curr_child->get_expr_id() << "\n";);
3526                     for (enode* curr_parent : euf::enode_parents(curr_child)) {
3527 #ifdef _PROFILE_PATH_TREE
3528                         if (curr_parent->is_equality())
3529                             t->m_num_eq_visited++;
3530                         else
3531                             t->m_num_neq_visited++;
3532 #endif
3533                         // Remark: equality is never in the inverted path index.
3534                         if (curr_parent->is_equality())
3535                             continue;
3536                         func_decl * lbl            = curr_parent->get_decl();
3537                         bool is_flat_assoc         = lbl->is_flat_associative();
3538                         enode * curr_parent_root   = curr_parent->get_root();
3539                         enode * curr_parent_cg     = curr_parent->get_cg();
3540                         TRACE("mam_path_tree", tout << "processing parent:\n" << mk_pp(curr_parent->get_expr(), m) << "\n";);
3541                         TRACE("mam_path_tree", tout << "parent is marked: " << curr_parent->is_marked1() << "\n";);
3542                         if (filter.may_contain(m_lbl_hasher(lbl)) &&
3543                             !curr_parent->is_marked1() &&
3544                             (curr_parent_cg == curr_parent || !is_eq(curr_parent_cg, curr_parent_root)) &&
3545                             ctx.is_relevant(curr_parent)
3546                             ) {
3547                             path_tree * curr_tree = t;
3548                             while (curr_tree) {
3549                                 if (curr_tree->m_label == lbl &&
3550                                     // Starting at Z3 3.0, some associative operators (e.g., + and *) are represented using n-ary applications.
3551                                     // In this cases, we say the declarations is is_flat_assoc().
3552                                     // The MAM was implemented in Z3 2.0 when the following invariant was true:
3553                                     //    For every application f(x_1, ..., x_n) of a function symbol f, n = f->get_arity().
3554                                     // Starting at Z3 3.0, this is only true if !f->is_flat_associative().
3555                                     // Thus, we need the extra checks.
3556                                     curr_tree->m_arg_idx < curr_parent->num_args() &&
3557                                     (!is_flat_assoc || curr_tree->m_ground_arg_idx < curr_parent->num_args())) {
3558                                     enode * curr_parent_child = curr_parent->get_arg(curr_tree->m_arg_idx)->get_root();
3559                                     if (// Filter 1. the curr_child is equal to child of the current parent.
3560                                         curr_child == curr_parent_child &&
3561                                         // Filter 2. m_ground_arg_idx is a valid argument
3562                                         curr_tree->m_ground_arg_idx < curr_parent->num_args() &&
3563                                         // Filter 3.
3564                                         (
3565                                          // curr_tree has no support for the filter based on a ground argument.
3566                                          curr_tree->m_ground_arg == nullptr ||
3567                                          // checks whether the child of the parent is equal to the expected ground argument.
3568                                          is_eq(curr_tree->m_ground_arg, curr_parent->get_arg(curr_tree->m_ground_arg_idx))
3569                                          )) {
3570                                         if (curr_tree->m_code) {
3571                                             TRACE("mam_path_tree", tout << "found candidate " << expr_ref(curr_parent->get_expr(), m) << "\n";);
3572                                             add_candidate(curr_tree->m_code, curr_parent);
3573                                         }
3574                                         if (curr_tree->m_first_child) {
3575                                             path_tree * child = curr_tree->m_first_child;
3576                                             if (child->m_todo == nullptr) {
3577                                                 child->m_todo = mk_tmp_vector();
3578                                                 m_todo.push_back(child);
3579                                             }
3580                                             if (!curr_parent_root->is_marked2()) {
3581                                                 child->m_todo->push_back(curr_parent_root);
3582                                             }
3583                                         }
3584                                     }
3585                                 }
3586                                 curr_tree = curr_tree->m_sibling;
3587                             }
3588                             curr_parent->mark1();
3589                             to_unmark->push_back(curr_parent);
3590                             if (!curr_parent_root->is_marked2()) {
3591                                 curr_parent_root->mark2();
3592                                 to_unmark2->push_back(curr_parent_root);
3593                             }
3594                         }
3595                     }
3596                 }
3597                 recycle(t->m_todo);
3598                 t->m_todo = nullptr;
3599                 // remove both marks.
3600                 for (enode* n : *to_unmark) n->unmark1();
3601                 for (enode* n : *to_unmark2) n->unmark2();
3602                 to_unmark->reset();
3603                 to_unmark2->reset();
3604             }
3605             recycle(to_unmark);
3606             recycle(to_unmark2);
3607 #ifdef _PROFILE_PATH_TREE
3608             t->m_watch.stop();
3609             if (t->m_counter % _PROFILE_PATH_TREE_THRESHOLD == 0) {
3610                 std::cout << "EXPENSIVE " << t->m_watch.get_seconds() << " secs, counter: " << t->m_counter << "\n";
3611                 t->display(std::cout, 0);
3612                 t->m_already_displayed = true;
3613             }
3614 #endif
3615         }
3616 
process_pp(enode * r1,enode * r2)3617         void process_pp(enode * r1, enode * r2) {
3618             approx_set & plbls1 = r1->get_plbls();
3619             approx_set & plbls2 = r2->get_plbls();
3620             TRACE("incremental_matcher", tout << "pp: plbls1: " << plbls1 << ", plbls2: " << plbls2 << "\n";);
3621             TRACE("mam_info", tout << "pp: " << plbls1.size() * plbls2.size() << "\n";);
3622             if (!plbls1.empty() && !plbls2.empty()) {
3623                 for (unsigned plbl1 : plbls1) {
3624                     if (!m.inc()) {
3625                         break;
3626                     }
3627                     SASSERT(plbls1.may_contain(plbl1));
3628                     for (unsigned plbl2 : plbls2) {
3629                         SASSERT(plbls2.may_contain(plbl2));
3630                         unsigned n_plbl1 = plbl1;
3631                         unsigned n_plbl2 = plbl2;
3632                         enode *  n_r1    = r1;
3633                         enode *  n_r2    = r2;
3634                         if (n_plbl1 > n_plbl2) {
3635                             std::swap(n_plbl1, n_plbl2);
3636                             std::swap(n_r1,    n_r2);
3637                         }
3638                         if (n_plbl1 == n_plbl2) {
3639                             SASSERT(m_pp[n_plbl1][n_plbl2].second == 0);
3640                             if (n_r1->num_parents() <= n_r2->num_parents())
3641                                 collect_parents(n_r1, m_pp[n_plbl1][n_plbl2].first);
3642                             else
3643                                 collect_parents(n_r2, m_pp[n_plbl1][n_plbl2].first);
3644                         }
3645                         else {
3646                             SASSERT(n_plbl1 < n_plbl2);
3647                             if (n_r1->num_parents() <= n_r2->num_parents())
3648                                 collect_parents(n_r1, m_pp[n_plbl1][n_plbl2].first);
3649                             else
3650                                 collect_parents(n_r2, m_pp[n_plbl1][n_plbl2].second);
3651                         }
3652                     }
3653                 }
3654             }
3655         }
3656 
process_pc(enode * r1,enode * r2)3657         void process_pc(enode * r1, enode * r2) {
3658             approx_set & plbls = r1->get_plbls();
3659             approx_set & clbls = r2->get_lbls();
3660             if (!plbls.empty() && !clbls.empty()) {
3661                 for (unsigned plbl1 : plbls) {
3662                     if (!m.inc()) {
3663                         break;
3664                     }
3665                     SASSERT(plbls.may_contain(plbl1));
3666                     for (unsigned lbl2 : clbls) {
3667                         SASSERT(clbls.may_contain(lbl2));
3668                         collect_parents(r1, m_pc[plbl1][lbl2]);
3669                     }
3670                 }
3671             }
3672         }
3673 
3674         unsigned m_new_patterns_qhead = 0;
3675 
propagate_new_patterns()3676         void propagate_new_patterns() {
3677             if (m_new_patterns_qhead >= m_new_patterns.size())
3678                 return;
3679             ctx.push(value_trail<unsigned>(m_new_patterns_qhead));
3680 
3681             TRACE("mam_new_pat", tout << "matching new patterns:\n";);
3682             m_tmp_trees_to_delete.reset();
3683             for (; m_new_patterns_qhead < m_new_patterns.size(); ++m_new_patterns_qhead) {
3684                 if (!m.inc())
3685                     break;
3686                 auto [qa, mp] = m_new_patterns[m_new_patterns_qhead];
3687 
3688                 SASSERT(m.is_pattern(mp));
3689                 app *        p     = to_app(mp->get_arg(0));
3690                 func_decl *  lbl   = p->get_decl();
3691                 if (!m_egraph.enodes_of(lbl).empty()) {
3692                     unsigned lbl_id = lbl->get_decl_id();
3693                     m_tmp_trees.reserve(lbl_id+1, 0);
3694                     if (m_tmp_trees[lbl_id] == 0) {
3695                         m_tmp_trees[lbl_id] = m_compiler.mk_tree(qa, mp, 0, false);
3696                         m_tmp_trees_to_delete.push_back(lbl);
3697                     }
3698                     else {
3699                         m_compiler.insert(m_tmp_trees[lbl_id], qa, mp, 0, true);
3700                     }
3701                 }
3702             }
3703 
3704             for (func_decl * lbl : m_tmp_trees_to_delete) {
3705                 unsigned    lbl_id   = lbl->get_decl_id();
3706                 code_tree * tmp_tree = m_tmp_trees[lbl_id];
3707                 SASSERT(tmp_tree != 0);
3708                 SASSERT(!m_egraph.enodes_of(lbl).empty());
3709                 m_interpreter.init(tmp_tree);
3710                 auto& nodes = m_egraph.enodes_of(lbl);
3711                 for (unsigned i = 0; i < nodes.size(); ++i) {
3712                     enode* app = nodes[i];
3713                     if (ctx.is_relevant(app))
3714                         m_interpreter.execute_core(tmp_tree, app);
3715                 }
3716                 m_tmp_trees[lbl_id] = nullptr;
3717                 dealloc(tmp_tree);
3718             }
3719         }
3720 
3721     public:
mam_impl(euf::solver & ctx,ematch & ematch,bool use_filters)3722         mam_impl(euf::solver & ctx, ematch& ematch, bool use_filters):
3723             ctx(ctx),
3724             m_egraph(ctx.get_egraph()),
3725             m_ematch(ematch),
3726             m(ctx.get_manager()),
3727             m_use_filters(use_filters),
3728             m_ct_manager(m_lbl_hasher, ctx),
3729             m_compiler(m_egraph, m_ct_manager, m_lbl_hasher, use_filters),
3730             m_interpreter(ctx, *this, use_filters),
3731             m_trees(m, m_compiler, ctx),
3732             m_region(ctx.get_region()) {
3733             DEBUG_CODE(m_trees.set_egraph(&m_egraph););
3734             DEBUG_CODE(m_check_missing_instances = false;);
3735             reset_pp_pc();
3736         }
3737 
~mam_impl()3738         ~mam_impl() override {
3739         }
3740 
add_pattern(quantifier * qa,app * mp)3741         void add_pattern(quantifier * qa, app * mp) override {
3742             SASSERT(m.is_pattern(mp));
3743             TRACE("trigger_bug", tout << "adding pattern\n" << mk_ismt2_pp(qa, m) << "\n" << mk_ismt2_pp(mp, m) << "\n";);
3744             TRACE("mam_bug", tout << "adding pattern\n" << mk_pp(qa, m) << "\n" << mk_pp(mp, m) << "\n";);
3745             // Z3 checks if a pattern is ground or not before solving.
3746             // Ground patterns are discarded.
3747             // However, the simplifier may turn a non-ground pattern into a ground one.
3748             // So, we should check it again here.
3749             for (expr* arg : *mp)
3750                 if (is_ground(arg) || has_quantifiers(arg))
3751                     return; // ignore multi-pattern containing ground pattern.
3752             update_filters(qa, mp);
3753             m_new_patterns.push_back(qp_pair(qa, mp));
3754             ctx.push(push_back_trail<qp_pair, false>(m_new_patterns));
3755             // The matching abstract machine implements incremental
3756             // e-matching. So, for a multi-pattern [ p_1, ..., p_n ],
3757             // we have to make n insertions. In the i-th insertion,
3758             // the pattern p_i is assumed to be the first one.
3759             for (unsigned i = 0; i < mp->get_num_args(); i++)
3760                 m_trees.add_pattern(qa, mp, i);
3761         }
3762 
reset()3763         void reset() override {
3764             m_trees.reset();
3765             m_is_plbl.reset();
3766             m_is_clbl.reset();
3767             reset_pp_pc();
3768             m_tmp_region.reset();
3769         }
3770 
display(std::ostream & out)3771         std::ostream& display(std::ostream& out) override {
3772             m_lbl_hasher.display(out << "mam:\n");
3773             for (auto* t : m_trees)
3774                 if (t)
3775                     t->display(out);
3776             return out;
3777         }
3778 
propagate_to_match()3779         void propagate_to_match() {
3780             if (m_to_match_head >= m_to_match.size())
3781                 return;
3782             ctx.push(value_trail<unsigned>(m_to_match_head));
3783             for (; m_to_match_head < m_to_match.size(); ++m_to_match_head)
3784                 m_interpreter.execute(m_to_match[m_to_match_head]);
3785         }
3786 
propagate()3787         void propagate() override {
3788             TRACE("trigger_bug", tout << "match\n"; display(tout););
3789             propagate_to_match();
3790             propagate_new_patterns();
3791         }
3792 
rematch(bool use_irrelevant)3793         void rematch(bool use_irrelevant) override {
3794             unsigned lbl = 0;
3795             for (auto * t : m_trees) {
3796                 if (t) {
3797                     m_interpreter.init(t);
3798                     func_decl * lbl = t->get_root_lbl();
3799                     for (enode * curr : m_egraph.enodes_of(lbl)) {
3800                         if (use_irrelevant || ctx.is_relevant(curr))
3801                             m_interpreter.execute_core(t, curr);
3802                     }
3803                 }
3804                 ++lbl;
3805             }
3806         }
3807 
check_missing_instances()3808         bool check_missing_instances() override {
3809             TRACE("missing_instance", tout << "checking for missing instances...\n";);
3810             flet<bool> l(m_check_missing_instances, true);
3811             rematch(false);
3812             return true;
3813         }
3814 
on_match(quantifier * qa,app * pat,unsigned num_bindings,enode * const * bindings,unsigned max_generation)3815         void on_match(quantifier * qa, app * pat, unsigned num_bindings, enode * const * bindings, unsigned max_generation) override {
3816             TRACE("trigger_bug", tout << "found match " << mk_pp(qa, m) << "\n";);
3817             unsigned min_gen = 0, max_gen = 0;
3818             m_interpreter.get_min_max_top_generation(min_gen, max_gen);
3819             m_ematch.on_binding(qa, pat, bindings, max_generation, min_gen, max_gen);
3820         }
3821 
3822         // This method is invoked when n becomes relevant.
3823         // If lazy == true, then n is not added to the list of
3824         // candidate enodes for matching. That is, the method just updates the lbls.
add_node(enode * n,bool lazy)3825         void add_node(enode * n, bool lazy) override {
3826             TRACE("trigger_bug", tout << "relevant_eh:\n" << mk_ismt2_pp(n->get_expr(), m) << "\n";
3827                   tout << "mam: " << this << "\n";);
3828             TRACE("mam", tout << "relevant_eh: #" << n->get_expr_id() << "\n";);
3829             if (n->has_lbl_hash())
3830                 update_lbls(n, n->get_lbl_hash());
3831 
3832             if (n->num_args() > 0) {
3833                 func_decl * lbl = n->get_decl();
3834                 unsigned h      = m_lbl_hasher(lbl);
3835                 TRACE("trigger_bug", tout << "lbl: " << lbl->get_name() << " is_clbl(lbl): " << is_clbl(lbl)
3836                       << ", is_plbl(lbl): " << is_plbl(lbl) << ", h: " << h << "\n";
3837                       tout << "lbl_id: " << lbl->get_decl_id() << "\n";);
3838                 if (is_clbl(lbl))
3839                     update_lbls(n, h);
3840                 if (is_plbl(lbl))
3841                     update_children_plbls(n, h);
3842                 TRACE("mam_bug", tout << "adding relevant candidate:\n" << mk_ll_pp(n->get_expr(), m) << "\n";);
3843                 if (!lazy)
3844                     add_candidate(n);
3845             }
3846         }
3847 
can_propagate() const3848         bool can_propagate() const override {
3849             return !m_to_match.empty() || !m_new_patterns.empty();
3850         }
3851 
on_merge(enode * root,enode * other)3852         void on_merge(enode * root, enode * other) override {
3853             flet<enode *> l1(m_other, other);
3854             flet<enode *> l2(m_root, root);
3855 
3856             TRACE("mam", tout << "on_merge: #" << other->get_expr_id() << " #" << root->get_expr_id() << "\n";);
3857             TRACE("mam_inc_bug_detail", m_egraph.display(tout););
3858             TRACE("mam_inc_bug",
3859                   tout << "before:\n#" << other->get_expr_id() << " #" << root->get_expr_id() << "\n";
3860                   tout << "other.lbls:  " << other->get_lbls() << "\n";
3861                   tout << "root.lbls:  " << root->get_lbls() << "\n";
3862                   tout << "other.plbls: " << other->get_plbls() << "\n";
3863                   tout << "root.plbls: " << root->get_plbls() << "\n";);
3864 
3865             process_pc(other, root);
3866             process_pc(root, other);
3867             process_pp(other, root);
3868 
3869             approx_set   other_plbls = other->get_plbls();
3870             approx_set & root_plbls = root->get_plbls();
3871             approx_set   other_lbls  = other->get_lbls();
3872             approx_set & root_lbls  = root->get_lbls();
3873 
3874             ctx.push(mam_value_trail<approx_set>(root_lbls));
3875             ctx.push(mam_value_trail<approx_set>(root_plbls));
3876             root_lbls  |= other_lbls;
3877             root_plbls |= other_plbls;
3878             TRACE("mam_inc_bug",
3879                   tout << "after:\n";
3880                   tout << "other.lbls:  " << other->get_lbls() << "\n";
3881                   tout << "root.lbls:  " << root->get_lbls() << "\n";
3882                   tout << "other.plbls: " << other->get_plbls() << "\n";
3883                   tout << "root.plbls: " << root->get_plbls() << "\n";);
3884             SASSERT(approx_subset(other->get_plbls(), root->get_plbls()));
3885             SASSERT(approx_subset(other->get_lbls(), root->get_lbls()));
3886         }
3887     };
3888 
ground_subterms(expr * e,ptr_vector<app> & ground)3889     void mam::ground_subterms(expr* e, ptr_vector<app>& ground) {
3890         ground.reset();
3891         expr_fast_mark1 mark;
3892         ptr_buffer<app> todo;
3893         if (is_app(e))
3894             todo.push_back(to_app(e));
3895         while (!todo.empty()) {
3896             app * n = todo.back();
3897             todo.pop_back();
3898             if (mark.is_marked(n))
3899                 continue;
3900             mark.mark(n);
3901             if (n->is_ground())
3902                 ground.push_back(n);
3903             else {
3904                 for (expr* arg : *n)
3905                     if (is_app(arg))
3906                         todo.push_back(to_app(arg));
3907             }
3908         }
3909     }
3910 
mk(euf::solver & ctx,ematch & em)3911     mam* mam::mk(euf::solver& ctx, ematch& em) {
3912         return alloc(mam_impl, ctx, em, true);
3913     }
3914 
3915 }
3916 
3917