4337 lines
112 KiB
C
4337 lines
112 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: heapgc.c *
|
|
* Last rev: *
|
|
* mods: *
|
|
* comments: Global Stack garbage collector *
|
|
* *
|
|
*************************************************************************/
|
|
#ifdef SCCS
|
|
static char SccsId[] = "%W% %G%";
|
|
#endif /* SCCS */
|
|
|
|
#include "absmi.h"
|
|
#include "yapio.h"
|
|
#include "alloc.h"
|
|
#include "attvar.h"
|
|
|
|
#if !defined(TABLING)
|
|
//#define EASY_SHUNTING 1
|
|
#endif /* !TABLING */
|
|
#define HYBRID_SCHEME 1
|
|
|
|
#define DEBUG_printf0(A,B)
|
|
#define DEBUG_printf1(A,B,C)
|
|
#define DEBUG_printf20(A,B)
|
|
#define DEBUG_printf21(A,B,C)
|
|
|
|
/* global variables for garbage collection */
|
|
|
|
static Int p_inform_gc( CACHE_TYPE1 );
|
|
static Int p_gc( CACHE_TYPE1 );
|
|
static void marking_phase(tr_fr_ptr, CELL *, yamop * CACHE_TYPE);
|
|
static void compaction_phase(tr_fr_ptr, CELL *, yamop * CACHE_TYPE);
|
|
static void init_dbtable(tr_fr_ptr CACHE_TYPE);
|
|
static void mark_external_reference(CELL * CACHE_TYPE);
|
|
static void mark_db_fixed(CELL * CACHE_TYPE);
|
|
static void mark_regs(tr_fr_ptr CACHE_TYPE);
|
|
static void mark_trail(tr_fr_ptr, tr_fr_ptr, CELL *, choiceptr CACHE_TYPE);
|
|
static void mark_environments(CELL *, OPREG, CELL * CACHE_TYPE);
|
|
static void mark_choicepoints(choiceptr, tr_fr_ptr, int CACHE_TYPE);
|
|
static void into_relocation_chain(CELL *, CELL * CACHE_TYPE);
|
|
static void sweep_trail(choiceptr, tr_fr_ptr CACHE_TYPE);
|
|
static void sweep_environments(CELL *, OPREG, CELL * CACHE_TYPE);
|
|
static void sweep_choicepoints(choiceptr CACHE_TYPE);
|
|
static void compact_heap( CACHE_TYPE1 );
|
|
static void update_relocation_chain(CELL *, CELL * CACHE_TYPE);
|
|
static int is_gc_verbose(void);
|
|
static int is_gc_very_verbose(void);
|
|
static void LeaveGCMode( CACHE_TYPE1 );
|
|
#ifdef EASY_SHUNTING
|
|
static void set_conditionals(tr_fr_ptr CACHE_TYPE);
|
|
#endif /* EASY_SHUNTING */
|
|
|
|
#include "heapgc.h"
|
|
|
|
typedef struct gc_mark_continuation {
|
|
CELL *v;
|
|
int nof;
|
|
} cont;
|
|
|
|
/* straightforward binary tree scheme that, given a key, finds a
|
|
matching dbref */
|
|
|
|
typedef enum {
|
|
db_entry,
|
|
cl_entry,
|
|
lcl_entry,
|
|
li_entry,
|
|
dcl_entry
|
|
} db_entry_type;
|
|
|
|
typedef struct db_entry {
|
|
CODEADDR val;
|
|
db_entry_type db_type;
|
|
int in_use;
|
|
struct db_entry *left;
|
|
CODEADDR lim;
|
|
struct db_entry *right;
|
|
} *dbentry;
|
|
|
|
typedef struct RB_red_blk_node {
|
|
CODEADDR key;
|
|
CODEADDR lim;
|
|
db_entry_type db_type;
|
|
int in_use;
|
|
int red; /* if red=0 then the node is black */
|
|
struct RB_red_blk_node* left;
|
|
struct RB_red_blk_node* right;
|
|
struct RB_red_blk_node* parent;
|
|
} rb_red_blk_node;
|
|
|
|
#ifdef EASY_SHUNTING
|
|
#undef LOCAL_cont_top0
|
|
#define LOCAL_cont_top0 (cont *)LOCAL_sTR
|
|
#endif
|
|
|
|
/* support for hybrid garbage collection scheme */
|
|
|
|
static void
|
|
gc_growtrail(int committed, tr_fr_ptr begsTR, cont *old_cont_top0 USES_REGS)
|
|
{
|
|
UInt sz = LOCAL_TrailTop-(ADDR)LOCAL_OldTR;
|
|
/* ask for double the size */
|
|
sz = 2*sz;
|
|
|
|
if (!Yap_growtrail(sz, TRUE)) {
|
|
#ifdef EASY_SHUNTING
|
|
if (begsTR) {
|
|
LOCAL_sTR = (tr_fr_ptr)old_cont_top0;
|
|
while (begsTR != NULL) {
|
|
tr_fr_ptr newsTR = (tr_fr_ptr)TrailTerm(begsTR);
|
|
TrailTerm(LOCAL_sTR) = TrailTerm(begsTR+1);
|
|
TrailTerm(LOCAL_sTR+1) = TrailTerm(begsTR+2);
|
|
begsTR = newsTR;
|
|
LOCAL_sTR += 2;
|
|
}
|
|
}
|
|
set_conditionals(LOCAL_sTR PASS_REGS);
|
|
#endif
|
|
/* could not find more trail */
|
|
save_machine_regs();
|
|
siglongjmp(LOCAL_gc_restore, 2);
|
|
}
|
|
}
|
|
|
|
inline static void
|
|
PUSH_CONTINUATION(CELL *v, int nof USES_REGS) {
|
|
cont *x;
|
|
x = LOCAL_cont_top;
|
|
x++;
|
|
if ((ADDR)x > LOCAL_TrailTop-1024) {
|
|
gc_growtrail(TRUE, NULL, NULL PASS_REGS);
|
|
}
|
|
x->v = v;
|
|
x->nof = nof;
|
|
LOCAL_cont_top = x;
|
|
}
|
|
|
|
#define POP_CONTINUATION() { \
|
|
if (LOCAL_cont_top == LOCAL_cont_top0) \
|
|
return; \
|
|
else { \
|
|
int nof = LOCAL_cont_top->nof; \
|
|
cont *x = LOCAL_cont_top; \
|
|
\
|
|
current = x->v; \
|
|
if (nof == 1) \
|
|
LOCAL_cont_top = --x; \
|
|
else { \
|
|
x->nof = nof-1; \
|
|
x->v = current+1; \
|
|
} \
|
|
} \
|
|
goto begin; }
|
|
|
|
#ifdef HYBRID_SCHEME
|
|
|
|
inline static void
|
|
PUSH_POINTER(CELL *v USES_REGS) {
|
|
if (LOCAL_iptop >= (CELL_PTR *)ASP) return;
|
|
*LOCAL_iptop++ = v;
|
|
}
|
|
|
|
inline static void
|
|
POP_POINTER( USES_REGS1 ) {
|
|
if (LOCAL_iptop >= (CELL_PTR *)ASP) return;
|
|
--LOCAL_iptop;
|
|
}
|
|
|
|
inline static void
|
|
POPSWAP_POINTER(CELL_PTR *vp, CELL_PTR v USES_REGS) {
|
|
if (LOCAL_iptop >= (CELL_PTR *)ASP || LOCAL_iptop == vp) return;
|
|
if (*vp != v)
|
|
return;
|
|
--LOCAL_iptop;
|
|
if (vp != LOCAL_iptop)
|
|
*vp = *LOCAL_iptop;
|
|
}
|
|
|
|
/*
|
|
original code from In Hyuk Choi,
|
|
found at http://userpages.umbc.edu/~ichoi1/project/cs441.htm
|
|
*/
|
|
|
|
static inline void
|
|
exchange(CELL_PTR * b, Int i, Int j)
|
|
{
|
|
CELL *t = b[j];
|
|
|
|
b[j] = b[i];
|
|
b[i] = t;
|
|
}
|
|
|
|
static UInt
|
|
partition(CELL *a[], Int p, Int r)
|
|
{
|
|
CELL *x;
|
|
UInt i, j;
|
|
|
|
x = a[p];
|
|
i = p+1;
|
|
j = r;
|
|
|
|
while (a[j] > x && i < j) {
|
|
j--;
|
|
}
|
|
while (a[i] < x && i < j) {
|
|
i++;
|
|
}
|
|
while(i < j) {
|
|
exchange(a, i, j);
|
|
i++;
|
|
j--;
|
|
while (a[j] > x && i < j) {
|
|
j--;
|
|
}
|
|
while (a[i] < x && i < j) {
|
|
i++;
|
|
}
|
|
}
|
|
if (a[i] > x)
|
|
i--;
|
|
exchange(a, p, i);
|
|
return(i);
|
|
}
|
|
|
|
static void
|
|
insort(CELL *a[], Int p, Int q)
|
|
{
|
|
Int j;
|
|
|
|
for (j = p+1; j <= q; j ++) {
|
|
CELL *key;
|
|
Int i;
|
|
|
|
key = a[j];
|
|
i = j;
|
|
|
|
while (i > p && a[i-1] > key) {
|
|
a[i] = a[i-1];
|
|
i --;
|
|
}
|
|
a[i] = key;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
quicksort(CELL *a[], Int p, Int r)
|
|
{
|
|
Int q;
|
|
if (p < r) {
|
|
if (r - p < 100) {
|
|
insort(a, p, r);
|
|
return;
|
|
}
|
|
exchange(a, p, (p+r)/2);
|
|
q = partition (a, p, r);
|
|
quicksort(a, p, q-1);
|
|
quicksort(a, q + 1, r);
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
#define PUSH_POINTER(P PASS_REGS)
|
|
#define POP_POINTER( PASS_REGS1 )
|
|
#define POPSWAP_POINTER(P)
|
|
|
|
#endif /* HYBRID_SCHEME */
|
|
|
|
|
|
#ifdef MULTI_ASSIGNMENT_VARIABLES
|
|
/*
|
|
Based in opt.mavar.h. This is a set of routines to find out if a
|
|
ma trail entry has appeared before in the same trail segment. All ma
|
|
entries for the same cell are then linked. At the end of mark_trail() only
|
|
one will remain.
|
|
*/
|
|
|
|
static inline unsigned int
|
|
GC_MAVAR_HASH(CELL *addr) {
|
|
#if SIZEOF_INT_P==8
|
|
return((((unsigned int)((CELL)(addr)))>>3)%GC_MAVARS_HASH_SIZE);
|
|
#else
|
|
return((((unsigned int)((CELL)(addr)))>>2)%GC_MAVARS_HASH_SIZE);
|
|
#endif
|
|
}
|
|
|
|
static inline gc_ma_hash_entry *
|
|
GC_ALLOC_NEW_MASPACE( USES_REGS1 )
|
|
{
|
|
gc_ma_hash_entry *new = LOCAL_gc_ma_h_top;
|
|
if ((char *)LOCAL_gc_ma_h_top > LOCAL_TrailTop-1024)
|
|
gc_growtrail(FALSE, NULL, NULL PASS_REGS);
|
|
LOCAL_gc_ma_h_top++;
|
|
LOCAL_cont_top = (cont *)LOCAL_gc_ma_h_top;
|
|
#ifdef EASY_SHUNTING
|
|
LOCAL_sTR = LOCAL_sTR0 = (tr_fr_ptr)LOCAL_cont_top;
|
|
#else
|
|
LOCAL_cont_top0 = LOCAL_cont_top;
|
|
#endif
|
|
return new;
|
|
}
|
|
|
|
static inline gc_ma_hash_entry*
|
|
gc_lookup_ma_var(CELL *addr, tr_fr_ptr trp USES_REGS) {
|
|
unsigned int i = GC_MAVAR_HASH(addr);
|
|
gc_ma_hash_entry *nptr, *optr = NULL;
|
|
|
|
if (LOCAL_gc_ma_hash_table[i].timestmp != LOCAL_gc_timestamp) {
|
|
LOCAL_gc_ma_hash_table[i].timestmp = LOCAL_gc_timestamp;
|
|
LOCAL_gc_ma_hash_table[i].addr = addr;
|
|
#if TABLING
|
|
LOCAL_gc_ma_hash_table[i].loc = trp;
|
|
LOCAL_gc_ma_hash_table[i].more = LOCAL_gc_ma_h_list;
|
|
LOCAL_gc_ma_h_list = LOCAL_gc_ma_hash_table+i;
|
|
#endif /* TABLING */
|
|
LOCAL_gc_ma_hash_table[i].next = NULL;
|
|
return NULL;
|
|
}
|
|
nptr = LOCAL_gc_ma_hash_table+i;
|
|
while (nptr) {
|
|
optr = nptr;
|
|
if (nptr->addr == addr) {
|
|
#if TABLING
|
|
/*
|
|
we're moving from oldest to more recent, so only a new entry
|
|
has the correct new value
|
|
*/
|
|
TrailVal(nptr->loc+1) = TrailVal(trp+1);
|
|
#endif /* TABLING */
|
|
return nptr;
|
|
}
|
|
nptr = nptr->next;
|
|
}
|
|
nptr = GC_ALLOC_NEW_MASPACE( PASS_REGS1 );
|
|
optr->next = nptr;
|
|
nptr->addr = addr;
|
|
#if TABLING
|
|
nptr->loc = trp;
|
|
nptr->more = LOCAL_gc_ma_h_list;
|
|
#endif /* TABLING */
|
|
nptr->next = NULL;
|
|
LOCAL_gc_ma_h_list = nptr;
|
|
return NULL;
|
|
}
|
|
|
|
static inline void
|
|
GC_NEW_MAHASH(gc_ma_hash_entry *top USES_REGS) {
|
|
UInt time = ++LOCAL_gc_timestamp;
|
|
|
|
LOCAL_gc_ma_h_list = NULL;
|
|
if (time == 0) {
|
|
unsigned int i;
|
|
|
|
/* damn, we overflowed */
|
|
for (i = 0; i < GC_MAVARS_HASH_SIZE; i++)
|
|
LOCAL_gc_ma_hash_table[i].timestmp = 0L;
|
|
time = ++LOCAL_gc_timestamp;
|
|
}
|
|
LOCAL_gc_ma_h_top = top;
|
|
LOCAL_cont_top = (cont *)LOCAL_gc_ma_h_top;
|
|
#ifdef EASY_SHUNTING
|
|
LOCAL_sTR = (tr_fr_ptr)LOCAL_cont_top;
|
|
#else
|
|
LOCAL_cont_top0 = LOCAL_cont_top;
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
|
|
/* find all accessible objects on the heap and squeeze out all the rest */
|
|
|
|
static void
|
|
check_pr_trail(tr_fr_ptr trp USES_REGS)
|
|
{
|
|
if ((tr_fr_ptr)LOCAL_TrailTop-TR < 1024) {
|
|
if (!Yap_growtrail(0, TRUE) || TRUE) {
|
|
/* could not find more trail */
|
|
save_machine_regs();
|
|
siglongjmp(LOCAL_gc_restore, 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* push the active registers onto the trail for inclusion during gc */
|
|
|
|
static void
|
|
push_registers(Int num_regs, yamop *nextop USES_REGS)
|
|
{
|
|
int i;
|
|
StaticArrayEntry *sal = LOCAL_StaticArrays;
|
|
|
|
/* push array entries first */
|
|
ArrayEntry *al = LOCAL_DynamicArrays;
|
|
GlobalEntry *gl = LOCAL_GlobalVariables;
|
|
TrailTerm(TR++) = LOCAL_GlobalArena;
|
|
while (al) {
|
|
check_pr_trail(TR PASS_REGS);
|
|
TrailTerm(TR++) = al->ValueOfVE;
|
|
al = al->NextAE;
|
|
}
|
|
while (gl) {
|
|
Term t = gl->global;
|
|
if (!IsUnboundVar(&gl->global) &&
|
|
!IsAtomTerm(t) &&
|
|
!IsIntTerm(t)
|
|
) {
|
|
check_pr_trail(TR PASS_REGS);
|
|
//fprintf(stderr,"in=%s %p\n", gl->AtomOfGE->StrOfAE, gl->global);
|
|
TrailTerm(TR++) = t;
|
|
}
|
|
gl = gl->NextGE;
|
|
}
|
|
while (sal) {
|
|
if (sal->ArrayType == array_of_nb_terms) {
|
|
UInt arity = -sal->ArrayEArity, i;
|
|
for (i=0; i < arity; i++) {
|
|
Term tlive = sal->ValueOfVE.lterms[i].tlive;
|
|
if (!IsVarTerm(tlive) || !IsUnboundVar(&sal->ValueOfVE.lterms[i].tlive)) {
|
|
check_pr_trail(TR PASS_REGS);
|
|
TrailTerm(TR++) = tlive;
|
|
}
|
|
}
|
|
}
|
|
sal = sal->NextAE;
|
|
}
|
|
check_pr_trail(TR PASS_REGS);
|
|
TrailTerm(TR) = LOCAL_GcGeneration;
|
|
TR++;
|
|
TrailTerm(TR) = LOCAL_GcPhase;
|
|
TR++;
|
|
#ifdef COROUTINING
|
|
TrailTerm(TR) = LOCAL_WokenGoals;
|
|
TrailTerm(TR+1) = LOCAL_AttsMutableList;
|
|
TR += 2;
|
|
#endif
|
|
for (i = 1; i <= num_regs; i++) {
|
|
check_pr_trail(TR PASS_REGS);
|
|
TrailTerm(TR++) = (CELL) XREGS[i];
|
|
}
|
|
/* push any live registers we might have hanging around */
|
|
if (nextop->opc == Yap_opcode(_move_back) ||
|
|
nextop->opc == Yap_opcode(_skip)) {
|
|
CELL *lab = (CELL *)(nextop->u.l.l);
|
|
CELL max = lab[0];
|
|
Int curr = lab[1];
|
|
lab += 2;
|
|
if (max) {
|
|
CELL i;
|
|
for (i=0L; i <= max; i++) {
|
|
if (i == 8*CellSize) {
|
|
curr = lab[0];
|
|
lab++;
|
|
}
|
|
if (curr & 1) {
|
|
check_pr_trail(TR PASS_REGS);
|
|
TrailTerm(TR++) = XREGS[i];
|
|
}
|
|
curr >>= 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* pop the corrected register values from the trail and update the registers */
|
|
|
|
static void
|
|
pop_registers(Int num_regs, yamop *nextop USES_REGS)
|
|
{
|
|
int i;
|
|
tr_fr_ptr ptr = TR;
|
|
StaticArrayEntry *sal = LOCAL_StaticArrays;
|
|
|
|
/* pop info on opaque variables */
|
|
while (LOCAL_extra_gc_cells > LOCAL_extra_gc_cells_base) {
|
|
Opaque_CallOnGCRelocate f;
|
|
CELL *ptr = LOCAL_extra_gc_cells-1;
|
|
size_t n = ptr[0], t = ptr[-1];
|
|
|
|
LOCAL_extra_gc_cells -= (n+1);
|
|
if ( (f = Yap_blob_gc_relocate_handler(t)) ) {
|
|
int out = (f)(Yap_BlobTag(t), Yap_BlobInfo(t), LOCAL_extra_gc_cells, n);
|
|
if (out < 0) {
|
|
/* error: we don't have enough room */
|
|
/* could not find more trail */
|
|
save_machine_regs();
|
|
siglongjmp(LOCAL_gc_restore, 4);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* pop array entries first */
|
|
ArrayEntry *al = LOCAL_DynamicArrays;
|
|
GlobalEntry *gl = LOCAL_GlobalVariables;
|
|
|
|
LOCAL_GlobalArena = TrailTerm(ptr++);
|
|
while (al) {
|
|
al->ValueOfVE = TrailTerm(ptr++);
|
|
al = al->NextAE;
|
|
}
|
|
while (gl) {
|
|
Term t = gl->global;
|
|
if (!IsUnboundVar(&gl->global) &&
|
|
!IsAtomTerm(t) &&
|
|
!IsIntTerm(t)
|
|
) {
|
|
//fprintf(stderr,"out=%s %p\n", gl->AtomOfGE->StrOfAE, gl->global);
|
|
gl->global = TrailTerm(ptr++);
|
|
}
|
|
gl = gl->NextGE;
|
|
}
|
|
sal = LOCAL_StaticArrays;
|
|
while (sal) {
|
|
if (sal->ArrayType == array_of_nb_terms) {
|
|
UInt arity = -sal->ArrayEArity;
|
|
for (i=0; i < arity; i++) {
|
|
Term tlive = sal->ValueOfVE.lterms[i].tlive;
|
|
if (!IsVarTerm(tlive) || !IsUnboundVar(&sal->ValueOfVE.lterms[i].tlive)) {
|
|
sal->ValueOfVE.lterms[i].tlive = TrailTerm(ptr++);
|
|
}
|
|
}
|
|
}
|
|
sal = sal->NextAE;
|
|
}
|
|
LOCAL_GcGeneration = TrailTerm(ptr++);
|
|
LOCAL_GcPhase = TrailTerm(ptr++);
|
|
#ifdef COROUTINING
|
|
#ifdef MULTI_ASSIGNMENT_VARIABLES
|
|
LOCAL_WokenGoals = TrailTerm(ptr++);
|
|
LOCAL_AttsMutableList = TrailTerm(ptr++);
|
|
#endif
|
|
#endif
|
|
for (i = 1; i <= num_regs; i++)
|
|
XREGS[i] = TrailTerm(ptr++);
|
|
/* pop any live registers we might have hanging around */
|
|
if (nextop->opc == Yap_opcode(_move_back) ||
|
|
nextop->opc == Yap_opcode(_skip)) {
|
|
CELL *lab = (CELL *)(nextop->u.l.l);
|
|
CELL max = lab[0];
|
|
Int curr = lab[1];
|
|
lab += 2;
|
|
if (max) {
|
|
CELL i;
|
|
for (i=0L; i <= max; i++) {
|
|
if (i == 8*CellSize) {
|
|
curr = lab[0];
|
|
lab++;
|
|
}
|
|
if (curr & 1) {
|
|
XREGS[i] = TrailTerm(ptr++);
|
|
}
|
|
curr >>= 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if DEBUG && COUNT_CELLS_MARKED
|
|
static int
|
|
count_cells_marked(void)
|
|
{
|
|
CELL *current;
|
|
int found_marked = 0;
|
|
|
|
for (current = H - 1; current >= H0; current--) {
|
|
if (MARKED_PTR(current)) {
|
|
found_marked++;
|
|
}
|
|
}
|
|
return(found_marked);
|
|
}
|
|
#endif
|
|
|
|
|
|
static rb_red_blk_node *
|
|
RBMalloc(UInt size USES_REGS)
|
|
{
|
|
ADDR new = LOCAL_db_vec;
|
|
|
|
LOCAL_db_vec += size;
|
|
if ((ADDR)LOCAL_db_vec > LOCAL_TrailTop-1024) {
|
|
gc_growtrail(FALSE, NULL, NULL PASS_REGS);
|
|
}
|
|
return (rb_red_blk_node *)new;
|
|
}
|
|
|
|
static rb_red_blk_node *
|
|
RBTreeCreate(void) {
|
|
CACHE_REGS
|
|
rb_red_blk_node* temp;
|
|
|
|
/* see the comment in the rb_red_blk_tree structure in red_black_tree.h */
|
|
/* for information on nil and root */
|
|
temp=LOCAL_db_nil= RBMalloc(sizeof(rb_red_blk_node) PASS_REGS);
|
|
temp->parent=temp->left=temp->right=temp;
|
|
temp->red=0;
|
|
temp->key=NULL;
|
|
temp = RBMalloc(sizeof(rb_red_blk_node) PASS_REGS);
|
|
temp->parent=temp->left=temp->right=LOCAL_db_nil;
|
|
temp->key=NULL;
|
|
temp->red=0;
|
|
return temp;
|
|
}
|
|
|
|
/* This is code originally written by Emin Martinian */
|
|
|
|
/***********************************************************************/
|
|
/* FUNCTION: LeftRotate */
|
|
/**/
|
|
/* INPUTS: This takes a tree so that it can access the appropriate */
|
|
/* root and nil pointers, and the node to rotate on. */
|
|
/**/
|
|
/* OUTPUT: None */
|
|
/**/
|
|
/* Modifies Input: tree, x */
|
|
/**/
|
|
/* EFFECTS: Rotates as described in _Introduction_To_Algorithms by */
|
|
/* Cormen, Leiserson, Rivest (Chapter 14). Basically this */
|
|
/* makes the parent of x be to the left of x, x the parent of */
|
|
/* its parent before the rotation and fixes other pointers */
|
|
/* accordingly. */
|
|
/***********************************************************************/
|
|
|
|
static void
|
|
LeftRotate(rb_red_blk_node* x USES_REGS) {
|
|
rb_red_blk_node* y;
|
|
rb_red_blk_node* rb_nil=LOCAL_db_nil;
|
|
|
|
/* I originally wrote this function to use the sentinel for */
|
|
/* nil to avoid checking for nil. However this introduces a */
|
|
/* very subtle bug because sometimes this function modifies */
|
|
/* the parent pointer of nil. This can be a problem if a */
|
|
/* function which calls LeftRotate also uses the nil sentinel */
|
|
/* and expects the nil sentinel's parent pointer to be unchanged */
|
|
/* after calling this function. For example, when RBDeleteFixUP */
|
|
/* calls LeftRotate it expects the parent pointer of nil to be */
|
|
/* unchanged. */
|
|
|
|
y=x->right;
|
|
x->right=y->left;
|
|
|
|
if (y->left != rb_nil) y->left->parent=x; /* used to use sentinel here */
|
|
/* and do an unconditional assignment instead of testing for nil */
|
|
|
|
y->parent=x->parent;
|
|
|
|
/* instead of checking if x->parent is the root as in the book, we */
|
|
/* count on the root sentinel to implicitly take care of this case */
|
|
if( x == x->parent->left) {
|
|
x->parent->left=y;
|
|
} else {
|
|
x->parent->right=y;
|
|
}
|
|
y->left=x;
|
|
x->parent=y;
|
|
|
|
#ifdef DEBUG_ASSERT
|
|
Assert(!LOCAL_db_nil->red,"nil not red in LeftRotate");
|
|
#endif
|
|
}
|
|
|
|
|
|
/***********************************************************************/
|
|
/* FUNCTION: RighttRotate */
|
|
/**/
|
|
/* INPUTS: This takes a tree so that it can access the appropriate */
|
|
/* root and nil pointers, and the node to rotate on. */
|
|
/**/
|
|
/* OUTPUT: None */
|
|
/**/
|
|
/* Modifies Input?: tree, y */
|
|
/**/
|
|
/* EFFECTS: Rotates as described in _Introduction_To_Algorithms by */
|
|
/* Cormen, Leiserson, Rivest (Chapter 14). Basically this */
|
|
/* makes the parent of x be to the left of x, x the parent of */
|
|
/* its parent before the rotation and fixes other pointers */
|
|
/* accordingly. */
|
|
/***********************************************************************/
|
|
|
|
static void
|
|
RightRotate(rb_red_blk_node* y USES_REGS) {
|
|
rb_red_blk_node* x;
|
|
rb_red_blk_node* rb_nil=LOCAL_db_nil;
|
|
|
|
/* I originally wrote this function to use the sentinel for */
|
|
/* nil to avoid checking for nil. However this introduces a */
|
|
/* very subtle bug because sometimes this function modifies */
|
|
/* the parent pointer of nil. This can be a problem if a */
|
|
/* function which calls LeftRotate also uses the nil sentinel */
|
|
/* and expects the nil sentinel's parent pointer to be unchanged */
|
|
/* after calling this function. For example, when RBDeleteFixUP */
|
|
/* calls LeftRotate it expects the parent pointer of nil to be */
|
|
/* unchanged. */
|
|
|
|
x=y->left;
|
|
y->left=x->right;
|
|
|
|
if (rb_nil != x->right) x->right->parent=y; /*used to use sentinel here */
|
|
/* and do an unconditional assignment instead of testing for nil */
|
|
|
|
/* instead of checking if x->parent is the root as in the book, we */
|
|
/* count on the root sentinel to implicitly take care of this case */
|
|
x->parent=y->parent;
|
|
if( y == y->parent->left) {
|
|
y->parent->left=x;
|
|
} else {
|
|
y->parent->right=x;
|
|
}
|
|
x->right=y;
|
|
y->parent=x;
|
|
|
|
#ifdef DEBUG_ASSERT
|
|
Assert(!LOCAL_db_nil->red,"nil not red in RightRotate");
|
|
#endif
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/* FUNCTION: TreeInsertHelp */
|
|
/**/
|
|
/* INPUTS: tree is the tree to insert into and z is the node to insert */
|
|
/**/
|
|
/* OUTPUT: none */
|
|
/**/
|
|
/* Modifies Input: tree, z */
|
|
/**/
|
|
/* EFFECTS: Inserts z into the tree as if it were a regular binary tree */
|
|
/* using the algorithm described in _Introduction_To_Algorithms_ */
|
|
/* by Cormen et al. This funciton is only intended to be called */
|
|
/* by the RBTreeInsert function and not by the user */
|
|
/***********************************************************************/
|
|
|
|
static void
|
|
TreeInsertHelp(rb_red_blk_node* z USES_REGS) {
|
|
/* This function should only be called by InsertRBTree (see above) */
|
|
rb_red_blk_node* x;
|
|
rb_red_blk_node* y;
|
|
rb_red_blk_node* rb_nil=LOCAL_db_nil;
|
|
|
|
z->left=z->right=rb_nil;
|
|
y=LOCAL_db_root;
|
|
x=LOCAL_db_root->left;
|
|
while( x != rb_nil) {
|
|
y=x;
|
|
if (x->key < z->key) { /* x.key > z.key */
|
|
x=x->left;
|
|
} else { /* x,key <= z.key */
|
|
x=x->right;
|
|
}
|
|
}
|
|
z->parent=y;
|
|
if ( (y == LOCAL_db_root) ||
|
|
(y->key < z->key)) { /* y.key > z.key */
|
|
y->left=z;
|
|
} else {
|
|
y->right=z;
|
|
}
|
|
|
|
#ifdef DEBUG_ASSERT
|
|
Assert(!LOCAL_db_nil->red,"nil not red in TreeInsertHelp");
|
|
#endif
|
|
}
|
|
|
|
/* Before calling Insert RBTree the node x should have its key set */
|
|
|
|
/***********************************************************************/
|
|
/* FUNCTION: RBTreeInsert */
|
|
/**/
|
|
/* INPUTS: tree is the red-black tree to insert a node which has a key */
|
|
/* pointed to by key and info pointed to by info. */
|
|
/**/
|
|
/* OUTPUT: This function returns a pointer to the newly inserted node */
|
|
/* which is guarunteed to be valid until this node is deleted. */
|
|
/* What this means is if another data structure stores this */
|
|
/* pointer then the tree does not need to be searched when this */
|
|
/* is to be deleted. */
|
|
/**/
|
|
/* Modifies Input: tree */
|
|
/**/
|
|
/* EFFECTS: Creates a node node which contains the appropriate key and */
|
|
/* info pointers and inserts it into the tree. */
|
|
/***********************************************************************/
|
|
|
|
static rb_red_blk_node *
|
|
RBTreeInsert(CODEADDR key, CODEADDR end, db_entry_type db_type USES_REGS) {
|
|
rb_red_blk_node * y;
|
|
rb_red_blk_node * x;
|
|
rb_red_blk_node * newNode;
|
|
|
|
x=(rb_red_blk_node*) RBMalloc(sizeof(rb_red_blk_node) PASS_REGS);
|
|
x->key=key;
|
|
x->lim=end;
|
|
x->db_type=db_type;
|
|
x->in_use = FALSE;
|
|
|
|
TreeInsertHelp(x PASS_REGS);
|
|
newNode=x;
|
|
x->red=1;
|
|
while(x->parent->red) { /* use sentinel instead of checking for root */
|
|
if (x->parent == x->parent->parent->left) {
|
|
y=x->parent->parent->right;
|
|
if (y->red) {
|
|
x->parent->red=0;
|
|
y->red=0;
|
|
x->parent->parent->red=1;
|
|
x=x->parent->parent;
|
|
} else {
|
|
if (x == x->parent->right) {
|
|
x=x->parent;
|
|
LeftRotate(x PASS_REGS);
|
|
}
|
|
x->parent->red=0;
|
|
x->parent->parent->red=1;
|
|
RightRotate(x->parent->parent PASS_REGS);
|
|
}
|
|
} else { /* case for x->parent == x->parent->parent->right */
|
|
y=x->parent->parent->left;
|
|
if (y->red) {
|
|
x->parent->red=0;
|
|
y->red=0;
|
|
x->parent->parent->red=1;
|
|
x=x->parent->parent;
|
|
} else {
|
|
if (x == x->parent->left) {
|
|
x=x->parent;
|
|
RightRotate(x PASS_REGS);
|
|
}
|
|
x->parent->red=0;
|
|
x->parent->parent->red=1;
|
|
LeftRotate(x->parent->parent PASS_REGS);
|
|
}
|
|
}
|
|
}
|
|
LOCAL_db_root->left->red=0;
|
|
return newNode;
|
|
|
|
#ifdef DEBUG_ASSERT
|
|
Assert(!LOCAL_db_nil->red,"nil not red in RBTreeInsert");
|
|
Assert(!LOCAL_db_root->red,"root not red in RBTreeInsert");
|
|
#endif
|
|
}
|
|
|
|
|
|
/* init the table */
|
|
static void
|
|
store_in_dbtable(CODEADDR entry, CODEADDR end, db_entry_type db_type USES_REGS)
|
|
{
|
|
RBTreeInsert(entry, end, db_type PASS_REGS);
|
|
}
|
|
|
|
/* find an element in the dbentries table */
|
|
static rb_red_blk_node *
|
|
find_ref_in_dbtable(CODEADDR entry USES_REGS)
|
|
{
|
|
rb_red_blk_node *current = LOCAL_db_root->left;
|
|
|
|
while (current != LOCAL_db_nil) {
|
|
if (current->key <= entry && current->lim > entry) {
|
|
return current;
|
|
}
|
|
if (entry < current->key)
|
|
current = current->right;
|
|
else
|
|
current = current->left;
|
|
}
|
|
return current;
|
|
}
|
|
|
|
/* find an element in the dbentries table */
|
|
static void
|
|
mark_ref_in_use(DBRef ref USES_REGS)
|
|
{
|
|
rb_red_blk_node *el = find_ref_in_dbtable((CODEADDR)ref PASS_REGS);
|
|
el->in_use = TRUE;
|
|
}
|
|
|
|
static int
|
|
ref_in_use(DBRef ref USES_REGS)
|
|
{
|
|
rb_red_blk_node *el = find_ref_in_dbtable((CODEADDR)ref PASS_REGS);
|
|
return el->in_use;
|
|
}
|
|
|
|
static void
|
|
mark_db_fixed(CELL *ptr USES_REGS) {
|
|
rb_red_blk_node *el;
|
|
|
|
el = find_ref_in_dbtable((CODEADDR)ptr PASS_REGS);
|
|
if (el != LOCAL_db_nil) {
|
|
el->in_use = TRUE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
init_dbtable(tr_fr_ptr trail_ptr USES_REGS) {
|
|
StaticClause *sc = DeadStaticClauses;
|
|
MegaClause *mc = DeadMegaClauses;
|
|
StaticIndex *si = DeadStaticIndices;
|
|
|
|
LOCAL_extra_gc_cells =
|
|
LOCAL_extra_gc_cells_base = (CELL *)TR;
|
|
LOCAL_extra_gc_cells_top = LOCAL_extra_gc_cells_base+
|
|
LOCAL_extra_gc_cells_size;
|
|
if ((char *)LOCAL_extra_gc_cells_top > LOCAL_TrailTop-1024)
|
|
gc_growtrail(FALSE, NULL, NULL PASS_REGS);
|
|
LOCAL_db_vec0 = LOCAL_db_vec = (ADDR)LOCAL_extra_gc_cells_top;
|
|
LOCAL_db_root = RBTreeCreate();
|
|
while (trail_ptr > (tr_fr_ptr)LOCAL_TrailBase) {
|
|
register CELL trail_cell;
|
|
|
|
trail_ptr--;
|
|
|
|
trail_cell = TrailTerm(trail_ptr);
|
|
|
|
if (!IsVarTerm(trail_cell) && IsPairTerm(trail_cell)) {
|
|
CELL *pt0 = RepPair(trail_cell);
|
|
/* DB pointer */
|
|
CELL flags;
|
|
|
|
#ifdef FROZEN_STACKS /* TRAIL */
|
|
/* avoid frozen segments */
|
|
if (
|
|
#ifdef YAPOR_SBA
|
|
(ADDR) pt0 >= HeapTop
|
|
#else
|
|
(ADDR) pt0 >= LOCAL_TrailBase && (ADDR) pt0 < LOCAL_TrailTop
|
|
#endif
|
|
) {
|
|
continue;
|
|
}
|
|
#endif /* FROZEN_STACKS */
|
|
|
|
flags = *pt0;
|
|
/* for the moment, if all references to the term in the stacks
|
|
are only pointers, reset the flag */
|
|
if (FlagOn(DBClMask, flags)) {
|
|
DBRef dbr = DBStructFlagsToDBStruct(pt0);
|
|
store_in_dbtable((CODEADDR)dbr,
|
|
(CODEADDR)dbr+sizeof(DBStruct)+sizeof(CELL)*dbr->DBT.NOfCells,
|
|
db_entry PASS_REGS);
|
|
} else if (flags & LogUpdMask) {
|
|
if (flags & IndexMask) {
|
|
LogUpdIndex *li = ClauseFlagsToLogUpdIndex(pt0);
|
|
store_in_dbtable((CODEADDR)li, (CODEADDR)li+li->ClSize, li_entry PASS_REGS);
|
|
} else {
|
|
LogUpdClause *cli = ClauseFlagsToLogUpdClause(pt0);
|
|
store_in_dbtable((CODEADDR)cli, (CODEADDR)cli+cli->ClSize, lcl_entry PASS_REGS);
|
|
}
|
|
} else {
|
|
DynamicClause *dcl = ClauseFlagsToDynamicClause(pt0);
|
|
store_in_dbtable((CODEADDR)dcl, (CODEADDR)dcl+dcl->ClSize, dcl_entry PASS_REGS);
|
|
}
|
|
}
|
|
}
|
|
while (sc) {
|
|
store_in_dbtable((CODEADDR)sc, (CODEADDR)sc+sc->ClSize, dcl_entry PASS_REGS);
|
|
sc = sc->ClNext;
|
|
}
|
|
while (si) {
|
|
store_in_dbtable((CODEADDR)si, (CODEADDR)si+si->ClSize, dcl_entry PASS_REGS);
|
|
si = si->SiblingIndex;
|
|
}
|
|
while (mc) {
|
|
store_in_dbtable((CODEADDR)mc, (CODEADDR)mc+mc->ClSize, dcl_entry PASS_REGS);
|
|
mc = mc->ClNext;
|
|
}
|
|
if (LOCAL_db_vec == LOCAL_db_vec0) {
|
|
/* could not find any entries: probably using LOG UPD semantics */
|
|
LOCAL_db_vec0 = NULL;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
|
|
/* #define INSTRUMENT_GC 1 */
|
|
|
|
#ifdef INSTRUMENT_GC
|
|
typedef enum {
|
|
gc_var,
|
|
gc_ref,
|
|
gc_atom,
|
|
gc_int,
|
|
gc_num,
|
|
gc_list,
|
|
gc_appl,
|
|
gc_func,
|
|
gc_susp
|
|
} gc_types;
|
|
unsigned long chain[16];
|
|
unsigned long env_vars;
|
|
unsigned long vars[gc_susp+1];
|
|
|
|
unsigned long num_bs;
|
|
unsigned long old_vars, new_vars;
|
|
|
|
static CELL *TrueHB;
|
|
|
|
static void
|
|
inc_vars_of_type(CELL *curr,gc_types val) {
|
|
if (curr >= H0 && curr < TrueHB) {
|
|
old_vars++;
|
|
} else if (curr >= TrueHB && curr < HR) {
|
|
new_vars++;
|
|
} else {
|
|
return;
|
|
}
|
|
vars[val]++;
|
|
}
|
|
|
|
static void
|
|
put_type_info(unsigned long total)
|
|
{
|
|
fprintf(GLOBAL_stderr,"%% type info for %lu cells\n", total);
|
|
fprintf(GLOBAL_stderr,"%% %lu vars\n", vars[gc_var]);
|
|
fprintf(GLOBAL_stderr,"%% %lu refs\n", vars[gc_ref]);
|
|
fprintf(GLOBAL_stderr,"%% %lu references from env\n", env_vars);
|
|
fprintf(GLOBAL_stderr,"%% %lu atoms\n", vars[gc_atom]);
|
|
fprintf(GLOBAL_stderr,"%% %lu small ints\n", vars[gc_int]);
|
|
fprintf(GLOBAL_stderr,"%% %lu other numbers\n", vars[gc_num]);
|
|
fprintf(GLOBAL_stderr,"%% %lu lists\n", vars[gc_list]);
|
|
fprintf(GLOBAL_stderr,"%% %lu compound terms\n", vars[gc_appl]);
|
|
fprintf(GLOBAL_stderr,"%% %lu functors\n", vars[gc_func]);
|
|
fprintf(GLOBAL_stderr,"%% %lu suspensions\n", vars[gc_susp]);
|
|
}
|
|
|
|
static void
|
|
inc_var(CELL *current, CELL *next)
|
|
{
|
|
int len = 1;
|
|
CELL *mynext=next;
|
|
|
|
if (ONHEAP(current)) {
|
|
if (next == current) {
|
|
inc_vars_of_type(current,gc_var);
|
|
chain[0]++;
|
|
} else {
|
|
inc_vars_of_type(current,gc_ref);
|
|
while(ONHEAP(mynext) && IsVarTerm(*mynext)) {
|
|
CELL *prox = GET_NEXT(*mynext);
|
|
if (prox == mynext) {
|
|
chain[0]++;
|
|
break;
|
|
}
|
|
len++;
|
|
mynext = prox;
|
|
}
|
|
if (len>=15)
|
|
(chain[15])++;
|
|
else
|
|
(chain[len])++;
|
|
}
|
|
}
|
|
}
|
|
#endif /* INSTRUMENT_GC */
|
|
|
|
int vsc_stop(void);
|
|
|
|
int
|
|
vsc_stop(void) {
|
|
return(1);
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef CHECK_GLOBAL
|
|
static void
|
|
check_global(void) {
|
|
CELL *current;
|
|
|
|
#ifdef INSTRUMENT_GC
|
|
vars[gc_var] = 0;
|
|
vars[gc_ref] = 0;
|
|
vars[gc_atom] = 0;
|
|
vars[gc_int] = 0;
|
|
vars[gc_num] = 0;
|
|
vars[gc_list] = 0;
|
|
vars[gc_appl] = 0;
|
|
vars[gc_func] = 0;
|
|
vars[gc_susp] = 0;
|
|
#endif
|
|
for (current = H - 1; current >= H0; current--) {
|
|
CELL ccurr = *current;
|
|
|
|
if (MARKED_PTR(current)) {
|
|
CELL ccell = UNMARK_CELL(ccurr);
|
|
if (ccell == EndSpecials) {
|
|
/* oops, we found a blob */
|
|
CELL *ptr = current-1;
|
|
UInt nofcells;
|
|
|
|
while (!MARKED_PTR(ptr)) ptr--;
|
|
nofcells = current-ptr;
|
|
current = ptr;
|
|
ccurr = *current;
|
|
/* process the functor next */
|
|
}
|
|
}
|
|
#if INSTRUMENT_GC
|
|
if (IsVarTerm(ccurr)) {
|
|
if (IsBlobFunctor((Functor)ccurr)) vars[gc_num]++;
|
|
else if (ccurr != 0 && (ccurr < (CELL)LOCAL_GlobalBase || ccurr > (CELL)LOCAL_TrailTop)) {
|
|
/* printf("%p: %s/%d\n", current,
|
|
RepAtom(NameOfFunctor((Functor)ccurr))->StrOfAE,
|
|
ArityOfFunctor((Functor)ccurr));*/
|
|
vars[gc_func]++;
|
|
}
|
|
else if (IsUnboundVar(current)) vars[gc_var]++;
|
|
else vars[gc_ref]++;
|
|
} else if (IsApplTerm(ccurr)) {
|
|
/* printf("%p: f->%p\n",current,RepAppl(ccurr)); */
|
|
vars[gc_appl]++;
|
|
} else if (IsPairTerm(ccurr)) {
|
|
/* printf("%p: l->%p\n",current,RepPair(ccurr)); */
|
|
vars[gc_list]++;
|
|
} else if (IsAtomTerm(ccurr)) {
|
|
/* printf("%p: %s\n",current,RepAtom(AtomOfTerm(ccurr))->StrOfAE); */
|
|
vars[gc_atom]++;
|
|
} else if (IsIntTerm(ccurr)) {
|
|
/* printf("%p: %d\n",current,IntOfTerm(ccurr)); */
|
|
vars[gc_int]++;
|
|
}
|
|
#endif
|
|
}
|
|
#if INSTRUMENT_GC
|
|
put_type_info(H-H0);
|
|
vars[gc_var] = 0;
|
|
vars[gc_ref] = 0;
|
|
vars[gc_atom] = 0;
|
|
vars[gc_int] = 0;
|
|
vars[gc_num] = 0;
|
|
vars[gc_list] = 0;
|
|
vars[gc_appl] = 0;
|
|
vars[gc_func] = 0;
|
|
vars[gc_susp] = 0;
|
|
#endif
|
|
}
|
|
#else
|
|
#define check_global()
|
|
#endif /* CHECK_GLOBAL */
|
|
|
|
/* mark a heap object and all heap objects accessible from it */
|
|
|
|
static void
|
|
mark_variable(CELL_PTR current USES_REGS)
|
|
{
|
|
CELL_PTR next;
|
|
register CELL ccur;
|
|
unsigned int arity;
|
|
char *local_bp = LOCAL_bp;
|
|
|
|
begin:
|
|
if (UNMARKED_MARK(current,local_bp)) {
|
|
POP_CONTINUATION();
|
|
}
|
|
if (current >= H0 && current < HR) {
|
|
//fprintf(stderr,"%p M\n", current);
|
|
LOCAL_total_marked++;
|
|
if (current < LOCAL_HGEN) {
|
|
LOCAL_total_oldies++;
|
|
} else {
|
|
DEBUG_printf0("%p 1\n", current);
|
|
}
|
|
}
|
|
PUSH_POINTER(current PASS_REGS);
|
|
ccur = *current;
|
|
next = GET_NEXT(ccur);
|
|
|
|
if (IsVarTerm(ccur)) {
|
|
if (IN_BETWEEN(LOCAL_GlobalBase,current,HR) && GlobalIsAttVar(current) && current==next) {
|
|
if (next < H0) POP_CONTINUATION();
|
|
if (!UNMARKED_MARK(next-1,local_bp)) {
|
|
//fprintf(stderr,"%p M\n", next-1);
|
|
LOCAL_total_marked++;
|
|
if (next-1 < LOCAL_HGEN) {
|
|
LOCAL_total_oldies++;
|
|
} else {
|
|
DEBUG_printf0("%p 1\n", next-1);
|
|
}
|
|
PUSH_POINTER(next-1 PASS_REGS);
|
|
}
|
|
PUSH_CONTINUATION(next+1,2 PASS_REGS);
|
|
current = next;
|
|
goto begin;
|
|
} else if (ONHEAP(next)) {
|
|
#ifdef EASY_SHUNTING
|
|
CELL cnext;
|
|
/* do variable shunting between variables in the global */
|
|
cnext = *next;
|
|
|
|
if (!MARKED_PTR(next)) {
|
|
if (IsVarTerm(cnext) && (CELL)next == cnext) {
|
|
/* new global variable to new global variable */
|
|
if (next > current && current < LOCAL_prev_HB && current >= HB && next >= HB && next < LOCAL_prev_HB) {
|
|
#ifdef INSTRUMENT_GC
|
|
inc_var(current, current);
|
|
#endif
|
|
*next = (CELL)current;
|
|
UNMARK(next);
|
|
MARK(current);
|
|
*current = (CELL)current;
|
|
POP_CONTINUATION();
|
|
} else {
|
|
/* can't help here */
|
|
#ifdef INSTRUMENT_GC
|
|
inc_var(current, next);
|
|
#endif
|
|
current = next;
|
|
}
|
|
} else {
|
|
/* binding to a determinate reference */
|
|
if (next >= HB && current < LCL0 && cnext != TermFoundVar) {
|
|
UNMARK(current);
|
|
*current = cnext;
|
|
if (current >= H0 && current < HR) {
|
|
//fprintf(stderr,"%p M\n", current-1);
|
|
LOCAL_total_marked--;
|
|
if (current < LOCAL_HGEN) {
|
|
LOCAL_total_oldies--;
|
|
} else {
|
|
DEBUG_printf0("%p-1\n", next-1);
|
|
}
|
|
}
|
|
POP_POINTER( PASS_REGS1 );
|
|
} else {
|
|
#ifdef INSTRUMENT_GC
|
|
inc_var(current, next);
|
|
#endif
|
|
current = next;
|
|
}
|
|
}
|
|
/* try to shorten chains if they go through the current CP */
|
|
} else if (next > HB &&
|
|
IsVarTerm(cnext) &&
|
|
UNMARK_CELL(cnext) != (CELL)next &&
|
|
current < LCL0) {
|
|
/* This step is possible because we clean up the trail */
|
|
*current = UNMARK_CELL(cnext);
|
|
UNMARK(current);
|
|
if (current >= H0 && current < HR ) {
|
|
//fprintf(stderr,"%p M\n", current);
|
|
LOCAL_total_marked--;
|
|
if (current < LOCAL_HGEN) {
|
|
LOCAL_total_oldies--;
|
|
} else {
|
|
DEBUG_printf0("%p-1\n", next-1);
|
|
}
|
|
}
|
|
POP_POINTER( PASS_REGS1 );
|
|
} else
|
|
#endif
|
|
/* what I'd do without variable shunting */
|
|
{
|
|
#ifdef INSTRUMENT_GC
|
|
inc_var(current, next);
|
|
#endif
|
|
current = next;
|
|
}
|
|
goto begin;
|
|
#ifdef DEBUG
|
|
} else if (next < (CELL *)LOCAL_GlobalBase || next > (CELL *)LOCAL_TrailTop) {
|
|
fprintf(GLOBAL_stderr, "OOPS in GC: marking, current=%p, *current=" UInt_FORMAT " next=%p\n", current, ccur, next);
|
|
#endif
|
|
} else {
|
|
#ifdef COROUTING
|
|
LOCAL_total_smarked++;
|
|
#endif
|
|
#ifdef INSTRUMENT_GC
|
|
inc_var(current, next);
|
|
#endif
|
|
}
|
|
POP_CONTINUATION();
|
|
} else if (IsAtomOrIntTerm(ccur)) {
|
|
#ifdef INSTRUMENT_GC
|
|
if (IsAtomTerm(ccur))
|
|
inc_vars_of_type(current,gc_atom);
|
|
else
|
|
inc_vars_of_type(current, gc_int);
|
|
#endif
|
|
POP_CONTINUATION();
|
|
} else if (IsPairTerm(ccur)) {
|
|
#ifdef INSTRUMENT_GC
|
|
inc_vars_of_type(current,gc_list);
|
|
#endif
|
|
if (ONHEAP(next)) {
|
|
/* speedup for strings */
|
|
if (IsAtomOrIntTerm(*next)) {
|
|
if (!UNMARKED_MARK(next,local_bp)) {
|
|
//fprintf(stderr,"%p M\n", next);
|
|
LOCAL_total_marked++;
|
|
if (next < LOCAL_HGEN) {
|
|
LOCAL_total_oldies++;
|
|
} else {
|
|
DEBUG_printf0("%p 1\n", next);
|
|
}
|
|
PUSH_POINTER(next PASS_REGS);
|
|
}
|
|
current = next+1;
|
|
goto begin;
|
|
} else {
|
|
PUSH_CONTINUATION(next+1,1 PASS_REGS);
|
|
current = next;
|
|
goto begin;
|
|
}
|
|
} else if (ONCODE(next)) {
|
|
mark_db_fixed(RepPair(ccur) PASS_REGS);
|
|
}
|
|
POP_CONTINUATION();
|
|
} else if (IsApplTerm(ccur)) {
|
|
register CELL cnext = *next;
|
|
|
|
#ifdef INSTRUMENT_GC
|
|
if (!IsExtensionFunctor((Functor)cnext))
|
|
inc_vars_of_type(current,gc_appl);
|
|
else
|
|
inc_vars_of_type(current,gc_num);
|
|
#endif
|
|
if (ONCODE(next)) {
|
|
if ((Functor)cnext == FunctorDBRef) {
|
|
DBRef tref = DBRefOfTerm(ccur);
|
|
|
|
/* make sure the reference is marked as in use */
|
|
if ((tref->Flags & (ErasedMask|LogUpdMask)) == (ErasedMask|LogUpdMask)) {
|
|
*current = MkDBRefTerm((DBRef)LogDBErasedMarker);
|
|
MARK(current);
|
|
} else {
|
|
mark_ref_in_use(tref PASS_REGS);
|
|
}
|
|
} else {
|
|
mark_db_fixed(next PASS_REGS);
|
|
}
|
|
POP_CONTINUATION();
|
|
}
|
|
if ( MARKED_PTR(next) || !ONHEAP(next) )
|
|
POP_CONTINUATION();
|
|
|
|
if (next < H0) POP_CONTINUATION();
|
|
if (IsExtensionFunctor((Functor)cnext)) {
|
|
switch (cnext) {
|
|
case (CELL)FunctorLongInt:
|
|
MARK(next);
|
|
MARK(next+2);
|
|
if (next < LOCAL_HGEN) {
|
|
LOCAL_total_oldies+=3;
|
|
} else {
|
|
DEBUG_printf0("%p 1\n", next);
|
|
DEBUG_printf0("%p 3\n", next);
|
|
}
|
|
//fprintf(stderr,"%p M 3\n", next);
|
|
LOCAL_total_marked += 3;
|
|
PUSH_POINTER(next PASS_REGS);
|
|
PUSH_POINTER(next+2 PASS_REGS);
|
|
POP_CONTINUATION();
|
|
case (CELL)FunctorDouble:
|
|
MARK(next);
|
|
PUSH_POINTER(next PASS_REGS);
|
|
{
|
|
UInt sz = 1+SIZEOF_DOUBLE/SIZEOF_INT_P;
|
|
if (next < LOCAL_HGEN) {
|
|
LOCAL_total_oldies+= 1+sz;
|
|
} else {
|
|
DEBUG_printf0("%p 1\n", next);
|
|
DEBUG_printf1("%p %ld\n", next, (long int)(sz+1));
|
|
}
|
|
//fprintf(stderr,"%p M %d\n", next,1+sz);
|
|
LOCAL_total_marked += 1+sz;
|
|
PUSH_POINTER(next+sz PASS_REGS);
|
|
MARK(next+sz);
|
|
}
|
|
POP_CONTINUATION();
|
|
case (CELL)FunctorString:
|
|
MARK(next);
|
|
PUSH_POINTER(next PASS_REGS);
|
|
{
|
|
UInt sz = 2+next[1];
|
|
if (next < LOCAL_HGEN) {
|
|
LOCAL_total_oldies+= 1+sz;
|
|
} else {
|
|
DEBUG_printf0("%p 1\n", next);
|
|
DEBUG_printf1("%p %ld\n", next, (long int)(sz+1));
|
|
}
|
|
//fprintf(stderr,"%p M %d\n", next,1+sz);
|
|
LOCAL_total_marked += 1+sz;
|
|
PUSH_POINTER(next+sz PASS_REGS);
|
|
MARK(next+sz);
|
|
}
|
|
POP_CONTINUATION();
|
|
case (CELL)FunctorBigInt:
|
|
{
|
|
Opaque_CallOnGCMark f;
|
|
Term t = AbsAppl(next);
|
|
UInt sz = (sizeof(MP_INT)+CellSize+
|
|
((MP_INT *)(next+2))->_mp_alloc*sizeof(mp_limb_t))/CellSize;
|
|
|
|
MARK(next);
|
|
if ( (f = Yap_blob_gc_mark_handler(t)) ) {
|
|
Int n = (f)(Yap_BlobTag(t), Yap_BlobInfo(t), LOCAL_extra_gc_cells, LOCAL_extra_gc_cells_top - (LOCAL_extra_gc_cells+2));
|
|
if (n < 0) {
|
|
/* error: we don't have enough room */
|
|
/* could not find more trail */
|
|
save_machine_regs();
|
|
siglongjmp(LOCAL_gc_restore, 3);
|
|
} else if (n > 0) {
|
|
CELL *ptr = LOCAL_extra_gc_cells;
|
|
|
|
LOCAL_extra_gc_cells += n+2;
|
|
PUSH_CONTINUATION(ptr, n+1 PASS_REGS);
|
|
ptr += n;
|
|
ptr[0] = t;
|
|
ptr[1] = n+1;
|
|
}
|
|
}
|
|
|
|
/* size is given by functor + friends */
|
|
if (next < LOCAL_HGEN) {
|
|
LOCAL_total_oldies += 2+sz;
|
|
} else {
|
|
DEBUG_printf0("%p 1\n", next);
|
|
DEBUG_printf1("%p %ld\n", next, (long int)(sz+2));
|
|
}
|
|
//fprintf(stderr,"%p M %d\n", next,2+sz);
|
|
LOCAL_total_marked += 2+sz;
|
|
PUSH_POINTER(next PASS_REGS);
|
|
sz++;
|
|
#if DEBUG
|
|
if (next[sz] != EndSpecials) {
|
|
fprintf(stderr,"[ Error: could not find EndSpecials at blob %p type " UInt_FORMAT " ]\n", next, next[1]);
|
|
}
|
|
#endif
|
|
MARK(next+sz);
|
|
PUSH_POINTER(next+sz PASS_REGS);
|
|
}
|
|
default:
|
|
POP_CONTINUATION();
|
|
}
|
|
}
|
|
if (next < H0) POP_CONTINUATION();
|
|
#ifdef INSTRUMENT_GC
|
|
inc_vars_of_type(next,gc_func);
|
|
#endif
|
|
arity = ArityOfFunctor((Functor)(cnext));
|
|
MARK(next);
|
|
//fprintf(stderr,"%p M\n", next);
|
|
++LOCAL_total_marked;
|
|
if (next < LOCAL_HGEN) {
|
|
++LOCAL_total_oldies;
|
|
} else {
|
|
DEBUG_printf0("%p 1\n", next);
|
|
}
|
|
PUSH_POINTER(next PASS_REGS);
|
|
next++;
|
|
/* speedup for leaves */
|
|
while (arity && IsAtomOrIntTerm(*next)) {
|
|
if (!UNMARKED_MARK(next,local_bp)) {
|
|
//fprintf(stderr,"%p M\n", next);
|
|
LOCAL_total_marked++;
|
|
if (next < LOCAL_HGEN) {
|
|
LOCAL_total_oldies++;
|
|
} else {
|
|
DEBUG_printf0("%p 1\n", next);
|
|
}
|
|
PUSH_POINTER(next PASS_REGS);
|
|
}
|
|
next++;
|
|
arity--;
|
|
}
|
|
if (!arity) POP_CONTINUATION();
|
|
current = next;
|
|
if (arity == 1) goto begin;
|
|
PUSH_CONTINUATION(current+1,arity-1 PASS_REGS);
|
|
goto begin;
|
|
}
|
|
}
|
|
|
|
void
|
|
Yap_mark_variable(CELL_PTR current)
|
|
{
|
|
CACHE_REGS
|
|
mark_variable(current PASS_REGS);
|
|
}
|
|
|
|
static void
|
|
mark_code(CELL_PTR ptr, CELL *next USES_REGS)
|
|
{
|
|
if (ONCODE(next)) {
|
|
CELL reg = *ptr;
|
|
if (IsApplTerm(reg) && (Functor)(*next) == FunctorDBRef) {
|
|
DBRef tref = DBRefOfTerm(reg);
|
|
/* make sure the reference is marked as in use */
|
|
if ((tref->Flags & (LogUpdMask|ErasedMask)) == (LogUpdMask|ErasedMask)) {
|
|
*ptr = MkDBRefTerm((DBRef)LogDBErasedMarker);
|
|
} else {
|
|
mark_ref_in_use(tref PASS_REGS);
|
|
}
|
|
} else {
|
|
mark_db_fixed(next PASS_REGS);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
mark_external_reference(CELL *ptr USES_REGS) {
|
|
CELL *next = GET_NEXT(*ptr);
|
|
|
|
if (ONHEAP(next)) {
|
|
#ifdef HYBRID_SCHEME
|
|
CELL_PTR *old = LOCAL_iptop;
|
|
#endif
|
|
mark_variable(ptr PASS_REGS);
|
|
POPSWAP_POINTER(old, ptr PASS_REGS);
|
|
} else {
|
|
MARK(ptr);
|
|
mark_code(ptr, next PASS_REGS);
|
|
}
|
|
}
|
|
|
|
static void inline
|
|
mark_external_reference2(CELL *ptr USES_REGS) {
|
|
CELL *next = GET_NEXT(*ptr);
|
|
|
|
if (ONHEAP(next)) {
|
|
#ifdef HYBRID_SCHEME
|
|
CELL_PTR *old = LOCAL_iptop;
|
|
#endif
|
|
mark_variable(ptr PASS_REGS);
|
|
POPSWAP_POINTER(old, ptr PASS_REGS);
|
|
} else {
|
|
mark_code(ptr,next PASS_REGS);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* mark all heap objects accessible from the trail (which includes the active
|
|
* general purpose registers)
|
|
*/
|
|
|
|
void
|
|
Yap_mark_external_reference(CELL *ptr) {
|
|
CACHE_REGS
|
|
mark_external_reference(ptr PASS_REGS);
|
|
}
|
|
|
|
static void
|
|
mark_regs(tr_fr_ptr old_TR USES_REGS)
|
|
{
|
|
tr_fr_ptr trail_ptr;
|
|
|
|
|
|
/* first, whatever we dumped on the trail. Easier just to do
|
|
the registers separately? */
|
|
for (trail_ptr = old_TR; trail_ptr < TR; trail_ptr++) {
|
|
mark_external_reference(&TrailTerm(trail_ptr) PASS_REGS);
|
|
}
|
|
}
|
|
|
|
/* mark all heap objects accessible from a chain of environments */
|
|
|
|
static void
|
|
mark_environments(CELL_PTR gc_ENV, OPREG size, CELL *pvbmap USES_REGS)
|
|
{
|
|
CELL_PTR saved_var;
|
|
while (gc_ENV != NULL) { /* no more environments */
|
|
Int bmap = 0;
|
|
int currv = 0;
|
|
|
|
// printf("MARK %p--%p\n", gc_ENV, gc_ENV-size);
|
|
#ifdef DEBUG
|
|
if (size < 0 || size > 512)
|
|
fprintf(GLOBAL_stderr,"OOPS in GC: env size for %p is " UInt_FORMAT "\n", gc_ENV, (CELL)size);
|
|
#endif
|
|
mark_db_fixed((CELL *)gc_ENV[E_CP] PASS_REGS);
|
|
/* for each saved variable */
|
|
if (size > EnvSizeInCells) {
|
|
int tsize = size - EnvSizeInCells;
|
|
|
|
currv = sizeof(CELL)*8-tsize%(sizeof(CELL)*8);
|
|
if (pvbmap != NULL) {
|
|
pvbmap += tsize/(sizeof(CELL)*8);
|
|
bmap = *pvbmap;
|
|
} else {
|
|
bmap = ((CELL)-1);
|
|
}
|
|
bmap = (Int)(((CELL)bmap) << currv);
|
|
}
|
|
|
|
for (saved_var = gc_ENV - size; saved_var < gc_ENV - EnvSizeInCells; saved_var++) {
|
|
if (currv == sizeof(CELL)*8) {
|
|
if (pvbmap) {
|
|
pvbmap--;
|
|
bmap = *pvbmap;
|
|
} else {
|
|
bmap = ((CELL)-1);
|
|
}
|
|
currv = 0;
|
|
}
|
|
/* we may have already been here */
|
|
if (bmap < 0 && !MARKED_PTR(saved_var)) {
|
|
#ifdef INSTRUMENT_GC
|
|
Term ccur = *saved_var;
|
|
|
|
if (IsVarTerm(ccur)) {
|
|
int len = 1;
|
|
CELL *mynext= GET_NEXT(ccur);
|
|
|
|
if (ONHEAP(mynext)) {
|
|
env_vars++;
|
|
while(ONHEAP(mynext) && IsVarTerm(*mynext)) {
|
|
CELL *prox = GET_NEXT(*mynext);
|
|
if (prox == mynext) {
|
|
chain[0]++;
|
|
break;
|
|
}
|
|
len++;
|
|
mynext = prox;
|
|
}
|
|
if (len>=15) {
|
|
(chain[15])++;
|
|
} else {
|
|
(chain[len])++;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
mark_external_reference(saved_var PASS_REGS);
|
|
}
|
|
bmap <<= 1;
|
|
currv++;
|
|
}
|
|
/* have we met this environment before?? */
|
|
/* we use the B field in the environment to tell whether we have
|
|
been here before or not.
|
|
|
|
We do it at the end because we don't want to lose any variables
|
|
that would have been trimmed at the first environment visit.
|
|
*/
|
|
if (MARKED_PTR(gc_ENV+E_CB))
|
|
return;
|
|
MARK(gc_ENV+E_CB);
|
|
|
|
size = EnvSize((yamop *) (gc_ENV[E_CP])); /* size = EnvSize(CP) */
|
|
pvbmap = EnvBMap((yamop *) (gc_ENV[E_CP]));
|
|
#if 0
|
|
if (size < 0) {
|
|
PredEntry *pe = EnvPreg(gc_ENV[E_CP]);
|
|
op_numbers op = Yap_op_from_opcode(ENV_ToOp(gc_ENV[E_CP]));
|
|
#if defined(ANALYST) || defined(DEBUG)
|
|
fprintf(GLOBAL_stderr,"ENV %p-%p(%d) %s\n", gc_ENV, pvbmap, size-EnvSizeInCells, Yap_op_names[op]);
|
|
#else
|
|
fprintf(GLOBAL_stderr,"ENV %p-%p(%d) %d\n", gc_ENV, pvbmap, size-EnvSizeInCells, (int)op);
|
|
#endif
|
|
if (pe->ArityOfPE)
|
|
fprintf(GLOBAL_stderr," %s/%d\n", RepAtom(NameOfFunctor(pe->FunctorOfPred))->StrOfAE, pe->ArityOfPE);
|
|
else
|
|
fprintf(GLOBAL_stderr," %s\n", RepAtom((Atom)(pe->FunctorOfPred))->StrOfAE);
|
|
}
|
|
#endif
|
|
gc_ENV = (CELL_PTR) gc_ENV[E_E]; /* link to prev
|
|
* environment */
|
|
}
|
|
}
|
|
|
|
/*
|
|
Cleaning the trail should be quick and simple, right? Well, not
|
|
really :-(. The problem is that the trail includes a dumping ground
|
|
of the WAM registers and of extra choice-point fields, which need
|
|
to be cleaned from somewhere.
|
|
|
|
And cleaning the trail itself is not easy. The problem is that we
|
|
may not have cleaned the trail after cuts. If we naively followed
|
|
these pointers, we could have direct references to the global
|
|
stack! A solution is to verify whether we are poiting at a
|
|
legitimate trail entry. Unfortunately this requires some extra work
|
|
following choice-points.
|
|
|
|
*/
|
|
|
|
|
|
static void
|
|
mark_trail(tr_fr_ptr trail_ptr, tr_fr_ptr trail_base, CELL *gc_H, choiceptr gc_B USES_REGS)
|
|
{
|
|
#ifdef EASY_SHUNTING
|
|
tr_fr_ptr begsTR = NULL, endsTR = NULL;
|
|
tr_fr_ptr OldsTR0 = LOCAL_sTR0;
|
|
#endif
|
|
#ifdef COROUTINING
|
|
CELL *detatt = NULL;
|
|
#endif
|
|
cont *old_cont_top0 = LOCAL_cont_top0;
|
|
|
|
|
|
GC_NEW_MAHASH((gc_ma_hash_entry *)LOCAL_cont_top0 PASS_REGS);
|
|
while (trail_base < trail_ptr) {
|
|
register CELL trail_cell;
|
|
|
|
trail_cell = TrailTerm(trail_base);
|
|
if (IsVarTerm(trail_cell)) {
|
|
CELL *hp = (CELL *)trail_cell;
|
|
/* if a variable older than the current CP has not been marked yet,
|
|
than its new binding is not accessible and we can reset it. Note
|
|
we must use gc_H to avoid trouble with dangling variables
|
|
in the heap */
|
|
if (((hp < gc_H && hp >= H0) || (hp > (CELL *)gc_B && hp < LCL0) ) && !MARKED_PTR(hp)) {
|
|
/* perform early reset */
|
|
/* reset term to be a variable */
|
|
RESET_VARIABLE(hp);
|
|
LOCAL_discard_trail_entries++;
|
|
RESET_VARIABLE(&TrailTerm(trail_base));
|
|
#ifdef FROZEN_STACKS
|
|
RESET_VARIABLE(&TrailVal(trail_base));
|
|
#endif
|
|
} else if (hp < (CELL *)LOCAL_GlobalBase || hp > (CELL *)LOCAL_TrailTop) {
|
|
/* pointers from the Heap back into the trail are process in mark_regs. */
|
|
/* do nothing !!! */
|
|
} else if ((hp < (CELL *)gc_B && hp >= gc_H) || hp > (CELL *)LOCAL_TrailBase) {
|
|
/* clean the trail, avoid dangling pointers! */
|
|
RESET_VARIABLE(&TrailTerm(trail_base));
|
|
#ifdef FROZEN_STACKS
|
|
RESET_VARIABLE(&TrailVal(trail_base));
|
|
#endif
|
|
LOCAL_discard_trail_entries++;
|
|
} else {
|
|
if (trail_cell == (CELL)trail_base)
|
|
LOCAL_discard_trail_entries++;
|
|
else {
|
|
/* This is a bit of a mess: when I find an attributed variable that was bound
|
|
nondeterministically, I know that after backtracking it will be back to be an unbound variable.
|
|
The ideal solution would be to unbind all variables. The current solution is to
|
|
remark it as an attributed variable */
|
|
if (IN_BETWEEN(LOCAL_GlobalBase,hp,HR) && GlobalIsAttVar(hp) && !UNMARKED_MARK(hp-1,LOCAL_bp)) {
|
|
//fprintf(stderr,"%p M\n", hp);
|
|
LOCAL_total_marked++;
|
|
PUSH_POINTER(hp-1 PASS_REGS);
|
|
if (hp-1 < LOCAL_HGEN) {
|
|
LOCAL_total_oldies++;
|
|
} else {
|
|
DEBUG_printf0("%p 1\n", hp-1);
|
|
}
|
|
mark_variable(hp+1 PASS_REGS);
|
|
mark_variable(hp+2 PASS_REGS);
|
|
}
|
|
#ifdef FROZEN_STACKS
|
|
mark_external_reference(&TrailVal(trail_base) PASS_REGS);
|
|
#endif
|
|
}
|
|
#ifdef EASY_SHUNTING
|
|
if (hp < gc_H && hp >= H0 && !MARKED_PTR(hp)) {
|
|
tr_fr_ptr nsTR = (tr_fr_ptr)LOCAL_cont_top0;
|
|
CELL *cptr = (CELL *)trail_cell;
|
|
|
|
if ((ADDR)nsTR > LOCAL_TrailTop-1024) {
|
|
gc_growtrail(TRUE, begsTR, old_cont_top0 PASS_REGS);
|
|
}
|
|
TrailTerm(nsTR) = (CELL)NULL;
|
|
TrailTerm(nsTR+1) = *hp;
|
|
TrailTerm(nsTR+2) = trail_cell;
|
|
if (begsTR == NULL)
|
|
begsTR = nsTR;
|
|
else
|
|
TrailTerm(endsTR) = (CELL)nsTR;
|
|
endsTR = nsTR;
|
|
LOCAL_cont_top = (cont *)(nsTR+3);
|
|
LOCAL_sTR = (tr_fr_ptr)LOCAL_cont_top;
|
|
LOCAL_gc_ma_h_top = (gc_ma_hash_entry *)(nsTR+3);
|
|
RESET_VARIABLE(cptr);
|
|
MARK(cptr);
|
|
}
|
|
#endif
|
|
}
|
|
} else if (IsPairTerm(trail_cell)) {
|
|
/* cannot safely ignore this */
|
|
CELL *cptr = RepPair(trail_cell);
|
|
if (IN_BETWEEN(LOCAL_GlobalBase,cptr,HR)) {
|
|
if (GlobalIsAttVar(cptr)) {
|
|
TrailTerm(trail_base) = (CELL)cptr;
|
|
mark_external_reference(&TrailTerm(trail_base) PASS_REGS);
|
|
TrailTerm(trail_base) = trail_cell;
|
|
} else if (*cptr == (CELL)FunctorBigInt) {
|
|
TrailTerm(trail_base) = AbsAppl(cptr);
|
|
mark_external_reference(&TrailTerm(trail_base) PASS_REGS);
|
|
TrailTerm(trail_base) = trail_cell;
|
|
}
|
|
#ifdef DEBUG
|
|
else
|
|
fprintf(GLOBAL_stderr,"OOPS in GC: weird trail entry at %p:" UInt_FORMAT "\n", &TrailTerm(trail_base), (CELL)cptr);
|
|
#endif
|
|
}
|
|
}
|
|
#if MULTI_ASSIGNMENT_VARIABLES
|
|
else {
|
|
CELL *cptr = RepAppl(trail_cell);
|
|
/* This is a bit complex. The idea is that we may have several
|
|
trailings for the same mavar in the same trail segment. Essentially,
|
|
the problem arises because of !. What we want is to ignore all but
|
|
the last entry, or in this case, all but the first entry with the last
|
|
value.
|
|
|
|
*/
|
|
if (cptr < (CELL *)gc_B && cptr >= gc_H) {
|
|
goto remove_trash_entry;
|
|
} else if (IsAttVar(cptr)) {
|
|
/* MABINDING that should be recovered */
|
|
if (detatt && cptr < detatt) {
|
|
goto remove_trash_entry;
|
|
} else {
|
|
/* This attributed variable is still in play */
|
|
mark_variable(cptr PASS_REGS);
|
|
}
|
|
}
|
|
if (!gc_lookup_ma_var(cptr, trail_base PASS_REGS)) {
|
|
/* check whether this is the first time we see it*/
|
|
Term t0 = TrailTerm(trail_base+1);
|
|
|
|
if (!IsAtomicTerm(t0)) {
|
|
CELL *next = GET_NEXT(t0);
|
|
/* check if we have a garbage entry, where we are setting a
|
|
pointer to ourselves. */
|
|
if (next < (CELL *)gc_B && next >= gc_H) {
|
|
goto remove_trash_entry;
|
|
}
|
|
}
|
|
if (HEAP_PTR(trail_cell)) {
|
|
/* fool the gc into thinking this is a variable */
|
|
TrailTerm(trail_base) = (CELL)cptr;
|
|
mark_external_reference(&(TrailTerm(trail_base)) PASS_REGS);
|
|
/* reset the gc to believe the original tag */
|
|
TrailTerm(trail_base) = AbsAppl((CELL *)TrailTerm(trail_base));
|
|
}
|
|
#ifdef FROZEN_STACKS
|
|
mark_external_reference(&(TrailVal(trail_base)) PASS_REGS);
|
|
trail_base++;
|
|
if (HEAP_PTR(trail_cell)) {
|
|
TrailTerm(trail_base) = (CELL)cptr;
|
|
mark_external_reference(&(TrailTerm(trail_base)) PASS_REGS);
|
|
/* reset the gc to believe the original tag */
|
|
TrailTerm(trail_base) = AbsAppl((CELL *)TrailTerm(trail_base));
|
|
}
|
|
/* don't need to mark the next TrailVal, this is done at the end
|
|
of segment */
|
|
#else
|
|
trail_base++;
|
|
mark_external_reference(&(TrailTerm(trail_base)) PASS_REGS);
|
|
trail_base ++;
|
|
if (HEAP_PTR(trail_cell)) {
|
|
/* fool the gc into thinking this is a variable */
|
|
TrailTerm(trail_base) = (CELL)cptr;
|
|
mark_external_reference(&(TrailTerm(trail_base)) PASS_REGS);
|
|
/* reset the gc to believe the original tag */
|
|
TrailTerm(trail_base) = AbsAppl((CELL *)TrailTerm(trail_base));
|
|
}
|
|
#endif /* TABLING */
|
|
} else {
|
|
remove_trash_entry:
|
|
/* we can safely ignore this little monster */
|
|
#ifdef FROZEN_STACKS
|
|
LOCAL_discard_trail_entries += 2;
|
|
RESET_VARIABLE(&TrailTerm(trail_base));
|
|
RESET_VARIABLE(&TrailVal(trail_base));
|
|
#else
|
|
LOCAL_discard_trail_entries += 3;
|
|
RESET_VARIABLE(&TrailTerm(trail_base));
|
|
trail_base++;
|
|
RESET_VARIABLE(&TrailTerm(trail_base));
|
|
#endif
|
|
trail_base++;
|
|
RESET_VARIABLE(&TrailTerm(trail_base));
|
|
#ifdef FROZEN_STACKS
|
|
RESET_VARIABLE(&TrailVal(trail_base));
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
trail_base++;
|
|
}
|
|
#if TABLING
|
|
/*
|
|
Ugly, but needed: we're not really sure about what were the new
|
|
values until the very end
|
|
*/
|
|
{
|
|
gc_ma_hash_entry *gl = LOCAL_gc_ma_h_list;
|
|
while (gl) {
|
|
mark_external_reference(&(TrailVal(gl->loc+1)) PASS_REGS);
|
|
gl = gl->more;
|
|
}
|
|
}
|
|
#endif /* TABLING */
|
|
#ifdef EASY_SHUNTING
|
|
/* set back old variables */
|
|
LOCAL_sTR = (tr_fr_ptr)old_cont_top0;
|
|
while (begsTR != NULL) {
|
|
tr_fr_ptr newsTR = (tr_fr_ptr)TrailTerm(begsTR);
|
|
TrailTerm(LOCAL_sTR) = TrailTerm(begsTR+1);
|
|
TrailTerm(LOCAL_sTR+1) = TrailTerm(begsTR+2);
|
|
begsTR = newsTR;
|
|
LOCAL_sTR += 2;
|
|
}
|
|
LOCAL_sTR0 = OldsTR0;
|
|
#else
|
|
LOCAL_cont_top0 = old_cont_top0;
|
|
#endif
|
|
LOCAL_cont_top = LOCAL_cont_top0;
|
|
}
|
|
|
|
/*
|
|
* mark all heap objects accessible from each choicepoint & its chain of
|
|
* environments
|
|
*/
|
|
|
|
#ifdef TABLING
|
|
#define init_substitution_pointer(GCB, SUBS_PTR, DEP_FR) \
|
|
if (DepFr_leader_cp(DEP_FR) == GCB) { \
|
|
/* GCB is a generator-consumer node */ \
|
|
/* never here if batched scheduling */ \
|
|
SUBS_PTR = (CELL *) (GEN_CP(GCB) + 1); \
|
|
SUBS_PTR += SgFr_arity(GEN_CP(GCB)->cp_sg_fr); \
|
|
} else { \
|
|
SUBS_PTR = (CELL *) (CONS_CP(GCB) + 1); \
|
|
}
|
|
#endif /* TABLING */
|
|
|
|
|
|
static void
|
|
mark_slots( USES_REGS1 )
|
|
{
|
|
Int curslot = LOCAL_CurSlot;
|
|
while (curslot) {
|
|
CELL *ptr = LCL0-curslot;
|
|
Int ns = IntegerOfTerm(ptr[-1]);
|
|
curslot = IntegerOfTerm(ptr[0]);
|
|
ptr-=2;
|
|
while (ns > 0) {
|
|
mark_external_reference(ptr PASS_REGS);
|
|
ptr--;
|
|
ns--;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef TABLING
|
|
static choiceptr
|
|
youngest_cp(choiceptr gc_B, dep_fr_ptr *depfrp)
|
|
{
|
|
dep_fr_ptr depfr = *depfrp;
|
|
choiceptr min = gc_B;
|
|
|
|
if (!gc_B) {
|
|
return gc_B;
|
|
}
|
|
if (depfr && min > DepFr_cons_cp(depfr)) {
|
|
min = DepFr_cons_cp(depfr);
|
|
}
|
|
if (depfr && min == DepFr_cons_cp(depfr)) {
|
|
*depfrp = DepFr_next(depfr);
|
|
}
|
|
return min;
|
|
}
|
|
#endif /* TABLING */
|
|
|
|
|
|
static void
|
|
mark_choicepoints(register choiceptr gc_B, tr_fr_ptr saved_TR, int very_verbose USES_REGS)
|
|
{
|
|
OPCODE
|
|
trust_lu = Yap_opcode(_trust_logical),
|
|
count_trust_lu = Yap_opcode(_count_trust_logical),
|
|
profiled_trust_lu = Yap_opcode(_profiled_trust_logical);
|
|
|
|
yamop *lu_cl0 = NEXTOP(PredLogUpdClause0->CodeOfPred,Otapl),
|
|
*lu_cl = NEXTOP(PredLogUpdClause->CodeOfPred,Otapl),
|
|
*lu_cle = NEXTOP(PredLogUpdClauseErase->CodeOfPred,Otapl),
|
|
*su_cl = NEXTOP(PredStaticClause->CodeOfPred,Otapl);
|
|
#ifdef TABLING
|
|
dep_fr_ptr depfr = LOCAL_top_dep_fr;
|
|
sg_fr_ptr aux_sg_fr = LOCAL_top_sg_fr;
|
|
#endif /* TABLING */
|
|
|
|
#ifdef TABLING
|
|
gc_B = youngest_cp(gc_B, &depfr);
|
|
#endif /* TABLING */
|
|
while (gc_B != NULL) {
|
|
op_numbers opnum;
|
|
register OPCODE op;
|
|
yamop *rtp = gc_B->cp_ap;
|
|
|
|
mark_db_fixed((CELL *)rtp PASS_REGS);
|
|
#ifdef DETERMINISTIC_TABLING
|
|
if (!IS_DET_GEN_CP(gc_B))
|
|
#endif /* DETERMINISTIC_TABLING */
|
|
mark_db_fixed((CELL *)(gc_B->cp_cp) PASS_REGS);
|
|
#ifdef EASY_SHUNTING
|
|
LOCAL_current_B = gc_B;
|
|
LOCAL_prev_HB = HB;
|
|
#endif
|
|
HB = gc_B->cp_h;
|
|
#ifdef INSTRUMENT_GC
|
|
num_bs++;
|
|
#endif
|
|
#ifdef TABLING
|
|
if (rtp == NULL) {
|
|
if (aux_sg_fr && gc_B == SgFr_gen_cp(aux_sg_fr)) {
|
|
/* found generator */
|
|
opnum = _table_completion;
|
|
} else {
|
|
/* found sld node is done */
|
|
opnum = _trust_fail;
|
|
}
|
|
} else {
|
|
#endif /* TABLING */
|
|
op = rtp->opc;
|
|
opnum = Yap_op_from_opcode(op);
|
|
// fprintf(stderr, "%s\n", Yap_op_names[opnum]);
|
|
#ifdef TABLING
|
|
}
|
|
// printf("MARK CP %p (%d)\n", gc_B, opnum);
|
|
if (aux_sg_fr && gc_B == SgFr_gen_cp(aux_sg_fr)) {
|
|
aux_sg_fr = SgFr_next(aux_sg_fr);
|
|
}
|
|
#endif /* TABLING */
|
|
if (very_verbose) {
|
|
PredEntry *pe = Yap_PredForChoicePt(gc_B);
|
|
#if defined(ANALYST) || DEBUG
|
|
if (pe == NULL) {
|
|
fprintf(GLOBAL_stderr,"%% marked " UInt_FORMAT " (%s)\n", LOCAL_total_marked, Yap_op_names[opnum]);
|
|
} else if (pe->ArityOfPE) {
|
|
fprintf(GLOBAL_stderr,"%% %s/" UInt_FORMAT " marked " UInt_FORMAT " (%s)\n", RepAtom(NameOfFunctor(pe->FunctorOfPred))->StrOfAE, pe->ArityOfPE, LOCAL_total_marked, Yap_op_names[opnum]);
|
|
} else {
|
|
fprintf(GLOBAL_stderr,"%% %s marked " UInt_FORMAT " (%s)\n", RepAtom((Atom)(pe->FunctorOfPred))->StrOfAE, LOCAL_total_marked, Yap_op_names[opnum]);
|
|
}
|
|
#else
|
|
if (pe == NULL) {
|
|
fprintf(GLOBAL_stderr,"%% marked " Int_FORMAT " (%u)\n", LOCAL_total_marked, (unsigned int)opnum);
|
|
} else if (pe->ArityOfPE) {
|
|
fprintf(GLOBAL_stderr,"%% %s/%lu marked " Int_FORMAT " (%u)\n", RepAtom(NameOfFunctor(pe->FunctorOfPred))->StrOfAE, (unsigned long int)pe->ArityOfPE, LOCAL_total_marked, (unsigned int)opnum);
|
|
} else {
|
|
fprintf(GLOBAL_stderr,"%% %s marked " Int_FORMAT " (%u)\n", RepAtom((Atom)(pe->FunctorOfPred))->StrOfAE, LOCAL_total_marked, (unsigned int)opnum);
|
|
}
|
|
#endif
|
|
}
|
|
{
|
|
/* find out how many cells are still alive in the trail */
|
|
mark_trail(saved_TR, gc_B->cp_tr, gc_B->cp_h, gc_B PASS_REGS);
|
|
saved_TR = gc_B->cp_tr;
|
|
}
|
|
if (opnum == _or_else || opnum == _or_last) {
|
|
/* ; choice point */
|
|
mark_environments((CELL_PTR) (gc_B->cp_a1),
|
|
-gc_B->cp_cp->u.Osblp.s / ((OPREG)sizeof(CELL)),
|
|
gc_B->cp_cp->u.Osblp.bmap
|
|
PASS_REGS);
|
|
} else {
|
|
/* choicepoint with arguments */
|
|
register CELL_PTR saved_reg;
|
|
OPREG nargs;
|
|
|
|
//printf("gc_B=%p %ld\n", gc_B, opnum);
|
|
if (opnum == _Nstop)
|
|
mark_environments((CELL_PTR) gc_B->cp_env,
|
|
EnvSizeInCells,
|
|
NULL PASS_REGS);
|
|
else if (opnum != _trust_fail) {
|
|
Int mark = TRUE;
|
|
#ifdef DETERMINISTIC_TABLING
|
|
mark &= !IS_DET_GEN_CP(gc_B);
|
|
#endif /* DETERMINISTIC_TABLING */
|
|
if (mark)
|
|
mark_environments((CELL_PTR) gc_B->cp_env,
|
|
EnvSize((yamop *) (gc_B->cp_cp)),
|
|
EnvBMap((yamop *) (gc_B->cp_cp)) PASS_REGS);
|
|
}
|
|
/* extended choice point */
|
|
restart_cp:
|
|
switch (opnum) {
|
|
case _Nstop:
|
|
if (gc_B->cp_b != NULL) {
|
|
nargs = 0;
|
|
break;
|
|
} else {
|
|
/* this is the last choice point, the work is done ;-) */
|
|
return;
|
|
}
|
|
case _retry_c:
|
|
case _retry_userc:
|
|
if (gc_B->cp_ap == RETRY_C_RECORDED_K_CODE
|
|
|| gc_B->cp_ap == RETRY_C_RECORDEDP_CODE) {
|
|
/* we have a reference from the choice-point stack to a term */
|
|
choiceptr old_b = B;
|
|
DBRef ref;
|
|
B = gc_B;
|
|
ref = (DBRef)EXTRA_CBACK_ARG(3,1);
|
|
if (IsVarTerm((CELL)ref)) {
|
|
mark_ref_in_use(ref PASS_REGS);
|
|
} else {
|
|
if (ONCODE((CELL)ref)) {
|
|
mark_db_fixed(RepAppl((CELL)ref) PASS_REGS);
|
|
}
|
|
}
|
|
B = old_b;
|
|
}
|
|
nargs = rtp->u.OtapFs.s+rtp->u.OtapFs.extra;
|
|
break;
|
|
case _jump:
|
|
rtp = rtp->u.l.l;
|
|
op = rtp->opc;
|
|
opnum = Yap_op_from_opcode(op);
|
|
goto restart_cp;
|
|
case _retry_profiled:
|
|
case _count_retry:
|
|
rtp = NEXTOP(rtp,l);
|
|
op = rtp->opc;
|
|
opnum = Yap_op_from_opcode(op);
|
|
goto restart_cp;
|
|
case _trust_fail:
|
|
nargs = 0;
|
|
break;
|
|
#ifdef TABLING
|
|
case _table_load_answer:
|
|
{
|
|
CELL *vars_ptr, vars;
|
|
vars_ptr = (CELL *) (LOAD_CP(gc_B) + 1);
|
|
vars = *vars_ptr++;
|
|
while (vars--) {
|
|
mark_external_reference(vars_ptr PASS_REGS);
|
|
vars_ptr++;
|
|
}
|
|
}
|
|
nargs = 0;
|
|
break;
|
|
case _table_try_answer:
|
|
case _table_retry_me:
|
|
case _table_trust_me:
|
|
case _table_retry:
|
|
case _table_trust:
|
|
{
|
|
CELL *vars_ptr, vars;
|
|
vars_ptr = (CELL *)(GEN_CP(gc_B) + 1);
|
|
nargs = rtp->u.Otapl.s;
|
|
while (nargs--) {
|
|
mark_external_reference(vars_ptr PASS_REGS);
|
|
vars_ptr++;
|
|
}
|
|
vars = *vars_ptr++;
|
|
while (vars--) {
|
|
mark_external_reference(vars_ptr PASS_REGS);
|
|
vars_ptr++;
|
|
}
|
|
}
|
|
nargs = 0;
|
|
break;
|
|
case _table_completion:
|
|
#ifdef THREADS_CONSUMER_SHARING
|
|
case _table_answer_resolution_completion:
|
|
#endif /* THREADS_CONSUMER_SHARING */
|
|
{
|
|
CELL *vars_ptr, vars;
|
|
#ifdef DETERMINISTIC_TABLING
|
|
if (IS_DET_GEN_CP(gc_B))
|
|
vars_ptr = (CELL *)(DET_GEN_CP(gc_B) + 1);
|
|
else
|
|
#endif /* DETERMINISTIC_TABLING */
|
|
{
|
|
vars_ptr = (CELL *)(GEN_CP(gc_B) + 1);
|
|
nargs = SgFr_arity(GEN_CP(gc_B)->cp_sg_fr);
|
|
while (nargs--) {
|
|
mark_external_reference(vars_ptr PASS_REGS);
|
|
vars_ptr++;
|
|
}
|
|
}
|
|
vars = *vars_ptr++;
|
|
while (vars--) {
|
|
mark_external_reference(vars_ptr PASS_REGS);
|
|
vars_ptr++;
|
|
}
|
|
}
|
|
nargs = 0;
|
|
break;
|
|
case _table_answer_resolution:
|
|
{
|
|
CELL *vars_ptr, vars;
|
|
dep_fr_ptr dep_fr = CONS_CP(gc_B)->cp_dep_fr;
|
|
ans_node_ptr ans_node = DepFr_last_answer(dep_fr);
|
|
if (TRUE || TrNode_child(ans_node)) {
|
|
/* unconsumed answers */
|
|
#ifdef MODE_DIRECTED_TABLING
|
|
if (TrNode_child(ans_node) && IS_ANSWER_INVALID_NODE(TrNode_child(ans_node))) {
|
|
ans_node_ptr old_ans_node;
|
|
old_ans_node = ans_node;
|
|
ans_node = TrNode_child(ans_node);
|
|
do {
|
|
ans_node = TrNode_child(ans_node);
|
|
} while (IS_ANSWER_INVALID_NODE(ans_node));
|
|
TrNode_child(old_ans_node) = ans_node;
|
|
} else
|
|
#endif /* MODE_DIRECTED_TABLING */
|
|
ans_node = TrNode_child(ans_node);
|
|
if (gc_B == DepFr_leader_cp(dep_fr)) {
|
|
/* gc_B is a generator-consumer node */
|
|
/* never here if batched scheduling */
|
|
TABLING_ERROR_CHECKING(generator_consumer, IS_BATCHED_GEN_CP(gc_B));
|
|
vars_ptr = (CELL *) (GEN_CP(gc_B) + 1);
|
|
vars_ptr += SgFr_arity(GEN_CP(gc_B)->cp_sg_fr);
|
|
} else {
|
|
vars_ptr = (CELL *) (CONS_CP(gc_B) + 1);
|
|
}
|
|
|
|
vars = *vars_ptr++;
|
|
while (vars--) {
|
|
mark_external_reference(vars_ptr PASS_REGS);
|
|
vars_ptr++;
|
|
}
|
|
}
|
|
}
|
|
nargs = 0;
|
|
break;
|
|
case _trie_trust_var:
|
|
case _trie_retry_var:
|
|
case _trie_trust_var_in_pair:
|
|
case _trie_retry_var_in_pair:
|
|
case _trie_trust_val:
|
|
case _trie_retry_val:
|
|
case _trie_trust_val_in_pair:
|
|
case _trie_retry_val_in_pair:
|
|
case _trie_trust_atom:
|
|
case _trie_retry_atom:
|
|
case _trie_trust_atom_in_pair:
|
|
case _trie_retry_atom_in_pair:
|
|
case _trie_trust_null:
|
|
case _trie_retry_null:
|
|
case _trie_trust_null_in_pair:
|
|
case _trie_retry_null_in_pair:
|
|
case _trie_trust_pair:
|
|
case _trie_retry_pair:
|
|
case _trie_trust_appl:
|
|
case _trie_retry_appl:
|
|
case _trie_trust_appl_in_pair:
|
|
case _trie_retry_appl_in_pair:
|
|
case _trie_trust_extension:
|
|
case _trie_retry_extension:
|
|
case _trie_trust_double:
|
|
case _trie_retry_double:
|
|
case _trie_trust_longint:
|
|
case _trie_retry_longint:
|
|
case _trie_trust_gterm:
|
|
case _trie_retry_gterm:
|
|
{
|
|
CELL *vars_ptr;
|
|
int heap_arity, vars_arity, subs_arity;
|
|
vars_ptr = (CELL *)(gc_B + 1);
|
|
heap_arity = vars_ptr[0];
|
|
vars_arity = vars_ptr[1 + heap_arity];
|
|
subs_arity = vars_ptr[2 + heap_arity + vars_arity];
|
|
vars_ptr += 2 + heap_arity + subs_arity + vars_arity;
|
|
if (subs_arity) {
|
|
while (subs_arity--) {
|
|
mark_external_reference(vars_ptr PASS_REGS);
|
|
vars_ptr--;
|
|
}
|
|
}
|
|
vars_ptr--; /* skip subs_arity entry */
|
|
if (vars_arity) {
|
|
while (vars_arity--) {
|
|
mark_external_reference(vars_ptr PASS_REGS);
|
|
vars_ptr--;
|
|
}
|
|
}
|
|
vars_ptr--; /* skip vars_arity entry */
|
|
if (heap_arity) {
|
|
while (heap_arity--) {
|
|
if (*vars_ptr == 0) /* double/longint extension mark */
|
|
break;
|
|
mark_external_reference(vars_ptr PASS_REGS);
|
|
vars_ptr--;
|
|
}
|
|
}
|
|
}
|
|
nargs = 0;
|
|
break;
|
|
#endif /* TABLING */
|
|
case _profiled_retry_and_mark:
|
|
case _count_retry_and_mark:
|
|
case _retry_and_mark:
|
|
mark_ref_in_use((DBRef)ClauseCodeToDynamicClause(gc_B->cp_ap) PASS_REGS);
|
|
case _retry2:
|
|
nargs = 2;
|
|
break;
|
|
case _retry3:
|
|
nargs = 3;
|
|
break;
|
|
case _retry4:
|
|
nargs = 4;
|
|
break;
|
|
case _try_logical:
|
|
case _retry_logical:
|
|
{
|
|
/* find out who owns this sequence of try-retry-trust */
|
|
/* I don't like this code, it's a bad idea to do a linear scan,
|
|
on the other hand it's the only way we can be sure we can reclaim
|
|
space
|
|
*/
|
|
yamop *end = rtp->u.OtaLl.n;
|
|
while (end->opc != trust_lu &&
|
|
end->opc != count_trust_lu &&
|
|
end->opc != profiled_trust_lu )
|
|
end = end->u.OtaLl.n;
|
|
mark_ref_in_use((DBRef)end->u.OtILl.block PASS_REGS);
|
|
}
|
|
/* mark timestamp */
|
|
nargs = rtp->u.OtaLl.s+1;
|
|
break;
|
|
case _count_retry_logical:
|
|
{
|
|
/* find out who owns this sequence of try-retry-trust */
|
|
/* I don't like this code, it's a bad idea to do a linear scan,
|
|
on the other hand it's the only way we can be sure we can reclaim
|
|
space
|
|
*/
|
|
yamop *end = rtp->u.OtaLl.n;
|
|
while (Yap_op_from_opcode(end->opc) != _count_trust_logical)
|
|
end = end->u.OtaLl.n;
|
|
mark_ref_in_use((DBRef)end->u.OtILl.block PASS_REGS);
|
|
}
|
|
/* mark timestamp */
|
|
nargs = rtp->u.OtaLl.s+1;
|
|
break;
|
|
case _profiled_retry_logical:
|
|
{
|
|
/* find out who owns this sequence of try-retry-trust */
|
|
/* I don't like this code, it's a bad idea to do a linear scan,
|
|
on the other hand it's the only way we can be sure we can reclaim
|
|
space
|
|
*/
|
|
yamop *end = rtp->u.OtaLl.n;
|
|
while (Yap_op_from_opcode(end->opc) != _profiled_trust_logical)
|
|
end = end->u.OtaLl.n;
|
|
mark_ref_in_use((DBRef)end->u.OtILl.block PASS_REGS);
|
|
}
|
|
/* mark timestamp */
|
|
nargs = rtp->u.OtaLl.s+1;
|
|
break;
|
|
case _trust_logical:
|
|
case _count_trust_logical:
|
|
case _profiled_trust_logical:
|
|
/* mark timestamp */
|
|
mark_ref_in_use((DBRef)rtp->u.OtILl.block PASS_REGS);
|
|
nargs = rtp->u.OtILl.d->ClPred->ArityOfPE+1;
|
|
break;
|
|
case _retry_exo:
|
|
case _retry_exo_udi:
|
|
case _retry_all_exo:
|
|
nargs = rtp->u.lp.p->ArityOfPE;
|
|
break;
|
|
case _retry_udi:
|
|
nargs = rtp->u.p.p->ArityOfPE;
|
|
break;
|
|
#ifdef DEBUG
|
|
case _retry_me:
|
|
case _trust_me:
|
|
case _profiled_retry_me:
|
|
case _profiled_trust_me:
|
|
case _count_retry_me:
|
|
case _count_trust_me:
|
|
case _retry:
|
|
case _trust:
|
|
if (IN_BETWEEN(H0,(CELL *)(gc_B->cp_ap),HR)) {
|
|
fprintf(stderr,"OOPS in GC: gc not supported in this case!!!\n");
|
|
exit(1);
|
|
}
|
|
nargs = rtp->u.Otapl.s;
|
|
break;
|
|
default:
|
|
fprintf(GLOBAL_stderr, "OOPS in GC: Unexpected opcode: %d\n", opnum);
|
|
nargs = 0;
|
|
#else
|
|
default:
|
|
nargs = rtp->u.Otapl.s;
|
|
#endif
|
|
}
|
|
|
|
|
|
if (gc_B->cp_ap == lu_cl0 ||
|
|
gc_B->cp_ap == lu_cl ||
|
|
gc_B->cp_ap == lu_cle ||
|
|
gc_B->cp_ap == su_cl) {
|
|
yamop *pt = (yamop *)IntegerOfTerm(gc_B->cp_args[1]);
|
|
if (gc_B->cp_ap == su_cl) {
|
|
mark_db_fixed((CELL *)pt PASS_REGS);
|
|
} else {
|
|
while (pt->opc != trust_lu &&
|
|
pt->opc != count_trust_lu &&
|
|
pt->opc != profiled_trust_lu
|
|
)
|
|
pt = pt->u.OtaLl.n;
|
|
mark_ref_in_use((DBRef)pt->u.OtILl.block PASS_REGS);
|
|
}
|
|
}
|
|
/* for each saved register */
|
|
for (saved_reg = &gc_B->cp_a1;
|
|
/* assumes we can count registers in CP this
|
|
way */
|
|
saved_reg < &gc_B->cp_a1 + nargs;
|
|
saved_reg++) {
|
|
mark_external_reference(saved_reg PASS_REGS);
|
|
}
|
|
}
|
|
#if TABLING
|
|
gc_B = youngest_cp(gc_B->cp_b, &depfr);
|
|
#else
|
|
gc_B = gc_B->cp_b;
|
|
#endif /* TABLING */
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* insert a cell which points to a heap object into relocation chain of that
|
|
* object
|
|
*/
|
|
|
|
static inline void
|
|
into_relocation_chain(CELL_PTR current, CELL_PTR next USES_REGS)
|
|
{
|
|
CELL current_tag;
|
|
|
|
current_tag = TAG(*current);
|
|
if (RMARKED(next))
|
|
RMARK(current);
|
|
else {
|
|
UNRMARK(current);
|
|
RMARK(next);
|
|
}
|
|
*current = *next;
|
|
*next = (CELL) current | current_tag;
|
|
}
|
|
|
|
|
|
static void
|
|
CleanDeadClauses( USES_REGS1 )
|
|
{
|
|
{
|
|
StaticClause **cptr;
|
|
StaticClause *cl;
|
|
|
|
cptr = &(DeadStaticClauses);
|
|
cl = DeadStaticClauses;
|
|
while (cl) {
|
|
if (!ref_in_use((DBRef)cl PASS_REGS)) {
|
|
char *ocl = (char *)cl;
|
|
Yap_ClauseSpace -= cl->ClSize;
|
|
cl = cl->ClNext;
|
|
*cptr = cl;
|
|
Yap_FreeCodeSpace(ocl);
|
|
} else {
|
|
cptr = &(cl->ClNext);
|
|
cl = cl->ClNext;
|
|
}
|
|
}
|
|
}
|
|
{
|
|
StaticIndex **cptr;
|
|
StaticIndex *cl;
|
|
|
|
cptr = &(DeadStaticIndices);
|
|
cl = DeadStaticIndices;
|
|
while (cl) {
|
|
if (!ref_in_use((DBRef)cl PASS_REGS)) {
|
|
char *ocl = (char *)cl;
|
|
if (cl->ClFlags & SwitchTableMask)
|
|
Yap_IndexSpace_SW -= cl->ClSize;
|
|
else
|
|
Yap_IndexSpace_Tree -= cl->ClSize;
|
|
cl = cl->SiblingIndex;
|
|
*cptr = cl;
|
|
Yap_FreeCodeSpace(ocl);
|
|
} else {
|
|
cptr = &(cl->SiblingIndex);
|
|
cl = cl->SiblingIndex;
|
|
}
|
|
}
|
|
}
|
|
{
|
|
MegaClause **cptr;
|
|
MegaClause *cl;
|
|
|
|
cptr = &(DeadMegaClauses);
|
|
cl = DeadMegaClauses;
|
|
while (cl) {
|
|
if (!ref_in_use((DBRef)cl PASS_REGS)) {
|
|
char *ocl = (char *)cl;
|
|
Yap_ClauseSpace -= cl->ClSize;
|
|
cl = cl->ClNext;
|
|
*cptr = cl;
|
|
Yap_FreeCodeSpace(ocl);
|
|
} else {
|
|
cptr = &(cl->ClNext);
|
|
cl = cl->ClNext;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* insert trail cells which point to heap objects into relocation chains */
|
|
|
|
static void
|
|
sweep_trail(choiceptr gc_B, tr_fr_ptr old_TR USES_REGS)
|
|
{
|
|
tr_fr_ptr trail_ptr, dest;
|
|
Int OldHeapUsed = HeapUsed;
|
|
#ifdef DEBUG
|
|
Int hp_entrs = 0, hp_erased = 0, hp_not_in_use = 0,
|
|
hp_in_use_erased = 0, code_entries = 0;
|
|
#endif
|
|
CELL *ptr = LOCAL_extra_gc_cells;
|
|
|
|
while (ptr > LOCAL_extra_gc_cells_base) {
|
|
Int k = ptr[-1], i;
|
|
ptr = ptr-1;
|
|
|
|
for (i = 0; i < k; i++) {
|
|
ptr--;
|
|
if (IN_BETWEEN(LOCAL_GlobalBase,ptr[0],LOCAL_TrailTop) &&
|
|
MARKED_PTR(ptr)) {
|
|
UNMARK(ptr);
|
|
if (HEAP_PTR(ptr[0])) {
|
|
into_relocation_chain(ptr, GET_NEXT(ptr[0]) PASS_REGS);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef FROZEN_STACKS
|
|
{
|
|
choiceptr current = gc_B;
|
|
choiceptr next = gc_B->cp_b;
|
|
tr_fr_ptr source, dest;
|
|
|
|
/* invert cp ptrs */
|
|
current->cp_b = NULL;
|
|
while (next) {
|
|
choiceptr n = next;
|
|
next = n->cp_b;
|
|
n->cp_b = current;
|
|
current = n;
|
|
}
|
|
|
|
next = current;
|
|
current = NULL;
|
|
/* next, clean trail */
|
|
source = dest = (tr_fr_ptr)LOCAL_TrailBase;
|
|
while (source < old_TR) {
|
|
CELL trail_cell;
|
|
|
|
while (next && source == next->cp_tr) {
|
|
choiceptr b = next;
|
|
b->cp_tr = dest;
|
|
next = b->cp_b;
|
|
b->cp_b = current;
|
|
current = b;
|
|
}
|
|
trail_cell = TrailTerm(source);
|
|
if (trail_cell != (CELL)source) {
|
|
dest++;
|
|
}
|
|
source++;
|
|
}
|
|
while (next) {
|
|
choiceptr b = next;
|
|
b->cp_tr = dest;
|
|
next = b->cp_b;
|
|
b->cp_b = current;
|
|
current = b;
|
|
}
|
|
}
|
|
#endif /* FROZEN_STACKS */
|
|
|
|
/* first, whatever we dumped on the trail. Easier just to do
|
|
the registers separately? */
|
|
for (trail_ptr = old_TR; trail_ptr < TR; trail_ptr++) {
|
|
if (IN_BETWEEN(LOCAL_GlobalBase,TrailTerm(trail_ptr),LOCAL_TrailTop) &&
|
|
MARKED_PTR(&TrailTerm(trail_ptr))) {
|
|
UNMARK(&TrailTerm(trail_ptr));
|
|
if (HEAP_PTR(TrailTerm(trail_ptr))) {
|
|
into_relocation_chain(&TrailTerm(trail_ptr), GET_NEXT(TrailTerm(trail_ptr)) PASS_REGS);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* next, follows the real trail entries */
|
|
trail_ptr = (tr_fr_ptr)LOCAL_TrailBase;
|
|
dest = trail_ptr;
|
|
while (trail_ptr < old_TR) {
|
|
register CELL trail_cell;
|
|
|
|
trail_cell = TrailTerm(trail_ptr);
|
|
|
|
#ifndef FROZEN_STACKS
|
|
/* recover a trail cell */
|
|
if (trail_cell == (CELL)trail_ptr) {
|
|
TrailTerm(dest) = trail_cell;
|
|
trail_ptr++;
|
|
/* just skip cell */
|
|
} else
|
|
#endif
|
|
{
|
|
TrailTerm(dest) = trail_cell;
|
|
if (IsVarTerm(trail_cell)) {
|
|
/* we need to check whether this is a honest to god trail entry */
|
|
/* make sure it is a heap cell before we test whether it has been marked */
|
|
if ((CELL *)trail_cell < HR && (CELL *)trail_cell >= H0 && MARKED_PTR((CELL *)trail_cell)) {
|
|
if (HEAP_PTR(trail_cell)) {
|
|
into_relocation_chain(&TrailTerm(dest), GET_NEXT(trail_cell) PASS_REGS);
|
|
}
|
|
}
|
|
#ifdef FROZEN_STACKS
|
|
/* it is complex to recover cells with frozen segments */
|
|
TrailVal(dest) = TrailVal(trail_ptr);
|
|
if (MARKED_PTR(&TrailVal(dest))) {
|
|
if (HEAP_PTR(TrailVal(dest))) {
|
|
into_relocation_chain(&TrailVal(dest), GET_NEXT(TrailVal(dest)) PASS_REGS);
|
|
}
|
|
}
|
|
#endif
|
|
} else if (IsPairTerm(trail_cell)) {
|
|
CELL *pt0 = RepPair(trail_cell);
|
|
CELL flags;
|
|
|
|
if (IN_BETWEEN(LOCAL_GlobalBase, pt0, HR)) {
|
|
if (GlobalIsAttVar(pt0)) {
|
|
TrailTerm(dest) = trail_cell;
|
|
/* be careful with partial gc */
|
|
if (HEAP_PTR(TrailTerm(dest))) {
|
|
into_relocation_chain(&TrailTerm(dest), GET_NEXT(trail_cell) PASS_REGS);
|
|
}
|
|
} else if (*pt0 == (CELL)FunctorBigInt) {
|
|
TrailTerm(dest) = trail_cell;
|
|
/* be careful with partial gc */
|
|
if (HEAP_PTR(TrailTerm(dest))) {
|
|
into_relocation_chain(&TrailTerm(dest), GET_NEXT(trail_cell) PASS_REGS);
|
|
}
|
|
}
|
|
dest++;
|
|
trail_ptr++;
|
|
continue;
|
|
}
|
|
#ifdef FROZEN_STACKS /* TRAIL */
|
|
/* process all segments */
|
|
if (
|
|
#ifdef YAPOR_SBA
|
|
(ADDR) pt0 >= LOCAL_GlobalBase
|
|
#else
|
|
(ADDR) pt0 >= LOCAL_TrailBase
|
|
#endif
|
|
) {
|
|
trail_ptr++;
|
|
dest++;
|
|
continue;
|
|
}
|
|
#endif /* FROZEN_STACKS */
|
|
flags = *pt0;
|
|
#ifdef DEBUG
|
|
hp_entrs++;
|
|
if (!ref_in_use((DBRef)pt0 PASS_REGS)) {
|
|
hp_not_in_use++;
|
|
if (!FlagOn(DBClMask, flags)) {
|
|
code_entries++;
|
|
}
|
|
if (FlagOn(ErasedMask, flags)) {
|
|
hp_erased++;
|
|
}
|
|
} else {
|
|
if (FlagOn(ErasedMask, flags)) {
|
|
hp_in_use_erased++;
|
|
}
|
|
}
|
|
#endif
|
|
if (!ref_in_use((DBRef)pt0 PASS_REGS)) {
|
|
if (FlagOn(DBClMask, flags)) {
|
|
DBRef dbr = (DBRef) ((CELL)pt0 - (CELL) &(((DBRef) NIL)->Flags));
|
|
dbr->Flags &= ~InUseMask;
|
|
DEC_DBREF_COUNT(dbr);
|
|
if (dbr->Flags & ErasedMask) {
|
|
Yap_ErDBE(dbr);
|
|
}
|
|
} else {
|
|
if (flags & LogUpdMask) {
|
|
if (flags & IndexMask) {
|
|
LogUpdIndex *indx = ClauseFlagsToLogUpdIndex(pt0);
|
|
int erase;
|
|
#if defined(YAPOR) || defined(THREADS)
|
|
/*
|
|
gc may be called when executing a dynamic goal,
|
|
check PP to avoid deadlock
|
|
*/
|
|
PredEntry *ap = indx->ClPred;
|
|
if (ap != PP)
|
|
PELOCK(85,ap);
|
|
#endif
|
|
DEC_CLREF_COUNT(indx);
|
|
indx->ClFlags &= ~InUseMask;
|
|
erase = (indx->ClFlags & ErasedMask
|
|
&& !indx->ClRefCount);
|
|
if (erase) {
|
|
/* at this point,
|
|
no one is accessing the clause */
|
|
Yap_ErLogUpdIndex(indx);
|
|
}
|
|
#if defined(YAPOR) || defined(THREADS)
|
|
if (ap != PP)
|
|
UNLOCK(ap->PELock);
|
|
#endif
|
|
} else {
|
|
LogUpdClause *cl = ClauseFlagsToLogUpdClause(pt0);
|
|
#if defined(YAPOR) || defined(THREADS)
|
|
PredEntry *ap = cl->ClPred;
|
|
#endif
|
|
int erase;
|
|
|
|
#if defined(YAPOR) || defined(THREADS)
|
|
if (ap != PP)
|
|
PELOCK(86,ap);
|
|
#endif
|
|
DEC_CLREF_COUNT(cl);
|
|
cl->ClFlags &= ~InUseMask;
|
|
erase = ((cl->ClFlags & ErasedMask) && !cl->ClRefCount);
|
|
if (erase) {
|
|
/* at this point,
|
|
no one is accessing the clause */
|
|
Yap_ErLogUpdCl(cl);
|
|
}
|
|
#if defined(YAPOR) || defined(THREADS)
|
|
if (ap != PP)
|
|
UNLOCK(ap->PELock);
|
|
#endif
|
|
}
|
|
} else {
|
|
DynamicClause *cl = ClauseFlagsToDynamicClause(pt0);
|
|
int erase;
|
|
DEC_CLREF_COUNT(cl);
|
|
cl->ClFlags &= ~InUseMask;
|
|
erase = (cl->ClFlags & ErasedMask)
|
|
#if defined(YAPOR) || defined(THREADS)
|
|
&& (cl->ClRefCount == 0)
|
|
#endif
|
|
;
|
|
if (erase) {
|
|
/* at this point,
|
|
no one is accessing the clause */
|
|
Yap_ErCl(cl);
|
|
}
|
|
}
|
|
}
|
|
RESET_VARIABLE(&TrailTerm(dest));
|
|
#ifdef FROZEN_STACKS
|
|
RESET_VARIABLE(&TrailVal(dest));
|
|
#endif
|
|
LOCAL_discard_trail_entries++;
|
|
}
|
|
#if MULTI_ASSIGNMENT_VARIABLES
|
|
} else {
|
|
#ifdef FROZEN_STACKS
|
|
CELL trail_cell = TrailTerm(trail_ptr+1);
|
|
CELL old = TrailVal(trail_ptr);
|
|
CELL old1 = TrailVal(trail_ptr+1);
|
|
Int marked_ptr = MARKED_PTR(&TrailTerm(trail_ptr+1));
|
|
Int marked_val_old = MARKED_PTR(&TrailVal(trail_ptr));
|
|
Int marked_val_ptr = MARKED_PTR(&TrailVal(trail_ptr+1));
|
|
|
|
TrailTerm(dest+1) = TrailTerm(dest) = trail_cell;
|
|
TrailVal(dest) = old;
|
|
TrailVal(dest+1) = old1;
|
|
if (marked_ptr) {
|
|
UNMARK(&TrailTerm(dest));
|
|
UNMARK(&TrailTerm(dest+1));
|
|
if (HEAP_PTR(trail_cell)) {
|
|
into_relocation_chain(&TrailTerm(dest), GET_NEXT(trail_cell) PASS_REGS);
|
|
into_relocation_chain(&TrailTerm(dest+1), GET_NEXT(trail_cell) PASS_REGS);
|
|
}
|
|
}
|
|
if (marked_val_old) {
|
|
UNMARK(&TrailVal(dest));
|
|
if (HEAP_PTR(old)) {
|
|
into_relocation_chain(&TrailVal(dest), GET_NEXT(old) PASS_REGS);
|
|
}
|
|
}
|
|
if (marked_val_ptr) {
|
|
UNMARK(&TrailVal(dest+1));
|
|
if (HEAP_PTR(old1)) {
|
|
into_relocation_chain(&TrailVal(dest+1), GET_NEXT(old1) PASS_REGS);
|
|
}
|
|
}
|
|
trail_ptr ++;
|
|
dest ++;
|
|
#else
|
|
CELL trail_cell = TrailTerm(trail_ptr+2);
|
|
CELL old = TrailTerm(trail_ptr+1);
|
|
Int marked_ptr = MARKED_PTR(&TrailTerm(trail_ptr+2));
|
|
Int marked_old = MARKED_PTR(&TrailTerm(trail_ptr+1));
|
|
CELL *ptr;
|
|
/* be sure we don't overwrite before we read */
|
|
|
|
if (marked_ptr)
|
|
ptr = RepAppl(UNMARK_CELL(trail_cell));
|
|
else
|
|
ptr = RepAppl(trail_cell);
|
|
|
|
TrailTerm(dest+1) = old;
|
|
if (marked_old) {
|
|
UNMARK(&TrailTerm(dest+1));
|
|
if (HEAP_PTR(old)) {
|
|
into_relocation_chain(&TrailTerm(dest+1), GET_NEXT(old) PASS_REGS);
|
|
}
|
|
}
|
|
TrailTerm(dest+2) = TrailTerm(dest) = trail_cell;
|
|
if (marked_ptr) {
|
|
UNMARK(&TrailTerm(dest));
|
|
UNMARK(&TrailTerm(dest+2));
|
|
if (HEAP_PTR(trail_cell)) {
|
|
into_relocation_chain(&TrailTerm(dest), GET_NEXT(trail_cell) PASS_REGS);
|
|
into_relocation_chain(&TrailTerm(dest+2), GET_NEXT(trail_cell) PASS_REGS);
|
|
}
|
|
}
|
|
trail_ptr += 2;
|
|
dest += 2;
|
|
#endif
|
|
#endif
|
|
}
|
|
trail_ptr++;
|
|
dest++;
|
|
}
|
|
}
|
|
LOCAL_new_TR = dest;
|
|
if (is_gc_verbose()) {
|
|
if (old_TR != (tr_fr_ptr)LOCAL_TrailBase)
|
|
fprintf(GLOBAL_stderr,
|
|
"%% Trail: discarded %d (%ld%%) cells out of %ld\n",
|
|
LOCAL_discard_trail_entries,
|
|
(unsigned long int)(LOCAL_discard_trail_entries*100/(old_TR-(tr_fr_ptr)LOCAL_TrailBase)),
|
|
(unsigned long int)(old_TR-(tr_fr_ptr)LOCAL_TrailBase));
|
|
#ifdef DEBUG
|
|
if (hp_entrs > 0)
|
|
fprintf(GLOBAL_stderr,
|
|
"%% Trail: unmarked %ld dbentries (%ld%%) out of %ld\n",
|
|
(long int)hp_not_in_use,
|
|
(long int)(hp_not_in_use*100/hp_entrs),
|
|
(long int)hp_entrs);
|
|
if (hp_in_use_erased > 0 && hp_erased > 0)
|
|
fprintf(GLOBAL_stderr,
|
|
"%% Trail: deleted %ld dbentries (%ld%%) out of %ld\n",
|
|
(long int)hp_erased,
|
|
(long int)(hp_erased*100/(hp_erased+hp_in_use_erased)),
|
|
(long int)(hp_erased+hp_in_use_erased));
|
|
#endif
|
|
if (OldHeapUsed) {
|
|
fprintf(GLOBAL_stderr,
|
|
"%% Heap: recovered %ld bytes (%ld%%) out of %ld\n",
|
|
(unsigned long int)(OldHeapUsed-HeapUsed),
|
|
(unsigned long int)((OldHeapUsed-HeapUsed)/(OldHeapUsed/100)),
|
|
(unsigned long int)OldHeapUsed);
|
|
}
|
|
}
|
|
CleanDeadClauses( PASS_REGS1 );
|
|
}
|
|
|
|
|
|
/*
|
|
* insert cells of a chain of environments which point to heap objects into
|
|
* relocation chains
|
|
*/
|
|
|
|
static void
|
|
sweep_environments(CELL_PTR gc_ENV, OPREG size, CELL *pvbmap USES_REGS)
|
|
{
|
|
CELL_PTR saved_var;
|
|
|
|
while (gc_ENV != NULL) { /* no more environments */
|
|
Int bmap = 0;
|
|
int currv = 0;
|
|
|
|
// printf("SWEEP %p--%p\n", gc_ENV, gc_ENV-size);
|
|
|
|
/* for each saved variable */
|
|
|
|
if (size > EnvSizeInCells) {
|
|
int tsize = size - EnvSizeInCells;
|
|
|
|
|
|
currv = sizeof(CELL)*8-tsize%(sizeof(CELL)*8);
|
|
if (pvbmap != NULL) {
|
|
pvbmap += tsize/(sizeof(CELL)*8);
|
|
bmap = *pvbmap;
|
|
} else {
|
|
bmap = ((CELL)-1);
|
|
}
|
|
bmap = (Int)(((CELL)bmap) << currv);
|
|
}
|
|
for (saved_var = gc_ENV - size; saved_var < gc_ENV - EnvSizeInCells; saved_var++) {
|
|
if (currv == sizeof(CELL)*8) {
|
|
if (pvbmap != NULL) {
|
|
pvbmap--;
|
|
bmap = *pvbmap;
|
|
} else {
|
|
bmap = ((CELL)-1);
|
|
}
|
|
currv = 0;
|
|
}
|
|
if (bmap < 0) {
|
|
CELL env_cell = *saved_var;
|
|
if (MARKED_PTR(saved_var)) {
|
|
UNMARK(saved_var);
|
|
if (HEAP_PTR(env_cell)) {
|
|
into_relocation_chain(saved_var, GET_NEXT(env_cell) PASS_REGS);
|
|
}
|
|
}
|
|
}
|
|
bmap <<= 1;
|
|
currv++;
|
|
}
|
|
/* have we met this environment before?? */
|
|
/* we use the B field in the environment to tell whether we have
|
|
been here before or not
|
|
*/
|
|
if (!MARKED_PTR(gc_ENV+E_CB))
|
|
return;
|
|
UNMARK(gc_ENV+E_CB);
|
|
|
|
size = EnvSize((yamop *) (gc_ENV[E_CP])); /* size = EnvSize(CP) */
|
|
pvbmap = EnvBMap((yamop *) (gc_ENV[E_CP]));
|
|
gc_ENV = (CELL_PTR) gc_ENV[E_E]; /* link to prev
|
|
* environment */
|
|
}
|
|
}
|
|
|
|
static void
|
|
sweep_slots( USES_REGS1 )
|
|
{
|
|
Int curslot = LOCAL_CurSlot;
|
|
while (curslot) {
|
|
CELL *ptr = LCL0-curslot;
|
|
Int ns = IntOfTerm(ptr[-1]);
|
|
curslot = IntegerOfTerm(ptr[0]);
|
|
ptr-=2;
|
|
while (ns > 0) {
|
|
CELL cp_cell = *ptr;
|
|
if (MARKED_PTR(ptr)) {
|
|
UNMARK(ptr);
|
|
if (HEAP_PTR(cp_cell)) {
|
|
into_relocation_chain(ptr, GET_NEXT(cp_cell) PASS_REGS);
|
|
}
|
|
}
|
|
ptr--;
|
|
ns--;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
sweep_b(choiceptr gc_B, UInt arity USES_REGS)
|
|
{
|
|
register CELL_PTR saved_reg;
|
|
|
|
sweep_environments(gc_B->cp_env,
|
|
EnvSize((yamop *) (gc_B->cp_cp)),
|
|
EnvBMap((yamop *) (gc_B->cp_cp)) PASS_REGS);
|
|
|
|
/* for each saved register */
|
|
for (saved_reg = &gc_B->cp_a1;
|
|
saved_reg < &gc_B->cp_a1 + arity;
|
|
saved_reg++) {
|
|
CELL cp_cell = *saved_reg;
|
|
if (MARKED_PTR(saved_reg)) {
|
|
UNMARK(saved_reg);
|
|
if (HEAP_PTR(cp_cell)) {
|
|
into_relocation_chain(saved_reg, GET_NEXT(cp_cell) PASS_REGS);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* insert cells of each choicepoint & its chain of environments which point
|
|
* to heap objects into relocation chains
|
|
*/
|
|
static void
|
|
sweep_choicepoints(choiceptr gc_B USES_REGS)
|
|
{
|
|
#ifdef TABLING
|
|
dep_fr_ptr depfr = LOCAL_top_dep_fr;
|
|
sg_fr_ptr aux_sg_fr = LOCAL_top_sg_fr;
|
|
#endif /* TABLING */
|
|
|
|
#ifdef TABLING
|
|
gc_B = youngest_cp(gc_B, &depfr);
|
|
#endif /* TABLING */
|
|
while (gc_B != NULL) {
|
|
yamop *rtp = gc_B->cp_ap;
|
|
register OPCODE op;
|
|
op_numbers opnum;
|
|
|
|
#ifdef TABLING
|
|
if (rtp == NULL) {
|
|
if (aux_sg_fr && gc_B == SgFr_gen_cp(aux_sg_fr)) {
|
|
/* found generator */
|
|
opnum = _table_completion;
|
|
} else {
|
|
/* found sld node is done */
|
|
opnum = _trust_fail;
|
|
}
|
|
} else {
|
|
#endif /* TABLING */
|
|
op = rtp->opc;
|
|
opnum = Yap_op_from_opcode(op);
|
|
#ifdef TABLING
|
|
}
|
|
if (aux_sg_fr && gc_B == SgFr_gen_cp(aux_sg_fr)) {
|
|
aux_sg_fr = SgFr_next(aux_sg_fr);
|
|
}
|
|
#endif /* TABLING */
|
|
|
|
restart_cp:
|
|
/*
|
|
* fprintf(GLOBAL_stderr,"sweeping cps: %x, %x, %x\n",
|
|
* *gc_B,CP_Extra(gc_B),CP_Nargs(gc_B));
|
|
*/
|
|
/* any choice point */
|
|
switch (opnum) {
|
|
case _Nstop:
|
|
/* end of the road, say bye bye! */
|
|
sweep_environments(gc_B->cp_env,
|
|
EnvSizeInCells,
|
|
NULL PASS_REGS);
|
|
if (gc_B->cp_b != NULL) {
|
|
break;
|
|
} else
|
|
return;
|
|
case _trust_fail:
|
|
break;
|
|
case _or_else:
|
|
case _or_last:
|
|
|
|
sweep_environments((CELL_PTR)(gc_B->cp_a1),
|
|
-gc_B->cp_cp->u.Osblp.s / ((OPREG)sizeof(CELL)),
|
|
gc_B->cp_cp->u.Osblp.bmap
|
|
PASS_REGS);
|
|
break;
|
|
case _retry_profiled:
|
|
case _count_retry:
|
|
rtp = NEXTOP(rtp,l);
|
|
op = rtp->opc;
|
|
opnum = Yap_op_from_opcode(op);
|
|
goto restart_cp;
|
|
case _jump:
|
|
rtp = rtp->u.l.l;
|
|
op = rtp->opc;
|
|
opnum = Yap_op_from_opcode(op);
|
|
goto restart_cp;
|
|
#ifdef TABLING
|
|
case _table_load_answer:
|
|
{
|
|
CELL *vars_ptr, vars;
|
|
sweep_environments(gc_B->cp_env, EnvSize(gc_B->cp_cp), EnvBMap(gc_B->cp_cp) PASS_REGS);
|
|
vars_ptr = (CELL *) (LOAD_CP(gc_B) + 1);
|
|
vars = *vars_ptr++;
|
|
while (vars--) {
|
|
CELL cp_cell = *vars_ptr;
|
|
if (MARKED_PTR(vars_ptr)) {
|
|
UNMARK(vars_ptr);
|
|
if (HEAP_PTR(cp_cell)) {
|
|
into_relocation_chain(vars_ptr, GET_NEXT(cp_cell) PASS_REGS);
|
|
}
|
|
}
|
|
vars_ptr++;
|
|
}
|
|
}
|
|
break;
|
|
case _table_try_answer:
|
|
case _table_retry_me:
|
|
case _table_trust_me:
|
|
case _table_retry:
|
|
case _table_trust:
|
|
{
|
|
int nargs;
|
|
CELL *vars_ptr, vars;
|
|
sweep_environments(gc_B->cp_env, EnvSize(gc_B->cp_cp), EnvBMap(gc_B->cp_cp) PASS_REGS);
|
|
vars_ptr = (CELL *)(GEN_CP(gc_B) + 1);
|
|
nargs = rtp->u.Otapl.s;
|
|
while(nargs--) {
|
|
CELL cp_cell = *vars_ptr;
|
|
if (MARKED_PTR(vars_ptr)) {
|
|
UNMARK(vars_ptr);
|
|
if (HEAP_PTR(cp_cell)) {
|
|
into_relocation_chain(vars_ptr, GET_NEXT(cp_cell) PASS_REGS);
|
|
}
|
|
}
|
|
vars_ptr++;
|
|
}
|
|
vars = *vars_ptr++;
|
|
while (vars--) {
|
|
CELL cp_cell = *vars_ptr;
|
|
if (MARKED_PTR(vars_ptr)) {
|
|
UNMARK(vars_ptr);
|
|
if (HEAP_PTR(cp_cell)) {
|
|
into_relocation_chain(vars_ptr, GET_NEXT(cp_cell) PASS_REGS);
|
|
}
|
|
}
|
|
vars_ptr++;
|
|
}
|
|
}
|
|
break;
|
|
case _table_completion:
|
|
#ifdef THREADS_CONSUMER_SHARING
|
|
case _table_answer_resolution_completion:
|
|
#endif /* THREADS_CONSUMER_SHARING */
|
|
{
|
|
int nargs;
|
|
CELL *vars_ptr, vars;
|
|
#ifdef DETERMINISTIC_TABLING
|
|
if (IS_DET_GEN_CP(gc_B))
|
|
vars_ptr = (CELL *)(DET_GEN_CP(gc_B) + 1);
|
|
else
|
|
#endif /* DETERMINISTIC_TABLING */
|
|
{
|
|
sweep_environments(gc_B->cp_env, EnvSize(gc_B->cp_cp), EnvBMap(gc_B->cp_cp) PASS_REGS);
|
|
vars_ptr = (CELL *)(GEN_CP(gc_B) + 1);
|
|
nargs = SgFr_arity(GEN_CP(gc_B)->cp_sg_fr);
|
|
while(nargs--) {
|
|
CELL cp_cell = *vars_ptr;
|
|
if (MARKED_PTR(vars_ptr)) {
|
|
UNMARK(vars_ptr);
|
|
if (HEAP_PTR(cp_cell)) {
|
|
into_relocation_chain(vars_ptr, GET_NEXT(cp_cell) PASS_REGS);
|
|
}
|
|
}
|
|
vars_ptr++;
|
|
}
|
|
}
|
|
vars = *vars_ptr++;
|
|
while (vars--) {
|
|
CELL cp_cell = *vars_ptr;
|
|
if (MARKED_PTR(vars_ptr)) {
|
|
UNMARK(vars_ptr);
|
|
if (HEAP_PTR(cp_cell)) {
|
|
into_relocation_chain(vars_ptr, GET_NEXT(cp_cell) PASS_REGS);
|
|
}
|
|
}
|
|
vars_ptr++;
|
|
}
|
|
}
|
|
break;
|
|
case _table_answer_resolution:
|
|
{
|
|
CELL *vars_ptr, vars;
|
|
dep_fr_ptr dep_fr = CONS_CP(gc_B)->cp_dep_fr;
|
|
ans_node_ptr ans_node = DepFr_last_answer(dep_fr);
|
|
if (TRUE || TrNode_child(ans_node)) {
|
|
/* unconsumed answers */
|
|
#ifdef MODE_DIRECTED_TABLING
|
|
if (TrNode_child(ans_node) && IS_ANSWER_INVALID_NODE(TrNode_child(ans_node))) {
|
|
ans_node_ptr old_ans_node;
|
|
old_ans_node = ans_node;
|
|
ans_node = TrNode_child(ans_node);
|
|
do {
|
|
ans_node = TrNode_child(ans_node);
|
|
} while (IS_ANSWER_INVALID_NODE(ans_node));
|
|
TrNode_child(old_ans_node) = ans_node;
|
|
} else
|
|
#endif /* MODE_DIRECTED_TABLING */
|
|
ans_node = TrNode_child(ans_node);
|
|
if (gc_B == DepFr_leader_cp(dep_fr)) { \
|
|
/* gc_B is a generator-consumer node */
|
|
/* never here if batched scheduling */
|
|
TABLING_ERROR_CHECKING(generator_consumer, IS_BATCHED_GEN_CP(gc_B));
|
|
vars_ptr = (CELL *) (GEN_CP(gc_B) + 1);
|
|
vars_ptr += SgFr_arity(GEN_CP(gc_B)->cp_sg_fr);
|
|
} else {
|
|
vars_ptr = (CELL *) (CONS_CP(gc_B) + 1); \
|
|
}
|
|
sweep_environments(gc_B->cp_env, EnvSize(gc_B->cp_cp), EnvBMap(gc_B->cp_cp) PASS_REGS);
|
|
vars = *vars_ptr++;
|
|
while (vars--) {
|
|
CELL cp_cell = *vars_ptr;
|
|
if (MARKED_PTR(vars_ptr)) {
|
|
UNMARK(vars_ptr);
|
|
if (HEAP_PTR(cp_cell)) {
|
|
into_relocation_chain(vars_ptr, GET_NEXT(cp_cell) PASS_REGS);
|
|
}
|
|
}
|
|
vars_ptr++;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case _trie_trust_var:
|
|
case _trie_retry_var:
|
|
case _trie_trust_var_in_pair:
|
|
case _trie_retry_var_in_pair:
|
|
case _trie_trust_val:
|
|
case _trie_retry_val:
|
|
case _trie_trust_val_in_pair:
|
|
case _trie_retry_val_in_pair:
|
|
case _trie_trust_atom:
|
|
case _trie_retry_atom:
|
|
case _trie_trust_atom_in_pair:
|
|
case _trie_retry_atom_in_pair:
|
|
case _trie_trust_null:
|
|
case _trie_retry_null:
|
|
case _trie_trust_null_in_pair:
|
|
case _trie_retry_null_in_pair:
|
|
case _trie_trust_pair:
|
|
case _trie_retry_pair:
|
|
case _trie_trust_appl:
|
|
case _trie_retry_appl:
|
|
case _trie_trust_appl_in_pair:
|
|
case _trie_retry_appl_in_pair:
|
|
case _trie_trust_extension:
|
|
case _trie_retry_extension:
|
|
case _trie_trust_double:
|
|
case _trie_retry_double:
|
|
case _trie_trust_longint:
|
|
case _trie_retry_longint:
|
|
case _trie_trust_gterm:
|
|
case _trie_retry_gterm:
|
|
{
|
|
CELL *vars_ptr;
|
|
int heap_arity, vars_arity, subs_arity;
|
|
sweep_environments(gc_B->cp_env, EnvSize(gc_B->cp_cp), EnvBMap(gc_B->cp_cp) PASS_REGS);
|
|
vars_ptr = (CELL *)(gc_B + 1);
|
|
heap_arity = vars_ptr[0];
|
|
vars_arity = vars_ptr[1 + heap_arity];
|
|
subs_arity = vars_ptr[2 + heap_arity + vars_arity];
|
|
vars_ptr += 2 + heap_arity + subs_arity + vars_arity;
|
|
if (subs_arity) {
|
|
while (subs_arity--) {
|
|
CELL cp_cell = *vars_ptr;
|
|
if (MARKED_PTR(vars_ptr)) {
|
|
UNMARK(vars_ptr);
|
|
if (HEAP_PTR(cp_cell)) {
|
|
into_relocation_chain(vars_ptr, GET_NEXT(cp_cell) PASS_REGS);
|
|
}
|
|
}
|
|
vars_ptr--;
|
|
}
|
|
}
|
|
vars_ptr--; /* skip subs_arity entry */
|
|
if (vars_arity) {
|
|
while (vars_arity--) {
|
|
CELL cp_cell = *vars_ptr;
|
|
if (MARKED_PTR(vars_ptr)) {
|
|
UNMARK(vars_ptr);
|
|
if (HEAP_PTR(cp_cell)) {
|
|
into_relocation_chain(vars_ptr, GET_NEXT(cp_cell) PASS_REGS);
|
|
}
|
|
}
|
|
vars_ptr--;
|
|
}
|
|
}
|
|
vars_ptr--; /* skip vars_arity entry */
|
|
if (heap_arity) {
|
|
while (heap_arity--) {
|
|
CELL cp_cell = *vars_ptr;
|
|
if (*vars_ptr == 0) /* double/longint extension mark */
|
|
break;
|
|
if (MARKED_PTR(vars_ptr)) {
|
|
UNMARK(vars_ptr);
|
|
if (HEAP_PTR(cp_cell)) {
|
|
into_relocation_chain(vars_ptr, GET_NEXT(cp_cell) PASS_REGS);
|
|
}
|
|
}
|
|
vars_ptr--;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
#endif /* TABLING */
|
|
case _try_logical:
|
|
case _retry_logical:
|
|
case _count_retry_logical:
|
|
case _profiled_retry_logical:
|
|
/* sweep timestamp */
|
|
sweep_b(gc_B, rtp->u.OtaLl.s+1 PASS_REGS);
|
|
break;
|
|
case _trust_logical:
|
|
case _count_trust_logical:
|
|
case _profiled_trust_logical:
|
|
sweep_b(gc_B, rtp->u.OtILl.d->ClPred->ArityOfPE+1 PASS_REGS);
|
|
break;
|
|
case _retry2:
|
|
sweep_b(gc_B, 2 PASS_REGS);
|
|
break;
|
|
case _retry3:
|
|
sweep_b(gc_B, 3 PASS_REGS);
|
|
break;
|
|
case _retry4:
|
|
sweep_b(gc_B, 4 PASS_REGS);
|
|
break;
|
|
case _retry_udi:
|
|
sweep_b(gc_B, rtp->u.p.p->ArityOfPE PASS_REGS);
|
|
break;
|
|
case _retry_exo:
|
|
case _retry_exo_udi:
|
|
case _retry_all_exo:
|
|
sweep_b(gc_B, rtp->u.lp.p->ArityOfPE PASS_REGS);
|
|
break;
|
|
case _retry_c:
|
|
case _retry_userc:
|
|
{
|
|
register CELL_PTR saved_reg;
|
|
|
|
/* for each extra saved register */
|
|
for (saved_reg = &(gc_B->cp_a1)+rtp->u.OtapFs.s;
|
|
saved_reg < &(gc_B->cp_a1)+rtp->u.OtapFs.s+rtp->u.OtapFs.extra;
|
|
saved_reg++) {
|
|
CELL cp_cell = *saved_reg;
|
|
if (MARKED_PTR(saved_reg)) {
|
|
UNMARK(saved_reg);
|
|
if (HEAP_PTR(cp_cell)) {
|
|
into_relocation_chain(saved_reg, GET_NEXT(cp_cell) PASS_REGS);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* continue to clean environments and arguments */
|
|
default:
|
|
sweep_b(gc_B,rtp->u.Otapl.s PASS_REGS);
|
|
}
|
|
|
|
/* link to prev choicepoint */
|
|
#if TABLING
|
|
gc_B = youngest_cp(gc_B->cp_b, &depfr);
|
|
#else
|
|
gc_B = gc_B->cp_b;
|
|
#endif /* TABLING */
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/* update a relocation chain to point all its cells to new location of object */
|
|
static void
|
|
update_relocation_chain(CELL_PTR current, CELL_PTR dest USES_REGS)
|
|
{
|
|
CELL_PTR next;
|
|
CELL ccur = *current;
|
|
|
|
int rmarked = RMARKED(current);
|
|
|
|
UNRMARK(current);
|
|
while (rmarked) {
|
|
CELL current_tag;
|
|
next = GET_NEXT(ccur);
|
|
current_tag = TAG(ccur);
|
|
ccur = *next;
|
|
rmarked = RMARKED(next);
|
|
UNRMARK(next);
|
|
*next = (CELL) dest | current_tag;
|
|
}
|
|
*current = ccur;
|
|
}
|
|
|
|
static inline choiceptr
|
|
update_B_H( choiceptr gc_B, CELL *current, CELL *dest, CELL *odest
|
|
#ifdef TABLING
|
|
, dep_fr_ptr *depfrp
|
|
#endif /* TABLING */
|
|
) {
|
|
/* also make the value of H in a choicepoint
|
|
coherent with the new global
|
|
*/
|
|
#ifdef TABLING
|
|
dep_fr_ptr depfr = *depfrp;
|
|
#endif /* TABLING */
|
|
|
|
while (gc_B && current <= gc_B->cp_h) {
|
|
if (gc_B->cp_h == current) {
|
|
gc_B->cp_h = dest;
|
|
} else {
|
|
gc_B->cp_h = odest;
|
|
}
|
|
gc_B = gc_B->cp_b;
|
|
#ifdef TABLING
|
|
/* make sure we include consumers */
|
|
if (depfr && gc_B >= DepFr_cons_cp(depfr)) {
|
|
gc_B = DepFr_cons_cp(depfr);
|
|
*depfrp = depfr = DepFr_next(depfr);
|
|
}
|
|
#endif /* TABLING */
|
|
}
|
|
return gc_B;
|
|
}
|
|
|
|
static inline CELL *
|
|
set_next_hb(choiceptr gc_B USES_REGS)
|
|
{
|
|
if (gc_B) {
|
|
return gc_B->cp_h;
|
|
} else {
|
|
return H0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* move marked objects on the heap upwards over unmarked objects, and reset
|
|
* all pointers to point to new locations
|
|
*/
|
|
static void
|
|
compact_heap( USES_REGS1 )
|
|
{
|
|
CELL_PTR dest, current, next;
|
|
#ifdef DEBUG
|
|
Int found_marked = 0;
|
|
#endif /* DEBUG */
|
|
choiceptr gc_B = B;
|
|
int in_garbage = 0;
|
|
CELL *next_hb;
|
|
CELL *start_from = H0;
|
|
#ifdef TABLING
|
|
dep_fr_ptr depfr = LOCAL_top_dep_fr;
|
|
#endif /* TABLING */
|
|
|
|
|
|
/*
|
|
* upward phase - scan heap from high to low, setting marked upward
|
|
* ptrs to point to what will be the new locations of the
|
|
* objects pointed to
|
|
*/
|
|
|
|
#ifdef TABLING
|
|
if (depfr && gc_B >= DepFr_cons_cp(depfr)) {
|
|
gc_B = DepFr_cons_cp(depfr);
|
|
depfr = DepFr_next(depfr);
|
|
}
|
|
#endif /* TABLING */
|
|
next_hb = set_next_hb(gc_B PASS_REGS);
|
|
dest = H0 + LOCAL_total_marked - 1;
|
|
|
|
gc_B = update_B_H(gc_B, HR, dest+1, dest+2
|
|
#ifdef TABLING
|
|
, &depfr
|
|
#endif /* TABLING */
|
|
);
|
|
for (current = HR - 1; current >= start_from; current--) {
|
|
|
|
if (MARKED_PTR(current)) {
|
|
CELL ccell = UNMARK_CELL(*current);
|
|
|
|
if (in_garbage > 0) {
|
|
current[1] = in_garbage;
|
|
in_garbage = 0;
|
|
}
|
|
|
|
if (current <= next_hb) {
|
|
gc_B = update_B_H(gc_B, current, dest, dest+1
|
|
#ifdef TABLING
|
|
, &depfr
|
|
#endif /* TABLING */
|
|
);
|
|
next_hb = set_next_hb(gc_B PASS_REGS);
|
|
}
|
|
|
|
if (ccell == EndSpecials) {
|
|
/* oops, we found a blob */
|
|
CELL *ptr = current-1;
|
|
UInt nofcells;
|
|
|
|
while (!MARKED_PTR(ptr)) {
|
|
ptr--;
|
|
}
|
|
nofcells = current-ptr;
|
|
ptr++;
|
|
MARK(ptr);
|
|
#ifdef DEBUG
|
|
//fprintf(stderr,"%p U %d\n", ptr, nofcells);
|
|
found_marked+=nofcells;
|
|
#endif
|
|
/* first swap the tag so that it will be seen by the next step */
|
|
current[0] = ptr[0];
|
|
ptr[0] = EndSpecials;
|
|
dest -= nofcells;
|
|
current = ptr;
|
|
/* process the functor on a separate cycle */
|
|
DEBUG_printf21("%p %ld\n", current-1, (long int)(nofcells+1));
|
|
continue;
|
|
} else {
|
|
DEBUG_printf20("%p 1\n", current);
|
|
}
|
|
#ifdef DEBUG
|
|
// fprintf(stderr,"%p U\n", current);
|
|
found_marked++;
|
|
#endif /* DEBUG */
|
|
update_relocation_chain(current, dest PASS_REGS);
|
|
if (HEAP_PTR(*current)) {
|
|
next = GET_NEXT(*current);
|
|
if (next < current) /* push into reloc.
|
|
* chain */
|
|
into_relocation_chain(current, next PASS_REGS);
|
|
else if (current == next) { /* cell pointing to
|
|
* itself */
|
|
UNRMARK(current);
|
|
*current = (CELL) dest; /* no tag */
|
|
}
|
|
}
|
|
dest--;
|
|
} else {
|
|
in_garbage++;
|
|
}
|
|
}
|
|
if (in_garbage)
|
|
start_from[0] = in_garbage;
|
|
|
|
#ifdef DEBUG
|
|
if (dest != start_from-1)
|
|
fprintf(GLOBAL_stderr,"%% Bad Dest (%lu): %p should be %p\n",
|
|
(unsigned long int)LOCAL_GcCalls,
|
|
dest,
|
|
start_from-1);
|
|
if (LOCAL_total_marked != found_marked)
|
|
fprintf(GLOBAL_stderr,"%% Upward (%lu): %lu total against %lu found\n",
|
|
(unsigned long int)LOCAL_GcCalls,
|
|
(unsigned long int)LOCAL_total_marked,
|
|
(unsigned long int)found_marked);
|
|
found_marked = start_from-H0;
|
|
#endif
|
|
|
|
|
|
/*
|
|
* downward phase - scan heap from low to high, moving marked objects
|
|
* to their new locations & setting downward pointers to pt to new
|
|
* locations
|
|
*/
|
|
|
|
dest = (CELL_PTR) start_from;
|
|
for (current = start_from; current < HR; current++) {
|
|
CELL ccur = *current;
|
|
if (MARKED_PTR(current)) {
|
|
CELL uccur = UNMARK_CELL(ccur);
|
|
if (uccur == EndSpecials) {
|
|
CELL *old_dest = dest;
|
|
|
|
dest++;
|
|
current++;
|
|
while (!MARKED_PTR(current)) {
|
|
*dest++ = *current++;
|
|
}
|
|
*old_dest = *current;
|
|
/* if we have are calling from the C-interface,
|
|
we may have an open array when we start the gc */
|
|
if (LOCAL_OpenArray) {
|
|
CELL *start = current + (dest-old_dest);
|
|
if (LOCAL_OpenArray < current &&
|
|
LOCAL_OpenArray > start) {
|
|
UInt off = LOCAL_OpenArray-start;
|
|
LOCAL_OpenArray = old_dest+off;
|
|
}
|
|
}
|
|
*dest++ = EndSpecials;
|
|
#ifdef DEBUG
|
|
found_marked += (dest-old_dest);
|
|
#endif
|
|
continue;
|
|
}
|
|
#ifdef DEBUG
|
|
found_marked++;
|
|
#endif
|
|
update_relocation_chain(current, dest PASS_REGS);
|
|
ccur = *current;
|
|
next = GET_NEXT(ccur);
|
|
if (HEAP_PTR(ccur) &&
|
|
(next = GET_NEXT(ccur)) < HR && /* move current cell &
|
|
* push */
|
|
next > current) { /* into relocation chain */
|
|
*dest = ccur;
|
|
into_relocation_chain(dest, next PASS_REGS);
|
|
UNMARK(dest);
|
|
} else {
|
|
/* just move current cell */
|
|
*dest = ccur = UNMARK_CELL(ccur);
|
|
}
|
|
/* next cell, please */
|
|
dest++;
|
|
} else {
|
|
current += (ccur-1);
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
if (LOCAL_total_marked != found_marked)
|
|
fprintf(GLOBAL_stderr,"%% Downward (%lu): %lu total against %lu found\n",
|
|
(unsigned long int)LOCAL_GcCalls,
|
|
(unsigned long int)LOCAL_total_marked,
|
|
(unsigned long int)found_marked);
|
|
#endif
|
|
|
|
HR = dest; /* reset H */
|
|
HB = B->cp_h;
|
|
#ifdef TABLING
|
|
if (B_FZ == (choiceptr)LCL0)
|
|
H_FZ = H0;
|
|
else
|
|
H_FZ = B_FZ->cp_h;
|
|
#endif /* TABLING */
|
|
|
|
}
|
|
|
|
#ifdef HYBRID_SCHEME
|
|
/*
|
|
* move marked objects on the heap upwards over unmarked objects, and reset
|
|
* all pointers to point to new locations
|
|
*/
|
|
static void
|
|
icompact_heap( USES_REGS1 )
|
|
{
|
|
CELL_PTR *iptr, *ibase = (CELL_PTR *)HR;
|
|
CELL_PTR dest;
|
|
CELL *next_hb;
|
|
#ifdef DEBUG
|
|
Int found_marked = 0;
|
|
#endif /* DEBUG */
|
|
#ifdef TABLING
|
|
dep_fr_ptr depfr = LOCAL_top_dep_fr;
|
|
#endif /* TABLING */
|
|
choiceptr gc_B = B;
|
|
|
|
/*
|
|
* upward phase - scan heap from high to low, setting marked upward
|
|
* ptrs to point to what will be the new locations of the
|
|
* objects pointed to
|
|
*/
|
|
|
|
#ifdef TABLING
|
|
if (depfr && gc_B >= DepFr_cons_cp(depfr)) {
|
|
gc_B = DepFr_cons_cp(depfr);
|
|
depfr = DepFr_next(depfr);
|
|
}
|
|
#endif /* TABLING */
|
|
next_hb = set_next_hb(gc_B PASS_REGS);
|
|
dest = (CELL_PTR) H0 + LOCAL_total_marked - 1;
|
|
gc_B = update_B_H(gc_B, HR, dest+1, dest+2
|
|
#ifdef TABLING
|
|
, &depfr
|
|
#endif /* TABLING */
|
|
);
|
|
for (iptr = LOCAL_iptop - 1; iptr >= ibase; iptr--) {
|
|
CELL ccell;
|
|
CELL_PTR current;
|
|
|
|
current = *iptr;
|
|
ccell = UNMARK_CELL(*current);
|
|
if (current <= next_hb) {
|
|
gc_B = update_B_H(gc_B, current, dest, dest+1
|
|
#ifdef TABLING
|
|
, &depfr
|
|
#endif /* TABLING */
|
|
);
|
|
next_hb = set_next_hb(gc_B PASS_REGS);
|
|
}
|
|
if (ccell == EndSpecials) {
|
|
/* oops, we found a blob */
|
|
CELL_PTR ptr;
|
|
UInt nofcells;
|
|
|
|
/* use the first cell after the functor for all our dirty tricks */
|
|
ptr = iptr[-1]+1;
|
|
nofcells = current-ptr;
|
|
#ifdef DEBUG
|
|
found_marked+=(nofcells+1);
|
|
#endif /* DEBUG */
|
|
dest -= nofcells+1;
|
|
/* this one's being used */
|
|
/* make the second step see the EndSpecial tag */
|
|
current[0] = ptr[0];
|
|
ptr[0] = EndSpecials;
|
|
iptr[0] = ptr;
|
|
continue;
|
|
}
|
|
#ifdef DEBUG
|
|
found_marked++;
|
|
#endif /* DEBUG */
|
|
update_relocation_chain(current, dest PASS_REGS);
|
|
if (HEAP_PTR(*current)) {
|
|
CELL_PTR next;
|
|
next = GET_NEXT(*current);
|
|
if (next < current) /* push into reloc.
|
|
* chain */
|
|
into_relocation_chain(current, next PASS_REGS);
|
|
else if (current == next) { /* cell pointing to
|
|
* itself */
|
|
UNRMARK(current);
|
|
*current = (CELL) dest; /* no tag */
|
|
}
|
|
}
|
|
dest--;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (dest != H0-1)
|
|
fprintf(GLOBAL_stderr,"%% Bad Dest (%lu): %p should be %p\n",
|
|
(unsigned long int)LOCAL_GcCalls,
|
|
dest,
|
|
H0-1);
|
|
if (LOCAL_total_marked != found_marked)
|
|
fprintf(GLOBAL_stderr,"%% Upward (%lu): %lu total against %lu found\n",
|
|
(unsigned long int)LOCAL_GcCalls,
|
|
(unsigned long int)LOCAL_total_marked,
|
|
(unsigned long int)found_marked);
|
|
found_marked = 0;
|
|
#endif
|
|
|
|
|
|
/*
|
|
* downward phase - scan heap from low to high, moving marked objects
|
|
* to their new locations & setting downward pointers to pt to new
|
|
* locations
|
|
*/
|
|
|
|
dest = H0;
|
|
for (iptr = ibase; iptr < LOCAL_iptop; iptr++) {
|
|
CELL_PTR next;
|
|
CELL *current = *iptr;
|
|
CELL ccur = *current;
|
|
CELL uccur = UNMARK_CELL(ccur);
|
|
|
|
if (uccur == EndSpecials) {
|
|
CELL *old_dest = dest;
|
|
|
|
/* leave a hole */
|
|
dest++;
|
|
current++;
|
|
while (!MARKED_PTR(current)) {
|
|
*dest++ = *current++;
|
|
}
|
|
/* fill in hole */
|
|
*old_dest = *current;
|
|
*dest++ = EndSpecials;
|
|
#ifdef DEBUG
|
|
found_marked += dest-old_dest;
|
|
#endif
|
|
continue;
|
|
}
|
|
#ifdef DEBUG
|
|
found_marked++;
|
|
#endif
|
|
update_relocation_chain(current, dest PASS_REGS);
|
|
ccur = *current;
|
|
next = GET_NEXT(ccur);
|
|
if (HEAP_PTR(ccur) && /* move current cell &
|
|
* push */
|
|
next > current) { /* into relocation chain */
|
|
*dest = ccur;
|
|
into_relocation_chain(dest, next PASS_REGS);
|
|
UNMARK(dest);
|
|
dest++;
|
|
} else {
|
|
/* just move current cell */
|
|
*dest++ = ccur = UNMARK_CELL(ccur);
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
if (H0+LOCAL_total_marked != dest)
|
|
fprintf(GLOBAL_stderr,"%% Downward (%lu): %p total against %p found\n",
|
|
(unsigned long int)LOCAL_GcCalls,
|
|
H0+LOCAL_total_marked,
|
|
dest);
|
|
if (LOCAL_total_marked != found_marked)
|
|
fprintf(GLOBAL_stderr,"%% Downward (%lu): %lu total against %lu found\n",
|
|
(unsigned long int)LOCAL_GcCalls,
|
|
(unsigned long int)LOCAL_total_marked,
|
|
(unsigned long int)found_marked);
|
|
#endif
|
|
|
|
HR = dest; /* reset H */
|
|
HB = B->cp_h;
|
|
#ifdef TABLING
|
|
if (B_FZ == (choiceptr)LCL0)
|
|
H_FZ = H0;
|
|
else
|
|
H_FZ = B_FZ->cp_h;
|
|
#endif /* TABLING */
|
|
|
|
}
|
|
#endif /* HYBRID_SCHEME */
|
|
|
|
|
|
#ifdef EASY_SHUNTING
|
|
static void
|
|
set_conditionals(tr_fr_ptr str USES_REGS) {
|
|
while (str != LOCAL_sTR0) {
|
|
CELL *cptr;
|
|
str -= 2;
|
|
cptr = (CELL *)TrailTerm(str+1);
|
|
*cptr = TrailTerm(str);
|
|
}
|
|
LOCAL_sTR = LOCAL_sTR0 = NULL;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
* mark all objects on the heap that are accessible from active registers,
|
|
* the trail, environments, and choicepoints
|
|
*/
|
|
|
|
static void
|
|
marking_phase(tr_fr_ptr old_TR, CELL *current_env, yamop *curp USES_REGS)
|
|
{
|
|
|
|
#ifdef EASY_SHUNTING
|
|
LOCAL_current_B = B;
|
|
LOCAL_prev_HB = H;
|
|
#endif
|
|
init_dbtable(old_TR PASS_REGS);
|
|
#ifdef EASY_SHUNTING
|
|
LOCAL_sTR0 = (tr_fr_ptr)LOCAL_db_vec;
|
|
LOCAL_sTR = (tr_fr_ptr)LOCAL_db_vec;
|
|
/* make sure we set HB before we do any variable shunting!!! */
|
|
#else
|
|
LOCAL_cont_top0 = (cont *)LOCAL_db_vec;
|
|
#endif
|
|
LOCAL_cont_top = (cont *)LOCAL_db_vec;
|
|
/* These two must be marked first so that our trail optimisation won't lose
|
|
values */
|
|
mark_slots( PASS_REGS1 );
|
|
mark_regs(old_TR PASS_REGS); /* active registers & trail */
|
|
/* active environments */
|
|
mark_environments(current_env, EnvSize(curp), EnvBMap(curp) PASS_REGS);
|
|
mark_choicepoints(B, old_TR, is_gc_very_verbose() PASS_REGS); /* choicepoints, and environs */
|
|
#ifdef EASY_SHUNTING
|
|
set_conditionals(LOCAL_sTR PASS_REGS);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
sweep_oldgen(CELL *max, CELL *base USES_REGS)
|
|
{
|
|
CELL *ptr = base;
|
|
char *bpb = LOCAL_bp+(base-(CELL*)LOCAL_GlobalBase);
|
|
|
|
while (ptr < max) {
|
|
if (*bpb) {
|
|
if (HEAP_PTR(*ptr)) {
|
|
into_relocation_chain(ptr, GET_NEXT(*ptr) PASS_REGS);
|
|
}
|
|
}
|
|
ptr++;
|
|
bpb++;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* move marked heap objects upwards over unmarked objects, and reset all
|
|
* pointers to point to new locations
|
|
*/
|
|
|
|
static void
|
|
compaction_phase(tr_fr_ptr old_TR, CELL *current_env, yamop *curp USES_REGS)
|
|
{
|
|
CELL *CurrentH0 = NULL;
|
|
|
|
int icompact = (LOCAL_iptop < (CELL_PTR *)ASP && 10*LOCAL_total_marked < HR-H0);
|
|
|
|
if (icompact) {
|
|
/* we are going to reuse the total space */
|
|
if (LOCAL_HGEN != H0) {
|
|
/* undo optimisation */
|
|
LOCAL_total_marked += LOCAL_total_oldies;
|
|
}
|
|
} else {
|
|
if (LOCAL_HGEN != H0) {
|
|
CurrentH0 = H0;
|
|
H0 = LOCAL_HGEN;
|
|
sweep_oldgen(LOCAL_HGEN, CurrentH0 PASS_REGS);
|
|
}
|
|
}
|
|
sweep_slots( PASS_REGS1 );
|
|
sweep_environments(current_env, EnvSize(curp), EnvBMap(curp) PASS_REGS);
|
|
sweep_choicepoints(B PASS_REGS);
|
|
sweep_trail(B, old_TR PASS_REGS);
|
|
#ifdef HYBRID_SCHEME
|
|
if (icompact) {
|
|
#ifdef DEBUG
|
|
/*
|
|
if (LOCAL_total_marked
|
|
#ifdef COROUTINING
|
|
-LOCAL_total_smarked
|
|
#endif
|
|
!= LOCAL_iptop-(CELL_PTR *)H && LOCAL_iptop < (CELL_PTR *)ASP -1024)
|
|
fprintf(GLOBAL_stderr,"%% Oops on LOCAL_iptop-H (%ld) vs %ld\n", (unsigned long int)(LOCAL_iptop-(CELL_PTR *)HR), LOCAL_total_marked);
|
|
*/
|
|
#endif
|
|
#if DEBUGX
|
|
int effectiveness = (((H-H0)-LOCAL_total_marked)*100)/(H-H0);
|
|
fprintf(GLOBAL_stderr,"%% using pointers (%d)\n", effectiveness);
|
|
#endif
|
|
if (CurrentH0) {
|
|
H0 = CurrentH0;
|
|
LOCAL_HGEN = H0;
|
|
LOCAL_total_marked += LOCAL_total_oldies;
|
|
CurrentH0 = NULL;
|
|
}
|
|
quicksort((CELL_PTR *)HR, 0, (LOCAL_iptop-(CELL_PTR *)HR)-1);
|
|
icompact_heap( PASS_REGS1 );
|
|
} else
|
|
#endif /* HYBRID_SCHEME */
|
|
{
|
|
#ifdef DEBUG
|
|
/*
|
|
#ifdef HYBRID_SCHEME
|
|
int effectiveness = (((H-H0)-LOCAL_total_marked)*100)/(H-H0);
|
|
fprintf(stderr,"%% not using pointers (%d) ASP: %p, ip %p (expected %p) \n", effectiveness, ASP, LOCAL_iptop, H+LOCAL_total_marked);
|
|
|
|
#endif
|
|
*/
|
|
#endif
|
|
compact_heap( PASS_REGS1 );
|
|
}
|
|
if (CurrentH0) {
|
|
H0 = CurrentH0;
|
|
#ifdef TABLING
|
|
/* make sure that we have the correct H_FZ if we're not tabling */
|
|
if (B_FZ == (choiceptr)LCL0)
|
|
H_FZ = H0;
|
|
#endif /* TABLING */
|
|
}
|
|
}
|
|
|
|
static int
|
|
do_gc(Int predarity, CELL *current_env, yamop *nextop USES_REGS)
|
|
{
|
|
Int heap_cells;
|
|
int gc_verbose;
|
|
volatile tr_fr_ptr old_TR = NULL;
|
|
UInt m_time, c_time, time_start, gc_time;
|
|
Int effectiveness, tot;
|
|
int gc_trace;
|
|
UInt gc_phase;
|
|
UInt alloc_sz;
|
|
int jmp_res;
|
|
|
|
heap_cells = HR-H0;
|
|
gc_verbose = is_gc_verbose();
|
|
effectiveness = 0;
|
|
gc_trace = FALSE;
|
|
LOCAL_GcCalls++;
|
|
#ifdef INSTRUMENT_GC
|
|
{
|
|
int i;
|
|
for (i=0; i<16; i++)
|
|
chain[i]=0;
|
|
vars[gc_var] = 0;
|
|
vars[gc_ref] = 0;
|
|
vars[gc_atom] = 0;
|
|
vars[gc_int] = 0;
|
|
vars[gc_num] = 0;
|
|
vars[gc_list] = 0;
|
|
vars[gc_appl] = 0;
|
|
vars[gc_func] = 0;
|
|
vars[gc_susp] = 0;
|
|
env_vars = 0;
|
|
old_vars = new_vars = 0;
|
|
TrueHB = HB;
|
|
num_bs = 0;
|
|
}
|
|
#endif
|
|
#ifdef DEBUG
|
|
check_global();
|
|
#endif
|
|
if (Yap_GetValue(AtomGcTrace) != TermNil)
|
|
gc_trace = 1;
|
|
if (gc_trace) {
|
|
fprintf(GLOBAL_stderr, "%% gc\n");
|
|
} else if (gc_verbose) {
|
|
#if defined(YAPOR) || defined(THREADS)
|
|
fprintf(GLOBAL_stderr, "%% Worker Id %d:\n", worker_id);
|
|
#endif
|
|
fprintf(GLOBAL_stderr, "%% Start of garbage collection %lu:\n", (unsigned long int)LOCAL_GcCalls);
|
|
fprintf(GLOBAL_stderr, "%% Global: %8ld cells (%p-%p)\n", (long int)heap_cells,H0,HR);
|
|
fprintf(GLOBAL_stderr, "%% Local:%8ld cells (%p-%p)\n", (unsigned long int)(LCL0-ASP),LCL0,ASP);
|
|
fprintf(GLOBAL_stderr, "%% Trail:%8ld cells (%p-%p)\n",
|
|
(unsigned long int)(TR-(tr_fr_ptr)LOCAL_TrailBase),LOCAL_TrailBase,TR);
|
|
}
|
|
#if !USE_SYSTEM_MALLOC
|
|
if (HeapTop >= LOCAL_GlobalBase - MinHeapGap) {
|
|
*--ASP = (CELL)current_env;
|
|
if (!Yap_growheap(FALSE, MinHeapGap, NULL)) {
|
|
Yap_Error(OUT_OF_HEAP_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
return -1;
|
|
}
|
|
current_env = (CELL *)*ASP;
|
|
ASP++;
|
|
}
|
|
#endif
|
|
time_start = Yap_cputime();
|
|
jmp_res = sigsetjmp(LOCAL_gc_restore, 0);
|
|
if (jmp_res == 2) {
|
|
UInt sz;
|
|
|
|
/* we cannot recover, fail system */
|
|
restore_machine_regs();
|
|
sz = LOCAL_TrailTop-(ADDR)LOCAL_OldTR;
|
|
/* ask for double the size */
|
|
sz = 2*sz;
|
|
TR = LOCAL_OldTR;
|
|
|
|
*--ASP = (CELL)current_env;
|
|
if (
|
|
!Yap_growtrail(sz, FALSE)
|
|
) {
|
|
Yap_Error(OUT_OF_TRAIL_ERROR,TermNil,"out of %lB during gc", sz);
|
|
return -1;
|
|
} else {
|
|
LOCAL_total_marked = 0;
|
|
LOCAL_total_oldies = 0;
|
|
#ifdef COROUTING
|
|
LOCAL_total_smarked = 0;
|
|
#endif
|
|
LOCAL_discard_trail_entries = 0;
|
|
current_env = (CELL *)*ASP;
|
|
ASP++;
|
|
}
|
|
} else if (jmp_res == 3) {
|
|
/* we cannot recover, fail system */
|
|
restore_machine_regs();
|
|
TR = LOCAL_OldTR;
|
|
|
|
LOCAL_total_marked = 0;
|
|
LOCAL_total_oldies = 0;
|
|
#ifdef COROUTING
|
|
LOCAL_total_smarked = 0;
|
|
#endif
|
|
LOCAL_discard_trail_entries = 0;
|
|
if (LOCAL_extra_gc_cells_size < 1024 *104) {
|
|
LOCAL_extra_gc_cells_size <<= 1;
|
|
} else {
|
|
LOCAL_extra_gc_cells_size += 1024*1024;
|
|
}
|
|
} else if (jmp_res == 4) {
|
|
/* we cannot recover, fail completely */
|
|
Yap_exit(1);
|
|
}
|
|
#if EASY_SHUNTING
|
|
LOCAL_sTR0 = LOCAL_sTR = NULL;
|
|
#endif
|
|
LOCAL_total_marked = 0;
|
|
LOCAL_total_oldies = 0;
|
|
#ifdef COROUTING
|
|
LOCAL_total_smarked = 0;
|
|
#endif
|
|
LOCAL_discard_trail_entries = 0;
|
|
alloc_sz = (CELL *)LOCAL_TrailTop-(CELL*)LOCAL_GlobalBase;
|
|
LOCAL_bp = Yap_PreAllocCodeSpace();
|
|
while (IN_BETWEEN(LOCAL_bp, AuxSp, LOCAL_bp+alloc_sz)) {
|
|
/* not enough space */
|
|
*--ASP = (CELL)current_env;
|
|
LOCAL_bp = (char *)Yap_ExpandPreAllocCodeSpace(alloc_sz, NULL, TRUE);
|
|
if (!LOCAL_bp)
|
|
return -1;
|
|
current_env = (CELL *)*ASP;
|
|
ASP++;
|
|
}
|
|
memset((void *)LOCAL_bp, 0, alloc_sz);
|
|
#ifdef HYBRID_SCHEME
|
|
LOCAL_iptop = (CELL_PTR *)HR;
|
|
#endif
|
|
/* get the number of active registers */
|
|
LOCAL_HGEN = VarOfTerm(Yap_ReadTimedVar(LOCAL_GcGeneration));
|
|
|
|
gc_phase = (UInt)IntegerOfTerm(Yap_ReadTimedVar(LOCAL_GcPhase));
|
|
/* old LOCAL_HGEN are not very reliable, but still may have data to recover */
|
|
if (gc_phase != LOCAL_GcCurrentPhase) {
|
|
LOCAL_HGEN = H0;
|
|
}
|
|
/* fprintf(stderr,"LOCAL_HGEN is %ld, %p, %p/%p\n", IntegerOfTerm(Yap_ReadTimedVar(LOCAL_GcGeneration)), LOCAL_HGEN, H,H0);*/
|
|
LOCAL_OldTR = (tr_fr_ptr)(old_TR = TR);
|
|
push_registers(predarity, nextop PASS_REGS);
|
|
/* make sure we clean bits after a reset */
|
|
marking_phase(old_TR, current_env, nextop PASS_REGS);
|
|
if (LOCAL_total_oldies > ((LOCAL_HGEN-H0)*8)/10) {
|
|
LOCAL_total_marked -= LOCAL_total_oldies;
|
|
tot = LOCAL_total_marked+(LOCAL_HGEN-H0);
|
|
} else {
|
|
if (LOCAL_HGEN != H0) {
|
|
LOCAL_HGEN = H0;
|
|
LOCAL_GcCurrentPhase++;
|
|
}
|
|
tot = LOCAL_total_marked;
|
|
}
|
|
m_time = Yap_cputime();
|
|
gc_time = m_time-time_start;
|
|
if (heap_cells) {
|
|
if (heap_cells > 1000000)
|
|
effectiveness = (heap_cells-tot)/(heap_cells/100);
|
|
else
|
|
effectiveness = 100*(heap_cells-tot)/heap_cells;
|
|
} else
|
|
effectiveness = 0;
|
|
if (gc_verbose) {
|
|
fprintf(GLOBAL_stderr, "%% Mark: Marked %ld cells of %ld (efficiency: %ld%%) in %g sec\n",
|
|
(long int)tot, (long int)heap_cells, (long int)effectiveness, (double)(m_time-time_start)/1000);
|
|
if (LOCAL_HGEN-H0)
|
|
fprintf(GLOBAL_stderr,"%% previous generation has size " UInt_FORMAT ", with " UInt_FORMAT " (" UInt_FORMAT "%%) unmarked\n", (UInt)(LOCAL_HGEN-H0), (UInt)((LOCAL_HGEN-H0)-LOCAL_total_oldies), (UInt)(100*((LOCAL_HGEN-H0)-LOCAL_total_oldies)/(LOCAL_HGEN-H0)));
|
|
#ifdef INSTRUMENT_GC
|
|
{
|
|
int i;
|
|
for (i=0; i<16; i++) {
|
|
if (chain[i]) {
|
|
fprintf(GLOBAL_stderr, "%% chain[%d]=%lu\n", i, chain[i]);
|
|
}
|
|
}
|
|
put_type_info((unsigned long int)tot);
|
|
fprintf(GLOBAL_stderr,"%% %lu/%ld before and %lu/%ld after\n", old_vars, (unsigned long int)(B->cp_h-H0), new_vars, (unsigned long int)(H-B->cp_h));
|
|
fprintf(GLOBAL_stderr,"%% %ld choicepoints\n", num_bs);
|
|
}
|
|
#endif
|
|
}
|
|
time_start = m_time;
|
|
compaction_phase(old_TR, current_env, nextop PASS_REGS);
|
|
TR = old_TR;
|
|
pop_registers(predarity, nextop PASS_REGS);
|
|
TR = LOCAL_new_TR;
|
|
/* fprintf(GLOBAL_stderr,"NEW LOCAL_HGEN %ld (%ld)\n", H-H0, LOCAL_HGEN-H0);*/
|
|
{
|
|
Term t = MkVarTerm();
|
|
Yap_UpdateTimedVar(LOCAL_GcGeneration, t);
|
|
}
|
|
Yap_UpdateTimedVar(LOCAL_GcPhase, MkIntegerTerm(LOCAL_GcCurrentPhase));
|
|
c_time = Yap_cputime();
|
|
if (gc_verbose) {
|
|
fprintf(GLOBAL_stderr, "%% Compress: took %g sec\n", (double)(c_time-time_start)/1000);
|
|
}
|
|
gc_time += (c_time-time_start);
|
|
LOCAL_TotGcTime += gc_time;
|
|
LOCAL_TotGcRecovered += heap_cells-tot;
|
|
if (gc_verbose) {
|
|
fprintf(GLOBAL_stderr, "%% GC %lu took %g sec, total of %g sec doing GC so far.\n", (unsigned long int)LOCAL_GcCalls, (double)gc_time/1000, (double)LOCAL_TotGcTime/1000);
|
|
fprintf(GLOBAL_stderr, "%% Left %ld cells free in stacks.\n",
|
|
(unsigned long int)(ASP-HR));
|
|
}
|
|
check_global();
|
|
return effectiveness;
|
|
}
|
|
|
|
static int
|
|
is_gc_verbose(void)
|
|
{
|
|
CACHE_REGS
|
|
if (LOCAL_PrologMode == BootMode)
|
|
return FALSE;
|
|
#ifdef INSTRUMENT_GC
|
|
/* always give info when we are debugging gc */
|
|
return(TRUE);
|
|
#else
|
|
return(Yap_GetValue(AtomGcVerbose) != TermNil ||
|
|
Yap_GetValue(AtomGcVeryVerbose) != TermNil);
|
|
#endif
|
|
}
|
|
|
|
int
|
|
Yap_is_gc_verbose(void)
|
|
{
|
|
return is_gc_verbose();
|
|
}
|
|
|
|
static int
|
|
is_gc_very_verbose(void)
|
|
{
|
|
CACHE_REGS
|
|
if (LOCAL_PrologMode == BootMode)
|
|
return FALSE;
|
|
return Yap_GetValue(AtomGcVeryVerbose) != TermNil;
|
|
}
|
|
|
|
Int
|
|
Yap_total_gc_time(void)
|
|
{
|
|
CACHE_REGS
|
|
return(LOCAL_TotGcTime);
|
|
}
|
|
|
|
static Int
|
|
p_inform_gc( USES_REGS1 )
|
|
{
|
|
Term tn = MkIntegerTerm(LOCAL_TotGcTime);
|
|
Term tt = MkIntegerTerm(LOCAL_GcCalls);
|
|
Term ts = Yap_Mk64IntegerTerm((LOCAL_TotGcRecovered*sizeof(CELL)));
|
|
|
|
return(Yap_unify(tn, ARG2) && Yap_unify(tt, ARG1) && Yap_unify(ts, ARG3));
|
|
|
|
}
|
|
|
|
|
|
static int
|
|
call_gc(UInt gc_lim, Int predarity, CELL *current_env, yamop *nextop USES_REGS)
|
|
{
|
|
UInt gc_margin = MinStackGap;
|
|
Term Tgc_margin;
|
|
Int effectiveness = 0;
|
|
int gc_on = FALSE, gc_t = FALSE;
|
|
|
|
if (Yap_GetValue(AtomGc) != TermNil)
|
|
gc_on = TRUE;
|
|
if (IsIntegerTerm(Tgc_margin = Yap_GetValue(AtomGcMargin)) &&
|
|
gc_margin > 0) {
|
|
gc_margin = (UInt)IntegerOfTerm(Tgc_margin);
|
|
gc_t = TRUE;
|
|
} else {
|
|
/* only go exponential for the first 6 calls, that would ask about 2MB minimum */
|
|
if (LOCAL_GcCalls < 8)
|
|
gc_margin <<= LOCAL_GcCalls;
|
|
else {
|
|
/* next grow linearly */
|
|
gc_margin <<= 8;
|
|
/* don't do this: it forces the system to ask for ever more stack!!
|
|
gc_margin *= LOCAL_GcCalls;
|
|
*/
|
|
}
|
|
}
|
|
if (gc_margin < gc_lim)
|
|
gc_margin = gc_lim;
|
|
LOCAL_HGEN = VarOfTerm(Yap_ReadTimedVar(LOCAL_GcGeneration));
|
|
if (gc_on && !(LOCAL_PrologMode & InErrorMode) &&
|
|
/* make sure there is a point in collecting the heap */
|
|
(ASP-H0)*sizeof(CELL) > gc_lim &&
|
|
HR-LOCAL_HGEN > (LCL0-ASP)/2) {
|
|
effectiveness = do_gc(predarity, current_env, nextop PASS_REGS);
|
|
if (effectiveness < 0)
|
|
return FALSE;
|
|
if (effectiveness > 90 && !gc_t) {
|
|
while (gc_margin < (HR-H0)/sizeof(CELL))
|
|
gc_margin <<= 1;
|
|
}
|
|
} else {
|
|
effectiveness = 0;
|
|
}
|
|
/* expand the stack if effectiveness is less than 20 % */
|
|
if (ASP - HR < gc_margin/sizeof(CELL) ||
|
|
effectiveness < 20) {
|
|
LeaveGCMode( PASS_REGS1 );
|
|
#ifndef YAPOR
|
|
CalculateStackGap( PASS_REGS1 );
|
|
if (gc_margin < 2*EventFlag)
|
|
gc_margin = 2*EventFlag;
|
|
return Yap_growstack(gc_margin);
|
|
#endif
|
|
}
|
|
/*
|
|
* debug for(save_total=1; save_total<=N; ++save_total)
|
|
* plwrite(XREGS[save_total],NULL,30,0,0,0);
|
|
*/
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
LeaveGCMode( USES_REGS1 )
|
|
{
|
|
if (LOCAL_PrologMode & GCMode)
|
|
LOCAL_PrologMode &= ~GCMode;
|
|
if (LOCAL_PrologMode & AbortMode) {
|
|
LOCAL_PrologMode &= ~AbortMode;
|
|
/* in case someone mangles the P register */
|
|
Yap_Error(PURE_ABORT, TermNil, "abort from console");
|
|
Yap_RestartYap( 1 );
|
|
}
|
|
}
|
|
|
|
int
|
|
Yap_gc(Int predarity, CELL *current_env, yamop *nextop)
|
|
{
|
|
CACHE_REGS
|
|
int res;
|
|
#if YAPOR_COPY
|
|
fprintf(stderr, "\n\n***** Trying to call the garbage collector in YAPOR/copying ****\n\n\n");
|
|
exit( 1 );
|
|
#endif
|
|
LOCAL_PrologMode |= GCMode;
|
|
res=call_gc(4096, predarity, current_env, nextop PASS_REGS);
|
|
LeaveGCMode( PASS_REGS1 );
|
|
if (LOCAL_PrologMode & GCMode)
|
|
LOCAL_PrologMode &= ~GCMode;
|
|
return res;
|
|
}
|
|
|
|
int
|
|
Yap_gcl(UInt gc_lim, Int predarity, CELL *current_env, yamop *nextop)
|
|
{
|
|
CACHE_REGS
|
|
int res;
|
|
UInt min;
|
|
|
|
CalculateStackGap( PASS_REGS1 );
|
|
min = EventFlag*sizeof(CELL);
|
|
LOCAL_PrologMode |= GCMode;
|
|
if (gc_lim < min)
|
|
gc_lim = min;
|
|
res = call_gc(gc_lim, predarity, current_env, nextop PASS_REGS);
|
|
LeaveGCMode( PASS_REGS1 );
|
|
return res;
|
|
}
|
|
|
|
|
|
static Int
|
|
p_gc( USES_REGS1 )
|
|
{
|
|
int res;
|
|
LOCAL_PrologMode |= GCMode;
|
|
if (P->opc == Yap_opcode(_execute_cpred))
|
|
res = do_gc(0, ENV, CP PASS_REGS) >= 0;
|
|
else
|
|
res = do_gc(0, ENV, P PASS_REGS) >= 0;
|
|
LeaveGCMode( PASS_REGS1 );
|
|
return res;
|
|
}
|
|
|
|
void
|
|
Yap_init_gc(void)
|
|
{
|
|
Yap_InitCPred("$gc", 0, p_gc, 0);
|
|
Yap_InitCPred("$inform_gc", 3, p_inform_gc, 0);
|
|
}
|
|
|
|
void
|
|
Yap_inc_mark_variable()
|
|
{
|
|
CACHE_REGS
|
|
LOCAL_total_marked++;
|
|
}
|