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