/* $Id: gccode.c,v 1.3 1999/05/31 23:35:30 sybalsky Exp $ (C) Copyright Venue, All Rights Reserved
 */

/************************************************************************/
/*									*/
/*	(C) Copyright 1989-95 Venue. All Rights Reserved.		*/
/*	Manufactured in the United States of America.			*/
/*									*/
/************************************************************************/

#include "version.h"

/************************************************************************/
/* File Name : gccode.c						*/
/*									*/
/************************************************************************/
/*									*/
/* Creation Date : Sep-25-1987						*/
/* Written by Tomoru Teruuchi						*/
/* Edit by Larry Masinter						*/
/************************************************************************/
/*									*/
/* Functions :								*/
/* reclaimcodeblock();							*/
/*									*/
/*									*/
/*									*/
/************************************************************************/
/* \Tomtom								*/
/************************************************************************/

#include <stdio.h>
#include "lispemul.h"
#include "lsptypes.h"
#include "address.h"
#include "adr68k.h"
#include "lspglob.h"
#include "lispmap.h"
#include "stack.h"
#include "cell.h"
#include "ifpage.h"
#include "gcdata.h"
#include "array.h"

#include "gccodedefs.h"
#include "commondefs.h"
#include "gchtfinddefs.h"

#define min(a, b) (((a) > (b)) ? (b) : (a))

#define ENDOFX 0
#define GCONST 111

#define Reprobefn(bits, index) ((((bits) ^ ((bits) >> 8)) & min(63, index)) | 1)
#define Fn16bits(a, b) (((a) + (b)) & 0x0ffff)
#define Hashingbits(item) (HILOC(item) ^ (((LOLOC(item) & 0x1fff) << 3) ^ (LOLOC(item) >> 9)))
#define Getikvalue(base, index) (*(LispPTR *)Addr68k_from_LADDR((base) + ((index) << 1)))

#ifndef BYTESWAP
typedef struct implicit_key_hash_table {
  LispPTR base;
  unsigned last_index : 16;
  unsigned num_slots : 16;
  unsigned num_keys : 16;
  unsigned null_slots : 16;
  LispPTR key_accessor;
} Ikhashtbl;
#else
typedef struct implicit_key_hash_table {
  LispPTR base;
  unsigned num_slots : 16;
  unsigned last_index : 16;
  unsigned null_slots : 16;
  unsigned num_keys : 16;
  LispPTR key_accessor;
} Ikhashtbl;
#endif

#ifdef BIGVM
/* Table of opcode lengths for 4-byte atom opcode cases */
#define LONGEST_OPCODE 5
unsigned int oplength[256] = {
    0, 0, 0, 0, 0, 1, 4, 2, 4, 4, 4, 4, 4, 5, 0, 0, 0, 2, 0, 0, 1, 1, 0, 4, 0, 0, 0, 0, 0, 0, 1, 0,
    0, 0, 0, 1, 2, 9, 0, 0, 9, 9, 9, 9, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1,
    4, 0, 1, 1, 0, 0, 0, 4, 0, 0, 0, 0, 1, 1, 2, 4, 9, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 2, 0, 4,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    1, 1, 0, 4, 0, 1, 1, 0, 1, 1, 2, 9, 0, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0};
#elif defined(BIGATOMS)
/* Table of opcode lengths for 3-byte atom opcode cases */
#define LONGEST_OPCODE 4
unsigned int oplength[256] = {
    0, 0, 0, 0, 0, 1, 3, 2, 3, 3, 3, 3, 3, 4, 0, 0, 0, 2, 0, 0, 1, 1, 0, 3, 0, 0, 0, 0, 0, 0, 1, 0,
    0, 0, 0, 1, 2, 9, 0, 0, 9, 9, 9, 9, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1,
    3, 0, 1, 1, 0, 0, 0, 3, 0, 0, 0, 0, 1, 1, 2, 3, 9, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 2, 0, 3,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    1, 1, 0, 3, 0, 1, 1, 0, 1, 1, 2, 9, 0, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0};
#else
/* Table of opcode lengths for old, 2-byte atom opcodes. */
#define LONGEST_OPCODE 3
unsigned int oplength[256] = {
    0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 3, 0, 0, 0, 2, 0, 0, 1, 1, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0,
    0, 0, 0, 1, 2, 9, 0, 0, 9, 9, 9, 9, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1,
    2, 0, 1, 1, 0, 0, 0, 2, 0, 0, 0, 0, 1, 1, 2, 3, 9, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 2, 0, 3,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    1, 1, 0, 2, 0, 1, 1, 0, 1, 1, 2, 9, 0, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0};
#endif /* BIGATOMS */

typedef ByteCode *InstPtr;

/************************************************************************/
/*									*/
/*		    m a p _ c o d e _ p o i n t e r s			*/
/*									*/
/*	Find all pointers in a block of compiled code (they're the	*/
/*	the args to the GCONST opcode), and change the reference	*/
/*	count according to "casep".  Complains if it hits an unknown	*/
/*	opcode.								*/
/*									*/
/************************************************************************/

