/*************************************************************************
*									 *
*	 YAP Prolog 							 *
*									 *
*	Yap Prolog was developed at NCCUP - Universidade do Porto	 *
*									 *
* Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997	 *
*									 *
**************************************************************************
*									 *
* File:		computils.c						 *
* Last rev:								 *
* mods:									 *
* comments:	some useful routines for YAP's compiler			 *
*									 *
*************************************************************************/
#ifdef SCCS
static char SccsId[] = "%W% %G%";
#endif

/*
 * This file includes a set of utilities, useful to the several compilation
 * modules 
 */

#include "Yap.h"
#include "Yatom.h"
#include "Heap.h"
#include "compile.h"
#include "yapio.h"
#if HAVE_STRING_H
#include <string.h>
#endif

#ifdef DEBUG
STATIC_PROTO (void ShowOp, (char *));
#endif /* DEBUG */

/*
 * The compiler creates an instruction chain which will be assembled after
 * afterwards 
 */

char *freep, *freep0;

Int arg, rn;

compiler_vm_op ic;

CELL *cptr;

char *
AllocCMem (int size)
{
  char *p;
  p = freep;
#if SIZEOF_INT_P==8
  size = (size + 7) & 0xfffffffffffffff8L;
#else
  size = (size + 3) & 0xfffffffcL;
#endif
  freep += size;
  if (ASP <= CellPtr (freep) + 256) {
    save_machine_regs();
    longjmp(CompilerBotch,3);
  }
  return (p);
}

int
is_a_test_pred (Term arg)
{
  Atom At;
  int arity;
  if (IsVarTerm (arg))
    return (FALSE);
  else if (IsAtomTerm (arg))
    {
      At = AtomOfTerm (arg);
      arity = 0;
    }
  else if (IsApplTerm (arg))
    {
      Functor f = FunctorOfTerm (arg);
      At = NameOfFunctor (f);
      arity = ArityOfFunctor (f);
    }
  else
    return (FALSE);
  if (RepPredProp (PredProp (At, arity)) == NULL)
    return (FALSE);
  return (RepPredProp (PredProp (At, arity))->PredFlags & TestPredFlag);
}

void
emit (compiler_vm_op o, Int r1, CELL r2)
{
  PInstr *p;
  p = (PInstr *) AllocCMem (sizeof (*p));
  p->op = o;
  p->rnd1 = r1;
  p->rnd2 = r2;
  p->nextInst = NIL;
  if (cpc == NIL)
    cpc = CodeStart = p;
  else
    {
      cpc->nextInst = p;
      cpc = p;
    }
}

void
emit_3ops (compiler_vm_op o, CELL r1, CELL r2, CELL r3)
{
  PInstr *p;
  p = (PInstr *) AllocCMem (sizeof (*p)+sizeof(CELL));
  p->op = o;
  p->rnd1 = r1;
  p->rnd2 = r2;
  p->rnd3 = r3;
  p->nextInst = NIL;
  if (cpc == NIL)
    cpc = CodeStart = p;
  else
    {
      cpc->nextInst = p;
      cpc = p;
    }
}

CELL *
emit_extra_size (compiler_vm_op o, CELL r1, int size)
{
  PInstr *p;
  p = (PInstr *) AllocCMem (sizeof (*p) + size - CellSize);
  p->op = o;
  p->rnd1 = r1;
  p->nextInst = NIL;
  if (cpc == NIL)
    cpc = CodeStart = p;
  else
    {
      cpc->nextInst = p;
      cpc = p;
    }
  return (p->arnds);
}

