mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-23 02:11:38 +00:00
501e4bb91a
svn-id: r38961
293 lines
7.5 KiB
C++
293 lines
7.5 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
#include "sci/engine/gc.h"
|
|
|
|
namespace Sci {
|
|
|
|
#define WORKLIST_CHUNK_SIZE 32
|
|
|
|
//#define DEBUG_GC
|
|
//#define DEBUG_GC_VERBOSE
|
|
|
|
struct worklist_t {
|
|
int used;
|
|
reg_t entries[WORKLIST_CHUNK_SIZE];
|
|
worklist_t *next;
|
|
};
|
|
|
|
static worklist_t *fresh_worklist(worklist_t *old) {
|
|
worklist_t *retval = (worklist_t*)sci_malloc(sizeof(worklist_t));
|
|
retval->used = 0;
|
|
retval->next = old;
|
|
return retval;
|
|
}
|
|
|
|
static worklist_t *new_worklist() {
|
|
return fresh_worklist(NULL);
|
|
}
|
|
|
|
static void worklist_push(worklist_t **wlp, reg_t_hash_map *hashmap, reg_t reg) {
|
|
worklist_t *wl = *wlp;
|
|
|
|
if (!reg.segment) // No numbers
|
|
return;
|
|
|
|
#ifdef DEBUG_GC_VERBOSE
|
|
sciprintf("[GC] Adding "PREG"\n", PRINT_REG(reg));
|
|
#endif
|
|
|
|
if (hashmap->contains(reg))
|
|
return; // already dealt with it
|
|
|
|
hashmap->setVal(reg, true);
|
|
|
|
if (!wl || wl->used == WORKLIST_CHUNK_SIZE)
|
|
*wlp = wl = fresh_worklist(wl);
|
|
|
|
wl->entries[wl->used++] = reg;
|
|
}
|
|
|
|
static int worklist_has_next(worklist_t *wl) {
|
|
return (wl && wl->used);
|
|
}
|
|
|
|
static reg_t worklist_pop(worklist_t **wlp) {
|
|
worklist_t *wl = *wlp;
|
|
reg_t retval;
|
|
|
|
if (!wl || !wl->used) {
|
|
error("Attempt to pop from empty worklist");
|
|
}
|
|
|
|
retval = wl->entries[--wl->used];
|
|
|
|
if (!wl->used) {
|
|
*wlp = wl->next;
|
|
free(wl);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static void free_worklist(worklist_t *wl) {
|
|
if (wl) {
|
|
if (wl->next)
|
|
free_worklist(wl->next);
|
|
free(wl);
|
|
}
|
|
}
|
|
|
|
static reg_t_hash_map *normalise_hashmap_ptrs(reg_t_hash_map *nonnormal_map, SegInterface **interfaces, int interfaces_nr) {
|
|
reg_t_hash_map *normal_map = new reg_t_hash_map();
|
|
|
|
for (reg_t_hash_map::iterator i = nonnormal_map->begin(); i != nonnormal_map->end(); ++i) {
|
|
SegInterface *interfce;
|
|
reg_t reg = i->_key;
|
|
interfce = (reg.segment < interfaces_nr) ? interfaces[reg.segment] : NULL;
|
|
|
|
if (interfce) {
|
|
reg = interfce->findCanonicAddress(reg);
|
|
normal_map->setVal(reg, true);
|
|
}
|
|
}
|
|
|
|
return normal_map;
|
|
}
|
|
|
|
|
|
struct worklist_manager_t {
|
|
reg_t_hash_map *nonnormal_map;
|
|
worklist_t **worklist_ref;
|
|
};
|
|
|
|
void add_outgoing_refs(void *pre_wm, reg_t addr) {
|
|
worklist_manager_t *wm = (worklist_manager_t *) pre_wm;
|
|
worklist_push(wm->worklist_ref, wm->nonnormal_map, addr);
|
|
}
|
|
|
|
reg_t_hash_map *find_all_used_references(EngineState *s) {
|
|
SegManager *sm = s->seg_manager;
|
|
SegInterface **interfaces = (SegInterface **)sci_calloc(sizeof(SegInterface *), sm->heap_size);
|
|
reg_t_hash_map *nonnormal_map = new reg_t_hash_map();
|
|
reg_t_hash_map *normal_map = NULL;
|
|
worklist_t *worklist = new_worklist();
|
|
worklist_manager_t worklist_manager;
|
|
int i;
|
|
|
|
worklist_manager.worklist_ref = &worklist;
|
|
worklist_manager.nonnormal_map = nonnormal_map;
|
|
|
|
for (i = 1; i < sm->heap_size; i++)
|
|
if (sm->heap[i] == NULL)
|
|
interfaces[i] = NULL;
|
|
else
|
|
interfaces[i] = sm->getSegInterface(i);
|
|
|
|
// Initialise
|
|
// Init: Registers
|
|
worklist_push(&worklist, nonnormal_map, s->r_acc);
|
|
worklist_push(&worklist, nonnormal_map, s->r_prev);
|
|
// Init: Value Stack
|
|
// We do this one by hand since the stack doesn't know the current execution stack
|
|
{
|
|
ExecStack *xs = s->execution_stack + s->execution_stack_pos;
|
|
reg_t *pos;
|
|
|
|
for (pos = s->stack_base; pos < xs->sp; pos++)
|
|
worklist_push(&worklist, nonnormal_map, *pos);
|
|
}
|
|
#ifdef DEBUG_GC_VERBOSE
|
|
sciprintf("[GC] -- Finished adding value stack");
|
|
#endif
|
|
|
|
// Init: Execution Stack
|
|
for (i = 0; i <= s->execution_stack_pos; i++) {
|
|
ExecStack *es = s->execution_stack + i;
|
|
|
|
if (es->type != EXEC_STACK_TYPE_KERNEL) {
|
|
worklist_push(&worklist, nonnormal_map, es->objp);
|
|
worklist_push(&worklist, nonnormal_map, es->sendp);
|
|
if (es->type == EXEC_STACK_TYPE_VARSELECTOR)
|
|
worklist_push(&worklist, nonnormal_map, *(es->addr.varp));
|
|
}
|
|
}
|
|
#ifdef DEBUG_GC_VERBOSE
|
|
sciprintf("[GC] -- Finished adding execution stack");
|
|
#endif
|
|
|
|
// Init: Explicitly loaded scripts
|
|
for (i = 1; i < sm->heap_size; i++)
|
|
if (interfaces[i]
|
|
&& interfaces[i]->getType() == MEM_OBJ_SCRIPT) {
|
|
Script *script = &(interfaces[i]->getMobj()->data.script);
|
|
|
|
if (script->lockers) { // Explicitly loaded?
|
|
int obj_nr;
|
|
|
|
// Locals, if present
|
|
worklist_push(&worklist, nonnormal_map, make_reg(script->locals_segment, 0));
|
|
|
|
// All objects (may be classes, may be indirectly reachable)
|
|
for (obj_nr = 0; obj_nr < script->objects_nr; obj_nr++) {
|
|
Object *obj = script->objects + obj_nr;
|
|
worklist_push(&worklist, nonnormal_map, obj->pos);
|
|
}
|
|
}
|
|
}
|
|
#ifdef DEBUG_GC_VERBOSE
|
|
sciprintf("[GC] -- Finished explicitly loaded scripts, done with root set");
|
|
#endif
|
|
|
|
// Run Worklist Algorithm
|
|
while (worklist_has_next(worklist)) {
|
|
reg_t reg = worklist_pop(&worklist);
|
|
if (reg.segment != s->stack_segment) { // No need to repeat this one
|
|
#ifdef DEBUG_GC_VERBOSE
|
|
sciprintf("[GC] Checking "PREG"\n", PRINT_REG(reg));
|
|
#endif
|
|
if (reg.segment < sm->heap_size && interfaces[reg.segment])
|
|
interfaces[reg.segment]->listAllOutgoingReferences(s, reg,
|
|
&worklist_manager, add_outgoing_refs);
|
|
}
|
|
}
|
|
|
|
// Normalise
|
|
normal_map = normalise_hashmap_ptrs(nonnormal_map, interfaces, sm->heap_size);
|
|
|
|
// Cleanup
|
|
for (i = 1; i < sm->heap_size; i++)
|
|
if (interfaces[i])
|
|
delete interfaces[i];
|
|
|
|
free(interfaces);
|
|
delete nonnormal_map;
|
|
|
|
return normal_map;
|
|
}
|
|
|
|
struct deallocator_t {
|
|
SegInterface *interfce;
|
|
#ifdef DEBUG_GC
|
|
char *segnames[MEM_OBJ_MAX + 1];
|
|
int segcount[MEM_OBJ_MAX + 1];
|
|
#endif
|
|
reg_t_hash_map *use_map;
|
|
};
|
|
|
|
void free_unless_used(void *pre_use_map, reg_t addr) {
|
|
deallocator_t *deallocator = (deallocator_t *)pre_use_map;
|
|
reg_t_hash_map *use_map = deallocator->use_map;
|
|
|
|
if (!use_map->contains(addr)) {
|
|
// Not found -> we can free it
|
|
deallocator->interfce->freeAtAddress(addr);
|
|
#ifdef DEBUG_GC
|
|
sciprintf("[GC] Deallocating "PREG"\n", PRINT_REG(addr));
|
|
deallocator->segcount[deallocator->interfce->type_id]++;
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
void run_gc(EngineState *s) {
|
|
int seg_nr;
|
|
deallocator_t deallocator;
|
|
SegManager *sm = s->seg_manager;
|
|
|
|
#ifdef DEBUG_GC
|
|
c_segtable(s);
|
|
sciprintf("[GC] Running...\n");
|
|
memset(&(deallocator.segcount), 0, sizeof(int) * (MEM_OBJ_MAX + 1));
|
|
#endif
|
|
|
|
deallocator.use_map = find_all_used_references(s);
|
|
|
|
for (seg_nr = 1; seg_nr < sm->heap_size; seg_nr++) {
|
|
if (sm->heap[seg_nr] != NULL) {
|
|
deallocator.interfce = sm->getSegInterface(seg_nr);
|
|
#ifdef DEBUG_GC
|
|
deallocator.segnames[deallocator.interfce->type_id] = deallocator.interfce->type;
|
|
#endif
|
|
deallocator.interfce->listAllDeallocatable(&deallocator, free_unless_used);
|
|
delete deallocator.interfce;
|
|
}
|
|
}
|
|
|
|
delete deallocator.use_map;
|
|
|
|
#ifdef DEBUG_GC
|
|
{
|
|
int i;
|
|
sciprintf("[GC] Summary:\n");
|
|
for (i = 0; i <= MEM_OBJ_MAX; i++)
|
|
if (deallocator.segcount[i])
|
|
sciprintf("\t%d\t* %s\n", deallocator.segcount[i], deallocator.segnames[i]);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
} // End of namespace Sci
|