LispPTR map_code_pointers(LispPTR codeblock, short int casep) {
  InstPtr codeptr;
  register unsigned int opnum;
  register unsigned int len;
  struct fnhead *fnbase;
  fnbase = (struct fnhead *)Addr68k_from_LADDR(codeblock);
  codeptr = ((InstPtr)fnbase) + fnbase->startpc;

#ifdef RESWAPPEDCODESTREAM
  if (!fnbase->byteswapped) byte_swap_code_block(fnbase);
#endif

  while (T) {
    switch (opnum = Get_code_BYTE(codeptr)) {
      case ENDOFX: /* -X- */ return (NIL);
      case GCONST: /* GCONST */
#ifdef BIGVM
      {
        LispPTR reclaimed = (Get_code_BYTE(codeptr + 1) << 24) |
                            (Get_code_BYTE(codeptr + 2) << 16) | (Get_code_BYTE(codeptr + 3) << 8) |
                            Get_code_BYTE(codeptr + 4);
#else
      {
        LispPTR reclaimed = (Get_code_BYTE(codeptr + 1) << 16) | (Get_code_BYTE(codeptr + 2) << 8) |
                            Get_code_BYTE(codeptr + 3);
#endif /* BIGVM */
        if (reclaimed != codeblock)
        /*			      {htfind(reclaimed, casep);} */
        {
          REC_GCLOOKUP(reclaimed, casep);
        }
      };
    };
    if ((len = oplength[opnum]) >
        LONGEST_OPCODE) { /* len > biggest possible marks an unknown opcode */
      char errtext[200];
      sprintf(errtext,
              "Unrecognized bytecode (0%o) at offset 0%to in code block x%x,x%x; continue to use "
              "UFN length",
              opnum, codeptr - (InstPtr)fnbase, (codeblock >> 16) & 0xFF, codeblock & 0xFFFF);
      error(errtext);
      oplength[opnum] = len = (((UFN *)UFNTable) + (opnum))->byte_num;
    }
    codeptr += len + 1;
  };
}

/************************************************************************/
/*									*/
/*		    r e m i m p l i c i t k e y h a s h			*/
/*									*/
/*	Remove a fn defn from the implicit-key hash table of defns	*/
/*									*/
/************************************************************************/

/* JRB - These values are xpointers; their high bytes are not set and
        shouldn't be looked at */
#define getikkey(value) ((*(LispPTR *)Addr68k_from_LADDR(value)) & POINTERMASK)

LispPTR remimplicitkeyhash(LispPTR item, LispPTR ik_hash_table) {
  Ikhashtbl *ik_htable;
  LispPTR reprobe, bits, limits, index, base, value;
  ik_htable = (Ikhashtbl *)Addr68k_from_LADDR(ik_hash_table);
  bits = Hashingbits(item);
  limits = ik_htable->last_index;
  index = (bits & limits);
  base = ik_htable->base;
  value = Getikvalue(base, index);
  if (value != *Deleted_Implicit_Hash_Slot_word) {
    if (value != NIL) {
      if (item == getikkey(value)) { goto found; };
    } else
      return (NIL);
  };
  reprobe = Reprobefn(bits, limits);
lp:
  index = Fn16bits(index, reprobe) & limits;
  value = Getikvalue(base, index);
  if (value != *Deleted_Implicit_Hash_Slot_word) {
    if (value != NIL) {
      if (item == getikkey(value)) { goto found; };
    } else
      return (NIL);
  };
  goto lp;
found:
  /*
      htfind(*Deleted_Implicit_Hash_Slot_word, ADDREF);
      htfind(Getikvalue(base, index), DELREF);
  */
  REC_GCLOOKUP(*Deleted_Implicit_Hash_Slot_word, ADDREF);
  REC_GCLOOKUP(Getikvalue(base, index), DELREF);
  Getikvalue(base, index) = *Deleted_Implicit_Hash_Slot_word;
  (ik_htable->num_keys)--;
  return (T);
}

/************************************************************************/
/*									*/
/*		    r e c l a i m c o d e b l o c k			*/
/*									*/
/*	Reclaim an array block that contains compiled code.  When	*/
/*	this happens, we need to decrement the reference counts for	*/
/*									*/
/*		* The frame name, which may be a string, list, etc.	*/
/*		* Any GCONSTs in the code (constants, internal fns,	*/
/*		  etc.), since they're no longer needed.		*/
/*									*/
/************************************************************************/

LispPTR reclaimcodeblock(LispPTR codebase) {
  struct fnhead *fnbase;
  if ((*Closure_Cache_Enabled_word != NIL) &&
      (remimplicitkeyhash(codebase, *Closure_Cache_word) != NIL)) {
    return (T);
  };
  fnbase = (struct fnhead *)Addr68k_from_LADDR(codebase);
  REC_GCLOOKUP((POINTERMASK & fnbase->framename), DELREF);
  if (fnbase->startpc != 0) map_code_pointers(codebase, DELREF);
  return (NIL);
}

/************************************************************************/
/*									*/
/*		    c o d e _ b l o c k _ s i z e			*/
/*									*/
/*	Given a native pointer to a code block, return its size.	*/
/*									*/
/************************************************************************/

int code_block_size(long unsigned int codeblock68k) {
  InstPtr codeptr, initcodeptr;
  register unsigned int opnum;
  register unsigned int len;
  struct fnhead *fnbase;
  fnbase = (struct fnhead *)codeblock68k;
  initcodeptr = codeptr = ((InstPtr)fnbase) + fnbase->startpc;
  while (T) {
    switch (opnum = Get_BYTE(codeptr)) {
      case ENDOFX: /* -X- */ return (codeptr - initcodeptr);
    };
    if ((len = oplength[opnum]) >
        LONGEST_OPCODE) { /* len > biggest possible marks an unknown opcode */
      char errtext[200];
      sprintf(errtext,
              "Unrecognized bytecode (0%o) at offset 0%to in code block x%x,x%x; continue to use "
              "UFN length",
              opnum, codeptr - (InstPtr)fnbase, (int)((codeblock68k >> 16) & 0xFF), (int)(codeblock68k & 0xFFFF));
      error(errtext);
      oplength[opnum] = len = (((UFN *)UFNTable) + (opnum))->byte_num;
    }
    codeptr += len + 1;
  };
}
