3755 lines
103 KiB
C
3755 lines
103 KiB
C
/*************************************************************************
|
|
* *
|
|
* YAP Prolog *
|
|
* *
|
|
* Yap Prolog was developed at NCCUP - Universidade do Porto *
|
|
* *
|
|
* Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997 *
|
|
* *
|
|
**************************************************************************
|
|
* *
|
|
* File: compiler.c *
|
|
* comments: Clause compiler *
|
|
* *
|
|
* Last rev: $Date: 2008-08-06 17:32:18 $,$Author: vsc $ *
|
|
* $Log: not supported by cvs2svn $
|
|
* Revision 1.88 2008/03/13 14:37:58 vsc
|
|
* update chr
|
|
*
|
|
* Revision 1.87 2007/12/18 17:46:58 vsc
|
|
* purge_clauses does not need to do anything if there are no clauses
|
|
* fix gprof bugs.
|
|
*
|
|
* Revision 1.86 2007/11/26 23:43:08 vsc
|
|
* fixes to support threads and assert correctly, even if inefficiently.
|
|
*
|
|
* Revision 1.85 2007/11/06 17:02:11 vsc
|
|
* compile ground terms away.
|
|
*
|
|
* Revision 1.84 2007/03/27 13:48:51 vsc
|
|
* fix number of overflows (comments by Bart Demoen).
|
|
*
|
|
* Revision 1.83 2007/03/26 15:18:43 vsc
|
|
* debugging and clause/3 over tabled predicates would kill YAP.
|
|
*
|
|
* Revision 1.82 2006/11/06 18:35:03 vsc
|
|
* 1estranha
|
|
*
|
|
* Revision 1.81 2006/10/11 15:08:03 vsc
|
|
* fix bb entries
|
|
* comment development code for timestamp overflow.
|
|
*
|
|
* Revision 1.80 2006/09/20 20:03:51 vsc
|
|
* improve indexing on floats
|
|
* fix sending large lists to DB
|
|
*
|
|
* Revision 1.79 2006/08/01 13:14:17 vsc
|
|
* fix compilation of |
|
|
*
|
|
* Revision 1.78 2006/07/27 19:04:56 vsc
|
|
* fix nasty overflows in and add some very preliminary support for very large
|
|
* clauses with lots
|
|
* of disjuncts (eg, query packs).
|
|
*
|
|
* Revision 1.77 2006/05/19 14:31:31 vsc
|
|
* get rid of IntArrays and FloatArray code.
|
|
* include holes when calculating memory usage.
|
|
*
|
|
* Revision 1.76 2006/05/19 13:48:11 vsc
|
|
* help to make Yap work with dynamic libs
|
|
*
|
|
* Revision 1.75 2006/05/16 18:37:30 vsc
|
|
* WIN32 fixes
|
|
* compiler bug fixes
|
|
* extend interface
|
|
*
|
|
* Revision 1.74 2006/04/13 02:04:24 vsc
|
|
* fix debugging typo
|
|
*
|
|
* Revision 1.73 2006/04/12 20:08:51 vsc
|
|
* make it sure that making vars safe does not propagate across branches of disjunctions.
|
|
*
|
|
* Revision 1.72 2006/04/05 00:16:54 vsc
|
|
* Lots of fixes (check logfile for details
|
|
*
|
|
* Revision 1.71 2006/03/24 17:13:41 rslopes
|
|
* New update to BEAM engine.
|
|
* BEAM now uses YAP Indexing (JITI)
|
|
*
|
|
* Revision 1.70 2005/12/17 03:25:39 vsc
|
|
* major changes to support online event-based profiling
|
|
* improve error discovery and restart on scanner.
|
|
*
|
|
* Revision 1.69 2005/09/08 22:06:44 rslopes
|
|
* BEAM for YAP update...
|
|
*
|
|
* Revision 1.68 2005/07/06 15:10:03 vsc
|
|
* improvements to compiler: merged instructions and fixes for ->
|
|
*
|
|
* Revision 1.67 2005/05/25 21:43:32 vsc
|
|
* fix compiler bug in 1 << X, found by Nuno Fonseca.
|
|
* compiler internal errors get their own message.
|
|
*
|
|
* Revision 1.66 2005/05/12 03:36:32 vsc
|
|
* debugger was making predicates meta instead of testing
|
|
* fix handling of dbrefs in facts and in subarguments.
|
|
*
|
|
* Revision 1.65 2005/04/10 04:01:10 vsc
|
|
* bug fixes, I hope!
|
|
*
|
|
* Revision 1.64 2005/03/13 06:26:10 vsc
|
|
* fix excessive pruning in meta-calls
|
|
* fix Term->int breakage in compiler
|
|
* improve JPL (at least it does something now for amd64).
|
|
*
|
|
* Revision 1.63 2005/03/04 20:30:11 ricroc
|
|
* bug fixes for YapTab support
|
|
*
|
|
* Revision 1.62 2005/02/21 16:49:39 vsc
|
|
* amd64 fixes
|
|
* library fixes
|
|
*
|
|
* Revision 1.61 2005/01/28 23:14:35 vsc
|
|
* move to Yap-4.5.7
|
|
* Fix clause size
|
|
*
|
|
* Revision 1.60 2005/01/14 20:55:16 vsc
|
|
* improve register liveness calculations.
|
|
*
|
|
* Revision 1.59 2005/01/04 02:50:21 vsc
|
|
* - allow MegaClauses with blobs
|
|
* - change Diffs to be thread specific
|
|
* - include Christian's updates
|
|
*
|
|
* Revision 1.58 2005/01/03 17:06:03 vsc
|
|
* fix discontiguous stack overflows in parser
|
|
*
|
|
* Revision 1.57 2004/12/20 21:44:57 vsc
|
|
* more fixes to CLPBN
|
|
* fix some Yap overflows.
|
|
*
|
|
* Revision 1.56 2004/12/16 05:57:32 vsc
|
|
* fix overflows
|
|
*
|
|
* Revision 1.55 2004/12/05 05:01:23 vsc
|
|
* try to reduce overheads when running with goal expansion enabled.
|
|
* CLPBN fixes
|
|
* Handle overflows when allocating big clauses properly.
|
|
*
|
|
* Revision 1.54 2004/11/19 22:08:41 vsc
|
|
* replace SYSTEM_ERROR by out OUT_OF_WHATEVER_ERROR whenever appropriate.
|
|
*
|
|
* Revision 1.53 2004/09/03 03:11:08 vsc
|
|
* memory management fixes
|
|
*
|
|
* Revision 1.52 2004/07/15 17:20:23 vsc
|
|
* fix error message
|
|
* change makefile and configure for clpbn
|
|
*
|
|
* Revision 1.51 2004/06/29 19:04:41 vsc
|
|
* fix multithreaded version
|
|
* include new version of Ricardo's profiler
|
|
* new predicat atomic_concat
|
|
* allow multithreaded-debugging
|
|
* small fixes
|
|
*
|
|
* Revision 1.50 2004/04/22 20:07:04 vsc
|
|
* more fixes for USE_SYSTEM_MEMORY
|
|
*
|
|
* Revision 1.49 2004/03/10 16:27:39 vsc
|
|
* skip compilation steps for ground facts.
|
|
*
|
|
* Revision 1.48 2004/03/08 19:31:01 vsc
|
|
* move to 4.5.3
|
|
* *
|
|
* *
|
|
*************************************************************************/
|
|
#ifdef SCCS
|
|
static char SccsId[] = "%W% %G%";
|
|
|
|
#endif /* SCCS */
|
|
#include "Yap.h"
|
|
#include "compile.h"
|
|
#include "clause.h"
|
|
#include "alloc.h"
|
|
#include "yapio.h"
|
|
#if HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif
|
|
|
|
#ifdef BEAM
|
|
extern int EAM;
|
|
//extern PInstr *CodeStart, *ppc, *ppc1, *BodyStart, *ppc_body;
|
|
#endif
|
|
|
|
typedef struct branch_descriptor {
|
|
int id; /* the branch id */
|
|
Term cm; /* if a banch is associated with a commit */
|
|
} branch;
|
|
|
|
typedef struct compiler_struct_struct {
|
|
branch parent_branches[256];
|
|
branch *branch_pointer;
|
|
PInstr *BodyStart;
|
|
Ventry *vtable;
|
|
CExpEntry *common_exps;
|
|
int is_a_fact;
|
|
int hasdbrefs;
|
|
int n_common_exps;
|
|
int goalno;
|
|
int onlast;
|
|
int onhead;
|
|
int onbranch;
|
|
int curbranch;
|
|
Int space_used;
|
|
PInstr *space_op;
|
|
Prop current_p0;
|
|
#ifdef TABLING_INNER_CUTS
|
|
PInstr *cut_mark;
|
|
#endif /* TABLING_INNER_CUTS */
|
|
#ifdef DEBUG
|
|
int pbvars;
|
|
#endif /* DEBUG */
|
|
int nvars;
|
|
UInt labelno;
|
|
int or_found;
|
|
UInt max_args;
|
|
int MaxCTemps;
|
|
UInt tmpreg;
|
|
Int vreg;
|
|
Int vadr;
|
|
Int *Uses;
|
|
Term *Contents;
|
|
int needs_env;
|
|
CIntermediates cint;
|
|
} compiler_struct;
|
|
|
|
static int active_branch(int, int);
|
|
static void c_var(Term, Int, unsigned int, unsigned int, compiler_struct *);
|
|
static void reset_vars(Ventry *);
|
|
static Term optimize_ce(Term, unsigned int, unsigned int, compiler_struct *);
|
|
static void c_arg(Int, Term, unsigned int, unsigned int, compiler_struct *);
|
|
static void c_args(Term, unsigned int, compiler_struct *);
|
|
static void c_eq(Term, Term, compiler_struct *);
|
|
static void c_test(Int, Term, compiler_struct *);
|
|
static void c_bifun(basic_preds, Term, Term, Term, Term, Term, compiler_struct *);
|
|
static void c_goal(Term, Term, compiler_struct *);
|
|
static void c_body(Term, Term, compiler_struct *);
|
|
static void c_head(Term, compiler_struct *);
|
|
static bool usesvar(compiler_vm_op);
|
|
static CELL *init_bvarray(int, compiler_struct *);
|
|
#ifdef DEBUG
|
|
static void clear_bvarray(int, CELL *, compiler_struct *);
|
|
#else
|
|
static void clear_bvarray(int, CELL *);
|
|
#endif
|
|
static void add_bvarray_op(PInstr *,CELL *, int, compiler_struct *);
|
|
static void AssignPerm(PInstr *, compiler_struct *);
|
|
static void CheckUnsafe(PInstr *, compiler_struct *);
|
|
static void CheckVoids(compiler_struct *);
|
|
static int checktemp(Int, Int, compiler_vm_op, compiler_struct *);
|
|
static Int checkreg(Int, Int, compiler_vm_op, int, compiler_struct *);
|
|
static void c_layout(compiler_struct *);
|
|
static void c_optimize(PInstr *);
|
|
#ifdef SFUNC
|
|
static void compile_sf_term(Term, int);
|
|
#endif
|
|
|
|
static void
|
|
push_branch(int id, Term cmvar, compiler_struct *cglobs) {
|
|
cglobs->branch_pointer->id = id;
|
|
cglobs->branch_pointer->cm = cmvar;
|
|
cglobs->branch_pointer++;
|
|
}
|
|
|
|
static int
|
|
pop_branch(compiler_struct *cglobs) {
|
|
cglobs->branch_pointer--;
|
|
return(cglobs->branch_pointer->id);
|
|
}
|
|
|
|
#ifdef TABLING
|
|
#define is_tabled(pe) (pe->PredFlags & TabledPredFlag)
|
|
#endif /* TABLING */
|
|
|
|
static inline int
|
|
active_branch(int i, int onbranch)
|
|
{
|
|
/* register int *bp;*/
|
|
|
|
return (i == onbranch);
|
|
/* bp = cglobs->branch_pointer;
|
|
while (bp > parent_branches) {
|
|
if (*--bp == onbranch)
|
|
return (TRUE);
|
|
}
|
|
return(i==onbranch);*/
|
|
}
|
|
|
|
#define FAIL(M,T,E) { LOCAL_ErrorMessage=M; LOCAL_Error_TYPE = T; LOCAL_Error_Term = E; return; }
|
|
|
|
#if USE_SYSTEM_MALLOC
|
|
#define IsNewVar(v) ((CELL *)(v) >= H0 && (CELL *)(v) < LCL0)
|
|
#else
|
|
#define IsNewVar(v) (Addr(v)<cglobs->cint.freep0 || Addr(v)>cglobs->cint.freep)
|
|
#endif
|
|
|
|
inline static void pop_code(unsigned int, compiler_struct *);
|
|
|
|
inline static void
|
|
pop_code(unsigned int level, compiler_struct *cglobs)
|
|
{
|
|
if (level == 0)
|
|
return;
|
|
if (cglobs->cint.cpc->op == pop_op)
|
|
++(cglobs->cint.cpc->rnd1);
|
|
else {
|
|
Yap_emit(pop_op, One, Zero, &cglobs->cint);
|
|
}
|
|
}
|
|
|
|
static void
|
|
adjust_current_commits(compiler_struct *cglobs) {
|
|
branch *bp = cglobs->branch_pointer;
|
|
while (bp > cglobs->parent_branches) {
|
|
bp--;
|
|
if (bp->cm != TermNil) {
|
|
c_var(bp->cm, patch_b_flag, 1, 0, cglobs);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
check_var(Term t, unsigned int level, Int argno, compiler_struct *cglobs) {
|
|
CACHE_REGS
|
|
int flags, new = FALSE;
|
|
Ventry *v = (Ventry *)t;
|
|
|
|
if (IsNewVar(v)) { /* new var */
|
|
v = (Ventry *) Yap_AllocCMem(sizeof(*v), &cglobs->cint);
|
|
#if YAPOR_SBA
|
|
v->SelfOfVE = 0;
|
|
#else
|
|
v->SelfOfVE = (CELL) v;
|
|
#endif
|
|
v->AdrsOfVE = t;
|
|
*CellPtr(t) = (CELL) v;
|
|
v->KindOfVE = v->NoOfVE = Unassigned;
|
|
flags = 0;
|
|
/* Be careful with eithers. I may make a variable global in a branch,
|
|
and not in another.
|
|
a :- (b([X]) ; c), go(X).
|
|
This variable will not be globalised if we are coming from
|
|
the second branch.
|
|
|
|
I also need to protect the onhead because Luis uses that to
|
|
optimise unification in the body of a clause, eg
|
|
a :- (X = 2 ; c), go(X).
|
|
|
|
And, yes, there is code like this...
|
|
*/
|
|
if (((level > 0 || cglobs->onhead) && cglobs->curbranch == 0)
|
|
|| argno == save_pair_flag ||
|
|
argno == save_appl_flag)
|
|
flags |= SafeVar;
|
|
if ((level > 0 && cglobs->curbranch == 0) || argno == save_pair_flag ||
|
|
argno == save_appl_flag)
|
|
flags |= GlobalVal;
|
|
v->FlagsOfVE = flags;
|
|
v->BranchOfVE = cglobs->onbranch;
|
|
v->NextOfVE = cglobs->vtable;
|
|
v->RCountOfVE = 0;
|
|
v->AgeOfVE = v->FirstOfVE = cglobs->goalno;
|
|
new = TRUE;
|
|
cglobs->vtable = v;
|
|
} else {
|
|
v->FlagsOfVE |= NonVoid;
|
|
if (v->BranchOfVE > 0) {
|
|
if (!active_branch(v->BranchOfVE, cglobs->onbranch)) {
|
|
v->AgeOfVE = v->FirstOfVE = 1;
|
|
new = FALSE;
|
|
v->FlagsOfVE |= BranchVar;
|
|
/* set the original instruction correctly */
|
|
switch (v->FirstOpForV->op) {
|
|
case get_var_op:
|
|
v->FirstOpForV->op = get_val_op;
|
|
break;
|
|
case unify_var_op:
|
|
v->FirstOpForV->op = unify_val_op;
|
|
break;
|
|
case unify_last_var_op:
|
|
v->FirstOpForV->op = unify_last_val_op;
|
|
break;
|
|
case put_var_op:
|
|
v->FirstOpForV->op = put_val_op;
|
|
break;
|
|
case write_var_op:
|
|
v->FirstOpForV->op = write_val_op;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (cglobs->onhead)
|
|
v->FlagsOfVE |= OnHeadFlag;
|
|
return new;
|
|
}
|
|
|
|
static void
|
|
tag_var(Term t, int new, compiler_struct *cglobs)
|
|
{
|
|
Ventry *v = (Ventry *) t;
|
|
|
|
if (new) {
|
|
v->FirstOpForV = cglobs->cint.cpc;
|
|
}
|
|
v->LastOpForV = cglobs->cint.cpc;
|
|
++(v->RCountOfVE);
|
|
if (cglobs->onlast)
|
|
v->FlagsOfVE |= OnLastGoal;
|
|
if (v->AgeOfVE < cglobs->goalno)
|
|
v->AgeOfVE = cglobs->goalno;
|
|
}
|
|
|
|
static void
|
|
c_var(Term t, Int argno, unsigned int arity, unsigned int level, compiler_struct *cglobs)
|
|
{
|
|
int new = check_var(Deref(t), level, argno, cglobs);
|
|
t = Deref(t);
|
|
|
|
switch (argno) {
|
|
case save_b_flag:
|
|
Yap_emit(save_b_op, t, Zero, &cglobs->cint);
|
|
break;
|
|
case commit_b_flag:
|
|
Yap_emit(commit_b_op, t, Zero, &cglobs->cint);
|
|
Yap_emit(empty_call_op, Zero, Zero, &cglobs->cint);
|
|
Yap_emit(restore_tmps_and_skip_op, Zero, Zero, &cglobs->cint);
|
|
break;
|
|
case patch_b_flag:
|
|
Yap_emit(patch_b_op, t, 0, &cglobs->cint);
|
|
break;
|
|
case save_pair_flag:
|
|
Yap_emit(save_pair_op, t, 0, &cglobs->cint);
|
|
break;
|
|
case save_appl_flag:
|
|
Yap_emit(save_appl_op, t, 0, &cglobs->cint);
|
|
break;
|
|
case f_flag:
|
|
if (new) {
|
|
++cglobs->nvars;
|
|
Yap_emit(f_var_op, t, (CELL)arity, &cglobs->cint);
|
|
} else {
|
|
Yap_emit(f_val_op, t, (CELL)arity, &cglobs->cint);
|
|
}
|
|
break;
|
|
default:
|
|
#ifdef SFUNC
|
|
if (argno < 0) {
|
|
if (new)
|
|
Yap_emit((cglobs->onhead ? unify_s_var_op : write_s_var_op), v, -argno, &cglobs->cint);
|
|
else
|
|
Yap_emit((cglobs->onhead ? unify_s_val_op : write_s_val_op), v, -argno, &cglobs->cint);
|
|
} else
|
|
#endif
|
|
if (cglobs->onhead) {
|
|
cglobs->space_used ++;
|
|
if (level == 0)
|
|
Yap_emit((new ? (++cglobs->nvars, get_var_op) : get_val_op), t, argno, &cglobs->cint);
|
|
else
|
|
Yap_emit((new ? (++cglobs->nvars, (argno == (Int)arity ?
|
|
unify_last_var_op :
|
|
unify_var_op)) :
|
|
(argno == (Int)arity ? unify_last_val_op :
|
|
unify_val_op)),
|
|
t, Zero, &cglobs->cint);
|
|
}
|
|
else {
|
|
if (level == 0)
|
|
Yap_emit((new ? (++cglobs->nvars, put_var_op) : put_val_op), t, argno, &cglobs->cint);
|
|
else
|
|
Yap_emit((new ? (++cglobs->nvars, write_var_op) : write_val_op), t, Zero, &cglobs->cint);
|
|
}
|
|
}
|
|
tag_var(t, new, cglobs);
|
|
}
|
|
|
|
// built-in like X >= Y.
|
|
static void
|
|
c_2vars(int op, Term t1, Int argno1, Term t2, Int argno2, CELL extra, unsigned int arity, unsigned int level, compiler_struct *cglobs)
|
|
{
|
|
int new1 = check_var((t1 = Deref(t1)), level, argno1, cglobs);
|
|
int new2 = check_var((t2 = Deref(t2)), level, argno2, cglobs);
|
|
|
|
switch (op) {
|
|
case bt_flag:
|
|
Yap_emit_5ops(bccall_op, t1, argno1, t2, argno2, extra, &cglobs->cint);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
tag_var(t1, new1, cglobs);
|
|
tag_var(t2, new2, cglobs);
|
|
}
|
|
|
|
static void
|
|
reset_vars(Ventry *vtable)
|
|
{
|
|
Ventry *v = vtable;
|
|
CELL *t;
|
|
|
|
while (v != NIL) {
|
|
t = (CELL *) v->AdrsOfVE;
|
|
RESET_VARIABLE(t);
|
|
v = v->NextOfVE;
|
|
}
|
|
}
|
|
|
|
static Term
|
|
optimize_ce(Term t, unsigned int arity, unsigned int level, compiler_struct *cglobs)
|
|
{
|
|
CACHE_REGS
|
|
CExpEntry *p = cglobs->common_exps;
|
|
int cmp = 0;
|
|
|
|
#ifdef BEAM
|
|
if (EAM) return t;
|
|
#endif
|
|
|
|
if (IsApplTerm(t) && IsExtensionFunctor(FunctorOfTerm(t)))
|
|
return (t);
|
|
while (p != NULL) {
|
|
CELL *oldH = HR;
|
|
HR = (CELL *)cglobs->cint.freep;
|
|
cmp = Yap_compare_terms(t, (p->TermOfCE));
|
|
HR = oldH;
|
|
|
|
if (cmp) {
|
|
p = p->NextCE;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (p != NULL) { /* already there */
|
|
return (p->VarOfCE);
|
|
}
|
|
/* first occurrence */
|
|
if (cglobs->onbranch || level > 1) {
|
|
return t;
|
|
}
|
|
++(cglobs->n_common_exps);
|
|
p = (CExpEntry *) Yap_AllocCMem(sizeof(CExpEntry), &cglobs->cint);
|
|
|
|
p->TermOfCE = t;
|
|
p->VarOfCE = MkVarTerm();
|
|
if (HR >= (CELL *)cglobs->cint.freep0) {
|
|
/* oops, too many new variables */
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,OUT_OF_TEMPS_BOTCH);
|
|
}
|
|
p->NextCE = cglobs->common_exps;
|
|
cglobs->common_exps = p;
|
|
if (IsApplTerm(t))
|
|
c_var(p->VarOfCE, save_appl_flag, arity, level, cglobs);
|
|
else if (IsPairTerm(t))
|
|
c_var(p->VarOfCE, save_pair_flag, arity, level, cglobs);
|
|
return (t);
|
|
}
|
|
|
|
#ifdef SFUNC
|
|
static void
|
|
compile_sf_term(Term t, int argno, int level)
|
|
{
|
|
Functor f = FunctorOfTerm(t);
|
|
CELL *p = ArgsOfSFTerm(t) - 1;
|
|
SFEntry *pe = RepSFProp(Yap_GetAProp(NameOfFunctor(f), SFProperty));
|
|
Term nullvalue = pe->NilValue;
|
|
|
|
if (level == 0)
|
|
Yap_emit((cglobs->onhead ? get_s_f_op : put_s_f_op), f, argno, &cglobs->cint);
|
|
else
|
|
Yap_emit((cglobs->onhead ? unify_s_f_op : write_s_f_op), f, Zero, &cglobs->cint);
|
|
++level;
|
|
while ((argno = *++p)) {
|
|
t = Derefa(++p);
|
|
if (t != nullvalue) {
|
|
if (IsAtomicTerm(t))
|
|
Yap_emit((cglobs->onhead ? unify_s_a_op : write_s_a_op), t, (CELL) argno, &cglobs->cint);
|
|
else if (!IsVarTerm(t)) {
|
|
LOCAL_Error_TYPE = INTERNAL_COMPILER_ERROR;
|
|
LOCAL_Error_Term = TermNil;
|
|
LOCAL_ErrorMessage = "illegal argument of soft functor";
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch, COMPILER_ERR_BOTCH);
|
|
}
|
|
else
|
|
c_var(t, -argno, arity, level, cglobs);
|
|
}
|
|
}
|
|
--level;
|
|
if (level == 0)
|
|
Yap_emit((cglobs->onhead ? get_s_end_op : put_s_end_op), Zero, Zero, &cglobs->cint);
|
|
else
|
|
Yap_emit((cglobs->onhead ? unify_s_end_op : write_s_end_op), Zero, Zero, &cglobs->cint);
|
|
}
|
|
#endif
|
|
|
|
inline static void
|
|
c_args(Term app, unsigned int level, compiler_struct *cglobs)
|
|
{
|
|
CACHE_REGS
|
|
Functor f = FunctorOfTerm(app);
|
|
unsigned int Arity = ArityOfFunctor(f);
|
|
unsigned int i;
|
|
|
|
if (level == 0) {
|
|
if (Arity >= MaxTemps) {
|
|
LOCAL_Error_TYPE = INTERNAL_COMPILER_ERROR;
|
|
LOCAL_Error_Term = TermNil;
|
|
LOCAL_ErrorMessage = "exceed maximum arity of compiled goal";
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch, COMPILER_ERR_BOTCH);
|
|
}
|
|
if (Arity > cglobs->max_args)
|
|
cglobs->max_args = Arity;
|
|
}
|
|
for (i = 1; i <= Arity; ++i)
|
|
c_arg(i, ArgOfTerm(i, app), Arity, level, cglobs);
|
|
}
|
|
|
|
static int
|
|
try_store_as_dbterm(Term t, Int argno, unsigned int arity, int level, compiler_struct *cglobs)
|
|
{
|
|
CACHE_REGS
|
|
DBTerm *dbt;
|
|
int g;
|
|
CELL *h0 = HR;
|
|
|
|
while ((g=Yap_SizeGroundTerm(t,TRUE)) < 0) {
|
|
/* oops, too deep a term */
|
|
save_machine_regs();
|
|
LOCAL_Error_Size = 0;
|
|
siglongjmp(cglobs->cint.CompilerBotch, OUT_OF_AUX_BOTCH);
|
|
}
|
|
// if (g < 16)
|
|
return FALSE;
|
|
/* store ground term away */
|
|
HR = CellPtr(cglobs->cint.freep);
|
|
if ((dbt = Yap_StoreTermInDB(t, -1)) == NULL) {
|
|
HR = h0;
|
|
switch(LOCAL_Error_TYPE) {
|
|
case OUT_OF_STACK_ERROR:
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
siglongjmp(cglobs->cint.CompilerBotch,OUT_OF_STACK_BOTCH);
|
|
case OUT_OF_TRAIL_ERROR:
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
siglongjmp(cglobs->cint.CompilerBotch,OUT_OF_TRAIL_BOTCH);
|
|
case OUT_OF_HEAP_ERROR:
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
siglongjmp(cglobs->cint.CompilerBotch,OUT_OF_HEAP_BOTCH);
|
|
case OUT_OF_AUXSPACE_ERROR:
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
siglongjmp(cglobs->cint.CompilerBotch,OUT_OF_AUX_BOTCH);
|
|
default:
|
|
siglongjmp(cglobs->cint.CompilerBotch,COMPILER_ERR_BOTCH);
|
|
}
|
|
}
|
|
HR = h0;
|
|
if (level == 0)
|
|
Yap_emit((cglobs->onhead ? get_dbterm_op : put_dbterm_op), dbt->Entry, argno, &cglobs->cint);
|
|
else
|
|
Yap_emit((cglobs->onhead ? (argno == (Int)arity ? unify_last_dbterm_op
|
|
: unify_dbterm_op) :
|
|
write_dbterm_op), dbt->Entry, Zero, &cglobs->cint);
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
c_arg(Int argno, Term t, unsigned int arity, unsigned int level, compiler_struct *cglobs)
|
|
{
|
|
restart:
|
|
if (IsVarTerm(t))
|
|
c_var(t, argno, arity, level, cglobs);
|
|
else if (IsAtomTerm(t)) {
|
|
if (level == 0) {
|
|
Yap_emit((cglobs->onhead ? get_atom_op : put_atom_op), (CELL) t, argno, &cglobs->cint);
|
|
} else
|
|
Yap_emit((cglobs->onhead ? (argno == (Int)arity ? unify_last_atom_op
|
|
: unify_atom_op) :
|
|
write_atom_op), (CELL) t, Zero, &cglobs->cint);
|
|
} else if (IsIntegerTerm(t) || IsFloatTerm(t) || IsBigIntTerm(t) || IsStringTerm(t)) {
|
|
if (!IsIntTerm(t)) {
|
|
if (IsFloatTerm(t)) {
|
|
if (level == 0)
|
|
Yap_emit((cglobs->onhead ? get_float_op : put_float_op), t, argno, &cglobs->cint);
|
|
else
|
|
Yap_emit((cglobs->onhead ? (argno == (Int)arity ? unify_last_float_op
|
|
: unify_float_op) :
|
|
write_float_op), t, Zero, &cglobs->cint);
|
|
} else if (IsLongIntTerm(t)) {
|
|
if (level == 0)
|
|
Yap_emit((cglobs->onhead ? get_longint_op : put_longint_op), t, argno, &cglobs->cint);
|
|
else
|
|
Yap_emit((cglobs->onhead ? (argno == (Int)arity ? unify_last_longint_op
|
|
: unify_longint_op) :
|
|
write_longint_op), t, Zero, &cglobs->cint);
|
|
} else if (IsStringTerm(t)) {
|
|
/* we are taking a string, that is supposed to be
|
|
guarded in the clause itself. . */
|
|
CELL l1 = ++cglobs->labelno;
|
|
CELL *src = RepAppl(t);
|
|
PInstr *ocpc = cglobs->cint.cpc, *OCodeStart = cglobs->cint.CodeStart;
|
|
Int sz = (3+src[1])*sizeof(CELL);
|
|
CELL *dest;
|
|
|
|
/* use a special list to store the blobs */
|
|
cglobs->cint.cpc = cglobs->cint.icpc;
|
|
/* if (IsFloatTerm(t)) {
|
|
Yap_emit(align_float_op, Zero, Zero, &cglobs->cint);
|
|
}*/
|
|
Yap_emit(label_op, l1, Zero, &cglobs->cint);
|
|
dest =
|
|
Yap_emit_extra_size(blob_op, sz/CellSize, sz, &cglobs->cint);
|
|
|
|
/* copy the bignum */
|
|
memcpy(dest, src, sz);
|
|
/* note that we don't need to copy size info, unless we wanted
|
|
to garbage collect clauses ;-) */
|
|
cglobs->cint.icpc = cglobs->cint.cpc;
|
|
if (cglobs->cint.BlobsStart == NULL)
|
|
cglobs->cint.BlobsStart = cglobs->cint.CodeStart;
|
|
cglobs->cint.cpc = ocpc;
|
|
cglobs->cint.CodeStart = OCodeStart;
|
|
/* The argument to pass to the structure is now the label for
|
|
where we are storing the blob */
|
|
if (level == 0)
|
|
Yap_emit((cglobs->onhead ? get_string_op : put_string_op), l1, argno, &cglobs->cint);
|
|
else
|
|
Yap_emit((cglobs->onhead ? (argno == (Int)arity ? unify_last_string_op
|
|
: unify_string_op) :
|
|
write_string_op), l1, Zero, &cglobs->cint);
|
|
} else {
|
|
/* we are taking a blob, that is a binary that is supposed to be
|
|
guarded in the clause itself. Possible examples include
|
|
floats, long ints, bignums, bitmaps.... */
|
|
CELL l1 = ++cglobs->labelno;
|
|
CELL *src = RepAppl(t);
|
|
PInstr *ocpc = cglobs->cint.cpc, *OCodeStart = cglobs->cint.CodeStart;
|
|
Int sz = 2*sizeof(CELL)+sizeof(Functor)+
|
|
sizeof(MP_INT)+
|
|
((((MP_INT *)(RepAppl(t)+2))->_mp_alloc)*sizeof(mp_limb_t));
|
|
CELL *dest;
|
|
|
|
/* use a special list to store the blobs */
|
|
cglobs->cint.cpc = cglobs->cint.icpc;
|
|
/* if (IsFloatTerm(t)) {
|
|
Yap_emit(align_float_op, Zero, Zero, &cglobs->cint);
|
|
}*/
|
|
Yap_emit(label_op, l1, Zero, &cglobs->cint);
|
|
dest =
|
|
Yap_emit_extra_size(blob_op, sz/CellSize, sz, &cglobs->cint);
|
|
|
|
/* copy the bignum */
|
|
memcpy(dest, src, sz);
|
|
/* note that we don't need to copy size info, unless we wanted
|
|
to garbage collect clauses ;-) */
|
|
cglobs->cint.icpc = cglobs->cint.cpc;
|
|
if (cglobs->cint.BlobsStart == NULL)
|
|
cglobs->cint.BlobsStart = cglobs->cint.CodeStart;
|
|
cglobs->cint.cpc = ocpc;
|
|
cglobs->cint.CodeStart = OCodeStart;
|
|
/* The argument to pass to the structure is now the label for
|
|
where we are storing the blob */
|
|
if (level == 0)
|
|
Yap_emit((cglobs->onhead ? get_bigint_op : put_bigint_op), l1, argno, &cglobs->cint);
|
|
else
|
|
Yap_emit((cglobs->onhead ? (argno == (Int)arity ? unify_last_bigint_op
|
|
: unify_bigint_op) :
|
|
write_bigint_op), l1, Zero, &cglobs->cint);
|
|
}
|
|
/* That's it folks! */
|
|
return;
|
|
}
|
|
if (level == 0)
|
|
Yap_emit((cglobs->onhead ? get_num_op : put_num_op), (CELL) t, argno, &cglobs->cint);
|
|
else
|
|
Yap_emit((cglobs->onhead ? (argno == (Int)arity ? unify_last_num_op
|
|
: unify_num_op) :
|
|
write_num_op), (CELL) t, Zero, &cglobs->cint);
|
|
} else if (IsPairTerm(t)) {
|
|
cglobs->space_used += 2;
|
|
if (optimizer_on && level < 6) {
|
|
#if !defined(THREADS) && !defined(YAPOR)
|
|
/* discard code sharing because we cannot write on shared stuff */
|
|
if (FALSE && !(cglobs->cint.CurrentPred->PredFlags & (DynamicPredFlag|LogUpdatePredFlag))) {
|
|
if (try_store_as_dbterm(t, argno, arity, level, cglobs))
|
|
return;
|
|
}
|
|
#endif
|
|
t = optimize_ce(t, arity, level, cglobs);
|
|
if (IsVarTerm(t)) {
|
|
c_var(t, argno, arity, level, cglobs);
|
|
return;
|
|
}
|
|
}
|
|
if (level == 0)
|
|
Yap_emit((cglobs->onhead ? get_list_op : put_list_op), Zero, argno, &cglobs->cint);
|
|
else if (argno == (Int)arity)
|
|
Yap_emit((cglobs->onhead ? unify_last_list_op : write_last_list_op), Zero, Zero, &cglobs->cint);
|
|
else
|
|
Yap_emit((cglobs->onhead ? unify_list_op : write_list_op), Zero, Zero, &cglobs->cint);
|
|
++level;
|
|
c_arg(1, HeadOfTerm(t), 2, level, cglobs);
|
|
if (argno == (Int)arity) {
|
|
/* optimise for tail recursion */
|
|
t = TailOfTerm(t);
|
|
goto restart;
|
|
}
|
|
c_arg(2, TailOfTerm(t), 2, level, cglobs);
|
|
--level;
|
|
if (argno != (Int)arity) {
|
|
pop_code(level, cglobs);
|
|
}
|
|
} else if (IsRefTerm(t)) {
|
|
PELOCK(40,cglobs->cint.CurrentPred);
|
|
if (!(cglobs->cint.CurrentPred->PredFlags & (DynamicPredFlag|LogUpdatePredFlag))) {
|
|
CACHE_REGS
|
|
UNLOCK(cglobs->cint.CurrentPred->PELock);
|
|
FAIL("can not compile data base reference",TYPE_ERROR_CALLABLE,t);
|
|
} else {
|
|
UNLOCK(cglobs->cint.CurrentPred->PELock);
|
|
cglobs->hasdbrefs = TRUE;
|
|
if (level == 0)
|
|
Yap_emit((cglobs->onhead ? get_atom_op : put_atom_op), (CELL) t, argno, &cglobs->cint);
|
|
else
|
|
Yap_emit((cglobs->onhead ? (argno == (Int)arity ? unify_last_atom_op
|
|
: unify_atom_op) :
|
|
write_atom_op), (CELL) t, Zero, &cglobs->cint);
|
|
}
|
|
} else {
|
|
|
|
#ifdef SFUNC
|
|
if (SFTerm(t)) {
|
|
compile_sf_term(t, argno);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (optimizer_on) {
|
|
if (!(cglobs->cint.CurrentPred->PredFlags & (DynamicPredFlag|LogUpdatePredFlag))) {
|
|
if (try_store_as_dbterm(t, argno, arity, level, cglobs))
|
|
return;
|
|
}
|
|
t = optimize_ce(t, arity, level, cglobs);
|
|
if (IsVarTerm(t)) {
|
|
c_var(t, argno, arity, level, cglobs);
|
|
return;
|
|
}
|
|
}
|
|
cglobs->space_used += 1+arity;
|
|
if (level == 0)
|
|
Yap_emit((cglobs->onhead ? get_struct_op : put_struct_op),
|
|
(CELL) FunctorOfTerm(t), argno, &cglobs->cint);
|
|
else if (argno == (Int)arity)
|
|
Yap_emit((cglobs->onhead ? unify_last_struct_op : write_last_struct_op),
|
|
(CELL) FunctorOfTerm(t), Zero, &cglobs->cint);
|
|
else
|
|
Yap_emit((cglobs->onhead ? unify_struct_op : write_struct_op),
|
|
(CELL) FunctorOfTerm(t), Zero, &cglobs->cint);
|
|
++level;
|
|
c_args(t, level, cglobs);
|
|
--level;
|
|
if (argno != (Int)arity) {
|
|
pop_code(level, cglobs);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
c_eq(Term t1, Term t2, compiler_struct *cglobs)
|
|
{
|
|
CACHE_REGS
|
|
if (t1 == t2) {
|
|
Yap_emit(nop_op, Zero, Zero, &cglobs->cint);
|
|
return;
|
|
}
|
|
if (IsNonVarTerm(t1)) {
|
|
if (IsVarTerm(t2)) {
|
|
Term t = t1;
|
|
t1 = t2;
|
|
t2 = t;
|
|
} else {
|
|
/* compile unification */
|
|
if (IsAtomicTerm(t1)) {
|
|
/* just check if they unify */
|
|
if (!IsAtomicTerm(t2) || !Yap_unify(t1,t2)) {
|
|
/* they don't */
|
|
Yap_emit(fail_op, Zero, Zero, &cglobs->cint);
|
|
return;
|
|
}
|
|
/* they do */
|
|
Yap_emit(nop_op, Zero, Zero, &cglobs->cint);
|
|
return;
|
|
} else if (IsPairTerm(t1)) {
|
|
/* just check if they unify */
|
|
if (!IsPairTerm(t2)) {
|
|
/* they don't */
|
|
Yap_emit(fail_op, Zero, Zero, &cglobs->cint);
|
|
return;
|
|
}
|
|
/* they might */
|
|
c_eq(HeadOfTerm(t1), HeadOfTerm(t2), cglobs);
|
|
c_eq(TailOfTerm(t1), TailOfTerm(t2), cglobs);
|
|
return;
|
|
} else if (IsRefTerm(t1)) {
|
|
/* just check if they unify */
|
|
if (t1 != t2) {
|
|
/* they don't */
|
|
Yap_emit(fail_op, Zero, Zero, &cglobs->cint);
|
|
return;
|
|
}
|
|
/* they do */
|
|
Yap_emit(nop_op, Zero, Zero, &cglobs->cint);
|
|
return;
|
|
} else {
|
|
/* compound terms */
|
|
Functor f = FunctorOfTerm(t1);
|
|
UInt i, max;
|
|
/* just check if they unify */
|
|
if (!IsApplTerm(t2) ||
|
|
FunctorOfTerm(t2) != f) {
|
|
/* they don't */
|
|
Yap_emit(fail_op, Zero, Zero, &cglobs->cint);
|
|
return;
|
|
}
|
|
/* they might */
|
|
max = ArityOfFunctor(f);
|
|
for (i=0; i < max; i++) {
|
|
c_eq(ArgOfTerm(i+1,t1), ArgOfTerm(i+1,t2), cglobs);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
/* first argument is an unbound var */
|
|
if (IsNewVar(t1) && !IsVarTerm(t2) && !(cglobs->cint.CurrentPred->PredFlags & TabledPredFlag)) {
|
|
Int v;
|
|
|
|
v = --cglobs->tmpreg;
|
|
c_arg(v, t2, 0, 0, cglobs);
|
|
cglobs->onhead = TRUE;
|
|
c_var(t1, v, 0, 0, cglobs);
|
|
cglobs->onhead = FALSE;
|
|
} else {
|
|
if (IsVarTerm(t2)) {
|
|
c_var(t1, 0, 0, 0, cglobs);
|
|
cglobs->onhead = TRUE;
|
|
c_var(t2, 0, 0, 0, cglobs);
|
|
} else {
|
|
Int v = --cglobs->tmpreg;
|
|
c_var(t1, v, 0, 0, cglobs);
|
|
cglobs->onhead = TRUE;
|
|
c_arg(v, t2, 0, 0, cglobs);
|
|
}
|
|
cglobs->onhead = FALSE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
c_test(Int Op, Term t1, compiler_struct *cglobs) {
|
|
CACHE_REGS
|
|
Term t = Deref(t1);
|
|
|
|
/* be caareful, has to be first occurrence */
|
|
if (Op == _save_by) {
|
|
if (!IsNewVar(t)) {
|
|
char s[32];
|
|
|
|
LOCAL_Error_TYPE = TYPE_ERROR_VARIABLE;
|
|
LOCAL_Error_Term = t;
|
|
LOCAL_ErrorMessage = LOCAL_ErrorSay;
|
|
Yap_bip_name(Op, s);
|
|
sprintf(LOCAL_ErrorMessage, "compiling %s/2 on bound variable", s);
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,1);
|
|
}
|
|
c_var(t, save_b_flag, 1, 0, cglobs);
|
|
return;
|
|
}
|
|
if (!IsVarTerm(t) || IsNewVar(t)) {
|
|
Term tn = MkVarTerm();
|
|
c_eq(t, tn, cglobs);
|
|
t = tn;
|
|
}
|
|
if (Op == _cut_by)
|
|
c_var(t, commit_b_flag, 1, 0, cglobs);
|
|
else
|
|
c_var(t, f_flag,(unsigned int)Op, 0, cglobs);
|
|
}
|
|
|
|
/* Arithmetic builtins will be compiled in the form:
|
|
|
|
fetch_args_vv Xi,Xj
|
|
put_val Xi,Ri
|
|
put_val Xj,Rj
|
|
put_var Xk,Ak
|
|
bip_body Op,Xk
|
|
|
|
The put_var should always be disposable, and the put_vals can be disposed of if R is an X.
|
|
This, in the best case, Ri and Rj are WAM temp registers and this will reduce to:
|
|
|
|
bip Op,Ak,Ri,Rj
|
|
|
|
meaning a single WAM op will call the clause.
|
|
|
|
|
|
If one of the arguments is a constant, the result will be:
|
|
|
|
fetch_args_vc Xi,C
|
|
put_val Xi,Ri
|
|
put_var Xk,Ak
|
|
bip_body Op,Xk
|
|
|
|
and this should reduce to :
|
|
|
|
bip_cons Op,Xk,Ri,C
|
|
|
|
*/
|
|
static void
|
|
c_bifun(basic_preds Op, Term t1, Term t2, Term t3, Term Goal, Term mod, compiler_struct *cglobs)
|
|
{
|
|
CACHE_REGS
|
|
/* compile Z = X Op Y arithmetic function */
|
|
/* first we fetch the arguments */
|
|
|
|
if (IsVarTerm(t1)) {
|
|
if (IsVarTerm(t2)) {
|
|
/* first temp */
|
|
Int v1 = --cglobs->tmpreg;
|
|
/* second temp */
|
|
Int v2 = --cglobs->tmpreg;
|
|
|
|
Yap_emit(fetch_args_vv_op, Zero, Zero, &cglobs->cint);
|
|
/* these should be the arguments */
|
|
c_var(t1, v1, 0, 0, cglobs);
|
|
c_var(t2, v2, 0, 0, cglobs);
|
|
/* now we know where the arguments are */
|
|
} else {
|
|
if (Op == _arg) {
|
|
/* we know the second argument is bound */
|
|
if (IsPrimitiveTerm(t2) || IsNumTerm(t2)) {
|
|
Yap_emit(fail_op, Zero, Zero, &cglobs->cint);
|
|
return;
|
|
} else {
|
|
Term tn = MkVarTerm();
|
|
Int v1 = --cglobs->tmpreg;
|
|
Int v2 = --cglobs->tmpreg;
|
|
|
|
c_eq(t2, tn, cglobs);
|
|
Yap_emit(fetch_args_vv_op, Zero, Zero, &cglobs->cint);
|
|
/* these should be the arguments */
|
|
c_var(t1, v1, 0, 0, cglobs);
|
|
c_var(tn, v2, 0, 0, cglobs);
|
|
}
|
|
/* it has to be either an integer or a floating point */
|
|
} else if (IsIntegerTerm(t2)) {
|
|
/* first temp */
|
|
Int v1 = 0;
|
|
|
|
Yap_emit(fetch_args_vi_op, IntegerOfTerm(t2), 0L, &cglobs->cint);
|
|
/* these should be the arguments */
|
|
c_var(t1, v1, 0, 0, cglobs);
|
|
/* now we know where the arguments are */
|
|
} else {
|
|
char s[32];
|
|
|
|
LOCAL_Error_TYPE = TYPE_ERROR_NUMBER;
|
|
LOCAL_Error_Term = t2;
|
|
LOCAL_ErrorMessage = LOCAL_ErrorSay;
|
|
Yap_bip_name(Op, s);
|
|
sprintf(LOCAL_ErrorMessage, "compiling %s/2 with output bound", s);
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,1);
|
|
}
|
|
}
|
|
} else { /* t1 is bound */
|
|
/* it has to be either an integer or a floating point */
|
|
if (IsVarTerm(t2)) {
|
|
if (IsNewVar(t2)) {
|
|
char s[32];
|
|
|
|
LOCAL_Error_TYPE = INSTANTIATION_ERROR;
|
|
LOCAL_Error_Term = t2;
|
|
LOCAL_ErrorMessage = LOCAL_ErrorSay;
|
|
Yap_bip_name(Op, s);
|
|
sprintf(LOCAL_ErrorMessage, "compiling %s/3",s);
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,1);
|
|
}
|
|
} else {
|
|
if (Op == _functor) {
|
|
/* both arguments are bound, we must perform unification */
|
|
Int i2;
|
|
|
|
if (!IsIntegerTerm(t2)) {
|
|
char s[32];
|
|
|
|
LOCAL_Error_TYPE = TYPE_ERROR_INTEGER;
|
|
LOCAL_Error_Term = t2;
|
|
LOCAL_ErrorMessage = LOCAL_ErrorSay;
|
|
Yap_bip_name(Op, s);
|
|
sprintf(LOCAL_ErrorMessage, "compiling functor/3");
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,1);
|
|
}
|
|
i2 = IntegerOfTerm(t2);
|
|
if (i2 < 0) {
|
|
char s[32];
|
|
|
|
LOCAL_Error_TYPE = DOMAIN_ERROR_NOT_LESS_THAN_ZERO;
|
|
LOCAL_Error_Term = t2;
|
|
LOCAL_ErrorMessage = LOCAL_ErrorSay;
|
|
Yap_bip_name(Op, s);
|
|
sprintf(LOCAL_ErrorMessage, "compiling functor/3");
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,1);
|
|
}
|
|
if (IsNumTerm(t1)) {
|
|
/* we will always fail */
|
|
if (i2)
|
|
c_goal(MkAtomTerm(AtomFalse), mod, cglobs);
|
|
} else if (!IsAtomTerm(t1)) {
|
|
char s[32];
|
|
|
|
LOCAL_Error_TYPE = TYPE_ERROR_ATOM;
|
|
LOCAL_Error_Term = t2;
|
|
LOCAL_ErrorMessage = LOCAL_ErrorSay;
|
|
Yap_bip_name(Op, s);
|
|
sprintf(LOCAL_ErrorMessage, "compiling functor/3");
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,1);
|
|
}
|
|
if (i2 == 0)
|
|
c_eq(t1, t3, cglobs);
|
|
else {
|
|
CELL *hi = HR;
|
|
Int i;
|
|
|
|
if (t1 == TermDot && i2 == 2) {
|
|
if (HR+2 >= (CELL *)cglobs->cint.freep0) {
|
|
/* oops, too many new variables */
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,OUT_OF_TEMPS_BOTCH);
|
|
}
|
|
RESET_VARIABLE(HR);
|
|
RESET_VARIABLE(HR+1);
|
|
HR += 2;
|
|
c_eq(AbsPair(HR-2),t3, cglobs);
|
|
} else if (i2 < 256 && IsAtomTerm(t1)) {
|
|
*HR++ = (CELL)Yap_MkFunctor(AtomOfTerm(t1),i2);
|
|
for (i=0; i < i2; i++) {
|
|
if (HR >= (CELL *)cglobs->cint.freep0) {
|
|
/* oops, too many new variables */
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,OUT_OF_TEMPS_BOTCH);
|
|
}
|
|
RESET_VARIABLE(HR);
|
|
HR++;
|
|
}
|
|
c_eq(AbsAppl(hi),t3, cglobs);
|
|
} else {
|
|
/* compile as default */
|
|
Functor f = FunctorOfTerm(Goal);
|
|
Prop p0 = PredPropByFunc(f, mod);
|
|
if (EndOfPAEntr(p0)) {
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch, OUT_OF_HEAP_BOTCH);
|
|
}
|
|
c_args(Goal, 0, cglobs);
|
|
Yap_emit(safe_call_op, (CELL)p0 , Zero, &cglobs->cint);
|
|
Yap_emit(empty_call_op, Zero, Zero, &cglobs->cint);
|
|
Yap_emit(restore_tmps_and_skip_op, Zero, Zero, &cglobs->cint);
|
|
return;
|
|
}
|
|
}
|
|
} else if (Op == _arg) {
|
|
Int i1;
|
|
if (IsIntegerTerm(t1))
|
|
i1 = IntegerOfTerm(t1);
|
|
else {
|
|
char s[32];
|
|
|
|
LOCAL_Error_TYPE = TYPE_ERROR_INTEGER;
|
|
LOCAL_Error_Term = t2;
|
|
LOCAL_ErrorMessage = LOCAL_ErrorSay;
|
|
Yap_bip_name(Op, s);
|
|
sprintf(LOCAL_ErrorMessage, "compiling %s/2", s);
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,1);
|
|
}
|
|
if (IsAtomicTerm(t2) ||
|
|
(IsApplTerm(t2) && IsExtensionFunctor(FunctorOfTerm(t2)))) {
|
|
char s[32];
|
|
|
|
LOCAL_Error_TYPE = TYPE_ERROR_COMPOUND;
|
|
LOCAL_Error_Term = t2;
|
|
LOCAL_ErrorMessage = LOCAL_ErrorSay;
|
|
Yap_bip_name(Op, s);
|
|
sprintf(LOCAL_ErrorMessage, "compiling %s/2", s);
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,1);
|
|
} else if (IsApplTerm(t2)) {
|
|
Functor f = FunctorOfTerm(t2);
|
|
if (i1 < 1 || i1 > ArityOfFunctor(f)) {
|
|
c_goal(MkAtomTerm(AtomFalse), mod, cglobs);
|
|
} else {
|
|
c_eq(ArgOfTerm(i1, t2), t3, cglobs);
|
|
}
|
|
return;
|
|
} else if (IsPairTerm(t2)) {
|
|
switch (i1) {
|
|
case 1:
|
|
c_eq(HeadOfTerm(t2), t3, cglobs);
|
|
return;
|
|
case 2:
|
|
c_eq(TailOfTerm(t2), t3, cglobs);
|
|
return;
|
|
default:
|
|
c_goal(MkAtomTerm(AtomFalse), mod, cglobs);
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
char s[32];
|
|
|
|
LOCAL_Error_TYPE = TYPE_ERROR_INTEGER;
|
|
LOCAL_Error_Term = t2;
|
|
LOCAL_ErrorMessage = LOCAL_ErrorSay;
|
|
Yap_bip_name(Op, s);
|
|
sprintf(LOCAL_ErrorMessage, "compiling %s/2", s);
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,1);
|
|
}
|
|
}
|
|
if (Op == _functor) {
|
|
if (!IsAtomicTerm(t1)) {
|
|
char s[32];
|
|
|
|
LOCAL_Error_TYPE = TYPE_ERROR_ATOM;
|
|
LOCAL_Error_Term = t1;
|
|
LOCAL_ErrorMessage = LOCAL_ErrorSay;
|
|
Yap_bip_name(Op, s);
|
|
sprintf(LOCAL_ErrorMessage, "compiling %s/2", s);
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,1);
|
|
} else {
|
|
if (!IsVarTerm(t2)) {
|
|
Int arity;
|
|
|
|
/* We actually have the term ready, so let's just do the unification now */
|
|
if (!IsIntegerTerm(t2)) {
|
|
char s[32];
|
|
|
|
LOCAL_Error_TYPE = TYPE_ERROR_INTEGER;
|
|
LOCAL_Error_Term = t2;
|
|
LOCAL_ErrorMessage = LOCAL_ErrorSay;
|
|
Yap_bip_name(Op, s);
|
|
sprintf(LOCAL_ErrorMessage, "compiling %s/2", s);
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,1);
|
|
}
|
|
arity = IntOfTerm(t2);
|
|
if (arity < 0) {
|
|
/* fail straight away */
|
|
Yap_emit(fail_op, Zero, Zero, &cglobs->cint);
|
|
}
|
|
if (arity) {
|
|
Term tnew;
|
|
if (!IsAtomTerm(t1)) {
|
|
char s[32];
|
|
|
|
LOCAL_Error_TYPE = TYPE_ERROR_ATOM;
|
|
LOCAL_Error_Term = t1;
|
|
LOCAL_ErrorMessage = LOCAL_ErrorSay;
|
|
Yap_bip_name(Op, s);
|
|
sprintf(LOCAL_ErrorMessage, "compiling %s/2", s);
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,1);
|
|
}
|
|
if (HR+1+arity >= (CELL *)cglobs->cint.freep0) {
|
|
/* oops, too many new variables */
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,OUT_OF_TEMPS_BOTCH);
|
|
}
|
|
tnew = AbsAppl(HR);
|
|
*HR++ = (CELL)Yap_MkFunctor(AtomOfTerm(t1),arity);
|
|
while (arity--) {
|
|
RESET_VARIABLE(HR);
|
|
HR++;
|
|
}
|
|
c_eq(tnew, t3, cglobs);
|
|
} else {
|
|
/* just unify the two arguments */
|
|
c_eq(t1,t3, cglobs);
|
|
}
|
|
return;
|
|
} else {
|
|
/* first temp */
|
|
Int v1 = 0;
|
|
Yap_emit(fetch_args_cv_op, t1, Zero, &cglobs->cint);
|
|
/* these should be the arguments */
|
|
c_var(t2, v1, 0, 0, cglobs);
|
|
/* now we know where the arguments are */
|
|
}
|
|
}
|
|
} else if (IsIntegerTerm(t1)) {
|
|
/* first temp */
|
|
Int v1 = 0;
|
|
Yap_emit(fetch_args_iv_op, IntegerOfTerm(t1), 0L, &cglobs->cint);
|
|
/* these should be the arguments */
|
|
c_var(t2, v1, 0, 0, cglobs);
|
|
/* now we know where the arguments are */
|
|
} else {
|
|
char s[32];
|
|
|
|
LOCAL_Error_TYPE = TYPE_ERROR_VARIABLE;
|
|
LOCAL_Error_Term = t1;
|
|
LOCAL_ErrorMessage = LOCAL_ErrorSay;
|
|
Yap_bip_name(Op, s);
|
|
sprintf(LOCAL_ErrorMessage, "compiling %s/2 with output bound", s);
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,1);
|
|
}
|
|
}
|
|
/* then we compile the opcode/result */
|
|
if (!IsVarTerm(t3)) {
|
|
if (Op == _arg) {
|
|
Term tmpvar = MkVarTerm();
|
|
if (HR == (CELL *)cglobs->cint.freep0) {
|
|
/* oops, too many new variables */
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,OUT_OF_TEMPS_BOTCH);
|
|
}
|
|
c_var(tmpvar,f_flag,(unsigned int)Op, 0, cglobs);
|
|
c_eq(tmpvar,t3, cglobs);
|
|
} else {
|
|
char s[32];
|
|
|
|
LOCAL_Error_TYPE = TYPE_ERROR_VARIABLE;
|
|
LOCAL_Error_Term = t3;
|
|
LOCAL_ErrorMessage = LOCAL_ErrorSay;
|
|
Yap_bip_name(Op, s);
|
|
sprintf(LOCAL_ErrorMessage, "compiling %s/2 with input unbound", s);
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,1);
|
|
}
|
|
} else if (IsNewVar(t3) && cglobs->curbranch == 0 && cglobs->cint.CurrentPred->PredFlags & TabledPredFlag) {
|
|
Term nv = MkVarTerm();
|
|
c_var(nv,f_flag,(unsigned int)Op, 0, cglobs);
|
|
if (Op == _functor) {
|
|
Yap_emit(empty_call_op, Zero, Zero, &cglobs->cint);
|
|
Yap_emit(restore_tmps_and_skip_op, Zero, Zero, &cglobs->cint);
|
|
}
|
|
/* make sure that we first get the true t3, and then bind it to nv. That way it will be confitional */
|
|
c_eq(t3, nv, cglobs);
|
|
} else if (IsNewVar(t3) && cglobs->curbranch == 0 /* otherwise you may have trouble with z(X) :- ( Z is X*2 ; write(Z)) */) {
|
|
c_var(t3,f_flag,(unsigned int)Op, 0, cglobs);
|
|
if (Op == _functor) {
|
|
Yap_emit(empty_call_op, Zero, Zero, &cglobs->cint);
|
|
Yap_emit(restore_tmps_and_skip_op, Zero, Zero, &cglobs->cint);
|
|
}
|
|
} else {
|
|
/* generate code for a temp and then unify temp with previous variable */
|
|
Yap_emit(f_0_op, 0, (unsigned int)Op, &cglobs->cint);
|
|
/* I have to do it here, before I do the unification */
|
|
if (Op == _functor) {
|
|
Yap_emit(empty_call_op, Zero, (unsigned int)Op, &cglobs->cint);
|
|
Yap_emit(restore_tmps_and_skip_op, Zero, Zero, &cglobs->cint);
|
|
}
|
|
cglobs->onhead = TRUE;
|
|
c_var(t3, 0, 0, 0, cglobs);
|
|
cglobs->onhead = FALSE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
c_functor(Term Goal, Term mod, compiler_struct *cglobs)
|
|
{
|
|
CACHE_REGS
|
|
Term t1 = ArgOfTerm(1, Goal);
|
|
Term t2 = ArgOfTerm(2, Goal);
|
|
Term t3 = ArgOfTerm(3, Goal);
|
|
|
|
if (IsVarTerm(t1) && IsNewVar(t1)) {
|
|
c_bifun(_functor, t2, t3, t1, Goal, mod, cglobs);
|
|
} else if (IsNonVarTerm(t1)) {
|
|
/* just split the structure */
|
|
if (IsAtomicTerm(t1)) {
|
|
c_eq(t1,t2, cglobs);
|
|
c_eq(t3,MkIntTerm(0), cglobs);
|
|
} else if (IsApplTerm(t1)) {
|
|
Functor f = FunctorOfTerm(t1);
|
|
c_eq(t2,MkAtomTerm(NameOfFunctor(f)), cglobs);
|
|
c_eq(t3,MkIntegerTerm(ArityOfFunctor(f)), cglobs);
|
|
} else /* list */ {
|
|
c_eq(t2,TermDot, cglobs);
|
|
c_eq(t3,MkIntTerm(2), cglobs);
|
|
}
|
|
} else if (IsVarTerm(t2) && IsNewVar(t2) &&
|
|
IsVarTerm(t3) && IsNewVar(t3)) {
|
|
Int v1 = --cglobs->tmpreg;
|
|
Yap_emit(fetch_args_vi_op, Zero, Zero, &cglobs->cint);
|
|
c_var(t1, v1, 0, 0, cglobs);
|
|
c_var(t2,f_flag,(unsigned int)_functor, 0, cglobs);
|
|
c_var(t3,f_flag,(unsigned int)_functor, 0, cglobs);
|
|
} else {
|
|
Functor f = FunctorOfTerm(Goal);
|
|
Prop p0 = PredPropByFunc(f, mod);
|
|
|
|
if (EndOfPAEntr(p0)) {
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch, OUT_OF_HEAP_BOTCH);
|
|
}
|
|
if (profiling)
|
|
Yap_emit(enter_profiling_op, (CELL)RepPredProp(p0), Zero, &cglobs->cint);
|
|
else if (call_counting)
|
|
Yap_emit(count_call_op, (CELL)RepPredProp(p0), Zero, &cglobs->cint);
|
|
c_args(Goal, 0, cglobs);
|
|
Yap_emit(safe_call_op, (CELL)p0 , Zero, &cglobs->cint);
|
|
Yap_emit(empty_call_op, Zero, Zero, &cglobs->cint);
|
|
Yap_emit(restore_tmps_and_skip_op, Zero, Zero, &cglobs->cint);
|
|
}
|
|
}
|
|
|
|
static int
|
|
IsTrueGoal(Term t) {
|
|
if (IsVarTerm(t)) return(FALSE);
|
|
if (IsApplTerm(t)) {
|
|
Functor f = FunctorOfTerm(t);
|
|
if (f == FunctorModule) {
|
|
return(IsTrueGoal(ArgOfTerm(2,t)));
|
|
}
|
|
if (f == FunctorComma || f == FunctorOr || f == FunctorVBar || f == FunctorArrow) {
|
|
return(IsTrueGoal(ArgOfTerm(1,t)) && IsTrueGoal(ArgOfTerm(2,t)));
|
|
}
|
|
return(FALSE);
|
|
}
|
|
return(t == MkAtomTerm(AtomTrue));
|
|
}
|
|
|
|
static void
|
|
emit_special_label(Term Goal, compiler_struct *cglobs)
|
|
{
|
|
special_label_op lab_op = IntOfTerm(ArgOfTerm(1,Goal));
|
|
special_label_id lab_id = IntOfTerm(ArgOfTerm(2,Goal));
|
|
UInt label_name;
|
|
|
|
switch (lab_op) {
|
|
case SPECIAL_LABEL_INIT:
|
|
label_name = ++cglobs->labelno;
|
|
switch (lab_id) {
|
|
case SPECIAL_LABEL_EXCEPTION:
|
|
cglobs->cint.exception_handler = label_name;
|
|
break;
|
|
case SPECIAL_LABEL_SUCCESS:
|
|
cglobs->cint.success_handler = label_name;
|
|
break;
|
|
case SPECIAL_LABEL_FAILURE:
|
|
cglobs->cint.failure_handler = label_name;
|
|
break;
|
|
}
|
|
Yap_emit_3ops(label_ctl_op, lab_op, lab_id, label_name, &cglobs->cint);
|
|
break;
|
|
case SPECIAL_LABEL_SET:
|
|
switch (lab_id) {
|
|
case SPECIAL_LABEL_EXCEPTION:
|
|
Yap_emit(label_op, cglobs->cint.exception_handler, Zero, &cglobs->cint);
|
|
break;
|
|
case SPECIAL_LABEL_SUCCESS:
|
|
Yap_emit(label_op, cglobs->cint.success_handler, Zero, &cglobs->cint);
|
|
break;
|
|
case SPECIAL_LABEL_FAILURE:
|
|
Yap_emit(label_op, cglobs->cint.failure_handler, Zero, &cglobs->cint);
|
|
break;
|
|
}
|
|
case SPECIAL_LABEL_CLEAR:
|
|
switch (lab_id) {
|
|
case SPECIAL_LABEL_EXCEPTION:
|
|
cglobs->cint.exception_handler = 0L;
|
|
break;
|
|
case SPECIAL_LABEL_SUCCESS:
|
|
cglobs->cint.success_handler = 0L;
|
|
break;
|
|
case SPECIAL_LABEL_FAILURE:
|
|
cglobs->cint.failure_handler = 0L;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
c_goal(Term Goal, Term mod, compiler_struct *cglobs)
|
|
{
|
|
Functor f;
|
|
PredEntry *p;
|
|
Prop p0;
|
|
|
|
if (IsVarTerm(Goal)) {
|
|
Goal = Yap_MkApplTerm(FunctorCall, 1, &Goal);
|
|
}
|
|
if (IsApplTerm(Goal) && FunctorOfTerm(Goal) == FunctorModule) {
|
|
Term M = ArgOfTerm(1, Goal);
|
|
|
|
if (IsVarTerm(M) || !IsAtomTerm(M)) {
|
|
CACHE_REGS
|
|
if (IsVarTerm(M)) {
|
|
LOCAL_Error_TYPE = INSTANTIATION_ERROR;
|
|
} else {
|
|
LOCAL_Error_TYPE = TYPE_ERROR_ATOM;
|
|
}
|
|
LOCAL_Error_Term = M;
|
|
LOCAL_ErrorMessage = "in module name";
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch, COMPILER_ERR_BOTCH);
|
|
}
|
|
Goal = ArgOfTerm(2, Goal);
|
|
mod = M;
|
|
}
|
|
if (IsVarTerm(Goal)) {
|
|
Goal = Yap_MkApplTerm(FunctorCall, 1, &Goal);
|
|
} else if (IsNumTerm(Goal)) {
|
|
CACHE_REGS
|
|
FAIL("goal can not be a number", TYPE_ERROR_CALLABLE, Goal);
|
|
} else if (IsRefTerm(Goal)) {
|
|
CACHE_REGS
|
|
LOCAL_Error_TYPE = TYPE_ERROR_DBREF;
|
|
LOCAL_Error_Term = Goal;
|
|
FAIL("goal argument in static procedure can not be a data base reference", TYPE_ERROR_CALLABLE, Goal);
|
|
}
|
|
else if (IsPairTerm(Goal)) {
|
|
Goal = Yap_MkApplTerm(FunctorCall, 1, &Goal);
|
|
}
|
|
if (IsAtomTerm(Goal)) {
|
|
Atom atom = AtomOfTerm(Goal);
|
|
|
|
if (atom == AtomFail || atom == AtomFalse) {
|
|
Yap_emit(fail_op, Zero, Zero, &cglobs->cint);
|
|
return;
|
|
}
|
|
else if (atom == AtomTrue || atom == AtomOtherwise) {
|
|
if (cglobs->onlast) {
|
|
Yap_emit(deallocate_op, Zero, Zero, &cglobs->cint);
|
|
#ifdef TABLING
|
|
PELOCK(41,cglobs->cint.CurrentPred);
|
|
if (is_tabled(cglobs->cint.CurrentPred))
|
|
Yap_emit(table_new_answer_op, Zero, cglobs->cint.CurrentPred->ArityOfPE, &cglobs->cint);
|
|
else
|
|
#endif /* TABLING */
|
|
Yap_emit(procceed_op, Zero, Zero, &cglobs->cint);
|
|
#ifdef TABLING
|
|
UNLOCK(cglobs->cint.CurrentPred->PELock);
|
|
#endif
|
|
}
|
|
return;
|
|
}
|
|
else if (atom == AtomCut) {
|
|
if (profiling)
|
|
Yap_emit(enter_profiling_op, (CELL)RepPredProp(PredPropByAtom(AtomCut,0)), Zero, &cglobs->cint);
|
|
else if (call_counting)
|
|
Yap_emit(count_call_op, (CELL)RepPredProp(PredPropByAtom(AtomCut,0)), Zero, &cglobs->cint);
|
|
if (cglobs->onlast) {
|
|
/* never a problem here with a -> b, !, c ; d */
|
|
Yap_emit(deallocate_op, Zero, Zero, &cglobs->cint);
|
|
#ifdef TABLING
|
|
PELOCK(42,cglobs->cint.CurrentPred);
|
|
if (is_tabled(cglobs->cint.CurrentPred)) {
|
|
Yap_emit_3ops(cut_op, Zero, Zero, Zero, &cglobs->cint);
|
|
/* needs to adjust previous commits */
|
|
Yap_emit(empty_call_op, Zero, Zero, &cglobs->cint);
|
|
Yap_emit(restore_tmps_and_skip_op, Zero, Zero, &cglobs->cint);
|
|
Yap_emit(table_new_answer_op, Zero, cglobs->cint.CurrentPred->ArityOfPE, &cglobs->cint);
|
|
}
|
|
else
|
|
#endif /* TABLING */
|
|
{
|
|
Yap_emit_3ops(cutexit_op, Zero, Zero, Zero, &cglobs->cint);
|
|
/* needs to adjust previous commits */
|
|
Yap_emit(empty_call_op, Zero, Zero, &cglobs->cint);
|
|
Yap_emit(restore_tmps_and_skip_op, Zero, Zero, &cglobs->cint);
|
|
Yap_emit(procceed_op, Zero, Zero, &cglobs->cint);
|
|
}
|
|
#ifdef TABLING
|
|
UNLOCK(cglobs->cint.CurrentPred->PELock);
|
|
#endif
|
|
}
|
|
else {
|
|
Yap_emit_3ops(cut_op, Zero, Zero, Zero, &cglobs->cint);
|
|
/* needs to adjust previous commits */
|
|
Yap_emit(empty_call_op, Zero, Zero, &cglobs->cint);
|
|
Yap_emit(restore_tmps_and_skip_op, Zero, Zero, &cglobs->cint);
|
|
adjust_current_commits(cglobs);
|
|
}
|
|
return;
|
|
}
|
|
#ifndef YAPOR
|
|
else if (atom == AtomRepeat) {
|
|
CELL l1 = ++cglobs->labelno;
|
|
CELL l2 = ++cglobs->labelno;
|
|
|
|
/* I need an either_me */
|
|
cglobs->needs_env = TRUE;
|
|
if (profiling)
|
|
Yap_emit(enter_profiling_op, (CELL)RepPredProp(PredPropByAtom(AtomRepeat,0)), Zero, &cglobs->cint);
|
|
else if (call_counting)
|
|
Yap_emit(count_call_op, (CELL)RepPredProp(PredPropByAtom(AtomRepeat,0)), Zero, &cglobs->cint);
|
|
cglobs->or_found = TRUE;
|
|
push_branch(cglobs->onbranch, TermNil, cglobs);
|
|
cglobs->curbranch++;
|
|
cglobs->onbranch = cglobs->curbranch;
|
|
if (cglobs->onlast)
|
|
Yap_emit(deallocate_op, Zero, Zero, &cglobs->cint);
|
|
Yap_emit_3ops(push_or_op, l1, Zero, Zero, &cglobs->cint);
|
|
Yap_emit_3ops(either_op, l1, Zero, Zero, &cglobs->cint);
|
|
Yap_emit(restore_tmps_op, Zero, Zero, &cglobs->cint);
|
|
Yap_emit(jump_op, l2, Zero, &cglobs->cint);
|
|
Yap_emit(label_op, l1, Zero, &cglobs->cint);
|
|
Yap_emit(pushpop_or_op, Zero, Zero, &cglobs->cint);
|
|
Yap_emit_3ops(orelse_op, l1, Zero, Zero, &cglobs->cint);
|
|
Yap_emit(label_op, l2, Zero, &cglobs->cint);
|
|
if (cglobs->onlast) {
|
|
#ifdef TABLING
|
|
PELOCK(43,cglobs->cint.CurrentPred);
|
|
if (is_tabled(cglobs->cint.CurrentPred))
|
|
Yap_emit(table_new_answer_op, Zero, cglobs->cint.CurrentPred->ArityOfPE, &cglobs->cint);
|
|
else
|
|
#endif /* TABLING */
|
|
Yap_emit(procceed_op, Zero, Zero, &cglobs->cint);
|
|
#ifdef TABLING
|
|
UNLOCK(cglobs->cint.CurrentPred->PELock);
|
|
#endif
|
|
}
|
|
else
|
|
++cglobs->goalno;
|
|
cglobs->onbranch = pop_branch(cglobs);
|
|
Yap_emit(pop_or_op, Zero, Zero, &cglobs->cint);
|
|
/* --cglobs->onbranch; */
|
|
return;
|
|
}
|
|
#endif /* YAPOR */
|
|
p = RepPredProp(p0 = Yap_PredPropByAtomNonThreadLocal(atom, mod));
|
|
if (EndOfPAEntr(p0)) {
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch, OUT_OF_HEAP_BOTCH);
|
|
}
|
|
/* if we are profiling, make sure we register we entered this predicate */
|
|
if (profiling)
|
|
Yap_emit(enter_profiling_op, (CELL)p, Zero, &cglobs->cint);
|
|
if (call_counting)
|
|
Yap_emit(count_call_op, (CELL)p, Zero, &cglobs->cint);
|
|
}
|
|
else {
|
|
f = FunctorOfTerm(Goal);
|
|
p = RepPredProp(p0 = Yap_PredPropByFunctorNonThreadLocal(f, mod));
|
|
if (EndOfPAEntr(p0)) {
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch, OUT_OF_HEAP_BOTCH);
|
|
}
|
|
if (f == FunctorOr || f == FunctorVBar) {
|
|
Term arg;
|
|
CELL l = ++cglobs->labelno;
|
|
CELL m = ++cglobs->labelno;
|
|
int save = cglobs->onlast;
|
|
int savegoalno = cglobs->goalno;
|
|
int frst = TRUE;
|
|
int commitflag = 0;
|
|
int looking_at_commit = FALSE;
|
|
int optimizing_commit = FALSE;
|
|
Term commitvar = 0;
|
|
PInstr *FirstP = cglobs->cint.cpc, *savecpc, *savencpc;
|
|
|
|
push_branch(cglobs->onbranch, TermNil, cglobs);
|
|
++cglobs->curbranch;
|
|
cglobs->onbranch = cglobs->curbranch;
|
|
cglobs->or_found = TRUE;
|
|
do {
|
|
arg = ArgOfTerm(1, Goal);
|
|
looking_at_commit = IsApplTerm(arg) &&
|
|
FunctorOfTerm(arg) == FunctorArrow;
|
|
if (frst) {
|
|
if (optimizing_commit) {
|
|
Yap_emit(label_op, l, Zero, &cglobs->cint);
|
|
l = ++cglobs->labelno;
|
|
}
|
|
Yap_emit_3ops(push_or_op, l, Zero, Zero, &cglobs->cint);
|
|
if (looking_at_commit &&
|
|
Yap_is_a_test_pred(ArgOfTerm(1, arg), mod)) {
|
|
/*
|
|
* let them think they are still the
|
|
* first
|
|
*/
|
|
// Yap_emit(commit_opt_op, l, Zero, &cglobs->cint);
|
|
optimizing_commit = TRUE;
|
|
Yap_emit_3ops(label_ctl_op, SPECIAL_LABEL_INIT, SPECIAL_LABEL_FAILURE, l, &cglobs->cint);
|
|
}
|
|
else {
|
|
optimizing_commit = FALSE;
|
|
cglobs->needs_env = TRUE;
|
|
Yap_emit_3ops(either_op, l, Zero, Zero, &cglobs->cint);
|
|
Yap_emit(restore_tmps_op, Zero, Zero, &cglobs->cint);
|
|
frst = FALSE;
|
|
}
|
|
}
|
|
else {
|
|
optimizing_commit = FALSE;
|
|
Yap_emit(label_op, l, Zero, &cglobs->cint);
|
|
Yap_emit(pushpop_or_op, Zero, Zero, &cglobs->cint);
|
|
Yap_emit_3ops(orelse_op, l = ++cglobs->labelno, Zero, Zero, &cglobs->cint);
|
|
cglobs->needs_env = TRUE;
|
|
}
|
|
/*
|
|
* if(IsApplTerm(arg) &&
|
|
* FunctorOfTerm(arg)==FunctorArrow) {
|
|
*/
|
|
if (looking_at_commit) {
|
|
if (!optimizing_commit && !commitflag) {
|
|
CACHE_REGS
|
|
/* This instruction is placed before
|
|
* the disjunction. This means that
|
|
* the program counter must point
|
|
* correctly, and also that the age
|
|
* of variable is older than the
|
|
* current branch.
|
|
*/
|
|
int my_goalno = cglobs->goalno;
|
|
|
|
cglobs->goalno = savegoalno;
|
|
commitflag = cglobs->labelno;
|
|
commitvar = MkVarTerm();
|
|
if (HR == (CELL *)cglobs->cint.freep0) {
|
|
/* oops, too many new variables */
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,OUT_OF_TEMPS_BOTCH);
|
|
}
|
|
savecpc = cglobs->cint.cpc;
|
|
savencpc = FirstP->nextInst;
|
|
cglobs->cint.cpc = FirstP;
|
|
cglobs->onbranch = pop_branch(cglobs);
|
|
c_var(commitvar, save_b_flag, 1, 0, cglobs);
|
|
push_branch(cglobs->onbranch, commitvar, cglobs);
|
|
cglobs->onbranch = cglobs->curbranch;
|
|
cglobs->cint.cpc->nextInst = savencpc;
|
|
cglobs->cint.cpc = savecpc;
|
|
cglobs->goalno = my_goalno;
|
|
}
|
|
save = cglobs->onlast;
|
|
cglobs->onlast = FALSE;
|
|
c_goal(ArgOfTerm(1, arg), mod, cglobs);
|
|
if (!optimizing_commit) {
|
|
c_var((Term) commitvar, commit_b_flag,
|
|
1, 0, cglobs);
|
|
} else {
|
|
Yap_emit_3ops(label_ctl_op, SPECIAL_LABEL_CLEAR, SPECIAL_LABEL_FAILURE, l, &cglobs->cint);
|
|
}
|
|
cglobs->onlast = save;
|
|
c_goal(ArgOfTerm(2, arg), mod, cglobs);
|
|
}
|
|
else {
|
|
/* standard disjunction */
|
|
c_goal(ArgOfTerm(1, Goal), mod, cglobs);
|
|
}
|
|
if (!cglobs->onlast) {
|
|
Yap_emit(jump_op, m, Zero, &cglobs->cint);
|
|
} else {
|
|
|
|
}
|
|
if (!optimizing_commit || !cglobs->onlast) {
|
|
cglobs->goalno = savegoalno + 1;
|
|
}
|
|
Goal = ArgOfTerm(2, Goal);
|
|
++cglobs->curbranch;
|
|
cglobs->onbranch = cglobs->curbranch;
|
|
} while (IsNonVarTerm(Goal) && IsApplTerm(Goal)
|
|
&& (FunctorOfTerm(Goal) == FunctorOr
|
|
|| FunctorOfTerm(Goal) == FunctorVBar));
|
|
Yap_emit(pushpop_or_op, Zero, Zero, &cglobs->cint);
|
|
Yap_emit(label_op, l, Zero, &cglobs->cint);
|
|
if (!optimizing_commit) {
|
|
Yap_emit(orlast_op, Zero, Zero, &cglobs->cint);
|
|
} else {
|
|
optimizing_commit = FALSE; /* not really necessary */
|
|
}
|
|
c_goal(Goal, mod, cglobs);
|
|
/* --cglobs->onbranch; */
|
|
cglobs->onbranch = pop_branch(cglobs);
|
|
if (!cglobs->onlast) {
|
|
Yap_emit(label_op, m, Zero, &cglobs->cint);
|
|
if ((cglobs->onlast = save))
|
|
c_goal(MkAtomTerm(AtomTrue), mod, cglobs);
|
|
}
|
|
Yap_emit(pop_or_op, Zero, Zero, &cglobs->cint);
|
|
return;
|
|
}
|
|
else if (f == FunctorComma) {
|
|
int save = cglobs->onlast;
|
|
Term t2 = ArgOfTerm(2, Goal);
|
|
|
|
cglobs->onlast = FALSE;
|
|
c_goal(ArgOfTerm(1, Goal), mod, cglobs);
|
|
cglobs->onlast = save;
|
|
c_goal(t2, mod, cglobs);
|
|
return;
|
|
}
|
|
else if (f == FunctorNot || f == FunctorAltNot) {
|
|
CACHE_REGS
|
|
CELL label = (cglobs->labelno += 2);
|
|
CELL end_label = (cglobs->labelno += 2);
|
|
int save = cglobs->onlast;
|
|
Term commitvar;
|
|
|
|
/* for now */
|
|
cglobs->needs_env = TRUE;
|
|
commitvar = MkVarTerm();
|
|
if (HR == (CELL *)cglobs->cint.freep0) {
|
|
/* oops, too many new variables */
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,OUT_OF_TEMPS_BOTCH);
|
|
}
|
|
push_branch(cglobs->onbranch, commitvar, cglobs);
|
|
++cglobs->curbranch;
|
|
cglobs->onbranch = cglobs->curbranch;
|
|
cglobs->or_found = TRUE;
|
|
cglobs->onlast = FALSE;
|
|
c_var(commitvar, save_b_flag, 1, 0, cglobs);
|
|
Yap_emit_3ops(push_or_op, label, Zero, Zero, &cglobs->cint);
|
|
Yap_emit_3ops(either_op, label, Zero, Zero, &cglobs->cint);
|
|
Yap_emit(restore_tmps_op, Zero, Zero, &cglobs->cint);
|
|
c_goal(ArgOfTerm(1, Goal), mod, cglobs);
|
|
c_var(commitvar, commit_b_flag, 1, 0, cglobs);
|
|
cglobs->onlast = save;
|
|
Yap_emit(fail_op, end_label, Zero, &cglobs->cint);
|
|
Yap_emit(pushpop_or_op, Zero, Zero, &cglobs->cint);
|
|
Yap_emit(label_op, label, Zero, &cglobs->cint);
|
|
Yap_emit(orlast_op, Zero, Zero, &cglobs->cint);
|
|
Yap_emit(label_op, end_label, Zero, &cglobs->cint);
|
|
cglobs->onlast = save;
|
|
/* --cglobs->onbranch; */
|
|
cglobs->onbranch = pop_branch(cglobs);
|
|
c_goal(MkAtomTerm(AtomTrue), mod, cglobs);
|
|
++cglobs->goalno;
|
|
Yap_emit(pop_or_op, Zero, Zero, &cglobs->cint);
|
|
return;
|
|
}
|
|
else if (f == FunctorArrow) {
|
|
CACHE_REGS
|
|
Term commitvar;
|
|
int save = cglobs->onlast;
|
|
|
|
commitvar = MkVarTerm();
|
|
if (HR == (CELL *)cglobs->cint.freep0) {
|
|
/* oops, too many new variables */
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,OUT_OF_TEMPS_BOTCH);
|
|
}
|
|
cglobs->onlast = FALSE;
|
|
c_var(commitvar, save_b_flag, 1, 0, cglobs);
|
|
c_goal(ArgOfTerm(1, Goal), mod, cglobs);
|
|
c_var(commitvar, commit_b_flag, 1, 0, cglobs);
|
|
cglobs->onlast = save;
|
|
c_goal(ArgOfTerm(2, Goal), mod, cglobs);
|
|
return;
|
|
}
|
|
else if (f == FunctorEq) {
|
|
if (profiling)
|
|
Yap_emit(enter_profiling_op, (CELL)p, Zero, &cglobs->cint);
|
|
else if (call_counting)
|
|
Yap_emit(count_call_op, (CELL)p, Zero, &cglobs->cint);
|
|
c_eq(ArgOfTerm(1, Goal), ArgOfTerm(2, Goal), cglobs);
|
|
if (cglobs->onlast) {
|
|
Yap_emit(deallocate_op, Zero, Zero, &cglobs->cint);
|
|
#ifdef TABLING
|
|
PELOCK(44,cglobs->cint.CurrentPred);
|
|
if (is_tabled(cglobs->cint.CurrentPred))
|
|
Yap_emit(table_new_answer_op, Zero, cglobs->cint.CurrentPred->ArityOfPE, &cglobs->cint);
|
|
else
|
|
#endif /* TABLING */
|
|
Yap_emit(procceed_op, Zero, Zero, &cglobs->cint);
|
|
#ifdef TABLING
|
|
UNLOCK(cglobs->cint.CurrentPred->PELock);
|
|
#endif
|
|
}
|
|
return;
|
|
}
|
|
else if (f == FunctorSafe) {
|
|
Ventry *v = (Ventry *)ArgOfTerm(1, Goal);
|
|
/* This variable must be known before */
|
|
v->FlagsOfVE |= SafeVar;
|
|
return;
|
|
}
|
|
else if (p->PredFlags & (AsmPredFlag)) {
|
|
basic_preds op = p->PredFlags & 0x7f;
|
|
if (profiling)
|
|
Yap_emit(enter_profiling_op, (CELL)p, Zero, &cglobs->cint);
|
|
else if (call_counting)
|
|
Yap_emit(count_call_op, (CELL)p, Zero, &cglobs->cint);
|
|
if (op >= _atom && op <= _primitive) {
|
|
c_test(op, ArgOfTerm(1, Goal), cglobs);
|
|
if (cglobs->onlast) {
|
|
Yap_emit(deallocate_op, Zero, Zero, &cglobs->cint);
|
|
#ifdef TABLING
|
|
PELOCK(45,cglobs->cint.CurrentPred);
|
|
if (is_tabled(cglobs->cint.CurrentPred))
|
|
Yap_emit(table_new_answer_op, Zero, cglobs->cint.CurrentPred->ArityOfPE, &cglobs->cint);
|
|
else
|
|
#endif /* TABLING */
|
|
Yap_emit(procceed_op, Zero, Zero, &cglobs->cint);
|
|
#ifdef TABLING
|
|
UNLOCK(cglobs->cint.CurrentPred->PELock);
|
|
#endif
|
|
}
|
|
return;
|
|
}
|
|
else if (op >= _plus && op <= _functor) {
|
|
if (profiling)
|
|
Yap_emit(enter_profiling_op, (CELL)p, Zero, &cglobs->cint);
|
|
else if (call_counting)
|
|
Yap_emit(count_call_op, (CELL)p, Zero, &cglobs->cint);
|
|
if (op == _functor) {
|
|
c_functor(Goal, mod, cglobs);
|
|
}
|
|
else {
|
|
c_bifun(op,
|
|
ArgOfTerm(1, Goal),
|
|
ArgOfTerm(2, Goal),
|
|
ArgOfTerm(3, Goal),
|
|
Goal,
|
|
mod,
|
|
cglobs);
|
|
}
|
|
if (cglobs->onlast) {
|
|
Yap_emit(deallocate_op, Zero, Zero, &cglobs->cint);
|
|
#ifdef TABLING
|
|
PELOCK(46,cglobs->cint.CurrentPred);
|
|
if (is_tabled(cglobs->cint.CurrentPred))
|
|
Yap_emit(table_new_answer_op, Zero, cglobs->cint.CurrentPred->ArityOfPE, &cglobs->cint);
|
|
else
|
|
#endif /* TABLING */
|
|
Yap_emit(procceed_op, Zero, Zero, &cglobs->cint);
|
|
#ifdef TABLING
|
|
UNLOCK(cglobs->cint.CurrentPred->PELock);
|
|
#endif
|
|
}
|
|
return;
|
|
} else if (op == _p_label_ctl) {
|
|
emit_special_label(Goal, cglobs);
|
|
return;
|
|
} else {
|
|
c_args(Goal, 0, cglobs);
|
|
}
|
|
}
|
|
#ifdef BEAM
|
|
else if (p->PredFlags & BinaryPredFlag && !EAM) {
|
|
#else
|
|
else if (p->PredFlags & BinaryPredFlag ) {
|
|
#endif
|
|
CACHE_REGS
|
|
Term a1 = ArgOfTerm(1,Goal);
|
|
|
|
if (IsVarTerm(a1) && !IsNewVar(a1)) {
|
|
Term a2 = ArgOfTerm(2,Goal);
|
|
if (IsVarTerm(a2) && !IsNewVar(a2)) {
|
|
cglobs->current_p0 = p0;
|
|
c_2vars(bt_flag, a1, 0, a2, 0, (CELL)p0, 0, 0, cglobs);
|
|
}
|
|
else {
|
|
Term t2 = MkVarTerm();
|
|
//c_var(t2, --cglobs->tmpreg, 0, 0, cglobs);
|
|
if (HR == (CELL *)cglobs->cint.freep0) {
|
|
/* oops, too many new variables */
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,OUT_OF_TEMPS_BOTCH);
|
|
}
|
|
cglobs->current_p0 = p0;
|
|
c_eq(t2, a2, cglobs);
|
|
c_2vars(bt_flag, a1, 0, t2, 0, (CELL)p0, 0, 0, cglobs);
|
|
}
|
|
} else {
|
|
Term a2 = ArgOfTerm(2,Goal);
|
|
Term t1 = MkVarTerm();
|
|
//c_var(t1, --cglobs->tmpreg, 0, 0, cglobs);
|
|
if (HR == (CELL *)cglobs->cint.freep0) {
|
|
/* oops, too many new variables */
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,OUT_OF_TEMPS_BOTCH);
|
|
}
|
|
c_eq(t1, a1, cglobs);
|
|
|
|
if (IsVarTerm(a2) && !IsNewVar(a2)) {
|
|
cglobs->current_p0 = p0;
|
|
c_2vars(bt_flag, t1, 0, a2, 0, (CELL)p0, 0, 0, cglobs);
|
|
}
|
|
else {
|
|
Term t2 = MkVarTerm();
|
|
// c_var(t2, --cglobs->tmpreg, 0, 0, cglobs);
|
|
if (HR == (CELL *)cglobs->cint.freep0) {
|
|
/* oops, too many new variables */
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch,OUT_OF_TEMPS_BOTCH);
|
|
}
|
|
c_eq(t2, a2, cglobs);
|
|
cglobs->current_p0 = p0;
|
|
c_2vars(bt_flag, t1, 0, t2, 0, (CELL)p0, 0, 0, cglobs);
|
|
}
|
|
}
|
|
if (cglobs->onlast) {
|
|
Yap_emit(deallocate_op, Zero, Zero, &cglobs->cint);
|
|
#ifdef TABLING
|
|
PELOCK(47,cglobs->cint.CurrentPred);
|
|
if (is_tabled(cglobs->cint.CurrentPred))
|
|
Yap_emit(table_new_answer_op, Zero, cglobs->cint.CurrentPred->ArityOfPE, &cglobs->cint);
|
|
else
|
|
#endif /* TABLING */
|
|
Yap_emit(procceed_op, Zero, Zero, &cglobs->cint);
|
|
#ifdef TABLING
|
|
UNLOCK(cglobs->cint.CurrentPred->PELock);
|
|
#endif
|
|
}
|
|
return;
|
|
} else {
|
|
if (profiling)
|
|
Yap_emit(enter_profiling_op, (CELL)p, Zero, &cglobs->cint);
|
|
else if (call_counting)
|
|
Yap_emit(count_call_op, (CELL)p, Zero, &cglobs->cint);
|
|
if (f == FunctorExecuteInMod) {
|
|
/* compile the first argument only */
|
|
c_arg(1, ArgOfTerm(1,Goal), 1, 0, cglobs);
|
|
} else {
|
|
c_args(Goal, 0, cglobs);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (p->PredFlags & SafePredFlag
|
|
#ifdef YAPOR
|
|
/* synchronisation means saving the state, so it is never safe in YAPOR */
|
|
&& !(p->PredFlags & SyncPredFlag)
|
|
#endif /* YAPOR */
|
|
) {
|
|
Yap_emit(safe_call_op, (CELL) p0, Zero, &cglobs->cint);
|
|
if (cglobs->onlast) {
|
|
Yap_emit(deallocate_op, Zero, Zero, &cglobs->cint);
|
|
#ifdef TABLING
|
|
PELOCK(48,cglobs->cint.CurrentPred);
|
|
if (is_tabled(cglobs->cint.CurrentPred))
|
|
Yap_emit(table_new_answer_op, Zero, cglobs->cint.CurrentPred->ArityOfPE, &cglobs->cint);
|
|
else
|
|
#endif /* TABLING */
|
|
Yap_emit(procceed_op, Zero, Zero, &cglobs->cint);
|
|
#ifdef TABLING
|
|
UNLOCK(cglobs->cint.CurrentPred->PELock);
|
|
#endif
|
|
}
|
|
}
|
|
else {
|
|
if ((p->PredFlags & (AsmPredFlag |
|
|
ModuleTransparentPredFlag |
|
|
UserCPredFlag)) ||
|
|
p->FunctorOfPred == FunctorExecuteInMod) {
|
|
#ifdef YAPOR
|
|
if (p->PredFlags & SyncPredFlag)
|
|
Yap_emit(sync_op, (CELL)p, (CELL)(p->ArityOfPE), &cglobs->cint);
|
|
#endif /* YAPOR */
|
|
if (p->FunctorOfPred == FunctorExecuteInMod) {
|
|
cglobs->needs_env = TRUE;
|
|
Yap_emit_4ops(call_op, (CELL) p0, Zero, Zero, ArgOfTerm(2,Goal), &cglobs->cint);
|
|
} else {
|
|
cglobs->needs_env = TRUE;
|
|
Yap_emit_3ops(call_op, (CELL) p0, Zero, Zero, &cglobs->cint);
|
|
}
|
|
/* functor is allowed to call the garbage collector */
|
|
if (cglobs->onlast) {
|
|
Yap_emit(deallocate_op, Zero, Zero, &cglobs->cint);
|
|
cglobs->or_found = TRUE;
|
|
#ifdef TABLING
|
|
PELOCK(49,cglobs->cint.CurrentPred);
|
|
if (is_tabled(cglobs->cint.CurrentPred))
|
|
Yap_emit(table_new_answer_op, Zero, cglobs->cint.CurrentPred->ArityOfPE, &cglobs->cint);
|
|
else
|
|
#endif /* TABLING */
|
|
Yap_emit(procceed_op, Zero, Zero, &cglobs->cint);
|
|
#ifdef TABLING
|
|
UNLOCK(cglobs->cint.CurrentPred->PELock);
|
|
#endif
|
|
}
|
|
}
|
|
else {
|
|
if (cglobs->onlast) {
|
|
Yap_emit(deallocate_op, Zero, Zero, &cglobs->cint);
|
|
#ifdef TABLING
|
|
PELOCK(50,cglobs->cint.CurrentPred);
|
|
if (is_tabled(cglobs->cint.CurrentPred)) {
|
|
cglobs->needs_env = TRUE;
|
|
Yap_emit_3ops(call_op, (CELL) p0, Zero, Zero, &cglobs->cint);
|
|
Yap_emit(table_new_answer_op, Zero, cglobs->cint.CurrentPred->ArityOfPE, &cglobs->cint);
|
|
}
|
|
else
|
|
#endif /* TABLING */
|
|
Yap_emit(execute_op, (CELL) p0, Zero, &cglobs->cint);
|
|
#ifdef TABLING
|
|
UNLOCK(cglobs->cint.CurrentPred->PELock);
|
|
#endif
|
|
}
|
|
else {
|
|
cglobs->needs_env = TRUE;
|
|
Yap_emit_3ops(call_op, (CELL) p0, Zero, Zero, &cglobs->cint);
|
|
}
|
|
}
|
|
if (!cglobs->onlast)
|
|
++cglobs->goalno;
|
|
}
|
|
}
|
|
|
|
static void
|
|
c_body(Term Body, Term mod, compiler_struct *cglobs)
|
|
{
|
|
cglobs->onhead = FALSE;
|
|
cglobs->BodyStart = cglobs->cint.cpc;
|
|
cglobs->goalno = 1;
|
|
while (IsNonVarTerm(Body) && IsApplTerm(Body)
|
|
&& FunctorOfTerm(Body) == FunctorComma) {
|
|
Term t2 = ArgOfTerm(2, Body);
|
|
if (!cglobs->cint.success_handler && IsTrueGoal(t2)) {
|
|
/* optimise the case where some idiot left trues at the end
|
|
of the clause.
|
|
*/
|
|
Body = ArgOfTerm(1, Body);
|
|
break;
|
|
}
|
|
c_goal(ArgOfTerm(1, Body), mod, cglobs);
|
|
Body = t2;
|
|
#ifdef BEAM
|
|
if (EAM) Yap_emit(endgoal_op, Zero, Zero, &cglobs->cint);
|
|
#endif
|
|
|
|
}
|
|
cglobs->onlast = TRUE;
|
|
c_goal(Body, mod, cglobs);
|
|
#ifdef BEAM
|
|
if (EAM && cglobs->goalno > 1) {
|
|
if (cglobs->cint.cpc->op==procceed_op) {
|
|
cglobs->cint.cpc->op=endgoal_op;
|
|
Yap_emit(procceed_op, Zero, Zero, &cglobs->cint);
|
|
} else
|
|
Yap_emit(endgoal_op, Zero, Zero, &cglobs->cint);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
c_head(Term t, compiler_struct *cglobs)
|
|
{
|
|
Functor f;
|
|
|
|
cglobs->goalno = 0;
|
|
cglobs->onhead = TRUE;
|
|
cglobs->onlast = FALSE;
|
|
cglobs->curbranch = cglobs->onbranch = 0;
|
|
cglobs->branch_pointer = cglobs->parent_branches;
|
|
cglobs->space_used = 0;
|
|
cglobs->space_op = NULL;
|
|
if (IsAtomTerm(t)) {
|
|
Yap_emit(name_op, (CELL) AtomOfTerm(t), Zero, &cglobs->cint);
|
|
#ifdef BEAM
|
|
if (EAM) {
|
|
Yap_emit(run_op,Zero,(UInt) cglobs->cint.CurrentPred,&cglobs->cint);
|
|
}
|
|
#endif
|
|
Yap_emit(ensure_space_op, Zero , Zero, &cglobs->cint);
|
|
cglobs->space_op = cglobs->cint.cpc;
|
|
return;
|
|
}
|
|
f = FunctorOfTerm(t);
|
|
Yap_emit(name_op, (CELL) NameOfFunctor(f), ArityOfFunctor(f), &cglobs->cint);
|
|
#ifdef BEAM
|
|
if (EAM) {
|
|
Yap_emit(run_op,Zero,(UInt) cglobs->cint.CurrentPred,&cglobs->cint);
|
|
}
|
|
#endif
|
|
if (Yap_ExecutionMode == MIXED_MODE_USER)
|
|
Yap_emit(native_op, 0, 0, &cglobs->cint);
|
|
Yap_emit(ensure_space_op, Zero , Zero, &cglobs->cint);
|
|
cglobs->space_op = cglobs->cint.cpc;
|
|
#ifdef BEAM
|
|
if (EAM) {
|
|
Yap_emit(run_op,Zero,(UInt) cglobs->cint.CurrentPred,&cglobs->cint);
|
|
}
|
|
#endif
|
|
if (Yap_ExecutionMode == MIXED_MODE || Yap_ExecutionMode == COMPILED)
|
|
#if YAP_JIT
|
|
Yap_emit(native_op, 0, 0, &cglobs->cint);
|
|
#else
|
|
{
|
|
if (Yap_ExecutionMode == MIXED_MODE)
|
|
Yap_NilError(NOJIT_ERROR, "mixed");
|
|
else /* Yap_ExecutionMode == COMPILED */
|
|
Yap_NilError(NOJIT_ERROR, "just compiled");
|
|
}
|
|
#endif
|
|
c_args(t, 0, cglobs);
|
|
}
|
|
|
|
|
|
inline static bool
|
|
usesvar(compiler_vm_op ic)
|
|
{
|
|
if (ic >= get_var_op && ic <= put_val_op)
|
|
return true;
|
|
switch (ic) {
|
|
case save_b_op:
|
|
case commit_b_op:
|
|
case patch_b_op:
|
|
case save_appl_op:
|
|
case save_pair_op:
|
|
case f_val_op:
|
|
case f_var_op:
|
|
case bccall_op:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
#ifdef SFUNC
|
|
if (ic >= unify_s_var_op && ic <= write_s_val_op)
|
|
return true;
|
|
#endif
|
|
return ((ic >= unify_var_op && ic <= write_val_op)
|
|
||
|
|
(ic >= unify_last_var_op && ic <= unify_last_val_op));
|
|
}
|
|
|
|
/*
|
|
inline static bool
|
|
uses_this_var(PInstr *pc, Term arg)
|
|
{
|
|
compiler_vm_op ic = pc->op;
|
|
|
|
if (pc->rnd1 != arg)
|
|
return arg == pc->rnd3 && ic == bccall_op;
|
|
return usesvar( ic );
|
|
}
|
|
*/
|
|
|
|
inline static bool
|
|
usesvar2(compiler_vm_op ic)
|
|
{
|
|
return ic == bccall_op;
|
|
}
|
|
|
|
/*
|
|
* Do as in the traditional WAM and make sure voids are in
|
|
* environments
|
|
*/
|
|
#define LOCALISE_VOIDS 1
|
|
|
|
#ifdef LOCALISE_VOIDS
|
|
typedef struct env_tmp {
|
|
Ventry * Var;
|
|
struct env_tmp *Next;
|
|
} EnvTmp;
|
|
#endif
|
|
|
|
|
|
static void
|
|
tag_use(Ventry *v USES_REGS)
|
|
{
|
|
#ifdef BEAM
|
|
if (EAM) {
|
|
if (v->NoOfVE == Unassigned || v->KindOfVE!=PermVar) {
|
|
v->NoOfVE = PermVar | (LOCAL_nperm++);
|
|
v->KindOfVE = PermVar;
|
|
v->FlagsOfVE |= PermFlag;
|
|
}
|
|
}
|
|
#endif
|
|
if (v->NoOfVE == Unassigned) {
|
|
if ((v->AgeOfVE > 1 && (v->AgeOfVE > v->FirstOfVE))
|
|
|| v->KindOfVE == PermVar /*
|
|
* * || (v->FlagsOfVE & NonVoid && !(v->FlagsOfVE &
|
|
* * OnHeadFlag))
|
|
*/ ) {
|
|
v->NoOfVE = PermVar | (LOCAL_nperm++);
|
|
v->KindOfVE = PermVar;
|
|
v->FlagsOfVE |= PermFlag;
|
|
} else {
|
|
v->NoOfVE = v->KindOfVE = TempVar;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
AssignPerm(PInstr *pc, compiler_struct *cglobs)
|
|
{
|
|
CACHE_REGS
|
|
int uses_var;
|
|
PInstr *opc = NULL;
|
|
#ifdef LOCALISE_VOIDS
|
|
EnvTmp *EnvTmps = NULL;
|
|
#endif
|
|
|
|
/* The WAM tries to keep voids on the
|
|
* environment. Traditionally, YAP liberally globalises
|
|
* voids.
|
|
*
|
|
* The new version goes to some length to keep void variables
|
|
* in environments, but it is dubious that improves
|
|
* performance, and may actually slow down the system
|
|
*/
|
|
while (pc != NULL) {
|
|
PInstr *tpc = pc->nextInst;
|
|
#ifdef LOCALISE_VOIDS
|
|
if (pc->op == put_var_op) {
|
|
Ventry *v = (Ventry *) (pc->rnd1);
|
|
if (v->AgeOfVE == v->FirstOfVE
|
|
&& !(v->FlagsOfVE & (GlobalVal|OnHeadFlag|OnLastGoal|NonVoid)) ) {
|
|
EnvTmp *x = (EnvTmp *)Yap_AllocCMem(sizeof(*x), &cglobs->cint);
|
|
x->Next = EnvTmps;
|
|
x->Var = v;
|
|
EnvTmps = x;
|
|
}
|
|
} else
|
|
#endif
|
|
if (pc->op == call_op || pc->op == either_op || pc->op == orelse_op || pc->op == push_or_op) {
|
|
#ifdef LOCALISE_VOIDS
|
|
pc->ops.opseqt[1] = (CELL)EnvTmps;
|
|
if (EnvTmps)
|
|
EnvTmps = NULL;
|
|
#endif
|
|
}
|
|
pc->nextInst = opc;
|
|
opc = pc;
|
|
pc = tpc;
|
|
}
|
|
pc = opc;
|
|
opc = NULL;
|
|
do {
|
|
PInstr *npc = pc->nextInst;
|
|
|
|
pc->nextInst = opc;
|
|
uses_var = usesvar(pc->op);
|
|
if (uses_var) {
|
|
Ventry *v = (Ventry *) (pc->rnd1);
|
|
|
|
tag_use(v PASS_REGS);
|
|
if (usesvar2(pc->op) ) {
|
|
Ventry *v2 = (Ventry *) (pc->rnd3);
|
|
tag_use(v2 PASS_REGS);
|
|
}
|
|
|
|
} else if (pc->op == empty_call_op) {
|
|
pc->rnd2 = LOCAL_nperm;
|
|
} else if (pc->op == call_op || pc->op == either_op || pc->op == orelse_op || pc->op == push_or_op) {
|
|
#ifdef LOCALISE_VOIDS
|
|
EnvTmps = (EnvTmp *)(pc->ops.opseqt[1]);
|
|
while (EnvTmps) {
|
|
Ventry *v = EnvTmps->Var;
|
|
v->NoOfVE = PermVar | (LOCAL_nperm++);
|
|
v->KindOfVE = PermVar;
|
|
v->FlagsOfVE |= (PermFlag|SafeVar);
|
|
EnvTmps = EnvTmps->Next;
|
|
}
|
|
#endif
|
|
pc->rnd2 = LOCAL_nperm;
|
|
} else if (pc->op == cut_op ||
|
|
pc->op == cutexit_op ||
|
|
pc->op == commit_b_op) {
|
|
pc->rnd2 = LOCAL_nperm;
|
|
}
|
|
opc = pc;
|
|
pc = npc;
|
|
} while (pc != NULL);
|
|
}
|
|
|
|
static CELL *
|
|
init_bvarray(int nperm, compiler_struct *cglobs)
|
|
{
|
|
CELL *vinfo = NULL;
|
|
size_t sz = sizeof(CELL)*(1+nperm/(8*sizeof(CELL)));
|
|
vinfo = (CELL *)Yap_AllocCMem(sz, &cglobs->cint);
|
|
memset((void *)vinfo, 0, sz);
|
|
return vinfo;
|
|
}
|
|
|
|
static void
|
|
clear_bvarray(int var, CELL *bvarray
|
|
#ifdef DEBUG
|
|
, compiler_struct *cglobs
|
|
#endif
|
|
)
|
|
{
|
|
int max = 8*sizeof(CELL);
|
|
CELL nbit;
|
|
|
|
/* get to the array position */
|
|
while (var >= max) {
|
|
bvarray++;
|
|
var -= max;
|
|
}
|
|
/* now put a 0 on it, from now on the variable is initialised */
|
|
nbit = ((CELL)1 << var);
|
|
#ifdef DEBUG
|
|
if (*bvarray & nbit) {
|
|
CACHE_REGS
|
|
/* someone had already marked this variable: complain */
|
|
LOCAL_Error_TYPE = INTERNAL_COMPILER_ERROR;
|
|
LOCAL_Error_Term = TermNil;
|
|
LOCAL_ErrorMessage = "compiler internal error: variable initialised twice";
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch, COMPILER_ERR_BOTCH);
|
|
}
|
|
cglobs->pbvars++;
|
|
#endif
|
|
*bvarray |= nbit;
|
|
}
|
|
|
|
/* copy the current state of the perm variable state array to code space */
|
|
static void
|
|
add_bvarray_op(PInstr *cp, CELL *bvarray, int env_size, compiler_struct *cglobs)
|
|
{
|
|
int i, size = env_size/(8*sizeof(CELL));
|
|
CELL *dest;
|
|
|
|
dest =
|
|
Yap_emit_extra_size(mark_initialised_pvars_op, (CELL)env_size, (size+1)*sizeof(CELL), &cglobs->cint);
|
|
/* copy the cells to dest */
|
|
for (i = 0; i <= size; i++)
|
|
*dest++ = *bvarray++;
|
|
}
|
|
|
|
/* vsc: this code is not working, as it is too complex */
|
|
|
|
typedef struct {
|
|
int lab;
|
|
int last;
|
|
PInstr *pc;
|
|
} bventry;
|
|
|
|
#define MAX_DISJUNCTIONS 128
|
|
static bventry *bvstack;
|
|
static int bvindex = 0;
|
|
|
|
static void
|
|
push_bvmap(int label, PInstr *pcpc, compiler_struct *cglobs)
|
|
{
|
|
if (bvindex == MAX_DISJUNCTIONS) {
|
|
CACHE_REGS
|
|
LOCAL_Error_TYPE = INTERNAL_COMPILER_ERROR;
|
|
LOCAL_Error_Term = TermNil;
|
|
LOCAL_ErrorMessage = "Too many embedded disjunctions";
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch, COMPILER_ERR_BOTCH);
|
|
}
|
|
/* the label instruction */
|
|
bvstack[bvindex].lab = label;
|
|
bvstack[bvindex].last = -1;
|
|
/* where we have the code */
|
|
bvstack[bvindex].pc = pcpc;
|
|
bvindex++;
|
|
}
|
|
|
|
static void
|
|
reset_bvmap(CELL *bvarray, int nperm, compiler_struct *cglobs)
|
|
{
|
|
int size, size1, env_size, i;
|
|
CELL *source;
|
|
|
|
if (bvarray == NULL)
|
|
|
|
if (bvindex == 0) {
|
|
CACHE_REGS
|
|
LOCAL_Error_TYPE = INTERNAL_COMPILER_ERROR;
|
|
LOCAL_Error_Term = TermNil;
|
|
LOCAL_ErrorMessage = "No embedding in disjunctions";
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch, COMPILER_ERR_BOTCH);
|
|
}
|
|
env_size = (bvstack[bvindex-1].pc)->rnd1;
|
|
size = env_size/(8*sizeof(CELL));
|
|
size1 = nperm/(8*sizeof(CELL));
|
|
source = (bvstack[bvindex-1].pc)->arnds;
|
|
for (i = 0; i <= size; i++)
|
|
*bvarray++ = *source++;
|
|
for (i = size+1; i<= size1; i++)
|
|
*bvarray++ = (CELL)(0);
|
|
}
|
|
|
|
static void
|
|
pop_bvmap(CELL *bvarray, int nperm, compiler_struct *cglobs)
|
|
{
|
|
if (bvindex == 0) {
|
|
CACHE_REGS
|
|
LOCAL_Error_TYPE = INTERNAL_COMPILER_ERROR;
|
|
LOCAL_Error_Term = TermNil;
|
|
LOCAL_ErrorMessage = "Too few embedded disjunctions";
|
|
/* save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch, OUT_OF_HEAP_BOTCH); */
|
|
}
|
|
reset_bvmap(bvarray, nperm, cglobs);
|
|
bvindex--;
|
|
}
|
|
|
|
typedef struct {
|
|
PInstr *p;
|
|
Ventry *v;
|
|
} UnsafeEntry;
|
|
|
|
/* extend to also support variable usage bitmaps for garbage collection */
|
|
static void
|
|
CheckUnsafe(PInstr *pc, compiler_struct *cglobs)
|
|
{
|
|
CACHE_REGS
|
|
int pending = 0;
|
|
|
|
/* say that all variables are yet to initialise */
|
|
CELL *vstat = init_bvarray(LOCAL_nperm, cglobs);
|
|
UnsafeEntry *UnsafeStack =
|
|
(UnsafeEntry *) Yap_AllocCMem(LOCAL_nperm * sizeof(UnsafeEntry), &cglobs->cint);
|
|
/* keep a copy of previous cglobs->cint.cpc and CodeStart */
|
|
PInstr *opc = cglobs->cint.cpc;
|
|
PInstr *OldCodeStart = cglobs->cint.CodeStart;
|
|
|
|
cglobs->cint.CodeStart = cglobs->cint.BlobsStart;
|
|
cglobs->cint.cpc = cglobs->cint.icpc;
|
|
bvindex = 0;
|
|
bvstack = (bventry *)Yap_AllocCMem(MAX_DISJUNCTIONS * sizeof(bventry), &cglobs->cint);
|
|
while (pc != NIL) {
|
|
switch(pc->op) {
|
|
case put_val_op:
|
|
{
|
|
Ventry *v = (Ventry *) (pc->rnd1);
|
|
if ((v->FlagsOfVE & PermFlag) && !(v->FlagsOfVE & SafeVar)) {
|
|
UnsafeStack[pending].p = pc;
|
|
UnsafeStack[pending++].v = v;
|
|
v->FlagsOfVE |= SafeVar;
|
|
}
|
|
break;
|
|
}
|
|
case bccall_op:
|
|
{
|
|
Ventry *v = (Ventry *) (pc->rnd1),
|
|
*v3 = (Ventry *) (pc->rnd3);
|
|
|
|
if ( (v->FlagsOfVE & PermFlag && pc == v->FirstOpForV) ||
|
|
(v3->FlagsOfVE & PermFlag && pc == v3->FirstOpForV) ) {
|
|
CACHE_REGS
|
|
LOCAL_Error_TYPE = INTERNAL_COMPILER_ERROR;
|
|
LOCAL_Error_Term = TermNil;
|
|
LOCAL_ErrorMessage = "comparison should not have first instance of variables";
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch, COMPILER_ERR_BOTCH);
|
|
}
|
|
}
|
|
break;
|
|
case put_var_op:
|
|
case get_var_op:
|
|
case save_b_op:
|
|
case unify_var_op:
|
|
case unify_last_var_op:
|
|
case write_var_op:
|
|
case save_appl_op:
|
|
case save_pair_op:
|
|
case f_var_op:
|
|
{
|
|
Ventry *v = (Ventry *) (pc->rnd1);
|
|
|
|
if (v->FlagsOfVE & PermFlag && pc == v->FirstOpForV) {
|
|
/* the second condition covers cases such as save_b_op
|
|
in a disjunction */
|
|
clear_bvarray((v->NoOfVE & MaskVarAdrs), vstat
|
|
#ifdef DEBUG
|
|
, cglobs
|
|
#endif
|
|
);
|
|
}
|
|
}
|
|
break;
|
|
case push_or_op:
|
|
Yap_emit(label_op, ++cglobs->labelno, Zero, &cglobs->cint);
|
|
pc->ops.opseqt[1] = (CELL)cglobs->labelno;
|
|
add_bvarray_op(pc, vstat, pc->rnd2, cglobs);
|
|
push_bvmap((CELL)cglobs->labelno, cglobs->cint.cpc, cglobs);
|
|
break;
|
|
case either_op:
|
|
/* add a first entry to the array */
|
|
Yap_emit(label_op, ++cglobs->labelno, Zero, &cglobs->cint);
|
|
pc->ops.opseqt[1] = (CELL)cglobs->labelno;
|
|
add_bvarray_op(pc, vstat, pc->rnd2, cglobs);
|
|
break;
|
|
case pushpop_or_op:
|
|
reset_bvmap(vstat, LOCAL_nperm, cglobs);
|
|
goto reset_safe_map;
|
|
case orelse_op:
|
|
Yap_emit(label_op, ++cglobs->labelno, Zero, &cglobs->cint);
|
|
pc->ops.opseqt[1] = (CELL)cglobs->labelno;
|
|
add_bvarray_op(pc, vstat, pc->rnd2, cglobs);
|
|
break;
|
|
case pop_or_op:
|
|
pop_bvmap(vstat, LOCAL_nperm, cglobs);
|
|
goto reset_safe_map;
|
|
break;
|
|
case empty_call_op:
|
|
/* just get ourselves a label describing how
|
|
many permanent variables are alive */
|
|
Yap_emit(label_op, ++cglobs->labelno, Zero, &cglobs->cint);
|
|
pc->rnd1 = (CELL)cglobs->labelno;
|
|
add_bvarray_op(pc, vstat, pc->rnd2, cglobs);
|
|
break;
|
|
case cut_op:
|
|
case cutexit_op:
|
|
/* just get ourselves a label describing how
|
|
many permanent variables are alive */
|
|
Yap_emit(label_op, ++cglobs->labelno, Zero, &cglobs->cint);
|
|
pc->rnd1 = (CELL)cglobs->labelno;
|
|
add_bvarray_op(pc, vstat, pc->rnd2, cglobs);
|
|
break;
|
|
case call_op:
|
|
Yap_emit(label_op, ++cglobs->labelno, Zero, &cglobs->cint);
|
|
pc->ops.opseqt[1] = (CELL)cglobs->labelno;
|
|
add_bvarray_op(pc, vstat, pc->rnd2, cglobs);
|
|
case deallocate_op:
|
|
reset_safe_map:
|
|
{
|
|
int n = pc->op == call_op ? pc->rnd2 : 0;
|
|
int no;
|
|
|
|
while (pending) {
|
|
Ventry *v = UnsafeStack[--pending].v;
|
|
|
|
v->FlagsOfVE &= ~SafeVar;
|
|
no = (v->NoOfVE) & MaskVarAdrs;
|
|
if (no >= n)
|
|
UnsafeStack[pending].p->op = put_unsafe_op;
|
|
}
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
pc = pc->nextInst;
|
|
}
|
|
cglobs->cint.icpc = cglobs->cint.cpc;
|
|
cglobs->cint.cpc = opc;
|
|
cglobs->cint.BlobsStart = cglobs->cint.CodeStart;
|
|
cglobs->cint.CodeStart = OldCodeStart;
|
|
}
|
|
|
|
static void
|
|
CheckVoids(compiler_struct *cglobs)
|
|
{ /* establish voids in the head and initial
|
|
* uses */
|
|
Ventry *ve;
|
|
compiler_vm_op ic;
|
|
struct PSEUDO *cpc;
|
|
|
|
cpc = cglobs->cint.CodeStart;
|
|
while ((ic = cpc->op) != allocate_op) {
|
|
switch (ic) {
|
|
case get_var_op:
|
|
case unify_var_op:
|
|
case unify_last_var_op:
|
|
#ifdef SFUNC
|
|
case unify_s_var_op:
|
|
#endif
|
|
case save_pair_op:
|
|
case save_appl_op:
|
|
ve = ((Ventry *) cpc->rnd1);
|
|
if ((ve->FlagsOfVE & PermFlag) == 0 && ve->RCountOfVE <= 1) {
|
|
ve->NoOfVE = ve->KindOfVE = VoidVar;
|
|
if (ic == get_var_op || ic ==
|
|
save_pair_op || ic == save_appl_op
|
|
#ifdef SFUNC
|
|
|| ic == unify_s_var_op
|
|
#endif
|
|
) {
|
|
cpc->op = nop_op;
|
|
break;
|
|
}
|
|
}
|
|
if (ic != get_var_op)
|
|
break;
|
|
case get_val_op:
|
|
case get_atom_op:
|
|
case get_num_op:
|
|
case get_float_op:
|
|
case get_dbterm_op:
|
|
case get_longint_op:
|
|
case get_string_op:
|
|
case get_bigint_op:
|
|
case get_list_op:
|
|
case get_struct_op:
|
|
cglobs->Uses[cpc->rnd2] = 1;
|
|
break;
|
|
case bccall_op:
|
|
cglobs->Uses[cpc->rnd2] = 1;
|
|
cglobs->Uses[cpc->rnd4] = 1;
|
|
default:
|
|
break;
|
|
}
|
|
cpc = cpc->nextInst;
|
|
}
|
|
}
|
|
|
|
static int
|
|
checktemp(Int arg, Int rn, compiler_vm_op ic, compiler_struct *cglobs)
|
|
{
|
|
Ventry *v = (Ventry *) arg;
|
|
PInstr *q;
|
|
Int Needed[MaxTemps];
|
|
Int r, target1, target2;
|
|
Int n, *np, *rp;
|
|
CELL *cp;
|
|
Int vadr;
|
|
Int vreg;
|
|
|
|
|
|
cglobs->vadr = vadr = (v->NoOfVE);
|
|
cglobs->vreg = vreg = vadr & MaskVarAdrs;
|
|
if (v->KindOfVE == PermVar || v->KindOfVE == VoidVar)
|
|
return 0;
|
|
if (v->RCountOfVE == 1)
|
|
return 0;
|
|
if (vreg) {
|
|
--cglobs->Uses[vreg];
|
|
return 1;
|
|
}
|
|
/* follow the life of the variable */
|
|
q = cglobs->cint.cpc;
|
|
/*
|
|
* for(r=0; r<cglobs->MaxCTemps; ++r) Needed[r] = cglobs->Uses[r]; might be written
|
|
* as:
|
|
*/
|
|
np = Needed;
|
|
rp = cglobs->Uses;
|
|
for (r = 0; r < cglobs->MaxCTemps; ++r)
|
|
*np++ = *rp++;
|
|
if (rn > 0 && (ic == get_var_op || ic == put_var_op)) {
|
|
if (ic == put_var_op)
|
|
Needed[rn] = 1;
|
|
target1 = rn; /* try to leave it where it is */
|
|
}
|
|
else
|
|
target1 = cglobs->MaxCTemps;
|
|
target2 = cglobs->MaxCTemps;
|
|
n = v->RCountOfVE - 1;
|
|
while (q != v->LastOpForV && (q = q->nextInst) != NIL) {
|
|
if (q->rnd2 <= 0); /* don't try to reuse REGISTER 0 */
|
|
else if ((usesvar(ic = q->op) && arg == q->rnd1) ||
|
|
(ic == bccall_op && arg == q->rnd3)/*uses_this_var(q, arg)*/) {
|
|
ic = q->op;
|
|
--n;
|
|
if (ic == put_val_op) {
|
|
if (target1 == cglobs->MaxCTemps && Needed[q->rnd2] == 0)
|
|
target1 = q->rnd2;
|
|
else if (target1 != (r = q->rnd2)) {
|
|
if (target2 == cglobs->MaxCTemps && Needed[r] == 0)
|
|
target2 = r;
|
|
else if (target2 > r && cglobs->Uses[r] == 0 && Needed[r] == 0)
|
|
target2 = r;
|
|
}
|
|
}
|
|
}
|
|
#ifdef SFUNC
|
|
else if ((ic >= get_var_op && ic <= put_unsafe_op)
|
|
|| ic == get_s_f_op || ic == put_s_f_op)
|
|
Needed[q->rnd2] = 1;
|
|
#else
|
|
else if (ic >= get_var_op && ic <= put_unsafe_op)
|
|
Needed[q->rnd2] = 1;
|
|
#endif
|
|
if ((ic == call_op || ic == safe_call_op) && n == 0)
|
|
break;
|
|
}
|
|
if (target2 < target1) {
|
|
r = target2;
|
|
target2 = target1;
|
|
target1 = r;
|
|
}
|
|
if (target1 == cglobs->MaxCTemps || cglobs->Uses[target1] || Needed[target1])
|
|
if ((target1 = target2) == cglobs->MaxCTemps || cglobs->Uses[target1] || Needed[target1]) {
|
|
target1 = cglobs->MaxCTemps;
|
|
do
|
|
--target1;
|
|
while (target1 && cglobs->Uses[target1] == 0 && Needed[target1] == 0);
|
|
++target1;
|
|
}
|
|
if (target1 == cglobs->MaxCTemps) {
|
|
CACHE_REGS
|
|
LOCAL_Error_TYPE = INTERNAL_COMPILER_ERROR;
|
|
LOCAL_Error_Term = TermNil;
|
|
LOCAL_ErrorMessage = "too many temporaries";
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch, COMPILER_ERR_BOTCH);
|
|
}
|
|
v->NoOfVE = cglobs->vadr = vadr = TempVar | target1;
|
|
v->KindOfVE = TempVar;
|
|
cglobs->Uses[cglobs->vreg = vreg = target1] = v->RCountOfVE - 1;
|
|
/*
|
|
* for(r=0; r<cglobs->MaxCTemps; ++r) if(cglobs->Contents[r]==vadr) cglobs->Contents[r] =
|
|
* NIL;
|
|
*/
|
|
cp = cglobs->Contents;
|
|
for (r = 0; r < cglobs->MaxCTemps; ++r)
|
|
if (*cp++ == (Term)vadr)
|
|
cp[-1] = NIL;
|
|
cglobs->Contents[vreg] = vadr;
|
|
return 1;
|
|
}
|
|
|
|
static Int
|
|
checkreg(Int arg, Int rn, compiler_vm_op ic, int var_arg, compiler_struct *cglobs)
|
|
{
|
|
PInstr *p = cglobs->cint.cpc;
|
|
Int vreg;
|
|
|
|
if (rn >= 0)
|
|
return rn;
|
|
if (var_arg) {
|
|
Ventry *v = (Ventry *) arg;
|
|
|
|
vreg = (v->NoOfVE) & MaskVarAdrs;
|
|
if (v->KindOfVE == PermVar)
|
|
vreg = 0;
|
|
else if (vreg == 0) {
|
|
checktemp(arg, rn, ic, cglobs);
|
|
vreg = (v->NoOfVE) & MaskVarAdrs;
|
|
++cglobs->Uses[vreg];
|
|
}
|
|
if (!vreg) {
|
|
vreg = 1;
|
|
while (cglobs->Uses[vreg] != 0) {
|
|
++vreg;
|
|
}
|
|
cglobs->Uses[vreg] = v->RCountOfVE;
|
|
}
|
|
} else {
|
|
vreg = 1;
|
|
while (cglobs->Uses[vreg] != 0) {
|
|
++vreg;
|
|
}
|
|
}
|
|
while (p) {
|
|
if (p->op >= get_var_op && p->op <= put_unsafe_op && p->rnd2 == rn)
|
|
p->rnd2 = vreg;
|
|
/* only copy variables until you reach a call */
|
|
if (p->op == procceed_op || p->op == call_op || p->op == push_or_op || p->op == pushpop_or_op)
|
|
break;
|
|
p = p->nextInst;
|
|
}
|
|
return vreg;
|
|
}
|
|
|
|
/* Create a bitmap with all live variables */
|
|
static CELL
|
|
copy_live_temps_bmap(int max, compiler_struct *cglobs)
|
|
{
|
|
unsigned int size = AdjustSize((max|7)/8+1);
|
|
int i;
|
|
CELL *dest = Yap_emit_extra_size(mark_live_regs_op, max, size, &cglobs->cint);
|
|
CELL *ptr=dest;
|
|
*ptr = 0L;
|
|
for (i=1; i <= max; i++) {
|
|
/* move to next cell */
|
|
if (i%(8*CellSize) == 0) {
|
|
ptr++;
|
|
*ptr = 0L;
|
|
}
|
|
/* set the register live bit */
|
|
if (cglobs->Contents[i]) {
|
|
int j = i%(8*CellSize);
|
|
*ptr |= (1<<j);
|
|
}
|
|
}
|
|
return((CELL)dest);
|
|
}
|
|
|
|
static void
|
|
c_layout(compiler_struct *cglobs)
|
|
{
|
|
CACHE_REGS
|
|
PInstr *savepc = cglobs->BodyStart->nextInst;
|
|
register Ventry *v = cglobs->vtable;
|
|
Int *up = cglobs->Uses;
|
|
CELL *cop = cglobs->Contents;
|
|
/* tell put_values used in bip optimisation */
|
|
int rn_kills = 0;
|
|
Int rn_to_kill[2];
|
|
int needs_either = 0;
|
|
|
|
rn_to_kill[0] = rn_to_kill[1] = 0;
|
|
cglobs->cint.cpc = cglobs->BodyStart;
|
|
/*
|
|
#ifdef BEAM
|
|
if (!cglobs->is_a_fact || EAM) {
|
|
#else
|
|
*/
|
|
if (!cglobs->is_a_fact) {
|
|
while (v != NIL) {
|
|
if (v->FlagsOfVE & BranchVar) {
|
|
v->AgeOfVE = v->FirstOfVE + 1; /* force permanent */
|
|
++(v->RCountOfVE);
|
|
Yap_emit(put_var_op, (CELL) v, Zero, &cglobs->cint);
|
|
v->FlagsOfVE &= ~GlobalVal;
|
|
v->FirstOpForV = cglobs->cint.cpc;
|
|
}
|
|
v = v->NextOfVE;
|
|
}
|
|
cglobs->cint.cpc->nextInst = savepc;
|
|
|
|
#ifdef BEAM
|
|
if (cglobs->needs_env || EAM) {
|
|
#else
|
|
if (cglobs->needs_env) {
|
|
#endif
|
|
LOCAL_nperm = 0;
|
|
AssignPerm(cglobs->cint.CodeStart, cglobs);
|
|
#ifdef DEBUG
|
|
cglobs->pbvars = 0;
|
|
#endif
|
|
CheckUnsafe(cglobs->cint.CodeStart, cglobs);
|
|
#ifdef DEBUG
|
|
if (cglobs->pbvars != LOCAL_nperm) {
|
|
CACHE_REGS
|
|
LOCAL_Error_TYPE = INTERNAL_COMPILER_ERROR;
|
|
LOCAL_Error_Term = TermNil;
|
|
LOCAL_ErrorMessage = "wrong number of variables found in bitmap";
|
|
save_machine_regs();
|
|
siglongjmp(cglobs->cint.CompilerBotch, OUT_OF_HEAP_BOTCH);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
cglobs->MaxCTemps = cglobs->nvars + cglobs->max_args - cglobs->tmpreg + cglobs->n_common_exps + 2;
|
|
if (cglobs->MaxCTemps >= MaxTemps)
|
|
cglobs->MaxCTemps = MaxTemps;
|
|
{
|
|
Int rn;
|
|
for (rn = 0; rn < cglobs->MaxCTemps; ++rn) {
|
|
/* cglobs->Uses[rn] = 0; cglobs->Contents[rn] = NIL; */
|
|
*up++ = 0;
|
|
*cop++ = NIL;
|
|
}
|
|
}
|
|
|
|
CheckVoids(cglobs);
|
|
|
|
/* second scan: allocate registers */
|
|
cglobs->cint.cpc = cglobs->cint.CodeStart;
|
|
while (cglobs->cint.cpc) {
|
|
compiler_vm_op ic = cglobs->cint.cpc->op;
|
|
Int arg = cglobs->cint.cpc->rnd1;
|
|
Int rn = cglobs->cint.cpc->rnd2;
|
|
switch (ic) {
|
|
case pop_or_op:
|
|
if (needs_either)
|
|
needs_either--;
|
|
case either_op:
|
|
needs_either++;
|
|
break;
|
|
#ifdef TABLING_INNER_CUTS
|
|
case cut_op:
|
|
case cutexit_op:
|
|
cglobs->cut_mark->op = clause_with_cut_op;
|
|
break;
|
|
#else
|
|
case cut_op:
|
|
case cutexit_op:
|
|
{
|
|
int i, max;
|
|
|
|
max = 0;
|
|
for (i = 1; i < cglobs->MaxCTemps; ++i) {
|
|
if (cglobs->Contents[i]) max = i;
|
|
}
|
|
cglobs->cint.cpc->ops.opseqt[1] = max;
|
|
}
|
|
break;
|
|
#endif /* TABLING_INNER_CUTS */
|
|
case allocate_op:
|
|
case deallocate_op:
|
|
if (!cglobs->needs_env) {
|
|
cglobs->cint.cpc->op = nop_op;
|
|
} else {
|
|
#ifdef TABLING
|
|
PELOCK(51,cglobs->cint.CurrentPred);
|
|
if (is_tabled(cglobs->cint.CurrentPred))
|
|
cglobs->cint.cpc->op = nop_op;
|
|
else
|
|
#endif /* TABLING */
|
|
if (cglobs->goalno == 1 && !cglobs->or_found && LOCAL_nperm == 0)
|
|
cglobs->cint.cpc->op = nop_op;
|
|
#ifdef TABLING
|
|
UNLOCK(cglobs->cint.CurrentPred->PELock);
|
|
#endif
|
|
}
|
|
break;
|
|
case pop_op:
|
|
ic = (cglobs->cint.cpc->nextInst)->op;
|
|
if (ic >= get_var_op && ic <= put_unsafe_op)
|
|
cglobs->cint.cpc->op = nop_op;
|
|
break;
|
|
case get_var_op:
|
|
--cglobs->Uses[rn];
|
|
if (checktemp(arg, rn, ic, cglobs)) {
|
|
#ifdef BEAM
|
|
if (cglobs->vreg == rn && !EAM)
|
|
#else
|
|
if (cglobs->vreg == rn)
|
|
#endif
|
|
cglobs->cint.cpc->op = nop_op;
|
|
}
|
|
if (!cglobs->Uses[rn])
|
|
cglobs->Contents[rn] = cglobs->vadr;
|
|
break;
|
|
case get_val_op:
|
|
--cglobs->Uses[rn];
|
|
checktemp(arg, rn, ic, cglobs);
|
|
if (!cglobs->Uses[rn])
|
|
cglobs->Contents[rn] = cglobs->vadr;
|
|
break;
|
|
case f_0_op:
|
|
if (rn_to_kill[0]) --cglobs->Uses[rn_to_kill[0]];
|
|
rn_to_kill[1]=rn_to_kill[0]=0;
|
|
break;
|
|
case f_var_op:
|
|
if (rn_to_kill[0]) --cglobs->Uses[rn_to_kill[0]];
|
|
rn_to_kill[1]=rn_to_kill[0]=0;
|
|
case unify_var_op:
|
|
case unify_val_op:
|
|
case unify_last_var_op:
|
|
case unify_last_val_op:
|
|
#ifdef SFUNC
|
|
case unify_s_var_op:
|
|
case unify_s_val_op:
|
|
#endif
|
|
checktemp(arg, rn, ic, cglobs);
|
|
break;
|
|
case bccall_op:
|
|
checktemp(arg, rn, ic, cglobs);
|
|
checktemp(cglobs->cint.cpc->rnd3, cglobs->cint.cpc->rnd4, ic, cglobs);
|
|
break;
|
|
case get_atom_op:
|
|
case get_num_op:
|
|
case get_float_op:
|
|
case get_longint_op:
|
|
case get_string_op:
|
|
case get_dbterm_op:
|
|
case get_bigint_op:
|
|
--cglobs->Uses[rn];
|
|
/* This is not safe if we are in the middle of a disjunction and there
|
|
is something ahead.
|
|
*/
|
|
if (!cglobs->Uses[rn])
|
|
cglobs->Contents[rn] = arg;
|
|
break;
|
|
case get_list_op:
|
|
case get_struct_op:
|
|
--cglobs->Uses[rn];
|
|
if (!cglobs->Uses[rn])
|
|
cglobs->Contents[rn] = NIL;
|
|
break;
|
|
case put_var_op:
|
|
case put_unsafe_op:
|
|
rn = checkreg(arg, rn, ic, TRUE, cglobs);
|
|
checktemp(arg, rn, ic, cglobs);
|
|
cglobs->Contents[rn] = cglobs->vadr;
|
|
++cglobs->Uses[rn];
|
|
break;
|
|
case put_val_op:
|
|
rn = checkreg(arg, rn, ic, TRUE, cglobs);
|
|
checktemp(arg, rn, ic, cglobs);
|
|
#ifdef BEAM
|
|
if (rn && cglobs->Contents[rn] == (Term)cglobs->vadr && !EAM)
|
|
#else
|
|
if (rn && cglobs->Contents[rn] == (Term)cglobs->vadr)
|
|
#endif
|
|
{
|
|
cglobs->cint.cpc->op = nop_op;
|
|
}
|
|
cglobs->Contents[rn] = cglobs->vadr;
|
|
++cglobs->Uses[rn];
|
|
if (rn_kills) {
|
|
rn_kills--;
|
|
rn_to_kill[rn_kills]=rn;
|
|
}
|
|
break;
|
|
case fetch_args_cv_op:
|
|
case fetch_args_vc_op:
|
|
case fetch_args_iv_op:
|
|
case fetch_args_vi_op:
|
|
rn_to_kill[1]=rn_to_kill[0]=0;
|
|
if (cglobs->cint.cpc->nextInst &&
|
|
cglobs->cint.cpc->nextInst->op == put_val_op &&
|
|
cglobs->cint.cpc->nextInst->nextInst &&
|
|
(cglobs->cint.cpc->nextInst->nextInst->op == f_var_op ||
|
|
cglobs->cint.cpc->nextInst->nextInst->op == f_0_op) )
|
|
rn_kills = 1;
|
|
break;
|
|
case f_val_op:
|
|
#ifdef SFUNC
|
|
case write_s_var_op:
|
|
{
|
|
Ventry *ve = (Ventry *) arg;
|
|
|
|
if ((ve->FlagsOfVE & PermFlag) == 0 && ve->RCountOfVE <= 1)
|
|
cglobs->cint.cpc->op = nop_op;
|
|
}
|
|
break;
|
|
case write_s_val_op:
|
|
#endif
|
|
case write_var_op:
|
|
case write_val_op:
|
|
checktemp(arg, rn, ic, cglobs);
|
|
break;
|
|
#ifdef SFUNC
|
|
case put_s_f_op:
|
|
cglobs->Contents[rn] = arg;
|
|
++cglobs->Uses[rn];
|
|
break;
|
|
#endif
|
|
case put_atom_op:
|
|
case put_num_op:
|
|
case put_float_op:
|
|
case put_longint_op:
|
|
case put_string_op:
|
|
case put_dbterm_op:
|
|
case put_bigint_op:
|
|
rn = checkreg(arg, rn, ic, FALSE, cglobs);
|
|
if (cglobs->Contents[rn] == arg)
|
|
cglobs->cint.cpc->op = nop_op;
|
|
cglobs->Contents[rn] = arg;
|
|
++cglobs->Uses[rn];
|
|
break;
|
|
case put_list_op:
|
|
case put_struct_op:
|
|
rn = checkreg(arg, rn, ic, FALSE, cglobs);
|
|
cglobs->Contents[rn] = NIL;
|
|
++cglobs->Uses[rn];
|
|
break;
|
|
case commit_b_op:
|
|
#ifdef TABLING_INNER_CUTS
|
|
cglobs->cut_mark->op = clause_with_cut_op;
|
|
#endif /* TABLING_INNER_CUTS */
|
|
case save_b_op:
|
|
case patch_b_op:
|
|
case save_appl_op:
|
|
case save_pair_op:
|
|
checktemp(arg, rn, ic, cglobs);
|
|
break;
|
|
case safe_call_op:
|
|
/*
|
|
vsc: The variables will be in use after this!!!!
|
|
{
|
|
UInt Arity = RepPredProp((Prop) arg)->ArityOfPE;
|
|
for (rn = 1; rn <= Arity; ++rn)
|
|
--cglobs->Uses[rn];
|
|
}
|
|
*/
|
|
break;
|
|
case call_op:
|
|
case orelse_op:
|
|
case orlast_op:
|
|
{
|
|
up = cglobs->Uses;
|
|
cop = cglobs->Contents;
|
|
for (rn = 1; rn < cglobs->MaxCTemps; ++rn) {
|
|
*up++ = *cop++ = NIL;
|
|
}
|
|
}
|
|
break;
|
|
case label_op:
|
|
{
|
|
up = cglobs->Uses;
|
|
cop = cglobs->Contents;
|
|
for (rn = 0; rn < cglobs->MaxCTemps; ++rn) {
|
|
if (*cop != (TempVar | rn)) {
|
|
*up++ = *cop++ = NIL;
|
|
} else {
|
|
up++;
|
|
cop++;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case restore_tmps_and_skip_op:
|
|
case restore_tmps_op:
|
|
/*
|
|
This instruction is required by the garbage collector to find out
|
|
how many temporaries are live right now. It is also useful when
|
|
waking up goals before an either or ! instruction.
|
|
*/
|
|
{
|
|
PInstr *mycpc = cglobs->cint.cpc, *oldCodeStart = cglobs->cint.CodeStart;
|
|
int i, max;
|
|
|
|
/* instructions must be placed at BlobsStart */
|
|
cglobs->cint.CodeStart = cglobs->cint.BlobsStart;
|
|
cglobs->cint.cpc = cglobs->cint.icpc;
|
|
max = 0;
|
|
for (i = 1; i < cglobs->MaxCTemps; ++i) {
|
|
if (cglobs->Contents[i]) max = i;
|
|
}
|
|
Yap_emit(label_op, ++cglobs->labelno, Zero, &cglobs->cint);
|
|
mycpc->rnd1 = cglobs->labelno;
|
|
rn = copy_live_temps_bmap(max, cglobs);
|
|
cglobs->cint.icpc = cglobs->cint.cpc;
|
|
cglobs->cint.BlobsStart = cglobs->cint.CodeStart;
|
|
cglobs->cint.cpc = mycpc;
|
|
cglobs->cint.CodeStart = oldCodeStart;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
if (cglobs->cint.cpc->nextInst)
|
|
cglobs->cint.cpc = cglobs->cint.cpc->nextInst;
|
|
else return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
push_allocate(PInstr *pc, PInstr *oldpc)
|
|
{
|
|
/*
|
|
The idea is to push an allocate forward as much as we can. This
|
|
delays work in the emulated code, and in the best case we may get rid of
|
|
allocates altogether.
|
|
*/
|
|
/* we can push the allocate */
|
|
int safe = TRUE;
|
|
PInstr *initial = oldpc, *dealloc_founds[16];
|
|
int d_founds = 0;
|
|
int level = 0;
|
|
|
|
while (pc) {
|
|
switch (pc->op) {
|
|
case jump_op:
|
|
return;
|
|
case call_op:
|
|
case safe_call_op:
|
|
if (!safe)
|
|
return;
|
|
else {
|
|
PInstr *where = initial->nextInst->nextInst;
|
|
while (d_founds)
|
|
dealloc_founds[--d_founds]->op = nop_op;
|
|
if (where == pc || oldpc == initial->nextInst)
|
|
return;
|
|
oldpc->nextInst = initial->nextInst;
|
|
initial->nextInst->nextInst = pc;
|
|
initial->nextInst = where;
|
|
return;
|
|
}
|
|
case push_or_op:
|
|
/* we cannot just put an allocate here, because it may never be executed */
|
|
level++;
|
|
safe = FALSE;
|
|
break;
|
|
case pushpop_or_op:
|
|
/* last branch and we did not need an allocate so far, cool! */
|
|
level--;
|
|
if (!level)
|
|
safe = TRUE;
|
|
break;
|
|
case cut_op:
|
|
case either_op:
|
|
case execute_op:
|
|
return;
|
|
case deallocate_op:
|
|
dealloc_founds[d_founds++] = pc;
|
|
if (d_founds == 16)
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
oldpc = pc;
|
|
pc = pc->nextInst;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
c_optimize(PInstr *pc)
|
|
{
|
|
char onTail;
|
|
Ventry *v;
|
|
PInstr *opc = NULL;
|
|
PInstr *inpc = pc;
|
|
|
|
pc = inpc;
|
|
opc = NULL;
|
|
/* first reverse the pointers */
|
|
while (pc != NULL) {
|
|
PInstr *tpc = pc->nextInst;
|
|
pc->nextInst = opc;
|
|
opc = pc;
|
|
pc = tpc;
|
|
}
|
|
pc = opc;
|
|
opc = NULL;
|
|
onTail = 1;
|
|
do {
|
|
PInstr *npc = pc->nextInst;
|
|
pc->nextInst = opc;
|
|
switch (pc->op) {
|
|
case get_var_op:
|
|
/* handle clumsy either branches */
|
|
if (npc->op == f_0_op) {
|
|
npc->rnd1 = pc->rnd1;
|
|
npc->op = f_var_op;
|
|
pc->op = nop_op;
|
|
break;
|
|
}
|
|
case put_val_op:
|
|
case get_val_op:
|
|
{
|
|
Ventry *ve = (Ventry *) pc->rnd1;
|
|
|
|
if (ve->KindOfVE == TempVar) {
|
|
UInt argno = ve->NoOfVE & MaskVarAdrs;
|
|
if (argno && argno == pc->rnd2) {
|
|
pc->op = nop_op;
|
|
}
|
|
}
|
|
}
|
|
onTail = 1;
|
|
break;
|
|
case save_pair_op:
|
|
{
|
|
Term ve = (Term) pc->rnd1;
|
|
PInstr *npc = pc->nextInst;
|
|
|
|
if (((Ventry *) ve)->RCountOfVE <= 1)
|
|
pc->op = nop_op;
|
|
else {
|
|
*pc = *npc;
|
|
pc->nextInst = npc;
|
|
npc->op = save_pair_op;
|
|
npc->rnd1 = (CELL) ve;
|
|
}
|
|
}
|
|
break;
|
|
case save_appl_op:
|
|
{
|
|
Term ve = (Term) pc->rnd1;
|
|
PInstr *npc = pc->nextInst;
|
|
|
|
if (((Ventry *) ve)->RCountOfVE <= 1)
|
|
pc->op = nop_op;
|
|
else {
|
|
*pc = *npc;
|
|
pc->nextInst = npc;
|
|
npc->op = save_appl_op;
|
|
npc->rnd1 = (CELL) ve;
|
|
}
|
|
break;
|
|
}
|
|
case nop_op:
|
|
break;
|
|
case unify_var_op:
|
|
case unify_last_var_op:
|
|
#ifdef OLD_SYSTEM
|
|
/* In the good old days Yap would remove lots of small void
|
|
* instructions for a structure. This is not such a
|
|
* good idea nowadays, as we need to know where we
|
|
* finish the structure for the last instructions to
|
|
* work correctly. Instead, we will use unify_void
|
|
* with very little overhead */
|
|
v = (Ventry *) (pc->rnd1);
|
|
if (v->KindOfVE == VoidVar && onTail) {
|
|
pc->op = nop_op;
|
|
}
|
|
else
|
|
#endif /* OLD_SYSTEM */
|
|
onTail = 0;
|
|
break;
|
|
case unify_val_op:
|
|
v = (Ventry *) (pc->rnd1);
|
|
if (!(v->FlagsOfVE & GlobalVal))
|
|
pc->op = unify_local_op;
|
|
onTail = 0;
|
|
break;
|
|
case unify_last_val_op:
|
|
v = (Ventry *) (pc->rnd1);
|
|
if (!(v->FlagsOfVE & GlobalVal))
|
|
pc->op = unify_last_local_op;
|
|
onTail = 0;
|
|
break;
|
|
case write_val_op:
|
|
v = (Ventry *) (pc->rnd1);
|
|
if (!(v->FlagsOfVE & GlobalVal))
|
|
pc->op = write_local_op;
|
|
onTail = 0;
|
|
break;
|
|
case pop_op:
|
|
if (FALSE && onTail == 1) {
|
|
pc->op = nop_op;
|
|
onTail = 1;
|
|
break;
|
|
}
|
|
else {
|
|
PInstr *p = pc->nextInst;
|
|
|
|
while (p != NIL && p->op == nop_op)
|
|
p = p->nextInst;
|
|
if (p != NIL && p->op == pop_op) {
|
|
pc->rnd1 += p->rnd1;
|
|
pc->nextInst = p->nextInst;
|
|
}
|
|
onTail = 2;
|
|
break;
|
|
}
|
|
case write_var_op:
|
|
case unify_atom_op:
|
|
case unify_last_atom_op:
|
|
case write_atom_op:
|
|
case unify_num_op:
|
|
case unify_last_num_op:
|
|
case write_num_op:
|
|
case unify_float_op:
|
|
case unify_last_float_op:
|
|
case write_float_op:
|
|
case unify_longint_op:
|
|
case unify_string_op:
|
|
case unify_bigint_op:
|
|
case unify_last_longint_op:
|
|
case unify_last_string_op:
|
|
case unify_last_bigint_op:
|
|
case write_longint_op:
|
|
case write_string_op:
|
|
case write_bigint_op:
|
|
case unify_list_op:
|
|
case write_list_op:
|
|
case unify_struct_op:
|
|
case write_struct_op:
|
|
case write_unsafe_op:
|
|
case unify_last_list_op:
|
|
case write_last_list_op:
|
|
case unify_last_struct_op:
|
|
case write_last_struct_op:
|
|
#ifdef SFUNC
|
|
case unify_s_f_op:
|
|
case write_s_f_op:
|
|
#endif
|
|
onTail = 0;
|
|
break;
|
|
default:
|
|
onTail = 1;
|
|
break;
|
|
}
|
|
opc = pc;
|
|
pc = npc;
|
|
} while (pc != NULL);
|
|
pc = inpc;
|
|
opc = NULL;
|
|
while (pc != NULL) {
|
|
if (pc->op == allocate_op) {
|
|
push_allocate(pc, opc);
|
|
break;
|
|
}
|
|
opc = pc;
|
|
pc = pc->nextInst;
|
|
}
|
|
}
|
|
|
|
yamop *
|
|
Yap_cclause(volatile Term inp_clause, Int NOfArgs, Term mod, volatile Term src)
|
|
{ /* compile a prolog clause, copy of clause myst be in ARG1 */
|
|
CACHE_REGS
|
|
/* returns address of code for clause */
|
|
Term head, body;
|
|
yamop *acode;
|
|
Term my_clause;
|
|
|
|
volatile int maxvnum = 512;
|
|
int botch_why;
|
|
/* may botch while doing a different module */
|
|
/* first, initialise cglobs->cint.CompilerBotch to handle all cases of interruptions */
|
|
compiler_struct cglobs;
|
|
|
|
#ifdef TABLING_INNER_CUTS
|
|
PInstr cglobs_cut_mark;
|
|
cglobs.cut_mark = &cglobs_cut_mark;
|
|
#endif /* TABLING_INNER_CUTS */
|
|
|
|
/* make sure we know there was no error yet */
|
|
LOCAL_ErrorMessage = NULL;
|
|
if ((botch_why = sigsetjmp(cglobs.cint.CompilerBotch, 0))) {
|
|
restore_machine_regs();
|
|
reset_vars(cglobs.vtable);
|
|
Yap_ReleaseCMem(&cglobs.cint);
|
|
switch(botch_why) {
|
|
case OUT_OF_STACK_BOTCH:
|
|
/* out of local stack, just duplicate the stack */
|
|
{
|
|
Int osize = 2*sizeof(CELL)*(ASP-HR);
|
|
ARG1 = inp_clause;
|
|
ARG3 = src;
|
|
|
|
YAPLeaveCriticalSection();
|
|
if (!Yap_gcl(LOCAL_Error_Size, NOfArgs, ENV, gc_P(P,CP))) {
|
|
LOCAL_Error_TYPE = OUT_OF_STACK_ERROR;
|
|
LOCAL_Error_Term = inp_clause;
|
|
}
|
|
if (osize > ASP-HR) {
|
|
if (!Yap_growstack(2*sizeof(CELL)*(ASP-HR))) {
|
|
LOCAL_Error_TYPE = OUT_OF_STACK_ERROR;
|
|
LOCAL_Error_Term = inp_clause;
|
|
}
|
|
}
|
|
YAPEnterCriticalSection();
|
|
src = ARG3;
|
|
inp_clause = ARG1;
|
|
}
|
|
break;
|
|
case OUT_OF_AUX_BOTCH:
|
|
/* out of local stack, just duplicate the stack */
|
|
YAPLeaveCriticalSection();
|
|
ARG1 = inp_clause;
|
|
ARG3 = src;
|
|
if (!Yap_ExpandPreAllocCodeSpace(LOCAL_Error_Size, NULL, TRUE)) {
|
|
LOCAL_Error_TYPE = OUT_OF_AUXSPACE_ERROR;
|
|
LOCAL_Error_Term = inp_clause;
|
|
}
|
|
YAPEnterCriticalSection();
|
|
src = ARG3;
|
|
inp_clause = ARG1;
|
|
break;
|
|
case OUT_OF_TEMPS_BOTCH:
|
|
/* out of temporary cells */
|
|
if (maxvnum < 16*1024) {
|
|
maxvnum *= 2;
|
|
} else {
|
|
maxvnum += 4096;
|
|
}
|
|
break;
|
|
case OUT_OF_HEAP_BOTCH:
|
|
/* not enough heap */
|
|
ARG1 = inp_clause;
|
|
ARG3 = src;
|
|
YAPLeaveCriticalSection();
|
|
if (!Yap_growheap(FALSE, LOCAL_Error_Size, NULL)) {
|
|
LOCAL_Error_TYPE = OUT_OF_HEAP_ERROR;
|
|
LOCAL_Error_Term = inp_clause;
|
|
return NULL;
|
|
}
|
|
YAPEnterCriticalSection();
|
|
src = ARG3;
|
|
inp_clause = ARG1;
|
|
break;
|
|
case OUT_OF_TRAIL_BOTCH:
|
|
/* not enough trail */
|
|
ARG1 = inp_clause;
|
|
ARG3 = src;
|
|
YAPLeaveCriticalSection();
|
|
if (!Yap_growtrail(LOCAL_TrailTop-(ADDR)TR, FALSE)) {
|
|
LOCAL_Error_TYPE = OUT_OF_TRAIL_ERROR;
|
|
LOCAL_Error_Term = inp_clause;
|
|
return NULL;
|
|
}
|
|
YAPEnterCriticalSection();
|
|
src = ARG3;
|
|
inp_clause = ARG1;
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
my_clause = inp_clause;
|
|
HB = HR;
|
|
LOCAL_ErrorMessage = NULL;
|
|
LOCAL_Error_Size = 0;
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
/* initialize variables for code generation */
|
|
|
|
cglobs.cint.CodeStart = cglobs.cint.cpc = NULL;
|
|
cglobs.cint.BlobsStart = cglobs.cint.icpc = NULL;
|
|
cglobs.cint.dbterml = NULL;
|
|
cglobs.cint.blks = NULL;
|
|
cglobs.cint.label_offset = NULL;
|
|
cglobs.cint.freep =
|
|
cglobs.cint.freep0 =
|
|
(char *) (HR + maxvnum+(sizeof(Int)/sizeof(CELL))*MaxTemps+MaxTemps);
|
|
cglobs.cint.success_handler = 0L;
|
|
if (ASP <= CellPtr (cglobs.cint.freep) + 256) {
|
|
cglobs.vtable = NULL;
|
|
LOCAL_Error_Size = (256+maxvnum)*sizeof(CELL);
|
|
save_machine_regs();
|
|
siglongjmp(cglobs.cint.CompilerBotch,3);
|
|
}
|
|
cglobs.Uses = (Int *)(HR+maxvnum);
|
|
cglobs.Contents = (Term *)(HR+maxvnum+(sizeof(Int)/sizeof(CELL))*MaxTemps);
|
|
cglobs.curbranch = cglobs.onbranch = 0;
|
|
cglobs.branch_pointer = cglobs.parent_branches;
|
|
cglobs.or_found = FALSE;
|
|
cglobs.max_args = 0;
|
|
cglobs.nvars = 0;
|
|
cglobs.tmpreg = 0;
|
|
cglobs.needs_env = FALSE;
|
|
/*
|
|
* 2000 added to H in case we need to construct call(G) when G is a
|
|
* variable used as a goal
|
|
*/
|
|
cglobs.vtable = NULL;
|
|
cglobs.common_exps = NULL;
|
|
cglobs.n_common_exps = 0;
|
|
cglobs.labelno = 0L;
|
|
cglobs.is_a_fact = FALSE;
|
|
cglobs.hasdbrefs = FALSE;
|
|
if (IsVarTerm(my_clause)) {
|
|
LOCAL_Error_TYPE = INSTANTIATION_ERROR;
|
|
LOCAL_Error_Term = my_clause;
|
|
LOCAL_ErrorMessage = "in compiling clause";
|
|
return 0;
|
|
}
|
|
if (IsApplTerm(my_clause) && FunctorOfTerm(my_clause) == FunctorAssert) {
|
|
head = ArgOfTerm(1, my_clause);
|
|
body = ArgOfTerm(2, my_clause);
|
|
}
|
|
else {
|
|
head = my_clause, body = MkAtomTerm(AtomTrue);
|
|
}
|
|
if (IsVarTerm(head) || IsPairTerm(head) || IsIntTerm(head) || IsFloatTerm(head) || IsRefTerm(head)) {
|
|
LOCAL_Error_TYPE = TYPE_ERROR_CALLABLE;
|
|
LOCAL_Error_Term = head;
|
|
LOCAL_ErrorMessage = "clause head should be atom or compound term";
|
|
return (0);
|
|
} else {
|
|
|
|
/* find out which predicate we are compiling for */
|
|
if (IsAtomTerm(head)) {
|
|
Atom ap = AtomOfTerm(head);
|
|
cglobs.cint.CurrentPred = RepPredProp(PredPropByAtom(ap, mod));
|
|
} else {
|
|
cglobs.cint.CurrentPred = RepPredProp(PredPropByFunc(FunctorOfTerm(head),mod));
|
|
}
|
|
/* insert extra instructions to count calls */
|
|
PELOCK(52,cglobs.cint.CurrentPred);
|
|
if ((cglobs.cint.CurrentPred->PredFlags & ProfiledPredFlag) ||
|
|
(PROFILING && (cglobs.cint.CurrentPred->cs.p_code.FirstClause == NIL))) {
|
|
profiling = TRUE;
|
|
call_counting = FALSE;
|
|
} else if ((cglobs.cint.CurrentPred->PredFlags & CountPredFlag) ||
|
|
(CALL_COUNTING && (cglobs.cint.CurrentPred->cs.p_code.FirstClause == NIL))) {
|
|
call_counting = TRUE;
|
|
profiling = FALSE;
|
|
} else {
|
|
profiling = FALSE;
|
|
call_counting = FALSE;
|
|
}
|
|
UNLOCK(cglobs.cint.CurrentPred->PELock);
|
|
}
|
|
cglobs.is_a_fact = (body == MkAtomTerm(AtomTrue));
|
|
/* phase 1 : produce skeleton code and variable information */
|
|
|
|
c_head(head, &cglobs);
|
|
|
|
if (cglobs.is_a_fact && !cglobs.vtable) {
|
|
#ifdef TABLING
|
|
PELOCK(53,cglobs.cint.CurrentPred);
|
|
if (is_tabled(cglobs.cint.CurrentPred))
|
|
Yap_emit(table_new_answer_op, Zero, cglobs.cint.CurrentPred->ArityOfPE, &cglobs.cint);
|
|
else
|
|
#endif /* TABLING */
|
|
Yap_emit(procceed_op, Zero, Zero, &cglobs.cint);
|
|
#ifdef TABLING
|
|
UNLOCK(cglobs.cint.CurrentPred->PELock);
|
|
#endif
|
|
/* ground term, do not need much more work */
|
|
if (cglobs.cint.BlobsStart != NULL) {
|
|
cglobs.cint.cpc->nextInst = cglobs.cint.BlobsStart;
|
|
cglobs.cint.BlobsStart = NULL;
|
|
}
|
|
if (LOCAL_ErrorMessage)
|
|
return (0);
|
|
/* make sure we give enough space for the fact */
|
|
if (cglobs.space_op)
|
|
cglobs.space_op->rnd1 = cglobs.space_used;
|
|
|
|
#ifdef DEBUG
|
|
if (GLOBAL_Option['g' - 96])
|
|
Yap_ShowCode(&cglobs.cint);
|
|
#endif
|
|
} else {
|
|
#ifdef TABLING_INNER_CUTS
|
|
Yap_emit(nop_op, Zero, Zero, &cglobs.cint);
|
|
cglobs.cut_mark->op = clause_with_cut_op;
|
|
#endif /* TABLING_INNER_CUTS */
|
|
Yap_emit(allocate_op, Zero, Zero, &cglobs.cint);
|
|
|
|
#ifdef BEAM
|
|
if (EAM) Yap_emit(body_op, Zero, Zero, &cglobs.cint);
|
|
#endif
|
|
|
|
c_body(body, mod, &cglobs);
|
|
/* Insert blobs at the very end */
|
|
|
|
if (cglobs.space_op)
|
|
cglobs.space_op->rnd1 = cglobs.space_used;
|
|
|
|
if (cglobs.cint.BlobsStart != NULL) {
|
|
cglobs.cint.cpc->nextInst = cglobs.cint.BlobsStart;
|
|
cglobs.cint.BlobsStart = NULL;
|
|
}
|
|
|
|
reset_vars(cglobs.vtable);
|
|
HR = HB;
|
|
if (B != NULL) {
|
|
HB = B->cp_h;
|
|
}
|
|
if (LOCAL_ErrorMessage)
|
|
return (0);
|
|
#ifdef DEBUG
|
|
if (GLOBAL_Option['g' - 96])
|
|
Yap_ShowCode(&cglobs.cint);
|
|
#endif
|
|
/* phase 2: classify variables and optimize temporaries */
|
|
c_layout(&cglobs);
|
|
/* Insert blobs at the very end */
|
|
if (cglobs.cint.BlobsStart != NULL) {
|
|
cglobs.cint.cpc->nextInst = cglobs.cint.BlobsStart;
|
|
cglobs.cint.BlobsStart = NULL;
|
|
while (cglobs.cint.cpc->nextInst != NULL)
|
|
cglobs.cint.cpc = cglobs.cint.cpc->nextInst;
|
|
}
|
|
}
|
|
/* eliminate superfluous pop's and unify_var's */
|
|
c_optimize(cglobs.cint.CodeStart);
|
|
#ifdef DEBUG
|
|
if (GLOBAL_Option['f' - 96])
|
|
Yap_ShowCode(&cglobs.cint);
|
|
#endif
|
|
|
|
#ifdef BEAM
|
|
{
|
|
void codigo_eam(compiler_struct *);
|
|
|
|
if (EAM) codigo_eam(&cglobs);
|
|
}
|
|
#endif
|
|
|
|
/* phase 3: assemble code */
|
|
acode = Yap_assemble(ASSEMBLING_CLAUSE, src, cglobs.cint.CurrentPred, (cglobs.is_a_fact && !cglobs.hasdbrefs && !(cglobs.cint.CurrentPred->PredFlags & TabledPredFlag)), &cglobs.cint, cglobs.labelno+1);
|
|
/* check first if there was space for us */
|
|
Yap_ReleaseCMem (&cglobs.cint);
|
|
if (acode == NULL) {
|
|
return NULL;
|
|
} else {
|
|
return acode;
|
|
}
|
|
}
|
|
|
|
#ifdef BEAM
|
|
#include "toeam.c"
|
|
#endif
|
|
|