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)  1985-2020, University of Amsterdam
7                               VU University Amsterdam
8 			      CWI, Amsterdam
9     All rights reserved.
10 
11     Redistribution and use in source and binary forms, with or without
12     modification, are permitted provided that the following conditions
13     are met:
14 
15     1. Redistributions of source code must retain the above copyright
16        notice, this list of conditions and the following disclaimer.
17 
18     2. Redistributions in binary form must reproduce the above copyright
19        notice, this list of conditions and the following disclaimer in
20        the documentation and/or other materials provided with the
21        distribution.
22 
23     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27     COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33     ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34     POSSIBILITY OF SUCH DAMAGE.
35 */
36 
37 /*#define O_DEBUG 1*/
38 #include "pl-incl.h"
39 #include "pl-comp.h"
40 #include "pl-arith.h"
41 #include "pl-inline.h"
42 #include "pl-dbref.h"
43 #include "pl-wrap.h"
44 #include "pl-prof.h"
45 #include "pl-event.h"
46 #include "pl-tabling.h"
47 #include <fenv.h>
48 #ifdef _MSC_VER
49 #pragma warning(disable: 4102)		/* unreferenced labels */
50 #endif
51 
52 #define	     BFR (LD->choicepoints)	/* choicepoint registration */
53 
54 #if sun
55 #include <prof.h>			/* in-function profiling */
56 #else
57 #define MARK(label)
58 #endif
59 
60 static Choice	newChoice(choice_type type, LocalFrame fr ARG_LD);
61 
62 #if COUNTING
63 
64 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
65 The counting code has been added   while investigating the time critical
66 WAM  instructions.  The  current  implementation  runs  on  top  of  the
67 information  provided  by  code_info   (from    pl-comp.c)   and  should
68 automatically addapt to modifications in the VM instruction set.
69 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
70 
71 typedef struct
72 { code  code;
73   int	times;
74   int  *vartimesptr;
75 } count_info;
76 
77 #define MAXVAR 8
78 
79 static count_info counting[I_HIGHEST];
80 
81 static void
count(code c,Code PC)82 count(code c, Code PC)
83 { const code_info *info = &codeTable[c];
84 
85   counting[c].times++;
86   switch(info->argtype)
87   { case CA1_VAR:
88     case CA1_FVAR:
89     case CA1_CHP:
90     { int v = (int)*PC;
91 
92       v -= ARGOFFSET/sizeof(word);
93       assert(v>=0);
94       if ( v >= MAXVAR )
95 	v = MAXVAR-1;
96 
97       if ( !counting[c].vartimesptr )
98       { int bytes = sizeof(int)*MAXVAR;
99 
100 	counting[c].vartimesptr = allocHeapOrHalt(bytes);
101 	memset(counting[c].vartimesptr, 0, bytes);
102       }
103       counting[c].vartimesptr[v]++;
104     }
105   }
106 }
107 
108 
109 static void
countHeader()110 countHeader()
111 { int m;
112   int amax = MAXVAR;
113   char last[20];
114 
115   Sfprintf(Scurout, "%-13s %8s ", "Instruction", "times");
116   for(m=0; m < amax-1; m++)
117     Sfprintf(Scurout, " %8d", m);
118   Ssprintf(last, ">%d", m);
119   Sfprintf(Scurout, " %8s\n", last);
120   for(m=0; m<(31+amax*8); m++)
121     Sputc('=', Scurout);
122   Sfprintf(Scurout, "\n");
123 }
124 
125 
126 static int
cmpcounts(const void * p1,const void * p2)127 cmpcounts(const void *p1, const void *p2)
128 { const count_info *c1 = p1;
129   const count_info *c2 = p2;
130 
131   return c2->times - c1->times;
132 }
133 
134 
135 word
pl_count()136 pl_count()
137 { int i;
138   count_info counts[I_HIGHEST];
139   count_info *c;
140 
141   countHeader();
142 
143   memcpy(counts, counting, sizeof(counts));
144   for(i=0, c=counts; i<I_HIGHEST; i++, c++)
145     c->code = i;
146   qsort(counts, I_HIGHEST, sizeof(count_info), cmpcounts);
147 
148   for(c = counts, i=0; i<I_HIGHEST; i++, c++)
149   { const code_info *info = &codeTable[c->code];
150 
151     Sfprintf(Scurout, "%-13s %8d ", info->name, c->times);
152     if ( c->vartimesptr )
153     { int n, m=MAXVAR;
154 
155       while(m>0 && c->vartimesptr[m-1] == 0 )
156 	m--;
157       for(n=0; n<m; n++)
158 	Sfprintf(Scurout, " %8d", c->vartimesptr[n]);
159     }
160     Sfprintf(Scurout, "\n");
161   }
162 
163   succeed;
164 }
165 
166 #else /* ~COUNTING */
167 
168 #define count(id, pc)			/* no debugging not counting */
169 
170 #endif /* COUNTING */
171 
172 		 /*******************************
173 		 *	     DEBUGGING		*
174 		 *******************************/
175 
176 #if defined(O_DEBUG) || defined(SECURE_GC) || defined(O_MAINTENANCE)
177 #define loffset(p) loffset__LD(p PASS_LD)
178 static intptr_t
loffset__LD(void * p ARG_LD)179 loffset__LD(void *p ARG_LD)
180 { if ( p == NULL )
181     return 0;
182 
183   assert((intptr_t)p % sizeof(word) == 0);
184   return (Word)p-(Word)lBase;
185 }
186 #endif
187 
188 #ifdef O_DEBUG
189 
190 static void
DbgPrintInstruction(LocalFrame FR,Code PC)191 DbgPrintInstruction(LocalFrame FR, Code PC)
192 { static LocalFrame ofr = NULL;		/* not thread-safe */
193 
194   if ( DEBUGGING(MSG_VMI) )
195   { GET_LD
196 
197     if ( ofr != FR )
198     { Sfprintf(Serror, "#%ld at [%ld] predicate %s\n",
199 	       loffset(FR),
200 	       levelFrame(FR),
201 	       predicateName(FR->predicate));
202       ofr = FR;
203     }
204 
205     { Code relto = NULL;
206       intptr_t offset;
207 
208       if ( FR->predicate->codes &&
209 	   (offset = (PC - FR->predicate->codes)) >= 0 &&
210 	   (offset < (intptr_t)FR->predicate->codes[-1] ||
211 	    (offset == 0 && FR->predicate->codes[-1] == 0)) ) /* see initSupervisors() */
212       { relto = FR->predicate->codes;
213       } else if ( FR->clause )
214       { relto = FR->clause->value.clause->codes;
215       } else
216 	relto = NULL;
217 
218       Sdprintf("\t%4ld %s\n", (long)(PC-relto), codeTable[decode(*PC)].name);
219     }
220   }
221 }
222 
223 #else
224 
225 #define DbgPrintInstruction(fr, pc)
226 
227 #endif
228 
229 
230 
231 
232 #include "pl-alloc.c"
233 #include "pl-index.c"
234 
235 
236 		 /*******************************
237 		 *	     SIGNALS		*
238 		 *******************************/
239 
240 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
241 LD->alerted indicates the system is running in  some sort of `safe' mode
242 and therefore should perform various checks. It   is  a disjunction of a
243 number of conditions that would ortherwise  have to be tested one-by-one
244 in several virtual machine instructions.  Currently covers:
245 
246 	* Pending signals
247 	* pthread_cancel() requested
248         * Activation of the profiler
249 	* out-of-stack signalled
250 	* active depth-limit
251 	* attributed variable wakeup
252 	* debugmode active
253 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
254 
255 void
updateAlerted(PL_local_data_t * ld)256 updateAlerted(PL_local_data_t *ld)
257 { int mask = 0;
258 
259   if ( is_signalled(PASS_LDARG1(ld)) )		mask |= ALERT_SIGNAL;
260 #ifdef O_PROFILE
261   if ( ld->profile.active )			mask |= ALERT_PROFILE;
262 #endif
263 #ifdef O_PLMT
264   if ( ld->exit_requested )			mask |= ALERT_EXITREQ;
265 #endif
266 #ifdef O_LIMIT_DEPTH
267   if ( ld->depth_info.limit != DEPTH_NO_LIMIT ) mask |= ALERT_DEPTHLIMIT;
268 #endif
269 #ifdef O_INFERENCE_LIMIT
270   if ( ld->inference_limit.limit != INFERENCE_NO_LIMIT )
271 						mask |= ALERT_INFERENCELIMIT;
272 #endif
273 #ifdef O_ATTVAR
274 					/* is valTermRef(ld->attvar.head) */
275   if ( ld->stacks.local.base &&
276        !isVar(((Word)ld->stacks.local.base)[ld->attvar.head]) )
277 						mask |= ALERT_WAKEUP;
278 #endif
279 #ifdef O_DEBUGGER
280   if ( ld->_debugstatus.debugging )		mask |= ALERT_DEBUG;
281 #endif
282   if ( ld->fli.string_buffers.top )		mask |= ALERT_BUFFER;
283 
284   ld->alerted = mask;
285 
286   if ( (mask&ALERT_DEBUG) || ld->prolog_flag.occurs_check != OCCURS_CHECK_FALSE )
287     ld->slow_unify = TRUE;		/* see VMI B_UNIFY_VAR */
288   else
289     ld->slow_unify = FALSE;
290 }
291 
292 
293 /* raiseSignal() sets a signal in a target thread.  This implies manipulating
294    the mask and setting ld->alerted. Note that we cannot call
295    updateAlerted() because the O_ATTVAR might go wrong if the target
296    thread performs a stack-shift.
297 */
298 
299 int
raiseSignal(PL_local_data_t * ld,int sig)300 raiseSignal(PL_local_data_t *ld, int sig)
301 { if ( sig > 0 && sig <= MAXSIGNAL && ld )
302   { int off = (sig-1) / 32;
303     int mask = (1 << ((sig-1)%32));
304     int alerted;
305 
306     ATOMIC_OR(&ld->signal.pending[off], mask);
307 
308     do
309     { alerted = ld->alerted;
310     } while ( !COMPARE_AND_SWAP_INT(&ld->alerted, alerted, alerted|ALERT_SIGNAL) );
311 
312     return TRUE;
313   }
314 
315   return FALSE;
316 }
317 
318 
319 		 /*******************************
320 		 *	   STACK-LAYOUT		*
321 		 *******************************/
322 
323 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
324 Brief description of the local stack-layout.  This stack contains:
325 
326 	* struct localFrame structures for the Prolog stackframes.
327 	* argument vectors and local variables for Prolog goals.
328 	* choice-points (struct choice)
329 	* term-references for foreign code.  The layout:
330 
331 
332 	lTop  -->| first free location |
333 		 -----------------------
334 		 | local variables     |
335 		 |        ...	       |
336 		 | arguments for goal  |
337 		 | localFrame struct   |
338 		 | queryFrame struct   |
339 		 -----------------------
340 		 |        ...	       |
341 		 | term-references     |
342 		 -----------------------
343 	lBase -->| # fliFrame struct   |
344 		 -----------------------
345 
346 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
347 
348 
349 		 /*******************************
350 		 *	 LOCAL ALLOCATION	*
351 		 *******************************/
352 
353 /* Note that lTop can be >= lMax when calling ENSURE_LOCAL_SPACE() */
354 
355 #define ENSURE_LOCAL_SPACE(bytes, ifnot) \
356 	if ( unlikely(addPointer(lTop, (bytes)) > (void*)lMax) ) \
357         { int rc; \
358 	  SAVE_REGISTERS(qid); \
359 	  rc = growLocalSpace__LD(bytes, ALLOW_SHIFT PASS_LD); \
360 	  LOAD_REGISTERS(qid); \
361 	  if ( rc != TRUE ) \
362 	  { rc = raiseStackOverflow(rc); \
363 	    ifnot; \
364 	  } \
365 	}
366 
367 
368 
369 		 /*******************************
370 		 *	    FOREIGN FRAME	*
371 		 *******************************/
372 
373 static fid_t
open_foreign_frame(ARG1_LD)374 open_foreign_frame(ARG1_LD)
375 { FliFrame fr = (FliFrame) lTop;
376 
377   assert((LocalFrame)(fr+1) <= lMax);
378   lTop = (LocalFrame)(fr+1);
379   fr->size = 0;
380   Mark(fr->mark);
381   DEBUG(CHK_SECURE, assert(fr>fli_context));
382   fr->parent = fli_context;
383   fr->magic = FLI_MAGIC;
384   fli_context = fr;
385 
386   return consTermRef(fr);
387 }
388 
389 
390 void
PL_close_foreign_frame__LD(fid_t id ARG_LD)391 PL_close_foreign_frame__LD(fid_t id ARG_LD)
392 { FliFrame fr = (FliFrame) valTermRef(id);
393 
394   if ( !id || fr->magic != FLI_MAGIC )
395     sysError("PL_close_foreign_frame(): illegal frame: %d", id);
396   DiscardMark(fr->mark);
397   fr->magic = FLI_MAGIC_CLOSED;
398   fli_context = fr->parent;
399   lTop = (LocalFrame) fr;
400 }
401 
402 
403 fid_t
PL_open_foreign_frame__LD(ARG1_LD)404 PL_open_foreign_frame__LD(ARG1_LD)
405 { size_t lneeded = sizeof(struct fliFrame) + MINFOREIGNSIZE*sizeof(word);
406 
407   if ( !ensureLocalSpace(lneeded) )
408     return 0;
409 
410   return open_foreign_frame(PASS_LD1);
411 }
412 
413 
414 #undef PL_open_foreign_frame
415 fid_t
PL_open_foreign_frame(void)416 PL_open_foreign_frame(void)
417 { GET_LD
418 
419   return PL_open_foreign_frame__LD(PASS_LD1);
420 }
421 				/* This local definition was here before */
422 #define PL_open_foreign_frame() open_foreign_frame(PASS_LD1)
423 
424 
425 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
426 Open a foreign frame to handle a signal.  We must skip MAXARITY words to
427 deal with the fact that the WAM write-mode   writes above the top of the
428 stack.
429 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
430 
431 fid_t
PL_open_signal_foreign_frame(int sync)432 PL_open_signal_foreign_frame(int sync)
433 { GET_LD
434   FliFrame fr;
435   size_t minspace = sizeof(struct localFrame) + MINFOREIGNSIZE*sizeof(word);
436   size_t margin   = sync ? 0 : MAXARITY*sizeof(word);
437 
438   if ( (char*)lTop + minspace + margin  > (char*)lMax )
439   { if ( sync )
440     { int rc;
441 
442       if ( (rc=growLocalSpace__LD(minspace, ALLOW_SHIFT PASS_LD)) != TRUE )
443 	return 0;
444     } else
445     { return 0;
446     }
447   }
448 
449   fr = addPointer(lTop, margin);
450   fr->magic = FLI_MAGIC;
451   fr->size = 0;
452   Mark(fr->mark);
453   fr->parent = fli_context;
454   lTop = (LocalFrame)(fr+1);
455   fli_context = fr;
456 
457   return consTermRef(fr);
458 }
459 
460 
461 #undef PL_close_foreign_frame
462 void
PL_close_foreign_frame(fid_t id)463 PL_close_foreign_frame(fid_t id)
464 { GET_LD
465 
466   PL_close_foreign_frame__LD(id PASS_LD);
467 }
468 #define PL_close_foreign_frame(id) PL_close_foreign_frame__LD(id PASS_LD)
469 
470 
471 void
PL_rewind_foreign_frame(fid_t id)472 PL_rewind_foreign_frame(fid_t id)
473 { GET_LD
474   FliFrame fr = (FliFrame) valTermRef(id);
475 
476   fli_context = fr;
477   Undo(fr->mark);
478   lTop = addPointer(fr, sizeof(struct fliFrame));
479   fr->size = 0;
480 }
481 
482 
483 void
PL_discard_foreign_frame(fid_t id)484 PL_discard_foreign_frame(fid_t id)
485 { GET_LD
486   FliFrame fr = (FliFrame) valTermRef(id);
487 
488   DEBUG(8, Sdprintf("Discarding foreign frame %p\n", fr));
489   fli_context = fr->parent;
490   Undo(fr->mark);
491   DiscardMark(fr->mark);
492   lTop = (LocalFrame) fr;
493 }
494 
495 		/********************************
496 		*         FOREIGN CALLS         *
497 		*********************************/
498 
499 #define CALL_FCUTTED(argc, f, c) \
500   { switch(argc) \
501     { case 0: \
502 	f(c); \
503         break; \
504       case 1: \
505 	f(0,(c)); \
506 	break; \
507       case 2: \
508 	f(0,0,(c)); \
509         break; \
510       case 3: \
511 	f(0,0,0,(c)); \
512         break; \
513       case 4: \
514 	f(0,0,0,0,(c)); \
515         break; \
516       case 5: \
517 	f(0,0,0,0,0,(c)); \
518         break; \
519       case 6: \
520 	f(0,0,0,0,0,0,(c)); \
521         break; \
522       case 7: \
523 	f(0,0,0,0,0,0,0,(c)); \
524         break; \
525       case 8: \
526 	f(0,0,0,0,0,0,0,0,(c)); \
527         break; \
528       case 9: \
529 	f(0,0,0,0,0,0,0,0,0,(c)); \
530         break; \
531       case 10: \
532 	f(0,0,0,0,0,0,0,0,0,0,(c)); \
533         break; \
534       default: \
535 	assert(0); \
536     } \
537   }
538 
539 static void
discardForeignFrame(LocalFrame fr ARG_LD)540 discardForeignFrame(LocalFrame fr ARG_LD)
541 { Definition def = fr->predicate;
542   int argc       = (int)def->functor->arity;
543   Func function  = def->impl.foreign.function;
544   struct foreign_context context;
545   fid_t fid;
546 
547   DEBUG(5, Sdprintf("\tCut %s, context = %p\n",
548 		    predicateName(def), fr->clause));
549 
550   context.context = (word)fr->clause;
551   context.control = FRG_CUTTED;
552   context.engine  = LD;
553 
554   fid = PL_open_foreign_frame();
555   if ( true(def, P_VARARG) )
556   { (*function)(0, argc, &context);
557   } else
558   { CALL_FCUTTED(argc, (*function), &context);
559   }
560   PL_close_foreign_frame(fid);
561 }
562 
563 
564 typedef struct finish_reason
565 { atom_t	name;			/* name of the reason */
566   int		is_exception;		/* is an exception reason */
567 } finish_reason;
568 
569 enum finished
570 { FINISH_EXIT = 0,			/* keep consistent with reason_decls[] */
571   FINISH_FAIL,
572   FINISH_CUT,
573   FINISH_EXITCLEANUP,
574   FINISH_EXTERNAL_EXCEPT_UNDO,
575   FINISH_EXTERNAL_EXCEPT,
576   FINISH_EXCEPT
577 };
578 
579 static const finish_reason reason_decls[] =
580 { { ATOM_exit,               FALSE },	/* keep consistent with enum finished */
581   { ATOM_fail,               FALSE },
582   { ATOM_cut,                FALSE },
583   { ATOM_exit,               FALSE },
584   { ATOM_external_exception, TRUE },
585   { ATOM_external_exception, TRUE },
586   { ATOM_exception,          TRUE }
587 };
588 
589 
590 static inline int
is_exception_finish(enum finished reason)591 is_exception_finish(enum finished reason)
592 { return reason_decls[reason].is_exception;
593 }
594 
595 
596 static int
unify_finished(term_t catcher,enum finished reason)597 unify_finished(term_t catcher, enum finished reason)
598 { GET_LD
599 
600   /* make sure declaration is consistent */
601   DEBUG(0, assert(reason_decls[FINISH_EXCEPT].name == ATOM_exception));
602 
603   if ( is_exception_finish(reason) )
604   { functor_t f = (reason == FINISH_EXCEPT ? FUNCTOR_exception1
605 					   : FUNCTOR_external_exception1);
606 
607     DEBUG(CHK_SECURE, checkData(valTermRef(exception_bin)));
608 
609     return PL_unify_term(catcher,
610 			 PL_FUNCTOR, f,
611 			   PL_TERM, exception_bin);
612   } else if ( reason == FINISH_EXIT )
613   { fail;
614   } else
615   { return PL_unify_atom(catcher, reason_decls[reason].name);
616   }
617 }
618 
619 
620 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
621 call_term() calls a term from C. The  sound   and  simple way is to call
622 call/1 with the term as argument, but in   most cases we can avoid that.
623 As frameFinished() is rather time critical this seems worthwhile.
624 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
625 
626 
627 static int
call1(Module mdef,term_t goal ARG_LD)628 call1(Module mdef, term_t goal ARG_LD)
629 { static predicate_t PRED_call1 = NULL;
630   qid_t qid;
631   int rc;
632 
633   if ( !PRED_call1 )
634     PRED_call1 = PL_predicate("call", 1, "system");
635 
636   qid = PL_open_query(mdef, PL_Q_PASS_EXCEPTION, PRED_call1, goal);
637   rc = PL_next_solution(qid);
638   PL_cut_query(qid);
639 
640   return rc;
641 }
642 
643 
644 static int
call_term(Module mdef,term_t goal ARG_LD)645 call_term(Module mdef, term_t goal ARG_LD)
646 { Word p = valTermRef(goal);
647   Module module = mdef;
648 
649   deRef(p);
650   if ( (p=stripModule(p, &module, 0 PASS_LD)) )
651   { functor_t functor;
652     term_t av;
653     Procedure proc;
654     qid_t qid;
655     int rval;
656 
657     if ( isAtom(*p) )
658     { if ( isTextAtom(*p) )
659       { functor = lookupFunctorDef(*p, 0);
660 	av = 0;
661       } else
662 	return call1(mdef, goal PASS_LD);
663     } else if ( isTerm(*p) )
664     { Functor f = valueTerm(*p);
665       FunctorDef fd = valueFunctor(f->definition);
666 
667       if ( isTextAtom(fd->name) &&
668 	   false(fd, CONTROL_F) &&
669 	   !(fd->name == ATOM_call && fd->arity > 8) )
670       { size_t arity = fd->arity;
671 	Word args = f->arguments;
672 	Word ap;
673 	size_t i;
674 
675 	av = PL_new_term_refs(arity);
676 	ap = valTermRef(av);
677 
678 	for(i=0; i<arity; i++, ap++)
679 	  *ap = linkVal(&args[i]);
680 	functor = f->definition;
681       } else
682 	return call1(mdef, goal PASS_LD);
683     } else
684     { return PL_type_error("callable", goal);
685     }
686 
687     proc = resolveProcedure(functor, module);
688     qid = PL_open_query(module, PL_Q_PASS_EXCEPTION, proc, av);
689     rval = PL_next_solution(qid);
690     PL_cut_query(qid);
691 
692     return rval;
693   } else
694     return FALSE;				/* exception in env */
695 }
696 
697 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
698 frameFinished() is used for two reasons:   providing hooks for the (GUI)
699 debugger  for  updating   the   stack-view    and   for   dealing   with
700 call_cleanup/3.  Both may call-back the Prolog engine.
701 
702 Note that the cleanup handler is called while protected against signals.
703 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
704 
705 static void
callCleanupHandler(LocalFrame fr,enum finished reason ARG_LD)706 callCleanupHandler(LocalFrame fr, enum finished reason ARG_LD)
707 { if ( false(fr, FR_CATCHED) )		/* from handler */
708   { size_t fref = consTermRef(fr);
709     fid_t cid;
710     term_t catcher;
711 
712     assert(fr->predicate == PROCEDURE_setup_call_catcher_cleanup4->definition);
713 
714     if ( !(cid=PL_open_foreign_frame()) )
715       return;				/* exception is in the environment */
716 
717     fr = (LocalFrame)valTermRef(fref);
718     catcher = consTermRef(argFrameP(fr, 2));
719 
720     set(fr, FR_CATCHED);
721     if ( unify_finished(catcher, reason) )
722     { term_t clean;
723       wakeup_state wstate;
724 
725       fr = (LocalFrame)valTermRef(fref);
726       clean = consTermRef(argFrameP(fr, 3));
727       if ( saveWakeup(&wstate, FALSE PASS_LD) )
728       { static predicate_t PRED_call1 = NULL;
729 	int rval;
730 
731 	if ( !PRED_call1 )
732 	  PRED_call1 = PL_predicate("call", 1, "system");
733 
734 	startCritical;
735         rval = call_term(contextModule(fr), clean PASS_LD);
736 	if ( !endCritical )
737 	  rval = FALSE;
738 	if ( !rval && exception_term )
739 	  wstate.flags |= WAKEUP_KEEP_URGENT_EXCEPTION;
740 	restoreWakeup(&wstate PASS_LD);
741       }
742     }
743 
744     PL_close_foreign_frame(cid);
745   }
746 }
747 
748 
749 static int
frameFinished(LocalFrame fr,enum finished reason ARG_LD)750 frameFinished(LocalFrame fr, enum finished reason ARG_LD)
751 { if ( true(fr, FR_CLEANUP) )
752   { size_t fref = consTermRef(fr);
753     callCleanupHandler(fr, reason PASS_LD);
754     fr = (LocalFrame)valTermRef(fref);
755   }
756 
757   if ( true(fr, FR_DEBUG) )
758     return callEventHook(PLEV_FRAMEFINISHED, fr);
759 
760   return TRUE;
761 }
762 
763 
764 static int
mustBeCallable(term_t call ARG_LD)765 mustBeCallable(term_t call ARG_LD)
766 { Word p = valTermRef(call);
767   Word ap;
768 
769   deRef(p);
770   if ( isVar(*p) )
771   { instantiation_error:
772     return PL_error(NULL, 0, NULL, ERR_INSTANTIATION);
773   }
774   if ( !hasFunctor(*p, FUNCTOR_colon2) )
775   { type_error:
776     return PL_error(NULL, 0, NULL, ERR_TYPE, ATOM_callable, call);
777   }
778   ap = argTermP(*p, 0);
779   deRef(ap);
780   if ( isVar(*ap) )
781     goto instantiation_error;
782   if ( !isAtom(*ap) )
783     goto type_error;
784   ap = argTermP(*p, 1);
785   deRef(ap);
786   if ( isVar(*ap) )
787     goto instantiation_error;
788   if ( !(isTerm(*ap) || isTextAtom(*ap)) )
789     goto type_error;
790 
791   succeed;
792 }
793 
794 
795 		 /*******************************
796 		 *	   BREAKPOINTS		*
797 		 *******************************/
798 
799 typedef enum
800 { BRK_ERROR = 0,			/* Exception */
801   BRK_CONTINUE,				/* continue execution */
802   BRK_TRACE,				/* trace from here */
803   BRK_DEBUG,				/* debug from here */
804   BRK_CALL				/* Call returned term */
805 } break_action;
806 
807 #define SAVE_PTRS() \
808 	frameref = consTermRef(frame); \
809 	chref    = consTermRef(bfr); \
810 	pcref    = (onStack(local, PC) ? consTermRef(PC) : 0);
811 #define RESTORE_PTRS() \
812 	frame = (LocalFrame)valTermRef(frameref); \
813 	bfr   = (Choice)valTermRef(chref); \
814 	PC    = (pcref ? (Code)valTermRef(pcref) : PC);
815 
816 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
817 Unify a pointer into a new  global  term   (g)  with  a  pointer into an
818 environment as obtained from some instruction VAR argument. This assumes
819 we have allocated enough trail stack.
820 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
821 
822 static void
protect_var(Word v ARG_LD)823 protect_var(Word v ARG_LD)
824 { term_t t = PL_new_term_ref_noshift();
825 
826   if ( t )
827     *valTermRef(t) = makeRefL(v);
828   else
829     assert(0);		/* cannot happen due to MINFOREIGNSIZE */
830 }
831 
832 
833 static void
unify_gl(Word g,Word l,int has_firstvar ARG_LD)834 unify_gl(Word g, Word l, int has_firstvar ARG_LD)
835 { if ( has_firstvar )
836     protect_var(l PASS_LD);
837 
838   deRef(l);
839   if ( isVar(*l) )
840   { setVar(*g);
841     *l = makeRefG(g);
842     assert(tTop+1 < tMax);
843     LTrail(l);
844   } else if ( needsRef(*l) )
845   { *g = makeRef(l);
846   } else
847   { *g = *l;
848   }
849 }
850 
851 
852 static int
put_call_goal(term_t t,Procedure proc ARG_LD)853 put_call_goal(term_t t, Procedure proc ARG_LD)
854 { FunctorDef fd  = proc->definition->functor;
855 
856   if ( fd->arity > 0 )
857   { Word        gt = allocGlobal(fd->arity+1);
858     LocalFrame NFR = LD->query->next_environment;
859     Word ap        = argFrameP(NFR, 0);
860     Word gp	   = gt;
861     int i;
862 
863     if ( !gt )
864       return FALSE;			/* could not allocate */
865 
866     DEBUG(MSG_TRACE,
867 	  Sdprintf("Copy %d call args from %p\n", fd->arity, ap));
868 
869     *gp++ = fd->functor;
870     for(i=0; i<fd->arity; i++)
871       unify_gl(gp++, ap++, FALSE PASS_LD);
872     *valTermRef(t) = consPtr(gt, STG_GLOBAL|TAG_COMPOUND);
873   } else
874   { *valTermRef(t) = fd->name;
875   }
876 
877   return TRUE;
878 }
879 
880 
881 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
882 put_vm_call() creates a description of  the   instruction  to  which the
883 break applied.
884 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
885 
886 static int
put_vm_call(term_t t,term_t frref,Code PC,code op,int has_firstvar,int * pop ARG_LD)887 put_vm_call(term_t t, term_t frref, Code PC, code op, int has_firstvar,
888 	    int *pop ARG_LD)
889 { atom_t simple_goal;
890   functor_t ftor;
891   int clean;
892 
893   switch(op)
894   { case I_CALL:			/* procedure */
895     case I_DEPART:
896     { return ( put_call_goal(t, (Procedure) PC[1] PASS_LD) &&
897 	       PL_cons_functor_v(t, FUNCTOR_call1, t) );
898     }
899     case I_CALLM:			/* module, procedure */
900     case I_DEPARTM:
901     { Module m = (Module)PC[1];
902       term_t av;
903 
904       return ( (av = PL_new_term_refs(2)) &&
905 	       PL_put_atom(av+0, m->name) &&
906 	       put_call_goal(av+1, (Procedure) PC[2] PASS_LD) &&
907 	       PL_cons_functor_v(t, FUNCTOR_colon2, av) &&
908 	       PL_cons_functor_v(t, FUNCTOR_call1, t) );
909     }
910     case I_CALLATM:			/* procm, contextm, proc */
911     case I_DEPARTATM:			/* call(@(procm:g, contextm)) */
912     { Module procm    = (Module)PC[1];
913       Module contextm = (Module)PC[2];
914       term_t av;
915 
916       return ( (av = PL_new_term_refs(2)) &&
917 	       PL_put_atom(av+0, procm->name) &&
918 	       put_call_goal(av+1, (Procedure) PC[3] PASS_LD) &&
919 	       PL_cons_functor_v(av+0, FUNCTOR_colon2, av) &&
920 	       PL_put_atom(av+1, contextm->name) &&
921 	       PL_cons_functor_v(t, FUNCTOR_xpceref2, av) &&
922 	       PL_cons_functor_v(t, FUNCTOR_call1, t) );
923     }
924     case I_CALLATMV:			/* procm, contextm, proc */
925     case I_DEPARTATMV:			/* call(@(procm:g, contextm)) */
926     { Module procm    = (Module)PC[1];
927       LocalFrame   fr = (LocalFrame)valTermRef(frref);
928       term_t      cmv = consTermRef(varFrameP(fr, (int)PC[2]));
929       term_t av;
930 
931       return ( (av = PL_new_term_refs(2)) &&
932 	       PL_put_atom(av+0, procm->name) &&
933 	       put_call_goal(av+1, (Procedure) PC[3] PASS_LD) &&
934 	       PL_cons_functor_v(av+0, FUNCTOR_colon2, av) &&
935 	       PL_put_term(av+1, cmv) &&
936 	       PL_cons_functor_v(t, FUNCTOR_xpceref2, av) &&
937 	       PL_cons_functor_v(t, FUNCTOR_call1, t) );
938     }
939     case I_USERCALL0:
940     { LocalFrame NFR = LD->query->next_environment;
941       term_t       g = consTermRef(argFrameP(NFR, 0));
942 
943       return PL_cons_functor_v(t, FUNCTOR_call1, g);
944     }
945     case I_USERCALLN:			/* call(call(G, ...)) */
946     { int      extra = (int)PC[1];
947       functor_t   cf = PL_new_functor(ATOM_call, 1+extra);
948       LocalFrame NFR = LD->query->next_environment;
949       term_t       g = consTermRef(argFrameP(NFR, 0));
950 
951       return ( PL_cons_functor_v(t, cf, g) &&
952 	       PL_cons_functor_v(t, FUNCTOR_call1, t) );
953     }
954     case I_FAIL:	simple_goal = ATOM_fail; goto simple;
955     case I_TRUE:	simple_goal = ATOM_true; goto simple;
956     simple:
957     { Word gt = allocGlobal(2);
958 
959       gt[0] = FUNCTOR_call1;
960       gt[1] = simple_goal;
961       *valTermRef(t) = consPtr(gt, STG_GLOBAL|TAG_COMPOUND);
962 
963       return TRUE;
964     }
965     case B_EQ_VC:    clean = 0x0; ftor = FUNCTOR_strict_equal2;     goto vc_2;
966     case B_NEQ_VC:   clean = 0x0; ftor = FUNCTOR_not_strict_equal2; goto vc_2;
967     case B_UNIFY_VC: clean = 0x0; ftor = FUNCTOR_equals2;           goto vc_2;
968     case B_UNIFY_FC: clean = 0x1; ftor = FUNCTOR_equals2;           goto vc_2;
969     vc_2:
970     { Word gt       = allocGlobal(2+1+2);	/* call(f(V,C)) */
971       LocalFrame fr = (LocalFrame)valTermRef(frref);
972       Word       v1 = varFrameP(fr, (int)PC[1]);
973 
974       if ( !gt )
975 	return FALSE;
976 
977       if ( clean&0x1 ) setVar(*v1);
978 
979       gt[0] = ftor;
980       unify_gl(&gt[1], v1, has_firstvar PASS_LD);
981       gt[2] = (word)PC[2];
982       gt[3] = FUNCTOR_call1;
983       gt[4] = consPtr(gt, STG_GLOBAL|TAG_COMPOUND);
984       *valTermRef(t) = consPtr(&gt[3], STG_GLOBAL|TAG_COMPOUND);
985 
986       return TRUE;
987     }
988     case B_EQ_VV:    clean = 0x0; ftor = FUNCTOR_strict_equal2;     goto fa_2;
989     case B_NEQ_VV:   clean = 0x0; ftor = FUNCTOR_not_strict_equal2; goto fa_2;
990     case B_UNIFY_FF: clean = 0x3; ftor = FUNCTOR_equals2;	    goto fa_2;
991     case B_UNIFY_VF:
992     case B_UNIFY_FV: clean = 0x1; ftor = FUNCTOR_equals2;           goto fa_2;
993     case B_UNIFY_VV: clean = 0x0; ftor = FUNCTOR_equals2;           goto fa_2;
994     fa_2:
995     { Word gt       = allocGlobal(2+1+2);	/* call(A=B) */
996       LocalFrame fr = (LocalFrame)valTermRef(frref);
997       Word v1       = varFrameP(fr, (int)PC[1]);
998       Word v2       = varFrameP(fr, (int)PC[2]);
999 
1000       if ( !gt )
1001 	return FALSE;
1002 
1003       if ( clean&0x1 ) setVar(*v1);
1004       if ( clean&0x2 ) setVar(*v2);
1005 
1006       gt[0] = ftor;
1007       unify_gl(&gt[1], v1, has_firstvar PASS_LD);
1008       unify_gl(&gt[2], v2, has_firstvar PASS_LD);
1009       gt[3] = FUNCTOR_call1;
1010       gt[4] = consPtr(gt, STG_GLOBAL|TAG_COMPOUND);
1011       *valTermRef(t) = consPtr(&gt[3], STG_GLOBAL|TAG_COMPOUND);
1012 
1013       return TRUE;
1014     }
1015     case B_UNIFY_EXIT:
1016     { if ( debugstatus.debugging )
1017       { return ( put_call_goal(t, GD->procedures.equals2 PASS_LD) &&
1018 		 PL_cons_functor_v(t, FUNCTOR_call1, t) );
1019       } else
1020       { return PL_put_atom_chars(t, "unify_exit");
1021       }
1022     }
1023     case I_VAR:		ftor = FUNCTOR_var1;      goto fa_1;
1024     case I_NONVAR:	ftor = FUNCTOR_nonvar1;   goto fa_1;
1025     case I_INTEGER:	ftor = FUNCTOR_integer1;  goto fa_1;
1026     case I_FLOAT:	ftor = FUNCTOR_float1;    goto fa_1;
1027     case I_NUMBER:	ftor = FUNCTOR_number1;   goto fa_1;
1028     case I_ATOMIC:	ftor = FUNCTOR_atomic1;   goto fa_1;
1029     case I_ATOM:	ftor = FUNCTOR_atom1;     goto fa_1;
1030     case I_STRING:	ftor = FUNCTOR_string1;   goto fa_1;
1031     case I_COMPOUND:	ftor = FUNCTOR_compound1; goto fa_1;
1032     case I_CALLABLE:	ftor = FUNCTOR_callable1; goto fa_1;
1033     fa_1:
1034     { Word gt       = allocGlobal(1+1+2);	/* call(f(A)) */
1035       LocalFrame fr = (LocalFrame)valTermRef(frref);
1036       Word       v1 = varFrameP(fr, (int)PC[1]);
1037 
1038       if ( !gt )
1039 	return FALSE;
1040 
1041       gt[0] = ftor;
1042       unify_gl(&gt[1], v1, has_firstvar PASS_LD);
1043       gt[2] = FUNCTOR_call1;
1044       gt[3] = consPtr(gt, STG_GLOBAL|TAG_COMPOUND);
1045       *valTermRef(t) = consPtr(&gt[2], STG_GLOBAL|TAG_COMPOUND);
1046 
1047       return TRUE;
1048     }
1049     case A_LT:	ftor = FUNCTOR_smaller2;       goto ar_2;
1050     case A_LE:	ftor = FUNCTOR_smaller_equal2; goto ar_2;
1051     case A_GT:	ftor = FUNCTOR_larger2;        goto ar_2;
1052     case A_GE:	ftor = FUNCTOR_larger_equal2;  goto ar_2;
1053     case A_EQ:	ftor = FUNCTOR_ar_equals2;     goto ar_2;
1054     case A_NE:	ftor = FUNCTOR_ar_not_equal2;  goto ar_2;
1055     ar_2:
1056     { Number n1, n2;
1057       term_t av;
1058       int rc;
1059 
1060       n1 = argvArithStack(2 PASS_LD);
1061       n2 = n1+1;
1062 
1063       rc = ((av = PL_new_term_refs(2)) &&
1064 	    PL_put_number(av+0, n1) &&
1065 	    PL_put_number(av+1, n2) &&
1066 	    PL_cons_functor_v(t, ftor, av) &&
1067 	    PL_cons_functor_v(t, FUNCTOR_call1, t));
1068 
1069       *pop = 2;
1070       return rc;
1071     }
1072     case A_IS:
1073     { Number     val = argvArithStack(1 PASS_LD);
1074       LocalFrame NFR = LD->query->next_environment;
1075       term_t       r = consTermRef(argFrameP(NFR, 0));
1076       term_t av;
1077       int rc;
1078 
1079       rc = ((av = PL_new_term_refs(2)) &&
1080 	    PL_put_term(av+0, r) &&
1081 	    PL_put_number(av+1, val) &&
1082 	    PL_cons_functor_v(t, FUNCTOR_is2, av) &&
1083 	    PL_cons_functor_v(t, FUNCTOR_call1, t));
1084 
1085       *pop = 1;
1086       return rc;
1087     }
1088     case A_FIRSTVAR_IS:			/* call(A is B) */
1089     { Number     val = argvArithStack(1 PASS_LD);
1090       LocalFrame  fr = (LocalFrame)valTermRef(frref);
1091       Word A         = varFrameP(fr, (int)PC[1]);
1092       term_t       r = consTermRef(A);
1093       term_t av;
1094       int rc;
1095 
1096       setVar(*A);
1097       rc = ((av = PL_new_term_refs(2)) &&
1098 	    PL_put_term(av+0, r) &&
1099 	    PL_put_number(av+1, val) &&
1100 	    PL_cons_functor_v(t, FUNCTOR_is2, av) &&
1101 	    PL_cons_functor_v(t, FUNCTOR_call1, t));
1102 
1103       *pop = 1;
1104       return rc;
1105     }
1106     case A_ADD_FC:
1107     { Word gt       = allocGlobal(2+1+2+1+2);	/* call(A is B+Int) */
1108       LocalFrame fr = (LocalFrame)valTermRef(frref);
1109       Word A        = varFrameP(fr, (int)PC[1]);
1110       Word B        = varFrameP(fr, (int)PC[2]);
1111       intptr_t add  = (intptr_t)PC[3];
1112 
1113       if ( !gt )
1114 	return FALSE;
1115 
1116       setVar(*A);
1117       gt[0] = FUNCTOR_plus2;
1118       unify_gl(&gt[1], B, has_firstvar PASS_LD);
1119       gt[2] = consInt(add);
1120       gt[3] = FUNCTOR_is2;
1121       unify_gl(&gt[4], A, has_firstvar PASS_LD);
1122       gt[5] = consPtr(&gt[0], STG_GLOBAL|TAG_COMPOUND);
1123       gt[6] = FUNCTOR_call1;
1124       gt[7] = consPtr(&gt[3], STG_GLOBAL|TAG_COMPOUND);
1125       *valTermRef(t) = consPtr(&gt[6], STG_GLOBAL|TAG_COMPOUND);
1126 
1127       return TRUE;
1128     }
1129     case I_CUT:
1130       return PL_put_atom(t, ATOM_cut);
1131     case I_ENTER:
1132       return PL_put_atom(t, ATOM_prove);
1133     case I_EXIT:
1134       return PL_put_atom(t, ATOM_exit);
1135     default:
1136       assert(0);
1137       return PL_put_atom_chars(t, codeTable[op].name);
1138   }
1139 }
1140 
1141 
1142 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1143 callBreakHook() calls prolog:break_hook/6 as
1144 
1145     prolog:break_hook(+Clause, +PC, +Frame, +Choice, +Goal, -Action) is semidet.
1146 
1147 (*) If put_vm_call() addresses  `F`  (first   var)  variables,  it  will
1148 initialise these to bind to the  goal.   However,  if GC comes along, it
1149 will reset these variables.  Therefore,  we   fake  GC  that  we already
1150 executed this instruction. The price is   that  V (normal var) arguments
1151 are not marked as used, and GC migh   thus  clean them. We fix that with
1152 protect_var(), which creates a  term-reference   to  the local variable,
1153 such that it is marked from the foreign environment.
1154 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1155 
1156 static break_action
callBreakHook(LocalFrame frame,Choice bfr,Code PC,code op,int * pop ARG_LD)1157 callBreakHook(LocalFrame frame, Choice bfr,
1158 	      Code PC, code op, int *pop ARG_LD)
1159 { predicate_t proc;
1160   fid_t cid;
1161   term_t frameref, chref, pcref;
1162   wakeup_state wstate;
1163   size_t pc_offset;
1164 
1165   *pop = 0;
1166   if (op == B_UNIFY_VAR || op == B_UNIFY_FIRSTVAR)
1167   { LD->slow_unify = TRUE;
1168     goto default_action;
1169   }
1170   proc = _PL_predicate("break_hook", 6, "prolog",
1171 		       &GD->procedures.prolog_break_hook6);
1172   if ( !getProcDefinition(proc)->impl.any.defined )
1173     goto default_action;
1174 
1175   if ( strchr(codeTable[op].argtype, CA1_FVAR) )
1176     pc_offset = stepPC(PC)-PC;
1177   else
1178     pc_offset = 0;
1179 
1180   SAVE_PTRS();
1181 
1182   /* make enough space to avoid GC/shift in the	critical region*/
1183   if ( !hasGlobalSpace(10) )
1184   { int rc;
1185 
1186     if ( (rc=ensureGlobalSpace(10, ALLOW_GC)) != TRUE )
1187     { raiseStackOverflow(rc);
1188       return BRK_ERROR;
1189     }
1190   }
1191 
1192   if ( saveWakeup(&wstate, FALSE PASS_LD) )
1193   { if ( (cid=PL_open_foreign_frame()) )
1194     { term_t argv = PL_new_term_refs(6);
1195       Clause clause = frame->clause->value.clause;
1196       qid_t qid;
1197 
1198       RESTORE_PTRS();
1199       PL_put_clref(argv+0, clause);
1200       PL_put_intptr(argv+1, PC - clause->codes);
1201       PL_put_frame(argv+2, frame);
1202       PL_put_choice(argv+3, bfr);
1203       if ( ( op == B_UNIFY_EXIT &&
1204              put_call_goal(argv+4, GD->procedures.equals2 PASS_LD) &&
1205              PL_cons_functor_v(argv+4, FUNCTOR_call1, argv+4) ) ||
1206            put_vm_call(argv+4, frameref, PC, op, pc_offset != 0, pop PASS_LD) )
1207       { DEBUG(CHK_SECURE, checkStacks(NULL));
1208 	if ( (qid = PL_open_query(MODULE_user,
1209 				  PL_Q_NODEBUG|PL_Q_PASS_EXCEPTION, proc, argv)) )
1210 	{ int rc;
1211 
1212 	  LD->query->parent->registers.pc += pc_offset; /* see (*) */
1213 	  rc = PL_next_solution(qid);
1214 	  LD->query->parent->registers.pc -= pc_offset;
1215 	  PL_cut_query(qid);
1216 
1217 	  if ( rc )
1218 	  { atom_t a_action;
1219 	    break_action action;
1220 
1221 	    if ( PL_get_atom(argv+5, &a_action) )
1222 	    { if ( a_action == ATOM_continue )
1223 	      { action = BRK_CONTINUE;
1224 	      } else if ( a_action == ATOM_trace )
1225 	      { action = BRK_TRACE;
1226 	      } else if ( a_action == ATOM_debug )
1227 	      { action = BRK_DEBUG;
1228 	      } else
1229 		goto invalid_action;
1230 
1231 	      PL_close_foreign_frame(cid);
1232 	      restoreWakeup(&wstate PASS_LD);
1233 
1234 	      return action;
1235 	    } else if ( PL_is_functor(argv+5, FUNCTOR_call1) )
1236 	    { LocalFrame NFR = LD->query->next_environment;
1237 	      Word p = valTermRef(argv+5);
1238 
1239 	      deRef(p);
1240 	      assert(hasFunctor(p[0], FUNCTOR_call1));
1241 	      p = argTermP(*p, 0);
1242 	      deRef(p);
1243 	      argFrame(NFR, 0) = *p;
1244 
1245 	      PL_close_foreign_frame(cid);
1246 	      restoreWakeup(&wstate PASS_LD);
1247 
1248 	      return BRK_CALL;
1249 	    } else
1250 	    { invalid_action:
1251 	      PL_warning("prolog:break_hook/6: invalid action");
1252 	    }
1253 	  }
1254 	}
1255       }
1256 
1257       PL_discard_foreign_frame(cid);
1258     }
1259     restoreWakeup(&wstate PASS_LD);
1260   }
1261 
1262 default_action:
1263   if ( exception_term )
1264     return BRK_ERROR;
1265   if ( debugstatus.debugging )
1266     return BRK_TRACE;
1267 
1268   return BRK_CONTINUE;
1269 }
1270 
1271 #undef SAVE_PTRS
1272 #undef RESTORE_PTRS
1273 
1274 
1275 		 /*******************************
1276 		 *    DESTRUCTIVE ASSIGNMENT	*
1277 		 *******************************/
1278 
1279 #ifdef O_DESTRUCTIVE_ASSIGNMENT
1280 
1281 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1282 Trailing of destructive assignments. This feature   is  used by setarg/3
1283 and put_attr/2.
1284 
1285 Such an assignment is trailed by first  pushing the assigned address (as
1286 normal) and then pushing a marked pointer to  a cell on the global stack
1287 holding the old (overwritten) value.
1288 
1289 Undo is slightly more complicated as it has to check for these special
1290 cells on the trailstack.
1291 
1292 The garbage collector has to take care in  a number of places: it has to
1293 pass through the trail-stack, marking   the  global-stack references for
1294 assigned data and the sweep_trail() must be   careful about this type of
1295 marks.
1296 
1297 Typically, this is only called to modify   values in terms in the global
1298 stack. Unfortunately it is also called  for   the  head  and tail of the
1299 wakeup list which is allocated on the local stack.
1300 
1301 TBD: allocate the head and tail of the  wakeup list on the global stack.
1302 Possibly this should also hold for the other `special term references'.
1303 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1304 
1305 void
TrailAssignment__LD(Word p ARG_LD)1306 TrailAssignment__LD(Word p ARG_LD)
1307 { assert(gTop+1 <= gMax && tTop+2 <= tMax);
1308   assert(!(*p & (MARK_MASK|FIRST_MASK)));
1309 
1310   if ( p < LD->mark_bar || p >= (Word)lBase )
1311   { Word old = gTop;
1312 
1313     gTop++;
1314     *old = *p;				/* save the old value on the global */
1315     (tTop++)->address = p;
1316     (tTop++)->address = tagTrailPtr(old);
1317   }
1318 }
1319 
1320 
1321 #ifdef O_ATTVAR
1322 static void
reclaim_attvars(Word after ARG_LD)1323 reclaim_attvars(Word after ARG_LD)
1324 { while ( LD->attvar.attvars >= after )
1325   { word w = *LD->attvar.attvars;
1326 
1327     if ( isVar(w) )
1328       LD->attvar.attvars = NULL;
1329     else
1330       LD->attvar.attvars = unRef(w);
1331   }
1332 }
1333 #endif
1334 
1335 
1336 static inline void
__do_undo(mark * m ARG_LD)1337 __do_undo(mark *m ARG_LD)
1338 { TrailEntry tt = tTop;
1339   TrailEntry mt = m->trailtop;
1340 
1341   while(--tt >= mt)
1342   { Word p = tt->address;
1343 
1344     if ( isTrailVal(p) )
1345     { DEBUG(2, Sdprintf("Undoing a trailed assignment\n"));
1346       tt--;
1347       *tt->address = trailVal(p);
1348       DEBUG(CHK_SECURE,
1349 	    if ( isAttVar(*tt->address) )
1350 	      assert(on_attvar_chain(tt->address)));
1351       assert(!(*tt->address & (MARK_MASK|FIRST_MASK)));
1352     } else
1353       setVar(*p);
1354   }
1355 
1356   tTop = mt;
1357   if ( LD->frozen_bar > m->globaltop )
1358   { DEBUG(CHK_SECURE, assert(gTop >= LD->frozen_bar));
1359     reclaim_attvars(LD->frozen_bar PASS_LD);
1360     gTop = LD->frozen_bar;
1361   } else
1362   { reclaim_attvars(m->globaltop PASS_LD);
1363     gTop = m->globaltop;
1364   }
1365 }
1366 
1367 
1368 void
do_undo(mark * m)1369 do_undo(mark *m)
1370 { GET_LD
1371   __do_undo(m PASS_LD);
1372 }
1373 
1374 #undef Undo
1375 #define Undo(m) __do_undo(&m PASS_LD)
1376 #endif /*O_DESTRUCTIVE_ASSIGNMENT*/
1377 
1378 
1379 		 /*******************************
1380 		 *	    PROCEDURES		*
1381 		 *******************************/
1382 
1383 /* Note that we use PL_malloc_uncollectable() here because the pointer in
1384    our block is not the real memory pointer.
1385 */
1386 
1387 #ifdef O_PLMT
1388 static Definition
localDefinition(Definition def ARG_LD)1389 localDefinition(Definition def ARG_LD)
1390 { unsigned int tid = LD->thread.info->pl_tid;
1391   size_t idx = MSB(tid);
1392   LocalDefinitions v = def->impl.local.local;
1393 
1394   if ( !v->blocks[idx] )
1395   { size_t bs = (size_t)1<<idx;
1396     Definition *newblock;
1397 
1398     if ( !(newblock=PL_malloc_uncollectable(bs*sizeof(Definition))) )
1399       outOfCore();
1400 
1401     memset(newblock, 0, bs*sizeof(Definition));
1402     if ( !COMPARE_AND_SWAP_PTR(&v->blocks[idx], NULL, newblock-bs) )
1403       PL_free(newblock);
1404   }
1405 
1406   if ( !v->blocks[idx][tid] )
1407     v->blocks[idx][tid] = localiseDefinition(def);
1408 
1409   return v->blocks[idx][tid];
1410 }
1411 
1412 void
destroyLocalDefinition(Definition def,unsigned int tid)1413 destroyLocalDefinition(Definition def, unsigned int tid)
1414 { size_t idx = MSB(tid);
1415   LocalDefinitions v = def->impl.local.local;
1416   Definition local;
1417 
1418   local = v->blocks[idx][tid];
1419   v->blocks[idx][tid] = NULL;
1420   destroyDefinition(local);
1421 }
1422 #endif
1423 
1424 Definition
getProcDefinition__LD(Definition def ARG_LD)1425 getProcDefinition__LD(Definition def ARG_LD)
1426 {
1427 #ifdef O_PLMT
1428   if ( true(def, P_THREAD_LOCAL) )
1429   { MEMORY_ACQUIRE();
1430     return localDefinition(def PASS_LD);
1431   }
1432 #endif
1433 
1434   return def;
1435 }
1436 
1437 
1438 Definition
getProcDefinitionForThread(Definition def,unsigned int tid)1439 getProcDefinitionForThread(Definition def, unsigned int tid)
1440 { size_t idx = MSB(tid);
1441   LocalDefinitions v = def->impl.local.local;
1442 
1443   if ( !v->blocks[idx] )
1444     return NULL;
1445 
1446   return v->blocks[idx][tid];
1447 }
1448 
1449 
1450 static inline Definition
getProcDefinedDefinition(Definition def ARG_LD)1451 getProcDefinedDefinition(Definition def ARG_LD)
1452 { if ( !def->impl.any.defined && false(def, PROC_DEFINED) )
1453     def = trapUndefined(def PASS_LD);
1454 
1455 #ifdef O_PLMT
1456   if ( true(def, P_THREAD_LOCAL) )
1457     return getProcDefinition__LD(def PASS_LD);
1458 #endif
1459 
1460   return def;
1461 }
1462 
1463 
1464 Module
contextModule(LocalFrame fr)1465 contextModule(LocalFrame fr)
1466 { for(; fr; fr = fr->parent)
1467   { if ( true(fr, FR_CONTEXT) )
1468       return fr->context;
1469     if ( false(fr->predicate, P_TRANSPARENT) )
1470       return fr->predicate->module;
1471   }
1472 
1473   return MODULE_user;
1474 }
1475 
1476 
1477 static inline void
setContextModule__(LocalFrame fr,Module context)1478 setContextModule__(LocalFrame fr, Module context)
1479 { fr->context = context;
1480   set(fr, FR_CONTEXT);
1481 }
1482 
1483 
1484 void
setContextModule(LocalFrame fr,Module context)1485 setContextModule(LocalFrame fr, Module context)
1486 { setContextModule__(fr, context);
1487 }
1488 #define setContextModule(fr, ctx) setContextModule__(fr, ctx)
1489 
1490 
1491 /* Earlier versions tested for <atom>:X, but this makes it very hard
1492    to write predicates such as current_resource/3. This is also
1493    compatible to at least SICStus and YAP.
1494 */
1495 
1496 static inline int
is_qualified(Word p ARG_LD)1497 is_qualified(Word p ARG_LD)
1498 { return hasFunctor(*p, FUNCTOR_colon2);
1499 }
1500 
1501 
1502 static int
m_qualify_argument(LocalFrame fr,int arg ARG_LD)1503 m_qualify_argument(LocalFrame fr, int arg ARG_LD)
1504 { Word k = varFrameP(fr, arg);
1505   Word p;
1506 
1507   deRef2(k, p);
1508   if ( !is_qualified(p PASS_LD) )
1509   { Word p2;
1510 
1511     if ( !hasGlobalSpace(3) )
1512     { int rc;
1513       term_t fref = consTermRef(fr);
1514 
1515       lTop = (LocalFrame)argFrameP(fr, fr->predicate->functor->arity);
1516       if ( (rc=ensureGlobalSpace(3, ALLOW_GC)) != TRUE )
1517 	return rc;
1518 
1519       fr = (LocalFrame)valTermRef(fref);
1520       k = varFrameP(fr, arg);
1521       deRef2(k, p);
1522     }
1523 
1524     p2 = gTop;
1525     gTop += 3;
1526     p2[0] = FUNCTOR_colon2;
1527     p2[1] = contextModule(fr)->name;
1528     if ( isVar(*p) && p > (Word)lBase )
1529     { setVar(p2[2]);
1530       LTrail(p);
1531       *p = makeRefG(&p2[2]);
1532     } else
1533     { p2[2] = (needsRef(*p) ? makeRef(p) : *p);
1534     }
1535     *k = consPtr(p2, STG_GLOBAL|TAG_COMPOUND);
1536   } else
1537   { int depth = 100;
1538 
1539     for(;;)
1540     { Word p2 = argTermP(*p, 1);
1541       Word ap;
1542 
1543       deRef2(p2, ap);
1544       if ( is_qualified(ap PASS_LD) )
1545       { Word a1 = argTermP(*p, 0);
1546 	deRef(a1);
1547 	if (! isAtom(*a1))
1548 	  break;
1549 	p = ap;
1550 	if ( --depth == 0 && !is_acyclic(p PASS_LD) )
1551 	{ term_t t = pushWordAsTermRef(p);
1552 	  PL_error(NULL, 0, NULL, ERR_TYPE, ATOM_acyclic_term, t);
1553 	  popTermRef();
1554 	  return FALSE;
1555 	}
1556       } else
1557       { break;
1558       }
1559     }
1560 
1561     *k = *p;
1562   }
1563 
1564   return TRUE;
1565 }
1566 
1567 
1568 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1569 Verify that p is  of  the   shape  @(Goal,Module)  that  is sufficiently
1570 instantiated to avoid teh compiler to generate a meta-call for it. Other
1571 errors will find their way to the user in other ways.
1572 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1573 
1574 static int
checkCallAtContextInstantiation(Word p ARG_LD)1575 checkCallAtContextInstantiation(Word p ARG_LD)
1576 { Word g, m;
1577   atom_t pm;
1578 
1579   deRef(p);
1580 
1581   m = argTermP(*p, 1);
1582   deRef(m);
1583   if ( canBind(*m) )
1584     return PL_error(NULL, 0, NULL, ERR_INSTANTIATION);
1585   g = argTermP(*p, 0);
1586   deRef(g);
1587   if ( !(g=stripModuleName(g, &pm PASS_LD)) )
1588     return FALSE;
1589   if ( hasFunctor(*g, FUNCTOR_colon2) )
1590   { m = argTermP(*g, 0);
1591     deRef(m);
1592     if ( canBind(*m) )
1593       return PL_error(NULL, 0, NULL, ERR_INSTANTIATION);
1594     g = argTermP(*g, 1);
1595     deRef(g);
1596   }
1597   if ( canBind(*g) )
1598     return PL_error(NULL, 0, NULL, ERR_INSTANTIATION);
1599 
1600   return TRUE;
1601 }
1602 
1603 
1604 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1605 foreignWakeup() calls delayed goals while executing a foreign procedure.
1606 Note that the  choicepoints  of  the   awoken  code  are  destroyed  and
1607 therefore this code can only be used in places introducing an (implicit)
1608 cut such as \=/2 (implemented as A \= B :- ( A = B -> fail ; true )).
1609 
1610 Can perform GC/shift and may leave overflow exceptions.
1611 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1612 
1613 bool
foreignWakeup(term_t ex ARG_LD)1614 foreignWakeup(term_t ex ARG_LD)
1615 { if ( unlikely(LD->alerted & ALERT_WAKEUP) )
1616   { LD->alerted &= ~ALERT_WAKEUP;
1617 
1618     if ( *valTermRef(LD->attvar.head) )
1619     { fid_t fid;
1620 
1621       if ( (fid=PL_open_foreign_frame()) )
1622       { term_t a0 = PL_new_term_ref();
1623 	int rval = FALSE;
1624 	qid_t qid;
1625 
1626 	PL_put_term(a0, LD->attvar.head);
1627 	if ( (qid = PL_open_query(NULL, PL_Q_CATCH_EXCEPTION, PROCEDURE_dwakeup1, a0)) )
1628 	{ setVar(*valTermRef(LD->attvar.head));
1629 	  setVar(*valTermRef(LD->attvar.tail));
1630 	  rval = PL_next_solution(qid);
1631 	  if ( rval == FALSE )
1632 	  { term_t t = PL_exception(qid);
1633 
1634 	    if ( t )
1635 	      PL_put_term(ex, t);
1636 	  }
1637 	  PL_cut_query(qid);
1638 	}
1639 
1640 	PL_close_foreign_frame(fid);
1641 
1642 	return rval;
1643       }
1644 
1645       PL_put_term(ex, exception_term);
1646       return FALSE;
1647     }
1648   }
1649 
1650   return TRUE;
1651 }
1652 
1653 
1654 		 /*******************************
1655 		 *	     EXCEPTIONS		*
1656 		 *******************************/
1657 
1658 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1659 Called at the end of handling an exception. We cannot do GC, however, we
1660 can request it, after it will be executed   at the start of the recovery
1661 handler. If no GC is needed the is enoush space so, we call trimStacks()
1662 to re-enable the spare stack-space if applicable.
1663 
1664 TBD: In these modern days we can  probably   do  GC. Still, if it is not
1665 needed why would we?
1666 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1667 
1668 static void
resumeAfterException(int clear,Stack outofstack)1669 resumeAfterException(int clear, Stack outofstack)
1670 { GET_LD
1671 
1672   if ( clear )
1673   { exception_term = 0;
1674     LD->exception.fr_rewritten = 0;
1675     setVar(*valTermRef(LD->exception.bin));
1676     setVar(*valTermRef(LD->exception.printed));
1677     setVar(*valTermRef(LD->exception.pending));
1678   }
1679 
1680   if ( outofstack && outofstack->gc )
1681   { LD->stacks.global.gced_size = 0;
1682     LD->stacks.trail.gced_size  = 0;
1683   }
1684 
1685   if ( !considerGarbageCollect((Stack)NULL) )
1686   { trimStacks((outofstack != NULL) PASS_LD);
1687   } else
1688   { trimStacks(FALSE PASS_LD);		/* just re-enable the spare stacks */
1689     if ( outofstack != NULL )
1690       LD->trim_stack_requested = TRUE;	/* next time with resize */
1691   }
1692 
1693   LD->exception.processing = FALSE;
1694   LD->outofstack = NULL;
1695 }
1696 
1697 
1698 static void
exceptionUnwindGC(void)1699 exceptionUnwindGC(void)
1700 { GET_LD
1701 
1702   LD->stacks.global.gced_size = 0;
1703   LD->stacks.trail.gced_size = 0;
1704   LD->trim_stack_requested = TRUE;
1705   if ( considerGarbageCollect(NULL) )
1706   { garbageCollect(GC_EXCEPTION);
1707     enableSpareStacks();
1708   }
1709 }
1710 
1711 
1712 		 /*******************************
1713 		 *   FOREIGN-LANGUAGE INTERFACE *
1714 		 *******************************/
1715 
1716 #include "pl-fli.c"
1717 
1718 #ifdef O_DEBUGGER
1719 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1720 findStartChoice(LocalFrame fr, Choice ch)
1721     Within the same query, find the choice-point that was created at the
1722     start of this frame.  This is used for the debugger at the fail-port
1723     as well as for realising retry.
1724 
1725     Note that older versions also considered the initial choicepoint a
1726     choicepoint for the initial frame, but this is not correct as the
1727     frame may be replaced due to last-call optimisation.
1728 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1729 
1730 static Choice
findStartChoice(LocalFrame fr,Choice ch)1731 findStartChoice(LocalFrame fr, Choice ch)
1732 { for( ; (void *)ch > (void *)fr; ch = ch->parent )
1733   { if ( ch->frame == fr )
1734     { switch ( ch->type )
1735       { case CHP_JUMP:
1736 	  continue;			/* might not be at start */
1737 	default:
1738 	  return ch;
1739       }
1740     }
1741   }
1742 
1743   return NULL;
1744 }
1745 
1746 
1747 static Choice
findChoiceBeforeFrame(LocalFrame fr,Choice ch)1748 findChoiceBeforeFrame(LocalFrame fr, Choice ch)
1749 { while ( (void*)ch > (void*)fr )
1750     ch = ch->parent;
1751 
1752   return ch;
1753 }
1754 
1755 #endif /*O_DEBUGGER*/
1756 
1757 
1758 #if O_CATCHTHROW
1759 		/********************************
1760 		*        EXCEPTION SUPPORT      *
1761 		*********************************/
1762 
1763 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1764 Find the I_EXIT of catch/3. We use this as the return address of catch/3
1765 when running the handler. Maybe we can remove the catch/3 in the future?
1766 This would also fix the problem that  we   need  to be sure not to catch
1767 exceptions from the handler.
1768 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1769 
1770 static Code
findCatchExit()1771 findCatchExit()
1772 { if ( !GD->exceptions.catch_exit_address )
1773   { Definition catch3 = PROCEDURE_catch3->definition;
1774     Clause cl = catch3->impl.clauses.first_clause->value.clause;
1775     Code Exit = &cl->codes[cl->code_size-1];
1776     assert(*Exit == encode(I_EXIT));
1777 
1778     GD->exceptions.catch_exit_address = Exit;
1779   }
1780 
1781   return GD->exceptions.catch_exit_address;
1782 }
1783 
1784 
1785 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1786 Find the frame running catch/3. If we found  it, we will mark this frame
1787 and not find it again, as a catcher   can  only catch once from the 1-st
1788 argument goal. Exceptions from the  recover   goal  should be passed (to
1789 avoid a loop and allow for re-throwing).   With  thanks from Gertjan van
1790 Noord.
1791 
1792 findCatcher() can do  GC/shift!  The  return   value  is  a  local-frame
1793 reference, so we can deal with relocation of the local stack.
1794 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1795 
1796 static term_t
findCatcher(LocalFrame fr,Choice ch,term_t ex ARG_LD)1797 findCatcher(LocalFrame fr, Choice ch, term_t ex ARG_LD)
1798 { Definition catch3  = PROCEDURE_catch3->definition;
1799 
1800   for(; fr; fr = fr->parent)
1801   { int rc;
1802     term_t tref, catcher;
1803 
1804     if ( fr->predicate != catch3 )
1805       continue;
1806     if ( true(fr, FR_CATCHED) )
1807       continue;				/* thrown from recover */
1808     if ( (void*)fr > (void*)ch )
1809       continue;				/* call-port of catch/3 */
1810 
1811     tref = consTermRef(fr);
1812     catcher = consTermRef(argFrameP(fr, 1));
1813     DEBUG(MSG_THROW, Sdprintf("Unify ball for frame %ld\n", (long)tref));
1814     rc = PL_unify(catcher, ex);
1815     fr = (LocalFrame)valTermRef(tref);
1816 
1817     if ( rc )
1818     { DEBUG(MSG_THROW, Sdprintf("Unified for frame %ld\n", (long)tref));
1819       set(fr, FR_CATCHED);
1820       return consTermRef(fr);
1821     }
1822   }
1823 
1824   return 0;
1825 }
1826 
1827 
1828 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1829 See whether some outer  environment  will   catch  this  exception. I.e.
1830 catch(Goal, ...), where Goal calls C, calls   Prolog  and then raises an
1831 exception somewhere.
1832 
1833 Note that when throwing from a catch/3,   the  catcher is subject to GC.
1834 Hence, we should not call can_unify() if  it has been garbage collected.
1835 Doing so generally does no harm as the unification will fail, but is not
1836 elegant and traps an assert() in do_unify().
1837 
1838 Returns:
1839 
1840   - term-reference to catch/3 frame
1841   - (term_t)0 if not caught
1842   - (term_t)-1 if caught in C
1843 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1844 
1845 #ifndef offset
1846 #define offset(s, f) ((size_t)(&((struct s *)NULL)->f))
1847 #endif
1848 
1849 #ifdef O_DEBUGGER
1850 static term_t
isCaughtInOuterQuery(qid_t qid,term_t ball ARG_LD)1851 isCaughtInOuterQuery(qid_t qid, term_t ball ARG_LD)
1852 { Definition catch3 = PROCEDURE_catch3->definition;
1853   QueryFrame qf = QueryFromQid(qid);
1854 
1855   while( qf && true(qf, PL_Q_PASS_EXCEPTION) )
1856   { LocalFrame fr = qf->saved_environment;
1857 
1858     if ( !fr )
1859       break;
1860 
1861     while( fr )
1862     { if ( fr->predicate == catch3 )
1863       { term_t fref  = consTermRef(fr);
1864 	Word catcher = argFrameP(fr, 1);
1865 
1866 	deRef(catcher);
1867 
1868 	if ( *catcher != ATOM_garbage_collected &&
1869 	     can_unify(catcher,		/* may shift */
1870 		       valTermRef(ball),
1871 		       0) )
1872 	  return fref;
1873 	fr = (LocalFrame)valTermRef(fref);
1874       }
1875 
1876       if ( fr->parent )
1877       { fr = fr->parent;
1878       } else
1879       { qf = queryOfFrame(fr);
1880 	break;
1881       }
1882     }
1883   }
1884 
1885   if ( qf && true(qf, PL_Q_CATCH_EXCEPTION|PL_Q_PASS_EXCEPTION) )
1886     return (term_t)-1;
1887 
1888   return 0;
1889 }
1890 
1891 
1892 static word
uncachableException(term_t t ARG_LD)1893 uncachableException(term_t t ARG_LD)
1894 { Word p = valTermRef(t);
1895 
1896   deRef(p);
1897   if ( *p == ATOM_aborted )
1898     return *p;
1899 
1900   return 0;
1901 }
1902 
1903 static word
resourceException(term_t t ARG_LD)1904 resourceException(term_t t ARG_LD)
1905 { Word p = valTermRef(t);
1906 
1907   deRef(p);
1908   if ( hasFunctor(*p, FUNCTOR_error2) )
1909   { p = argTermP(*p, 0);
1910     deRef(p);
1911     if ( hasFunctor(*p, FUNCTOR_resource_error1) )
1912     { p = argTermP(*p, 0);
1913       deRef(p);
1914       return *p;
1915     }
1916   }
1917 
1918   return 0;
1919 }
1920 
1921 
1922 
1923 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1924 dbgRedoFrame(LocalFrame fr)
1925 
1926 Find the frame we report  for  a  retry.   If  the  current  frame  is a
1927 debugable frame, we only debug if it is a user predicate.
1928 
1929 If the current frame  is  not  debuggable   we  have  a  choicepoint  in
1930 non-debug code. We walk up the stack to   find  a debug frame. If we are
1931 already in the box of this frame, we   have  an internal retry of called
1932 system predicate, which we should not trace. If we are outside the `box'
1933 however, we must trace the toplevel visible predicate.
1934 
1935 FR_INBOX is maintained when the debugger is   active.  It is set on CALL
1936 and REDO and reset on EXIT. So, if we find this set, we are dealing with
1937 an internal retry and otherwise we have an external retry.
1938 
1939 The cht argument is one of CHP_CLAUSE   or CHP_JUMP, indicating the type
1940 of redo.  We always show redo for an external redo.
1941 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1942 
1943 static LocalFrame
dbgRedoFrame(LocalFrame fr,choice_type cht ARG_LD)1944 dbgRedoFrame(LocalFrame fr, choice_type cht ARG_LD)
1945 { DEBUG(MSG_TRACE, Sdprintf("REDO on [%d] %s\n",
1946 			    (int)levelFrame(fr), predicateName(fr->predicate)));
1947 
1948   if ( SYSTEM_MODE )
1949     return fr;				/* system mode; debug everything */
1950   if ( isDebugFrame(fr) && false(fr->predicate, HIDE_CHILDS) )
1951     return fr;				/* normal user code */
1952   for( ; fr && fr->parent && true(fr->parent->predicate, HIDE_CHILDS);
1953        fr = fr->parent)
1954     ;					/* find top of hidden children */
1955   DEBUG(MSG_TRACE, if ( fr )
1956 	Sdprintf("REDO user frame of [%d] %s%s\n",
1957 		 (int)levelFrame(fr),
1958 		 predicateName(fr->predicate),
1959 		 true(fr, FR_INBOX) ? " (inbox)" : ""));
1960   if ( fr && false(fr, FR_INBOX) )
1961   { set(fr, FR_INBOX);			/* External retry */
1962     return fr;
1963   }
1964 
1965   return NULL;
1966 }
1967 
1968 #endif /*O_DEBUGGER*/
1969 
1970 static int
exception_hook(qid_t pqid,term_t fr,term_t catchfr_ref ARG_LD)1971 exception_hook(qid_t pqid, term_t fr, term_t catchfr_ref ARG_LD)
1972 { if ( PROCEDURE_exception_hook4->definition->impl.clauses.first_clause )
1973   { if ( !LD->exception.in_hook )
1974     { wakeup_state wstate;
1975       qid_t qid;
1976       term_t av, ex = 0;
1977       int debug, trace, rc;
1978 
1979       LD->exception.in_hook++;
1980       if ( !saveWakeup(&wstate, TRUE PASS_LD) )
1981 	return FALSE;
1982 
1983       av = PL_new_term_refs(4);
1984       PL_put_term(av+0, exception_bin);
1985       PL_put_frame(av+2, (LocalFrame)valTermRef(fr));
1986 
1987       if ( !catchfr_ref )
1988 	catchfr_ref = isCaughtInOuterQuery(pqid, exception_term PASS_LD);
1989       if ( catchfr_ref == (term_t)-1 )
1990       { PL_put_atom_chars(av+3, "C");
1991       } else if ( catchfr_ref && catchfr_ref == LD->exception.fr_rewritten )
1992       { DEBUG(MSG_THROW,
1993 	    Sdprintf("Already rewritting exception for frame %d\n",
1994 		     catchfr_ref));
1995 	rc = FALSE;
1996 	goto done;
1997       } else if ( catchfr_ref )
1998       { LocalFrame cfr = (LocalFrame)valTermRef(catchfr_ref);
1999 	cfr = parentFrame(cfr);
2000 	PL_put_frame(av+3, cfr);
2001 	LD->exception.fr_rewritten = catchfr_ref;
2002       } else
2003       { PL_put_frame(av+3, NULL);	/* puts 'none' */
2004       }
2005 
2006       qid = PL_open_query(MODULE_user, PL_Q_NODEBUG|PL_Q_CATCH_EXCEPTION,
2007 			  PROCEDURE_exception_hook4, av);
2008       rc = PL_next_solution(qid);
2009       debug = debugstatus.debugging;
2010       trace = debugstatus.tracing;
2011       if ( rc )				/* pass user setting trace/debug */
2012       { PL_cut_query(qid);
2013 	if ( debug ) debugstatus.debugging = TRUE;
2014 	if ( trace ) debugstatus.tracing = TRUE;
2015 	if ( !PL_is_variable(av+1) )
2016 	  ex = av+1;
2017       } else
2018       { ex = PL_exception(qid);
2019 	if ( ex )
2020 	{ PL_put_term(av+1, ex);
2021 	  ex = av+1;
2022 	}
2023 	PL_cut_query(qid);
2024       }
2025 
2026       if ( ex && !PL_same_term(ex, exception_term) )
2027       {	PL_raise_exception(ex);	/* copy term again */
2028 	wstate.flags |= WAKEUP_STATE_SKIP_EXCEPTION;
2029 	rc = TRUE;			/* handled */
2030       } else
2031       { rc = FALSE;
2032       }
2033 
2034     done:
2035       restoreWakeup(&wstate PASS_LD);
2036       LD->exception.in_hook--;
2037 
2038       return rc;
2039     } else
2040     { PL_warning("Recursive exception in prolog_exception_hook/4");
2041     }
2042   }
2043 
2044   return FALSE;
2045 }
2046 
2047 
2048 #endif /*O_CATCHTHROW*/
2049 
2050 
2051 		 /*******************************
2052 		 *	  TAIL-RECURSION	*
2053 		 *******************************/
2054 
2055 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2056 Tail recursion copy of the arguments of the new frame back into the  old
2057 one.   This  should  be  optimised  by the compiler someday, but for the
2058 moment this will do.
2059 
2060 The new arguments block can contain the following types:
2061   - Instantiated data (atoms, ints, reals, strings, terms
2062     These can just be copied.
2063   - Plain variables
2064     These can just be copied.
2065   - References to frames older than the `to' frame
2066     These can just be copied.
2067   - 1-deep references into the `to' frame.
2068     This is hard as there might be two of  them  pointing  to  the  same
2069     location  in  the  `to' frame, indicating sharing variables.  In the
2070     first pass we will fill the  variable  in  the  `to'  frame  with  a
2071     reference  to the new variable.  If we get another reference to this
2072     field we will copy the reference saved in the `to'  field.   Because
2073     on  entry  references into this frame are always 1 deep we KNOW this
2074     is a saved reference.  The critical program for this is:
2075 
2076 	a :- b(X, X).
2077 	b(X, Y) :- X == Y.
2078 	b(X, Y) :- write(bug), nl.
2079 
2080 					This one costed me 1/10 bottle of
2081 					brandy to Huub Knops, SWI
2082 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2083 
2084 static void
copyFrameArguments(LocalFrame from,LocalFrame to,size_t argc ARG_LD)2085 copyFrameArguments(LocalFrame from, LocalFrame to, size_t argc ARG_LD)
2086 { Word ARGD, ARGS, ARGE;
2087 
2088   if ( argc == 0 )
2089     return;
2090 
2091   ARGS = argFrameP(from, 0);
2092   ARGE = ARGS+argc;
2093   ARGD = argFrameP(to, 0);
2094   for( ; ARGS < ARGE; ARGS++, ARGD++) /* dereference the block */
2095   { word k = *ARGS;
2096 
2097     if ( isRefL(k) )
2098     { Word p = unRefL(k);
2099 
2100       if ( p > (Word)to )
2101       { if ( isVar(*p) )
2102 	{ *p = makeRefL(ARGD);
2103 	  setVar(*ARGS);
2104 	} else
2105 	  *ARGS = *p;
2106       }
2107     }
2108   }
2109   ARGS = argFrameP(from, 0);
2110   ARGD = argFrameP(to, 0);
2111   while( ARGS < ARGE )			/* now copy them */
2112     *ARGD++ = *ARGS++;
2113 }
2114 
2115 		/********************************
2116 		*          INTERPRETER          *
2117 		*********************************/
2118 
2119 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2120 			 MACHINE REGISTERS
2121 
2122   - DEF
2123     Definition structure of current procedure.
2124   - PC
2125     Virtual machine `program counter': pointer to the next byte code  to
2126     interpret.
2127   - ARGP
2128     Argument pointer.  Pointer to the next argument to be matched  (when
2129     in the clause head) or next argument to be instantiated (when in the
2130     clause  body).   Saved  and  restored  via  the  argument  stack for
2131     functors.
2132   - FR
2133     Current environment frame
2134   - BFR
2135     Frame where execution should continue if  the  current  goal  fails.
2136     Used by I_CALL and deviates to fill the backtrackFrame slot of a new
2137     frame and set by various instructions.
2138   - deterministic
2139     Last clause has been found deterministically
2140 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2141 
2142 #ifdef O_DEBUG_BACKTRACK
2143 int backtrack_from_line;
2144 choice_type last_choice;
2145 #define GO(label) do { backtrack_from_line = __LINE__; goto label; } while(0)
2146 #else
2147 #define GO(label) goto label
2148 #endif
2149 
2150 #define FRAME_FAILED		GO(frame_failed)
2151 #define CLAUSE_FAILED		GO(clause_failed)
2152 #define BODY_FAILED		GO(body_failed)
2153 #ifdef O_DEBUG
2154 #define THROW_EXCEPTION		do { throwed_from_line = __LINE__; \
2155 				     goto b_throw; } while(0)
2156 #else
2157 #define THROW_EXCEPTION		goto b_throw
2158 #endif
2159 
2160 #ifdef O_PROFILE
2161 #define Profile(g) if ( unlikely(LD->profile.active) ) g
2162 #else
2163 #define Profile(g) (void)0
2164 #endif
2165 
2166 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2167 {leave,discard}Frame()
2168      Exit from a frame.  leaveFrame() is used for normal leaving due to
2169      failure.  discardFrame() is used for frames that have
2170      been cut.  If such frames are running a foreign predicate, the
2171      functions should be called again using FRG_CUTTED context.
2172 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2173 
2174 static void
leaveFrame(LocalFrame fr)2175 leaveFrame(LocalFrame fr)
2176 { //Definition def = fr->predicate;
2177 
2178   fr->clause = NULL;
2179   leaveDefinition(def);
2180 }
2181 
2182 
2183 static void
discardFrame(LocalFrame fr ARG_LD)2184 discardFrame(LocalFrame fr ARG_LD)
2185 { Definition def = fr->predicate;
2186 
2187   DEBUG(2, Sdprintf("discard #%d running %s\n",
2188 		    loffset(fr),
2189 		    predicateName(fr->predicate)));
2190 
2191   if ( true(def, P_FOREIGN) )
2192   { if ( fr->clause )
2193     { discardForeignFrame(fr PASS_LD);
2194       fr->clause = NULL;
2195     }
2196   } else
2197   { fr->clause = NULL;	/* leaveDefinition() may destroy clauses (no more) */
2198     leaveDefinition(def);
2199   }
2200 }
2201 
2202 
2203 /* TRUE if fr is in the continuation of frame or the frame of ch or one
2204  * of its parents.
2205  */
2206 static int
in_continuation(LocalFrame fr,LocalFrame frame,Choice ch)2207 in_continuation(LocalFrame fr, LocalFrame frame, Choice ch)
2208 { for(;;)
2209   { while(frame > fr)
2210       frame = frame->parent;
2211     if ( frame == fr )
2212       return TRUE;
2213 
2214     if ( (void*)ch > (void*)fr )
2215     { frame = ch->frame;
2216       ch = ch->parent;
2217     } else
2218     { return FALSE;
2219     }
2220   }
2221 }
2222 
2223 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2224 Discard all choice-points created after  the   creation  of the argument
2225 environment. See also discardFrame().
2226 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2227 
2228 #if defined(O_DEBUG) || defined(SECURE_GC) || defined(O_MAINTENANCE)
2229 char *
chp_chars(Choice ch)2230 chp_chars(Choice ch)
2231 { GET_LD
2232   static char buf[256];
2233 
2234   Ssnprintf(buf, sizeof(buf), "Choice at #%ld for frame #%ld (%s), type %s",
2235 	    loffset(ch), loffset(ch->frame),
2236 	    predicateName(ch->frame->predicate),
2237 	    ch->type == CHP_JUMP ? "JUMP" :
2238 	    ch->type == CHP_CLAUSE ? "CLAUSE" :
2239 	    ch->type == CHP_TOP ? "TOP" :
2240 	    ch->type == CHP_DEBUG ? "DEBUG" :
2241 	    ch->type == CHP_CATCH ? "CATCH" : "NONE");
2242 
2243   return buf;
2244 }
2245 #endif
2246 
2247 
2248 int
existingChoice(Choice ch ARG_LD)2249 existingChoice(Choice ch ARG_LD)
2250 { if ( onStack(local, ch) && onStack(local, ch->frame) &&
2251        (int)ch->type >= 0 && (int)ch->type <= CHP_DEBUG )
2252   { Choice ch2;
2253 
2254     for(ch2 = BFR; ch2 > ch; ch2 = ch2->parent)
2255       ;
2256     if ( ch2 == ch )
2257       return TRUE;
2258   }
2259 
2260   return FALSE;
2261 }
2262 
2263 
2264 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2265 discardChoicesAfter() discards all choicepoints created  after fr, while
2266 calling  possible  hooks  on   the   frames.    If   the   `reason`   is
2267 FINISH_EXTERNAL_EXCEPT_UNDO, Undo() is called  on   the  oldest  removed
2268 choicepoint (before it is  actually   removed).  Older versions returned
2269 this choicepoint, but as it is already removed, this is not safe.
2270 
2271 GC interaction is tricky here. See also   C_CUT.  The loop needs to call
2272 the  cleanup  handlers  and  call   discardFrame().  The  latter  resets
2273 LocalFrame->clause to NULL. This means  that   these  frames  may not be
2274 visible to GC. In older versions, this was achieved using two loops: one
2275 for the cleanup and one to discard the  frames, but if Undo() is called,
2276 the resets may corrupt the datastructures,   which makes the second loop
2277 fail. We now moved discardFrame() back into the primary loop and set BFR
2278 before calling frameFinished() such that the discarded frames are really
2279 invisible to GC.
2280 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2281 
2282 static void
discardChoicesAfter(LocalFrame fr,enum finished reason ARG_LD)2283 discardChoicesAfter(LocalFrame fr, enum finished reason ARG_LD)
2284 { if ( (LocalFrame)BFR > fr )
2285   { Choice me;
2286 
2287     for(me = BFR; ; me=me->parent)
2288     { LocalFrame fr2;
2289       LocalFrame delto;
2290       int me_undone = FALSE;
2291 
2292       if ( me->parent && me->parent->frame > fr )
2293 	delto = me->parent->frame;
2294       else
2295 	delto = fr;
2296 
2297       DEBUG(3, Sdprintf("Discarding %s\n", chp_chars(me)));
2298 
2299       for(fr2 = me->frame;
2300 	  fr2 > delto;
2301 	  fr2 = fr2->parent)
2302       { assert(onStack(local, me));
2303 	assert(onStack(local, fr2));
2304 	assert(fr2->clause || true(fr2->predicate, P_FOREIGN));
2305 
2306 	if ( true(fr2, FR_WATCHED) )
2307 	{ char *lSave = (char*)lBase;
2308 
2309 	  if ( !me_undone && is_exception_finish(reason) )
2310 	  { me_undone = TRUE;
2311 	    Undo(me->mark);
2312 	    DiscardMark(me->mark);
2313 	  }
2314 	  BFR = me;
2315 	  frameFinished(fr2, reason PASS_LD);
2316 	  BFR = BFR->parent;
2317 	  if ( lSave != (char*)lBase )	/* shifted */
2318 	  { intptr_t offset = (char*)lBase - lSave;
2319 
2320 	    me  = addPointer(me, offset);
2321 	    fr  = addPointer(fr, offset);
2322 	    fr2 = addPointer(fr2, offset);
2323 	    delto = addPointer(delto, offset);
2324 	  }
2325 #if 0					/* What to do if we have multiple */
2326 	  if ( exception_term )		/* handlers and multiple exceptions? */
2327 	    break;
2328 #endif
2329 	}
2330 
2331 	discardFrame(fr2 PASS_LD);
2332       }
2333 
2334       if ( (LocalFrame)me->parent <= fr )
2335       { if ( !me_undone )
2336 	{ if ( reason == FINISH_EXTERNAL_EXCEPT_UNDO )
2337 	    Undo(me->mark);
2338 	  DiscardMark(me->mark);
2339 	}
2340 	BFR = me->parent;
2341 	return;
2342       }
2343     }
2344   }
2345 }
2346 
2347 
2348 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2349 Discard choicepoints in debugging mode.  As we might be doing callbacks
2350 on behalf of the debugger we need to preserve the pending exception.
2351 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2352 
2353 static void
dbg_discardChoicesAfter(LocalFrame fr,enum finished reason ARG_LD)2354 dbg_discardChoicesAfter(LocalFrame fr, enum finished reason ARG_LD)
2355 { if ( exception_term )
2356   { Word p = valTermRef(exception_term);
2357     word w;
2358 
2359     DEBUG(3, Sdprintf("dbg_discardChoicesAfter(): saving exception: ");
2360 	     pl_writeln(exception_term));
2361     deRef(p);
2362     w = *p;
2363     assert(!isVar(w));
2364     PushVal(w);
2365     exception_term = 0;
2366     discardChoicesAfter(fr, reason PASS_LD);
2367     PopVal(w);
2368     *valTermRef(exception_bin) = w;
2369     exception_term = exception_bin;
2370   } else
2371   { discardChoicesAfter(fr, reason PASS_LD);
2372   }
2373 }
2374 
2375 
2376 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2377 newChoice(CH_*, FR) Creates a new  choicepoint.   After  creation of the
2378 choice-point, the user has to fill the choice-points mark as well as the
2379 required context value.
2380 
2381 Note that a frame has only one choicepoint associated, except for choice
2382 points created from C_OR. Therefore, C_OR  ensures there is space; space
2383 for the one other choicepoint is ensured of a local frame is created.
2384 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2385 
2386 static Choice
newChoice(choice_type type,LocalFrame fr ARG_LD)2387 newChoice(choice_type type, LocalFrame fr ARG_LD)
2388 { Choice ch = (Choice)lTop;
2389 
2390   DEBUG(0, assert(ch+1 <= (Choice)lMax));
2391   DEBUG(0, assert(BFR < ch));
2392   lTop = (LocalFrame)(ch+1);
2393 
2394   ch->type = type;
2395   ch->frame = fr;
2396   ch->parent = BFR;
2397   Mark(ch->mark);
2398 #ifdef O_PROFILE
2399   ch->prof_node = LD->profile.current;
2400 #endif
2401   BFR = ch;
2402   DEBUG(3, Sdprintf("NEW %s\n", chp_chars(ch)));
2403 
2404   return ch;
2405 }
2406 
2407 
2408 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2409 Op top of the query frame there are two   local frames. The top one is a
2410 dummy one, just enough to satisfy stack-walking   and GC. The first real
2411 one has a programPointer pointing to  I_EXITQUERY, doing the return from
2412 a query.
2413 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2414 
2415 #define NDEBUG_SAVE_FLAGS (PLFLAG_LASTCALL)
2416 
2417 qid_t
PL_open_query(Module ctx,int flags,Procedure proc,term_t args)2418 PL_open_query(Module ctx, int flags, Procedure proc, term_t args)
2419 { GET_LD
2420   QueryFrame qf;
2421   LocalFrame fr, top;
2422   Definition def;
2423   size_t arity;
2424   Word ap;
2425   size_t lneeded;
2426   static int top_initialized = FALSE;
2427   static struct clause clause;
2428   static struct clause_ref cref;
2429 
2430   if ( !top_initialized )
2431   { clause.predicate = PROCEDURE_dc_call_prolog->definition;
2432     clause.generation.erased = ~(gen_t)0;
2433     clause.code_size = 1;
2434     clause.codes[0] = encode(I_EXITQUERY);
2435     cref.value.clause = &clause;
2436 
2437     top_initialized = TRUE;
2438   }
2439 
2440   DEBUG(2, { FunctorDef f = proc->definition->functor;
2441 	     size_t n;
2442 
2443 	     Sdprintf("PL_open_query: %s(", stringAtom(f->name));
2444 	     for(n=0; n < f->arity; n++)
2445 	     { if ( n > 0 )
2446 		 Sdprintf(", ");
2447 	       PL_write_term(Serror, args+n, 999, 0);
2448 	     }
2449 	     Sdprintf(")\n");
2450 	   });
2451   DEBUG(CHK_SECURE, checkStacks(NULL));
2452   assert((void*)fli_context > (void*)environment_frame);
2453   assert((Word)lTop >= refFliP(fli_context, fli_context->size));
2454 
2455 					/* resolve can call-back */
2456   def = getProcDefinedDefinition(proc->definition PASS_LD);
2457 
2458 #ifdef JMPBUF_ALIGNMENT
2459   lneeded = JMPBUF_ALIGNMENT + sizeof(struct queryFrame)+MAXARITY*sizeof(word);
2460 #else
2461   lneeded = sizeof(struct queryFrame)+MAXARITY*sizeof(word);
2462 #endif
2463 
2464   if ( !ensureLocalSpace(lneeded) )
2465     return (qid_t)0;
2466 					/* should be struct alignment, */
2467 					/* but for now, I think this */
2468 					/* is always the same */
2469   qf = (QueryFrame)lTop;
2470 #ifdef JMPBUF_ALIGNMENT
2471   while ( (uintptr_t)qf % JMPBUF_ALIGNMENT )
2472     qf = addPointer(qf, sizeof(word));
2473 #endif
2474   qf->saved_ltop = lTop;
2475 
2476 					/* fill top-frame */
2477   top	             = &qf->top_frame;
2478   top->parent        = NULL;
2479   top->predicate     = PROCEDURE_dc_call_prolog->definition;
2480   top->programPointer= NULL;
2481   top->clause        = &cref;
2482 #ifdef O_PROFILE
2483   if ( LD->profile.active )
2484     top->prof_node = profCall(top->predicate PASS_LD);
2485   else
2486     top->prof_node = NULL;
2487 #endif
2488   if ( environment_frame )
2489   { setNextFrameFlags(top, environment_frame);
2490   } else
2491   { top->flags	     = FR_MAGIC;
2492     top->level	     = 0;
2493   }
2494   fr                 = &qf->frame;
2495   fr->parent         = top;
2496   setNextFrameFlags(fr, top);
2497   set(top, FR_HIDE_CHILDS);
2498   fr->programPointer = clause.codes;
2499   arity		     = def->functor->arity;
2500 
2501   DEBUG(CHK_SECURE, checkStacks(NULL));
2502   assert((uintptr_t)fli_context > (uintptr_t)environment_frame);
2503   assert((uintptr_t)lTop >= (uintptr_t)(fli_context+1));
2504 
2505   if ( flags == TRUE )			/* compatibility */
2506     flags = PL_Q_NORMAL;
2507   else if ( flags == FALSE )
2508     flags = PL_Q_NODEBUG;
2509   flags &= 0xff;			/* mask reserved flags */
2510 
2511   qf->magic		= QID_MAGIC;
2512   qf->foreign_frame	= 0;
2513   qf->flags		= flags;
2514   qf->saved_environment = environment_frame;
2515   qf->saved_bfr		= LD->choicepoints;
2516   qf->aSave             = aTop;
2517   qf->solutions         = 0;
2518   qf->exception		= 0;
2519   qf->yield.term        = 0;
2520   qf->registers.fr      = NULL;		/* invalid */
2521   qf->next_environment  = NULL;		/* see D_BREAK */
2522 					/* fill frame arguments */
2523   ap = argFrameP(fr, 0);
2524   { size_t n;
2525     Word p = valTermRef(args);
2526 
2527     for( n = arity; n-- > 0; p++ )
2528       *ap++ = linkVal(p);
2529   }
2530 					/* lTop above the arguments */
2531   lTop = (LocalFrame)ap;
2532 
2533   DEBUG(3, Sdprintf("Level = %d\n", levelFrame(fr)));
2534   if ( true(qf, PL_Q_NODEBUG) )
2535   { set(fr, FR_HIDE_CHILDS);
2536     suspendTrace(TRUE);
2537     qf->debugSave = debugstatus.debugging;
2538     debugstatus.debugging = DBG_OFF;
2539     qf->flags_saved = (LD->prolog_flag.mask.flags & NDEBUG_SAVE_FLAGS);
2540     setPrologFlagMask(PLFLAG_LASTCALL);
2541 #ifdef O_LIMIT_DEPTH
2542     qf->saved_depth_limit   = depth_limit;
2543     qf->saved_depth_reached = depth_reached;
2544     depth_limit = DEPTH_NO_LIMIT;
2545 #endif
2546   }
2547   fr->predicate      = def;
2548   fr->clause         = NULL;
2549 					/* create initial choicepoint */
2550   qf->choice.type   = CHP_TOP;
2551   qf->choice.parent = NULL;
2552   qf->choice.frame  = top;
2553 #ifdef O_PROFILE
2554   qf->choice.prof_node = NULL;
2555   fr->prof_node = NULL;			/* true? */
2556 #endif
2557   Mark(qf->choice.mark);
2558   setGenerationFrame(fr);
2559 					/* context module */
2560   if ( true(def, P_TRANSPARENT) )
2561   { if ( ctx )
2562       setContextModule(fr, ctx);
2563     else if ( qf->saved_environment )
2564       setContextModule(fr, contextModule(qf->saved_environment));
2565     else
2566       setContextModule(fr, MODULE_user);
2567   }
2568 
2569 					/* publish environment */
2570   LD->choicepoints  = &qf->choice;
2571   environment_frame = fr;
2572   qf->parent = LD->query;
2573   LD->query = qf;
2574 
2575   DEBUG(2, Sdprintf("QID=%d\n", QidFromQuery(qf)));
2576   updateAlerted(LD);
2577 
2578   return QidFromQuery(qf);
2579 }
2580 
2581 
2582 static void
discard_query(qid_t qid ARG_LD)2583 discard_query(qid_t qid ARG_LD)
2584 { QueryFrame qf = QueryFromQid(qid);
2585 
2586   discardChoicesAfter(&qf->frame, FINISH_CUT PASS_LD);
2587   qf = QueryFromQid(qid);		/* may be shifted */
2588   discardFrame(&qf->frame PASS_LD);
2589   if ( true(&qf->frame, FR_WATCHED) )
2590   { lTop = (LocalFrame)argFrameP(&qf->frame,
2591 				 qf->frame.predicate->functor->arity);
2592     frameFinished(&qf->frame, FINISH_CUT PASS_LD);
2593   }
2594 }
2595 
2596 
2597 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2598 Restore the environment. If an exception was raised by the query, and no
2599 new  exception  has  been  thrown,  consider    it  handled.
2600 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2601 
2602 static void
restore_after_query(QueryFrame qf)2603 restore_after_query(QueryFrame qf)
2604 { GET_LD
2605   if ( qf->exception && !exception_term )
2606     *valTermRef(exception_printed) = 0;
2607 
2608   DiscardMark(qf->choice.mark);
2609 
2610   LD->query         = qf->parent;
2611   LD->choicepoints  = qf->saved_bfr;
2612   environment_frame = qf->saved_environment;
2613   aTop		    = qf->aSave;
2614   lTop		    = qf->saved_ltop;
2615   if ( true(qf, PL_Q_NODEBUG) )
2616   { suspendTrace(FALSE);
2617     debugstatus.debugging = qf->debugSave;
2618     LD->prolog_flag.mask.flags &= (~NDEBUG_SAVE_FLAGS);
2619     LD->prolog_flag.mask.flags |= qf->flags_saved;
2620 #ifdef O_LIMIT_DEPTH
2621     depth_limit   = qf->saved_depth_limit;
2622     depth_reached = qf->saved_depth_reached;
2623 #endif /*O_LIMIT_DEPTH*/
2624   }
2625   updateAlerted(LD);
2626   DEBUG(CHK_SECURE, checkStacks(NULL));
2627 }
2628 
2629 
2630 int
PL_cut_query(qid_t qid)2631 PL_cut_query(qid_t qid)
2632 { GET_LD
2633   QueryFrame qf = QueryFromQid(qid);
2634   int rc = TRUE;
2635 
2636   DEBUG(CHK_SECURE, assert(qf->magic == QID_MAGIC));
2637   if ( qf->foreign_frame )
2638     PL_close_foreign_frame(qf->foreign_frame);
2639 
2640   if ( false(qf, PL_Q_DETERMINISTIC) )
2641   { int exbefore = (exception_term != 0);
2642 
2643     discard_query(qid PASS_LD);
2644     qf = QueryFromQid(qid);
2645     if ( !exbefore && exception_term != 0 )
2646       rc = FALSE;
2647   }
2648 
2649   restore_after_query(qf);
2650   qf->magic = 0;			/* disqualify the frame */
2651 
2652   return rc;
2653 }
2654 
2655 
2656 int
PL_close_query(qid_t qid)2657 PL_close_query(qid_t qid)
2658 { int rc = TRUE;
2659 
2660   if ( qid != 0 )
2661   { GET_LD
2662     QueryFrame qf = QueryFromQid(qid);
2663 
2664     DEBUG(CHK_SECURE, assert(qf->magic == QID_MAGIC));
2665     if ( qf->foreign_frame )
2666       PL_close_foreign_frame(qf->foreign_frame);
2667 
2668     if ( false(qf, PL_Q_DETERMINISTIC) )
2669     { int exbefore = (exception_term != 0);
2670 
2671       discard_query(qid PASS_LD);
2672       qf = QueryFromQid(qid);
2673       if ( !exbefore && exception_term != 0 )
2674 	rc = FALSE;
2675     }
2676 
2677     if ( !(qf->exception && true(qf, PL_Q_PASS_EXCEPTION)) )
2678       Undo(qf->choice.mark);
2679 
2680     restore_after_query(qf);
2681     qf->magic = 0;			/* disqualify the frame */
2682   }
2683 
2684   return rc;
2685 }
2686 
2687 
2688 qid_t
PL_current_query(void)2689 PL_current_query(void)
2690 { GET_LD
2691 
2692   if ( HAS_LD )
2693   { if ( LD->query )
2694       return QidFromQuery(LD->query);
2695   }
2696 
2697   return 0;
2698 }
2699 
2700 
2701 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2702 PL_exception(qid) is used to extract exceptions   from an query executed
2703 using  PL_next_solution().  The  term-reference  itself   is  no  longer
2704 referenced and therefore we must create a new one and copy the term.
2705 
2706 If qid == 0, we return the currently pending exception.
2707 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2708 
2709 term_t
PL_exception(qid_t qid)2710 PL_exception(qid_t qid)
2711 { GET_LD
2712 
2713   if ( qid )
2714   { QueryFrame qf = QueryFromQid(qid);
2715 
2716     if ( qf->exception )
2717     { term_t ex;
2718 
2719       if ( (void*)fli_context <= (void*)environment_frame )
2720 	fatalError("PL_exception(): No foreign environment");
2721 
2722       ex = PL_new_term_ref();
2723       PL_put_term(ex, qf->exception);
2724       return ex;
2725     }
2726 
2727     return 0;
2728   } else
2729     return exception_term;
2730 }
2731 
2732 
2733 term_t
PL_yielded(qid_t qid)2734 PL_yielded(qid_t qid)
2735 { GET_LD
2736   QueryFrame qf = QueryFromQid(qid);
2737 
2738   return qf->yield.term;
2739 }
2740 
2741 
2742 #define SAVE_REGISTERS(qid) \
2743 	{ QueryFrame qf = QueryFromQid(qid); \
2744 	  qf->registers.fr   = FR; \
2745 	  qf->registers.argp = ARGP; \
2746 	  qf->registers.pc   = PC; \
2747 	}
2748 #define LOAD_REGISTERS(qid) \
2749 	{ QueryFrame qf = QueryFromQid(qid); \
2750 	  FR   = qf->registers.fr; \
2751 	  ARGP = qf->registers.argp; \
2752 	  PC   = qf->registers.pc; \
2753 	  qf->registers.fr = NULL; \
2754 	}
2755 
2756 typedef enum
2757 { uread = 0,				/* Unification in read-mode */
2758   uwrite				/* Unification in write mode */
2759 } unify_mode;
2760 
2761 #define IF_WRITE_MODE_GOTO(label) \
2762 	if ( umode == uwrite ) VMI_GOTO(label)
2763 
2764 #define TRUST_CLAUSE(cref) \
2765 	umode = uread; \
2766 	CL    = cref; \
2767 	lTop  = (LocalFrame)(ARGP + cref->value.clause->variables); \
2768 	ENSURE_LOCAL_SPACE(LOCAL_MARGIN, THROW_EXCEPTION); \
2769 	if ( debugstatus.debugging ) \
2770 	  newChoice(CHP_DEBUG, FR PASS_LD); \
2771 	PC    = cref->value.clause->codes; \
2772 	NEXT_INSTRUCTION;
2773 #define TRY_CLAUSE(cref, cond, altpc) \
2774 	umode = uread; \
2775 	CL    = cref; \
2776 	lTop  = (LocalFrame)(ARGP + cref->value.clause->variables); \
2777 	ENSURE_LOCAL_SPACE(LOCAL_MARGIN, THROW_EXCEPTION); \
2778 	if ( cond ) \
2779 	{ Choice ch = newChoice(CHP_JUMP, FR PASS_LD); \
2780 	  ch->value.PC = altpc; \
2781 	} else if ( debugstatus.debugging ) \
2782 	{ newChoice(CHP_DEBUG, FR PASS_LD); \
2783 	} \
2784 	PC    = cref->value.clause->codes; \
2785 	NEXT_INSTRUCTION;
2786 
2787 
2788 int
PL_next_solution(qid_t qid)2789 PL_next_solution(qid_t qid)
2790 { GET_LD
2791   AR_CTX
2792   QueryFrame QF;			/* Query frame */
2793   LocalFrame FR;			/* current frame */
2794   LocalFrame NFR;			/* Next frame */
2795   Word	     ARGP;			/* current argument pointer */
2796   Code	     PC = NULL;			/* program counter */
2797   Definition DEF = NULL;		/* definition of current procedure */
2798   unify_mode umode = uread;		/* Unification mode */
2799   int slow_unify = FALSE;		/* B_UNIFY_FIRSTVAR */
2800   exception_frame throw_env;		/* PL_thow() environment */
2801 #ifdef O_DEBUG
2802   int	     throwed_from_line=0;	/* Debugging: line we came from */
2803 #endif
2804 #define	     CL (FR->clause)		/* clause of current frame */
2805 
2806 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2807 Get the labels of the various  virtual-machine instructions in an array.
2808 This is for exploiting GCC's `goto   var' language extension. This array
2809 can only be allocated insite this   function. The initialisation process
2810 calls PL_next_solution() with qid =  QID_EXPORT_WAM_TABLE. This function
2811 will export jmp_table as the compiler  needs   to  know  this table. See
2812 pl-comp.c
2813 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2814 
2815 #include "pentium.h"
2816 
2817 #if VMCODE_IS_ADDRESS
2818 #include <pl-jumptable.ic>
2819 
2820 #define VMI(Name,f,na,a)	Name ## _LBL: \
2821 				  count(Name, PC); \
2822 				  START_PROF(Name, #Name);
2823 #define VMI_GOTO(n)		do { END_PROF(); \
2824 				       goto n ## _LBL; \
2825 				   } while(0)
2826 #define NEXT_INSTRUCTION	do { END_PROF(); \
2827 				     DbgPrintInstruction(FR, PC); \
2828 				     goto *(void *)((intptr_t)(*PC++)); \
2829 				   } while(0)
2830 
2831 /* This macro must ensure that two identical VMI instructions do not get
2832  * merged onto the same address by the compiler, causing decompilation
2833  * which translates the addresses back into the VMI number to fail.
2834  * initWamTable() verfies this does not happen.
2835  */
2836 #define SEPARATE_VMI { static volatile int nop = 0; (void)nop; }
2837 
2838 #else /* VMCODE_IS_ADDRESS */
2839 
2840 code thiscode;
2841 
2842 #if __GNUC__
2843 #define UNUSED_LABEL __attribute__ ((unused))
2844 #else
2845 #define UNUSED_LABEL
2846 #endif
2847 
2848 #define VMI(Name,f,na,a)	case Name: \
2849 				  case_ ## Name: UNUSED_LABEL \
2850 				  count(Name, PC); \
2851 				  START_PROF(Name, #Name);
2852 #define VMI_GOTO(n)		{ END_PROF(); \
2853 				  goto case_ ## n; \
2854 				}
2855 #define NEXT_INSTRUCTION	{ DbgPrintInstruction(FR, PC); \
2856 				  END_PROF(); \
2857                                   goto next_instruction; \
2858 				}
2859 #define SEPARATE_VMI		(void)0
2860 
2861 #endif /* VMCODE_IS_ADDRESS */
2862 
2863 #define FASTCOND_FAILED \
2864 	{ if ( !LD->fast_condition )   \
2865 	  { BODY_FAILED;	       \
2866 	  } else		       \
2867 	  { PC = LD->fast_condition;   \
2868 	    LD->fast_condition = NULL; \
2869 	    NEXT_INSTRUCTION;          \
2870 	  }                            \
2871 	}
2872 
2873 
2874 #if VMCODE_IS_ADDRESS
2875   if ( qid == QID_EXPORT_WAM_TABLE )
2876   { interpreter_jmp_table = jmp_table;	/* make it globally known */
2877     succeed;
2878   }
2879 #endif /* VMCODE_IS_ADDRESS */
2880 
2881   if ( qid == 0 )			/* PL_open_query() failed */
2882     return FALSE;
2883 
2884 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2885 This is the real start point  of   this  function.  Simply loads the VMI
2886 registers from the frame filled by   PL_open_query()  and either jump to
2887 depart_continue() to do the normal thing or to the backtrack point.
2888 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2889 
2890   QF  = QueryFromQid(qid);
2891   DEBUG(CHK_SECURE, assert(QF->magic == QID_MAGIC));
2892   if ( true(QF, PL_Q_DETERMINISTIC) )	/* last one succeeded */
2893   { fid_t fid = QF->foreign_frame;
2894     QF->foreign_frame = 0;
2895     PL_close_foreign_frame(fid);
2896     Undo(QF->choice.mark);
2897     fail;
2898   }
2899   FR  = &QF->frame;
2900   ARGP = argFrameP(FR, 0);
2901   DEBUG(9, Sdprintf("QF=%p, FR=%p\n", QF, FR));
2902 
2903 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2904 Check for exceptions raised by foreign code.  PL_throw() uses longjmp()
2905 to get back here.  Our task is to restore the environment and throw the
2906 Prolog exception.
2907 
2908 setjmp()/longjmp clobbers register variables. FR   is  restored from the
2909 environment. BFR is volatile, and qid is an argument. These are the only
2910 variables used in the B_THROW instruction.
2911 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2912 
2913   DEBUG(9, Sdprintf("Setjmp env at %p\n", &LD->exception.throw_environment));
2914   throw_env.parent = LD->exception.throw_environment;
2915   if ( setjmp(throw_env.exception_jmp_env) != 0 )
2916   { FliFrame ffr;
2917 #ifdef O_PLMT
2918     __PL_ld = GLOBAL_LD;		/* might be clobbered */
2919 #endif
2920     ffr = fli_context;
2921 
2922     FR = environment_frame;
2923     DEF = FR->predicate;
2924     while(ffr && (void *)ffr > (void *)FR) /* discard foreign contexts */
2925       ffr = ffr->parent;
2926     fli_context = ffr;
2927 
2928     AR_CLEANUP();
2929 
2930     if ( LD->signal.current )
2931     { unblockSignal(LD->signal.current);
2932       LD->signal.current = 0;	/* TBD: saved? */
2933     }
2934 
2935     THROW_EXCEPTION;
2936   } else				/* installation */
2937   { throw_env.magic = THROW_MAGIC;
2938     LD->exception.throw_environment = &throw_env;
2939   }
2940 
2941   DEF = FR->predicate;
2942   if ( QF->solutions || QF->yield.term ) /* retry or resume */
2943   { fid_t fid = QF->foreign_frame;
2944     QF->foreign_frame = 0;
2945     PL_close_foreign_frame(fid);
2946     if ( QF->yield.term )
2947     { LOAD_REGISTERS(qid);
2948       DEBUG(CHK_SECURE, checkStacks(NULL));
2949       if ( exception_term )
2950 	THROW_EXCEPTION;
2951       NEXT_INSTRUCTION;
2952     }
2953     BODY_FAILED;
2954   } else
2955     goto retry_continue;		/* first call */
2956 
2957 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2958 Main entry of the virtual machine cycle.  A branch to `next instruction'
2959 will  cause  the  next  instruction  to  be  interpreted.   All  machine
2960 registers  should  hold  valid  data  and  the  machine stacks should be
2961 initialised properly.
2962 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2963 
2964 #if !VMCODE_IS_ADDRESS			/* no goto *ptr; use a switch */
2965 next_instruction:
2966   thiscode = *PC++;
2967 #ifdef O_DEBUGGER
2968 resumebreak:
2969 #endif
2970   switch( thiscode )
2971 #endif
2972   {
2973 #include "pl-vmi.c"
2974   }
2975 
2976 #ifdef O_ATTVAR
2977 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2978 Attributed variable handling
2979 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2980 wakeup:
2981   DEBUG(1, Sdprintf("Activating wakeup\n"));
2982   NFR = lTop;
2983   setNextFrameFlags(NFR, FR);
2984   SAVE_REGISTERS(qid);
2985   DEF = GD->procedures.dwakeup1->definition;
2986   LOAD_REGISTERS(qid);
2987   ARGP = argFrameP(NFR, 0);
2988   ARGP[0] = *valTermRef(LD->attvar.head);
2989   setVar(*valTermRef(LD->attvar.head));
2990   setVar(*valTermRef(LD->attvar.tail));
2991 
2992   goto normal_call;
2993 #endif
2994 
2995 
2996 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2997 			TRACER RETRY ACTION
2998 
2999 By default, retries the  current  frame.  If   another  frame  is  to be
3000 retried, place the frame-reference, which  should   be  a  parent of the
3001 current frame, in debugstatus.retryFrame and jump to this label. This is
3002 implemented by returning retry(Frame) of the prolog_trace_interception/3
3003 hook.
3004 
3005 First, the system will leave any parent  frames. Next, it will undo back
3006 to the call-port and finally, restart the clause.
3007 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
3008 
3009 #if O_DEBUGGER
3010 retry:					MARK(RETRY);
3011 { LocalFrame rframe0, rframe;
3012   mark m;
3013   Choice ch;
3014 
3015   if ( debugstatus.retryFrame )
3016     rframe = (LocalFrame)valTermRef(debugstatus.retryFrame);
3017   else
3018     rframe = FR;
3019   debugstatus.retryFrame = 0;
3020   rframe0 = rframe;
3021 
3022   m.trailtop = tTop;
3023   m.globaltop = gTop;
3024   for( ; rframe; rframe = rframe->parent )
3025   { if ( (ch = findStartChoice(rframe, BFR)) )
3026     { m = ch->mark;
3027       goto do_retry;
3028     }
3029   }
3030   Sdprintf("[Could not find retry-point]\n");
3031   SAVE_REGISTERS(qid);
3032   abortProlog();				/* What else? */
3033   LOAD_REGISTERS(qid);
3034   THROW_EXCEPTION;
3035 
3036 do_retry:
3037   if ( rframe0 != rframe )
3038   { DEBUG(MSG_TRACE,
3039 	  Sdprintf("[No retry-information for requested frame]\n"));
3040   }
3041 
3042   DEBUG(MSG_TRACE,
3043 	Sdprintf("[Retrying frame %d running %s]\n",
3044 		 (Word)rframe - (Word)lBase,
3045 		 predicateName(rframe->predicate)));
3046 
3047   discardChoicesAfter(rframe, FINISH_CUT PASS_LD);
3048   rframe->clause = NULL;
3049   environment_frame = FR = rframe;
3050   DEF = FR->predicate;
3051   clear(FR, FR_SKIPPED);
3052   Undo(m);
3053   exception_term = 0;
3054 
3055   goto retry_continue;
3056 }
3057 #endif /*O_DEBUGGER*/
3058 
3059 		 /*******************************
3060 		 *	   BACKTRACKING		*
3061 		 *******************************/
3062 
3063 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3064 The rest of this giant procedure handles   backtracking. This used to be
3065 very complicated, but as of pl-3.3.6, choice-points are explicit objects
3066 and life is a lot easier. In the old days we distinquished between three
3067 cases to get here. We leave that   it for documentation purposes as well
3068 as to investigate optimization in the future.
3069 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
3070 
3071 				MARK(BKTRK);
3072 clause_failed:				/* shallow backtracking */
3073 body_failed:
3074 END_PROF();
3075 START_PROF(P_SHALLOW_BACKTRACK, "P_SHALLOW_BACKTRACK");
3076 { Choice ch = BFR;
3077 
3078   if ( FR == ch->frame )
3079   { Undo(ch->mark);
3080     QF = QueryFromQid(qid);
3081     aTop = QF->aSave;
3082 
3083     if ( ch->type == CHP_JUMP )
3084     { DiscardMark(ch->mark);
3085       PC   = ch->value.PC;
3086       BFR  = ch->parent;
3087       lTop = (LocalFrame)ch;
3088       ARGP = argFrameP(lTop, 0);
3089 
3090       NEXT_INSTRUCTION;
3091     } else if ( ch->type == CHP_CLAUSE )
3092     { ARGP = argFrameP(FR, 0);
3093       if ( !(CL = nextClause(&ch->value.clause, ARGP, FR, DEF)) )
3094 	FRAME_FAILED;		/* can happen if scan-ahead was too short */
3095       PC = CL->value.clause->codes;
3096       umode = uread;
3097 
3098       if ( ch == (Choice)argFrameP(FR, CL->value.clause->variables) )
3099       { DiscardMark(ch->mark);		/* is this needed? */
3100 	if ( ch->value.clause.cref )
3101 	{ Mark(ch->mark);
3102 	  lTop = (LocalFrame)(ch+1);
3103 	  NEXT_INSTRUCTION;
3104 	} else if ( unlikely(debugstatus.debugging) )
3105 	{ ch->type = CHP_DEBUG;
3106 	  Mark(ch->mark);
3107 	  lTop = (LocalFrame)(ch+1);
3108 	  NEXT_INSTRUCTION;
3109 	}
3110 
3111 	BFR = ch->parent;
3112 	lTop = (LocalFrame)ch;
3113 	NEXT_INSTRUCTION;
3114       } else				/* Choice point needs to move */
3115       { struct clause_choice chp;
3116 
3117         DiscardMark(ch->mark);
3118 	BFR = ch->parent;
3119 	chp = ch->value.clause;
3120 	lTop = (LocalFrame)argFrameP(FR, CL->value.clause->variables);
3121 	ENSURE_LOCAL_SPACE(LOCAL_MARGIN, THROW_EXCEPTION);
3122 
3123 	if ( chp.cref )
3124 	{ ch = newChoice(CHP_CLAUSE, FR PASS_LD);
3125 	  ch->value.clause = chp;
3126 	} else if ( unlikely(debugstatus.debugging) )
3127 	{ ch = newChoice(CHP_DEBUG, FR PASS_LD);
3128 	}
3129 	NEXT_INSTRUCTION;
3130       }
3131     }
3132   }
3133 }
3134 
3135 
3136 frame_failed:
3137 END_PROF();
3138 START_PROF(P_DEEP_BACKTRACK, "P_DEEP_BACKTRACK");
3139 {
3140 #ifdef O_DEBUGGER
3141   term_t ch0_ref = BFR ? consTermRef(BFR) : 0;
3142 #endif
3143   Choice ch;
3144 
3145   DEBUG(MSG_BACKTRACK, Sdprintf("BACKTRACKING\n"));
3146 
3147 next_choice:
3148   ch = BFR;
3149 					/* leave older frames */
3150   for(; (void *)FR > (void *)ch; FR = FR->parent)
3151   {
3152 #ifdef O_DEBUGGER
3153     if ( debugstatus.debugging && isDebugFrame(FR) )
3154     { Choice sch = ch0_ref ? findStartChoice(FR, (Choice)valTermRef(ch0_ref)) : NULL;
3155 
3156       DEBUG(1, Sdprintf("FAIL on %s\n", predicateName(FR->predicate)));
3157 
3158       if ( sch )
3159       { int rc;
3160 	Choice ch0 = findChoiceBeforeFrame(FR, sch);
3161 
3162 	ch0_ref = ch0 ? consTermRef(ch0) : 0;
3163 	Undo(sch->mark);
3164 	environment_frame = FR;
3165 	FR->clause = NULL;
3166 	lTop = (LocalFrame)argFrameP(FR, FR->predicate->functor->arity);
3167 	SAVE_REGISTERS(qid);
3168 	rc = tracePort(FR, BFR, FAIL_PORT, NULL PASS_LD);
3169 	LOAD_REGISTERS(qid);
3170 	ch = BFR;			/* can be shifted */
3171 
3172 	switch( rc )
3173 	{ case ACTION_RETRY:
3174 	    environment_frame = FR;
3175 	    DEF = FR->predicate;
3176 	    clear(FR, FR_CATCHED|FR_SKIPPED);
3177 	    goto retry_continue;
3178 	    case ACTION_ABORT:
3179 	      THROW_EXCEPTION;
3180 	}
3181       } else
3182       { ch0_ref = 0;
3183 	DEBUG(2, Sdprintf("Cannot trace FAIL [%d] %s\n",
3184 			  levelFrame(FR), predicateName(FR->predicate)));
3185       }
3186     }
3187 #endif
3188 
3189     leaveFrame(FR);
3190     if ( true(FR, FR_WATCHED) )
3191     { environment_frame = FR;
3192       lTop = (LocalFrame)argFrameP(FR, FR->predicate->functor->arity);
3193       FR->clause = NULL;
3194       SAVE_REGISTERS(qid);
3195       frameFinished(FR, FINISH_FAIL PASS_LD);
3196       LOAD_REGISTERS(qid);
3197       ch = BFR;			/* can be shifted */
3198       if ( exception_term )
3199 	THROW_EXCEPTION;
3200     }
3201   }
3202 
3203   environment_frame = FR = ch->frame;
3204   Undo(ch->mark);
3205   QF = QueryFromQid(qid);
3206   aTop = QF->aSave;
3207   DEF  = FR->predicate;
3208 #ifdef O_DEBUG_BACKTRACK
3209   last_choice = ch->type;
3210 #endif
3211 
3212   if ( (LD->alerted & ALERT_BUFFER) )
3213   { LD->alerted &= ~ALERT_BUFFER;
3214     release_string_buffers_from_frame(FR PASS_LD);
3215   }
3216 
3217   switch(ch->type)
3218   { case CHP_JUMP:
3219       DEBUG(MSG_BACKTRACK,
3220 	    Sdprintf("    REDO #%ld: Jump in %s\n",
3221 		     loffset(FR),
3222 		     predicateName(DEF)));
3223       PC   = ch->value.PC;
3224       DiscardMark(ch->mark);
3225       BFR  = ch->parent;
3226       lTop = (LocalFrame)ch;
3227       ARGP = argFrameP(lTop, 0);
3228       LD->statistics.inferences++;
3229       if ( unlikely(LD->alerted) )
3230       {
3231 #ifdef O_DEBUGGER
3232 	if ( debugstatus.debugging && !debugstatus.suspendTrace  )
3233 	{ LocalFrame fr = dbgRedoFrame(FR, CHP_JUMP PASS_LD);
3234 
3235 	  if ( fr )
3236 	  { int action;
3237 
3238 	    SAVE_REGISTERS(qid);
3239 	    action = tracePort(fr, BFR, REDO_PORT, ch->value.PC PASS_LD);
3240 	    LOAD_REGISTERS(qid);
3241 	    ch = BFR;			/* can be shifted */
3242 
3243 	    switch( action )
3244 	    { case ACTION_FAIL:
3245 		FRAME_FAILED;
3246 	      case ACTION_IGNORE:
3247 		VMI_GOTO(I_EXIT);
3248 	      case ACTION_RETRY:
3249 	        goto retry;
3250 	      case ACTION_ABORT:
3251 		THROW_EXCEPTION;
3252 	    }
3253 	  }
3254 	}
3255 #endif
3256 #ifdef O_INFERENCE_LIMIT
3257         if ( LD->statistics.inferences >= LD->inference_limit.limit )
3258 	{ SAVE_REGISTERS(qid);
3259 	  raiseInferenceLimitException();
3260 	  LOAD_REGISTERS(qid);
3261 	  if ( exception_term )
3262 	    THROW_EXCEPTION;
3263 	}
3264 #endif
3265         Profile(profRedo(ch->prof_node PASS_LD));
3266       }
3267       NEXT_INSTRUCTION;
3268     case CHP_CLAUSE:			/* try next clause */
3269     { Clause clause;
3270       struct clause_choice chp;
3271 
3272       DEBUG(MSG_BACKTRACK,
3273 	    Sdprintf("    REDO #%ld: Clause in %s\n",
3274 		     loffset(FR),
3275 		     predicateName(DEF)));
3276       ARGP = argFrameP(FR, 0);
3277       DiscardMark(ch->mark);
3278       BFR = ch->parent;
3279       if ( !(CL = nextClause(&ch->value.clause, ARGP, FR, DEF)) )
3280 	goto next_choice;	/* Can happen of look-ahead was too short */
3281 
3282       chp    = ch->value.clause;
3283       clause = CL->value.clause;
3284       PC     = clause->codes;
3285       lTop   = (LocalFrame)argFrameP(FR, clause->variables);
3286       umode  = uread;
3287 
3288       DEBUG(CHK_SECURE, assert(LD->mark_bar >= gBase && LD->mark_bar <= gTop));
3289 
3290       if ( unlikely(LD->alerted) )
3291       {
3292 #ifdef O_DEBUGGER
3293 	if ( debugstatus.debugging && !debugstatus.suspendTrace  )
3294 	{ LocalFrame fr = dbgRedoFrame(FR, CHP_CLAUSE PASS_LD);
3295 
3296 	  if ( fr )
3297 	  { int action;
3298 
3299 	    SAVE_REGISTERS(qid);
3300 	    clearLocalVariablesFrame(FR);
3301 	    action = tracePort(fr, BFR, REDO_PORT, NULL PASS_LD);
3302 	    LOAD_REGISTERS(qid);
3303 	    ch = BFR;			/* can be shifted */
3304 
3305 	    switch( action )
3306 	    { case ACTION_FAIL:
3307 		FRAME_FAILED;
3308 	      case ACTION_IGNORE:
3309 		VMI_GOTO(I_EXIT);
3310 	      case ACTION_RETRY:
3311 		goto retry_continue;
3312 	      case ACTION_ABORT:
3313 		THROW_EXCEPTION;
3314 	    }
3315 	  }
3316 	}
3317 #endif
3318 #ifdef O_INFERENCE_LIMIT
3319         if ( LD->statistics.inferences >= LD->inference_limit.limit )
3320 	{ SAVE_REGISTERS(qid);
3321 	  raiseInferenceLimitException();
3322 	  LOAD_REGISTERS(qid);
3323 	  if ( exception_term )
3324 	    THROW_EXCEPTION;
3325 	}
3326 #endif
3327         Profile(profRedo(ch->prof_node PASS_LD));
3328       }
3329 
3330       if ( chp.cref )
3331       { ch = newChoice(CHP_CLAUSE, FR PASS_LD);
3332 	ch->value.clause = chp;
3333       } else if ( unlikely(debugstatus.debugging) )
3334       { newChoice(CHP_DEBUG, FR PASS_LD);
3335       }
3336 
3337       if ( is_signalled(PASS_LD1) )
3338       { SAVE_REGISTERS(qid);
3339 	handleSignals(PASS_LD1);
3340 	LOAD_REGISTERS(qid);
3341 	if ( exception_term )
3342 	  THROW_EXCEPTION;
3343       }
3344 
3345 			/* require space for the args of the next frame */
3346       ENSURE_LOCAL_SPACE(LOCAL_MARGIN, THROW_EXCEPTION);
3347       NEXT_INSTRUCTION;
3348     }
3349     case CHP_TOP:			/* Query toplevel */
3350     { DEBUG(MSG_BACKTRACK,
3351 	    Sdprintf("    REDO #%ld: %s: TOP\n",
3352 		     loffset(FR),
3353 		     predicateName(DEF)));
3354       DiscardMark(ch->mark);
3355       Profile(profRedo(ch->prof_node PASS_LD));
3356       QF = QueryFromQid(qid);
3357       set(QF, PL_Q_DETERMINISTIC);
3358       QF->foreign_frame = PL_open_foreign_frame();
3359       assert(LD->exception.throw_environment == &throw_env);
3360       LD->exception.throw_environment = throw_env.parent;
3361       fail;
3362     }
3363     case CHP_CATCH:			/* catch/3 & setup_call_cleanup/3 */
3364       DEBUG(MSG_BACKTRACK,
3365 	    Sdprintf("    REDO #%ld: %s: CATCH\n",
3366 		     loffset(FR),
3367 		     predicateName(DEF)));
3368             if ( true(ch->frame, FR_WATCHED) )
3369       { DiscardMark(ch->mark);
3370 	environment_frame = FR = ch->frame;
3371 	lTop = (LocalFrame)(ch+1);
3372 	FR->clause = NULL;
3373 	if ( true(ch->frame, FR_CLEANUP) )
3374 	{ SAVE_REGISTERS(qid);
3375 	  callCleanupHandler(ch->frame, FINISH_FAIL PASS_LD);
3376 	  LOAD_REGISTERS(qid);
3377 	} else
3378 	{ set(ch->frame, FR_CATCHED);
3379 	}
3380 	ch = BFR;			/* can be shifted */
3381 	if ( exception_term )
3382 	  THROW_EXCEPTION;
3383       } else
3384       { set(ch->frame, FR_CATCHED);
3385       }
3386       /*FALLTHROUGH*/
3387     case CHP_DEBUG:			/* Just for debugging purposes */
3388       DEBUG(MSG_BACKTRACK,
3389 	    Sdprintf("    REDO #%ld: %s: DEBUG\n",
3390 		     loffset(FR),
3391 		     predicateName(DEF)));
3392       #ifdef O_DEBUGGER
3393       ch0_ref = consTermRef(ch);
3394 #endif
3395       BFR = ch->parent;
3396       DiscardMark(ch->mark);
3397       goto next_choice;
3398   }
3399 }
3400   assert(0);
3401   return FALSE;
3402 } /* end of PL_next_solution() */
3403