void
bip_name(Int op, char *s)
{
  switch (op) {
  case _atom:
    strcpy(s,"atom");
    break;
  case _atomic:
    strcpy(s,"atomic");
    break;
  case _integer:
    strcpy(s,"integer");
    break;
  case _nonvar:
    strcpy(s,"nonvar");
    break;
  case _number:
    strcpy(s,"number");
    break;
  case _var:
    strcpy(s,"var");
    break;
  case _cut_by:
    strcpy(s,"cut_by");
    break;
  case _db_ref:
    strcpy(s,"db_ref");
    break;
  case _compound:
    strcpy(s,"compound");
    break;
  case _float:
    strcpy(s,"float");
    break;
  case _primitive:
    strcpy(s,"primitive");
    break;
  case _equal:
    strcpy(s,"equal");
    break;
  case _dif:
    strcpy(s,"dif");
    break;
  case _eq:
    strcpy(s,"eq");
    break;
  case _functor:
    strcpy(s,"functor");
    break;
  case _plus:
    strcpy(s,"plus");
    break;
  case _minus:
    strcpy(s,"minus");
    break;
  case _times:
    strcpy(s,"times");
    break;
  case _div:
    strcpy(s,"div");
    break;
  case _and:
    strcpy(s,"and");
    break;
  case _or:
    strcpy(s,"or");
    break;
  case _sll:
    strcpy(s,"sll");
    break;
  case _slr:
    strcpy(s,"slr");
    break;
  case _arg:
    strcpy(s,"arg");
    break;
  default:
    strcpy(s,"");
    break;
  }
}

#ifdef DEBUG

