1 /* Part of SWI-Prolog
2
3 Author: Jan Wielemaker
4 E-mail: J.Wielemaker@vu.nl
5 WWW: http://www.swi-prolog.org
6 Copyright (c) 2017-2020, VU University Amsterdam
7 CWI, Amsterdam
8 All rights reserved.
9
10 Redistribution and use in source and binary forms, with or without
11 modification, are permitted provided that the following conditions
12 are met:
13
14 1. Redistributions of source code must retain the above copyright
15 notice, this list of conditions and the following disclaimer.
16
17 2. Redistributions in binary form must reproduce the above copyright
18 notice, this list of conditions and the following disclaimer in
19 the documentation and/or other materials provided with the
20 distribution.
21
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #include "pl-incl.h"
37 #include "pl-comp.h"
38 #include "pl-arith.h"
39 #include "pl-tabling.h"
40 #include "pl-copyterm.h"
41 #include "pl-wrap.h"
42 #include "pl-event.h"
43 #include "pl-allocpool.h"
44
45 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
46 We provide two answer completion strategies:
47
48 - Eager AC: #define O_AC_EAGER 1
49 Complete each component fully before continuing to the next.
50 - Lazy AC: #undef O_AC_EAGER
51 Only complete the leader of a component. This is what XSB is
52 doing.
53 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
54
55 #define O_AC_EAGER 1
56 // #undef O_AC_EAGER
57
58 #define record_t fastheap_term *
59 #define PL_record(t) term_to_fastheap(t PASS_LD)
60 #define PL_recorded(r, t) put_fastheap(r, t PASS_LD)
61 #define PL_erase(r) free_fastheap(r)
62
63 #define SINDEX_MAX 32
64
65 typedef struct sindex_key
66 { unsigned argn;
67 unsigned key;
68 } sindex_key;
69
70 typedef struct suspension
71 { record_t term; /* dependency/5 term with TNOT flag */
72 record_t instance; /* Filter for call subsumption */
73 sindex_key *keys; /* Index to find filter candidates */
74 } suspension;
75
76 typedef struct answer
77 { trie_node *node; /* Point into answer trie */
78 } answer;
79
80 typedef struct
81 { worklist *list; /* worklist we enumerate */
82 cluster *acp; /* Current answer cluster */
83 cluster *scp; /* Current suspension cluster */
84 answer *answer; /* Current answer */
85 int acp_index; /* Index in anser cluster */
86 struct
87 { suspension *base;
88 suspension *top;
89 suspension *here;
90 } suspensions;
91 int keys_inited; /* #Initialized keys */
92 sindex_key keys[SINDEX_MAX]; /* suspension matching */
93 } wkl_step_state;
94
95 static int destroy_answer_trie(trie *atrie);
96 static void free_worklist(worklist *wl);
97 static void clean_worklist(worklist *wl);
98 static void destroy_depending_worklists(worklist *wl0);
99 static void free_worklist_set(worklist_set *wls, int freewl);
100 static void add_global_worklist(worklist *wl);
101 static tbl_component *tbl_create_subcomponent(trie *leader ARG_LD);
102 static worklist *tbl_add_worklist(trie *atrie, tbl_component *scc);
103 static int wl_has_work(const worklist *wl);
104 static cluster *new_answer_cluster(worklist *wl, answer *first);
105 static void wkl_append_left(worklist *wl, cluster *c);
106 static int wkl_add_answer(worklist *wl, trie_node *node ARG_LD);
107 static int wkl_mode_add_answer(worklist *wl, term_t answer,
108 term_t delays ARG_LD);
109 static int tbl_put_moded_args(term_t t, trie_node *node ARG_LD);
110 static void del_child_component(tbl_component *parent, tbl_component *child);
111 static void free_components_set(component_set *cs, int destroy);
112 static int unify_skeleton(trie *trie, term_t wrapper, term_t skel ARG_LD);
113 #ifdef O_DEBUG
114 static void print_worklist(const char *prefix, worklist *wl);
115 static void print_delay(const char *msg,
116 trie_node *variant, trie_node *answer);
117 static void print_answer(const char *msg, trie_node *answer);
118 static int put_delay_info(term_t t, trie_node *answer);
119 static void print_answer_table(trie *atrie, const char *msg, ...);
120 #endif
121 static int simplify_component(tbl_component *scc);
122 static void idg_destroy(idg_node *node);
123 static void idg_reset(idg_node *node);
124 static int idg_init_variant(trie *atrie, Definition def, term_t variant
125 ARG_LD);
126 static void reeval_complete(trie *atrie);
127 static void reset_reevaluation(trie *atrie);
128 static int unify_component_status(term_t t, tbl_component *scc ARG_LD);
129 static int simplify_answer(worklist *wl, trie_node *answer, int truth);
130 static int table_is_incomplete(trie *trie);
131 static int idg_add_edge(trie *atrie, trie *ctrie ARG_LD);
132 static int idg_set_current_wl(term_t wlref ARG_LD);
133 #ifdef O_PLMT
134 static int claim_answer_table(trie *atrie, atom_t *clrefp,
135 int flags ARG_LD);
136 #endif
137 static atom_t tripwire_answers_for_subgoal(worklist *wl ARG_LD);
138 static int generalise_answer_substitution(term_t spec, term_t gen ARG_LD);
139 static int add_answer_count_restraint(void);
140 static int add_radial_restraint(void);
141 static int tbl_wl_tripwire(worklist *wl, atom_t action, atom_t wire);
142 static int tbl_pred_tripwire(Definition def, atom_t action, atom_t wire);
143
144 #define WL_IS_SPECIAL(wl) (((intptr_t)(wl)) & 0x1)
145 #define WL_IS_WORKLIST(wl) ((wl) && !WL_IS_SPECIAL(wl))
146
147 #define WL_GROUND ((worklist *)0x21)
148 #define WL_DYNAMIC ((worklist *)0x41)
149
150 #define WLFS_FREE_NONE 0x0000
151 #define WLFS_KEEP_COMPLETE 0x0001
152 #define WLFS_FREE_ALL 0x0002
153 #define WLFS_DISCARD_INCOMPLETE 0x0004
154
155 #define DV_DELETED ((trie*)0x1)
156 #define DL_UNDEFINED ((delay_info*)0x1)
157
158 #define DL_IS_DELAY_LIST(dl) ((dl) && (dl) != DL_UNDEFINED)
159
160
161 #ifdef O_PLMT
162 #define LOCK_SHARED_TABLE(t) countingMutexLock(&GD->tabling.mutex);
163 #define UNLOCK_SHARED_TABLE(t) countingMutexUnlock(&GD->tabling.mutex);
164
165 static inline void
drop_trie(trie * atrie)166 drop_trie(trie *atrie)
167 {
168 #ifdef O_DEBUG
169 int mytid = PL_thread_self();
170 assert(mytid == atrie->tid);
171 int rc = COMPARE_AND_SWAP_INT(&atrie->tid, mytid, 0);
172 assert(rc);
173 #else
174 atrie->tid = 0;
175 #endif
176 }
177
178 static inline void
take_trie(trie * atrie,int tid)179 take_trie(trie *atrie, int tid)
180 { assert(atrie->data.worklist != WL_DYNAMIC);
181 #ifdef O_DEBUG
182 int rc = COMPARE_AND_SWAP_INT(&atrie->tid, 0, tid);
183 assert(rc);
184 #else
185 atrie->tid = tid;
186 #endif
187 }
188
189 #define COMPLETE_WORKLIST(__trie, __code) \
190 do \
191 { LOCK_SHARED_TABLE(__trie); \
192 if ( __trie->tid ) \
193 { DEBUG(0, assert(__trie->tid == PL_thread_self())); \
194 } else \
195 { take_trie(__trie, PL_thread_self()); \
196 } \
197 __code; \
198 drop_trie(__trie); \
199 cv_broadcast(&GD->tabling.cvar); \
200 UNLOCK_SHARED_TABLE(__trie); \
201 } while(0)
202
203 static int wait_for_table_to_complete(trie *atrie);
204 static int table_needs_work(trie *atrie);
205 static void register_waiting(int tid, trie *atrie);
206 static void unregister_waiting(int tid, trie *atrie);
207 static int is_deadlock(trie *atrie);
208
209 #else /*O_PLMT*/
210
211 #define COMPLETE_WORKLIST(__trie, __code) \
212 do { __code; } while(0)
213
214 #endif /*O_PLMT*/
215
216
217 /*******************************
218 * COMPONENTS *
219 *******************************/
220
221 static tbl_component *
new_component(void)222 new_component(void)
223 { tbl_component *c = PL_malloc(sizeof(*c));
224
225 memset(c, 0, sizeof(*c));
226 c->magic = COMPONENT_MAGIC;
227
228 return c;
229 }
230
231 #define FC_DESTROY 0x0001
232 #define FC_CHILD 0x0002
233
234 static void
push_component_set(segstack * stack,component_set * cs)235 push_component_set(segstack *stack, component_set *cs)
236 { tbl_component **bp = baseBuffer(&cs->members, tbl_component*);
237 tbl_component **tp = topBuffer(&cs->members, tbl_component*);
238 typedef struct tbl_component *Component;
239
240 for(; bp < tp; bp++)
241 { if ( !pushSegStack(stack, *bp, Component) )
242 outOfCore();
243 }
244
245 discardBuffer(&cs->members);
246 PL_free(cs);
247 }
248
249 static void
free_component(tbl_component * c,int flags)250 free_component(tbl_component *c, int flags)
251 { GET_LD
252 assert(c->magic == COMPONENT_MAGIC);
253 c->magic = 0;
254 segstack stack;
255 typedef struct tbl_component *Component;
256 Component buf[100];
257
258 if ( c == LD->tabling.component )
259 { LD->tabling.component = c->parent;
260 if ( !c->parent && LD->tabling.has_scheduling_component )
261 LD->tabling.has_scheduling_component = FALSE;
262 }
263
264 initSegStack(&stack, sizeof(Component), sizeof(buf), buf);
265 if ( !pushSegStack(&stack, c, Component) )
266 outOfCore();
267
268 while( popSegStack(&stack, &c, Component) )
269 { if ( !(flags&FC_CHILD) && c->parent )
270 del_child_component(c->parent, c);
271 flags |= FC_CHILD; /* only for the first */
272 if ( c->worklist )
273 free_worklist_set(c->worklist, WLFS_FREE_NONE);
274 if ( c->delay_worklists )
275 free_worklist_set(c->delay_worklists, WLFS_FREE_NONE);
276 if ( c->created_worklists )
277 free_worklist_set(c->created_worklists, WLFS_FREE_ALL);
278 if ( c->children )
279 push_component_set(&stack, c->children);
280 if ( c->merged )
281 push_component_set(&stack, c->merged);
282
283 PL_free(c);
284 }
285
286 clearSegStack(&stack);
287 }
288
289
290 static void
add_child_component(tbl_component * parent,tbl_component * child)291 add_child_component(tbl_component *parent, tbl_component *child)
292 { component_set *cs;
293
294 if ( !(cs=parent->children) )
295 { cs = PL_malloc(sizeof(*cs));
296 initBuffer(&cs->members);
297 parent->children = cs;
298 }
299
300 addBuffer(&cs->members, child, tbl_component*);
301 }
302
303 static void
del_child_component(tbl_component * parent,tbl_component * child)304 del_child_component(tbl_component *parent, tbl_component *child)
305 { component_set *cs;
306
307 if ( (cs=parent->children) ) /* can be merged */
308 { tbl_component **bp = baseBuffer(&cs->members, tbl_component*);
309 tbl_component **tp = topBuffer(&cs->members, tbl_component*);
310
311 for(; *bp != child && bp < tp; bp++)
312 ;
313 if ( bp < tp )
314 { memmove(bp, bp+1, (tp-bp-1)*sizeof(*bp));
315 (void)popBuffer(&cs->members, tbl_component*);
316 }
317 }
318 }
319
320 static void
free_components_set(component_set * cs,int flags)321 free_components_set(component_set *cs, int flags)
322 { if ( (flags & FC_DESTROY) )
323 { tbl_component **bp = baseBuffer(&cs->members, tbl_component*);
324 tbl_component **tp = topBuffer(&cs->members, tbl_component*);
325
326 for(; bp < tp; bp++)
327 free_component(*bp, flags);
328 }
329
330 discardBuffer(&cs->members);
331 PL_free(cs);
332 }
333
334 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
335 Merge all subcomponets of c into c. The properties of the subcomponets
336 are destroyed and .status is set to SCC_MERGED.
337 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
338
339 static void merge_children(tbl_component *c, tbl_component *m);
340 static void merge_one_component(tbl_component *c, tbl_component *m);
341 static void wls_set_component(worklist_set *wls, size_t size0, tbl_component *c);
342
343 static size_t
wls_size(const worklist_set * wls)344 wls_size(const worklist_set *wls)
345 { return wls ? entriesBuffer(&wls->members, worklist*) : 0;
346 }
347
348
349 static void
merge_component(tbl_component * c)350 merge_component(tbl_component *c)
351 { size_t s_global = wls_size(c->worklist);
352 size_t s_created = wls_size(c->created_worklists);
353
354 if ( c->children )
355 merge_children(c, c);
356
357 wls_set_component(c->worklist, s_global, c);
358 wls_set_component(c->created_worklists, s_created, c);
359
360 DEBUG(MSG_TABLING_MERGE,
361 Sdprintf("Grown SCC %p from %zd to %zd worklists\n",
362 c, s_created, wls_size(c->created_worklists)));
363 }
364
365
366 static void
wl_set_component(worklist * wl,tbl_component * c)367 wl_set_component(worklist *wl, tbl_component *c)
368 { wl->component = c;
369 wl->executing = FALSE;
370 if ( !wl->in_global_wl && wl_has_work(wl) )
371 add_global_worklist(wl);
372 if ( wl->negative )
373 { DEBUG(MSG_TABLING_MERGE,
374 Sdprintf("Merging negative literal into SCC %zd\n",
375 pointerToInt(c)));
376 if ( c->neg_status == SCC_NEG_NONE )
377 c->neg_status = SCC_NEG_DELAY;
378 else if ( c->neg_status == SCC_NEG_SIMPLIFY )
379 c->neg_status = SCC_NEG_DELAY;
380 }
381 }
382
383
384 static void
wls_set_component(worklist_set * wls,size_t size0,tbl_component * c)385 wls_set_component(worklist_set *wls, size_t size0, tbl_component *c)
386 { worklist **base = baseBuffer(&wls->members, worklist*);
387 worklist **top = topBuffer(&wls->members, worklist*);
388
389 #ifdef O_DEBUG
390 size_t old = 0;
391 for(; base < top; base++, old++)
392 { if ( old < size0 )
393 assert((*base)->component == c);
394 else
395 wl_set_component(*base, c);
396 }
397 #else
398 base += size0; /* skip old ones */
399 for(; base < top; base++)
400 wl_set_component(*base, c);
401 #endif
402 }
403
404 static void
merge_component_sets(component_set ** into,component_set ** from)405 merge_component_sets(component_set **into, component_set **from)
406 { typedef tbl_component* Component;
407
408 if ( *into && *from )
409 { tbl_component **s = baseBuffer(&(*from)->members, tbl_component*);
410 size_t cnt = entriesBuffer(&(*from)->members, tbl_component*);
411 Buffer b = &(*into)->members;
412
413 addMultipleBuffer(b, s, cnt, Component);
414 free_components_set(*from, 0);
415 *from = NULL;
416 } else if ( *from )
417 { *into = *from;
418 *from = NULL;
419 }
420 }
421
422
423 /* Merge all components of cs into c */
424
425 static void
merge_children(tbl_component * c,tbl_component * m)426 merge_children(tbl_component *c, tbl_component *m)
427 { component_set *cs;
428
429 if ( (cs=m->children) )
430 { tbl_component **bp = baseBuffer(&cs->members, tbl_component*);
431 tbl_component **tp = topBuffer(&cs->members, tbl_component*);
432
433 for( ; bp < tp; bp++)
434 merge_one_component(c, *bp);
435
436 merge_component_sets(&m->merged, &m->children);
437 }
438 }
439
440
441 static void
merge_worklists(worklist_set ** into,worklist_set ** from)442 merge_worklists(worklist_set **into, worklist_set **from)
443 { typedef worklist* Worklist;
444
445 if ( *into && *from )
446 { worklist **s = baseBuffer(&(*from)->members, worklist*);
447 size_t cnt = entriesBuffer(&(*from)->members, worklist*);
448 Buffer b = &(*into)->members;
449
450 addMultipleBuffer(b, s, cnt, Worklist);
451 free_worklist_set(*from, WLFS_FREE_NONE);
452 *from = NULL;
453 } else if ( *from )
454 { *into = *from;
455 *from = NULL;
456 }
457 }
458
459
460 static void
merge_one_component(tbl_component * c,tbl_component * m)461 merge_one_component(tbl_component *c, tbl_component *m)
462 { assert(m->magic == COMPONENT_MAGIC);
463
464 if ( m->status != SCC_ACTIVE )
465 return;
466
467 merge_children(c, m);
468
469 DEBUG(MSG_TABLING_MERGE,
470 Sdprintf("Merged %zd into %zd, %zd worklists, %zd created\n",
471 pointerToInt(m), pointerToInt(c),
472 entriesBuffer(&m->worklist->members, worklist*),
473 entriesBuffer(&m->created_worklists->members, worklist*)));
474
475 merge_worklists(&c->worklist, &m->worklist);
476 merge_worklists(&c->created_worklists, &m->created_worklists);
477 merge_worklists(&c->delay_worklists, &m->delay_worklists);
478
479 m->status = SCC_MERGED;
480 }
481
482 /*******************************
483 * WORKLISTS *
484 *******************************/
485
486 static worklist_set *
new_worklist_set(worklist * wl)487 new_worklist_set(worklist *wl)
488 { worklist_set *wls = PL_malloc(sizeof(*wls));
489
490 initBuffer(&wls->members);
491 addBuffer(&wls->members, wl, worklist*);
492
493 return wls;
494 }
495
496
497 static void
add_global_worklist(worklist * wl)498 add_global_worklist(worklist *wl)
499 { tbl_component *c = wl->component;
500 worklist_set *wls;
501
502 if ( !(wls=c->worklist) )
503 c->worklist = new_worklist_set(wl);
504 else
505 addBuffer(&wls->members, wl, worklist*);
506
507 wl->in_global_wl = TRUE;
508 }
509
510
511 static void
add_delay_worklist(worklist * wl)512 add_delay_worklist(worklist *wl)
513 { tbl_component *c = wl->component;
514 worklist_set *wls;
515
516 if ( !(wls=c->delay_worklists) )
517 c->delay_worklists = new_worklist_set(wl);
518 else
519 addBuffer(&wls->members, wl, worklist*);
520 }
521
522
523 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
524 Normal completion is done. There may be worklists that are suspended
525 using negation_suspend/3. We wake these up by adding a new answer
526 cluster with a NULL node.
527 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
528
529 static worklist *
negative_worklist(tbl_component * scc ARG_LD)530 negative_worklist(tbl_component *scc ARG_LD)
531 { if ( scc->delay_worklists )
532 { while( !isEmptyBuffer(&scc->delay_worklists->members) )
533 { worklist *wl = popBuffer(&scc->delay_worklists->members, worklist*);
534
535 if ( !wl->has_answers ) /* we have an unconditional answers, so no delay */
536 { cluster *c;
537 answer ans = {NULL};
538
539 wl->neg_delayed = TRUE;
540 DEBUG(MSG_TABLING_NEG,
541 { term_t t = PL_new_term_ref();
542 unify_trie_term(wl->table->data.variant, NULL, t PASS_LD);
543 Sdprintf("Resuming negative node with delay list %zd: ",
544 pointerToInt(wl));
545 PL_write_term(Serror, t, 999, PL_WRT_NEWLINE);
546 });
547
548 c = new_answer_cluster(wl, &ans);
549 wkl_append_left(wl, c);
550 if ( !wl->riac )
551 wl->riac = c;
552
553 return wl;
554 }
555 }
556
557 scc->neg_status = SCC_NEG_SIMPLIFY;
558 }
559
560 #ifdef O_DEBUG
561 if ( !DEBUGGING(TABLING_NO_SIMPLIFY) )
562 { simplify_component(scc);
563 } else
564 { Sdprintf("Skipping (TABLING_NO_SIMPLIFY) simplifiation for SCC %zd\n",
565 pointerToInt(scc));
566 }
567 #else
568 simplify_component(scc);
569 #endif
570
571 return NULL;
572 }
573
574
575 static int
wl_has_work(const worklist * wl)576 wl_has_work(const worklist *wl)
577 { return wl->riac && wl->riac->next;
578 }
579
580 static worklist *
pop_worklist(tbl_component * c ARG_LD)581 pop_worklist(tbl_component *c ARG_LD)
582 { worklist_set *wls = c->worklist;
583
584 if ( wls )
585 { while( !isEmptyBuffer(&wls->members) )
586 { worklist *wl = popBuffer(&wls->members, worklist*);
587 wl->in_global_wl = FALSE;
588
589 if ( wl_has_work(wl) )
590 return wl;
591 }
592 }
593
594 return NULL;
595 }
596
597
598 static void
reset_global_worklist(tbl_component * c)599 reset_global_worklist(tbl_component *c)
600 { worklist_set *wls;
601
602 if ( c && (wls = c->worklist) )
603 { c->worklist = NULL;
604 free_worklist_set(wls, WLFS_FREE_NONE);
605 }
606 }
607
608
609 static void
add_newly_created_worklist(worklist * wl)610 add_newly_created_worklist(worklist *wl)
611 { tbl_component *c = wl->component;
612 worklist_set *wls;
613
614 if ( !(wls=c->created_worklists) )
615 { wls = c->created_worklists = PL_malloc(sizeof(*c->created_worklists));
616 initBuffer(&wls->members);
617 }
618
619 addBuffer(&wls->members, wl, worklist*);
620 }
621
622 static void
reset_newly_created_worklists(tbl_component * c,int flags)623 reset_newly_created_worklists(tbl_component *c, int flags)
624 { worklist_set *wls;
625
626 if ( c && (wls = c->created_worklists) )
627 { c->created_worklists = NULL;
628 free_worklist_set(wls, flags);
629 }
630 }
631
632 static size_t
worklist_set_to_array(worklist_set * wls,worklist *** wlp)633 worklist_set_to_array(worklist_set *wls, worklist ***wlp)
634 { if ( wls )
635 { *wlp = (worklist**)baseBuffer(&wls->members, worklist*);
636 return entriesBuffer(&wls->members, worklist*);
637 } else
638 { *wlp = NULL;
639 return 0;
640 }
641 }
642
643 static void
free_worklist_set(worklist_set * wls,int freewl)644 free_worklist_set(worklist_set *wls, int freewl)
645 { if ( freewl )
646 { worklist **wlp = (worklist**)baseBuffer(&wls->members, worklist*);
647 size_t i, nwpl = entriesBuffer(&wls->members, worklist*);
648
649 for(i=0; i<nwpl; i++)
650 { worklist *wl = wlp[i];
651
652 if ( (freewl&WLFS_FREE_ALL) || true(wl->table, TRIE_COMPLETE) )
653 { free_worklist(wl);
654 } else if ( (freewl&WLFS_DISCARD_INCOMPLETE) )
655 { trie *atrie = wl->table;
656
657 if ( atrie->data.IDG && atrie->data.IDG->reevaluating )
658 { atrie->data.worklist = NULL;
659 free_worklist(wl);
660 reset_reevaluation(atrie);
661 } else
662 { if ( table_is_incomplete(atrie) )
663 { DEBUG(MSG_TABLING_EXCEPTION,
664 print_answer_table(atrie, "Deleting incomplete answer table"));
665 destroy_answer_trie(atrie);
666 }
667 }
668 }
669 }
670 }
671
672 discardBuffer(&wls->members);
673 PL_free(wls);
674 }
675
676
677 /*******************************
678 * TABLE DELAY LISTS *
679 *******************************/
680
681 #ifdef O_DEBUG
682 static void print_dl_dependency(trie *from, trie *to);
683 #endif
684
685 static inline trie_node *
REC_DELAY(record_t r)686 REC_DELAY(record_t r)
687 { return (trie_node*)(((uintptr_t)r)|0x1);
688 }
689
690 static inline record_t
UNREC_DELAY(trie_node * r)691 UNREC_DELAY(trie_node *r)
692 { return (record_t)(((uintptr_t)r)&~(uintptr_t)1);
693 }
694
695 static int
IS_REC_DELAY(trie_node * r)696 IS_REC_DELAY(trie_node *r)
697 { return (uintptr_t)r & 0x1;
698 }
699
700 int
answer_is_conditional(trie_node * answer)701 answer_is_conditional(trie_node *answer)
702 { delay_info *di;
703
704 return ( (di=answer->data.delayinfo) &&
705 (di == DL_UNDEFINED || !isEmptyBuffer(&di->delay_sets)) );
706 }
707
708 static delay_info *
answer_delay_info(worklist * wl,trie_node * answer,int create)709 answer_delay_info(worklist *wl, trie_node *answer, int create)
710 { delay_info *di;
711
712 if ( (di=answer->data.delayinfo) )
713 { return di;
714 } else if ( !create )
715 { return NULL;
716 } else if ( (di=malloc(sizeof(*di))) )
717 { di->variant = wl->table->data.variant;
718 di->has_share_records = FALSE;
719 initBuffer(&di->delay_sets);
720 initBuffer(&di->delays);
721 answer->data.delayinfo = di;
722 wl->undefined++;
723
724 return di;
725 } else
726 { return NULL;
727 }
728 }
729
730
731 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
732 destroy_delay_info(trie_node *answer, int propagate) removes and
733 deallocates the delay info that may be associated to `answer`. If
734 `propagate` is TRUE, it also removes the backpointers to `answer` from
735 the worklist `delay` buffer of worklists that are references from the
736 delay elements.
737 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
738
739 static void
delete_answer(Buffer ab,trie_node * answer)740 delete_answer(Buffer ab, trie_node *answer)
741 { trie_node **ap = baseBuffer(ab, trie_node*);
742 trie_node **ep = topBuffer(ab, trie_node*);
743 trie_node **op = ap;
744
745 for(; ap < ep; ap++)
746 { if ( *ap != answer )
747 *op++ = *ap;
748 }
749
750 ab->top = (char*)op;
751 }
752
753 static void
destroy_delay_info(trie * atrie,trie_node * answer,int propagate)754 destroy_delay_info(trie *atrie, trie_node *answer, int propagate)
755 { delay_info *di = answer->data.delayinfo;
756
757 if ( DL_IS_DELAY_LIST(di) )
758 { answer->data.delayinfo = NULL;
759 if ( di->has_share_records )
760 { delay *d = baseBuffer(&di->delays, delay);
761 delay *z = topBuffer(&di->delays, delay);
762
763 for(; d < z; d++) /* keep a flag to see whether we have these */
764 { if ( IS_REC_DELAY(d->answer) )
765 PL_erase(UNREC_DELAY(d->answer));
766 }
767 }
768
769 if ( propagate )
770 { delay *db = baseBuffer(&di->delays, delay);
771 delay *dt = topBuffer(&di->delays, delay);
772 delay *d;
773
774 for(d=db; d < dt; d++)
775 { trie *at;
776
777 if ( (at=d->variant) && at != DV_DELETED )
778 { worklist *wl = at->data.worklist;
779
780 if ( WL_IS_WORKLIST(wl) && !isEmptyBuffer(&wl->delays) )
781 { DEBUG(MSG_TABLING_VTRIE_DEPENDENCIES,
782 { GET_LD
783 term_t tab = PL_new_term_ref();
784 term_t dep = PL_new_term_ref();
785 unify_trie_term(atrie->data.variant, NULL, tab PASS_LD);
786 unify_trie_term(wl->table->data.variant, NULL, dep PASS_LD);
787 Sdprintf(" Deleting answer from table ");
788 PL_write_term(Serror, tab, 999, 0);
789 Sdprintf(" <-- ");
790 PL_write_term(Serror, dep, 999, PL_WRT_NEWLINE);
791 });
792
793 delete_answer(&wl->delays, answer);
794 }
795 }
796 }
797 }
798
799 discardBuffer(&di->delay_sets);
800 discardBuffer(&di->delays);
801 free(di);
802 }
803 }
804
805
806 static void
answer_set_general_undefined(trie * atrie,trie_node * answer)807 answer_set_general_undefined(trie *atrie, trie_node *answer)
808 { destroy_delay_info(atrie, answer, TRUE);
809 answer->data.delayinfo = DL_UNDEFINED;
810 }
811
812 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
813 Delete the undefined answers that depend on this worklist
814 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
815
816 static void
delete_depending_answers(worklist * wl,TmpBuffer wlset)817 delete_depending_answers(worklist *wl, TmpBuffer wlset)
818 { DEBUG(MSG_TABLING_VTRIE_DEPENDENCIES,
819 { GET_LD
820 term_t t = PL_new_term_ref();
821 unify_trie_term(wl->table->data.variant, NULL, t PASS_LD);
822 Sdprintf("delete_depending_answers for ");
823 PL_write_term(Serror, t, 999, PL_WRT_NEWLINE);
824 });
825
826 while( !isEmptyBuffer(&wl->delays) )
827 { trie_node **top = topBuffer(&wl->delays, trie_node*);
828 trie_node *answer = top[-1];
829 delay_info *di;
830
831 assert(wl->depend_abolish);
832
833 if ( DL_IS_DELAY_LIST(di=answer->data.delayinfo) )
834 { trie *at = symbol_trie(di->variant->value);
835 worklist *dwl;
836
837 if ( WL_IS_WORKLIST((dwl=at->data.worklist)) &&
838 !dwl->depend_abolish )
839 { assert(dwl != wl);
840 assert(dwl->table != wl->table);
841 DEBUG(MSG_TABLING_VTRIE_DEPENDENCIES,
842 print_dl_dependency(wl->table, dwl->table));
843 dwl->depend_abolish = TRUE;
844 addBuffer(wlset, dwl, worklist *);
845 }
846 answer_set_general_undefined(at, answer);
847 } else
848 { (void)popBufferP(&wl->delays, trie_node *);
849 }
850 }
851
852 discardBuffer(&wl->delays);
853 initBuffer(&wl->delays);
854 }
855
856
857 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
858 destroy_depending_worklists(worklist *wl) destroys worklists that have
859 answers pointing to this worklist and its answers.
860 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
861
862 static void
destroy_depending_worklists(worklist * wl0)863 destroy_depending_worklists(worklist *wl0)
864 { tmp_buffer wlset;
865
866 initBuffer(&wlset);
867 wl0->depend_abolish = TRUE;
868 delete_depending_answers(wl0, &wlset);
869 while( !isEmptyBuffer(&wlset) )
870 { worklist *wl = popBuffer(&wlset, worklist *);
871
872 delete_depending_answers(wl, &wlset);
873 destroy_answer_trie(wl->table);
874 }
875 discardBuffer(&wlset);
876 }
877
878
879 static void *
destroy_delay_info_answer(trie_node * answer,void * ctx)880 destroy_delay_info_answer(trie_node *answer, void *ctx)
881 { trie *atrie = ctx;
882
883 if ( DL_IS_DELAY_LIST(answer->data.delayinfo) )
884 { answer_set_general_undefined(atrie, answer);
885 }
886
887 return NULL;
888 }
889
890
891 static void
destroy_delay_info_worklist(worklist * wl)892 destroy_delay_info_worklist(worklist *wl)
893 { map_trie_node(&wl->table->root, destroy_delay_info_answer, wl->table);
894 }
895
896
897 static delay_set *
create_delay_set(delay_info * di)898 create_delay_set(delay_info *di)
899 { delay_set *ds;
900
901 if ( di &&
902 (ds=allocFromBuffer(&di->delay_sets, sizeof(*ds))) )
903 { ds->offset = entriesBuffer(&di->delays, delay);
904 ds->size = 0;
905 ds->active = 0;
906
907 return ds;
908 }
909
910 return NULL;
911 }
912
913 static int
add_to_delay_set(delay_info * di,delay_set * ds,trie * variant,trie_node * answer)914 add_to_delay_set(delay_info *di, delay_set *ds,
915 trie *variant, trie_node *answer)
916 { delay *d;
917
918 if ( (d=allocFromBuffer(&di->delays, sizeof(*d))) )
919 { d->variant = variant;
920 d->answer = answer;
921 if ( variant )
922 ds->active++;
923 return ++ds->size;
924 } else
925 { return 0;
926 }
927 }
928
929
930 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
931 Register a conditional answer with the worklist associated with the
932 variant that contributes to the condition. The argument is a node in the
933 variant table. If this node is not associated with a worklist it is a
934 completed node and we are just propagating an undefined literal.
935 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
936
937 static int
add_to_wl_delays(trie * at,trie_node * answer,worklist * wla)938 add_to_wl_delays(trie *at, trie_node *answer, worklist *wla)
939 { worklist *wl = at->data.worklist;
940
941 if ( WL_IS_WORKLIST(wl) )
942 { DEBUG(MSG_TABLING_SIMPLIFY,
943 { GET_LD
944 term_t t = PL_new_term_ref();
945 term_t v = PL_new_term_ref();
946 term_t vt = PL_new_term_ref();
947 unify_trie_term(at->data.variant, NULL, t PASS_LD);
948 unify_trie_term(answer, NULL, v PASS_LD);
949 unify_trie_term(wla->table->data.variant, NULL, vt PASS_LD);
950 Sdprintf("Adding propagation to worklist for ");
951 PL_write_term(Serror, t, 999, 0);
952 Sdprintf(" to answer ");
953 PL_write_term(Serror, v, 999, 0);
954 Sdprintf(" of table ");
955 PL_write_term(Serror, vt, 999, PL_WRT_NEWLINE);
956 });
957 addBuffer(&wl->delays, answer, trie_node *);
958 } else
959 { /* see '$tbl_table_complete_all'/3 */
960 DEBUG(MSG_TABLING_VTRIE_DEPENDENCIES,
961 print_dl_dependency(wla->table, at));
962 assert(0);
963 }
964
965 return TRUE;
966 }
967
968
969 static void
add_to_wl_pos_undefined(worklist * wl,trie_node * answer)970 add_to_wl_pos_undefined(worklist *wl, trie_node *answer)
971 { addBuffer(&wl->pos_undefined, answer, trie_node *);
972 }
973
974
975 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
976 Simplify a delay set after adding ds. This pops the new delay set if it
977 is a duplicate.
978 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
979
980 static int
equal_delay(const delay * a,const delay * b)981 equal_delay(const delay *a, const delay *b)
982 { return ( a->variant == b->variant &&
983 a->answer == b->answer );
984 }
985
986 static int
equal_delay_set(const delay * delays,const delay_set * a,const delay_set * b)987 equal_delay_set(const delay *delays, const delay_set *a, const delay_set *b)
988 { if ( a->size == b->size )
989 { unsigned int ia = a->offset;
990 unsigned int ib = b->offset;
991 unsigned int ea = a->offset + a->size;
992
993 for( ; ia < ea; ia++, ib++)
994 { if ( !equal_delay(&delays[ia], &delays[ib]) )
995 return FALSE;
996 }
997
998 return TRUE;
999 }
1000
1001 return FALSE;
1002 }
1003
1004
1005 static int
simplify_delay_set(delay_info * di,delay_set * ds)1006 simplify_delay_set(delay_info *di, delay_set *ds)
1007 { delay *delays = baseBuffer(&di->delays, delay);
1008 delay_set *base = baseBuffer(&di->delay_sets, delay_set);
1009
1010 for(; base < ds; base++)
1011 { if ( equal_delay_set(delays, base, ds) )
1012 { seekBuffer(&di->delays, ds->offset, delay);
1013 popBufferP(&di->delay_sets, delay_set);
1014 return TRUE;
1015 }
1016 }
1017
1018 return FALSE;
1019 }
1020
1021
1022 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1023 The delay list (`delays`) is a list of delayed positive and negative
1024 literals:
1025
1026 - Negative literal ---> answer-trie ptr
1027 - Positive literal ---> answer-trie ptr + answer-node ptr
1028
1029 TBD: If we make an answer unconditional, should we propagate this? Note
1030 that the worklists still point to this answer. That should trigger
1031 propagation?
1032
1033 FIXME: delete variable sharing record
1034 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1035
1036 typedef enum
1037 { UDL_FALSE = 0,
1038 UDL_TRUE,
1039 UDL_COMPLETE
1040 } udl_status;
1041
1042 static int
update_delay_list(worklist * wl,trie_node * answer,term_t skel,term_t delays ARG_LD)1043 update_delay_list(worklist *wl, trie_node *answer,
1044 term_t skel, term_t delays ARG_LD)
1045 { Word ldlp;
1046 Word gdlp;
1047
1048 retry:
1049 deRef2(valTermRef(LD->tabling.delay_list), gdlp);
1050 gdlp = argTermP(*gdlp, 0);
1051 deRef(gdlp);
1052 deRef2(valTermRef(delays), ldlp);
1053
1054 if ( isNil(*ldlp) && isNil(*gdlp) )
1055 { delay_info *di;
1056
1057 if ( (di=answer->data.delayinfo) )
1058 { destroy_delay_info(wl->table, answer, TRUE);
1059 answer->data.delayinfo = NULL;
1060 wl->undefined--;
1061 DEBUG(MSG_TABLING_SIMPLIFY,
1062 Sdprintf("Unconditional answer after conditional\n"));
1063 simplify_answer(wl, answer, TRUE);
1064 }
1065 /* Incremental tabling */
1066 if ( wl->table->data.IDG && wl->table->data.IDG->reevaluating )
1067 { if ( false(answer, TN_IDG_UNCONDITIONAL) )
1068 { set(answer, TN_IDG_UNCONDITIONAL);
1069 simplify_answer(wl, answer, TRUE);
1070 }
1071 }
1072
1073 DEBUG(TABLING_NO_EARLY_COMPLETION,
1074 return UDL_TRUE);
1075
1076 if ( wl->ground ) /* early completion */
1077 return UDL_COMPLETE;
1078 return UDL_TRUE;
1079 } else
1080 { delay_info *di = answer_delay_info(wl, answer, TRUE);
1081 delay_set *ds = create_delay_set(di);
1082 size_t count;
1083 Word tail;
1084
1085 count = skip_list(ldlp, &tail PASS_LD);
1086 if ( !isNil(*tail) )
1087 return PL_type_error("delay_list", delays);
1088 count += skip_list(gdlp, &tail PASS_LD);
1089 if ( !isNil(*tail) )
1090 return PL_type_error("delay_list", LD->tabling.delay_list);
1091
1092 if ( !hasGlobalSpace(count+2) )
1093 { int rc;
1094
1095 if ( (rc = ensureGlobalSpace(count+2, ALLOW_GC)) != TRUE )
1096 return raiseStackOverflow(rc);
1097 goto retry;
1098 }
1099
1100 if ( ds )
1101 { int pass = 0;
1102 Word dlp;
1103 Word tshare = NULL;
1104 size_t nshare = 0;
1105
1106 for(pass = 0; pass <= 1; pass++)
1107 { dlp = pass ? ldlp : gdlp;
1108
1109 for(; !isNil(*dlp); dlp = TailList(dlp))
1110 { Word h;
1111 trie *at;
1112 trie_node *an;
1113
1114 deRef(dlp);
1115 if ( !isList(*dlp) )
1116 { PL_type_error("list", delays);
1117 return UDL_FALSE;
1118 }
1119
1120 h = HeadList(dlp);
1121 deRef(h);
1122 if ( isAtom(*h) ) /* Answer trie symbol */
1123 { if ( (at=symbol_trie(*h)) )
1124 { an = NULL;
1125 } else /* deleted trie or 'undefined' */
1126 { undef:
1127 destroy_delay_info(wl->table, answer, TRUE);
1128 answer->data.delayinfo = DL_UNDEFINED;
1129 return UDL_TRUE;
1130 }
1131 } else if ( isTerm(*h) )
1132 { Functor f = valueTerm(*h);
1133 Word p;
1134
1135 if ( f->definition == FUNCTOR_plus2 )
1136 { deRef2(&f->arguments[0], p);
1137 assert(isAtom(*p));
1138 if ( !(at=symbol_trie(*p)) )
1139 { goto undef;
1140 }
1141 deRef2(&f->arguments[1], p);
1142 if ( isInteger(*p) )
1143 {
1144 #if SIZEOF_VOIDP == 8
1145 assert(isTaggedInt(*p));
1146 an = intToPointer(valInt(*p));
1147 #else
1148 if ( isTaggedInt(*p) )
1149 { an = intToPointer(valInt(*p));
1150 } else
1151 { assert(isBignum(*p));
1152 an = intToPointer(valBignum(*p));
1153 }
1154 #endif
1155 assert(is_ground_trie_node(an));
1156 } else
1157 { int rc;
1158
1159 /* ground__LD() returns first var */
1160 if ( ground__LD(p PASS_LD) != NULL )
1161 { if ( !tshare )
1162 { tshare = allocGlobalNoShift(3);
1163 assert(tshare);
1164 tshare[1] = linkVal(valTermRef(skel));
1165 tshare[2] = *p;
1166 nshare = 1;
1167 } else
1168 { Word s = allocGlobalNoShift(1);
1169 assert(s);
1170 s[0] = *p;
1171 nshare++;
1172 }
1173 }
1174
1175 if ( true(at, TRIE_ISMAP) ) /* answer subsumption */
1176 { Word rp;
1177 trie_node *root;
1178
1179 assert(hasFunctor(*p, FUNCTOR_divide2));
1180 rp = argTermP(*p, 0);
1181 rc = trie_lookup(at, NULL, &root, rp+0, TRUE, NULL PASS_LD);
1182 if ( rc == TRUE )
1183 { rc = trie_lookup(at, root, &an, rp+1, TRUE, NULL PASS_LD);
1184 }
1185 } else
1186 { rc = trie_lookup(at, NULL, &an, p, TRUE, NULL PASS_LD);
1187 }
1188
1189 if ( rc == TRUE )
1190 { // TBD: can we immediately simplify if this already has a value?
1191 DEBUG(MSG_TABLING_DELAY_VAR,
1192 print_delay("Waiting for instantiated",
1193 at->data.variant, an));
1194 // TBD: at->data.worklist?
1195 add_to_wl_pos_undefined(wl, an);
1196 } else
1197 { return trie_trie_error(rc, at);
1198 }
1199 }
1200 } else
1201 { PL_type_error("delay_list", delays);
1202 return UDL_FALSE;
1203 }
1204 } else
1205 { PL_type_error("delay_list", delays);
1206 return UDL_FALSE;
1207 }
1208
1209 assert(at->magic == TRIE_MAGIC);
1210
1211 if ( !add_to_delay_set(di, ds, at, an) )
1212 goto nomem;
1213 } /*for list*/
1214 } /*for pass*/
1215
1216 if ( tshare )
1217 { word w = consPtr(tshare, TAG_COMPOUND|STG_GLOBAL);
1218 record_t r;
1219
1220 tshare[0] = PL_new_functor(ATOM_v, nshare+1);
1221 r = PL_record(pushWordAsTermRef(&w));
1222 popTermRef();
1223 if ( r )
1224 { if ( !add_to_delay_set(di, ds, NULL, REC_DELAY(r)) )
1225 goto nomem;
1226 di->has_share_records = TRUE;
1227 } else
1228 { return UDL_FALSE;
1229 }
1230 }
1231
1232 if ( tshare || !simplify_delay_set(di, ds) )
1233 { delay *d = baseBuffer(&di->delays, delay);
1234 unsigned int i, e = ds->offset+ds->size;
1235
1236 for(i=ds->offset; i<e; i++)
1237 { if ( d[i].variant )
1238 { if ( !add_to_wl_delays(d[i].variant, answer, wl) )
1239 return UDL_FALSE;
1240 }
1241 }
1242 }
1243
1244 return UDL_TRUE;
1245 }
1246
1247 nomem:
1248 PL_resource_error("memory");
1249 return UDL_FALSE;
1250 }
1251 }
1252
1253
1254 static void
delay_sets(delay_info * di,delay_set ** base,delay_set ** top)1255 delay_sets(delay_info *di, delay_set **base, delay_set **top)
1256 { *base = baseBuffer(&di->delay_sets, delay_set);
1257 *top = topBuffer(&di->delay_sets, delay_set);
1258 }
1259
1260 static void
get_delay_set(delay_info * di,delay_set * set,delay ** base,delay ** top)1261 get_delay_set(delay_info *di, delay_set *set, delay **base, delay **top)
1262 { *base = baseBuffer(&di->delays, delay) + set->offset;
1263 *top = (*base) + set->size;
1264 }
1265
1266
1267 term_t
init_delay_list(void)1268 init_delay_list(void)
1269 { GET_LD
1270 term_t t = PL_new_term_ref();
1271 Word p = allocGlobal(2);
1272
1273 p[0] = FUNCTOR_minus1;
1274 p[1] = ATOM_nil;
1275
1276 *valTermRef(t) = consPtr(p, TAG_COMPOUND|STG_GLOBAL);
1277
1278 return t;
1279 }
1280
1281 static
1282 PRED_IMPL("$tbl_delay_list", 1, tbl_delay_list, 0)
1283 { PRED_LD;
1284 term_t dl = LD->tabling.delay_list;
1285 term_t a = PL_new_term_ref();
1286
1287 return ( _PL_get_arg(1, dl, a) &&
1288 PL_unify(A1, a) );
1289 }
1290
1291 static
1292 PRED_IMPL("$tbl_set_delay_list", 1, tbl_set_delay_list, 0)
1293 { PRED_LD;
1294 term_t dl = LD->tabling.delay_list;
1295 Word p;
1296
1297 if ( !hasGlobalSpace(0) )
1298 { int rc;
1299
1300 if ( (rc=ensureGlobalSpace(0, ALLOW_GC)) != TRUE )
1301 return raiseStackOverflow(rc);
1302 }
1303
1304 p = valTermRef(dl);
1305 if ( isTerm(*p) )
1306 { p = argTermP(*p, 0);
1307
1308 TrailAssignment(p);
1309 unify_vp(p, valTermRef(A1) PASS_LD);
1310 }
1311
1312 return TRUE;
1313 }
1314
1315 static void
push_delay_list(Word p ARG_LD)1316 push_delay_list(Word p ARG_LD)
1317 { Word dl = valTermRef(LD->tabling.delay_list);
1318
1319 assert(isTerm(*dl));
1320 dl = argTermP(*dl, 0);
1321 p[2] = *dl;
1322 TrailAssignment(dl);
1323 *dl = consPtr(p, TAG_COMPOUND|STG_GLOBAL);
1324 }
1325
1326 /* Push a positive delay node. If the answer is ground this is a
1327 * term atrie+answer, else it is a term atrie+wrapper.
1328 */
1329
1330 static word
delay_to_data(trie_node * answer,Word wrapper ARG_LD)1331 delay_to_data(trie_node *answer, Word wrapper ARG_LD)
1332 { if ( unlikely(answer == NULL) )
1333 { return consInt(0);
1334 } else if ( is_ground_trie_node(answer) )
1335 {
1336 #if SIZEOF_VOIDP == 8
1337 intptr_t rc = consInt(pointerToInt(answer));
1338 DEBUG(0, assert(intToPointer(valInt(rc)) == answer));
1339 return rc;
1340 #else
1341 word rc;
1342 intptr_t i = pointerToInt(answer);
1343
1344 rc = consInt(i);
1345 if ( i == valInt(rc) )
1346 { return rc;
1347 } else
1348 { int rcp = put_int64(&rc, i, 0 PASS_LD);
1349 return rcp == TRUE ? rc : 0;
1350 }
1351 #endif
1352 } else
1353 { return linkVal(wrapper);
1354 }
1355 }
1356
1357
1358 void
tbl_push_delay(atom_t atrie,Word wrapper,trie_node * answer ARG_LD)1359 tbl_push_delay(atom_t atrie, Word wrapper, trie_node *answer ARG_LD)
1360 { Word p;
1361 word p5 = delay_to_data(answer, wrapper PASS_LD);
1362
1363 assert(p5);
1364
1365 if ( (p = allocGlobalNoShift(6)) )
1366 { p[0] = FUNCTOR_dot2;
1367 p[1] = consPtr(&p[3], TAG_COMPOUND|STG_GLOBAL);
1368 p[3] = FUNCTOR_plus2;
1369 p[4] = atrie;
1370 p[5] = p5;
1371
1372 push_delay_list(p PASS_LD);
1373 } else
1374 { assert(0);
1375 }
1376 }
1377
1378
1379 /** '$tbl_add_global_delays'(+Delays0, -Delays) is det.
1380 *
1381 * Delays is the result of appending the global delay list to Delays0.
1382 * This is a highly time critical operation and might eventually be
1383 * merged into '$tbl_wkl_add_answer'/4 and '$tbl_wkl_add_suspension'/2.
1384 */
1385
1386 static
1387 PRED_IMPL("$tbl_add_global_delays", 2, tbl_add_global_delays, 0)
1388 { PRED_LD
1389 term_t dl = PL_new_term_ref();
1390
1391 _PL_get_arg(1, LD->tabling.delay_list, dl);
1392
1393 if ( PL_get_nil(dl) )
1394 { return PL_unify(A1, A2);
1395 } else if ( PL_get_nil(A1) )
1396 { return PL_unify(A2, dl);
1397 } else
1398 { intptr_t len;
1399 Word tailp;
1400 Word dlp, p;
1401 word l;
1402
1403 len = skip_list(valTermRef(dl), &tailp PASS_LD);
1404 assert(isNil(*tailp));
1405
1406 if ( !(p=allocGlobal(3*len)) )
1407 return FALSE;
1408 l = consPtr(p, TAG_COMPOUND|STG_GLOBAL);
1409
1410 dlp = valTermRef(dl);
1411 deRef(dlp);
1412
1413 for(;;)
1414 { *p++ = FUNCTOR_dot2;
1415 *p++ = linkVal(HeadList(dlp));
1416 dlp = TailList(dlp);
1417 deRef(dlp);
1418 if ( isNil(*dlp) )
1419 { *p = linkVal(valTermRef(A1));
1420 return _PL_unify_atomic(A2, l);
1421 }
1422 *p = consPtr(&p[1], TAG_COMPOUND|STG_GLOBAL);
1423 p++;
1424 }
1425 }
1426 }
1427
1428 /*******************************
1429 * SIMPLIFICATION *
1430 *******************************/
1431
1432 static int answer_completion(tbl_component *scc);
1433
1434 typedef struct propagate
1435 { worklist *worklist;
1436 trie_node *answer;
1437 int result;
1438 } propagate;
1439
1440 typedef struct agenda
1441 { size_t done;
1442 tmp_buffer buffer;
1443 } spf_agenda;
1444
1445 static void
init_spf_agenda(spf_agenda * a)1446 init_spf_agenda(spf_agenda *a)
1447 { a->done = 0;
1448 initBuffer(&a->buffer);
1449 }
1450
1451 static void
exit_spf_agenda(spf_agenda * a)1452 exit_spf_agenda(spf_agenda *a)
1453 { discardBuffer(&a->buffer);
1454 }
1455
1456 static int
push_propagate(spf_agenda * a,worklist * wl,trie_node * answer,int result)1457 push_propagate(spf_agenda *a, worklist *wl, trie_node *answer, int result)
1458 { propagate *p = allocFromBuffer(&a->buffer, sizeof(*p));
1459
1460 p->worklist = wl;
1461 p->answer = answer;
1462 p->result = result;
1463
1464 return TRUE;
1465 }
1466
1467 propagate *
pop_propagate(spf_agenda * a)1468 pop_propagate(spf_agenda *a)
1469 { if ( isEmptyBuffer(&a->buffer) )
1470 return NULL;
1471
1472 return popBufferP(&a->buffer, propagate);
1473 }
1474
1475
1476 static int propagate_result(spf_agenda *agenda,
1477 worklist *wl, trie_node *answer, int result);
1478 #ifdef O_DEBUG
1479 static void print_delay(const char *msg, trie_node *variant, trie_node *answer);
1480 #endif
1481
1482 static int
make_answer_unconditional(spf_agenda * agenda,trie_node * answer)1483 make_answer_unconditional(spf_agenda *agenda, trie_node *answer)
1484 { delay_info *di = answer->data.delayinfo;
1485
1486 if ( DL_IS_DELAY_LIST(di) )
1487 { trie *at = symbol_trie(di->variant->value);
1488 worklist *wl = at->data.worklist;
1489 assert(wl->magic == WORKLIST_MAGIC);
1490
1491 DEBUG(MSG_TABLING_SIMPLIFY,
1492 print_delay(" Making answer unconditional", di->variant, answer));
1493
1494 destroy_delay_info(at, answer, TRUE);
1495 agenda->done++;
1496 wl->undefined--;
1497
1498 if ( !isEmptyBuffer(&wl->delays) )
1499 push_propagate(agenda, wl, answer, TRUE);
1500 } else
1501 { assert(0);
1502 }
1503
1504 return TRUE;
1505 }
1506
1507
1508 static int
remove_conditional_answer(spf_agenda * agenda,trie_node * answer)1509 remove_conditional_answer(spf_agenda *agenda, trie_node *answer)
1510 { delay_info *di = answer->data.delayinfo;
1511
1512 if ( DL_IS_DELAY_LIST(di) )
1513 { trie *at = symbol_trie(di->variant->value);
1514 worklist *wl = at->data.worklist;
1515
1516 assert(wl->magic == WORKLIST_MAGIC);
1517
1518 DEBUG(MSG_TABLING_SIMPLIFY,
1519 print_delay(" Removing conditional answer", di->variant, answer));
1520
1521 destroy_delay_info(at, answer, TRUE);
1522 trie_delete(at, answer, TRUE); /* cannot prune as may be */
1523 agenda->done++; /* in worklist delay lists */
1524 wl->undefined--;
1525
1526 if ( !isEmptyBuffer(&wl->delays) )
1527 push_propagate(agenda, wl, answer, FALSE);
1528 }
1529
1530 return TRUE;
1531 }
1532
1533
1534
1535 static void
answer_delay_sets(delay_info * di,delay_set ** base,delay_set ** top)1536 answer_delay_sets(delay_info *di, delay_set **base, delay_set **top)
1537 { *base = baseBuffer(&di->delay_sets, delay_set);
1538 *top = topBuffer(&di->delay_sets, delay_set);
1539 }
1540
1541
1542 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1543 Propagate a result in worklist wl. For negative nodes `answer` is NULL.
1544 The `result` indicates whether the answer is satisfied (TRUE) or not
1545 (FALSE). If an answer is satisfied it is removed from the delay list and
1546 if the resulting delay list becomes empty the answer is made
1547 unconditional. Otherwise the delay list can no longer become satisfied
1548 and we remove the delay list. If this was the last delay list the answer
1549 is definitely invalid and can be removed from the answer trie.
1550
1551 Answer to propagate is <wl,panswer> with truth result.
1552 This answer is propagate to `answer`
1553 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1554
1555 static int
propagate_to_answer(spf_agenda * agenda,worklist * wl,trie_node * panswer,int result,trie_node * answer)1556 propagate_to_answer(spf_agenda *agenda, worklist *wl,
1557 trie_node *panswer, int result, trie_node *answer)
1558 { delay_info *di;
1559 trie *variant = wl->table;
1560 int found = FALSE;
1561
1562 DEBUG(MSG_TABLING_SIMPLIFY, print_answer(" to", answer));
1563
1564 if ( (di=answer_delay_info(NULL, answer, FALSE)) )
1565 { delay_set *ds, *dz;
1566 delay *db = baseBuffer(&di->delays, delay);
1567
1568 for(answer_delay_sets(di, &ds, &dz); ds < dz; ds++)
1569 { unsigned o;
1570 unsigned oe = ds->offset+ds->size;
1571
1572 for(o=ds->offset; o<oe; o++)
1573 { delay *d = &db[o];
1574
1575 if ( d->variant == variant )
1576 { if ( d->answer == panswer || d->answer == NULL )
1577 { int res;
1578
1579 DEBUG(MSG_TABLING_SIMPLIFY,
1580 Sdprintf(" found (SCC=%zd, simplifications = %zd)\n",
1581 pointerToInt(wl->component),
1582 wl->component->simplifications));
1583
1584 if ( d->answer == NULL )
1585 { if ( result == FALSE &&
1586 (wl->has_answers || wl->undefined) )
1587 continue;
1588 res = !result;
1589 } else
1590 { res = result;
1591 }
1592
1593 found = TRUE;
1594 wl->component->simplifications++;
1595
1596 if ( res ) /* remove member from conjunction */
1597 { d->variant = DV_DELETED;
1598 if ( --ds->active == 0 )
1599 { make_answer_unconditional(agenda, answer);
1600 return found;
1601 }
1602 } else /* remove the conjunction */
1603 { memmove(ds, ds+1, sizeof(*ds)*(dz-ds-1));
1604 (void)popBufferP(&di->delay_sets, delay_set);
1605 ds--; /* compensate for(;;ds++) */
1606 dz--;
1607 break;
1608 }
1609 }
1610 }
1611 }
1612
1613 if ( isEmptyBuffer(&di->delay_sets) )
1614 { remove_conditional_answer(agenda, answer);
1615 return found;
1616 }
1617 }
1618 }
1619
1620 if ( found )
1621 DEBUG(MSG_TABLING_SIMPLIFY, print_answer(" now", answer));
1622
1623 return found;
1624 }
1625
1626
1627 static int
propagate_result(spf_agenda * agenda,worklist * wl,trie_node * panswer,int result)1628 propagate_result(spf_agenda *agenda,
1629 worklist *wl, trie_node *panswer, int result)
1630 { DEBUG(MSG_TABLING_SIMPLIFY,
1631 { print_delay(result ? "Propagating TRUE" : "Propagating FALSE",
1632 wl->table->data.variant, panswer);
1633 Sdprintf(" %zd dependent answers\n",
1634 entriesBuffer(&wl->delays, trie_node*));
1635 });
1636
1637 while( !isEmptyBuffer(&wl->delays) )
1638 { trie_node *answer = popBuffer(&wl->delays, trie_node*);
1639
1640 propagate_to_answer(agenda, wl, panswer, result, answer);
1641 }
1642
1643 return TRUE;
1644 }
1645
1646
1647 static int
simplify_answer(worklist * wl,trie_node * answer,int truth)1648 simplify_answer(worklist *wl, trie_node *answer, int truth)
1649 { spf_agenda agenda;
1650 propagate *p;
1651
1652 init_spf_agenda(&agenda);
1653 push_propagate(&agenda, wl, answer, truth);
1654 while( (p=pop_propagate(&agenda)) )
1655 propagate_result(&agenda, p->worklist, p->answer, p->result);
1656 exit_spf_agenda(&agenda);
1657
1658 return TRUE;
1659 }
1660
1661
1662 static int
simplify_component(tbl_component * scc)1663 simplify_component(tbl_component *scc)
1664 { spf_agenda agenda;
1665 propagate *p;
1666 worklist **wlp0 = baseBuffer(&scc->created_worklists->members, worklist*);
1667 worklist **top = topBuffer(&scc->created_worklists->members, worklist*);
1668 worklist **wlp;
1669 int undefined, pass;
1670 #ifndef O_AC_EAGER
1671 size_t simplified0 = scc->simplifications;
1672 #endif
1673
1674 DEBUG(MSG_TABLING_SIMPLIFY,
1675 { GET_LD
1676 term_t t = PL_new_term_ref();
1677 unify_trie_term(scc->leader->data.variant, NULL, t PASS_LD);
1678 Sdprintf("Simplifying SCC %zd; leader = ", pointerToInt(scc));
1679 PL_write_term(Serror, t, 999, PL_WRT_NEWLINE);
1680 });
1681
1682 init_spf_agenda(&agenda);
1683
1684 for(pass=0; ;pass++)
1685 { int count = 0;
1686
1687 undefined = 0;
1688
1689 for(wlp = wlp0; wlp < top; wlp++)
1690 { worklist *wl = *wlp;
1691
1692 if ( pass == 0 )
1693 clean_worklist(wl);
1694
1695 if ( wl->negative &&
1696 wl->neg_delayed &&
1697 wl->table->value_count == 0 &&
1698 !isEmptyBuffer(&wl->delays) )
1699 { DEBUG(MSG_TABLING_SIMPLIFY,
1700 { GET_LD
1701 term_t t = PL_new_term_ref();
1702 unify_trie_term(wl->table->data.variant, NULL, t PASS_LD);
1703 Sdprintf("No conditional answers for %zd: ", pointerToInt(wl));
1704 PL_write_term(Serror, t, 999, PL_WRT_NEWLINE);
1705 });
1706
1707 count++;
1708 push_propagate(&agenda, wl, NULL, FALSE);
1709 while( (p=pop_propagate(&agenda)) )
1710 propagate_result(&agenda, p->worklist, p->answer, p->result);
1711 }
1712
1713 if ( !isEmptyBuffer(&wl->pos_undefined) )
1714 { trie_node **bn = baseBuffer(&wl->pos_undefined, trie_node *);
1715 trie_node **en = topBuffer(&wl->pos_undefined, trie_node *);
1716 trie_node **on = bn;
1717
1718 for(; bn < en; bn++)
1719 { trie_node *an = *bn;
1720
1721 if ( !answer_is_conditional(an) )
1722 { DEBUG(MSG_TABLING_SIMPLIFY,
1723 print_answer("Propagating now unconditional answer", an));
1724 count++;
1725 push_propagate(&agenda, wl, an, an->value != 0);
1726 while( (p=pop_propagate(&agenda)) )
1727 propagate_result(&agenda, p->worklist, p->answer, p->result);
1728 } else
1729 { *on++ = an;
1730 }
1731 }
1732 wl->pos_undefined.top = (char*)on;
1733 }
1734
1735 if ( wl->undefined )
1736 undefined++;
1737 }
1738
1739 if ( count == 0 || undefined == 0 )
1740 break;
1741 }
1742
1743 exit_spf_agenda(&agenda);
1744
1745 #ifndef O_AC_EAGER
1746 if ( (simplified0 != scc->simplifications) )
1747 { size_t cnt = scc->simplifications - simplified0 ;
1748 tbl_component *c;
1749
1750 for(c = scc->parent; c; c = c->parent)
1751 c->simplifications += cnt;
1752 }
1753 #endif
1754
1755 /* DSW: there cannot be any "uncovering" of a positive loop if there
1756 * was no simplification
1757 */
1758
1759 DEBUG(MSG_TABLING_SIMPLIFY,
1760 Sdprintf("Simplified SCC %zd; undefined = %d; simplifications: %zd\n",
1761 pointerToInt(scc), undefined, scc->simplifications));
1762
1763 if ( undefined && scc->simplifications )
1764 return answer_completion(scc);
1765 else
1766 return TRUE;
1767 }
1768
1769 #ifdef O_DEBUG
1770 static void
print_dl_dependency(trie * from,trie * to)1771 print_dl_dependency(trie *from, trie *to)
1772 { GET_LD
1773 term_t From = PL_new_term_ref();
1774 term_t To = PL_new_term_ref();
1775
1776 unify_trie_term(from->data.variant, NULL, From PASS_LD);
1777 unify_trie_term(to->data.variant, NULL, To PASS_LD);
1778 Sdprintf("Delay list dep from %p (", from);
1779 PL_write_term(Serror, From, 999, 0);
1780 Sdprintf(") -> %p (", to);
1781 PL_write_term(Serror, To, 999, 0);
1782 Sdprintf(")\n");
1783 }
1784 #endif
1785
1786 /*******************************
1787 * ANSWER COMPLETION *
1788 *******************************/
1789
1790 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1791 The role of answer completion is to remove positive loops from the
1792 remaining delay lists. We do this by calling answer_completion(+ATrie)
1793 in boot/tabling.pl. This predicate uses recursive tabling on the
1794 residual program that involves the given ATrie and removing all answers
1795 deduced as false and marking those deduced as true as `answer_completed`
1796
1797 We search for a candidate worklist as one that is undefined (e.g., has a
1798 residual program) and has at least one depending answer that has a
1799 positive delay element because a positive loop needs to include at least
1800 one positive dependency.
1801 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1802
1803 static int
call_answer_completion(trie * atrie ARG_LD)1804 call_answer_completion(trie *atrie ARG_LD)
1805 { fid_t fid;
1806
1807 if ( (fid = PL_open_foreign_frame()) )
1808 { static predicate_t pred = NULL;
1809 term_t av = PL_new_term_refs(2);
1810 int rc;
1811 tbl_component *scc_old = LD->tabling.component;
1812 int hsc = LD->tabling.has_scheduling_component;
1813
1814 if ( !pred )
1815 pred = PL_predicate("answer_completion", 2, "$tabling");
1816
1817 DEBUG(MSG_TABLING_AC,
1818 { term_t t = PL_new_term_ref();
1819 unify_trie_term(atrie->data.variant, NULL, t PASS_LD);
1820 Sdprintf("Calling answer completion for: ");
1821 PL_write_term(Serror, t, 999, PL_WRT_NEWLINE);
1822 });
1823
1824 LD->tabling.component = NULL;
1825 LD->tabling.has_scheduling_component = FALSE;
1826 LD->tabling.in_answer_completion = TRUE;
1827 rc = ( PL_put_atom(av+0, atrie->symbol) &&
1828 unify_skeleton(atrie, 0, av+1 PASS_LD) &&
1829 PL_call_predicate(NULL, PL_Q_PASS_EXCEPTION, pred, av) );
1830 LD->tabling.in_answer_completion = FALSE;
1831 LD->tabling.has_scheduling_component = hsc;
1832 LD->tabling.component = scc_old;
1833
1834 PL_close_foreign_frame(fid);
1835 return rc;
1836 } else
1837 return FALSE; /* stack overflow */
1838 }
1839
1840
1841 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1842 A variant can only be subject to answer completion if it has at least
1843 one answer that is undefined and has a condition containing only
1844 positive delay elements.
1845 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1846
1847 #ifdef O_AC_EAGER
1848 static int
has_positive_dl(trie_node * n)1849 has_positive_dl(trie_node *n)
1850 { delay_info *di;
1851
1852 if ( n->value && DL_IS_DELAY_LIST(di=n->data.delayinfo) )
1853 { delay_set *ds, *dz;
1854 delay *db = baseBuffer(&di->delays, delay);
1855
1856 for(answer_delay_sets(di, &ds, &dz); ds < dz; ds++)
1857 { unsigned o;
1858 unsigned oe = ds->offset+ds->size;
1859
1860 for(o=ds->offset; o<oe; o++)
1861 { delay *d = &db[o];
1862
1863 if ( d->variant && d->variant != DV_DELETED )
1864 { if ( d->answer )
1865 return TRUE;
1866 }
1867 }
1868 }
1869 }
1870
1871 return FALSE;
1872 }
1873
1874 static int
is_ac_candidate_wl(worklist * wl)1875 is_ac_candidate_wl(worklist *wl)
1876 { if ( wl->undefined && !wl->answer_completed &&
1877 !isEmptyBuffer(&wl->delays) )
1878 { trie_node **n = baseBuffer(&wl->delays, trie_node*);
1879 trie_node **z = topBuffer(&wl->delays, trie_node*);
1880
1881 for( ; n < z; n++)
1882 { if ( has_positive_dl(*n) )
1883 return TRUE;
1884 }
1885 }
1886
1887 return FALSE;
1888 }
1889 #endif /*O_AC_EAGER*/
1890
1891
1892 static int
answer_completion(tbl_component * scc)1893 answer_completion(tbl_component *scc)
1894 { GET_LD
1895
1896 #ifdef O_DEBUG
1897 if ( DEBUGGING(TABLING_NO_AC) )
1898 return TRUE;
1899 #endif
1900
1901 if ( LD->tabling.in_answer_completion )
1902 return TRUE; /* not recursive! */
1903
1904 #ifdef O_AC_EAGER
1905 worklist **wlp = baseBuffer(&scc->created_worklists->members, worklist*);
1906 worklist **top = topBuffer(&scc->created_worklists->members, worklist*);
1907
1908 for(; wlp < top; wlp++)
1909 { worklist *wl = *wlp;
1910
1911 if ( is_ac_candidate_wl(wl) )
1912 { if ( !call_answer_completion(wl->table PASS_LD) )
1913 return FALSE;
1914 }
1915 }
1916
1917 return TRUE;
1918 #else
1919 return call_answer_completion(scc->leader PASS_LD);
1920 #endif /*O_AC_EAGER*/
1921 }
1922
1923 /** '$tbl_force_truth_value'(+AnswerNode, +Value, -Count)
1924 *
1925 * Force AnswerNode to have truth value Value. Count returns the
1926 * number of answer nodes that have been changed, which may be more
1927 * than one due to propagation.
1928 */
1929
1930 static
1931 PRED_IMPL("$tbl_force_truth_value", 3, tbl_force_truth_value, 0)
1932 { PRED_LD
1933 void *ptr;
1934 int truth;
1935 int rc = FALSE;
1936
1937 if ( PL_get_pointer_ex(A1, &ptr) &&
1938 PL_get_bool_ex(A2, &truth) )
1939 { trie_node *answer = ptr;
1940 delay_info *di = answer->data.delayinfo;
1941 spf_agenda agenda;
1942 propagate *p;
1943
1944 init_spf_agenda(&agenda);
1945
1946 if ( DL_IS_DELAY_LIST(di) )
1947 { trie *at = symbol_trie(di->variant->value);
1948 worklist *wl = at->data.worklist;
1949
1950 DEBUG(MSG_TABLING_AC,
1951 { term_t v = PL_new_term_ref();
1952 term_t a = PL_new_term_ref();
1953
1954 unify_trie_term(at->data.variant, NULL, v PASS_LD);
1955 unify_trie_term(answer, NULL, a PASS_LD);
1956 Sdprintf("Forcing answer ");
1957 PL_write_term(Serror, a, 999, 0);
1958 Sdprintf(" for ");
1959 PL_write_term(Serror, v, 999, 0);
1960 Sdprintf(" to FALSE\n");
1961 });
1962
1963 if ( WL_IS_WORKLIST(wl) )
1964 { if ( truth )
1965 rc = make_answer_unconditional(&agenda, answer);
1966 else
1967 rc = remove_conditional_answer(&agenda, answer);
1968 } else
1969 { rc = PL_permission_error("force_truth_value", "answer", A1);
1970 }
1971 } else /* answer is not conditional */
1972 { if ( !truth )
1973 { trie *at = get_trie_from_node(answer);
1974
1975 trie_delete(at, answer, FALSE); /* TBD: propagate? */
1976 }
1977 rc = TRUE;
1978 }
1979
1980 while( rc && (p=pop_propagate(&agenda)) )
1981 rc = propagate_result(&agenda, p->worklist, p->answer, p->result);
1982
1983 rc = rc && PL_unify_integer(A3, agenda.done);
1984
1985 exit_spf_agenda(&agenda);
1986 }
1987
1988 return rc;
1989 }
1990
1991
1992 static
1993 PRED_IMPL("$tbl_set_answer_completed", 1, tbl_set_answer_completed, 0)
1994 { trie *trie;
1995
1996 if ( get_trie(A1, &trie) )
1997 { worklist *wl;
1998
1999 if ( WL_IS_WORKLIST((wl=trie->data.worklist)) )
2000 { wl->answer_completed = TRUE;
2001
2002 return TRUE;
2003 }
2004
2005 if ( true(trie, TRIE_COMPLETE) )
2006 return TRUE;
2007
2008 return PL_permission_error("set_answer_complete", "trie", A1);
2009 }
2010
2011 return FALSE;
2012 }
2013
2014 static
2015 PRED_IMPL("$tbl_is_answer_completed", 1, tbl_is_answer_completed, 0)
2016 { trie *trie;
2017
2018 if ( get_trie(A1, &trie) )
2019 { worklist *wl;
2020
2021 if ( WL_IS_WORKLIST((wl=trie->data.worklist)) )
2022 return wl->answer_completed;
2023
2024 return !!true(trie, TRIE_COMPLETE);
2025 }
2026
2027 return FALSE;
2028 }
2029
2030
2031 #ifdef O_DEBUG
2032 static void
print_delay(const char * msg,trie_node * variant,trie_node * answer)2033 print_delay(const char *msg, trie_node *variant, trie_node *answer)
2034 { GET_LD
2035 term_t t = PL_new_term_ref();
2036
2037 unify_trie_term(variant, NULL, t PASS_LD);
2038 Sdprintf("%s: %s", msg, answer ? "" : "~");
2039 PL_write_term(Serror, t, 999, answer ? 0 : PL_WRT_NEWLINE);
2040 if ( answer )
2041 { PL_put_variable(t);
2042 unify_trie_term(answer, NULL, t PASS_LD);
2043 Sdprintf(", answer: ");
2044 PL_write_term(Serror, t, 999, PL_WRT_NEWLINE);
2045 }
2046 }
2047
2048 static void
print_answer(const char * msg,trie_node * answer)2049 print_answer(const char *msg, trie_node *answer)
2050 { GET_LD
2051 trie *at = get_trie_from_node(answer);
2052 term_t t = PL_new_term_ref();
2053
2054 unify_trie_term(at->data.variant, NULL, t PASS_LD);
2055 Sdprintf("%s: variant ", msg);
2056 PL_write_term(Serror, t, 999, 0);
2057 PL_put_variable(t);
2058 unify_trie_term(answer, NULL, t PASS_LD);
2059 Sdprintf(", answer: ");
2060 PL_write_term(Serror, t, 999, 0);
2061 if ( !answer->value )
2062 Sdprintf(" (NULL)");
2063 if ( answer_is_conditional(answer) )
2064 { put_delay_info(t, answer);
2065 Sdprintf(" (IF ");
2066 PL_write_term(Serror, t, 999, 0);
2067 Sdprintf(")\n");
2068 } else
2069 Sdprintf("\n");
2070 }
2071
2072 static void
print_answer_table(trie * atrie,const char * msg,...)2073 print_answer_table(trie *atrie, const char *msg, ...)
2074 { GET_LD
2075 va_list args;
2076 term_t t = PL_new_term_ref();
2077
2078 va_start(args, msg);
2079 unify_trie_term(atrie->data.variant, NULL, t PASS_LD);
2080 if ( msg )
2081 { if ( true(atrie, TRIE_ISSHARED) )
2082 Sdprintf("Thread [%d]: ", PL_thread_self());
2083
2084 Svdprintf(msg, args);
2085 Sdprintf(": <trie>(%p) for ", atrie);
2086 }
2087 va_end(args);
2088 PL_write_term(Serror, t, 999, PL_WRT_NEWLINE);
2089 }
2090
2091 #endif
2092
2093 /*******************************
2094 * SUSPEND *
2095 *******************************/
2096
2097 void
save_tabling_status(tbl_status * state)2098 save_tabling_status(tbl_status *state)
2099 { GET_LD
2100
2101 state->scc = LD->tabling.component;
2102 state->hsc = LD->tabling.has_scheduling_component;
2103 state->iac = LD->tabling.in_answer_completion;
2104
2105 LD->tabling.component = NULL;
2106 LD->tabling.has_scheduling_component = FALSE;
2107 LD->tabling.in_answer_completion = FALSE;
2108 }
2109
2110
2111 void
restore_tabling_status(tbl_status * state)2112 restore_tabling_status(tbl_status *state)
2113 { GET_LD
2114
2115 LD->tabling.component = state->scc;
2116 LD->tabling.has_scheduling_component = state->hsc;
2117 LD->tabling.in_answer_completion = state->iac;
2118 }
2119
2120
2121
2122 /*******************************
2123 * THREAD VARIANT TABLE *
2124 *******************************/
2125
2126 static void release_variant_table_node(trie *trie, trie_node *node);
2127
2128 static trie *
variant_table(int shared ARG_LD)2129 variant_table(int shared ARG_LD)
2130 { trie **tp;
2131 alloc_pool *pool;
2132
2133 #ifdef O_PLMT
2134 if ( shared )
2135 { tp = &GD->tabling.variant_table;
2136 if ( !(pool = GD->tabling.node_pool) )
2137 { if ( (pool = new_alloc_pool("shared_table_space",
2138 GD->options.sharedTableSpace)) )
2139 { if ( !COMPARE_AND_SWAP_PTR(&GD->tabling.node_pool, NULL, pool) )
2140 { free_alloc_pool(pool);
2141 pool = GD->tabling.node_pool;
2142 }
2143 } else
2144 { return NULL;
2145 }
2146 }
2147 } else
2148 #endif
2149 { tp = &LD->tabling.variant_table;
2150 if ( !(pool = LD->tabling.node_pool) )
2151 { pool = LD->tabling.node_pool = new_alloc_pool("private_table_space",
2152 GD->options.tableSpace);
2153 if ( !pool )
2154 return NULL;
2155 }
2156 }
2157
2158 if ( *tp == NULL )
2159 { trie *t;
2160
2161 if ( (t = trie_create(pool)) )
2162 { atom_t symb;
2163
2164 t->release_node = release_variant_table_node;
2165 symb = trie_symbol(t);
2166
2167 if ( COMPARE_AND_SWAP_PTR(tp, NULL, t) )
2168 { if ( shared )
2169 { set(t, TRIE_ISSHARED);
2170 acquire_trie(t); /* bit misuse */
2171 }
2172 } else
2173 { PL_unregister_atom(symb); /* destroyed by atom-GC */
2174 }
2175 }
2176 }
2177
2178 return *tp;
2179 }
2180
2181
2182 static void
reset_answer_table(trie * atrie,int cleanup)2183 reset_answer_table(trie *atrie, int cleanup)
2184 { worklist *wl;
2185 idg_node *n;
2186
2187 if ( WL_IS_WORKLIST(wl=atrie->data.worklist) )
2188 { if ( !isEmptyBuffer(&wl->delays) && !cleanup )
2189 destroy_depending_worklists(wl);
2190 if ( wl->undefined )
2191 destroy_delay_info_worklist(wl);
2192 atrie->data.worklist = NULL;
2193 clear(atrie, TRIE_ABOLISH_ON_COMPLETE);
2194 free_worklist(wl);
2195 } else if ( wl )
2196 { atrie->data.worklist = NULL; /* make fresh again */
2197 }
2198 clear(atrie, TRIE_COMPLETE);
2199
2200 if ( (n=atrie->data.IDG) )
2201 { if ( true(atrie, TRIE_ISSHARED) )
2202 { idg_reset(n);
2203 } else
2204 { atrie->data.IDG = NULL;
2205 idg_destroy(n);
2206 }
2207 }
2208
2209 trie_empty(atrie);
2210 }
2211
2212
2213 static void
release_variant_table_node(trie * variant_table,trie_node * node)2214 release_variant_table_node(trie *variant_table, trie_node *node)
2215 { (void)variant_table;
2216
2217 if ( node->value )
2218 { trie *atrie = symbol_trie(node->value);
2219
2220 reset_answer_table(atrie, variant_table->magic == TRIE_CMAGIC);
2221 assert(atrie->data.variant == node);
2222 atrie->data.variant = NULL;
2223 }
2224 }
2225
2226
2227 static int
is_variant_trie(trie * trie)2228 is_variant_trie(trie *trie)
2229 { return trie->release_node == release_variant_table_node;
2230 }
2231
2232
2233 static void
clear_variant_table(PL_local_data_t * ld)2234 clear_variant_table(PL_local_data_t *ld)
2235 { trie *vtrie;
2236
2237 if ( (vtrie=ld->tabling.variant_table) )
2238 { vtrie->magic = TRIE_CMAGIC;
2239 trie_empty(vtrie);
2240 PL_unregister_atom(vtrie->symbol);
2241 ld->tabling.variant_table = NULL;
2242 }
2243 }
2244
2245
2246 #define VAR_SKEL_FAST 8
2247
2248 static int
unify_trie_ret(term_t ret,TmpBuffer vars ARG_LD)2249 unify_trie_ret(term_t ret, TmpBuffer vars ARG_LD)
2250 { Word *pp = baseBuffer(vars, Word);
2251 Word *ep = topBuffer(vars, Word);
2252 static functor_t fast[VAR_SKEL_FAST] = {0};
2253 size_t arity = ep-pp;
2254 functor_t vf;
2255
2256 assert(arity > 0);
2257 if ( arity < VAR_SKEL_FAST )
2258 { if ( !(vf=fast[arity]) )
2259 fast[arity] = vf = PL_new_functor(ATOM_ret, arity);
2260 } else
2261 { vf = PL_new_functor(ATOM_ret, arity);
2262 }
2263
2264 if ( hasGlobalSpace(arity+1) )
2265 { Word p = allocGlobalNoShift(arity+1);
2266 word w = consPtr(p, TAG_COMPOUND|STG_GLOBAL);
2267 *p++ = vf;
2268
2269 for(; pp < ep; pp++)
2270 { Word ap = *pp;
2271
2272 if ( isVar(*ap) )
2273 *p++ = makeRefG(ap);
2274 else
2275 *p++ = *ap;
2276 }
2277
2278 if ( PL_is_variable(ret) )
2279 return _PL_unify_atomic(ret, w);
2280 else
2281 return unify_ptrs(valTermRef(ret), &w, ALLOW_RETCODE PASS_LD);
2282 }
2283
2284 return GLOBAL_OVERFLOW;
2285 }
2286
2287
2288 static void
release_answer_node(trie * atrie,trie_node * node)2289 release_answer_node(trie *atrie, trie_node *node)
2290 { if ( DL_IS_DELAY_LIST(node->data.delayinfo) )
2291 destroy_delay_info(atrie, node, TRUE);
2292 }
2293
2294
2295 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2296 get_answer_table(+Variant, -Return, int flags)
2297
2298 Find the answer table for Variant and its return template (a term
2299 ret/N). If `create` is TRUE, create the table if it does not exist.
2300
2301 (*) We must avoid a race with trie_discard_clause(). As long as the
2302 (atom) clause reference is not garbage collected the clause is safe. We
2303 first check there is an atom, then push it as a volatile atom and check
2304 it again. If the atom is still valid it is not protected against atom-gc
2305 by pushVolatileAtom() and will be unified before anything else in
2306 '$variant_table'/5, so it remains valid.
2307 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2308
2309 #define AT_CREATE 0x0001
2310 #define AT_MODED 0x0002 /* moded tabling: trie has values */
2311 #define AT_SHARED 0x0004 /* find a shared table */
2312 #define AT_PRIVATE 0x0008 /* find a private table */
2313 #define AT_NOCLAIM 0x0010 /* Do not claim ownership */
2314
2315
2316 #define AT_ABSTRACT 0x0020 /* subgoal_abstract(N) tabling */
2317 #define AT_SCOPE_MASK (AT_SHARED|AT_PRIVATE)
2318
2319 static inline size_t
pred_max_table_subgoal_size(const Definition def ARG_LD)2320 pred_max_table_subgoal_size(const Definition def ARG_LD)
2321 { size_t limit;
2322
2323 limit = def->tabling ? def->tabling->subgoal_abstract : (size_t)-1;
2324 if ( limit == (size_t)-1 )
2325 limit = LD->tabling.restraint.max_table_subgoal_size;
2326
2327 return limit;
2328 }
2329
2330
2331 static trie *
get_answer_table(Definition def,term_t t,term_t ret,atom_t * clrefp,int flags ARG_LD)2332 get_answer_table(Definition def, term_t t, term_t ret, atom_t *clrefp,
2333 int flags ARG_LD)
2334 { trie *variants;
2335 trie *atrie;
2336 trie_node *node;
2337 int rc;
2338 Word v;
2339 tmp_buffer vars;
2340 mark m;
2341 int shared;
2342 size_abstract sa = {.from_depth = 2, .size = (size_t)-1};
2343
2344 #ifdef O_PLMT
2345 if ( (flags & AT_SCOPE_MASK) )
2346 { shared = !!(flags&AT_SHARED);
2347 } else
2348 { if ( !def ) /* we should avoid these */
2349 { Procedure proc;
2350
2351 if ( get_procedure(t, &proc, 0, GP_RESOLVE) )
2352 { def = proc->definition;
2353 } else
2354 { assert(0);
2355 return NULL;
2356 }
2357 }
2358 shared = !!true(def, P_TSHARED);
2359 }
2360 #else
2361 shared = FALSE;
2362 #endif
2363
2364 if ( def ) /* otherwise we don't need it anyway */
2365 sa.size = pred_max_table_subgoal_size(def PASS_LD);
2366 variants = variant_table(shared PASS_LD);
2367 initBuffer(&vars);
2368
2369 retry:
2370 Mark(m);
2371 v = valTermRef(t);
2372 rc = trie_lookup_abstract(variants, NULL, &node, v, (flags&AT_CREATE),
2373 &sa, &vars PASS_LD);
2374
2375 if ( rc > 0 )
2376 { if ( rc == TRIE_ABSTRACTED )
2377 { atom_t action = LD->tabling.restraint.max_table_subgoal_size_action;
2378
2379 DEBUG(MSG_TABLING_RESTRAINT,
2380 Sdprintf("Trapped by subgoal size restraint\n"));
2381 if ( action == ATOM_abstract && !(flags&AT_ABSTRACT) )
2382 action = ATOM_error;
2383
2384 if ( action != ATOM_abstract )
2385 { if ( tbl_pred_tripwire(def, action, ATOM_max_table_subgoal_size) )
2386 { sa.size = (size_t)-1;
2387 emptyBuffer(&vars, (size_t)-1);
2388 goto retry;
2389 } else
2390 { discardBuffer(&vars);
2391 return NULL;
2392 }
2393 }
2394 }
2395
2396 if ( node->value )
2397 { atrie = symbol_trie(node->value);
2398 } else if ( (flags&AT_CREATE) )
2399 { atom_t symb;
2400 #ifdef O_PLMT
2401 alloc_pool *pool = (shared ? GD->tabling.node_pool
2402 : LD->tabling.node_pool);
2403 #else
2404 alloc_pool *pool = LD->tabling.node_pool;
2405 #endif
2406
2407 if ( !(atrie = trie_create(pool)) )
2408 return NULL;
2409 set(atrie, (flags&AT_MODED) ? TRIE_ISMAP : TRIE_ISSET);
2410 atrie->release_node = release_answer_node;
2411 atrie->data.variant = node;
2412 symb = trie_symbol(atrie);
2413
2414 #ifdef O_PLMT
2415 if ( shared )
2416 { set(atrie, TRIE_ISSHARED);
2417 if ( COMPARE_AND_SWAP_WORD(&node->value, 0, symb) )
2418 { set(node, TN_PRIMARY);
2419 ATOMIC_INC(&variants->value_count);
2420 } else
2421 { PL_unregister_atom(symb);
2422 trie_destroy(atrie);
2423 atrie = symbol_trie(node->value);
2424 }
2425 } else
2426 #endif
2427 { set(node, TN_PRIMARY);
2428 node->value = symb;
2429 ATOMIC_INC(&variants->value_count);
2430 }
2431 } else
2432 { discardBuffer(&vars);
2433 return NULL;
2434 }
2435
2436 #ifdef O_PLMT
2437 if ( !claim_answer_table(atrie, clrefp, flags PASS_LD) )
2438 { discardBuffer(&vars);
2439 return NULL;
2440 }
2441 #endif
2442
2443 if ( ret )
2444 { if ( isEmptyBuffer(&vars) ) /* TBD: only needed first time */
2445 { if ( WL_IS_WORKLIST(atrie->data.worklist) )
2446 { atrie->data.worklist->ground = TRUE;
2447 } else if ( !atrie->data.worklist )
2448 { atrie->data.worklist = WL_GROUND;
2449 }
2450 if ( !PL_unify_atom(ret, ATOM_ret) )
2451 atrie = NULL;
2452 } else
2453 { int rc;
2454
2455 if ( (rc=unify_trie_ret(ret, &vars PASS_LD)) != TRUE )
2456 { if ( rc < 0 )
2457 { Undo(m);
2458 emptyBuffer(&vars, (size_t)-1);
2459 if ( makeMoreStackSpace(rc, ALLOW_GC) )
2460 goto retry;
2461 }
2462 atrie = NULL;
2463 }
2464 }
2465 }
2466 discardBuffer(&vars);
2467
2468 return atrie;
2469 } else
2470 { discardBuffer(&vars);
2471 }
2472
2473 trie_error(rc, t);
2474 return NULL;
2475 }
2476
2477
2478 void
clearThreadTablingData(PL_local_data_t * ld)2479 clearThreadTablingData(PL_local_data_t *ld)
2480 { reset_global_worklist(ld->tabling.component);
2481 reset_newly_created_worklists(ld->tabling.component, WLFS_KEEP_COMPLETE);
2482 clear_variant_table(ld);
2483 }
2484
2485
2486 /*******************************
2487 * CALL SUBSUPTION INDEXING *
2488 *******************************/
2489
2490 /* TBD: Share with pl-index.c */
2491
2492 static word
indexOfWord(word w ARG_LD)2493 indexOfWord(word w ARG_LD)
2494 { for(;;)
2495 { switch(tag(w))
2496 { case TAG_VAR:
2497 case TAG_ATTVAR:
2498 return 0;
2499 case TAG_ATOM:
2500 break; /* atom_t */
2501 case TAG_INTEGER:
2502 if ( storage(w) == STG_INLINE )
2503 break;
2504 /*FALLTHROUGH*/
2505 case TAG_STRING:
2506 case TAG_FLOAT:
2507 { Word p = addressIndirect(w);
2508 size_t n = wsizeofInd(*p);
2509 word k;
2510
2511 k = MurmurHashAligned2(p+1, n*sizeof(*p), MURMUR_SEED);
2512 k &= ~((word)STG_GLOBAL); /* avoid confusion with functor_t */
2513 if ( !k ) k = 1; /* avoid no-key */
2514 return k;
2515 }
2516 case TAG_COMPOUND:
2517 w = *valPtr(w); /* functor_t */
2518 break;
2519 case TAG_REFERENCE:
2520 w = *unRef(w);
2521 continue;
2522 }
2523
2524 return w;
2525 }
2526 }
2527
2528
2529 static sindex_key *
suspension_keys(term_t instance ARG_LD)2530 suspension_keys(term_t instance ARG_LD)
2531 { Word p = valTermRef(instance);
2532
2533 deRef(p);
2534 if ( isTerm(*p) )
2535 { Functor f = valueTerm(*p);
2536 size_t i, arity = arityFunctor(f->definition);
2537 sindex_key keys[SINDEX_MAX];
2538 sindex_key *k = keys;
2539
2540 if ( arity > SINDEX_MAX )
2541 arity = SINDEX_MAX;
2542
2543 for(i=0; i<arity; i++)
2544 { unsigned int ki = indexOfWord(f->arguments[i] PASS_LD);
2545
2546 if ( ki )
2547 { k->argn = i+1;
2548 k->key = ki;
2549 if ( ++k >= &keys[SINDEX_MAX-1] )
2550 break;
2551 }
2552 }
2553
2554 if ( k > keys )
2555 { k->argn = 0;
2556 k->key = 0;
2557 k++;
2558
2559 size_t bytes = (char*)k - (char*)keys;
2560 sindex_key *gk = malloc(bytes);
2561
2562 if ( gk )
2563 memcpy(gk, keys, bytes);
2564 return gk;
2565 }
2566 }
2567
2568 return NULL;
2569 }
2570
2571
2572 static int
suspension_matches_index(const suspension * susp,const sindex_key * skeys)2573 suspension_matches_index(const suspension *susp, const sindex_key *skeys)
2574 { if ( likely(susp->keys!=NULL) )
2575 { sindex_key *k;
2576
2577 for(k=susp->keys; k->argn; k++)
2578 { const sindex_key *sk = &skeys[k->argn];
2579
2580 if ( unlikely(k->key != sk->key) && likely(!!sk->key) )
2581 return FALSE;
2582 }
2583 }
2584
2585 return TRUE;
2586 }
2587
2588
2589 static int
suspension_matches(term_t answer,const suspension * susp ARG_LD)2590 suspension_matches(term_t answer, const suspension *susp ARG_LD)
2591 { fid_t fid;
2592 term_t tmp;
2593
2594 if ( (fid=PL_open_foreign_frame()) &&
2595 (tmp = PL_new_term_ref()) &&
2596 PL_recorded(susp->instance, tmp) )
2597 { int ok;
2598
2599 DEBUG(MSG_TABLING_CALL_SUBSUMPTION,
2600 Sdprintf("Skeleton: ");
2601 PL_write_term(Serror, tmp, 1200, 0);
2602 Sdprintf(", instance: ");
2603 PL_write_term(Serror, answer, 1200, PL_WRT_NEWLINE));
2604
2605 ok = PL_unify(tmp, answer);
2606 PL_discard_foreign_frame(fid);
2607
2608 return ok ? TRUE : PL_exception(0) ? -1 : FALSE;
2609 } else
2610 return -1;
2611 }
2612
2613 /*******************************
2614 * ANSWER/SUSPENSION CLUSTERS *
2615 *******************************/
2616
2617 static cluster *
new_answer_cluster(worklist * wl,answer * ans)2618 new_answer_cluster(worklist *wl, answer *ans)
2619 { cluster *c;
2620
2621 if ( (c=wl->free_clusters) )
2622 { wl->free_clusters = c->next;
2623 c->type = CLUSTER_ANSWERS;
2624 } else
2625 { c = PL_malloc(sizeof(*c));
2626 c->type = CLUSTER_ANSWERS;
2627 initBuffer(&c->members);
2628 }
2629 addBuffer(&c->members, *ans, answer);
2630
2631 return c;
2632 }
2633
2634 static void
free_answer_cluster(cluster * c)2635 free_answer_cluster(cluster *c)
2636 { discardBuffer(&c->members);
2637 PL_free(c);
2638 }
2639
2640 static void
add_to_answer_cluster(cluster * c,answer * ans)2641 add_to_answer_cluster(cluster *c, answer *ans)
2642 { addBuffer(&c->members, *ans, answer);
2643 }
2644
2645 static void
merge_answer_clusters(cluster * to,cluster * from)2646 merge_answer_clusters(cluster *to, cluster *from)
2647 { typedef answer* Answer;
2648
2649 addMultipleBuffer(&to->members,
2650 baseBuffer(&from->members, answer),
2651 entriesBuffer(&from->members, answer),
2652 Answer);
2653 }
2654
2655 static answer *
get_answer_from_cluster(cluster * c,size_t index)2656 get_answer_from_cluster(cluster *c, size_t index)
2657 { return &fetchBuffer(&c->members, index, answer);
2658 }
2659
2660 static size_t
prune_answer_cluster(cluster * c)2661 prune_answer_cluster(cluster *c)
2662 { answer *base = baseBuffer(&c->members, answer);
2663 answer *top = topBuffer(&c->members, answer);
2664 answer *out = base;
2665
2666 for( ; base < top; base++)
2667 { trie_node *n = base->node;
2668 if ( n->value )
2669 *out++ = *base;
2670 }
2671
2672 c->members.top = (char*)out;
2673
2674 return top-out;
2675 }
2676
2677 static inline record_t
TNOT(record_t r,int is_tnot)2678 TNOT(record_t r, int is_tnot)
2679 { return (record_t)(((uintptr_t)r)|is_tnot);
2680 }
2681
2682 static inline record_t
UNTNOT(record_t r)2683 UNTNOT(record_t r)
2684 { return (record_t)(((uintptr_t)r)&~(uintptr_t)1);
2685 }
2686
2687 static int
IS_TNOT(record_t r)2688 IS_TNOT(record_t r)
2689 { return (uintptr_t)r & 0x1;
2690 }
2691
2692 static int
new_suspension(suspension * sp,term_t term,int is_tnot,term_t instance ARG_LD)2693 new_suspension(suspension *sp, term_t term, int is_tnot,
2694 term_t instance ARG_LD)
2695 { if ( !(sp->term=PL_record(term)) )
2696 return FALSE;
2697
2698 if ( unlikely(instance) )
2699 { if ( !(sp->instance=PL_record(instance)) )
2700 { PL_erase(sp->term);
2701 return FALSE;
2702 }
2703 sp->keys = suspension_keys(instance PASS_LD);
2704 } else
2705 { sp->instance = 0;
2706 sp->keys = NULL;
2707 }
2708
2709 sp->term = TNOT(sp->term, is_tnot);
2710
2711 return TRUE;
2712 }
2713
2714
2715 static cluster *
new_suspension_cluster(worklist * wl,term_t first,int is_tnot,term_t instance ARG_LD)2716 new_suspension_cluster(worklist *wl, term_t first, int is_tnot,
2717 term_t instance ARG_LD)
2718 { cluster *c;
2719 suspension s;
2720
2721 if ( !new_suspension(&s, first, is_tnot, instance PASS_LD) )
2722 return NULL;
2723
2724 if ( (c=wl->free_clusters) )
2725 { wl->free_clusters = c->next;
2726 c->type = CLUSTER_SUSPENSIONS;
2727 } else
2728 { c = PL_malloc(sizeof(*c));
2729 c->type = CLUSTER_SUSPENSIONS;
2730 initBuffer(&c->members);
2731 }
2732 addBuffer(&c->members, s, suspension);
2733
2734 return c;
2735 }
2736
2737 static void
free_suspension_cluster(cluster * c)2738 free_suspension_cluster(cluster *c)
2739 { suspension *base = baseBuffer(&c->members, suspension);
2740 size_t entries = entriesBuffer(&c->members, suspension);
2741 size_t i;
2742
2743 for(i=0; i<entries; i++)
2744 { suspension *s = &base[i];
2745
2746 PL_erase(UNTNOT(s->term));
2747 if ( s->instance )
2748 PL_erase(s->instance);
2749 if ( s->keys )
2750 free(s->keys);
2751 }
2752
2753 discardBuffer(&c->members);
2754 PL_free(c);
2755 }
2756
2757 static int
add_to_suspension_cluster(cluster * c,term_t sterm,int is_tnot,term_t instance ARG_LD)2758 add_to_suspension_cluster(cluster *c, term_t sterm, int is_tnot,
2759 term_t instance ARG_LD)
2760 { suspension s;
2761
2762 if ( !new_suspension(&s, sterm, is_tnot, instance PASS_LD) )
2763 return FALSE;
2764 addBuffer(&c->members, s, suspension);
2765
2766 return TRUE;
2767 }
2768
2769 static void
merge_suspension_cluster(cluster * to,cluster * from,int do_free)2770 merge_suspension_cluster(cluster *to, cluster *from, int do_free)
2771 { typedef suspension* Suspension;
2772
2773 addMultipleBuffer(&to->members,
2774 baseBuffer(&from->members, suspension*),
2775 entriesBuffer(&from->members, suspension*),
2776 Suspension);
2777 if ( do_free )
2778 { discardBuffer(&from->members);
2779 PL_free(from);
2780 }
2781 }
2782
2783
2784 static suspension *
get_suspension_from_cluster(cluster * c,size_t index)2785 get_suspension_from_cluster(cluster *c, size_t index)
2786 { DEBUG(CHK_SECURE, assert(index < entriesBuffer(&c->members, suspension)));
2787 return &fetchBuffer(&c->members, index, suspension);
2788 }
2789
2790 static void
free_cluster(cluster * c)2791 free_cluster(cluster *c)
2792 { if ( c->type == CLUSTER_ANSWERS )
2793 free_answer_cluster(c);
2794 else
2795 free_suspension_cluster(c);
2796 }
2797
2798 static int
acp_size(cluster * c)2799 acp_size(cluster *c)
2800 { return entriesBuffer(&c->members, answer);
2801 }
2802
2803 static int
scp_size(cluster * c)2804 scp_size(cluster *c)
2805 { return entriesBuffer(&c->members, suspension);
2806 }
2807
2808 /*******************************
2809 * TABLE WORKLIST *
2810 *******************************/
2811
2812 static worklist *
new_worklist(trie * trie)2813 new_worklist(trie *trie)
2814 { worklist *wl;
2815
2816 wl = PL_malloc(sizeof(*wl));
2817 memset(wl, 0, sizeof(*wl));
2818 wl->magic = WORKLIST_MAGIC;
2819 wl->table = trie;
2820 if ( trie->data.worklist == WL_GROUND )
2821 wl->ground = TRUE;
2822 initBuffer(&wl->delays);
2823 initBuffer(&wl->pos_undefined);
2824 trie->data.worklist = wl;
2825
2826 return wl;
2827 }
2828
2829
2830 static void
free_worklist(worklist * wl)2831 free_worklist(worklist *wl)
2832 { cluster *c, *next;
2833 trie *atrie;
2834
2835 assert(wl->magic == WORKLIST_MAGIC);
2836 wl->magic = 0;
2837
2838 if ( (atrie=wl->table) && atrie->data.worklist )
2839 { if ( atrie->data.worklist == wl )
2840 atrie->data.worklist = NULL;
2841 else
2842 Sdprintf("Oops, worklist trie doesn't point back at me!\n");
2843 }
2844
2845 for(c=wl->head; c; c = next)
2846 { next = c->next;
2847 free_cluster(c);
2848 }
2849 for(c=wl->free_clusters; c; c = next)
2850 { next = c->next;
2851 free_cluster(c);
2852 }
2853 discardBuffer(&wl->delays);
2854 discardBuffer(&wl->pos_undefined);
2855
2856 PL_free(wl);
2857 }
2858
2859
2860 static void
clean_worklist(worklist * wl)2861 clean_worklist(worklist *wl)
2862 { cluster *c, *next;
2863
2864 wl->riac = NULL;
2865 if ( wl->head )
2866 { for(c=wl->head; c; c = next)
2867 { next = c->next;
2868 free_cluster(c);
2869 }
2870 wl->head = wl->tail = NULL;
2871 }
2872
2873 if ( wl->free_clusters )
2874 { for(c=wl->free_clusters; c; c = next)
2875 { next = c->next;
2876 free_cluster(c);
2877 }
2878 wl->free_clusters = NULL;
2879 }
2880 }
2881
2882
2883 static void
complete_worklist(worklist * wl)2884 complete_worklist(worklist *wl)
2885 { clean_worklist(wl);
2886
2887 COMPLETE_WORKLIST(wl->table, set(wl->table, TRIE_COMPLETE));
2888 }
2889
2890
2891 static int
worklist_negative(worklist * wl)2892 worklist_negative(worklist *wl)
2893 { if ( !wl->negative )
2894 { wl->negative = TRUE;
2895 add_delay_worklist(wl);
2896 if ( wl->component->neg_status == SCC_NEG_NONE )
2897 wl->component->neg_status = SCC_NEG_DELAY;
2898 }
2899
2900 return TRUE;
2901 }
2902
2903
2904 static size_t
prune_answers_worklist(worklist * wl)2905 prune_answers_worklist(worklist *wl)
2906 { cluster *c;
2907 size_t gained = 0;
2908
2909 for(c=wl->head; c; c=c->next)
2910 { if ( c->type == CLUSTER_ANSWERS )
2911 gained += prune_answer_cluster(c);
2912 }
2913
2914 return gained;
2915 }
2916
2917
2918 /* The work is done if there is no answer cluster or there is
2919 no suspension right of the answer cluster
2920 */
2921
2922 static int
worklist_work_done(worklist * wl)2923 worklist_work_done(worklist *wl)
2924 { return !wl->riac || !wl->riac->next;
2925 }
2926
2927
2928 static void
wkl_append_left(worklist * wl,cluster * c)2929 wkl_append_left(worklist *wl, cluster *c)
2930 { if ( wl->head )
2931 { c->prev = NULL;
2932 c->next = wl->head;
2933 wl->head->prev = c;
2934 wl->head = c;
2935 } else
2936 { c->next = c->prev = NULL;
2937 wl->head = wl->tail = c;
2938 }
2939 }
2940
2941
2942 static void
wkl_append_right(worklist * wl,cluster * c)2943 wkl_append_right(worklist *wl, cluster *c)
2944 { if ( wl->tail )
2945 { c->next = NULL;
2946 c->prev = wl->tail;
2947 wl->tail->next = c;
2948 wl->tail = c;
2949 } else
2950 { c->next = c->prev = NULL;
2951 wl->head = wl->tail = c;
2952 }
2953 }
2954
2955
2956 static void
update_riac(worklist * wl,cluster * acp)2957 update_riac(worklist *wl, cluster *acp)
2958 { cluster *c;
2959
2960 if ( !acp->next ||
2961 acp->next->type == CLUSTER_ANSWERS )
2962 { for(c=acp->prev; c; c = c->prev)
2963 { if ( c->type == CLUSTER_ANSWERS )
2964 { wl->riac = c;
2965 return;
2966 }
2967 }
2968
2969 wl->riac = NULL;
2970 }
2971 }
2972
2973
2974 static void
wkl_swap_clusters(worklist * wl,cluster * acp,cluster * scp)2975 wkl_swap_clusters(worklist *wl, cluster *acp, cluster *scp)
2976 { cluster *a = acp->prev; /* before the couple */
2977 cluster *z = scp->next; /* after the couple */
2978
2979 assert(acp->next == scp);
2980
2981 if ( a ) a->next = scp; else wl->head = scp;
2982 if ( z ) z->prev = acp; else wl->tail = acp;
2983 scp->prev = a;
2984 acp->next = z;
2985 scp->next = acp;
2986 acp->prev = scp;
2987
2988 update_riac(wl, acp);
2989
2990 DEBUG(MSG_TABLING_WORK, print_worklist("Swapped: ", wl));
2991 }
2992
2993
2994 static void
potentially_add_to_global_worklist(worklist * wl ARG_LD)2995 potentially_add_to_global_worklist(worklist *wl ARG_LD)
2996 { if ( !wl->in_global_wl && !wl->executing )
2997 add_global_worklist(wl);
2998 }
2999
3000
3001 static int
wkl_add_answer(worklist * wl,trie_node * an ARG_LD)3002 wkl_add_answer(worklist *wl, trie_node *an ARG_LD)
3003 { potentially_add_to_global_worklist(wl PASS_LD);
3004 answer ans = {an};
3005
3006 if ( !answer_is_conditional(an) )
3007 wl->has_answers = TRUE;
3008
3009 if ( wl->head && wl->head->type == CLUSTER_ANSWERS )
3010 { add_to_answer_cluster(wl->head, &ans);
3011 } else
3012 { cluster *c = new_answer_cluster(wl, &ans);
3013 wkl_append_left(wl, c);
3014 if ( !wl->riac )
3015 wl->riac = c;
3016 }
3017 DEBUG(MSG_TABLING_WORK,
3018 { print_worklist("Added answer: ", wl);
3019 });
3020
3021 return TRUE;
3022 }
3023
3024
3025 static int
wkl_add_suspension(worklist * wl,term_t suspension,int is_tnot,term_t inst ARG_LD)3026 wkl_add_suspension(worklist *wl, term_t suspension, int is_tnot,
3027 term_t inst ARG_LD)
3028 { potentially_add_to_global_worklist(wl PASS_LD);
3029 if ( wl->tail && wl->tail->type == CLUSTER_SUSPENSIONS )
3030 { if ( !add_to_suspension_cluster(wl->tail, suspension, is_tnot, inst PASS_LD) )
3031 return FALSE;
3032 } else
3033 { cluster *c = new_suspension_cluster(wl, suspension, is_tnot, inst PASS_LD);
3034 if ( !c )
3035 return FALSE;
3036 wkl_append_right(wl, c);
3037 if ( c->prev && c->prev->type == CLUSTER_ANSWERS )
3038 wl->riac = c->prev;
3039 }
3040 DEBUG(MSG_TABLING_WORK, print_worklist("Added suspension: ", wl));
3041
3042 return TRUE;
3043 }
3044
3045
3046 #ifdef O_DEBUG
3047 static void
print_worklist(const char * prefix,worklist * wl)3048 print_worklist(const char *prefix, worklist *wl)
3049 { cluster *c;
3050
3051 Sdprintf("%s", prefix);
3052 for(c=wl->head; c; c=c->next)
3053 { if ( c->type == CLUSTER_ANSWERS )
3054 { Sdprintf("ACP(%d)%s ", acp_size(c), c == wl->riac ? "[RIAC]" : "");
3055 } else
3056 { Sdprintf("SCP(%d) ", scp_size(c));
3057 }
3058 }
3059 Sdprintf("\n");
3060 }
3061 #endif
3062
3063
3064
3065 /*******************************
3066 * PROLOG CONNECTION *
3067 *******************************/
3068
3069 static int
unify_fresh(term_t t,trie * atrie,Definition def,int create ARG_LD)3070 unify_fresh(term_t t, trie *atrie, Definition def, int create ARG_LD)
3071 { if ( create )
3072 { tbl_component *scc = tbl_create_subcomponent(atrie PASS_LD);
3073 worklist *wl = tbl_add_worklist(atrie, scc);
3074
3075 if ( wl )
3076 { wl->predicate = def;
3077 return PL_unify_term(t, PL_FUNCTOR, FUNCTOR_fresh2,
3078 PL_POINTER, scc,
3079 PL_POINTER, wl);
3080 } else
3081 { return FALSE;
3082 }
3083 } else
3084 { return PL_unify_atom(t, ATOM_fresh);
3085 }
3086 }
3087
3088
3089 static int
unify_complete_or_invalid(term_t t,trie * atrie,Definition def,int create ARG_LD)3090 unify_complete_or_invalid(term_t t, trie *atrie,
3091 Definition def, int create ARG_LD)
3092 { idg_node *n;
3093
3094 if ( (n=atrie->data.IDG) )
3095 { if ( n->falsecount > 0 )
3096 return PL_unify_atom(t, ATOM_invalid);
3097 if ( n->reevaluating )
3098 return unify_fresh(t, atrie, def, create PASS_LD);
3099 }
3100
3101 return PL_unify_atom(t, ATOM_complete);
3102 }
3103
3104
3105 /** unify_table_status(term_t t, trie *trie, Definition def, int create ARG_LD)
3106 *
3107 * @param `t` is unified with the status of `trie`. Possible values
3108 * are:
3109 *
3110 * - A worklist (if the trie is incomplete)
3111 * - `complete`
3112 * - `dynamic` (for pseudo answer tries representing an incremental
3113 * dynamic predicate)
3114 * - `fresh` (if `create` is `FALSE`)
3115 * - `fresh(SCC, WL)` (if `create` is `TRUE`)
3116 *
3117 * @param `create` If `TRUE`, we are going to use this worklist for
3118 * filling the trie. This is used by '$tbl_variant_table'/5 and
3119 * friends.
3120 */
3121
3122 static int
unify_table_status(term_t t,trie * trie,Definition def,int create ARG_LD)3123 unify_table_status(term_t t, trie *trie, Definition def, int create ARG_LD)
3124 { if ( true(trie, TRIE_COMPLETE) )
3125 { return unify_complete_or_invalid(t, trie, def, create PASS_LD);
3126 } else
3127 { worklist *wl = trie->data.worklist;
3128
3129 if ( WL_IS_WORKLIST(wl) )
3130 { if ( create && wl->component != LD->tabling.component )
3131 { DEBUG(MSG_TABLING_WORK,
3132 Sdprintf("Merging into %p (current = %p)\n",
3133 wl->component, LD->tabling.component));
3134 merge_component(wl->component);
3135 LD->tabling.component = wl->component;
3136 }
3137
3138 return PL_unify_pointer(t, wl);
3139 }
3140
3141 if ( wl == WL_DYNAMIC )
3142 return PL_unify_atom(t, ATOM_dynamic);
3143
3144 assert(!wl || wl == WL_GROUND);
3145 return unify_fresh(t, trie, def, create PASS_LD);
3146 }
3147 }
3148
3149
3150 static int
table_is_incomplete(trie * trie)3151 table_is_incomplete(trie *trie)
3152 { return ( WL_IS_WORKLIST(trie->data.worklist) &&
3153 false(trie, TRIE_COMPLETE) );
3154 }
3155
3156
3157 static int
unify_skeleton(trie * atrie,term_t wrapper,term_t skeleton ARG_LD)3158 unify_skeleton(trie *atrie, term_t wrapper, term_t skeleton ARG_LD)
3159 { if ( !wrapper )
3160 wrapper = PL_new_term_ref();
3161
3162 if ( atrie->data.variant && wrapper &&
3163 unify_trie_term(atrie->data.variant, NULL, wrapper PASS_LD) )
3164 { worklist *wl = atrie->data.worklist;
3165 Definition def = WL_IS_WORKLIST(wl) ? wl->predicate : NULL;
3166 int flags = true(atrie, TRIE_ISSHARED) ? AT_SHARED : AT_PRIVATE;
3167 return ( get_answer_table(def, wrapper, skeleton,
3168 NULL, flags|AT_NOCLAIM PASS_LD) != NULL);
3169 }
3170
3171 return FALSE;
3172 }
3173
3174
3175 static int
get_scc(term_t t,tbl_component ** cp)3176 get_scc(term_t t, tbl_component **cp)
3177 { void *ptr;
3178
3179 if ( PL_get_pointer_ex(t, &ptr) )
3180 { tbl_component *c = ptr;
3181
3182 if ( c->magic != COMPONENT_MAGIC )
3183 return PL_existence_error("table component", t),FALSE;
3184
3185 *cp = c;
3186
3187 return TRUE;
3188 }
3189
3190 return FALSE;
3191 }
3192
3193 static int
get_worklist(term_t t,worklist ** wlp ARG_LD)3194 get_worklist(term_t t, worklist **wlp ARG_LD)
3195 { void *ptr;
3196
3197 if ( PL_get_pointer(t, &ptr) )
3198 { worklist *wl = ptr;
3199 assert(wl->magic == WORKLIST_MAGIC);
3200 *wlp = wl;
3201 return TRUE;
3202 }
3203
3204 PL_type_error("worklist", t);
3205 return FALSE;
3206 }
3207
3208
3209 static int
tnot_get_worklist(term_t t,worklist ** wlp,int * is_tnot)3210 tnot_get_worklist(term_t t, worklist **wlp, int *is_tnot)
3211 { GET_LD
3212 void *ptr;
3213
3214 if ( PL_get_pointer(t, &ptr) )
3215 { worklist *wl = ptr;
3216 assert(wl->magic == WORKLIST_MAGIC);
3217 *wlp = wl;
3218 *is_tnot = FALSE;
3219 return TRUE;
3220 }
3221
3222 if ( PL_is_functor(t, FUNCTOR_tnot1) )
3223 { term_t a = PL_new_term_ref();
3224 _PL_get_arg(1, t, a);
3225 if ( PL_get_pointer(a, &ptr) )
3226 { worklist *wl = ptr;
3227 assert(wl->magic == WORKLIST_MAGIC);
3228 *wlp = wl;
3229 *is_tnot = TRUE;
3230 return TRUE;
3231 }
3232 }
3233
3234 PL_type_error("worklist", t);
3235 return FALSE;
3236 }
3237
3238
3239 static worklist *
tbl_add_worklist(trie * atrie,tbl_component * scc)3240 tbl_add_worklist(trie *atrie, tbl_component *scc)
3241 { worklist *wl;
3242
3243 if ( !WL_IS_WORKLIST(wl=atrie->data.worklist) )
3244 wl = new_worklist(atrie);
3245
3246 wl->component = scc;
3247 add_global_worklist(wl);
3248 add_newly_created_worklist(wl);
3249 clear(atrie, TRIE_COMPLETE);
3250
3251 return wl;
3252 }
3253
3254
3255 static int
destroy_answer_trie(trie * atrie)3256 destroy_answer_trie(trie *atrie)
3257 { if ( atrie->data.variant)
3258 { trie *vtrie = get_trie_from_node(atrie->data.variant);
3259
3260 if ( is_variant_trie(vtrie) )
3261 { DEBUG(MSG_TABLING_VTRIE_DEPENDENCIES,
3262 print_answer_table(atrie, "Delete answer trie for"));
3263
3264 if ( true(atrie, TRIE_ISSHARED) )
3265 { COMPLETE_WORKLIST(atrie, /* lock might be overkill */
3266 reset_answer_table(atrie, FALSE));
3267 } else
3268 trie_delete(vtrie, atrie->data.variant, TRUE);
3269
3270 return TRUE;
3271 }
3272 }
3273
3274 return FALSE;
3275 }
3276
3277
3278 static int
delayed_destroy_table(trie * atrie)3279 delayed_destroy_table(trie *atrie)
3280 { if ( table_is_incomplete(atrie) )
3281 { set(atrie, TRIE_ABOLISH_ON_COMPLETE);
3282 DEBUG(MSG_TABLING_ABOLISH,
3283 print_answer_table(atrie, "Scheduling for delayed abolish"));
3284 return TRUE;
3285 }
3286
3287 return FALSE;
3288 }
3289
3290
3291 /** '$tbl_destroy_table'(+Trie)
3292 *
3293 * Destroy a single trie table.
3294 */
3295
3296 static
3297 PRED_IMPL("$tbl_destroy_table", 1, tbl_destroy_table, 0)
3298 { trie *atrie;
3299
3300 if ( get_trie(A1, &atrie) )
3301 { if ( atrie->data.worklist == WL_DYNAMIC )
3302 return TRUE; /* quickly ignore dynamic pseudo tables */
3303
3304 if ( atrie->data.variant)
3305 { trie *vtrie = get_trie_from_node(atrie->data.variant);
3306
3307 if ( is_variant_trie(vtrie) )
3308 {
3309 #ifdef O_PLMT
3310 int mytid = PL_thread_self();
3311
3312 if ( true(atrie, TRIE_ISSHARED) )
3313 { LOCK_SHARED_TABLE(atrie);
3314 if ( !atrie->tid ) /* no owner */
3315 { take_trie(atrie, mytid);
3316 assert(!table_is_incomplete(atrie));
3317 reset_answer_table(atrie, FALSE);
3318 drop_trie(atrie);
3319 } else if ( atrie->tid == mytid ) /* I am the owner */
3320 { if ( !delayed_destroy_table(atrie) )
3321 { reset_answer_table(atrie, FALSE);
3322 drop_trie(atrie);
3323 cv_broadcast(&GD->tabling.cvar);
3324 }
3325 } else
3326 { set(atrie, TRIE_ABOLISH_ON_COMPLETE);
3327 DEBUG(MSG_TABLING_ABOLISH,
3328 print_answer_table(atrie, "Scheduling for delayed abolish"));
3329 }
3330 UNLOCK_SHARED_TABLE(atrie);
3331 } else
3332 #endif
3333 { if ( !delayed_destroy_table(atrie) )
3334 trie_delete(vtrie, atrie->data.variant, TRUE);
3335 }
3336
3337 return TRUE;
3338 }
3339 }
3340
3341 return PL_type_error("table", A1);
3342 }
3343
3344 return FALSE;
3345 }
3346
3347
3348 /** '$tbl_pop_worklist'(+SCC, -Worklist) is semidet.
3349 *
3350 * Pop next worklist from the component.
3351 */
3352
3353 static
3354 PRED_IMPL("$tbl_pop_worklist", 2, tbl_pop_worklist, 0)
3355 { PRED_LD
3356 tbl_component *scc;
3357
3358 if ( get_scc(A1, &scc) )
3359 { if ( scc->status == SCC_ACTIVE )
3360 { worklist *wl;
3361
3362 if ( (wl=pop_worklist(scc PASS_LD)) )
3363 return PL_unify_pointer(A2, wl);
3364
3365 if (
3366 #ifndef O_AC_EAGER
3367 scc->simplifications ||
3368 #endif
3369 scc->neg_status != SCC_NEG_NONE )
3370 { if ( (wl=negative_worklist(scc PASS_LD)) )
3371 return PL_unify_pointer(A2, wl);
3372 }
3373 }
3374 }
3375
3376 return FALSE;
3377 }
3378
3379 /** '$tbl_wkl_add_answer'(+Worklist, +Answer, +Delays, -Complete) is semidet.
3380 *
3381 * Add an answer to the worklist's trie and the worklist answer cluster
3382 * using trie_insert_new/3. Fails if a variant of Term is already in
3383 * Worklist.
3384 *
3385 * @arg Answer is either a ret/N (normal tabling) or a (ret/N)/ModeArgs
3386 * term (answer subsumption).
3387 */
3388
3389 static inline size_t
pred_max_table_answer_size(const Definition def ARG_LD)3390 pred_max_table_answer_size(const Definition def ARG_LD)
3391 { size_t limit;
3392
3393 limit = def->tabling ? def->tabling->answer_abstract : (size_t)-1;
3394 if ( limit == (size_t)-1 )
3395 limit = LD->tabling.restraint.max_table_answer_size;
3396
3397 return limit;
3398 }
3399
3400
3401 static
3402 PRED_IMPL("$tbl_wkl_add_answer", 4, tbl_wkl_add_answer, 0)
3403 { PRED_LD
3404 worklist *wl;
3405
3406 if ( get_worklist(A1, &wl PASS_LD) )
3407 { Word kp;
3408 trie_node *node;
3409 atom_t action;
3410 size_abstract sa = {.from_depth = 2};
3411 int rc;
3412
3413 #ifdef O_PLMT
3414 DEBUG(0, assert(false(wl->table, TRIE_ISSHARED) || wl->table->tid));
3415 #endif
3416
3417 kp = valTermRef(A2);
3418 if ( true(wl->table, TRIE_ISMAP) )
3419 return wkl_mode_add_answer(wl, A2, A3 PASS_LD);
3420
3421 sa.size = pred_max_table_answer_size(wl->predicate PASS_LD);
3422 rc = trie_lookup_abstract(wl->table, NULL, &node, kp,
3423 TRUE, &sa, NULL PASS_LD);
3424 if ( rc > 0 ) /* ok or abstracted */
3425 { idg_node *idg;
3426
3427 if ( rc == TRIE_ABSTRACTED )
3428 { atom_t action = LD->tabling.restraint.max_table_answer_size_action;
3429
3430 DEBUG(MSG_TABLING_RESTRAINT,
3431 print_answer_table(wl->table, "Max answer size exceeded"));
3432
3433 if ( action == ATOM_bounded_rationality )
3434 { if ( !add_radial_restraint() )
3435 return FALSE;
3436 } else if ( action == ATOM_fail ||
3437 !tbl_wl_tripwire(wl, action, ATOM_max_table_answer_size) )
3438 { trie_delete(wl->table, node, TRUE);
3439 return FALSE;
3440 }
3441 }
3442
3443 if ( node->value )
3444 { if ( node->value == ATOM_trienode )
3445 { if ( true(node, TN_IDG_DELETED) )
3446 { clear(node, TN_IDG_DELETED);
3447 goto update_dl;
3448 } else
3449 { if ( answer_is_conditional(node) )
3450 { if ( update_delay_list(wl, node, A2, A3 PASS_LD) == UDL_COMPLETE )
3451 return PL_unify_atom(A4, ATOM_cut);
3452 }
3453 }
3454
3455 return FALSE; /* already in trie */
3456 }
3457 return PL_permission_error("modify", "trie_key", A2);
3458 } else if ( (action=tripwire_answers_for_subgoal(wl PASS_LD)) )
3459 { DEBUG(MSG_TABLING_RESTRAINT,
3460 print_answer_table(wl->table, "Answer count exceeded"));
3461 if ( action == ATOM_bounded_rationality )
3462 { term_t gen;
3463
3464 trie_delete(wl->table, node, TRUE);
3465
3466 if ( !(gen = PL_new_term_ref()) ||
3467 !generalise_answer_substitution(A2, gen PASS_LD) ||
3468 !add_answer_count_restraint() )
3469 return FALSE;
3470
3471 kp = valTermRef(gen);
3472 rc = trie_lookup(wl->table, NULL, &node, kp, TRUE, NULL PASS_LD);
3473 if ( rc == TRUE )
3474 { if ( !PL_unify_atom(A4, ATOM_cut) )
3475 return FALSE;
3476 set_trie_value_word(wl->table, node, ATOM_trienode);
3477 if ( update_delay_list(wl, node, A2, A3 PASS_LD) == UDL_FALSE )
3478 return FALSE;
3479 return TRUE;
3480 } else
3481 { return trie_error(rc, gen);
3482 }
3483 } else
3484 { if ( tbl_wl_tripwire(wl, action, ATOM_max_answers_for_subgoal) )
3485 { goto add_anyway;
3486 } else
3487 { trie_delete(wl->table, node, TRUE);
3488 return FALSE;
3489 }
3490 }
3491 } else
3492 { add_anyway:
3493 set_trie_value_word(wl->table, node, ATOM_trienode);
3494 if ( (idg=wl->table->data.IDG) && idg->reevaluating )
3495 { set(node, TN_IDG_ADDED);
3496 idg->new_answer = TRUE;
3497 }
3498
3499 update_dl:
3500 rc = update_delay_list(wl, node, A2, A3 PASS_LD);
3501
3502 switch(rc)
3503 { case UDL_FALSE:
3504 return FALSE;
3505 case UDL_COMPLETE:
3506 if ( !PL_unify_atom(A4, ATOM_cut) )
3507 return FALSE;
3508 default:
3509 ;
3510 }
3511
3512 return wkl_add_answer(wl, node PASS_LD);
3513 }
3514 }
3515
3516 return trie_error(rc, A2);
3517 }
3518
3519 return FALSE;
3520 }
3521
3522 /** wkl_mode_add_answer(worklist *wl, term_t answer, term_t delays ARG_LD)
3523 *
3524 * Add an answer Args for moded arguments to the worklist's trie and the
3525 * worklist answer cluster using trie_insert_new/3 and mode directed
3526 * tabling.
3527 *
3528 * @param answer is a term Return/ModedArgs
3529 */
3530
3531 #define AS_NEW_DEFINED 0x1
3532 #define AS_OLD_DEFINED 0x2
3533
3534 typedef struct sa_context
3535 { worklist *wl;
3536 trie_node *root;
3537 term_t skel;
3538 term_t argv; /* Arguments for '$tabling':update/8 */
3539 term_t delays;
3540 int flags; /* AS_*_DEFINED */
3541 int garbage; /* # not pruned dummy nodes */
3542 } sa_context;
3543
3544 #define TRIE_MAP_FALSE ((void*)1) /* error while mapping */
3545 #define TRIE_MAP_DONE ((void*)2) /* found existing answer subsuming new */
3546 #define TRIE_MAP_TRUE NULL /* mapped all nodes */
3547
3548 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3549 add_subsuming_answer() adds a new aggregate value as a secondary node
3550 below `root`. If `old` is present, the new value replaces the one
3551 pointer to by `old`. This poses two problems.
3552
3553 - We must remove `old` from its answer cluster. We have little clue
3554 about which answer cluster holds the `old` answer though. Adding
3555 data in the node to help finding the cluster is not that useful
3556 as clusters can be merged and the answer array may be relocated.
3557
3558 - We must prune `old` from the trie. If we do not, we waste a lot
3559 of memory and update_subsuming_answers(), using map_trie_node()
3560 becomes slow, resulting in quadratic complexity.
3561
3562 These issues are currently resolved by calling prune_answers_worklist()
3563 and prune_trie() if there are more than 10 deleted answers. The number
3564 10 should depend on
3565
3566 - The ratio (deleted answers/real answers) in the worklist's answer
3567 clusters as the prune time is proportional to the total number
3568 of deleted plus real answers in these clusters.
3569 - The ration (prunable trie nodes/total trie nodes) in the answer
3570 trie as this refers wasted memory and map_trie_node() walking over
3571 deleted nodes.
3572 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
3573
3574 static int
add_subsuming_answer(worklist * wl,trie_node * root,term_t skel,trie_node * old,term_t margs,term_t delays ARG_LD)3575 add_subsuming_answer(worklist *wl, trie_node *root, term_t skel,
3576 trie_node *old, term_t margs, term_t delays ARG_LD)
3577 { trie_node *node;
3578 Word vp;
3579 int rc;
3580
3581 vp = valTermRef(margs);
3582 rc = trie_lookup(wl->table, root, &node, vp, TRUE, NULL PASS_LD);
3583 if ( rc == TRUE )
3584 { if ( false(node, TN_SECONDARY) )
3585 { node->value = ATOM_trienode;
3586 set(node, TN_SECONDARY);
3587
3588 if ( old )
3589 trie_delete(wl->table, old, FALSE);
3590
3591 if ( update_delay_list(wl, node, skel, delays PASS_LD) == UDL_FALSE )
3592 return FALSE;
3593
3594 wkl_add_answer(wl, node PASS_LD);
3595 return TRUE;
3596 } else if ( answer_is_conditional(node) )
3597 { update_delay_list(wl, node, skel, delays PASS_LD);
3598 }
3599
3600 return FALSE; /* no change */
3601 } else
3602 { term_t trie;
3603
3604 return ( (trie = PL_new_term_ref()) &&
3605 _PL_unify_atomic(trie, wl->table->symbol) &&
3606 trie_error(rc, trie) );
3607 }
3608 }
3609
3610
3611 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3612 Calls
3613
3614 '$tabling':update(+Flags, +Head, +Module, +Old, +New, -Agg, -Action)
3615 av+0, av+1, av+2, av+3, av+4, av+5, av+6
3616 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
3617
3618 static void *
update_subsuming_answer(trie_node * node,void * ptr)3619 update_subsuming_answer(trie_node *node, void *ptr)
3620 { sa_context *ctx = ptr;
3621
3622 if ( true(node, TN_SECONDARY) )
3623 { GET_LD
3624 static predicate_t PRED_update7 = 0;
3625 term_t av = ctx->argv;
3626 term_t agg = av+5;
3627 term_t action = av+6;
3628 atom_t conditional = answer_is_conditional(node) ? 0
3629 : AS_OLD_DEFINED;
3630
3631 if ( !PRED_update7 )
3632 PRED_update7 = PL_predicate("update", 7, "$tabling");
3633
3634 if ( tbl_put_moded_args(av+3, node PASS_LD) &&
3635 PL_put_integer(av+0, ctx->flags|conditional) &&
3636 PL_put_variable(agg) &&
3637 PL_put_variable(action) &&
3638 PL_call_predicate(NULL, PL_Q_PASS_EXCEPTION, PRED_update7, av) )
3639 { trie_node *del;
3640 atom_t action;
3641
3642 DEBUG(MSG_TABLING_MODED,
3643 { Sdprintf("Updated answer: ");
3644 PL_write_term(Serror, av+3, 1200, 0);
3645 Sdprintf(" to ");
3646 PL_write_term(Serror, agg, 1200, PL_WRT_NEWLINE);
3647 });
3648
3649 if ( !PL_get_atom_ex(av+6, &action) )
3650 return TRIE_MAP_FALSE;
3651
3652 if ( action == ATOM_done )
3653 return TRIE_MAP_DONE;
3654 else if ( action == ATOM_keep )
3655 del = NULL;
3656 else
3657 del = node;
3658
3659 if ( add_subsuming_answer(ctx->wl, ctx->root, ctx->skel, del, agg,
3660 ctx->delays PASS_LD) )
3661 return TRIE_MAP_TRUE;
3662
3663 return TRIE_MAP_FALSE;
3664 } else
3665 { if ( PL_exception(0) )
3666 return TRIE_MAP_FALSE;
3667 DEBUG(MSG_TABLING_MODED, Sdprintf("No change!\n"));
3668 return TRIE_MAP_TRUE;
3669 }
3670 } else
3671 { if ( !node->children.any )
3672 ctx->garbage++;
3673
3674 return TRIE_MAP_TRUE;
3675 }
3676 }
3677
3678
3679 static int
update_subsuming_answers(worklist * wl,trie_node * root,term_t skel,term_t margs,term_t delays ARG_LD)3680 update_subsuming_answers(worklist *wl, trie_node *root, term_t skel,
3681 term_t margs, term_t delays ARG_LD)
3682 { Word ldlp;
3683 Word gdlp;
3684
3685 sa_context ctx = { .wl = wl,
3686 .root = root,
3687 .skel = skel,
3688 .delays = delays,
3689 .flags = 0,
3690 .garbage = 0
3691 };
3692
3693 if ( !( (ctx.argv = PL_new_term_refs(7)) &&
3694 PL_put_functor(ctx.argv+1, wl->predicate->functor->functor) &&
3695 PL_put_atom(ctx.argv+2, wl->predicate->module->name) &&
3696 PL_put_term(ctx.argv+4, margs) ) )
3697 return FALSE;
3698
3699 deRef2(valTermRef(LD->tabling.delay_list), gdlp);
3700 gdlp = argTermP(*gdlp, 0);
3701 deRef(gdlp);
3702 deRef2(valTermRef(delays), ldlp);
3703
3704 if ( isNil(*ldlp) && isNil(*gdlp) )
3705 ctx.flags = AS_NEW_DEFINED;
3706
3707 if ( map_trie_node(root, update_subsuming_answer, &ctx) == TRIE_MAP_FALSE )
3708 return FALSE;
3709
3710 if ( ctx.garbage > 10 )
3711 { size_t gained = prune_answers_worklist(wl);
3712 DEBUG(MSG_TABLING_MODED,
3713 Sdprintf("Pruned %zd answers\n", gained));
3714 (void)gained;
3715 prune_trie(wl->table, root, NULL, NULL);
3716 }
3717
3718 return TRUE;
3719 }
3720
3721
3722 static int
wkl_mode_add_answer(worklist * wl,term_t answer,term_t delays ARG_LD)3723 wkl_mode_add_answer(worklist *wl, term_t answer, term_t delays ARG_LD)
3724 { Word kp;
3725 trie_node *root;
3726 int rc;
3727 term_t av = PL_new_term_refs(2);
3728 term_t skel = av+0;
3729 term_t margs = av+1;
3730
3731 DEBUG(MSG_TABLING_MODED,
3732 { print_answer_table(wl->table, "");
3733 });
3734
3735 kp = valTermRef(answer);
3736 deRef(kp);
3737 if ( hasFunctor(*kp, FUNCTOR_divide2) )
3738 { kp = argTermP(*kp, 0);
3739 *valTermRef(skel) = linkVal(kp);
3740 *valTermRef(margs) = linkVal(kp+1);
3741 } else
3742 { return PL_domain_error("moded_answer", answer);
3743 }
3744
3745 rc = trie_lookup(wl->table, NULL, &root, kp, TRUE, NULL PASS_LD);
3746 if ( rc == TRUE )
3747 { if ( true(root, TN_PRIMARY) )
3748 { return update_subsuming_answers(wl, root, skel, margs, delays PASS_LD);
3749 } else
3750 { DEBUG(MSG_TABLING_MODED,
3751 { Sdprintf("First answer: ");
3752 PL_write_term(Serror, margs, 1200, PL_WRT_NEWLINE);
3753 });
3754
3755 set_trie_value_word(wl->table, root, ATOM_trienode);
3756 return add_subsuming_answer(wl, root, skel, NULL, margs, delays PASS_LD);
3757 }
3758 } else
3759 { return trie_error(rc, skel);
3760 }
3761 }
3762
3763
3764 /** '$tbl_wkl_add_suspension'(+Worklist, +Suspension) is det.
3765 *
3766 * Add a suspension to the worklist.
3767 */
3768
3769 static
3770 PRED_IMPL("$tbl_wkl_add_suspension", 2, tbl_wkl_add_suspension, 0)
3771 { PRED_LD
3772 worklist *wl;
3773 int is_tnot;
3774
3775 if ( tnot_get_worklist(A1, &wl, &is_tnot) )
3776 return wkl_add_suspension(wl, A2, is_tnot, 0 PASS_LD);
3777
3778 return FALSE;
3779 }
3780
3781 /** '$tbl_wkl_add_suspension'(+Worklist, +Instance, +Suspension) is det.
3782 *
3783 * Add a suspension to the worklist for call subsumtive tabling. Only
3784 * answers that unify with Instance must be passed down to Suspension.
3785 */
3786
3787 static
3788 PRED_IMPL("$tbl_wkl_add_suspension", 3, tbl_wkl_add_suspension, 0)
3789 { PRED_LD
3790 worklist *wl;
3791 int is_tnot;
3792
3793 if ( tnot_get_worklist(A1, &wl, &is_tnot) )
3794 return wkl_add_suspension(wl, A3, is_tnot, A2 PASS_LD);
3795
3796 return FALSE;
3797 }
3798
3799
3800
3801 /** '$tbl_wkl_make_follower'(+Worklist) is det.
3802 *
3803 * Turn a worklist that used to be a leader into a follower after
3804 * merging it with a parent SCC. This implies we must move the answer
3805 * clusters in front of the dependency clusters.
3806 */
3807
3808 static
3809 PRED_IMPL("$tbl_wkl_make_follower", 1, tbl_wkl_make_follower, 0)
3810 { PRED_LD
3811 worklist *wl;
3812
3813 if ( get_worklist(A1, &wl PASS_LD) )
3814 { cluster *scp = NULL;
3815 cluster *acp = NULL;
3816 cluster *c, *next;
3817
3818 for(c=wl->head; c; c=next)
3819 { next = c->next;
3820
3821 if ( c->type == CLUSTER_ANSWERS )
3822 { if ( acp )
3823 { merge_answer_clusters(acp, c);
3824 free_answer_cluster(c);
3825 } else
3826 { acp = c;
3827 acp->prev = acp->next = NULL;
3828 }
3829 } else
3830 { if ( scp )
3831 { merge_suspension_cluster(scp, c, TRUE);
3832 } else
3833 { scp = c;
3834 scp->prev = scp->next = NULL;
3835 }
3836 }
3837 }
3838
3839 wl->head = wl->tail = NULL;
3840 if ( acp )
3841 { wkl_append_left(wl, acp);
3842 wl->riac = acp;
3843 } else
3844 { wl->riac = NULL;
3845 }
3846 if ( scp )
3847 wkl_append_right(wl, scp);
3848
3849 if ( acp && scp )
3850 add_global_worklist(wl);
3851
3852 return TRUE;
3853 }
3854
3855 return FALSE;
3856 }
3857
3858
3859 /** '$tbl_wkl_done'(+Worklist) is semidet.
3860 *
3861 * True if the worklist is complete
3862 */
3863
3864 static
3865 PRED_IMPL("$tbl_wkl_done", 1, tbl_wkl_done, 0)
3866 { PRED_LD
3867 worklist *wl;
3868
3869 return get_worklist(A1, &wl PASS_LD) && worklist_work_done(wl);
3870 }
3871
3872 /** '$tbl_wkl_negative'(+Worklist) is semidet.
3873 *
3874 * True if the worklist is complete
3875 */
3876
3877 static
3878 PRED_IMPL("$tbl_wkl_negative", 1, tbl_wkl_negative, 0)
3879 { PRED_LD
3880 worklist *wl;
3881
3882 return get_worklist(A1, &wl PASS_LD) && worklist_negative(wl);
3883 }
3884
3885
3886 /** '$tbl_tbl_wkl_is_false'(+Worklist) is semidet.
3887 *
3888 * True if the worklist is is a negative node that is true (has no
3889 * definite solutions). This is used at the end of negation_suspend/4,
3890 * after we delayed the negation. This means we must fail if we have
3891 * definite answers, but succeed if there are conditional answers as
3892 * simplification has not yet been done.
3893 */
3894
3895 static
3896 PRED_IMPL("$tbl_wkl_is_false", 1, tbl_wkl_is_false, 0)
3897 { PRED_LD
3898 worklist *wl;
3899
3900 if ( get_worklist(A1, &wl PASS_LD) )
3901 { assert(wl->negative);
3902
3903 return wl->neg_delayed && !wl->has_answers;
3904 }
3905
3906 return FALSE;
3907 }
3908
3909 /** '$tbl_wkl_answer_trie'(+Worklist, -Trie) is det.
3910 *
3911 * True when Trie is the answer trie associated with Worklist
3912 */
3913
3914 static
3915 PRED_IMPL("$tbl_wkl_answer_trie", 2, tbl_wkl_answer_trie, 0)
3916 { GET_LD
3917 worklist *wl;
3918
3919 return ( get_worklist(A1, &wl PASS_LD) &&
3920 PL_unify_atom(A2, wl->table->symbol) );
3921 }
3922
3923
3924 /** '$tbl_wkl_work'(+Worklist, -Answer, -ModeArgs,
3925 * -Goal, -Continuation, -Wrapper, -TargetTable,
3926 * -Delays) is nondet.
3927 *
3928 * True when Answer must be tried on Suspension. Backtracking
3929 * basically does
3930 *
3931 * ```
3932 * member(Answer, RIAC),
3933 * member(Suspension, LastSuspensionCluster)
3934 * ```
3935 *
3936 * If the carthesian product is exhausted it tries to re-start using the
3937 * possible new RIAC and SCP. During its execution, worklist->executing
3938 * is TRUE to avoid the worklist to become part of the global worklist
3939 * again.
3940 *
3941 * This replaces table_get_work/3 from the pure Prolog implementation.
3942 */
3943
3944 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3945 Unify the 4 arguments of the dependecy structure with subsequent 4
3946 output arguments.
3947 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
3948
3949 static inline void
unify_arg_term(term_t a,Word v ARG_LD)3950 unify_arg_term(term_t a, Word v ARG_LD)
3951 { Word p = valTermRef(a);
3952
3953 deRef(p);
3954 DEBUG(CHK_SECURE, assert(isVar(*p)));
3955 Trail(p, linkVal(v));
3956 }
3957
3958 static int
unify_dependency(term_t a0,term_t dependency,worklist * wl,trie_node * answer ARG_LD)3959 unify_dependency(term_t a0, term_t dependency,
3960 worklist *wl, trie_node *answer ARG_LD)
3961 { if ( likely(ensureStackSpace__LD(10, 5, ALLOW_GC PASS_LD)) )
3962 { term_t srcskel = PL_new_term_ref();
3963 Word dp = valTermRef(dependency);
3964 Functor f;
3965
3966 deRef(dp);
3967 if ( unlikely(!isTerm(*dp)) )
3968 return FALSE;
3969 f = valueTerm(*dp);
3970
3971 unify_arg_term(srcskel, &f->arguments[0] PASS_LD); /* SrcSkeleton */
3972 unify_arg_term(a0+1, &f->arguments[1] PASS_LD); /* Continuation */
3973 unify_arg_term(a0+2, &f->arguments[2] PASS_LD); /* TargetSkeleton */
3974 unify_arg_term(a0+3, &f->arguments[3] PASS_LD); /* TargetWL */
3975 unify_arg_term(a0+4, &f->arguments[4] PASS_LD); /* Delays */
3976
3977 if ( !PL_unify(srcskel, a0+0) )
3978 return FALSE;
3979 if ( !idg_set_current_wl(a0+3 PASS_LD) )
3980 return FALSE;
3981
3982 if ( unlikely(!answer) ) /* negative delay */
3983 { Word p = allocGlobalNoShift(3);
3984
3985 assert(p);
3986 p[0] = FUNCTOR_dot2;
3987 p[1] = wl->table->symbol;
3988
3989 push_delay_list(p PASS_LD);
3990 } else if ( unlikely(answer_is_conditional(answer)) )
3991 { word p5 = delay_to_data(answer, valTermRef(a0+0) PASS_LD);
3992 Word p = allocGlobalNoShift(6);
3993 assert(p);
3994
3995 p[0] = FUNCTOR_dot2;
3996 p[1] = consPtr(&p[3], TAG_COMPOUND|STG_GLOBAL);
3997 p[3] = FUNCTOR_plus2;
3998 p[4] = wl->table->symbol;
3999 p[5] = p5;
4000
4001 push_delay_list(p PASS_LD);
4002 }
4003
4004 return TRUE;
4005 }
4006
4007 return FALSE;
4008 }
4009
4010
4011 /* Unify `term` with the Prolog term represented by `node`. Note that
4012 * the node can be a secondary value in the case of answer subsumption,
4013 * in which case a term Ret/ModeArgs is created.
4014 */
4015
4016 static int
tbl_unify_answer(trie_node * node,term_t term ARG_LD)4017 tbl_unify_answer(trie_node *node, term_t term ARG_LD)
4018 { if ( node )
4019 { if ( unlikely(true(node, TN_SECONDARY)) )
4020 { term_t av = PL_new_term_refs(2);
4021
4022 return ( unify_trie_term(node, &node, av+1 PASS_LD) &&
4023 unify_trie_term(node, NULL, av+0 PASS_LD) &&
4024 PL_cons_functor_v(av+0, FUNCTOR_divide2, av) &&
4025 PL_unify_output(term, av+0) );
4026 } else
4027 { return unify_trie_term(node, NULL, term PASS_LD);
4028 }
4029 }
4030
4031 return TRUE; /* for negative dummy solutions */
4032 }
4033
4034
4035 static int
tbl_put_moded_args(term_t t,trie_node * node ARG_LD)4036 tbl_put_moded_args(term_t t, trie_node *node ARG_LD)
4037 { if ( node )
4038 { if ( unlikely(true(node, TN_SECONDARY)) )
4039 return unify_trie_term(node, NULL, t PASS_LD);
4040 else
4041 return put_trie_value(t, node PASS_LD); /* TBD: Can become ATOM_trienode */
4042 } else /* negative dummy solution */
4043 { *valTermRef(t) = ATOM_trienode;
4044 return TRUE;
4045 }
4046 }
4047
4048
4049 static int
advance_wkl_state(wkl_step_state * state)4050 advance_wkl_state(wkl_step_state *state)
4051 { next:
4052
4053 if ( --state->suspensions.here < state->suspensions.base )
4054 { state->suspensions.here = state->suspensions.top;
4055 if ( --state->acp_index == 0 )
4056 { cluster *acp, *scp;
4057
4058 /* Merge adjacent suspension clusters */
4059 if ( (scp=state->scp)->prev && scp->prev->type == CLUSTER_SUSPENSIONS )
4060 { scp->prev->next = scp->next;
4061 scp->next->prev = scp->prev;
4062 merge_suspension_cluster(scp->prev, scp, FALSE);
4063 seekBuffer(&scp->members, 0, record_t);
4064 scp->next = state->list->free_clusters;
4065 state->list->free_clusters = scp;
4066 }
4067
4068 /* Merge adjacent answer clusters */
4069 if ( (acp=state->acp)->next && acp->next->type == CLUSTER_ANSWERS )
4070 { acp->prev->next = acp->next;
4071 acp->next->prev = acp->prev;
4072 merge_answer_clusters(acp->next, acp);
4073 seekBuffer(&acp->members, 0, answer);
4074 acp->next = state->list->free_clusters;
4075 state->list->free_clusters = acp;
4076 }
4077
4078 /* If more work, re-initialize */
4079 if ( (acp=state->list->riac) && (scp=acp->next) )
4080 { DEBUG(MSG_TABLING_WORK,
4081 print_worklist("Next step: ", state->list));
4082 assert(acp->type == CLUSTER_ANSWERS);
4083 assert(scp->type == CLUSTER_SUSPENSIONS);
4084 wkl_swap_clusters(state->list, acp, scp);
4085 state->acp = acp;
4086 state->scp = scp;
4087 state->acp_index = acp_size(acp);
4088
4089 if ( state->acp_index > 0 && scp_size(scp) > 0 )
4090 { state->suspensions.base = get_suspension_from_cluster(scp, 0);
4091 state->suspensions.top = get_suspension_from_cluster(scp, scp_size(scp)-1);
4092 state->suspensions.here = state->suspensions.top;
4093 goto next_answer;
4094 } else
4095 { goto next;
4096 }
4097 }
4098
4099 return FALSE;
4100 } else
4101 { next_answer:
4102 state->answer = get_answer_from_cluster(state->acp, state->acp_index-1);
4103 state->keys_inited = 0;
4104 }
4105 }
4106
4107 return TRUE;
4108 }
4109
4110
4111 /**
4112 * '$tbl_wkl_work'(+WorkList,
4113 * -Answer,
4114 * -Continuation, -TargetSkeleton, -TargetWorklist,
4115 * -Delays)
4116 */
4117
4118 static
4119 PRED_IMPL("$tbl_wkl_work", 6, tbl_wkl_work, PL_FA_NONDETERMINISTIC)
4120 { PRED_LD
4121 wkl_step_state *state;
4122 trie_node *can = NULL;
4123
4124 switch( CTX_CNTRL )
4125 { case FRG_FIRST_CALL:
4126 { worklist *wl;
4127
4128 if ( get_worklist(A1, &wl PASS_LD) )
4129 { cluster *acp, *scp;
4130
4131 if ( (acp=wl->riac) && (scp=acp->next) )
4132 { int sz_acp = acp_size(acp);
4133 int sz_scp = scp_size(scp);
4134
4135 wkl_swap_clusters(wl, acp, scp);
4136
4137 if ( sz_acp > 0 && sz_scp > 0 )
4138 { DEBUG(MSG_TABLING_WORK,
4139 print_worklist("First step: ", wl));
4140 state = allocForeignState(sizeof(*state));
4141 state->list = wl;
4142 state->acp = acp;
4143 state->scp = scp;
4144 state->acp_index = sz_acp;
4145 state->answer = get_answer_from_cluster(acp, sz_acp-1);
4146 state->suspensions.base = get_suspension_from_cluster(scp, 0);
4147 state->suspensions.top = get_suspension_from_cluster(scp, sz_scp-1);
4148 state->suspensions.here = state->suspensions.top;
4149 state->keys_inited = 0;
4150 wl->executing = TRUE;
4151
4152 break;
4153 }
4154 }
4155 }
4156
4157 return FALSE;
4158 }
4159 case FRG_REDO:
4160 state = CTX_PTR;
4161 break;
4162 case FRG_CUTTED:
4163 state = CTX_PTR;
4164 state->list->executing = FALSE;
4165 freeForeignState(state, sizeof(*state));
4166 return TRUE;
4167 default:
4168 assert(0);
4169 return FALSE;
4170 }
4171
4172 Mark(fli_context->mark);
4173
4174 do
4175 { const suspension *sp;
4176 term_t susp;
4177 trie_node *an = state->answer->node;
4178
4179 /* Ignore (1) removed answer due to simplification
4180 * (2) dummy restart for delayed negation,
4181 * (3) conditional answer for tnot
4182 */
4183
4184 if ( (an != NULL && an->value == 0) ) /* removed answer */
4185 { state->suspensions.here = state->suspensions.base;/* skip all suspensions */
4186 continue;
4187 }
4188
4189 sp = state->suspensions.here;
4190
4191 if ( (an == NULL && !IS_TNOT(sp->term)) ||
4192 (an != NULL && IS_TNOT(sp->term) && answer_is_conditional(an)) )
4193 continue;
4194
4195 /* We got an answer we want to pass the suspension cluster.
4196 * Unify A2 with it and get the first suspension.
4197 */
4198
4199 if ( can != an ) /* reuse the answer */
4200 { if ( can )
4201 Undo(fli_context->mark);
4202 if ( !tbl_unify_answer(an, A2 PASS_LD) )
4203 break; /* resource error */
4204 can = an;
4205 }
4206
4207 /* WFS: need to add a positive node to the delay list if `an`
4208 * is conditional. The positive node contains the variant
4209 * we continue and `an`, but is _independant_ from the
4210 * condition on `an`.
4211 */
4212
4213 /* Call subsumption: filter out suspensions for which the
4214 * answer does not unify with the answer skeleton for the
4215 * subsumed table. Note that this implies that we often
4216 * hold the same answer (an) against multiple suspensions
4217 * and therefore we avoid unifying A2 with `an` multiple
4218 * times. This block may (1) just be traversed without
4219 * side effects, (2) `break` on resource errors or
4220 * (3) `continue`, calling advance_wkl_state() to skip
4221 * if this suspension is not applicable to this answer
4222 * instance.
4223 */
4224
4225 if ( sp->instance && an )
4226 { int rc;
4227
4228 if ( !state->keys_inited )
4229 { Word p = valTermRef(A2);
4230 Functor f;
4231 size_t arity, i;
4232
4233 deRef(p);
4234 assert(isTerm(*p));
4235 f = valueTerm(*p);
4236 arity = arityFunctor(f->definition);
4237
4238 if ( arity > SINDEX_MAX )
4239 arity = SINDEX_MAX;
4240
4241 for(i=0; i<arity; i++)
4242 state->keys[i].key = indexOfWord(f->arguments[i] PASS_LD);
4243
4244 state->keys_inited = TRUE;
4245 }
4246
4247 const sindex_key *skeys = state->keys;
4248 skeys--; /* key args are one based */
4249
4250 for(;;)
4251 { if ( unlikely((suspension_matches_index(sp, skeys))) )
4252 { rc = suspension_matches(A2, sp PASS_LD);
4253
4254 if ( rc == TRUE ) goto match;
4255 if ( rc != FALSE ) goto out_fail;
4256 }
4257
4258 if ( likely((sp = --state->suspensions.here) >= state->suspensions.base) )
4259 { if ( unlikely(!sp->instance) )
4260 goto match;
4261 } else
4262 { goto next;
4263 }
4264 }
4265 }
4266
4267 /* Found real work to do. If we get here we can only fail due to
4268 * resource errors. Normally we succeed with or without a choice
4269 * point.
4270 */
4271
4272 match:
4273 if ( !( (susp=PL_new_term_ref()) &&
4274 PL_recorded(UNTNOT(sp->term), susp) &&
4275 /* unifies A4..A8 */
4276 unify_dependency(A2, susp, state->list, an PASS_LD)
4277 ) )
4278 break; /* resource errors */
4279
4280 DEBUG(MSG_TABLING_WORK,
4281 { Sdprintf("Work: %d %d\n\t",
4282 (int)state->acp_index,
4283 (int)(state->suspensions.here - state->suspensions.base));
4284 PL_write_term(Serror, A2, 1200, PL_WRT_NEWLINE);
4285 Sdprintf("\t");
4286 PL_write_term(Serror, susp, 1200, PL_WRT_NEWLINE);
4287 });
4288
4289 if ( advance_wkl_state(state) )
4290 { ForeignRedoPtr(state);
4291 } else
4292 { state->list->executing = FALSE;
4293 freeForeignState(state, sizeof(*state));
4294 return TRUE;
4295 }
4296 next:
4297 ;
4298 } while ( advance_wkl_state(state) );
4299
4300 out_fail:
4301 state->list->executing = FALSE;
4302 freeForeignState(state, sizeof(*state));
4303 return FALSE;
4304 }
4305
4306
4307 /** '$tbl_variant_table'(+Closure, +Variant, -Trie, -Status, -Skeleton) is det.
4308 *
4309 * Retrieve the table for Variant. Status is one of
4310 *
4311 * - `fresh` if the table is new
4312 * - `complete` if the table is completed
4313 * - A worklist pointer
4314 */
4315
4316 static int
tbl_variant_table(term_t closure,term_t variant,term_t Trie,term_t abstract,term_t status,term_t ret,int flags ARG_LD)4317 tbl_variant_table(term_t closure, term_t variant, term_t Trie,
4318 term_t abstract, term_t status, term_t ret, int flags ARG_LD)
4319 { trie *atrie;
4320 Definition def = NULL;
4321 atom_t clref = 0;
4322
4323 get_closure_predicate(closure, &def);
4324
4325 if ( (atrie=get_answer_table(def, variant, ret, &clref, flags PASS_LD)) )
4326 { if ( !idg_init_variant(atrie, def, variant PASS_LD) ||
4327 !idg_add_edge(atrie, NULL PASS_LD) )
4328 return FALSE;
4329
4330 if ( clref )
4331 { TRIE_STAT_INC(atrie, gen_call);
4332 return ( _PL_unify_atomic(Trie, clref) &&
4333 _PL_unify_atomic(status, ATOM_complete) );
4334 } else
4335 { return ( _PL_unify_atomic(Trie, atrie->symbol) &&
4336 unify_table_status(status, atrie, def, TRUE PASS_LD) );
4337 }
4338 }
4339
4340 return FALSE;
4341 }
4342
4343 static
4344 PRED_IMPL("$tbl_variant_table", 5, tbl_variant_table, 0)
4345 { PRED_LD
4346
4347 return tbl_variant_table(A1, A2, A3, 0, A4, A5, AT_CREATE PASS_LD);
4348 }
4349
4350
4351 /** '$tbl_abstract_table'(+Closure, :Wrapper, -Trie,
4352 -Abstract, -Status, -Skeleton)
4353
4354 Abstract is one of `0` or a generalization of Wrapper
4355 */
4356
4357 static
4358 PRED_IMPL("$tbl_abstract_table", 6, tbl_abstract_table, 0)
4359 { PRED_LD
4360
4361 return tbl_variant_table(A1, A2, A3, A4, A5, A6, AT_CREATE|AT_ABSTRACT PASS_LD);
4362 }
4363
4364
4365 static
4366 PRED_IMPL("$tbl_moded_variant_table", 5, tbl_moded_variant_table, 0)
4367 { PRED_LD
4368
4369 return tbl_variant_table(A1, A2, A3, 0, A4, A5, AT_CREATE|AT_MODED PASS_LD);
4370 }
4371
4372
4373 static
4374 PRED_IMPL("$tbl_existing_variant_table", 5, tbl_existing_variant_table, 0)
4375 { PRED_LD
4376 trie *trie;
4377 Definition def = NULL;
4378 atom_t clref = 0;
4379
4380 get_closure_predicate(A1, &def);
4381
4382 if ( (trie=get_answer_table(def, A2, A5, &clref, FALSE PASS_LD)) )
4383 { return ( _PL_unify_atomic(A3, trie->symbol) &&
4384 unify_table_status(A4, trie, def, TRUE PASS_LD) );
4385 }
4386
4387 return FALSE;
4388 }
4389
4390
4391 static
4392 PRED_IMPL("$tbl_local_variant_table", 1, tbl_local_variant_table, 0)
4393 { PRED_LD
4394 trie *trie = LD->tabling.variant_table;
4395
4396 if ( trie )
4397 return _PL_unify_atomic(A1, trie->symbol);
4398
4399 return FALSE;
4400 }
4401
4402
4403 static
4404 PRED_IMPL("$tbl_global_variant_table", 1, tbl_global_variant_table, 0)
4405 {
4406 #ifdef O_PLMT
4407 PRED_LD
4408 trie *trie = GD->tabling.variant_table;
4409
4410 if ( trie )
4411 return _PL_unify_atomic(A1, trie->symbol);
4412 #endif
4413
4414 return FALSE;
4415 }
4416
4417
4418 /** '$tbl_variant_table'(?Table) is nondet.
4419 *
4420 * True when Table is a variant table. If there is both a local and
4421 * global table it first returns the local one and then the global one.
4422 * '$tbl_local_variant_table'/1 and '$tbl_global_variant_table'/1 fetch
4423 * a specific table.
4424 *
4425 * This predicate is in C to make it easy to be deterministic in case
4426 * there is no global table.
4427 */
4428
4429 #ifdef O_PLMT
4430 static
4431 PRED_IMPL("$tbl_variant_table", 1, tbl_variant_table, PL_FA_NONDETERMINISTIC)
4432 { PRED_LD
4433 trie *trie;
4434
4435 switch( CTX_CNTRL )
4436 { case FRG_FIRST_CALL:
4437 if ( (trie=LD->tabling.variant_table) )
4438 { if ( _PL_unify_atomic(A1, trie->symbol) )
4439 { if ( GD->tabling.variant_table )
4440 ForeignRedoInt(1);
4441 else
4442 return TRUE;
4443 }
4444 }
4445 /*FALLTHROUGH*/
4446 case FRG_REDO:
4447 if ( (trie=GD->tabling.variant_table) )
4448 return _PL_unify_atomic(A1, trie->symbol);
4449 return FALSE;
4450 case FRG_CUTTED:
4451 return TRUE;
4452 default:
4453 assert(0);
4454 return FALSE;
4455 }
4456 }
4457 #endif
4458
4459 /** '$tbl_table_status'(+Trie, -Status)
4460 *
4461 * Get the status of an answer table.
4462 */
4463
4464 static
4465 PRED_IMPL("$tbl_table_status", 2, tbl_table_status, 0)
4466 { PRED_LD
4467 trie *trie;
4468
4469 return ( get_trie(A1, &trie) &&
4470 unify_table_status(A2, trie, NULL, FALSE PASS_LD)
4471 );
4472 }
4473
4474
4475 /** '$tbl_table_status'(+Trie, -Status, -Wrapper, -Skeleton)
4476 *
4477 * Get the status of Trie as well as its wrapper and Skeleton.
4478 */
4479
4480 static
4481 PRED_IMPL("$tbl_table_status", 4, tbl_table_status, 0)
4482 { PRED_LD
4483 trie *trie;
4484 term_t wv = PL_new_term_ref();
4485
4486 return ( get_trie(A1, &trie) &&
4487 unify_table_status(A2, trie, NULL, FALSE PASS_LD) &&
4488 unify_skeleton(trie, wv, A4 PASS_LD) &&
4489 PL_unify(A3, wv)
4490 );
4491 }
4492
4493
4494 /** '$tbl_table_pi'(+ATrie, -PredicateIndicator)
4495 *
4496 * Get the predicate indicator that is associated with an answer trie.
4497 * This is used for e.g., abstracting the IDG to predicates.
4498 */
4499
4500 static
4501 PRED_IMPL("$tbl_table_pi", 2, tbl_table_pi, 0)
4502 { PRED_LD
4503 trie *atrie;
4504
4505 if ( get_trie(A1, &atrie) )
4506 { term_t av = PL_new_term_refs(3);
4507 term_t wrapper = av+0;
4508 term_t module = av+1;
4509 term_t head = av+2;
4510
4511 if ( unify_trie_term(atrie->data.variant, NULL, wrapper PASS_LD) )
4512 { atom_t name;
4513 size_t arity;
4514
4515 assert(PL_is_functor(wrapper, FUNCTOR_colon2));
4516 _PL_get_arg(1, wrapper, module);
4517 _PL_get_arg(2, wrapper, head);
4518 if ( PL_get_name_arity(head, &name, &arity) )
4519 { return PL_unify_term(A2, PL_FUNCTOR, FUNCTOR_colon2,
4520 PL_TERM, module,
4521 PL_FUNCTOR, FUNCTOR_divide2,
4522 PL_ATOM, name,
4523 PL_INTPTR, (intptr_t)arity);
4524 }
4525 }
4526 }
4527
4528 return FALSE;
4529 }
4530
4531
4532 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
4533 Finish re-evaluation of a component by running reeval_complete() on all
4534 re-evaluated answer tables. Typically either none of the answer tables
4535 is a re-evaluated one or all are. In some cases though, there may be
4536 some non-reevaluated tables, either because the table was abolished or
4537 because a new table gets involved into the SCC.
4538
4539 We could maintain a flag on the SCC telling re-evaluated tables may be
4540 involved. Not sure it is worth while.
4541 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
4542
4543 static void
wls_reeval_complete(worklist ** wls,size_t ntables)4544 wls_reeval_complete(worklist **wls, size_t ntables)
4545 { size_t i;
4546
4547 for(i=0; i<ntables; i++)
4548 { worklist *wl = wls[i];
4549 trie *atrie = wl->table;
4550 idg_node *n;
4551
4552 if ( (n=atrie->data.IDG) && n->reevaluating )
4553 reeval_complete(atrie);
4554 }
4555 }
4556
4557
4558 static int
unify_leader_clause(tbl_component * scc,term_t cl ARG_LD)4559 unify_leader_clause(tbl_component *scc, term_t cl ARG_LD)
4560 { trie *atrie = scc->leader;
4561 Procedure proc = (true(atrie, TRIE_ISMAP)
4562 ? GD->procedures.trie_gen_compiled3
4563 : GD->procedures.trie_gen_compiled2);
4564 atom_t clref = compile_trie(proc->definition, atrie PASS_LD);
4565
4566 TRIE_STAT_INC(atrie, gen_call);
4567 return _PL_unify_atomic(cl, clref);
4568 }
4569
4570
4571 /** '$tbl_table_complete_all'(+SCC, -Status, -Clause)
4572 *
4573 * Complete and reset all newly created tables.
4574 *
4575 * (*) currently we keep worklists that play a role on a network
4576 * of undefined answers. That is needed for lazy answer completion
4577 * (see O_AC_EAGER). If we do not do so, we still must keep track
4578 * of dependencies when abolishing tries.
4579 */
4580
4581 static
4582 PRED_IMPL("$tbl_table_complete_all", 3, tbl_table_complete_all, 0)
4583 { PRED_LD
4584 tbl_component *c;
4585
4586 if ( !get_scc(A1, &c) )
4587 return FALSE;
4588
4589 if ( c->status == SCC_ACTIVE )
4590 { worklist **wls;
4591 size_t ntables = worklist_set_to_array(c->created_worklists, &wls);
4592 size_t i;
4593 int rc;
4594
4595 wls_reeval_complete(wls, ntables);
4596 rc = unify_leader_clause(c, A3 PASS_LD);
4597
4598 for(i=0; i<ntables; i++)
4599 { worklist *wl = wls[i];
4600 trie *atrie = wl->table;
4601
4602 DEBUG(MSG_TABLING_WORK,
4603 { term_t t = PL_new_term_ref();
4604 unify_trie_term(atrie->data.variant, NULL, t PASS_LD);
4605 Sdprintf("Setting wl %zd in scc %zd to COMPLETE. Variant: ",
4606 pointerToInt(wl), pointerToInt(c));
4607 PL_write_term(Serror, t, 999, PL_WRT_NEWLINE);
4608 });
4609
4610 if ( true(atrie, TRIE_ABOLISH_ON_COMPLETE) )
4611 { atrie->data.worklist = NULL;
4612 wl->table = NULL;
4613 destroy_answer_trie(atrie);
4614 free_worklist(wl);
4615 } else if ( !wl->undefined && isEmptyBuffer(&wl->delays) ) /* see (*) */
4616 { free_worklist(wl);
4617 COMPLETE_WORKLIST(atrie,
4618 { atrie->data.worklist = NULL;
4619 set(atrie, TRIE_COMPLETE);
4620 });
4621 } else
4622 { complete_worklist(wl);
4623 }
4624 }
4625 reset_newly_created_worklists(c, WLFS_FREE_NONE);
4626 c->status = SCC_COMPLETED;
4627
4628 if ( c->parent && LD->tabling.component == c )
4629 LD->tabling.component = c->parent;
4630 if ( !c->parent )
4631 LD->tabling.has_scheduling_component = FALSE;
4632
4633 if ( !rc )
4634 return FALSE;
4635 }
4636
4637 return unify_component_status(A2, c PASS_LD);
4638 }
4639
4640
4641 /** '$tbl_free_component'(+SCC)
4642 *
4643 * Destroy a component and all subcomponents
4644 */
4645
4646 static
4647 PRED_IMPL("$tbl_free_component", 1, tbl_free_component, 0)
4648 { PRED_LD
4649 tbl_component *c;
4650
4651 if ( get_scc(A1, &c) )
4652 { assert(!c->parent);
4653 if ( LD->tabling.component == c )
4654 free_component(c, FC_DESTROY);
4655 assert(LD->tabling.component == NULL);
4656 return TRUE;
4657 }
4658
4659 return FALSE;
4660 }
4661
4662
4663 /** '$tbl_table_discard_all'(+SCC)
4664 *
4665 * Discard all newly created tables and the worklists. This is used if
4666 * an exception happens during tabling.
4667 */
4668
4669 static
4670 PRED_IMPL("$tbl_table_discard_all", 1, tbl_table_discard_all, 0)
4671 { PRED_LD
4672 tbl_component *c;
4673
4674 if ( get_scc(A1, &c) )
4675 { if ( c->status != SCC_MERGED )
4676 { tbl_component *parent = c->parent;
4677
4678 if ( c->created_worklists )
4679 reset_newly_created_worklists(c, WLFS_DISCARD_INCOMPLETE);
4680 reset_global_worklist(c);
4681
4682 LD->tabling.component = parent;
4683 free_component(c, FC_DESTROY);
4684
4685 if ( !parent )
4686 LD->tabling.has_scheduling_component = FALSE;
4687 }
4688 }
4689
4690 return TRUE;
4691 }
4692
4693
4694 static tbl_component *
tbl_create_subcomponent(trie * leader ARG_LD)4695 tbl_create_subcomponent(trie *leader ARG_LD)
4696 { if ( !LD->tabling.has_scheduling_component )
4697 { LD->tabling.has_scheduling_component = TRUE;
4698 if ( !LD->tabling.component || LD->tabling.in_answer_completion )
4699 LD->tabling.component = new_component();
4700 else
4701 LD->tabling.component->status = SCC_ACTIVE;
4702 LD->tabling.component->leader = leader;
4703 } else
4704 { tbl_component *c = new_component();
4705 tbl_component *p;
4706
4707 c->leader = leader;
4708 c->parent = (p=LD->tabling.component);
4709 LD->tabling.component = c;
4710 add_child_component(p, c);
4711 }
4712
4713 return LD->tabling.component;
4714 }
4715
4716
4717 static int
unify_component_status(term_t t,tbl_component * scc ARG_LD)4718 unify_component_status(term_t t, tbl_component *scc ARG_LD)
4719 { atom_t status;
4720
4721 switch(scc->status)
4722 { case SCC_ACTIVE: status = ATOM_active; break;
4723 case SCC_MERGED: status = ATOM_merged; break;
4724 case SCC_COMPLETED:
4725 { if ( scc->parent )
4726 status = ATOM_complete;
4727 else
4728 status = ATOM_final;
4729 break;
4730 }
4731 default:
4732 assert(0);
4733 return FALSE;
4734 }
4735
4736 return PL_unify_atom(t, status);
4737 }
4738
4739 /** '$tbl_abolish_local_tables' is det.
4740 *
4741 * Clear the thread table data. Fails silently if tabling is in
4742 * progress.
4743 */
4744
4745 static
4746 PRED_IMPL("$tbl_abolish_local_tables", 0, tbl_abolish_local_tables, 0)
4747 { PRED_LD
4748
4749 if ( !LD->tabling.has_scheduling_component )
4750 { clearThreadTablingData(LD);
4751 return TRUE;
4752 }
4753
4754 return FALSE;
4755 }
4756
4757 /** '$tbl_trienode'(-X) is det.
4758 *
4759 * X is the reserved node value for non-moded arguments.
4760 */
4761
4762 static
4763 PRED_IMPL("$tbl_trienode", 1, tbl_trienode, 0)
4764 { PRED_LD
4765
4766 return PL_unify_atom(A1, ATOM_trienode);
4767 }
4768
4769 /** '$tbl_is_trienode'(@X) is det.
4770 *
4771 * True if X is the reserved trie node.
4772 */
4773
4774 static
4775 PRED_IMPL("$tbl_is_trienode", 1, tbl_is_trienode, 0)
4776 { PRED_LD
4777 Word p = valTermRef(A1);
4778
4779 deRef(p);
4780 return *p == ATOM_trienode;
4781 }
4782
4783 /*******************************
4784 * INSPECT TABLING DATA *
4785 *******************************/
4786
4787 static
4788 PRED_IMPL("$tbl_scc", 1, tbl_scc, 0)
4789 { PRED_LD
4790
4791 if ( LD->tabling.component )
4792 return PL_unify_pointer(A1, LD->tabling.component);
4793
4794 return FALSE;
4795 }
4796
4797
4798
4799 static int
unify_wl_set(term_t l,worklist_set * wls)4800 unify_wl_set(term_t l, worklist_set *wls)
4801 { GET_LD
4802 worklist **p;
4803 size_t i, n = worklist_set_to_array(wls, &p);
4804
4805 term_t tail = PL_copy_term_ref(l);
4806 term_t head = PL_new_term_ref();
4807 for(i=0; i<n; i++)
4808 { if ( !PL_unify_list(tail, head, tail) ||
4809 !PL_unify_pointer(head, p[i]) )
4810 return FALSE;
4811 }
4812
4813 return PL_unify_nil(tail);
4814 }
4815
4816 static int
unify_scc_set(term_t l,component_set * cs)4817 unify_scc_set(term_t l, component_set *cs)
4818 { GET_LD
4819 term_t tail = PL_copy_term_ref(l);
4820
4821 if ( cs )
4822 { tbl_component **c = baseBuffer(&cs->members, tbl_component*);
4823 tbl_component **top = topBuffer(&cs->members, tbl_component*);
4824 term_t head = PL_new_term_ref();
4825
4826 for(; c < top; c++)
4827 { if ( !PL_unify_list(tail, head, tail) ||
4828 !PL_unify_pointer(head, *c) )
4829 return FALSE;
4830 }
4831 }
4832
4833 return PL_unify_nil(tail);
4834 }
4835
4836 static int
unify_pointer_or_nil(term_t t,void * ptr)4837 unify_pointer_or_nil(term_t t, void *ptr)
4838 { GET_LD
4839
4840 if ( ptr )
4841 return PL_unify_pointer(t, ptr);
4842 else
4843 return PL_unify_atom_chars(t, "null");
4844 }
4845
4846 static
4847 PRED_IMPL("$tbl_scc_data", 2, tbl_scc_data, 0)
4848 { PRED_LD
4849 tbl_component *scc;
4850
4851 if ( get_scc(A1, &scc) )
4852 { term_t av = PL_new_term_refs(5);
4853 term_t t = PL_new_term_ref();
4854 static functor_t f = 0;
4855
4856 if ( !f ) f = PL_new_functor(PL_new_atom("scc"),5);
4857
4858 return ( unify_pointer_or_nil(av+0, scc->parent) &&
4859 unify_scc_set(av+1, scc->children) &&
4860 unify_component_status(av+2, scc PASS_LD) &&
4861 unify_wl_set(av+3, scc->worklist) &&
4862 unify_wl_set(av+4, scc->created_worklists) &&
4863 PL_cons_functor_v(t, f, av) &&
4864 PL_unify(t, A2) );
4865 }
4866
4867 return FALSE;
4868 }
4869
4870
4871 static int
unify_cluster(term_t t,cluster * c,int is_riac)4872 unify_cluster(term_t t, cluster *c, int is_riac)
4873 { GET_LD
4874
4875 if ( is_riac )
4876 { term_t a = PL_new_term_ref();
4877 if ( !PL_unify_term(t, PL_FUNCTOR_CHARS, "riac", 1,
4878 PL_TERM, a) )
4879 return FALSE;
4880 t = a;
4881 }
4882
4883 term_t tail = PL_copy_term_ref(t);
4884 term_t head = PL_new_term_ref();
4885
4886 if ( c->type == CLUSTER_ANSWERS )
4887 { trie_node **ap = baseBuffer(&c->members, trie_node*);
4888 trie_node **top = topBuffer(&c->members, trie_node*);
4889
4890 for(; ap < top; ap++)
4891 { trie_node *an = *ap;
4892
4893 if ( !PL_unify_list(tail, head, tail) ||
4894 !tbl_unify_answer(an, head PASS_LD) )
4895 return FALSE;
4896 }
4897 return PL_unify_nil(tail);
4898 } else
4899 { suspension *sp = baseBuffer(&c->members, suspension);
4900 suspension *top = topBuffer(&c->members, suspension);
4901 term_t tmp = PL_new_term_ref();
4902
4903 assert(c->type == CLUSTER_SUSPENSIONS);
4904
4905 for(; sp < top; sp++)
4906 { if ( !PL_unify_list(tail, head, tail) ||
4907 !PL_recorded(UNTNOT(sp->term), tmp) ||
4908 !PL_unify(tmp, head) )
4909 return FALSE;
4910 }
4911 return PL_unify_nil(tail);
4912 }
4913 }
4914
4915
4916 static int
unify_clusters(term_t t,worklist * wl)4917 unify_clusters(term_t t, worklist *wl)
4918 { GET_LD
4919 cluster *c;
4920 term_t tail = PL_copy_term_ref(t);
4921 term_t head = PL_new_term_ref();
4922
4923 for(c=wl->head; c; c=c->next)
4924 { if ( !PL_unify_list(tail, head, tail) ||
4925 !unify_cluster(head, c, c==wl->riac) )
4926 return FALSE;
4927 }
4928
4929 return PL_unify_nil(tail);
4930 }
4931
4932
4933 static
4934 PRED_IMPL("$tbl_worklist_data", 2, tbl_worklist_data, 0)
4935 { PRED_LD
4936 worklist *wl;
4937
4938 if ( get_worklist(A1, &wl PASS_LD) )
4939 { term_t av = PL_new_term_refs(5);
4940 term_t t = PL_new_term_ref();
4941 static functor_t f = 0;
4942
4943 if ( !f ) f = PL_new_functor(PL_new_atom("worklist"),5);
4944
4945 return ( PL_unify_pointer(av+0, wl->component) &&
4946 _PL_unify_atomic(av+1, wl->table->symbol) &&
4947 PL_unify_bool(av+2, wl->in_global_wl) &&
4948 PL_unify_bool(av+3, wl->executing) &&
4949 unify_clusters(av+4, wl) &&
4950 PL_cons_functor_v(t, f, av) &&
4951 PL_unify(t, A2)
4952 );
4953 }
4954
4955 return FALSE;
4956 }
4957
4958
4959 static
4960 PRED_IMPL("$tbl_wkl_table", 2, tbl_wkl_table, 0)
4961 { PRED_LD
4962 worklist *wl;
4963
4964 return ( get_worklist(A1, &wl PASS_LD) &&
4965 _PL_unify_atomic(A2, wl->table->symbol) );
4966 }
4967
4968
4969 typedef struct
4970 { term_t skel;
4971 } answer_ctx;
4972
4973 /** '$tbl_answer'(+Trie, -Answer, -Condition) is nondet.
4974 */
4975
4976 static int
put_delay_set(term_t cond,delay_info * di,delay_set * set,answer_ctx * ctx ARG_LD)4977 put_delay_set(term_t cond, delay_info *di, delay_set *set,
4978 answer_ctx *ctx ARG_LD)
4979 { delay *base, *top;
4980 term_t av = PL_new_term_refs(6);
4981 int count = 0;
4982 term_t gshare = 0;
4983 term_t gskel = 0;
4984 size_t arity = 0;
4985 term_t tmp = av+4;
4986 term_t ret = av+5;
4987
4988 get_delay_set(di, set, &base, &top);
4989
4990 /* Get the variable sharing term */
4991 if ( top > base && top[-1].answer && !top[-1].variant )
4992 { record_t r;
4993
4994 assert(IS_REC_DELAY(top[-1].answer));
4995 r = UNREC_DELAY(top[-1].answer);
4996 if ( !(gshare = PL_new_term_refs(2)) ||
4997 !PL_recorded(r, gshare) )
4998 return FALSE;
4999 PL_get_name_arity(gshare, NULL, &arity);
5000 gskel = gshare+1;
5001 _PL_get_arg(1, gshare, gskel);
5002 if ( !PL_unify(gskel, ctx->skel) )
5003 { DEBUG(0, Sdprintf("Oops, global skeleton doesn't unify\n"));
5004 return FALSE;
5005 }
5006
5007 top--;
5008
5009 DEBUG(MSG_TABLING_DELAY_VAR,
5010 { Sdprintf("Got sharing skeleton of size %zd\n", arity);
5011 pl_writeln(gshare);
5012 });
5013 }
5014
5015 for(--top; top >= base; top--)
5016 { term_t c1 = count == 0 ? cond : av+0;
5017
5018 if ( top->variant == DV_DELETED )
5019 { if ( top->answer && !is_ground_trie_node(top->answer) )
5020 arity--;
5021 continue;
5022 }
5023 if ( top->answer ) /* positive delay */
5024 { term_t ans = av+1;
5025 term_t uans;
5026
5027 PL_put_variable(c1);
5028 PL_put_variable(ans);
5029 PL_put_variable(ret);
5030
5031 if ( !unify_trie_term(top->variant->data.variant, NULL, c1 PASS_LD) )
5032 return FALSE;
5033 if ( !get_answer_table(NULL, c1, ret, NULL, FALSE PASS_LD) )
5034 { Sdprintf("OOPS! could not find variant table\n");
5035 return FALSE;
5036 }
5037
5038 if ( true(top->answer, TN_SECONDARY) ) /* Ret/ModeArgs */
5039 { if ( !tbl_unify_answer(top->answer, ans PASS_LD) ||
5040 !PL_get_arg(1, ans, tmp) ||
5041 !PL_unify(tmp, ret) ||
5042 !PL_get_arg(2, ans, tmp) ||
5043 !PL_cons_functor(c1, FUNCTOR_divide2, c1, tmp) )
5044 return FALSE;
5045 uans = ans;
5046 } else
5047 { if ( !unify_trie_term(top->answer, NULL, ret PASS_LD) )
5048 return FALSE;
5049 uans = ret;
5050 }
5051
5052 if ( !is_ground_trie_node(top->answer) )
5053 { assert(gshare);
5054
5055 _PL_get_arg(arity, gshare, gskel);
5056 if ( !PL_unify(gskel, uans) )
5057 { DEBUG(0, Sdprintf("Oops, skeleton %zd does not unify\n", arity));
5058 pl_writeln(gskel);
5059 pl_writeln(ans);
5060 return FALSE;
5061 }
5062 arity--;
5063 }
5064 } else /* negative delay */
5065 { PL_put_variable(c1);
5066 if ( !unify_trie_term(top->variant->data.variant, NULL, c1 PASS_LD) ||
5067 !PL_cons_functor(c1, FUNCTOR_tnot1, c1) )
5068 return FALSE;
5069 }
5070
5071 if ( count++ > 0 )
5072 { if ( !PL_cons_functor(cond, FUNCTOR_comma2, c1, cond) )
5073 return FALSE;
5074 }
5075 }
5076
5077 PL_reset_term_refs(av);
5078
5079 return TRUE;
5080 }
5081
5082 static int
unify_delay_info(term_t t,trie_node * answer,void * ctxp ARG_LD)5083 unify_delay_info(term_t t, trie_node *answer, void *ctxp ARG_LD)
5084 { delay_info *di;
5085
5086 if ( (di=answer_delay_info(NULL, answer, FALSE)) )
5087 { if ( DL_IS_DELAY_LIST(di) )
5088 { term_t av = PL_new_term_refs(2);
5089 term_t cond = av+1;
5090 delay_set *base, *top;
5091 int count = 0;
5092 answer_ctx *ctx = ctxp;
5093
5094 delay_sets(di, &base, &top);
5095 for(; base < top; base++)
5096 { term_t c1 = count == 0 ? cond : av+0;
5097
5098 if ( isEmptyBuffer(&di->delay_sets) )
5099 continue;
5100
5101 if ( !put_delay_set(c1, di, base, ctx PASS_LD) )
5102 return FALSE;
5103
5104 if ( count++ > 0 )
5105 { if ( !PL_cons_functor_v(cond, FUNCTOR_semicolon2, av) )
5106 return FALSE;
5107 }
5108 }
5109
5110 return PL_unify(t, cond);
5111 } else
5112 { return PL_unify_atom(t, ATOM_undefined);
5113 }
5114 } else
5115 { return PL_unify_atom(t, ATOM_true);
5116 }
5117 }
5118
5119 #if O_DEBUG
5120 static int
put_delay_info(term_t t,trie_node * answer)5121 put_delay_info(term_t t, trie_node *answer)
5122 { GET_LD
5123 answer_ctx ctx;
5124
5125 ctx.skel = PL_new_term_ref(); /* TBD */
5126 PL_put_variable(t);
5127 return unify_delay_info(t, answer, &ctx PASS_LD);
5128 }
5129 #endif
5130
5131 /** '$tbl_answer'(+Trie, ?Skeleton, -Condition) is nondet.
5132 */
5133
5134 static
5135 PRED_IMPL("$tbl_answer", 3, tbl_answer, PL_FA_NONDETERMINISTIC)
5136 { answer_ctx ctx;
5137
5138 ctx.skel = A2;
5139 return trie_gen(A1, 0, A2, 0, A3, unify_delay_info, &ctx, PL__ctx);
5140 }
5141
5142 /** '$tbl_answer_c'(+Trie, +Skeleton, -ModedArgs, -Condition) is nondet.
5143 */
5144
5145 static
5146 PRED_IMPL("$tbl_answer_c", 4, tbl_answer_c, PL_FA_NONDETERMINISTIC)
5147 { PRED_LD
5148 trie *trie;
5149
5150 if ( get_trie(A1, &trie) )
5151 { Word kp;
5152 trie_node *root;
5153 int rc;
5154
5155 kp = valTermRef(A2);
5156 rc = trie_lookup(trie, NULL, &root, kp, FALSE, NULL PASS_LD);
5157 if ( rc == TRUE )
5158 { answer_ctx ctx;
5159
5160 ctx.skel = A2;
5161 return trie_gen_raw(trie, root, A3, 0, A4, unify_delay_info, &ctx, PL__ctx);
5162 } else
5163 { rc = trie_error(rc, A1);
5164 }
5165
5166 return rc;
5167 }
5168
5169 return FALSE;
5170 }
5171
5172
5173 static int
unify_delay_info_dl(term_t t,trie_node * answer,void * ctx ARG_LD)5174 unify_delay_info_dl(term_t t, trie_node *answer, void *ctx ARG_LD)
5175 { (void) ctx;
5176
5177 if ( answer_is_conditional(answer) )
5178 { if ( is_ground_trie_node(answer) )
5179 return PL_unify_pointer(t, answer);
5180 else
5181 return PL_unify_atom(t, ATOM_nonground);
5182 } else
5183 { return PL_unify_atom(t, ATOM_true);
5184 }
5185 }
5186
5187 static
5188 PRED_IMPL("$tbl_answer_dl", 3, tbl_answer_dl, PL_FA_NONDETERMINISTIC)
5189 { return trie_gen(A1, 0, A2, 0, A3, unify_delay_info_dl, NULL, PL__ctx);
5190 }
5191
5192 /** '$tbl_answer_dl'(+ATrie, +Skeleton, -Sumbsuming, -DL) is nondet.
5193 */
5194
5195 static
5196 PRED_IMPL("$tbl_answer_dl", 4, tbl_answer_dl, PL_FA_NONDETERMINISTIC)
5197 { PRED_LD
5198 trie *trie;
5199
5200 if ( get_trie(A1, &trie) )
5201 { Word kp;
5202 trie_node *root;
5203 int rc;
5204
5205 kp = valTermRef(A2);
5206 rc = trie_lookup(trie, NULL, &root, kp, FALSE, NULL PASS_LD);
5207 if ( rc == TRUE )
5208 { return trie_gen_raw(trie, root, A3, 0, A4, unify_delay_info_dl,
5209 NULL, PL__ctx);
5210 } else
5211 { rc = trie_error(rc, A1);
5212 }
5213
5214 return rc;
5215 }
5216
5217 return FALSE;
5218 }
5219
5220
5221 /** '$tbl_answer_update_dl'(+ATrie, -Skeleton) is nondet.
5222 *
5223 * Obtain an answer from ATrie. If the answer is conditional, update
5224 * the global delay list. If the answer is ground with a term
5225 * ATrie+ANode and otherwise ATrie+Wrapper
5226 */
5227
5228 typedef struct
5229 { term_t atrie;
5230 } update_dl_ctx;
5231
5232 static int
answer_update_delay_list(term_t wrapper,trie_node * answer,void * vctx ARG_LD)5233 answer_update_delay_list(term_t wrapper, trie_node *answer, void *vctx ARG_LD)
5234 { update_dl_ctx *ctx = vctx;
5235
5236 if ( answer_is_conditional(answer) )
5237 { Word p;
5238
5239 if ( !ensureStackSpace(10, 2) )
5240 return FALSE;
5241 p = valTermRef(ctx->atrie);
5242 deRef(p);
5243 assert(isAtom(*p));
5244
5245 tbl_push_delay(*p, valTermRef(wrapper), answer PASS_LD);
5246 }
5247
5248 return TRUE;
5249 }
5250
5251 static
5252 PRED_IMPL("$tbl_answer_update_dl", 2, tbl_answer_update_dl,
5253 PL_FA_NONDETERMINISTIC)
5254 { update_dl_ctx ctx;
5255
5256 ctx.atrie = A1;
5257
5258 return trie_gen(A1, 0, A2, 0, A2, answer_update_delay_list, &ctx, PL__ctx);
5259 }
5260
5261
5262 /** '$tbl_answer_update_dl'(+Trie, +Skeleton, -ModeArgs) is nondet.
5263 */
5264
5265 static
5266 PRED_IMPL("$tbl_answer_update_dl", 3, tbl_answer_update_dl,
5267 PL_FA_NONDETERMINISTIC)
5268 { PRED_LD
5269 trie *trie;
5270
5271 if ( get_trie(A1, &trie) )
5272 { Word kp;
5273 trie_node *root;
5274 int rc;
5275
5276 kp = valTermRef(A2);
5277 rc = trie_lookup(trie, NULL, &root, kp, FALSE, NULL PASS_LD);
5278 if ( rc == TRUE )
5279 { update_dl_ctx ctx;
5280
5281 ctx.atrie = A1;
5282 return trie_gen_raw(trie, root, A3, 0, A3,
5283 answer_update_delay_list, &ctx, PL__ctx);
5284 } else
5285 { rc = trie_error(rc, A1);
5286 }
5287
5288 return rc;
5289 }
5290
5291 return FALSE;
5292 }
5293
5294
5295 /** '$tbl_implementation'(:G0, -G) is det.
5296 *
5297 * Find location where G is actually defined and raise an error of the
5298 * predicate is not tabled.
5299 */
5300
5301 static int
tbl_implementation(term_t g0,term_t g,int must_be_tabled ARG_LD)5302 tbl_implementation(term_t g0, term_t g, int must_be_tabled ARG_LD)
5303 { Module m = NULL;
5304 term_t t = PL_new_term_ref();
5305 functor_t f = 0;
5306 Procedure proc;
5307 Definition def;
5308
5309 if ( !PL_strip_module(g0, &m, t) )
5310 return FALSE;
5311 if ( !PL_get_functor(t, &f) )
5312 return PL_type_error("callable", g0);
5313 if ( !(proc = resolveProcedure(f, m)) )
5314 return FALSE; /* should not happen */
5315
5316 if ( !isDefinedProcedure(proc) )
5317 trapUndefined(getProcDefinition(proc) PASS_LD);
5318 def = getProcDefinition(proc);
5319
5320 if ( must_be_tabled && false(def, P_TABLED) )
5321 { return PL_error(NULL, 0, NULL, ERR_PERMISSION_PROC,
5322 ATOM_tnot, ATOM_non_tabled_procedure, proc);
5323 }
5324
5325 if ( def->module == m )
5326 { return PL_unify(g, g0);
5327 } else
5328 { return PL_unify_term(g, PL_FUNCTOR, FUNCTOR_colon2,
5329 PL_ATOM, def->module->name,
5330 PL_TERM, t);
5331 }
5332 }
5333
5334
5335 static
5336 PRED_IMPL("$tnot_implementation", 2, tnot_implementation, PL_FA_TRANSPARENT)
5337 { PRED_LD
5338
5339 return tbl_implementation(A1, A2, TRUE PASS_LD);
5340 }
5341
5342 static
5343 PRED_IMPL("$tbl_implementation", 2, tbl_implementation, PL_FA_TRANSPARENT)
5344 { PRED_LD
5345
5346 return tbl_implementation(A1, A2, FALSE PASS_LD);
5347 }
5348
5349 /**
5350 * '$is_answer_trie'(@Trie) is semidet
5351 *
5352 * True if Trie is an answer trie, possible already destroyed. This
5353 * is used to find remaining tables for gc_tables/1.
5354 */
5355
5356 static
5357 PRED_IMPL("$is_answer_trie", 1, is_answer_trie, 0)
5358 { trie *trie;
5359
5360 if ( get_trie_noex(A1, &trie) )
5361 return trie->release_node == release_answer_node;
5362
5363 return FALSE;
5364 }
5365
5366 /*******************************
5367 * IDG CONSTRUCTION *
5368 *******************************/
5369
5370 static idg_node *
idg_new(trie * atrie)5371 idg_new(trie *atrie)
5372 { idg_node *n = PL_malloc(sizeof(*n));
5373
5374 memset(n, 0, sizeof(*n));
5375 n->atrie = atrie;
5376
5377 return n;
5378 }
5379
5380 static void
idg_clean_affected(idg_node * node)5381 idg_clean_affected(idg_node *node)
5382 { Table table;
5383
5384 if ( (table=node->affected) )
5385 clearHTable(table);
5386 }
5387
5388 static void
idg_clean_dependent(idg_node * node)5389 idg_clean_dependent(idg_node *node)
5390 { Table table;
5391
5392 if ( (table=node->dependent) )
5393 clearHTable(table);
5394 }
5395
5396
5397 static void
idg_reset(idg_node * node)5398 idg_reset(idg_node *node)
5399 { idg_clean_affected(node);
5400 idg_clean_dependent(node);
5401 node->answer_count = 0;
5402 node->new_answer = FALSE;
5403 node->reevaluating = FALSE;
5404 node->falsecount = 0;
5405 }
5406
5407 static void
idg_destroy(idg_node * node)5408 idg_destroy(idg_node *node)
5409 { Table table;
5410
5411 if ( (table=node->affected) )
5412 { node->affected = NULL;
5413 destroyHTable(table);
5414 }
5415 if ( (table=node->dependent) )
5416 { node->dependent = NULL;
5417 destroyHTable(table);
5418 }
5419
5420 PL_free(node);
5421 }
5422
5423 static void
idg_free_affected(void * n,void * v)5424 idg_free_affected(void *n, void *v)
5425 { idg_node *child = v;
5426 idg_node *parent = n;
5427
5428 assert(parent->dependent);
5429 if ( !deleteHTable(parent->dependent, child) )
5430 Sdprintf("OOPS: idg_free_affected() failed to delete backlink\n");
5431 }
5432
5433 static void
idg_free_dependent(void * n,void * v)5434 idg_free_dependent(void *n, void *v)
5435 { idg_node *parent = v;
5436 idg_node *child = n;
5437
5438 assert(child->affected);
5439 if ( !deleteHTable(child->affected, parent) )
5440 Sdprintf("OOPS: idg_free_dependent() failed to delete backlink\n");
5441 }
5442
5443
5444 /**
5445 * Throw error(idg_dependency_error(Parent, Child), _)
5446 */
5447
5448 static int
idg_dependency_error(idg_node * parent,idg_node * child ARG_LD)5449 idg_dependency_error(idg_node *parent, idg_node *child ARG_LD)
5450 { term_t av;
5451
5452 return ( (av=PL_new_term_refs(3)) &&
5453 unify_trie_term(parent->atrie->data.variant, NULL, av+0 PASS_LD) &&
5454 unify_trie_term(child->atrie->data.variant, NULL, av+1 PASS_LD) &&
5455 PL_unify_term(av+2,
5456 PL_FUNCTOR, FUNCTOR_error2,
5457 PL_FUNCTOR_CHARS, "idg_dependency_error", 2,
5458 PL_TERM, av+0,
5459 PL_TERM, av+1,
5460 PL_VARIABLE) &&
5461 PL_raise_exception(av+2));
5462 }
5463
5464
5465 static int
idg_dependency_error_dyncall(idg_node * parent,term_t call ARG_LD)5466 idg_dependency_error_dyncall(idg_node *parent, term_t call ARG_LD)
5467 { term_t av;
5468
5469 return ( (av=PL_new_term_refs(2)) &&
5470 unify_trie_term(parent->atrie->data.variant, NULL, av+0 PASS_LD) &&
5471 PL_unify_term(av+1,
5472 PL_FUNCTOR, FUNCTOR_error2,
5473 PL_FUNCTOR_CHARS, "idg_dependency_error", 2,
5474 PL_TERM, av+0,
5475 PL_TERM, call,
5476 PL_VARIABLE) &&
5477 PL_raise_exception(av+1));
5478 }
5479
5480
5481
5482 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
5483 Create a bi-directional link between parent and child node. We use the
5484 hash tables as sets only, but we use the _other side_ as entry _value_
5485 to recover the full link and allow ->free_symbol() to delete the back
5486 pointer.
5487 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
5488
5489 static int
idg_add_child(idg_node * parent,idg_node * child ARG_LD)5490 idg_add_child(idg_node *parent, idg_node *child ARG_LD)
5491 { volatile Table t;
5492
5493 if ( true(parent->atrie, TRIE_ISSHARED) &&
5494 false(child->atrie, TRIE_ISSHARED) )
5495 return idg_dependency_error(parent, child PASS_LD);
5496
5497 if ( !(t=child->affected) )
5498 { t = newHTable(4);
5499 t->free_symbol = idg_free_affected;
5500 if ( !COMPARE_AND_SWAP_PTR(&child->affected, NULL, t) )
5501 destroyHTable(t);
5502 }
5503 addHTable(t, parent, child);
5504
5505 if ( !(t=parent->dependent) )
5506 { t = newHTable(4);
5507 t->free_symbol = idg_free_dependent;
5508 if ( !COMPARE_AND_SWAP_PTR(&parent->dependent, NULL, t) )
5509 destroyHTable(t);
5510 }
5511 addHTable(t, child, parent);
5512
5513 return TRUE;
5514 }
5515
5516
5517 static int
idg_init_variant(trie * atrie,Definition def,term_t variant ARG_LD)5518 idg_init_variant(trie *atrie, Definition def, term_t variant ARG_LD)
5519 { if ( !atrie->data.IDG )
5520 { if ( unlikely(!def) )
5521 { Procedure proc;
5522
5523 if ( get_procedure(variant, &proc, 0, GP_RESOLVE|GP_EXISTENCE_ERROR) )
5524 def = proc->definition;
5525 else
5526 return FALSE;
5527 }
5528
5529 if ( true(def, P_INCREMENTAL) )
5530 { idg_node *n = idg_new(atrie);
5531
5532 if ( !COMPARE_AND_SWAP_PTR(&atrie->data.IDG, NULL, n) )
5533 idg_destroy(n);
5534 }
5535 }
5536
5537 return TRUE;
5538 }
5539
5540
5541 static int
set_idg_current(trie * atrie ARG_LD)5542 set_idg_current(trie *atrie ARG_LD)
5543 { int rc;
5544 Word p;
5545
5546 if ( (rc=ensureGlobalSpace(0, ALLOW_GC)) != TRUE )
5547 return raiseStackOverflow(rc);
5548 p = valTermRef(LD->tabling.idg_current);
5549 TrailAssignment(p);
5550 if ( atrie )
5551 *p = trie_symbol(atrie);
5552 else
5553 setVar(*p);
5554
5555 return TRUE;
5556 }
5557
5558 static trie *
idg_current(ARG1_LD)5559 idg_current(ARG1_LD)
5560 { atom_t current = *valTermRef(LD->tabling.idg_current);
5561
5562 if ( current )
5563 return symbol_trie(current);
5564
5565 return NULL;
5566 }
5567
5568
5569 /** Add an edge from the current node to the new child represented
5570 * by `atrie`. If `ctrie` is given it is used. Otherwise idg_current()
5571 * is used.
5572 *
5573 * - TRUE: created a dependency
5574 * - FALSE: something is wrong
5575 * - -1: there is no current node
5576 */
5577
5578 static int
idg_add_edge(trie * atrie,trie * ctrie ARG_LD)5579 idg_add_edge(trie *atrie, trie *ctrie ARG_LD)
5580 { if ( atrie->data.IDG )
5581 { if ( !ctrie )
5582 ctrie = idg_current(PASS_LD1);
5583
5584 if ( ctrie && ctrie->data.IDG )
5585 { DEBUG(MSG_TABLING_IDG,
5586 { term_t f = PL_new_term_ref();
5587 term_t t = PL_new_term_ref();
5588 unify_trie_term(ctrie->data.variant, NULL, f PASS_LD);
5589 unify_trie_term(atrie->data.variant, NULL, t PASS_LD);
5590 Sdprintf("IDG: Edge ");
5591 PL_write_term(Serror, f, 999, 0);
5592 Sdprintf(" -> ");
5593 PL_write_term(Serror, t, 999, PL_WRT_NEWLINE);
5594 });
5595
5596 return idg_add_child(ctrie->data.IDG, atrie->data.IDG PASS_LD);
5597 }
5598 }
5599
5600 return -1;
5601 }
5602
5603 /** '$idg_set_current'(-OldCurrent, +ATrie)
5604 *
5605 * Set the current to Atrie and return the old current.
5606 */
5607
5608 static
5609 PRED_IMPL("$idg_set_current", 2, idg_set_current, 0)
5610 { PRED_LD
5611 trie *atrie;
5612
5613 if ( get_trie(A2, &atrie) )
5614 { atom_t current;
5615
5616 if ( (current = *valTermRef(LD->tabling.idg_current)) )
5617 { if ( !PL_unify_atom(A1, current) )
5618 return FALSE;
5619 }
5620 return set_idg_current(atrie PASS_LD);
5621 }
5622
5623 return FALSE;
5624 }
5625
5626
5627 /** '$idg_add_dyncall'(+Variant)
5628 *
5629 * Called on a call to an incremental dynamic predicate.
5630 *
5631 * (*) If we make a call we should reset the `falsecount` to 0 as this
5632 * may have added a new dependency. Ideally we should keep track of the
5633 * edges we have signalled, although we need to lookup the dynamic trie
5634 * from the assert/removed variant anyway and propagation to node that
5635 * have already been triggered stop quickly. Setting the `falsecount`
5636 * to zero should be considered similar to re-evaluating an incremental
5637 * tabled predicate when it is called.
5638 */
5639
5640 int
idg_add_dyncall(Definition def,trie * ctrie,term_t variant ARG_LD)5641 idg_add_dyncall(Definition def, trie *ctrie, term_t variant ARG_LD)
5642 { trie *atrie;
5643 int flags = (AT_CREATE|AT_NOCLAIM);
5644
5645 if ( true(ctrie, TRIE_ISSHARED) )
5646 { flags |= AT_SHARED;
5647
5648 /* a shared table cannot depend on a thread-local predicate */
5649 /* TBD: Avoid the procedure lookup! */
5650 if ( !def )
5651 { Procedure proc;
5652
5653 if ( get_procedure(variant, &proc, 0, GP_RESOLVE) &&
5654 true(proc->definition, P_THREAD_LOCAL) )
5655 { return idg_dependency_error_dyncall(ctrie->data.IDG, variant PASS_LD);
5656 }
5657 } else if ( true(def, P_THREAD_LOCAL) )
5658 { return idg_dependency_error_dyncall(ctrie->data.IDG, variant PASS_LD);
5659 }
5660 } else
5661 { flags |= AT_PRIVATE;
5662 }
5663
5664 if ( (atrie=get_answer_table(NULL, variant, 0, NULL, flags PASS_LD)) )
5665 { if ( !atrie->data.IDG )
5666 { idg_node *n;
5667
5668 assert(!atrie->data.worklist || atrie->data.worklist == WL_GROUND);
5669 atrie->data.worklist = WL_DYNAMIC;
5670 n = idg_new(atrie);
5671 if ( !COMPARE_AND_SWAP_PTR(&atrie->data.IDG, NULL, n) )
5672 idg_destroy(n);
5673 }
5674
5675 idg_add_edge(atrie, ctrie PASS_LD);
5676 atrie->data.IDG->falsecount = 0; /* see (*) above */
5677
5678 return TRUE;
5679 }
5680
5681 return FALSE;
5682 }
5683
5684
5685 static
5686 PRED_IMPL("$idg_add_dyncall", 1, idg_add_dyncall, 0)
5687 { PRED_LD
5688 trie *ctrie = idg_current(PASS_LD1);
5689
5690 if ( ctrie && ctrie->data.IDG )
5691 return idg_add_dyncall(NULL, ctrie, A1 PASS_LD);
5692
5693 return TRUE;
5694 }
5695
5696
5697 static int
idg_set_current_wl(term_t wlref ARG_LD)5698 idg_set_current_wl(term_t wlref ARG_LD)
5699 { worklist *wl;
5700
5701 if ( get_worklist(wlref, &wl PASS_LD) )
5702 { trie *atrie = wl->table;
5703
5704 DEBUG(MSG_TABLING_IDG,
5705 { term_t t = PL_new_term_ref();
5706 unify_trie_term(atrie->data.variant, NULL, t PASS_LD);
5707 Sdprintf("IDG: Set current to ");
5708 PL_write_term(Serror, t, 999, PL_WRT_NEWLINE);
5709 });
5710
5711 return set_idg_current(atrie PASS_LD);
5712 }
5713
5714 return FALSE;
5715 }
5716
5717 static
5718 PRED_IMPL("$idg_set_current", 1, idg_set_current, 0)
5719 { PRED_LD
5720 trie *atrie;
5721
5722 if ( get_trie_noex(A1, &atrie) )
5723 { DEBUG(MSG_TABLING_IDG,
5724 { term_t t = PL_new_term_ref();
5725 unify_trie_term(atrie->data.variant, NULL, t PASS_LD);
5726 Sdprintf("IDG: Set current to ");
5727 PL_write_term(Serror, t, 999, PL_WRT_NEWLINE);
5728 });
5729
5730 return set_idg_current(atrie PASS_LD);
5731 } else
5732 { return set_idg_current(NULL PASS_LD);
5733 }
5734
5735 return TRUE;
5736 }
5737
5738
5739 static
5740 PRED_IMPL("$idg_reset_current", 0, idg_reset_current, 0)
5741 { PRED_LD
5742
5743 return set_idg_current(NULL PASS_LD);
5744 }
5745
5746
5747 /*******************************
5748 * IDG QUERYING *
5749 *******************************/
5750
5751 /** '$idg_edge'(+ATrie, ?Direction, ?Node)
5752 *
5753 * Enumerate over the edges of the dependency graph
5754 */
5755
5756 typedef struct idg_edge_state
5757 { trie * atrie;
5758 Table table;
5759 TableEnum tenum;
5760 atom_t dir;
5761 int fixed_dir;
5762 int allocated;
5763 atom_t deptrie_symbol;
5764 } idg_edge_state;
5765
5766
5767 static int
advance_idg_edge_state(idg_edge_state * state)5768 advance_idg_edge_state(idg_edge_state *state)
5769 { void *k, *v;
5770
5771 retry:
5772 if ( advanceTableEnum(state->tenum, &k, &v) )
5773 { idg_node *n = k;
5774
5775 state->deptrie_symbol = trie_symbol(n->atrie);
5776 return TRUE;
5777 } else
5778 { freeTableEnum(state->tenum);
5779 state->tenum = NULL;
5780
5781 if ( !state->fixed_dir && state->dir == ATOM_affected )
5782 { if ( (state->table = state->atrie->data.IDG->dependent) )
5783 { state->dir = ATOM_dependent;
5784 state->tenum = newTableEnum(state->table);
5785 goto retry;
5786 }
5787 }
5788 }
5789
5790 return FALSE;
5791 }
5792
5793 static void
free_idg_edge_state(idg_edge_state * state)5794 free_idg_edge_state(idg_edge_state *state)
5795 { if ( state->tenum )
5796 freeTableEnum(state->tenum);
5797 if ( state->allocated )
5798 freeForeignState(state, sizeof(*state));
5799 }
5800
5801 static idg_edge_state *
save_idg_edge_state(idg_edge_state * state)5802 save_idg_edge_state(idg_edge_state *state)
5803 { if ( !state->allocated )
5804 { idg_edge_state *n = allocForeignState(sizeof(*n));
5805
5806 *n = *state;
5807 n->allocated = TRUE;
5808 return n;
5809 }
5810
5811 return state;
5812 }
5813
5814
5815 static
5816 PRED_IMPL("$idg_edge", 3, idg_edge, PL_FA_NONDETERMINISTIC)
5817 { PRED_LD
5818 idg_edge_state sbuf;
5819 idg_edge_state *state;
5820
5821 switch( CTX_CNTRL )
5822 { case FRG_FIRST_CALL:
5823 { trie *to;
5824
5825 state = &sbuf;
5826 memset(state, 0, sizeof(*state));
5827
5828 if ( !get_trie(A1, &state->atrie) )
5829 return FALSE;
5830 if ( !state->atrie->data.IDG )
5831 return FALSE;
5832
5833 if ( PL_is_variable(A2) )
5834 { if ( (state->table = state->atrie->data.IDG->affected) )
5835 { state->dir = ATOM_affected;
5836 } else if ( (state->table = state->atrie->data.IDG->dependent) )
5837 { state->dir = ATOM_dependent;
5838 if ( !PL_unify_atom(A2, ATOM_dependent) )
5839 return FALSE;
5840 state->fixed_dir = TRUE;
5841 } else
5842 return FALSE;
5843 } else if ( PL_get_atom_ex(A2, &state->dir) )
5844 { state->fixed_dir = TRUE;
5845 if ( state->dir == ATOM_affected )
5846 state->table = state->atrie->data.IDG->affected;
5847 else if ( state->dir == ATOM_dependent )
5848 state->table = state->atrie->data.IDG->dependent;
5849 else
5850 return PL_domain_error("idg_edge_dir", A2);
5851 }
5852
5853 if ( !state->table )
5854 return FALSE;
5855
5856 if ( PL_is_variable(A3) )
5857 { state->tenum = newTableEnum(state->table);
5858 if ( advance_idg_edge_state(state) )
5859 break;
5860 free_idg_edge_state(state);
5861 return FALSE;
5862 } else if ( get_trie(A3, &to) )
5863 { return lookupHTable(state->table, to) != NULL;
5864 }
5865 }
5866 case FRG_REDO:
5867 state = CTX_PTR;
5868 break;
5869 case FRG_CUTTED:
5870 state = CTX_PTR;
5871 free_idg_edge_state(state);
5872 return TRUE;
5873 default:
5874 assert(0);
5875 return FALSE;
5876 }
5877
5878 Mark(fli_context->mark);
5879 do
5880 { if ( PL_unify_atom(A3, state->deptrie_symbol) )
5881 { if ( state->fixed_dir ||
5882 PL_unify_atom(A2, state->dir) )
5883 { if ( advance_idg_edge_state(state) )
5884 ForeignRedoPtr(save_idg_edge_state(state));
5885 free_idg_edge_state(state);
5886 return TRUE;
5887 }
5888 }
5889
5890 if ( PL_exception(0) )
5891 { free_idg_edge_state(state);
5892 return FALSE;
5893 }
5894
5895 Undo(fli_context->mark);
5896 } while(advance_idg_edge_state(state));
5897
5898 free_idg_edge_state(state);
5899 return FALSE;
5900 }
5901
5902
5903 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
5904 (*) If not-changed propagation re-validates the table someone may be
5905 waiting for it and we must release ownership for the table and signal
5906 possible waiters.
5907
5908 It is probably possible we re-validate a table that is claimed by some
5909 other thread. What should we do in this case?
5910 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
5911
5912 typedef struct idg_propagate_state
5913 { size_t modified;
5914 trie *incomplete; /* hit an incomplete trie */
5915 TableEnum en;
5916 segstack stack;
5917 idg_node *buf[100];
5918 } idg_propagate_state;
5919
5920
5921 static void
idg_changed_loop(idg_propagate_state * state,int changed)5922 idg_changed_loop(idg_propagate_state *state, int changed)
5923 { typedef struct idg_node *IDGNode;
5924
5925 for(;;)
5926 { void *k, *v;
5927 idg_node *next;
5928
5929 while( advanceTableEnum(state->en, &k, &v) )
5930 { idg_node *n = k;
5931
5932 DEBUG(MSG_TABLING_IDG_CHANGED,
5933 print_answer_table(
5934 n->atrie,
5935 "IDG: propagate falsecount (re-eval=%d, falsecount=%d)",
5936 n->reevaluating, n->falsecount));
5937
5938 if ( n->reevaluating )
5939 continue;
5940
5941 if ( changed )
5942 { if ( table_is_incomplete(n->atrie) )
5943 state->incomplete = n->atrie; /* return? */
5944 if ( ATOMIC_INC(&n->falsecount) == 1 )
5945 { TRIE_STAT_INC(n, invalidated);
5946 if ( n->affected )
5947 { if ( !pushSegStack(&state->stack, n, IDGNode) )
5948 outOfCore();
5949 }
5950 }
5951 } else
5952 { if ( ATOMIC_DEC(&n->falsecount) == 0 )
5953 {
5954 #ifdef O_PLMT
5955 if ( true(n->atrie, TRIE_ISSHARED) && n->atrie->tid )
5956 { if ( n->atrie->tid == PL_thread_self() ) /* See (*) */
5957 COMPLETE_WORKLIST(n->atrie, (void)0);
5958 else
5959 Sdprintf("IDG falsecount propagation re-validated a table "
5960 "from another thread\n");
5961 }
5962 #endif
5963 if ( n->affected )
5964 { if ( !pushSegStack(&state->stack, n, IDGNode) )
5965 outOfCore();
5966 }
5967 }
5968 }
5969 }
5970 freeTableEnum(state->en);
5971
5972 if ( popSegStack(&state->stack, &next, IDGNode) )
5973 { assert(next->affected);
5974 state->en = newTableEnum(next->affected);
5975 } else
5976 break;
5977 }
5978 }
5979
5980
5981 static trie *
idg_propagate_change(idg_node * n,int changed)5982 idg_propagate_change(idg_node *n, int changed)
5983 { if ( n->affected )
5984 { idg_propagate_state state;
5985
5986 state.modified = 0;
5987 state.incomplete = NULL;
5988 initSegStack(&state.stack, sizeof(idg_node*), sizeof(state.buf), state.buf);
5989 state.en = newTableEnum(n->affected);
5990 idg_changed_loop(&state, changed);
5991 clearSegStack(&state.stack);
5992
5993 return state.incomplete;
5994 }
5995
5996 return NULL;
5997 }
5998
5999
6000 static int
change_incomplete_error(trie * atrie)6001 change_incomplete_error(trie *atrie)
6002 { GET_LD
6003 term_t v;
6004
6005 return ( (v=PL_new_term_ref()) &&
6006 unify_trie_term(atrie->data.variant, NULL, v PASS_LD) &&
6007 PL_permission_error("update", "variant", v) );
6008 }
6009
6010
6011 static int
idg_changed(trie * atrie)6012 idg_changed(trie *atrie)
6013 { idg_node *n;
6014
6015 DEBUG(MSG_TABLING_IDG_CHANGED,
6016 print_answer_table(atrie, "IDG: dynamic change"));
6017
6018 if ( (n=atrie->data.IDG) && n->falsecount == 0 )
6019 { trie *incomplete;
6020
6021 DEBUG(MSG_TABLING_IDG_CHANGED, Sdprintf(" (propagating)\n"));
6022
6023 if ( table_is_incomplete(atrie) )
6024 return change_incomplete_error(atrie);
6025 if ( ATOMIC_INC(&n->falsecount) == 1 )
6026 { TRIE_STAT_INC(n, invalidated);
6027 if ( (incomplete=idg_propagate_change(n, TRUE)) )
6028 { n->falsecount = 0;
6029 idg_propagate_change(n, FALSE);
6030 return change_incomplete_error(incomplete);
6031 }
6032 }
6033 } else
6034 { DEBUG(MSG_TABLING_IDG_CHANGED,
6035 if ( n ) Sdprintf(" (already changed (%d))\n", n->falsecount);
6036 else Sdprintf(" (no IDG)\n"));
6037 }
6038
6039 return TRUE;
6040 }
6041
6042
6043 static
6044 PRED_IMPL("$idg_changed", 1, idg_changed, 0)
6045 { trie *atrie;
6046
6047 if ( get_trie(A1, &atrie) )
6048 return idg_changed(atrie);
6049
6050 return FALSE;
6051 }
6052
6053
6054 static
6055 PRED_IMPL("$idg_falsecount", 2, idg_falsecount, 0)
6056 { GET_LD
6057 trie *atrie;
6058
6059 if ( get_trie(A1, &atrie) )
6060 { idg_node *n;
6061
6062 if ( (n=atrie->data.IDG) )
6063 return PL_unify_integer(A2, n->falsecount);
6064
6065 return FALSE;
6066 }
6067
6068 return FALSE;
6069 }
6070
6071
6072 static
6073 PRED_IMPL("$idg_set_falsecount", 2, idg_set_falsecount, 0)
6074 { trie *atrie;
6075
6076 if ( get_trie(A1, &atrie) )
6077 { idg_node *n;
6078
6079 if ( (n=atrie->data.IDG) )
6080 return PL_get_integer_ex(A2, &n->falsecount);
6081
6082 return FALSE;
6083 }
6084
6085 return FALSE;
6086 }
6087
6088
6089 /*******************************
6090 * INCREMENTAL RE-EVALUATION *
6091 *******************************/
6092
6093 /** '$tbl_reeval_wait'(+Trie, -Status) is det.
6094 *
6095 * Get the status for Trie as one of `complete` or `invalid`, but wait
6096 * if another thread is evaluating the table.
6097 *
6098 * @error `deadlock` if claiming the table would cause a deadlock.
6099 */
6100
6101 static
6102 PRED_IMPL("$tbl_reeval_wait", 2, tbl_reeval_wait, 0)
6103 { GET_LD
6104 trie *atrie;
6105
6106 if ( get_trie(A1, &atrie) )
6107 { if ( atrie->data.worklist == WL_DYNAMIC )
6108 { return PL_unify_atom(A2, ATOM_dynamic);
6109 } else
6110 { int rc;
6111 #ifdef O_PLMT
6112 int tid = PL_thread_self();
6113
6114 if ( atrie->tid == tid ) /* remain owner */
6115 { rc = unify_table_status(A2, atrie, NULL, FALSE PASS_LD);
6116 } else
6117 { if ( !claim_answer_table(atrie, NULL, 0 PASS_LD) )
6118 return FALSE; /* deadlock */
6119 rc = unify_table_status(A2, atrie, NULL, FALSE PASS_LD);
6120 COMPLETE_WORKLIST(atrie, (void)0);
6121 }
6122 #else
6123 rc = unify_table_status(A2, atrie, NULL, FALSE PASS_LD);
6124 #endif
6125
6126 return rc;
6127 }
6128 }
6129
6130 return FALSE;
6131 }
6132
6133
6134 /** '$tbl_reeval_prepare'(+Trie, -Variant, -Clause) is det.
6135 *
6136 * Prepare Trie for re-evaluation. If Trie is invalid, it is claimed and
6137 * prepared for re-evaluation and Variant is unified with the goal to
6138 * re-evaluate. If the Trie is (already) valid, unify Clause with its
6139 * answer set.
6140 *
6141 * @error `deadlock` if claiming the table would cause a deadlock.
6142 */
6143
6144 static void *
reeval_prep_node(trie_node * n,void * ctx)6145 reeval_prep_node(trie_node *n, void *ctx)
6146 { trie *atrie = ctx;
6147
6148 if ( n->value )
6149 { set(n, TN_IDG_DELETED);
6150 clear(n, TN_IDG_ADDED);
6151
6152 if ( answer_is_conditional(n) )
6153 { destroy_delay_info(atrie, n, TRUE);
6154 n->data.delayinfo = NULL;
6155 clear(n, TN_IDG_UNCONDITIONAL);
6156 } else
6157 { set(n, TN_IDG_UNCONDITIONAL);
6158 }
6159 }
6160
6161 return NULL;
6162 }
6163
6164
6165 static
6166 PRED_IMPL("$tbl_reeval_prepare", 3, tbl_reeval_prepare, 0)
6167 { PRED_LD
6168 trie *atrie;
6169
6170 if ( get_trie(A1, &atrie) )
6171 { idg_node *idg = atrie->data.IDG;
6172
6173 #ifdef O_PLMT
6174 if ( true(atrie, TRIE_ISSHARED) )
6175 { atom_t cref = 0;
6176
6177 if ( !claim_answer_table(atrie, &cref, 0 PASS_LD) )
6178 return FALSE; /* deadlock */
6179 if ( cref )
6180 return PL_unify_atom(A3, cref);
6181 }
6182 #endif
6183 if ( idg->falsecount == 0 ) /* someone else re-evaluated it */
6184 return PL_unify_atom(A3, trie_symbol(atrie));
6185
6186 if ( !unify_trie_term(atrie->data.variant, NULL, A2 PASS_LD) )
6187 return FALSE;
6188
6189 DEBUG(MSG_TABLING_IDG_REEVAL,
6190 print_answer_table(atrie, "Preparing re-evaluation of"));
6191
6192 idg->answer_count = atrie->value_count;
6193 idg->new_answer = FALSE;
6194 idg->falsecount = 0;
6195 idg_clean_dependent(idg);
6196 if ( !idg->aborted )
6197 map_trie_node(&atrie->root, reeval_prep_node, atrie);
6198 idg->reevaluating = TRUE;
6199
6200 return TRUE;
6201 }
6202
6203 return FALSE;
6204 }
6205
6206
6207 /** '$tbl_reeval_abandon'(+ATrie)
6208 *
6209 * Release ownership of ATrie if we cannot re-evaluate it
6210 */
6211
6212 static
6213 PRED_IMPL("$tbl_reeval_abandon", 1, tbl_reeval_abandon, 0)
6214 { trie *atrie;
6215
6216 if ( get_trie(A1, &atrie) )
6217 { DEBUG(MSG_TABLING_SHARED,
6218 print_answer_table(atrie, "Abondon re-evaluation"));
6219 COMPLETE_WORKLIST(atrie, (void)0);
6220
6221 return TRUE;
6222 }
6223
6224 return FALSE;
6225 }
6226
6227
6228 static void *
reeval_complete_node(trie_node * n,void * ctx)6229 reeval_complete_node(trie_node *n, void *ctx)
6230 { trie *atrie = ctx;
6231
6232 if ( true(n, TN_IDG_DELETED) )
6233 { clear(n, TN_IDG_DELETED); /* not used by trie admin */
6234 trie_delete(atrie, n, FALSE); /* TBD: can we prune? */
6235 if ( false(n, TN_IDG_UNCONDITIONAL) )
6236 simplify_answer(atrie->data.worklist, n, FALSE);
6237 } else if ( true(n, TN_IDG_UNCONDITIONAL) &&
6238 answer_is_conditional(n) )
6239 { atrie->data.IDG->new_answer = TRUE;
6240 }
6241
6242 clear(n, TN_IDG_MASK);
6243
6244 return NULL;
6245 }
6246
6247
6248 static void
reeval_complete(trie * atrie)6249 reeval_complete(trie *atrie)
6250 { idg_node *n;
6251
6252 if ( (n=atrie->data.IDG) && n->reevaluating )
6253 { map_trie_node(&atrie->root, reeval_complete_node, atrie);
6254
6255 DEBUG(MSG_TABLING_IDG_REEVAL,
6256 print_answer_table(atrie, "Re-evaluation of"));
6257
6258 if ( n->new_answer == FALSE &&
6259 n->answer_count == atrie->value_count )
6260 { DEBUG(MSG_TABLING_IDG_REEVAL, Sdprintf(": same answers\n"));
6261 idg_propagate_change(n, FALSE);
6262 } else
6263 { DEBUG(MSG_TABLING_IDG_REEVAL,
6264 Sdprintf(": modified (new=%d, count %zd -> %zd)\n",
6265 n->new_answer, n->answer_count, atrie->value_count));
6266 }
6267
6268 TRIE_STAT_INC(n, reevaluated);
6269
6270 n->reevaluating = FALSE;
6271 n->aborted = FALSE;
6272 }
6273 }
6274
6275
6276 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6277 reset_reevaluation(trie *atrie)
6278
6279 Reset a table that is being reevaluated while an exception happened.
6280 This must ensure that a subsequent call on the table will restart the
6281 re-evaluation and forward a _not-changed_ to the affected nodes if the
6282 table evaluates to the same values.
6283
6284 We set the nodes back to the state after the initial preparation. The
6285 flag `aborted` is used by the subsequent prepare to keep the node
6286 states.
6287
6288 TBD: the dependency links to dependent nodes (and back) have been
6289 removed during preparation. This is ok for restoring the state. It poses
6290 problems for propagating the falsecounts from our dependent nodes to our
6291 affected nodes though if there are modifications to our dependent nodes
6292 while re-evaluation is in progress. In-progress here means the time
6293 between the node was prepared and completed. This period can be long if
6294 the re-evaluation has been aborted.
6295 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
6296
6297 static void *
reset_evaluate_node(trie_node * n,void * ctx)6298 reset_evaluate_node(trie_node *n, void *ctx)
6299 { trie *atrie = ctx;
6300
6301 if ( answer_is_conditional(n) )
6302 { destroy_delay_info(atrie, n, TRUE);
6303 n->data.delayinfo = NULL;
6304 }
6305
6306 if ( true(n, TN_IDG_ADDED) )
6307 { trie_delete(atrie, n, FALSE); /* we are enumerating this node */
6308 } else /* and cannot delete it (now) */
6309 { set(n, TN_IDG_DELETED);
6310 if ( true(n, TN_IDG_SAVED_UNCONDITIONAL) )
6311 set(n, TN_IDG_UNCONDITIONAL);
6312 else
6313 clear(n, TN_IDG_UNCONDITIONAL);
6314 }
6315
6316 return NULL;
6317 }
6318
6319
6320 static void
reset_reevaluation(trie * atrie)6321 reset_reevaluation(trie *atrie)
6322 { idg_node *n = atrie->data.IDG;
6323
6324 DEBUG(MSG_TABLING_EXCEPTION,
6325 print_answer_table(atrie, "Abort reevaluation of (%zd answers)",
6326 atrie->value_count));
6327
6328 map_trie_node(&atrie->root, reset_evaluate_node, atrie);
6329 assert(n->answer_count == atrie->value_count);
6330
6331 n->new_answer = FALSE;
6332 n->aborted = TRUE;
6333 n->falsecount = 1;
6334 set(atrie, TRIE_COMPLETE);
6335 COMPLETE_WORKLIST(atrie, n->reevaluating = FALSE);
6336 }
6337
6338
6339 /*******************************
6340 * RESTRAINTS *
6341 *******************************/
6342
6343 int
tbl_is_predicate_attribute(atom_t key)6344 tbl_is_predicate_attribute(atom_t key)
6345 { return ( key == ATOM_abstract ||
6346 key == ATOM_subgoal_abstract ||
6347 key == ATOM_answer_abstract ||
6348 key == ATOM_max_answers
6349 );
6350 }
6351
6352
6353 static void
clear_table_props(table_props * p)6354 clear_table_props(table_props *p)
6355 { p->abstract = (size_t)-1;
6356 p->subgoal_abstract = (size_t)-1;
6357 p->answer_abstract = (size_t)-1;
6358 p->max_answers = (size_t)-1;
6359 }
6360
6361
6362 void
tbl_reset_tabling_attributes(Definition def)6363 tbl_reset_tabling_attributes(Definition def)
6364 { table_props *p;
6365
6366 if ( (p=def->tabling) )
6367 clear_table_props(p);
6368 }
6369
6370
6371 int
tbl_get_predicate_attribute(Definition def,atom_t att,size_t * value)6372 tbl_get_predicate_attribute(Definition def, atom_t att, size_t *value)
6373 { table_props *p;
6374
6375 if ( (p=def->tabling) )
6376 { size_t v0;
6377
6378 if ( att == ATOM_abstract )
6379 v0 = p->abstract;
6380 else if ( att == ATOM_subgoal_abstract )
6381 v0 = p->subgoal_abstract;
6382 else if ( att == ATOM_answer_abstract )
6383 v0 = p->answer_abstract;
6384 else if ( att == ATOM_max_answers )
6385 v0 = p->max_answers;
6386 else
6387 return -1;
6388
6389 if ( v0 != (size_t)-1 )
6390 { *value = v0;
6391 return TRUE;
6392 }
6393 }
6394
6395 return FALSE;
6396 }
6397
6398
6399 int
tbl_set_predicate_attribute(Definition def,atom_t att,size_t value)6400 tbl_set_predicate_attribute(Definition def, atom_t att, size_t value)
6401 { table_props *p;
6402
6403 if ( !(p=def->tabling) )
6404 { p = allocHeapOrHalt(sizeof(*p));
6405
6406 clear_table_props(p);
6407 if ( !COMPARE_AND_SWAP_PTR(&def->tabling, NULL, p) )
6408 { p = def->tabling;
6409 freeHeap(p, sizeof(*p));
6410 }
6411 }
6412
6413 if ( att == ATOM_abstract )
6414 p->abstract = value;
6415 else if ( att == ATOM_subgoal_abstract )
6416 p->subgoal_abstract = value;
6417 else if ( att == ATOM_answer_abstract )
6418 p->answer_abstract = value;
6419 else if ( att == ATOM_max_answers )
6420 p->max_answers = value;
6421 else
6422 return -1;
6423
6424 return TRUE;
6425 }
6426
6427
6428 int
tbl_is_restraint_flag(atom_t key)6429 tbl_is_restraint_flag(atom_t key)
6430 { return ( key == ATOM_max_table_subgoal_size_action ||
6431 key == ATOM_max_table_subgoal_size ||
6432 key == ATOM_max_table_answer_size_action ||
6433 key == ATOM_max_table_answer_size ||
6434 key == ATOM_max_answers_for_subgoal_action ||
6435 key == ATOM_max_answers_for_subgoal );
6436 }
6437
6438
6439 static int
unify_restraint(term_t t,size_t val)6440 unify_restraint(term_t t, size_t val)
6441 { if ( val == (size_t)-1 )
6442 return FALSE;
6443 else
6444 return PL_unify_uint64(t, val);
6445 }
6446
6447
6448 int
tbl_get_restraint_flag(term_t t,atom_t key ARG_LD)6449 tbl_get_restraint_flag(term_t t, atom_t key ARG_LD)
6450 { if ( key == ATOM_max_table_subgoal_size_action )
6451 return PL_unify_atom(t, LD->tabling.restraint.max_table_subgoal_size_action);
6452 else if ( key == ATOM_max_table_answer_size_action )
6453 return PL_unify_atom(t, LD->tabling.restraint.max_table_answer_size_action);
6454 else if ( key == ATOM_max_answers_for_subgoal_action )
6455 return PL_unify_atom(t, LD->tabling.restraint.max_answers_for_subgoal_action);
6456 else if ( key == ATOM_max_table_subgoal_size )
6457 return unify_restraint(t, LD->tabling.restraint.max_table_subgoal_size);
6458 else if ( key == ATOM_max_table_answer_size )
6459 return unify_restraint(t, LD->tabling.restraint.max_table_answer_size);
6460 else if ( key == ATOM_max_answers_for_subgoal )
6461 return unify_restraint(t, LD->tabling.restraint.max_answers_for_subgoal);
6462 else
6463 return -1;
6464 }
6465
6466
6467 static int
set_restraint_action(term_t t,atom_t key,atom_t * valp ARG_LD)6468 set_restraint_action(term_t t, atom_t key, atom_t *valp ARG_LD)
6469 { atom_t act;
6470
6471 if ( PL_get_atom_ex(t, &act) )
6472 { if ( act == ATOM_error || act == ATOM_warning || act == ATOM_suspend )
6473 { ok:
6474 *valp = act;
6475 return TRUE;
6476 }
6477
6478 if ( act == ATOM_complete_soundly ) /* XSB compatibility */
6479 act = ATOM_bounded_rationality;
6480
6481 if ( key == ATOM_max_table_subgoal_size_action &&
6482 ( act == ATOM_abstract ) )
6483 goto ok;
6484 if ( key == ATOM_max_table_answer_size_action &&
6485 ( act == ATOM_bounded_rationality ||
6486 act == ATOM_fail) )
6487 goto ok;
6488 if ( key == ATOM_max_answers_for_subgoal_action &&
6489 ( act == ATOM_bounded_rationality ) )
6490 goto ok;
6491
6492 return PL_domain_error("restraint_action", t);
6493 }
6494
6495 return FALSE;
6496 }
6497
6498
6499 static int
set_restraint(term_t t,size_t * valp)6500 set_restraint(term_t t, size_t *valp)
6501 { GET_LD
6502 atom_t inf;
6503
6504 if ( PL_get_atom(t, &inf) && inf == ATOM_infinite )
6505 { *valp = (size_t)-1;
6506 return TRUE;
6507 }
6508 return PL_get_size_ex(t, valp);
6509 }
6510
6511
6512 int
tbl_set_restraint_flag(term_t t,atom_t key ARG_LD)6513 tbl_set_restraint_flag(term_t t, atom_t key ARG_LD)
6514 { if ( key == ATOM_max_table_subgoal_size_action )
6515 return set_restraint_action(
6516 t, key,
6517 &LD->tabling.restraint.max_table_subgoal_size_action PASS_LD);
6518 else if ( key == ATOM_max_table_answer_size_action )
6519 return set_restraint_action(
6520 t, key,
6521 &LD->tabling.restraint.max_table_answer_size_action PASS_LD);
6522 else if ( key == ATOM_max_answers_for_subgoal_action )
6523 return set_restraint_action(
6524 t, key,
6525 &LD->tabling.restraint.max_answers_for_subgoal_action PASS_LD);
6526 else if ( key == ATOM_max_table_subgoal_size )
6527 return set_restraint(t, &LD->tabling.restraint.max_table_subgoal_size);
6528 else if ( key == ATOM_max_table_answer_size )
6529 return set_restraint(t, &LD->tabling.restraint.max_table_answer_size);
6530 else if ( key == ATOM_max_answers_for_subgoal )
6531 return set_restraint(t, &LD->tabling.restraint.max_answers_for_subgoal);
6532 else
6533 return -1;
6534 }
6535
6536
6537 static atom_t
tripwire_answers_for_subgoal(worklist * wl ARG_LD)6538 tripwire_answers_for_subgoal(worklist *wl ARG_LD)
6539 { table_props *ps;
6540 size_t limit;
6541
6542 if ( ((ps=wl->predicate->tabling) &&
6543 (limit=ps->max_answers) != (size_t)-1) )
6544 { if ( wl->table->value_count >= limit )
6545 return ATOM_bounded_rationality;
6546 return NULL_ATOM;
6547 }
6548
6549 if ( (limit=LD->tabling.restraint.max_answers_for_subgoal) != (size_t)-1 )
6550 { if ( wl->table->value_count == limit )
6551 return LD->tabling.restraint.max_answers_for_subgoal_action;
6552 }
6553
6554 return NULL_ATOM;
6555 }
6556
6557
6558 /* Create the most general ret/N term compliant with `spec`. We need
6559 * this term when the answer count restraint is exceeded.
6560 */
6561
6562 static int
generalise_answer_substitution(term_t spec,term_t gen ARG_LD)6563 generalise_answer_substitution(term_t spec, term_t gen ARG_LD)
6564 { Word p = valTermRef(spec);
6565
6566 deRef(p);
6567 if ( isTerm(*p) )
6568 return PL_unify_functor(gen, functorTerm(*p));
6569 if ( *p == ATOM_ret )
6570 return PL_unify_atom(gen, ATOM_ret);
6571
6572 return PL_type_error("answer_substitution", spec);
6573 }
6574
6575
6576 /* Add the condition `answer_count_restraint` to the current delay list.
6577 * We can simply call the predicate as the constraint will be added to
6578 * the global delay list as a result.
6579 */
6580
6581
6582 static int
add_answer_count_restraint(void)6583 add_answer_count_restraint(void)
6584 { static predicate_t pred = NULL;
6585
6586 if ( !pred )
6587 pred = PL_predicate("answer_count_restraint", 0, "system");
6588
6589 DEBUG(MSG_TABLING_RESTRAINT,
6590 Sdprintf("Calling %s\n", procedureName(pred)));
6591
6592 return PL_call_predicate(NULL, PL_Q_PASS_EXCEPTION, pred, 0);
6593 }
6594
6595
6596 static int
add_radial_restraint(void)6597 add_radial_restraint(void)
6598 { static predicate_t pred = NULL;
6599
6600 if ( !pred )
6601 pred = PL_predicate("radial_restraint", 0, "system");
6602
6603 DEBUG(MSG_TABLING_RESTRAINT,
6604 Sdprintf("Calling %s\n", procedureName(pred)));
6605
6606 return PL_call_predicate(NULL, PL_Q_PASS_EXCEPTION, pred, 0);
6607 }
6608
6609
6610 static int
tbl_wl_tripwire(worklist * wl,atom_t action,atom_t wire)6611 tbl_wl_tripwire(worklist *wl, atom_t action, atom_t wire)
6612 { GET_LD
6613 static predicate_t pred = NULL;
6614 term_t av;
6615
6616 if ( !pred )
6617 pred = PL_predicate("tripwire", 3, "$tabling");
6618
6619 DEBUG(MSG_TABLING_RESTRAINT,
6620 Sdprintf("Calling %s\n", procedureName(pred)));
6621
6622 return ( (av = PL_new_term_refs(3)) &&
6623 PL_put_atom(av+0, wire) &&
6624 PL_put_atom(av+1, action) &&
6625 PL_put_atom(av+2, trie_symbol(wl->table)) &&
6626 PL_call_predicate(NULL, PL_Q_PASS_EXCEPTION, pred, av) );
6627 }
6628
6629
6630 static int
tbl_pred_tripwire(Definition def,atom_t action,atom_t wire)6631 tbl_pred_tripwire(Definition def, atom_t action, atom_t wire)
6632 { GET_LD
6633 static predicate_t pred = NULL;
6634 term_t av;
6635
6636 if ( !pred )
6637 pred = PL_predicate("tripwire", 3, "$tabling");
6638
6639 DEBUG(MSG_TABLING_RESTRAINT,
6640 Sdprintf("Calling %s\n", procedureName(pred)));
6641
6642 return ( (av = PL_new_term_refs(3)) &&
6643 PL_put_atom(av+0, wire) &&
6644 PL_put_atom(av+1, action) &&
6645 unify_definition(MODULE_user, av+2, def, 0, GP_QUALIFY) &&
6646 PL_call_predicate(NULL, PL_Q_PASS_EXCEPTION, pred, av) );
6647 }
6648
6649 /*******************************
6650 * CONCURRENCY *
6651 *******************************/
6652
6653 #if O_PLMT
6654
6655 static int
table_needs_work(trie * atrie)6656 table_needs_work(trie *atrie)
6657 { if ( true(atrie, TRIE_COMPLETE) )
6658 { idg_node *n;
6659
6660 if ( (n=atrie->data.IDG) )
6661 { if ( n->falsecount > 0 || /* invalid */
6662 n->reevaluating ) /* fresh (re-evaluating) */
6663 return TRUE;
6664 }
6665
6666 return FALSE;
6667 }
6668
6669 return TRUE;
6670 }
6671
6672
6673 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6674 Claim ownership for an answer table if the table is incomplete/invalid:
6675
6676 - If we alread own the table, fine
6677 - Else
6678 - If the table is incomplete, someone else is completing it
6679 (otherwise we were the owner). Either throw a `deadlock`
6680 exception or wait.
6681 - If the table needs work (fresh, invalid), try to claim it.
6682 - If the table is complete, return its compiled trie. As
6683 we are in a locked region we can do so safely.
6684
6685 Note that this code uses a mutex/condition variable pair. Currently
6686 there is a single mutex. Future versions could use an array of these to
6687 reduce contention.
6688 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
6689
6690 static int
claim_answer_table(trie * atrie,atom_t * clrefp,int flags ARG_LD)6691 claim_answer_table(trie *atrie, atom_t *clrefp, int flags ARG_LD)
6692 { if ( true(atrie, TRIE_ISSHARED) && !(flags&AT_NOCLAIM) )
6693 { int mytid = PL_thread_self();
6694 volatile atom_t clref = 0;
6695
6696 if ( atrie->tid != mytid )
6697 { LOCK_SHARED_TABLE(atrie);
6698 retry_shared:
6699 if ( atrie->tid )
6700 { register_waiting(mytid, atrie);
6701 if ( is_deadlock(atrie) )
6702 { term_t ex;
6703
6704 DEBUG(MSG_TABLING_SHARED,
6705 print_answer_table(atrie, "DEADLOCK"));
6706 unregister_waiting(mytid, atrie);
6707 if ( (ex = PL_new_term_ref()) &&
6708 PL_put_atom(ex, ATOM_deadlock) )
6709 PL_raise_exception(ex);
6710 UNLOCK_SHARED_TABLE(atrie);
6711 return FALSE;
6712 }
6713 TRIE_STAT_INC(atrie, wait);
6714 if ( !wait_for_table_to_complete(atrie) )
6715 { UNLOCK_SHARED_TABLE(atrie);
6716 return FALSE;
6717 }
6718 unregister_waiting(mytid, atrie);
6719 if ( !atrie->tid && table_needs_work(atrie) )
6720 { DEBUG(MSG_TABLING_SHARED,
6721 print_answer_table(atrie, "stealing abandonned trie"));
6722 take_trie(atrie, mytid);
6723 } else
6724 { goto complete;
6725 }
6726 } else if ( table_needs_work(atrie) )
6727 { DEBUG(MSG_TABLING_SHARED,
6728 print_answer_table(atrie, "claiming"));
6729 take_trie(atrie, mytid);
6730 } else /* complete and valid */
6731 { complete:
6732 if ( clrefp )
6733 { if ( !(clref=atrie->clause) )
6734 { Procedure proc = ((flags&AT_MODED)
6735 ? GD->procedures.trie_gen_compiled3
6736 : GD->procedures.trie_gen_compiled2);
6737
6738 clref = compile_trie(proc->definition, atrie PASS_LD);
6739 }
6740 pushVolatileAtom(clref); /* see (*) above */
6741 if ( clref != atrie->clause )
6742 goto retry_shared;
6743 *clrefp = clref;
6744 }
6745 }
6746 UNLOCK_SHARED_TABLE(atrie);
6747 }
6748 }
6749
6750 return TRUE;
6751 }
6752
6753
6754 static trie_array *
new_trie_array(void)6755 new_trie_array(void)
6756 { trie_array *a = allocHeapOrHalt(sizeof(*a));
6757
6758 memset(a, 0, sizeof(*a));
6759 a->blocks[0] = a->preallocated - 1;
6760 a->blocks[1] = a->preallocated - 1;
6761 a->blocks[2] = a->preallocated - 1;
6762
6763 return a;
6764 }
6765
6766
6767 static void
register_waiting(int tid,trie * atrie)6768 register_waiting(int tid, trie *atrie)
6769 { trie_array *ta;
6770 size_t idx = MSB(tid);
6771
6772 if ( !(ta=GD->tabling.waiting) )
6773 { ta = new_trie_array();
6774 if ( !COMPARE_AND_SWAP_PTR(&GD->tabling.waiting, NULL, ta) )
6775 { freeHeap(ta, sizeof(*ta));
6776 ta = GD->tabling.waiting;
6777 }
6778 }
6779
6780 if ( !ta->blocks[idx] )
6781 { if ( !ta->blocks[idx] )
6782 { size_t bs = (size_t)1<<idx;
6783 trie **newblock;
6784
6785 if ( !(newblock=PL_malloc_uncollectable(bs*sizeof(trie*))) )
6786 outOfCore();
6787
6788 memset(newblock, 0, bs*sizeof(trie*));
6789 if ( !COMPARE_AND_SWAP_PTR(&ta->blocks[idx], NULL, newblock-bs) )
6790 PL_free(newblock);
6791 }
6792 }
6793
6794 ta->blocks[idx][tid] = atrie;
6795 }
6796
6797
6798 static void
unregister_waiting(int tid,trie * atrie)6799 unregister_waiting(int tid, trie *atrie)
6800 { size_t idx = MSB(tid);
6801
6802 GD->tabling.waiting->blocks[idx][tid] = NULL;
6803 }
6804
6805
6806 static trie *
thread_waits_for_trie(int tid)6807 thread_waits_for_trie(int tid)
6808 { trie_array *ta;
6809
6810 if ( (ta=GD->tabling.waiting) )
6811 { size_t idx = MSB(tid);
6812
6813 if ( ta->blocks[idx] )
6814 return ta->blocks[idx][tid];
6815 }
6816
6817 return NULL;
6818 }
6819
6820
6821 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6822 is_deadlock() succeeds if the proposed situation would lead to a
6823 deadlock.
6824 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
6825
6826 static void
stat_deadlock(trie * atrie)6827 stat_deadlock(trie *atrie)
6828 {
6829 #ifdef O_TRIE_STATS
6830 int mytid = atrie->tid;
6831 trie *t = NULL;
6832 int tid = mytid;
6833
6834 TRIE_STAT_INC(atrie, deadlock);
6835
6836 for(;;)
6837 { t = thread_waits_for_trie(tid);
6838 if ( t )
6839 { TRIE_STAT_INC(t, deadlock);
6840 tid = t->tid;
6841 }
6842
6843 if ( !t || !tid )
6844 return;
6845 if ( tid == mytid )
6846 return;
6847 }
6848 #endif
6849 }
6850
6851
6852 static int
is_deadlock(trie * atrie)6853 is_deadlock(trie *atrie)
6854 { int mytid = atrie->tid;
6855 trie *t = NULL;
6856 int tid = mytid;
6857
6858 for(;;)
6859 { t = thread_waits_for_trie(tid);
6860 if ( t )
6861 tid = t->tid;
6862
6863 if ( !t || !tid )
6864 return FALSE;
6865 if ( tid == mytid )
6866 { stat_deadlock(atrie);
6867 return TRUE;
6868 }
6869 }
6870 }
6871
6872
6873 static int
wait_for_table_to_complete(trie * atrie)6874 wait_for_table_to_complete(trie *atrie)
6875 { DEBUG(MSG_TABLING_SHARED,
6876 print_answer_table(atrie, "waiting for %d to complete", atrie->tid));
6877
6878 do
6879 { if ( cv_wait(&GD->tabling.cvar, &GD->tabling.mutex.mutex) == CV_INTR )
6880 { if ( PL_handle_signals() < 0 )
6881 { DEBUG(MSG_TABLING_SHARED,
6882 print_answer_table(atrie, "Ready (interrupted"));
6883 return FALSE;
6884 }
6885 }
6886 } while( atrie->tid != 0 );
6887
6888 DEBUG(MSG_TABLING_SHARED,
6889 print_answer_table(atrie,
6890 table_needs_work(atrie) ? "Ready (abandonned)"
6891 : "Ready (completed)"));
6892
6893 return TRUE;
6894 }
6895 #endif /*O_PLMT*/
6896
6897
6898 /*******************************
6899 * UNTABLE *
6900 *******************************/
6901
6902 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6903 This predicate is called for clauses from M:'$tabled'(Head, Mode) are
6904 deleted during reconsult. It is called from the fixup phase of reloading
6905 a file (see pl-srcfile.c) and we may not use any error condition.
6906 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
6907
6908 void
untable_from_clause(Clause cl)6909 untable_from_clause(Clause cl)
6910 { if ( cl->codes[0] == encode(H_FUNCTOR) )
6911 { GET_LD
6912 functor_t f = (functor_t)cl->codes[1];
6913 Module m = cl->predicate->module;
6914 Procedure proc = isCurrentProcedure(f, m);
6915
6916 if ( proc && !callEventHook(PLEV_UNTABLE, proc) )
6917 fatalError("Failed to register untable event\n");
6918 } else
6919 { Sdprintf("WARNING: untable_from_clause(): unexpected clause\n");
6920 }
6921 }
6922
6923 /*******************************
6924 * INIT *
6925 *******************************/
6926
6927 void
initTabling(void)6928 initTabling(void)
6929 { GET_LD
6930
6931 #ifdef O_PLMT
6932 initSimpleMutex(&GD->tabling.mutex, "L_SHARED_TABLING");
6933 cv_init(&GD->tabling.cvar, NULL);
6934 #endif
6935
6936 LD->tabling.restraint.max_table_subgoal_size_action = ATOM_error;
6937 LD->tabling.restraint.max_table_subgoal_size = (size_t)-1;
6938 LD->tabling.restraint.max_table_answer_size_action = ATOM_error;
6939 LD->tabling.restraint.max_table_answer_size = (size_t)-1;
6940 LD->tabling.restraint.max_answers_for_subgoal_action = ATOM_error;
6941 LD->tabling.restraint.max_answers_for_subgoal = (size_t)-1;
6942
6943 setPrologFlag("max_table_subgoal_size_action", FT_ATOM, "error");
6944 setPrologFlag("max_table_answer_size_action", FT_ATOM, "error");
6945 setPrologFlag("max_answers_for_subgoal_action", FT_ATOM, "error");
6946 setPrologFlag("max_table_subgoal_size", FT_INTEGER, -1);
6947 setPrologFlag("max_table_answer_size", FT_INTEGER, -1);
6948 setPrologFlag("max_answers_for_subgoal", FT_INTEGER, -1);
6949 }
6950
6951 /*******************************
6952 * PUBLISH PREDICATES *
6953 *******************************/
6954
6955 #define NDET PL_FA_NONDETERMINISTIC
6956 #define META PL_FA_TRANSPARENT
6957
6958 BeginPredDefs(tabling)
6959 PRED_DEF("$tbl_pop_worklist", 2, tbl_pop_worklist, 0)
6960 PRED_DEF("$tbl_wkl_add_answer", 4, tbl_wkl_add_answer, 0)
6961 PRED_DEF("$tbl_wkl_make_follower", 1, tbl_wkl_make_follower, 0)
6962 PRED_DEF("$tbl_wkl_add_suspension", 2, tbl_wkl_add_suspension, 0)
6963 PRED_DEF("$tbl_wkl_add_suspension", 3, tbl_wkl_add_suspension, 0)
6964 PRED_DEF("$tbl_wkl_done", 1, tbl_wkl_done, 0)
6965 PRED_DEF("$tbl_wkl_negative", 1, tbl_wkl_negative, 0)
6966 PRED_DEF("$tbl_wkl_is_false", 1, tbl_wkl_is_false, 0)
6967 PRED_DEF("$tbl_wkl_answer_trie", 2, tbl_wkl_answer_trie, 0)
6968 PRED_DEF("$tbl_wkl_work", 6, tbl_wkl_work, NDET)
6969 PRED_DEF("$tbl_variant_table", 5, tbl_variant_table, 0)
6970 PRED_DEF("$tbl_abstract_table", 6, tbl_abstract_table, 0)
6971 PRED_DEF("$tbl_existing_variant_table", 5, tbl_existing_variant_table, 0)
6972 PRED_DEF("$tbl_moded_variant_table", 5, tbl_moded_variant_table, 0)
6973 #ifdef O_PLMT
6974 PRED_DEF("$tbl_variant_table", 1, tbl_variant_table, NDET)
6975 #else
6976 PRED_DEF("$tbl_variant_table", 1, tbl_local_variant_table, 0)
6977 #endif
6978 PRED_DEF("$tbl_local_variant_table", 1, tbl_local_variant_table, 0)
6979 PRED_DEF("$tbl_global_variant_table", 1, tbl_global_variant_table, 0)
6980 PRED_DEF("$tbl_table_status", 2, tbl_table_status, 0)
6981 PRED_DEF("$tbl_table_status", 4, tbl_table_status, 0)
6982 PRED_DEF("$tbl_table_pi", 2, tbl_table_pi, 0)
6983 PRED_DEF("$tbl_table_complete_all", 3, tbl_table_complete_all, 0)
6984 PRED_DEF("$tbl_free_component", 1, tbl_free_component, 0)
6985 PRED_DEF("$tbl_table_discard_all", 1, tbl_table_discard_all, 0)
6986 PRED_DEF("$tbl_abolish_local_tables", 0, tbl_abolish_local_tables, 0)
6987 PRED_DEF("$tbl_destroy_table", 1, tbl_destroy_table, 0)
6988 PRED_DEF("$tbl_trienode", 1, tbl_trienode, 0)
6989 PRED_DEF("$tbl_is_trienode", 1, tbl_is_trienode, 0)
6990 PRED_DEF("$tbl_delay_list", 1, tbl_delay_list, 0)
6991 PRED_DEF("$tbl_set_delay_list", 1, tbl_set_delay_list, 0)
6992 PRED_DEF("$tbl_add_global_delays", 2, tbl_add_global_delays, 0)
6993
6994 PRED_DEF("$tbl_scc", 1, tbl_scc, 0)
6995 PRED_DEF("$tbl_scc_data", 2, tbl_scc_data, 0)
6996 PRED_DEF("$tbl_worklist_data", 2, tbl_worklist_data, 0)
6997 PRED_DEF("$tbl_wkl_table", 2, tbl_wkl_table, 0)
6998 PRED_DEF("$tbl_answer", 3, tbl_answer, NDET)
6999 PRED_DEF("$tbl_answer_c", 4, tbl_answer_c, NDET)
7000 PRED_DEF("$tbl_answer_dl", 3, tbl_answer_dl, NDET)
7001 PRED_DEF("$tbl_answer_dl", 4, tbl_answer_dl, NDET)
7002 PRED_DEF("$tbl_answer_update_dl", 2, tbl_answer_update_dl, NDET)
7003 PRED_DEF("$tbl_answer_update_dl", 3, tbl_answer_update_dl, NDET)
7004 PRED_DEF("$tbl_force_truth_value", 3, tbl_force_truth_value, 0)
7005 PRED_DEF("$tbl_set_answer_completed", 1, tbl_set_answer_completed, 0)
7006 PRED_DEF("$tbl_is_answer_completed", 1, tbl_is_answer_completed, 0)
7007 PRED_DEF("$tnot_implementation", 2, tnot_implementation, META)
7008 PRED_DEF("$tbl_implementation", 2, tbl_implementation, META)
7009 PRED_DEF("$is_answer_trie", 1, is_answer_trie, 0)
7010
7011 PRED_DEF("$idg_add_dyncall", 1, idg_add_dyncall, 0)
7012 PRED_DEF("$idg_set_current", 1, idg_set_current, 0)
7013 PRED_DEF("$idg_set_current", 2, idg_set_current, 0)
7014 PRED_DEF("$idg_reset_current", 0, idg_reset_current, 0)
7015 PRED_DEF("$idg_edge", 3, idg_edge, NDET)
7016 PRED_DEF("$idg_changed", 1, idg_changed, 0)
7017 PRED_DEF("$idg_falsecount", 2, idg_falsecount, 0)
7018 PRED_DEF("$idg_set_falsecount", 2, idg_set_falsecount, 0)
7019
7020 PRED_DEF("$tbl_reeval_prepare", 3, tbl_reeval_prepare, 0)
7021 PRED_DEF("$tbl_reeval_abandon", 1, tbl_reeval_abandon, 0)
7022 PRED_DEF("$tbl_reeval_wait", 2, tbl_reeval_wait, 0)
7023 EndPredDefs
7024