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