static void
ShowOp (f)
     char *f;
{
  char ch;
  while ((ch = *f++) != 0)
    {
      if (ch == '%')
	switch (ch = *f++)
	  {
	  case 'a':
	  case 'n':
	    plwrite ((Term) arg, DebugPutc, 0);
	    break;
	  case 'b':
	    /* write a variable bitmap for a call */
	    {
	      int max = arg/(8*sizeof(CELL)), i;
	      CELL *ptr = cptr;
	      for (i = 0; i <= max; i++) {
		plwrite(MkIntegerTerm((Int)(*ptr++)), DebugPutc, 0);
	      }
	    }
	    break;		
	  case 'l':
	    plwrite (MkIntTerm (arg), DebugPutc, 0);
	    break;
	  case 'B':
	    {
	      char s[32];

	      bip_name(rn,s);
	      plwrite (MkAtomTerm(LookupAtom(s)), DebugPutc, 0);
	    }
	    break;
	  case 'd':
	    plwrite (MkIntTerm (rn), DebugPutc, 0);
	    break;
	  case 'z':
	    plwrite (MkIntTerm (cpc->rnd3), DebugPutc, 0);
	    break;
	  case 'v':
	    {
	      Ventry *v = (Ventry *) arg;
	      DebugPutc (c_output_stream,v->KindOfVE == PermVar ? 'Y' : 'X');
	      plwrite (MkIntTerm ((v->NoOfVE) & MaskVarAdrs), DebugPutc, 0);
	    }
	    break;
	  case 'N':
	    {
	      Ventry *v;

	      cpc = cpc->nextInst;
	      arg = cpc->rnd1;
	      v = (Ventry *) arg;
	      DebugPutc (c_output_stream,v->KindOfVE == PermVar ? 'Y' : 'X');
	      plwrite (MkIntTerm ((v->NoOfVE) & MaskVarAdrs), DebugPutc, 0);
	    }
	    break;
	  case 'm':
	    plwrite (MkAtomTerm ((Atom) arg), DebugPutc, 0);
	    DebugPutc (c_output_stream,'/');
	    plwrite (MkIntTerm (rn), DebugPutc, 0);
	    break;
	  case 'p':
	    {
	      PredEntry *p = RepPredProp ((Prop) arg);
	      Functor f = p->FunctorOfPred;
	      plwrite (ModuleName[p->ModuleOfPred], DebugPutc, 0);
	      DebugPutc (c_output_stream,':');
	      if (p->ArityOfPE == 0)
		f = MkFunctor ((Atom) f, 0);
	      plwrite (MkAtomTerm (NameOfFunctor (f)), DebugPutc, 0);
	      DebugPutc (c_output_stream,'/');
	      plwrite (MkIntTerm (ArityOfFunctor (f)), DebugPutc, 0);
	    }
	    break;
	  case 'P':
	    {
	      PredEntry *p = RepPredProp((Prop) rn);
	      Functor f = p->FunctorOfPred;
	      plwrite (ModuleName[p->ModuleOfPred], DebugPutc, 0);
	      DebugPutc (c_output_stream,':');
	      if (p->ArityOfPE == 0)
		f = MkFunctor ((Atom) f, 0);
	      plwrite (MkAtomTerm (NameOfFunctor (f)), DebugPutc, 0);
	      DebugPutc (c_output_stream,'/');
	      plwrite (MkIntTerm (ArityOfFunctor (f)), DebugPutc, 0);
	    }
	    break;
	  case 'f':
	    if (IsExtensionFunctor((Functor)arg)) {
	      if ((Functor)arg == FunctorDBRef) {
		plwrite(MkAtomTerm(LookupAtom("DBRef")), DebugPutc, 0);
	      } else if ((Functor)arg == FunctorLongInt) {
		plwrite(MkAtomTerm(LookupAtom("LongInt")), DebugPutc, 0);
	      } else if ((Functor)arg == FunctorDouble) {
		plwrite(MkAtomTerm(LookupAtom("Double")), DebugPutc, 0);
	      }
	    } else {
	      plwrite(MkAtomTerm(NameOfFunctor ((Functor) arg)), DebugPutc, 0);
	      DebugPutc (c_output_stream,'/');
	      plwrite(MkIntTerm(ArityOfFunctor ((Functor) arg)), DebugPutc, 0);
	    }
	    break;
	  case 'r':
	    DebugPutc (c_output_stream,'A');
	    plwrite (MkIntTerm (rn), DebugPutc, 0);
	    break;
	  case 'h':
	    {
	      CELL my_arg = *cptr++;
	      if (my_arg & 1)
		plwrite (MkIntTerm (my_arg),
			 DebugPutc, 0);
	      else if (my_arg == (CELL) FAILCODE)
		plwrite (MkAtomTerm (AtomFail), DebugPutc, 0);
	      else
		plwrite (MkIntegerTerm ((Int) my_arg),
			 DebugPutc, 0);
	    }
	    break;
	  case 'g':
	    if (arg & 1)
	      plwrite (MkIntTerm (arg),
		       DebugPutc, 0);
	    else if (arg == (CELL) FAILCODE)
	      plwrite (MkAtomTerm (AtomFail), DebugPutc, 0);
	    else
	      plwrite (MkIntegerTerm ((Int) arg), DebugPutc, 0);
	    break;
	  case 'i':
	    plwrite (MkIntTerm (arg), DebugPutc, 0);
	    break;
	  case 'j':
	    {
	      Functor fun = (Functor)*cptr++;
	      if (IsExtensionFunctor(fun)) {
		if (fun == FunctorDBRef) {
		  plwrite(MkAtomTerm(LookupAtom("DBRef")), DebugPutc, 0);
		} else if (fun == FunctorLongInt) {
		  plwrite(MkAtomTerm(LookupAtom("LongInt")), DebugPutc, 0);
		} else if (fun == FunctorDouble) {
		  plwrite(MkAtomTerm(LookupAtom("Double")), DebugPutc, 0);
		}
	      } else {
		plwrite (MkAtomTerm(NameOfFunctor(fun)), DebugPutc, 0);
		DebugPutc (c_output_stream,'/');
		plwrite (MkIntTerm(ArityOfFunctor(fun)), DebugPutc, 0);
	      }
	    }
	    break;
	  case 'O':
	    plwrite(AbsAppl(cptr), DebugPutc, 0);
	    break;
	  case 'x':
	    plwrite (MkIntTerm (rn >> 1), DebugPutc, 0);
	    DebugPutc (c_output_stream,'\t');
	    plwrite (MkIntTerm (rn & 1), DebugPutc, 0);
	    break;
	  case 'o':
	    plwrite ((Term) * cptr++, DebugPutc, 0);
	  case 'c':
	    {
	      int i;
	      for (i = 0; i < arg; ++i)
		{
		  CELL my_arg;
		  if (*cptr)
		    {
		      plwrite ((Term) * cptr++, DebugPutc, 0);
		    }
		  else
		    {
		      plwrite (MkIntTerm (0), DebugPutc, 0);
		      cptr++;
		    }
		  DebugPutc (c_output_stream,'\t');
		  my_arg = *cptr++;
		  if (my_arg & 1)
		    plwrite (MkIntTerm (my_arg),
			     DebugPutc, 0);
		  else if (my_arg == (CELL) FAILCODE)
		    plwrite (MkAtomTerm (AtomFail), DebugPutc, 0);
		  else
		    plwrite (MkIntegerTerm ((Int) my_arg), DebugPutc, 0);
		  DebugPutc (c_output_stream,'\n');
		}
	    }
	    break;
	  case 'e':
	    {
	      int i;
	      for (i = 0; i < arg; ++i)
		{
		  CELL my_arg;
		  if (*cptr)
		    {
		      plwrite (MkAtomTerm (NameOfFunctor ((Functor) * cptr)), DebugPutc, 0);
		      DebugPutc (c_output_stream,'/');
		      plwrite (MkIntTerm (ArityOfFunctor ((Functor) * cptr++)), DebugPutc, 0);
		    }
		  else
		    {
		      plwrite (MkIntTerm (0), DebugPutc, 0);
		      cptr++;
		    }
		  DebugPutc (c_output_stream,'\t');
		  my_arg = *cptr++;
		  if (my_arg & 1)
		    plwrite (MkIntTerm (my_arg),
			     DebugPutc, 0);
		  else if (my_arg == (CELL) FAILCODE)
		    plwrite (MkAtomTerm (AtomFail), DebugPutc, 0);
		  else
		    plwrite (MkIntegerTerm ((Int) my_arg), DebugPutc, 0);
		  DebugPutc (c_output_stream,'\n');
		}
	    }
	    break;
	  default:
	    DebugPutc (c_output_stream,'%');
	    DebugPutc (c_output_stream,ch);
	  }
      else
	DebugPutc (c_output_stream,ch);
    }
  DebugPutc (c_output_stream,'\n');
}

