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(>[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(>[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(>[1], v1, has_firstvar PASS_LD);
1008 unify_gl(>[2], v2, has_firstvar PASS_LD);
1009 gt[3] = FUNCTOR_call1;
1010 gt[4] = consPtr(gt, STG_GLOBAL|TAG_COMPOUND);
1011 *valTermRef(t) = consPtr(>[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(>[1], v1, has_firstvar PASS_LD);
1043 gt[2] = FUNCTOR_call1;
1044 gt[3] = consPtr(gt, STG_GLOBAL|TAG_COMPOUND);
1045 *valTermRef(t) = consPtr(>[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(>[1], B, has_firstvar PASS_LD);
1119 gt[2] = consInt(add);
1120 gt[3] = FUNCTOR_is2;
1121 unify_gl(>[4], A, has_firstvar PASS_LD);
1122 gt[5] = consPtr(>[0], STG_GLOBAL|TAG_COMPOUND);
1123 gt[6] = FUNCTOR_call1;
1124 gt[7] = consPtr(>[3], STG_GLOBAL|TAG_COMPOUND);
1125 *valTermRef(t) = consPtr(>[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