/************************************************************************* * * * 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 * * Last rev: 4/03/88 * * mods: * * comments: Clause compiler * * * *************************************************************************/ #ifdef SCCS static char SccsId[] = "%W% %G%"; #endif /* SCCS */ #include "Yap.h" #include "compile.h" #include "clause.h" #include "yapio.h" #if HAVE_STRING_H #include #endif STATIC_PROTO(int active_branch, (int)); STATIC_PROTO(void c_var, (Term, Int, unsigned int, unsigned int)); STATIC_PROTO(void reset_vars, (void)); STATIC_PROTO(Term optimize_ce, (Term, unsigned int, unsigned int)); STATIC_PROTO(void c_arg, (Int, Term, unsigned int, unsigned int)); STATIC_PROTO(void c_args, (Term, unsigned int)); STATIC_PROTO(void c_eq, (Term, Term)); STATIC_PROTO(void c_test, (Int, Term)); STATIC_PROTO(void c_bifun, (Int, Term, Term, Term, int)); STATIC_PROTO(void c_goal, (Term, int)); STATIC_PROTO(void c_body, (Term, int)); STATIC_PROTO(void c_head, (Term)); STATIC_PROTO(int usesvar, (int)); STATIC_PROTO(CELL *init_bvarray, (int)); STATIC_PROTO(void clear_bvarray, (int, CELL *)); STATIC_PROTO(void add_bvarray_op, (PInstr *,CELL *, int)); STATIC_PROTO(void AssignPerm, (PInstr *)); STATIC_PROTO(void CheckUnsafe, (PInstr *)); STATIC_PROTO(void CheckVoids, (void)); STATIC_PROTO( int checktemp, (void)); STATIC_PROTO(void checkreg, (int)); STATIC_PROTO(void c_layout, (void)); STATIC_PROTO(void c_optimize, (PInstr *)); #ifdef SFUNC STATIC_PROTO(void compile_sf_term, (Term, int)); #endif #ifdef TABLING_INNER_CUTS PInstr *cut_mark; #endif /* TABLING_INNER_CUTS */ static PInstr *BodyStart; static Ventry *vtable; static CExpEntry *common_exps; static int n_common_exps; static int goalno, onlast, onhead, onbranch, cur_branch; typedef struct branch_descriptor { int id; /* the branch id */ Term cm; /* if a banch is associated with a commit */ } branch; static branch parent_branches[256], *branch_pointer; static Prop current_p0; static void push_branch(int id, Term cmvar) { branch_pointer->id = onbranch; branch_pointer->cm = cmvar; branch_pointer++; } static int pop_branch(void) { branch_pointer--; return(branch_pointer->id); } #ifdef TABLING #define is_tabled(pe) (pe->PredFlags & TabledPredFlag) #endif /* TABLING */ #ifdef DEBUG static int pbvars; #endif /* DEBUG */ static inline int active_branch(int i) { /* register int *bp;*/ return (i == onbranch); /* bp = branch_pointer; while (bp > parent_branches) { if (*--bp == onbranch) return (TRUE); } return(i==onbranch);*/ } static Int labelno; static int or_found; static Int rn, ic, vreg, vadr; static Term arg; static Int Uses[MaxTemps]; static Term Contents[MaxTemps]; static Int tmpreg; static int nvars, MaxCTemps; static unsigned int max_args; #define FAIL(M,T,E) { Yap_ErrorMessage=M; Yap_Error_TYPE = T; Yap_Error_Term = E; return; } #define IsNewVar(v) (Addr(v)freep) inline static void pop_code(unsigned int); inline static void pop_code(unsigned int level) { if (level == 0) return; if (cpc->op == pop_op) ++(cpc->rnd1); else { Yap_emit(pop_op, One, Zero); } } static void adjust_current_commits(void) { branch *bp = branch_pointer; while (bp > parent_branches) { bp--; if (bp->cm != TermNil) { c_var(bp->cm, patch_b_flag, 1, 0); } } } static void c_var(Term t, Int argno, unsigned int arity, unsigned int level) { int flags, new = FALSE; Ventry *v = (Ventry *) Deref(t); if (IsNewVar(v)) { /* new var */ v = (Ventry *) Yap_AllocCMem(sizeof(*v)); #if 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 variaiable 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 || onhead) && cur_branch == 0) || argno == save_pair_flag || argno == save_appl_flag) flags |= SafeVar; if ((level > 0 && cur_branch == 0) || argno == save_pair_flag || argno == save_appl_flag) flags |= GlobalVal; v->FlagsOfVE = flags; v->BranchOfVE = onbranch; v->NextOfVE = vtable; v->RCountOfVE = 0; v->AgeOfVE = v->FirstOfVE = goalno; new = TRUE; vtable = v; } else { v->FlagsOfVE |= NonVoid; if (v->BranchOfVE > 0) { if (!active_branch(v->BranchOfVE)) { 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 (onhead) v->FlagsOfVE |= OnHeadFlag; switch (argno) { case save_b_flag: Yap_emit(save_b_op, (CELL) v, Zero); break; case commit_b_flag: Yap_emit(commit_b_op, (CELL) v, Zero); Yap_emit(empty_call_op, Zero, Zero); Yap_emit(restore_tmps_and_skip_op, Zero, Zero); break; case patch_b_flag: Yap_emit(patch_b_op, (CELL) v, 0); break; case save_pair_flag: Yap_emit(save_pair_op, (CELL) v, 0); break; case save_appl_flag: Yap_emit(save_appl_op, (CELL) v, 0); break; case f_flag: if (new) { ++nvars; Yap_emit(f_var_op, (CELL) v, (CELL)arity); } else Yap_emit(f_val_op, (CELL) v, (CELL)arity); break; case bt1_flag: Yap_emit(fetch_args_for_bccall, (CELL)v, 0); break; case bt2_flag: Yap_emit(bccall_op, (CELL)v, (CELL)current_p0); break; default: #ifdef SFUNC if (argno < 0) { if (new) Yap_emit((onhead ? unify_s_var_op : write_s_var_op), v, -argno); else Yap_emit((onhead ? unify_s_val_op : write_s_val_op), v, -argno); } else #endif if (onhead) { if (level == 0) Yap_emit((new ? (++nvars, get_var_op) : get_val_op), (CELL) v, argno); else Yap_emit((new ? (++nvars, (argno == (Int)arity ? unify_last_var_op : unify_var_op)) : (argno == (Int)arity ? unify_last_val_op : unify_val_op)), (CELL) v, Zero); } else { if (level == 0) Yap_emit((new ? (++nvars, put_var_op) : put_val_op), (CELL) v, argno); else Yap_emit((new ? (++nvars, write_var_op) : write_val_op), (CELL) v, Zero); } } if (new) { v->FirstOpForV = cpc; } ++(v->RCountOfVE); if (onlast) v->FlagsOfVE |= OnLastGoal; if (v->AgeOfVE < goalno) v->AgeOfVE = goalno; } static void reset_vars(void) { 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) { CExpEntry *p = common_exps, *parent = common_exps; int cmp = 0; if (IsApplTerm(t) && IsExtensionFunctor(FunctorOfTerm(t))) return (t); while (p != NULL) { CELL *OldH = H; H = (CELL *)freep; cmp = Yap_compare_terms(t, (p->TermOfCE)); H = OldH; if (cmp > 0) { parent = p; p = p->RightCE; } else if (cmp < 0) { parent = p; p = p->LeftCE; } else break; } if (p != NULL) { /* already there */ return (p->VarOfCE); } /* first occurrence */ if (onbranch) return (t); ++n_common_exps; p = (CExpEntry *) Yap_AllocCMem(sizeof(CExpEntry)); p->TermOfCE = t; p->VarOfCE = MkVarTerm(); if (H >= (CELL *)freep0) { /* oops, too many new variables */ save_machine_regs(); longjmp(Yap_CompilerBotch,4); } p->RightCE = NULL; p->LeftCE = NULL; if (parent == NULL) common_exps = p; else if (cmp > 0) parent->RightCE = p; else /* if (cmp < 0) */ parent->LeftCE = p; if (IsApplTerm(t)) c_var(p->VarOfCE, save_appl_flag, arity, level); else if (IsPairTerm(t)) c_var(p->VarOfCE, save_pair_flag, arity, level); 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((onhead ? get_s_f_op : put_s_f_op), f, argno); else Yap_emit((onhead ? unify_s_f_op : write_s_f_op), f, Zero); ++level; while ((argno = *++p)) { t = Derefa(++p); if (t != nullvalue) { if (IsAtomicTerm(t)) Yap_emit((onhead ? unify_s_a_op : write_s_a_op), t, (CELL) argno); else if (!IsVarTerm(t)) { Yap_Error_TYPE = SYSTEM_ERROR; Yap_Error_Term = TermNil; Yap_ErrorMessage = "illegal argument of soft functor"; save_machine_regs(); longjmp(Yap_CompilerBotch, 2); } else c_var(t, -argno, arity, level); } } --level; if (level == 0) Yap_emit((onhead ? get_s_end_op : put_s_end_op), Zero, Zero); else Yap_emit((onhead ? unify_s_end_op : write_s_end_op), Zero, Zero); } #endif inline static void c_args(Term app, unsigned int level) { Functor f = FunctorOfTerm(app); unsigned int Arity = ArityOfFunctor(f); unsigned int i; if (level == 0) { if (Arity >= MaxTemps) { Yap_Error_TYPE = SYSTEM_ERROR; Yap_Error_Term = TermNil; Yap_ErrorMessage = "exceed maximum arity of compiled goal"; save_machine_regs(); longjmp(Yap_CompilerBotch, 2); } if (Arity > max_args) max_args = Arity; } for (i = 1; i <= Arity; ++i) c_arg(i, ArgOfTerm(i, app), Arity, level); } static void c_arg(Int argno, Term t, unsigned int arity, unsigned int level) { restart: if (IsVarTerm(t)) c_var(t, argno, arity, level); else if (IsAtomTerm(t)) { if (level == 0) Yap_emit((onhead ? get_atom_op : put_atom_op), (CELL) t, argno); else Yap_emit((onhead ? (argno == (Int)arity ? unify_last_atom_op : unify_atom_op) : write_atom_op), (CELL) t, Zero); } else if (IsIntegerTerm(t) || IsFloatTerm(t) || IsBigIntTerm(t)) { if (!IsIntTerm(t)) { /* 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 = ++labelno; CELL *src = RepAppl(t); PInstr *ocpc = cpc, *OCodeStart = CodeStart; /* use a special list to store the blobs */ cpc = icpc; if (IsFloatTerm(t)) { Yap_emit(align_float_op, Zero, Zero); } Yap_emit(label_op, l1, Zero); if (IsFloatTerm(t)) { /* let us do floats first */ CELL *dest = Yap_emit_extra_size(blob_op, (CELL)(SIZEOF_DOUBLE/SIZEOF_LONG_INT+1), (1+SIZEOF_DOUBLE/SIZEOF_LONG_INT)*CellSize); /* copy the float bit by bit */ dest[0] = src[0]; dest[1] = src[1]; #if SIZEOF_DOUBLE == 2*SIZEOF_LONG_INT dest[2] = src[2]; #endif /* note that we don't need to copy size info, unless we wanted to garbage collect clauses ;-) */ icpc = cpc; if (BlobsStart == NULL) BlobsStart = CodeStart; cpc = ocpc; 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((onhead ? get_float_op : put_float_op), l1, argno); else Yap_emit((onhead ? (argno == (Int)arity ? unify_last_float_op : unify_float_op) : write_float_op), l1, Zero); #if USE_GMP } else if (IsBigIntTerm(t)) { /* next, let us do bigints */ Int sz = sizeof(CELL)+ sizeof(MP_INT)+ ((((MP_INT *)(RepAppl(t)+1))->_mp_alloc)*sizeof(mp_limb_t)); CELL *dest = Yap_emit_extra_size(blob_op, sz/CellSize, sz); /* copy the bignum */ memcpy(dest, src, sz); /* note that we don't need to copy size info, unless we wanted to garbage collect clauses ;-) */ icpc = cpc; if (BlobsStart == NULL) BlobsStart = CodeStart; cpc = ocpc; 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((onhead ? get_bigint_op : put_bigint_op), l1, argno); else Yap_emit((onhead ? (argno == (Int)arity ? unify_last_bigint_op : unify_bigint_op) : write_bigint_op), l1, Zero); #endif } else { /* for now, it's just a long int */ CELL *dest = Yap_emit_extra_size(blob_op, 2, 2*CellSize); /* copy the long int in one fell swoop */ dest[0] = src[0]; dest[1] = src[1]; icpc = cpc; if (BlobsStart == NULL) BlobsStart = CodeStart; cpc = ocpc; CodeStart = OCodeStart; if (level == 0) Yap_emit((onhead ? get_longint_op : put_longint_op), l1, argno); else Yap_emit((onhead ? (argno == (Int)arity ? unify_last_longint_op : unify_longint_op) : write_longint_op), l1, Zero); } /* That's it folks! */ return; } if (level == 0) Yap_emit((onhead ? get_num_op : put_num_op), (CELL) t, argno); else Yap_emit((onhead ? (argno == (Int)arity ? unify_last_num_op : unify_num_op) : write_num_op), (CELL) t, Zero); } else if (IsPairTerm(t)) { if (optimizer_on && (!onhead || argno != 1 || level > 1) && level < 6) { t = optimize_ce(t, arity, level); if (IsVarTerm(t)) { c_var(t, argno, arity, level); return; } } if (level == 0) Yap_emit((onhead ? get_list_op : put_list_op), Zero, argno); else if (argno == (Int)arity) Yap_emit((onhead ? unify_last_list_op : write_last_list_op), Zero, Zero); else Yap_emit((onhead ? unify_list_op : write_list_op), Zero, Zero); ++level; c_arg(1, HeadOfTerm(t), 2, level); if (argno == (Int)arity) { /* optimise for tail recursion */ t = TailOfTerm(t); goto restart; } c_arg(2, TailOfTerm(t), 2, level); --level; if (argno != (Int)arity) { pop_code(level); } } else if (IsRefTerm(t)) { READ_LOCK(CurrentPred->PRWLock); if (!(CurrentPred->PredFlags & (DynamicPredFlag|LogUpdatePredFlag))) { READ_UNLOCK(CurrentPred->PRWLock); FAIL("can not compile data base reference",TYPE_ERROR_CALLABLE,t); } else { Yap_emit((onhead ? get_atom_op : put_atom_op), (CELL) t, argno); } } else { #ifdef SFUNC if (SFTerm(t)) { compile_sf_term(t, argno); return; } #endif if (optimizer_on && (!onhead || argno != 1 || level > 1)) { t = optimize_ce(t, arity, level); if (IsVarTerm(t)) { c_var(t, argno, arity, level); return; } } if (level == 0) Yap_emit((onhead ? get_struct_op : put_struct_op), (CELL) FunctorOfTerm(t), argno); else if (argno == (Int)arity) Yap_emit((onhead ? unify_last_struct_op : write_last_struct_op), (CELL) FunctorOfTerm(t), Zero); else Yap_emit((onhead ? unify_struct_op : write_struct_op), (CELL) FunctorOfTerm(t), Zero); ++level; c_args(t, level); --level; if (argno != (Int)arity) { pop_code(level); } } } static void c_eq(Term t1, Term t2) { Term t; --tmpreg; if (IsVarTerm(t2)) t = t2, t2 = t1, t1 = t; if (IsVarTerm(t1)) { if (IsVarTerm(t2)) { /* both are variables */ if (IsNewVar(t2)) t = t2, t2 = t1, t1 = t; c_var(t2, tmpreg, 2, 0); onhead = 1; c_var(t1, tmpreg, 2, 0); onhead = 0; } else if (IsNewVar(t1)) { c_arg(tmpreg, t2, 0, 0); onhead = 1; c_var(t1, tmpreg, 2, 0); onhead = 0; } else { /* t2 is non var */ c_var(t1, tmpreg, 2, 0); onhead = 1; c_arg(tmpreg, t2, 0, 0); onhead = 0; } } else { c_arg(tmpreg, t1, 0, 0); onhead = 1; c_arg(tmpreg, t2, 0, 0); onhead = 0; } } static void c_test(Int Op, Term t1) { Term t = Deref(t1); if (!IsVarTerm(t)) { char s[32]; Yap_Error_TYPE = TYPE_ERROR_VARIABLE; Yap_Error_Term = t; Yap_ErrorMessage = Yap_ErrorSay; Yap_bip_name(Op, s); sprintf(Yap_ErrorMessage, "when compiling %s/1", s); save_machine_regs(); longjmp(Yap_CompilerBotch, 1); } if (IsNewVar(t)) { /* in this case, var trivially succeeds and the others trivially fail */ if (Op != _var) Yap_emit(fail_op, Zero, Zero); } else { c_var(t,f_flag,(unsigned int)Op, 0); } } /* 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(Int Op, Term t1, Term t2, Term t3, int mod) { /* compile Z = X Op Y arithmetic function */ /* first we fetch the arguments */ if (IsVarTerm(t1)) { if (IsNewVar(t1)) { char s[32]; Yap_Error_TYPE = INSTANTIATION_ERROR; Yap_Error_Term = t1; Yap_ErrorMessage = Yap_ErrorSay; Yap_bip_name(Op, s); sprintf(Yap_ErrorMessage, "when compiling %s/2", s); save_machine_regs(); longjmp(Yap_CompilerBotch, 1); } else if (IsVarTerm(t2)) { if (IsNewVar(t2)) { char s[32]; Yap_Error_TYPE = INSTANTIATION_ERROR; Yap_Error_Term = t2; Yap_ErrorMessage = Yap_ErrorSay; Yap_bip_name(Op, s); sprintf(Yap_ErrorMessage, "when compiling %s/2", s); save_machine_regs(); longjmp(Yap_CompilerBotch, 1); } else { /* first temp */ Int v1 = --tmpreg; /* second temp */ Int v2 = --tmpreg; Yap_emit(fetch_args_vv_op, Zero, Zero); /* these should be the arguments */ c_var(t1, v1, 0, 0); c_var(t2, v2, 0, 0); /* now we know where the arguments are */ } } else { if (Op == _arg) { Term tn = MkVarTerm(); Int v1 = --tmpreg; Int v2 = --tmpreg; c_arg(t2, v2, 0, 0); Yap_emit(fetch_args_vv_op, Zero, Zero); /* these should be the arguments */ c_var(t1, v1, 0, 0); c_var(tn, v2, 0, 0); /* it has to be either an integer or a floating point */ } else if (IsIntTerm(t2)) { /* first temp */ Int v1 = --tmpreg; Yap_emit(fetch_args_vc_op, (CELL)IntOfTerm(t2), Zero); /* these should be the arguments */ c_var(t1, v1, 0, 0); /* now we know where the arguments are */ } else if (IsLongIntTerm(t2)) { /* first temp */ Int v1 = --tmpreg; Yap_emit(fetch_args_vc_op, (CELL)LongIntOfTerm(t2), Zero); /* these should be the arguments */ c_var(t1, v1, 0, 0); /* now we know where the arguments are */ } else { char s[32]; Yap_Error_TYPE = TYPE_ERROR_NUMBER; Yap_Error_Term = t2; Yap_ErrorMessage = Yap_ErrorSay; Yap_bip_name(Op, s); sprintf(Yap_ErrorMessage, "compiling %s/2 with output bound", s); save_machine_regs(); longjmp(Yap_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]; Yap_Error_TYPE = INSTANTIATION_ERROR; Yap_Error_Term = t2; Yap_ErrorMessage = Yap_ErrorSay; Yap_bip_name(Op, s); sprintf(Yap_ErrorMessage, "compiling functor/3"); save_machine_regs(); longjmp(Yap_CompilerBotch,1); } } else { if (Op == _functor) { /* both arguments are bound, we must perform unification */ Int i2; if (!IsIntegerTerm(t2)) { char s[32]; Yap_Error_TYPE = TYPE_ERROR_INTEGER; Yap_Error_Term = t2; Yap_ErrorMessage = Yap_ErrorSay; Yap_bip_name(Op, s); sprintf(Yap_ErrorMessage, "compiling functor/3"); save_machine_regs(); longjmp(Yap_CompilerBotch,1); } i2 = IntegerOfTerm(t2); if (i2 < 0) { char s[32]; Yap_Error_TYPE = DOMAIN_ERROR_NOT_LESS_THAN_ZERO; Yap_Error_Term = t2; Yap_ErrorMessage = Yap_ErrorSay; Yap_bip_name(Op, s); sprintf(Yap_ErrorMessage, "compiling functor/3"); save_machine_regs(); longjmp(Yap_CompilerBotch,1); } if (IsNumTerm(t1)) { /* we will always fail */ if (i2) c_goal(MkAtomTerm(AtomFalse), mod); } else if (!IsAtomTerm(t1)) { char s[32]; Yap_Error_TYPE = TYPE_ERROR_ATOM; Yap_Error_Term = t2; Yap_ErrorMessage = Yap_ErrorSay; Yap_bip_name(Op, s); sprintf(Yap_ErrorMessage, "compiling functor/3"); save_machine_regs(); longjmp(Yap_CompilerBotch,1); } if (i2 == 0) c_eq(t1, t3); else { CELL *hi = H; Int i; if (t1 == TermDot && i2 == 2) { if (H+2 >= (CELL *)freep0) { /* oops, too many new variables */ save_machine_regs(); longjmp(Yap_CompilerBotch,4); } RESET_VARIABLE(H); RESET_VARIABLE(H+1); H += 2; c_eq(AbsPair(H-2),t3); } else { *H++ = (CELL)Yap_MkFunctor(AtomOfTerm(t1),i2); for (i=0; i < i2; i++) { if (H >= (CELL *)freep0) { /* oops, too many new variables */ save_machine_regs(); longjmp(Yap_CompilerBotch,4); } RESET_VARIABLE(H); H++; } c_eq(AbsAppl(hi),t3); } } } else if (Op == _arg) { Int i1; if (IsIntegerTerm(t1)) i1 = IntegerOfTerm(t1); else { char s[32]; Yap_Error_TYPE = TYPE_ERROR_INTEGER; Yap_Error_Term = t2; Yap_ErrorMessage = Yap_ErrorSay; Yap_bip_name(Op, s); sprintf(Yap_ErrorMessage, "compiling %s/2", s); save_machine_regs(); longjmp(Yap_CompilerBotch,1); } if (IsAtomicTerm(t2) || (IsApplTerm(t2) && IsExtensionFunctor(FunctorOfTerm(t2)))) { char s[32]; Yap_Error_TYPE = TYPE_ERROR_COMPOUND; Yap_Error_Term = t2; Yap_ErrorMessage = Yap_ErrorSay; Yap_bip_name(Op, s); sprintf(Yap_ErrorMessage, "compiling %s/2", s); save_machine_regs(); longjmp(Yap_CompilerBotch,1); } else if (IsApplTerm(t2)) { Functor f = FunctorOfTerm(t2); if (i1 < 1 || i1 > ArityOfFunctor(f)) { c_goal(MkAtomTerm(AtomFalse), mod); } else { c_eq(ArgOfTerm(i1, t2), t3); } return; } else if (IsPairTerm(t2)) { switch (i1) { case 1: c_eq(HeadOfTerm(t2), t3); return; case 2: c_eq(TailOfTerm(t2), t3); return; default: c_goal(MkAtomTerm(AtomFalse), mod); return; } } } else { char s[32]; Yap_Error_TYPE = TYPE_ERROR_INTEGER; Yap_Error_Term = t2; Yap_ErrorMessage = Yap_ErrorSay; Yap_bip_name(Op, s); sprintf(Yap_ErrorMessage, "compiling %s/2", s); save_machine_regs(); longjmp(Yap_CompilerBotch,1); } } if (Op == _functor) { if (!IsAtomicTerm(t1)) { char s[32]; Yap_Error_TYPE = TYPE_ERROR_ATOM; Yap_Error_Term = t1; Yap_ErrorMessage = Yap_ErrorSay; Yap_bip_name(Op, s); sprintf(Yap_ErrorMessage, "compiling %s/2", s); save_machine_regs(); longjmp(Yap_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]; Yap_Error_TYPE = TYPE_ERROR_INTEGER; Yap_Error_Term = t2; Yap_ErrorMessage = Yap_ErrorSay; Yap_bip_name(Op, s); sprintf(Yap_ErrorMessage, "compiling %s/2", s); save_machine_regs(); longjmp(Yap_CompilerBotch,1); } arity = IntOfTerm(t2); if (arity < 0) { /* fail straight away */ Yap_emit(fail_op, Zero, Zero); } if (arity) { Term tnew; if (!IsAtomTerm(t1)) { char s[32]; Yap_Error_TYPE = TYPE_ERROR_ATOM; Yap_Error_Term = t1; Yap_ErrorMessage = Yap_ErrorSay; Yap_bip_name(Op, s); sprintf(Yap_ErrorMessage, "compiling %s/2", s); save_machine_regs(); longjmp(Yap_CompilerBotch,1); } if (H+1+arity >= (CELL *)freep0) { /* oops, too many new variables */ save_machine_regs(); longjmp(Yap_CompilerBotch,4); } tnew = AbsAppl(H); *H++ = (CELL)Yap_MkFunctor(AtomOfTerm(t1),arity); while (arity--) { RESET_VARIABLE(H); H++; } c_eq(tnew, t3); } else { /* just unify the two arguments */ c_eq(t1,t3); } return; } else { /* first temp */ Int v1 = --tmpreg; Yap_emit(fetch_args_cv_op, t1, Zero); /* these should be the arguments */ c_var(t2, v1, 0, 0); /* now we know where the arguments are */ } } } else if (IsIntTerm(t1)) { /* first temp */ Int v1 = --tmpreg; Yap_emit(fetch_args_cv_op, (CELL)IntOfTerm(t1), Zero); /* these should be the arguments */ c_var(t2, v1, 0, 0); /* now we know where the arguments are */ } else if (IsLongIntTerm(t1)) { /* first temp */ Int v1 = --tmpreg; Yap_emit(fetch_args_cv_op, (CELL)LongIntOfTerm(t1), Zero); /* these should be the arguments */ c_var(t2, v1, 0, 0); /* now we know where the arguments are */ } else { char s[32]; Yap_Error_TYPE = TYPE_ERROR_VARIABLE; Yap_Error_Term = t1; Yap_ErrorMessage = Yap_ErrorSay; Yap_bip_name(Op, s); sprintf(Yap_ErrorMessage, "compiling %s/2 with output bound", s); save_machine_regs(); longjmp(Yap_CompilerBotch,1); } } /* then we compile the opcode/result */ if (!IsVarTerm(t3)) { if (Op == _arg) { Term tmpvar = MkVarTerm(); if (H == (CELL *)freep0) { /* oops, too many new variables */ save_machine_regs(); longjmp(Yap_CompilerBotch,4); } c_var(tmpvar,f_flag,(unsigned int)Op, 0); c_eq(tmpvar,t3); } else { char s[32]; Yap_Error_TYPE = TYPE_ERROR_VARIABLE; Yap_Error_Term = t3; Yap_ErrorMessage = Yap_ErrorSay; Yap_bip_name(Op, s); sprintf(Yap_ErrorMessage, "compiling %s/2 with input unbound", s); save_machine_regs(); longjmp(Yap_CompilerBotch,1); } } else if (IsNewVar(t3) && cur_branch == 0 /* otherwise you may have trouble with z(X) :- ( Z is X*2 ; write(Z)) */) { c_var(t3,f_flag,(unsigned int)Op, 0); if (Op == _functor) { Yap_emit(empty_call_op, Zero, Zero); Yap_emit(restore_tmps_and_skip_op, Zero, Zero); } } else { /* generate code for a temp and then unify temp with previous variable */ Term tmpvar = MkVarTerm(); if (H == (CELL *)freep0) { /* oops, too many new variables */ save_machine_regs(); longjmp(Yap_CompilerBotch,4); } c_var(tmpvar,f_flag,(unsigned int)Op, 0); /* I have to dit here, before I do the unification */ if (Op == _functor) { Yap_emit(empty_call_op, Zero, Zero); Yap_emit(restore_tmps_and_skip_op, Zero, Zero); } c_eq(tmpvar,t3); } } static void c_functor(Term Goal, int mod) { 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, mod); } else if (IsNonVarTerm(t1)) { /* just split the structure */ if (IsAtomicTerm(t1)) { c_eq(t1,t2); c_eq(t3,MkIntTerm(0)); } else if (IsApplTerm(t1)) { Functor f = FunctorOfTerm(t1); c_eq(t2,MkAtomTerm(NameOfFunctor(f))); c_eq(t3,MkIntegerTerm(ArityOfFunctor(f))); } else /* list */ { c_eq(t2,TermDot); c_eq(t3,MkIntTerm(2)); } } else if (IsVarTerm(t2) && IsNewVar(t2) && IsVarTerm(t3) && IsNewVar(t3)) { Int v1 = --tmpreg; Yap_emit(fetch_args_vc_op, Zero, Zero); c_var(t1, v1, 0, 0); c_var(t2,f_flag,(unsigned int)_functor, 0); c_var(t3,f_flag,(unsigned int)_functor, 0); } else { Functor f = FunctorOfTerm(Goal); Prop p0 = PredPropByFunc(f, mod); if (profiling) Yap_emit(enter_profiling_op, (CELL)RepPredProp(p0), Zero); else if (call_counting) Yap_emit(count_call_op, (CELL)RepPredProp(p0), Zero); c_args(Goal, 0); Yap_emit(safe_call_op, (CELL)p0 , Zero); Yap_emit(empty_call_op, Zero, Zero); Yap_emit(restore_tmps_and_skip_op, Zero, Zero); } } 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 == FunctorArrow) { return(IsTrueGoal(ArgOfTerm(1,t)) && IsTrueGoal(ArgOfTerm(2,t))); } return(FALSE); } return(t == MkAtomTerm(AtomTrue)); } static void c_goal(Term Goal, int mod) { 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)) { if (IsVarTerm(M)) { Yap_Error_TYPE = INSTANTIATION_ERROR; } else { Yap_Error_TYPE = TYPE_ERROR_ATOM; } Yap_Error_Term = M; Yap_ErrorMessage = "in module name"; save_machine_regs(); longjmp(Yap_CompilerBotch, 1); } Goal = ArgOfTerm(2, Goal); mod = Yap_LookupModule(M); } if (IsVarTerm(Goal)) { Goal = Yap_MkApplTerm(FunctorCall, 1, &Goal); } else if (IsNumTerm(Goal)) { FAIL("goal can not be a number", TYPE_ERROR_CALLABLE, Goal); } else if (IsRefTerm(Goal)) { Yap_Error_TYPE = TYPE_ERROR_DBREF; Yap_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); return; } else if (atom == AtomTrue || atom == AtomOtherwise) { if (onlast) { Yap_emit(deallocate_op, Zero, Zero); #ifdef TABLING if (is_tabled(CurrentPred)) Yap_emit(table_new_answer_op, Zero, CurrentPred->ArityOfPE); else #endif /* TABLING */ Yap_emit(procceed_op, Zero, Zero); } return; } else if (atom == AtomCut) { if (profiling) Yap_emit(enter_profiling_op, (CELL)RepPredProp(PredPropByAtom(AtomCut,0)), Zero); else if (call_counting) Yap_emit(count_call_op, (CELL)RepPredProp(PredPropByAtom(AtomCut,0)), Zero); if (onlast) { /* never a problem here with a -> b, !, c ; d */ Yap_emit(deallocate_op, Zero, Zero); #ifdef TABLING READ_LOCK(CurrentPred->PRWLock); if (is_tabled(CurrentPred)) { Yap_emit_3ops(cut_op, Zero, Zero, Zero); Yap_emit(table_new_answer_op, Zero, CurrentPred->ArityOfPE); } else #endif /* TABLING */ { Yap_emit_3ops(cutexit_op, Zero, Zero, Zero); } #ifdef TABLING READ_UNLOCK(CurrentPred->PRWLock); #endif } else { Yap_emit_3ops(cut_op, Zero, Zero, Zero); /* needs to adjust previous commits */ adjust_current_commits(); } return; } #ifndef YAPOR else if (atom == AtomRepeat) { CELL l1 = ++labelno; CELL l2 = ++labelno; if (profiling) Yap_emit(enter_profiling_op, (CELL)RepPredProp(PredPropByAtom(AtomRepeat,0)), Zero); else if (call_counting) Yap_emit(count_call_op, (CELL)RepPredProp(PredPropByAtom(AtomRepeat,0)), Zero); or_found = 1; push_branch(onbranch, TermNil); cur_branch++; onbranch = cur_branch; if (onlast) Yap_emit(deallocate_op, Zero, Zero); Yap_emit_3ops(push_or_op, l1, Zero, Zero); Yap_emit_3ops(either_op, l1, Zero, Zero); Yap_emit(restore_tmps_op, Zero, Zero); Yap_emit(jump_op, l2, Zero); Yap_emit(label_op, l1, Zero); Yap_emit(pushpop_or_op, Zero, Zero); Yap_emit_3ops(orelse_op, l1, Zero, Zero); Yap_emit(label_op, l2, Zero); if (onlast) { #ifdef TABLING READ_LOCK(CurrentPred->PRWLock); if (is_tabled(CurrentPred)) Yap_emit(table_new_answer_op, Zero, CurrentPred->ArityOfPE); else #endif /* TABLING */ Yap_emit(procceed_op, Zero, Zero); #ifdef TABLING READ_UNLOCK(CurrentPred->PRWLock); #endif } else ++goalno; onbranch = pop_branch(); Yap_emit(pop_or_op, Zero, Zero); /* --onbranch; */ return; } #endif /* YAPOR */ p = RepPredProp(p0 = PredPropByAtom(atom, mod)); /* if we are profiling, make sure we register we entered this predicate */ if (profiling) Yap_emit(enter_profiling_op, (CELL)p, Zero); if (call_counting) Yap_emit(count_call_op, (CELL)p, Zero); } else { f = FunctorOfTerm(Goal); p = RepPredProp(p0 = PredPropByFunc(f, mod)); if (f == FunctorOr) { CELL l = ++labelno; CELL m = ++labelno; Term arg; int save = onlast; int savegoalno = goalno; int frst = TRUE; int commitflag = 0; int looking_at_commit = FALSE; int optimizing_commit = FALSE; Term commitvar = 0; PInstr *FirstP = cpc, *savecpc, *savencpc; push_branch(onbranch, TermNil); ++cur_branch; onbranch = cur_branch; or_found = 1; do { arg = ArgOfTerm(1, Goal); looking_at_commit = IsApplTerm(arg) && FunctorOfTerm(arg) == FunctorArrow; if (frst) { if (optimizing_commit) { Yap_emit(label_op, l, Zero); l = ++labelno; } Yap_emit_3ops(push_or_op, l, Zero, Zero); 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); optimizing_commit = TRUE; } else { optimizing_commit = FALSE; Yap_emit_3ops(either_op, l, Zero, Zero); Yap_emit(restore_tmps_op, Zero, Zero); frst = FALSE; } } else { optimizing_commit = FALSE; Yap_emit(label_op, l, Zero); Yap_emit(pushpop_or_op, Zero, Zero); Yap_emit_3ops(orelse_op, l = ++labelno, Zero, Zero); } /* * if(IsApplTerm(arg) && * FunctorOfTerm(arg)==FunctorArrow) { */ if (looking_at_commit) { if (!optimizing_commit && !commitflag) { /* 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 = goalno; goalno = savegoalno; commitflag = labelno; commitvar = MkVarTerm(); if (H == (CELL *)freep0) { /* oops, too many new variables */ save_machine_regs(); longjmp(Yap_CompilerBotch,4); } savecpc = cpc; savencpc = FirstP->nextInst; cpc = FirstP; onbranch = pop_branch(); c_var(commitvar, save_b_flag, 1, 0); push_branch(onbranch, commitvar); onbranch = cur_branch; cpc->nextInst = savencpc; cpc = savecpc; goalno = my_goalno; } save = onlast; onlast = FALSE; c_goal(ArgOfTerm(1, arg), mod); if (!optimizing_commit) { c_var((Term) commitvar, commit_b_flag, 1, 0); } onlast = save; c_goal(ArgOfTerm(2, arg), mod); } else c_goal(ArgOfTerm(1, Goal), mod); if (!onlast) { Yap_emit(jump_op, m, Zero); } goalno = savegoalno + 1; Goal = ArgOfTerm(2, Goal); ++cur_branch; onbranch = cur_branch; } while (IsNonVarTerm(Goal) && IsApplTerm(Goal) && FunctorOfTerm(Goal) == FunctorOr); Yap_emit(pushpop_or_op, Zero, Zero); Yap_emit(label_op, l, Zero); if (!optimizing_commit) Yap_emit(orlast_op, Zero, Zero); else { optimizing_commit = FALSE; /* not really necessary */ } c_goal(Goal, mod); /* --onbranch; */ onbranch = pop_branch(); if (!onlast) { Yap_emit(label_op, m, Zero); if ((onlast = save)) c_goal(MkAtomTerm(AtomTrue), mod); } Yap_emit(pop_or_op, Zero, Zero); return; } else if (f == FunctorComma) { int save = onlast; int t2 = ArgOfTerm(2, Goal); onlast = FALSE; c_goal(ArgOfTerm(1, Goal), mod); onlast = save; c_goal(t2, mod); return; } else if (f == FunctorNot || f == FunctorAltNot) { CELL label = (labelno += 2); CELL end_label = (labelno += 2); int save = onlast; Term commitvar; commitvar = MkVarTerm(); if (H == (CELL *)freep0) { /* oops, too many new variables */ save_machine_regs(); longjmp(Yap_CompilerBotch,4); } push_branch(onbranch, commitvar); ++cur_branch; onbranch = cur_branch; or_found = 1; onlast = FALSE; c_var(commitvar, save_b_flag, 1, 0); Yap_emit_3ops(push_or_op, label, Zero, Zero); Yap_emit_3ops(either_op, label, Zero, Zero); Yap_emit(restore_tmps_op, Zero, Zero); c_goal(ArgOfTerm(1, Goal), mod); c_var(commitvar, commit_b_flag, 1, 0); onlast = save; Yap_emit(fail_op, end_label, Zero); Yap_emit(pushpop_or_op, Zero, Zero); Yap_emit(label_op, label, Zero); Yap_emit(orlast_op, Zero, Zero); Yap_emit(label_op, end_label, Zero); onlast = save; /* --onbranch; */ onbranch = pop_branch(); c_goal(MkAtomTerm(AtomTrue), mod); ++goalno; Yap_emit(pop_or_op, Zero, Zero); return; } else if (f == FunctorArrow) { Term commitvar; int save = onlast; commitvar = MkVarTerm(); if (H == (CELL *)freep0) { /* oops, too many new variables */ save_machine_regs(); longjmp(Yap_CompilerBotch,4); } onlast = FALSE; c_var(commitvar, save_b_flag, 1, 0); c_goal(ArgOfTerm(1, Goal), mod); c_var(commitvar, commit_b_flag, 1, 0); onlast = save; c_goal(ArgOfTerm(2, Goal), mod); return; } else if (f == FunctorEq) { if (profiling) Yap_emit(enter_profiling_op, (CELL)p, Zero); else if (call_counting) Yap_emit(count_call_op, (CELL)p, Zero); c_eq(ArgOfTerm(1, Goal), ArgOfTerm(2, Goal)); if (onlast) { Yap_emit(deallocate_op, Zero, Zero); #ifdef TABLING READ_LOCK(CurrentPred->PRWLock); if (is_tabled(CurrentPred)) Yap_emit(table_new_answer_op, Zero, CurrentPred->ArityOfPE); else #endif /* TABLING */ Yap_emit(procceed_op, Zero, Zero); #ifdef TABLING READ_UNLOCK(CurrentPred->PRWLock); #endif } return; } else if (p->PredFlags & AsmPredFlag) { int op = p->PredFlags & 0x7f; if (profiling) Yap_emit(enter_profiling_op, (CELL)p, Zero); else if (call_counting) Yap_emit(count_call_op, (CELL)p, Zero); if (op >= _atom && op <= _primitive) { c_test(op, ArgOfTerm(1, Goal)); if (onlast) { Yap_emit(deallocate_op, Zero, Zero); #ifdef TABLING READ_LOCK(CurrentPred->PRWLock); if (is_tabled(CurrentPred)) Yap_emit(table_new_answer_op, Zero, CurrentPred->ArityOfPE); else #endif /* TABLING */ Yap_emit(procceed_op, Zero, Zero); #ifdef TABLING READ_UNLOCK(CurrentPred->PRWLock); #endif } return; } else if (op >= _plus && op <= _functor) { if (op == _functor) { c_functor(Goal, mod); } else { c_bifun(op, ArgOfTerm(1, Goal), ArgOfTerm(2, Goal), ArgOfTerm(3, Goal), mod); } if (onlast) { Yap_emit(deallocate_op, Zero, Zero); #ifdef TABLING READ_LOCK(CurrentPred->PRWLock); if (is_tabled(CurrentPred)) Yap_emit(table_new_answer_op, Zero, CurrentPred->ArityOfPE); else #endif /* TABLING */ Yap_emit(procceed_op, Zero, Zero); #ifdef TABLING READ_UNLOCK(CurrentPred->PRWLock); #endif } return; } else { c_args(Goal, 0); } } else if (p->PredFlags & BinaryTestPredFlag) { Term a1 = ArgOfTerm(1,Goal); if (IsVarTerm(a1) && !IsNewVar(a1)) { Term a2 = ArgOfTerm(2,Goal); if (IsVarTerm(a2) && !IsNewVar(a2)) { if (IsNewVar(a2)) { Yap_Error_TYPE = INSTANTIATION_ERROR; Yap_Error_Term = a2; Yap_ErrorMessage = Yap_ErrorSay; sprintf(Yap_ErrorMessage, "compiling %s/2 with second arg unbound", RepAtom(NameOfFunctor(p->FunctorOfPred))->StrOfAE); save_machine_regs(); longjmp(Yap_CompilerBotch,1); } c_var(a1, bt1_flag, 2, 0); current_p0 = p0; c_var(a2, bt2_flag, 2, 0); } else { Term t2 = MkVarTerm(); if (H == (CELL *)freep0) { /* oops, too many new variables */ save_machine_regs(); longjmp(Yap_CompilerBotch,4); } c_eq(t2, a2); c_var(a1, bt1_flag, 2, 0); current_p0 = p0; c_var(t2, bt2_flag, 2, 0); } } else { Term a2 = ArgOfTerm(2,Goal); Term t1 = MkVarTerm(); if (H == (CELL *)freep0) { /* oops, too many new variables */ save_machine_regs(); longjmp(Yap_CompilerBotch,4); } c_eq(t1, a1); if (IsVarTerm(a2) && !IsNewVar(a2)) { c_var(t1, bt1_flag, 2, 0); current_p0 = p0; c_var(a2, bt2_flag, 2, 0); } else { Term t2 = MkVarTerm(); if (H == (CELL *)freep0) { /* oops, too many new variables */ save_machine_regs(); longjmp(Yap_CompilerBotch,4); } c_eq(t2, a2); c_var(t1, bt1_flag, 2, 0); current_p0 = p0; c_var(t2, bt2_flag, 2, 0); } } if (onlast) { Yap_emit(deallocate_op, Zero, Zero); #ifdef TABLING READ_LOCK(CurrentPred->PRWLock); if (is_tabled(CurrentPred)) Yap_emit(table_new_answer_op, Zero, CurrentPred->ArityOfPE); else #endif /* TABLING */ Yap_emit(procceed_op, Zero, Zero); #ifdef TABLING READ_UNLOCK(CurrentPred->PRWLock); #endif } return; } else { if (profiling) Yap_emit(enter_profiling_op, (CELL)p, Zero); else if (call_counting) Yap_emit(count_call_op, (CELL)p, Zero); if (f == FunctorExecuteInMod) { /* compile the first argument only */ c_arg(1, ArgOfTerm(1,Goal), 1, 0); } else { c_args(Goal, 0); } } } 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); if (onlast) { Yap_emit(deallocate_op, Zero, Zero); #ifdef TABLING READ_LOCK(CurrentPred->PRWLock); if (is_tabled(CurrentPred)) Yap_emit(table_new_answer_op, Zero, CurrentPred->ArityOfPE); else #endif /* TABLING */ Yap_emit(procceed_op, Zero, Zero); #ifdef TABLING READ_UNLOCK(CurrentPred->PRWLock); #endif } } else { if (p->PredFlags & (CPredFlag | AsmPredFlag)) { #ifdef YAPOR if (p->PredFlags & SyncPredFlag) Yap_emit(sync_op, (CELL)p, (CELL)(p->ArityOfPE)); #endif /* YAPOR */ if (p->FunctorOfPred == FunctorExecuteInMod) { Yap_emit_4ops(call_op, (CELL) p0, Zero, Zero, ArgOfTerm(2,Goal)); } else { Yap_emit_3ops(call_op, (CELL) p0, Zero, Zero); } /* functor is allowed to call the garbage collector */ if (onlast) { Yap_emit(deallocate_op, Zero, Zero); or_found = 1; #ifdef TABLING READ_LOCK(CurrentPred->PRWLock); if (is_tabled(CurrentPred)) Yap_emit(table_new_answer_op, Zero, CurrentPred->ArityOfPE); else #endif /* TABLING */ Yap_emit(procceed_op, Zero, Zero); #ifdef TABLING READ_UNLOCK(CurrentPred->PRWLock); #endif } } else { if (onlast) { Yap_emit(deallocate_op, Zero, Zero); #ifdef TABLING READ_LOCK(CurrentPred->PRWLock); if (is_tabled(CurrentPred)) { Yap_emit_3ops(call_op, (CELL) p0, Zero, Zero); Yap_emit(table_new_answer_op, Zero, CurrentPred->ArityOfPE); } else #endif /* TABLING */ Yap_emit(execute_op, (CELL) p0, Zero); #ifdef TABLING READ_UNLOCK(CurrentPred->PRWLock); #endif } else { Yap_emit_3ops(call_op, (CELL) p0, Zero, Zero); } } if (!onlast) ++goalno; } } static void c_body(Term Body, int mod) { onhead = FALSE; BodyStart = cpc; goalno = 1; while (IsNonVarTerm(Body) && IsApplTerm(Body) && FunctorOfTerm(Body) == FunctorComma) { Term t2 = ArgOfTerm(2, Body); if (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); Body = t2; } onlast = TRUE; c_goal(Body, mod); } static void c_head(Term t) { Functor f; goalno = 0; onhead = TRUE; onlast = FALSE; cur_branch = onbranch = 0; branch_pointer = parent_branches; if (IsAtomTerm(t)) { Yap_emit(name_op, (CELL) AtomOfTerm(t), Zero); return; } f = FunctorOfTerm(t); Yap_emit(name_op, (CELL) NameOfFunctor(f), ArityOfFunctor(f)); c_args(t, 0); } /* number of permanent variables in the clause */ static int nperm; inline static int usesvar(int 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 fetch_args_for_bccall: case bccall_op: return (TRUE); } #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)); } /* * 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 AssignPerm(PInstr *pc) { 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)); x->Next = EnvTmps; x->Var = v; EnvTmps = x; } } #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); if (v->NoOfVE == Unassigned) { if ((v->AgeOfVE > 1 && (v->AgeOfVE > v->FirstOfVE)) || v->KindOfVE == PermVar /* * * || (v->FlagsOfVE & NonVoid && !(v->FlagsOfVE & * * OnHeadFlag)) */ ) { v->NoOfVE = PermVar | (nperm++); v->KindOfVE = PermVar; v->FlagsOfVE |= PermFlag; } else v->NoOfVE = v->KindOfVE = TempVar; } } else if (pc->op == empty_call_op) { pc->rnd2 = 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 | (nperm++); v->KindOfVE = PermVar; v->FlagsOfVE |= (PermFlag|SafeVar); EnvTmps = EnvTmps->Next; } #endif pc->rnd2 = nperm; } else if (pc->op == cut_op || pc->op == cutexit_op) { pc->rnd2 = nperm; } opc = pc; pc = npc; } while (pc != NULL); } static CELL * init_bvarray(int nperm) { CELL *vinfo = NULL; unsigned int i; CELL *vptr; vptr = vinfo = (CELL *)Yap_AllocCMem(sizeof(CELL)*(1+nperm/(8*sizeof(CELL)))); for (i = 0; i <= nperm/(8*sizeof(CELL)); i++) { *vptr++ = (CELL)(0L); } return(vinfo); } static void clear_bvarray(int var, CELL *bvarray) { 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 = (1 << var); #ifdef DEBUG if (*bvarray & nbit) { /* someone had already marked this variable: complain */ Yap_Error_TYPE = SYSTEM_ERROR; Yap_Error_Term = TermNil; Yap_ErrorMessage = "repeated bit for variable"; save_machine_regs(); longjmp(Yap_CompilerBotch, 2); } 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) { 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)); /* 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 32 static bventry *bvstack; static int bvindex = 0; static void push_bvmap(int label, PInstr *pcpc) { if (bvindex == MAX_DISJUNCTIONS) { Yap_Error_TYPE = SYSTEM_ERROR; Yap_Error_Term = TermNil; Yap_ErrorMessage = "Too many embedded disjunctions"; save_machine_regs(); longjmp(Yap_CompilerBotch, 2); } /* 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) { int size, size1, env_size, i; CELL *source; if (bvindex == 0) { Yap_Error_TYPE = SYSTEM_ERROR; Yap_Error_Term = TermNil; Yap_ErrorMessage = "No embedding in disjunctions"; save_machine_regs(); longjmp(Yap_CompilerBotch, 2); } 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) { if (bvindex == 0) { Yap_Error_TYPE = SYSTEM_ERROR; Yap_Error_Term = TermNil; Yap_ErrorMessage = "Too few embedded disjunctions"; /* save_machine_regs(); longjmp(Yap_CompilerBotch, 2); */ } reset_bvmap(bvarray, nperm); bvindex--; } typedef struct { PInstr *p; Ventry *v; } UnsafeEntry; /* extend to also support variable usage bitmaps for garbage collection */ static void CheckUnsafe(PInstr *pc) { int pending = 0; /* say that all variables are yet to initialise */ CELL *vstat = init_bvarray(nperm); UnsafeEntry *UnsafeStack = (UnsafeEntry *) Yap_AllocCMem(nperm * sizeof(UnsafeEntry)); /* keep a copy of previous cpc and CodeStart */ PInstr *opc = cpc; PInstr *OldCodeStart = CodeStart; CodeStart = BlobsStart; cpc = icpc; bvindex = 0; bvstack = (bventry *)Yap_AllocCMem(MAX_DISJUNCTIONS * sizeof(bventry)); 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 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); } } break; case push_or_op: Yap_emit(label_op, ++labelno, Zero); pc->ops.opseqt[1] = (CELL)labelno; add_bvarray_op(pc, vstat, pc->rnd2); push_bvmap((CELL)labelno, cpc); break; case either_op: /* add a first entry to the array */ Yap_emit(label_op, ++labelno, Zero); pc->ops.opseqt[1] = (CELL)labelno; add_bvarray_op(pc, vstat, pc->rnd2); break; case pushpop_or_op: reset_bvmap(vstat, nperm); break; case orelse_op: Yap_emit(label_op, ++labelno, Zero); pc->ops.opseqt[1] = (CELL)labelno; add_bvarray_op(pc, vstat, pc->rnd2); break; case pop_or_op: pop_bvmap(vstat, nperm); break; case empty_call_op: /* just get ourselves a label describing how many permanent variables are alive */ Yap_emit(label_op, ++labelno, Zero); pc->rnd1 = (CELL)labelno; add_bvarray_op(pc, vstat, pc->rnd2); break; case cut_op: case cutexit_op: /* just get ourselves a label describing how many permanent variables are alive */ Yap_emit(label_op, ++labelno, Zero); pc->rnd1 = (CELL)labelno; add_bvarray_op(pc, vstat, pc->rnd2); break; case call_op: Yap_emit(label_op, ++labelno, Zero); pc->ops.opseqt[1] = (CELL)labelno; add_bvarray_op(pc, vstat, pc->rnd2); case deallocate_op: { 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; } icpc = cpc; cpc = opc; BlobsStart = CodeStart; CodeStart = OldCodeStart; } static void CheckVoids(void) { /* establish voids in the head and initial * uses */ Ventry *ve; cpc = CodeStart; while ((ic = cpc->op) != allocate_op) { ic = cpc->op; #ifdef M_WILLIAMS switch ((int) ic) { #else switch (ic) { #endif 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_longint_op: case get_bigint_op: case get_list_op: case get_struct_op: Uses[cpc->rnd2] = 1; } cpc = cpc->nextInst; } } static int checktemp(void) { Ventry *v = (Ventry *) arg; PInstr *q; Int Needed[MaxTemps]; Int r, target1, target2; Int n, *np, *rp; CELL *cp; vadr = (v->NoOfVE); vreg = vadr & MaskVarAdrs; if (v->KindOfVE == PermVar || v->KindOfVE == VoidVar) return (0); if (v->RCountOfVE == 1) return(0); if (vreg) { --Uses[vreg]; return (1); } /* follow the life of the variable */ q = cpc; /* * for(r=0; r 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 = MaxCTemps; target2 = MaxCTemps; n = v->RCountOfVE - 1; while ((q = q->nextInst) != NIL) { if (q->rnd2 < 0); else if (usesvar(ic = q->op) && arg == q->rnd1) { --n; if (ic == put_val_op) { if (target1 == MaxCTemps && Needed[q->rnd2] == 0) target1 = q->rnd2; else if (target1 != (r = q->rnd2)) { if (target2 == MaxCTemps && Needed[r] == 0) target2 = r; else if (target2 > r && 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 == MaxCTemps || Uses[target1] || Needed[target1]) if ((target1 = target2) == MaxCTemps || Uses[target1] || Needed[target1]) { target1 = MaxCTemps; do --target1; while (target1 && Uses[target1] == 0 && Needed[target1] == 0); ++target1; } if (target1 == MaxCTemps) { Yap_Error_TYPE = SYSTEM_ERROR; Yap_Error_Term = TermNil; Yap_ErrorMessage = "too many temporaries"; save_machine_regs(); longjmp(Yap_CompilerBotch, 1); } v->NoOfVE = vadr = TempVar | target1; v->KindOfVE = TempVar; Uses[vreg = target1] = v->RCountOfVE - 1; /* * for(r=0; rop; return (1); } static void checkreg(int var_arg) { PInstr *p = cpc; if (rn >= 0) return; vreg = 0; if (var_arg) { Ventry *v = (Ventry *) arg; vreg = (v->NoOfVE) & MaskVarAdrs; if (v->KindOfVE == PermVar) vreg = 0; else if (vreg == 0) { checktemp(); ++Uses[vreg]; } } if (vreg == 0) { vreg = MaxCTemps; do --vreg; while (vreg && Uses[vreg] == 0); ++vreg; ++Uses[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; } rn = vreg; } /* Create a bitmap with all live variables */ static CELL copy_live_temps_bmap(int max) { unsigned int size = (max|7)/8+1; int i; CELL *dest = Yap_emit_extra_size(mark_live_regs_op, max, size); 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 (Contents[i]) { int j = i%(8*CellSize); *ptr |= (1<nextInst; register Ventry *v = vtable; Int *up = Uses, Arity; CELL *cop = Contents; cpc = BodyStart; while (v != NIL) { if (v->FlagsOfVE & BranchVar) { v->AgeOfVE = v->FirstOfVE + 1; /* force permanent */ ++(v->RCountOfVE); Yap_emit(put_var_op, (CELL) v, Zero); v->FlagsOfVE &= ~GlobalVal; v->FirstOpForV = cpc; } v = v->NextOfVE; } cpc->nextInst = savepc; nperm = 0; AssignPerm(CodeStart); /* vsc: need to do it from the beginning to find which perm vars are active */ /* CheckUnsafe(BodyStart); */ #ifdef DEBUG pbvars = 0; #endif CheckUnsafe(CodeStart); #ifdef DEBUG if (pbvars != nperm) { Yap_Error_TYPE = SYSTEM_ERROR; Yap_Error_Term = TermNil; Yap_ErrorMessage = "wrong number of variables found in bitmap"; save_machine_regs(); longjmp(Yap_CompilerBotch, 2); } #endif MaxCTemps = nvars + max_args - tmpreg + n_common_exps + 2; if (MaxCTemps >= MaxTemps) MaxCTemps = MaxTemps; for (rn = 0; rn < MaxCTemps; ++rn) { /* Uses[rn] = 0; Contents[rn] = NIL; */ *up++ = 0; *cop++ = NIL; } CheckVoids(); /* second scan: allocate registers */ cpc = CodeStart; while (cpc) { ic = cpc->op; arg = cpc->rnd1; rn = cpc->rnd2; #ifdef M_WILLIAMS switch ((int) ic) { #else switch (ic) { #endif #ifdef TABLING_INNER_CUTS case cut_op: case cutexit_op: cut_mark->op = clause_with_cut_op; break; #endif /* TABLING_INNER_CUTS */ case allocate_op: case deallocate_op: #ifdef TABLING READ_LOCK(CurrentPred->PRWLock); if (is_tabled(CurrentPred)) cpc->op = nop_op; else #endif /* TABLING */ if (goalno == 1 && or_found == 0 && nperm == 0) cpc->op = nop_op; #ifdef TABLING READ_UNLOCK(CurrentPred->PRWLock); #endif break; case pop_op: ic = (cpc->nextInst)->op; if (ic >= get_var_op && ic <= put_unsafe_op) cpc->op = nop_op; break; case get_var_op: --Uses[rn]; if (checktemp()) { if (vreg == rn) cpc->op = nop_op; } Contents[rn] = vadr; break; case get_val_op: --Uses[rn]; checktemp(); Contents[rn] = vadr; break; case f_var_op: 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 case fetch_args_for_bccall: case bccall_op: checktemp(); break; case get_atom_op: case get_num_op: case get_float_op: case get_longint_op: case get_bigint_op: --Uses[rn]; Contents[rn] = arg; break; case get_list_op: case get_struct_op: Contents[rn] = NIL; --Uses[rn]; break; case put_var_op: case put_unsafe_op: checkreg(TRUE); checktemp(); Contents[rn] = vadr; ++Uses[rn]; break; case put_val_op: checkreg(TRUE); checktemp(); if (Contents[rn] == (Term)vadr) cpc->op = nop_op; Contents[rn] = vadr; ++Uses[rn]; break; #ifdef SFUNC case write_s_var_op: { Ventry *ve = (Ventry *) arg; if ((ve->FlagsOfVE & PermFlag) == 0 && ve->RCountOfVE <= 1) cpc->op = nop_op; } break; case write_s_val_op: #endif case write_var_op: case write_val_op: case f_val_op: checktemp(); break; #ifdef SFUNC case put_s_f_op: Contents[rn] = arg; ++Uses[rn]; break; #endif case put_atom_op: case put_num_op: case put_float_op: case put_longint_op: case put_bigint_op: checkreg(FALSE); if (Contents[rn] == arg) cpc->op = nop_op; Contents[rn] = arg; ++Uses[rn]; break; case put_list_op: case put_struct_op: checkreg(FALSE); Contents[rn] = NIL; ++Uses[rn]; break; case commit_b_op: #ifdef TABLING_INNER_CUTS 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(); break; case safe_call_op: Arity = RepPredProp((Prop) arg)->ArityOfPE; for (rn = 1; rn <= Arity; ++rn) --Uses[rn]; break; case call_op: case label_op: /* * for(rn=1; rnops.opseqt[1] = max; } 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 = cpc, *oldCodeStart = CodeStart; int i, max; /* instructions must be placed at BlobsStart */ CodeStart = BlobsStart; cpc = icpc; max = 0; for (i = 1; i < MaxCTemps; ++i) { if (Contents[i]) max = i; } Yap_emit(label_op, ++labelno, Zero); mycpc->rnd1 = labelno; rn = copy_live_temps_bmap(max); icpc = cpc; BlobsStart = CodeStart; cpc = mycpc; CodeStart = oldCodeStart; } break; } if (cpc->nextInst) cpc = cpc->nextInst; else return; } } static void c_optimize(PInstr *pc) { char onTail; Ventry *v; PInstr *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 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_bigint_op: case unify_last_longint_op: case unify_last_bigint_op: case write_longint_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); } yamop * Yap_cclause(Term inp_clause, int NOfArgs, int mod, Term src) { /* compile a prolog clause, copy of clause myst be in ARG1 */ /* returns address of code for clause */ Term head, body; yamop *acode; volatile int maxvnum = 512; int botch_why; volatile Term my_clause = inp_clause; /* may botch while doing a different module */ /* first, initialise Yap_CompilerBotch to handle all cases of interruptions */ Yap_ErrorMessage = NULL; Yap_Error_Size = 0; if ((botch_why = setjmp(Yap_CompilerBotch)) == 3) { /* out of local stack, just duplicate the stack */ restore_machine_regs(); reset_vars(); { Int osize = 2*sizeof(CELL)*(ASP-H); ARG1 = my_clause; *H++ = src; if (!Yap_gcl(Yap_Error_Size, 2, ENV, P)) { Yap_Error_TYPE = OUT_OF_STACK_ERROR; Yap_Error_Term = my_clause; } if (osize > ASP-H) { if (!Yap_growstack(2*sizeof(CELL)*(ASP-H))) { Yap_Error_TYPE = SYSTEM_ERROR; Yap_Error_Term = my_clause; } } src = *--H; my_clause = ARG1; } } else if (botch_why == 4) { /* out of temporary cells */ restore_machine_regs(); reset_vars(); if (maxvnum < 16*1024) { maxvnum *= 2; } else { maxvnum += 4096; } } else if (botch_why == 2) { /* not enough heap */ restore_machine_regs(); reset_vars(); Yap_Error_TYPE = SYSTEM_ERROR; Yap_Error_Term = TermNil; return(0); } restart_compilation: if (Yap_ErrorMessage != NULL) { reset_vars(); return (0); } HB = H; or_found = 0; Yap_ErrorMessage = NULL; /* initialize variables for code generation */ CodeStart = cpc = NULL; BlobsStart = icpc = NULL; freep = freep0 = (char *) (H + maxvnum); if (ASP <= CellPtr (freep) + 256) { vtable = NULL; Yap_Error_Size = (256+maxvnum)*sizeof(CELL); save_machine_regs(); longjmp(Yap_CompilerBotch,3); } common_exps = NULL; n_common_exps = 0; cur_branch = onbranch = 0; branch_pointer = parent_branches; tmpreg = 0; nvars = 0; max_args = 0; /* * 2000 added to H in case we need to construct call(G) when G is a * variable used as a goal */ vtable = NULL; labelno = 0L; if (IsVarTerm(my_clause)) { Yap_Error_TYPE = INSTANTIATION_ERROR; Yap_Error_Term = my_clause; Yap_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)) { Yap_Error_TYPE = TYPE_ERROR_CALLABLE; Yap_Error_Term = my_clause; Yap_ErrorMessage = "clause should be atom or term"; return (0); } else { /* find out which predicate we are compiling for */ if (IsAtomTerm(head)) { Atom ap = AtomOfTerm(head); CurrentPred = RepPredProp(PredPropByAtom(ap, mod)); } else { CurrentPred = RepPredProp(PredPropByFunc(FunctorOfTerm(head),mod)); } /* insert extra instructions to count calls */ READ_LOCK(CurrentPred->PRWLock); if ((CurrentPred->PredFlags & ProfiledPredFlag) || (PROFILING && (CurrentPred->cs.p_code.FirstClause == NIL))) { profiling = TRUE; call_counting = FALSE; } else if ((CurrentPred->PredFlags & CountPredFlag) || (CALL_COUNTING && (CurrentPred->cs.p_code.FirstClause == NIL))) { call_counting = TRUE; profiling = FALSE; } else { profiling = FALSE; call_counting = FALSE; } READ_UNLOCK(CurrentPred->PRWLock); } /* phase 1 : produce skeleton code and variable information */ c_head(head); #ifdef TABLING_INNER_CUTS Yap_emit(nop_op, Zero, Zero); cut_mark = cpc; #endif /* TABLING_INNER_CUTS */ Yap_emit(allocate_op, Zero, Zero); c_body(body, mod); /* Insert blobs at the very end */ if (BlobsStart != NULL) { cpc->nextInst = BlobsStart; BlobsStart = NULL; } reset_vars(); H = HB; if (B != NULL) { HB = B->cp_h; } if (Yap_ErrorMessage) return (0); #ifdef DEBUG if (Yap_Option['g' - 96]) Yap_ShowCode(); #endif /* phase 2: classify variables and optimize temporaries */ c_layout(); /* Insert blobs at the very end */ if (BlobsStart != NULL) { cpc->nextInst = BlobsStart; BlobsStart = NULL; while (cpc->nextInst != NULL) cpc = cpc->nextInst; } /* eliminate superfluous pop's and unify_var's */ c_optimize(CodeStart); #ifdef DEBUG if (Yap_Option['f' - 96]) Yap_ShowCode(); #endif /* phase 3: assemble code */ acode = Yap_assemble(ASSEMBLING_CLAUSE, src, CurrentPred, body == MkAtomTerm(AtomTrue)); /* check first if there was space for us */ if (acode == NULL) { /* make sure we have enough space */ reset_vars(); if (!Yap_growheap(FALSE, Yap_Error_Size)) { save_machine_regs(); my_clause = Deref(ARG1); longjmp(Yap_CompilerBotch, 2); return(NULL); } else { my_clause = Deref(ARG1); goto restart_compilation; } } else { #ifdef LOW_PROF if (ProfilerOn) { Yap_inform_profiler_of_clause(acode, Yap_prof_end, CurrentPred); } #endif return(acode); } }