static char *opformat[] =
{
  "nop",
  "get_var\t\t%v,%r",
  "put_var\t\t%v,%r",
  "get_val\t\t%v,%r",
  "put_val\t\t%v,%r",
  "get_atom\t%a,%r",
  "put_atom\t%a,%r",
  "get_num\t\t%n,%r",
  "put_num\t\t%n,%r",
  "get_float\t\t%l,%r",
  "put_float\t\t%l,%r",
  "get_longint\t\t%l,%r",
  "put_longint\t\t%l,%r",
  "get_bigint\t\t%l,%r",
  "put_bigint\t\t%l,%r",
  "get_list\t%r",
  "put_list\t%r",
  "get_struct\t%f,%r",
  "put_struct\t%f,%r",
  "put_unsafe\t%v,%r",
  "unify_var\t%v",
  "write_var\t%v",
  "unify_val\t%v",
  "write_val\t%v",
  "unify_atom\t%a",
  "write_atom\t%a",
  "unify_num\t%n",
  "write_num\t%n",
  "unify_float\t%l",
  "write_float\t%l",
  "unify_longint\t%l",
  "write_longint\t%l",
  "unify_bigint\t%l",
  "write_bigint\t%l",
  "unify_list",
  "write_list",
  "unify_struct\t%f",
  "write_struct\t%f",
  "write_unsafe\t%v",
  "fail",
  "cut",
  "cutexit",
  "allocate",
  "deallocate",
  "try_me_else\t\t%l\t%x",
  "jump\t\t%l",
  "procceed",
  "call\t\t%p,%d,%z",
  "execute\t\t%p",
  "sys\t\t%p",
  "%l:",
  "name\t\t%m,%d",
  "pop\t\t%l",
  "retry_me_else\t\t%l\t%x",
  "trust_me_else_fail\t%x",
  "either_me\t\t%l,%d,%z",
  "or_else\t\t%l,%z",
  "or_last",
  "push_or",
  "pushpop_or",
  "pop_or",
  "save_by\t\t%v",
  "comit_by\t\t%v",
  "patch_by\t\t%v",
  "try\t\t%g\t%x",
  "retry\t\t%g\t%x",
  "trust\t\t%g\t%x",
  "try_in\t\t%g\t%x",
  "retry_in\t\t%g\t%x",
  "trust_in\t\t%g\t%x",
  "try_first\t\t%g\t%x",
  "retry_first\t\t%g\t%x",
  "trust_first\t\t%g\t%x",
  "try_first in\t\t%g\t%x",
  "retry_first in\t\t%g\t%x",
  "trust_first in\t\t%g\t%x",
  "try_tail\t\t%g\t%x",
  "retry_tail\t\t%g\t%x",
  "trust_tail\t\t%g\t%x",
  "try_tail_in\t\t%g\t%x",
  "retry_tail_in\t\t%g\t%x",
  "trust_tail_in\t\t%g\t%x",
  "try_head\t\t%g\t%x",
  "retry_head\t\t%g\t%x",
  "trust_head\t\t%g\t%x",
  "try_head_in\t\t%g\t%x",
  "retry_head_in\t\t%g\t%x",
  "trust_head_in\t\t%g\t%x",
  "try_last_first\t\t%g\t%x",
  "try_last_head\t\t%g\t%x",
  "jump_if_var\t\t%g",
  "switch_on_type\t%h\t%h\t%h\t%h",
  "switch_on_type_if_nonvar\t%h\t%h\t%h",
  "switch_on_type_of_last\t%h\t%h\t%h",
  "switch_on_type_of_head\t%h\t%h\t%h\t%h",
  "switch_on_list_or_nil\t%h\t%h\t%h\t%h",
  "switch_if_list_or_nil\t%h\t%h\t%h",
  "switch_on_last_list_or_nil\t%h\t%h\t%h",
  "switch_on_constant\t%i\n%c",
  "if_a_constant\t%i\t%h\n%c",
  "go_if_ equals_constant\t%o\t%h\t%h",
  "switch_on_functor\t%i\n%e",
  "if_a_functor\t%i\t%h\n%e",
  "go_if_equals_functor\t%j\t%h\t%h",
  "if_not_then\t%i\t%h\t%h\t%h",
  "save_pair\t%v",
  "save_appl\t%v",
  "fail_label\t%l",
  "unify_local\t%v",
  "write local\t%v",
  "unify_last_list",
  "write_last_list",
  "unify_last_struct\t%f",
  "write_last_struct\t%f",
  "unify_last_var\t%v",
  "unify_last_val\t%v",
  "unify_last_local\t%v",
  "unify_last_atom\t%a",
  "unify_last_num\t%n",
  "unify_last_float\t%l",
  "unify_last_longint\t%l",
  "unify_last_bigint\t%l",
  "pvar_bitmap\t%l,%b",
  "pvar_live_regs\t%l,%b",
  "fetch_reg1_reg2\t%N,%N",
  "fetch_constant_reg\t%l,%N",
  "fetch_reg_constant\t%l,%N",
  "function_to_var\t%v,%B",
  "function_to_al\t%v,%B",
  "enter_profiling\t\t%g",
  "retry_profiled\t\t%g",
  "restore_temps\t\t%l",
  "restore_temps_and_skip\t\t%l",
  "empty_call\t\t%l,%d",
#ifdef TABLING
  "table_new_answer",
#endif /* TABLING */
#ifdef YAPOR
  "sync",
#endif /* YAPOR */
  "fetch_args_for_bccall\t%v",
  "binary_cfunc\t\t%v,%P",
  "blob\t%O"
#ifdef SFUNC
  ,
  "get_s_f_op\t%f,%r",
  "put_s_f_op\t%f,%r",
  "unify_s_f_op\t%f",
  "write_s_f_op\t%f",
  "unify_s_var\t%v,%r",
  "write_s_var\t%v,%r",
  "unify_s_val\t%v,%r",
  "write_s_val\t%v,%r",
  "unify_s_a\t%a,%r",
  "write_s_a\t%a,%r",
  "get_s_end",
  "put_s_end",
  "unify_s_end",
  "write_s_end"
#endif
};


void
ShowCode ()
{
  CELL *OldH = H;

  cpc = CodeStart;
  /* MkIntTerm and friends may build terms in the global stack */
  H = (CELL *)freep;
  while (cpc)
    {
      ic = cpc->op;
      arg = cpc->rnd1;
      rn = cpc->rnd2;
      cptr = cpc->arnds;
      if (ic != nop_op)
	ShowOp (opformat[ic]);
      cpc = cpc->nextInst;
    }
  DebugPutc (c_output_stream,'\n');
  H = OldH;
}

#endif /* DEBUG */