Commit 93002327 by Bryce McKinlay

Imported version version 5.0alpha6.

	* acinclude.m4: Bump version to 5.0a6.
	* configure.in: Don't use alpha_mach_dep.s.
	* include/private/config.h, irix_threads.c gc_watcom.asm: Delete
	obsolete files.

From-SVN: r33251
parent 5e787f07
2000-04-19 Bryce McKinlay <bryce@albatross.co.nz>
Imported version version 5.0alpha6.
* acinclude.m4: Bump version to 5.0a6.
* configure.in: Don't use alpha_mach_dep.s.
* include/private/config.h, irix_threads.c gc_watcom.asm: Delete
obsolete files.
2000-03-26 Anthony Green <green@redhat.com>
* misc.c (GC_enable): Always define GC_enable and GC_disable.
......@@ -105,7 +113,7 @@ Fri Jan 28 17:13:20 2000 Anthony Green <green@cygnus.com>
Tue Aug 10 00:08:29 1999 Rainer Orth <ro@TechFak.Uni-Bielefeld.DE>
* gc_priv.h: Merged IRIX thread changes from
* gc_priv.h: IRIX thread changes from
include/private/gc_priv.h.
Mon Aug 9 18:33:38 1999 Rainer Orth <ro@TechFak.Uni-Bielefeld.DE>
......
......@@ -15,7 +15,7 @@ Permission to modify the code and to distribute modified code is granted,
provided the above notices are retained, and a notice that the code was
modified is included with the above copyright notice.
This is version 5.0alpha4 of a conservative garbage collector for C and C++.
This is version 5.0alpha6 of a conservative garbage collector for C and C++.
You might find a more recent version of this at
......@@ -1506,10 +1506,11 @@ Since 5.0 alpha3
Henderson and Roman Hodek.
- Removed the tests for SGI_SOURCE in new_gc_alloc.h. This was causing that
interface to fail on nonSGI platforms.
- Changed the Linux stack finding code to use /proc, after chnging it
- Changed the Linux stack finding code to use /proc, after changing it
to use HEURISTIC1. (Thanks to David Mossberger for pointing out the
/proc hook.)
- Added HP/UX incremental GC support and HP/UX 11 thread support.
Thread support is currently still flakey.
- Added basic Linux/IA64 support.
- Integrated Anthony Green's PicoJava support.
- Integrated Scott Ananian's StrongARM/NetBSD support.
......@@ -1527,6 +1528,58 @@ Since 5.0 alpha3
- GC_debug_free(0, ...) failed. Thanks to Fergus Henderson for the
bug report and fix.
Since 5.0 alpha4
- GC_malloc_explicitly_typed and friends sometimes failed to
initialize first word.
- Added allocation routines and support in the marker for mark descriptors
in a type structure referenced by the first word of an object. This was
introduced to support gcj, but hopefully in a way that makes it
generically useful.
- Added GC_requested_heapsize, and inhibited collections in nonincremental
mode if the actual used heap size is less than what was explicitly
requested.
- The Solaris pthreads version of GC_pthread_create didn't handle a NULL
attribute pointer. Solaris thread support used the wrong default thread
stack size. (Thanks to Melissa O'Neill for the patch.)
- Changed PUSH_CONTENTS macro to no longer modify first parameter.
This usually doesn't matter, but it was certainly an accident waiting
to happen ...
- Added GC_register_finalizer_no_order and friends to gc.h. They're
needed by Java implementations.
- Integrated a fix for a win32 deadlock resulting from clock() calling
malloc. (Thanks to Chris Dodd.)
- Integrated Hiroshi Kawashima's port to Linux/MIPS. This was designed
for a handheld platform, and may or may not be sufficient for other
machines.
- Fixed a va_arg problem with the %c specifier in cordprnt.c. It appears
that this was always broken, but recent versions of gcc are the first to
report the (statically detectable) bug.
- Added an attempt at a more general solution to dlopen races/deadlocks.
GC_dlopen now temporarily disables collection. Still not ideal, but ...
- Added -DUSE_I686_PREFETCH, -DUSE_3DNOW_PREFETCH, and support for IA64
prefetch instructions. May improve performance measurably, but I'm not
sure the code will run correctly on processors that don't support the
instruction. Won't build except with very recent gcc.
- Added caching for header lookups in the marker. This seems to result
in a barely measurable performance gain. Added support for interleaved
lookups of two pointers, but unconfigured that since the performance
gain is currently near zero, and it adds to code size.
- Changed Linux DATA_START definition to check both data_start and
__data_start, since nothing else seems to be portable.
- Added -DUSE_LD_WRAP to optionally take advantage of the GNU ld function
wrapping mechanism. Probably currently useful only on Linux.
- Moved some variables for the scratch allocator into GC_arrays, on
Martin Hirzel's suggestion.
- Fixed a win32 threads bug that caused the collector to not look for
interior pointers from one of the thread stacks without
ALL_INTERIOR_POINTERS. (Thanks to Jeff Sturm.)
- Added Mingw32 support. (Thanks again to Jeff Sturm for the patch.)
- Changed the alpha port to use the generic register scanning code instead
of alpha_mach_dep.s. Alpha_mach_dep.s doesn't look for pointers in fp
registers, but gcc sometimes spills pointers there. (Thanks to Manuel Serrano
for helping me debug this by email.) Changed the IA64 code to do something
similar for similar reasons.
To do:
- Very large root set sizes (> 16 MB or so) could cause the collector
to abort with an unexpected mark stack overflow. (Thanks again to
......@@ -1543,3 +1596,7 @@ To do:
- Incremental collector should handle large objects better. Currently,
it looks like the whole object is treated as dirty if any part of it
is.
- Cord/cordprnt.c doesn't build on a few platforms (notably PowerPC), since
we make some unwarranted assumptions about how varargs are handled. This
currently makes the cord-aware versions of printf unusable on some platforms.
Fixing this is unfortunately not trivial.
......@@ -40,7 +40,8 @@ void * big_realloc(void *p, size_t new_size)
1) Consider using GC_malloc_atomic for objects containing nonpointers. This is especially important for large arrays containg compressed data, pseudo-random numbers, and the like. (This isn't all that likely to solve your problem, but it's a useful and easy optimization anyway, and this is a good time to try it.) If you allocate large objects containg only one or two pointers at the beginning, either try the typed allocation primitives is gc.h, or separate out the pointerfree component.
2) If you are using the collector in its default mode, with interior pointer recognition enabled, consider using GC_malloc_ignore_off_page to allocate large objects. (See gc.h and above for details. Large means > 100K in most environments.)
3) GC_print_block_list() will print a list of all currently allocated heap blocks and what size objects they contain. GC_print_hblkfreelist() will print a list of free heap blocks, and whether they are blacklisted. GC_dump calls both of these, and also prints information about heap sections, and root segments.
4) Write a tool that traces back references to the appropriate root. Send me the code. (I have code that does this for old PCR.)
4) Build the collector with -DKEEP_BACK_PTRS, and use the backptr.h
interface to determine why objects are being retained.
****If the collector appears to be losing objects:
......@@ -54,5 +55,14 @@ void * big_realloc(void *p, size_t new_size)
6) "print *GC_find_header(p)" in dbx or gdb will print the garbage collector block header information associated with the object p (e.g. object size, etc.)
7) GC_is_marked(p) determines whether p is the base address of a marked object. Note that objects allocated since the last collection should not be marked, and that unmarked objects are reclaimed incrementally. It's usually most interesting to set a breakpoint in GC_finish_collection and then to determine how much of the damaged data structure is marked at that point.
8) Look at the tracing facility in mark.c. (Ignore this suggestion unless you are very familiar with collector internals.)
9) [From Melissa O'Neill:]
If you're using multiple threads, double check that all thread
creation goes through the GC_ wrapper functions rather than
calling the thread-creation functions themselves (e.g.,
GC_pthread_create rather than pthread_create). The gc.h header
file includes suitable preprocessor definitions to accomplish
this mapping transparently -- the question is: are you including
it in all the modules that create threads?
......@@ -31,16 +31,28 @@ To use threads, you need to abide by the following requirements:
2) You must compile the collector with -DLINUX_THREADS and -D_REENTRANT
specified in the Makefile.
3) Every file that makes thread calls should define LINUX_THREADS and
3a) Every file that makes thread calls should define LINUX_THREADS and
_REENTRANT and then include gc.h. Gc.h redefines some of the
pthread primitives as macros which also provide the collector with
information it requires.
4) Currently dlopen() is probably not safe. The collector must traverse
the list of libraries maintained by the runtime loader. That can
probably be an inconsistent state when a thread calling the loader is
is stopped for GC. (It's possible that this is fixable in the
same way it is handled for SOLARIS_THREADS, with GC_dlopen.)
3b) A new alternative to (3a) is to build the collector with
-DUSE_LD_WRAP, and to link the final program with
(for ld) --wrap read --wrap dlopen --wrap pthread_create \
--wrap pthread_join --wrap pthread_sigmask
(for gcc) -Wl,--wrap -Wl,read -Wl,--wrap -Wl,dlopen -Wl,--wrap \
-Wl,pthread_create -Wl,--wrap -Wl,pthread_join -Wl,--wrap \
-Wl,pthread_sigmask
In any case, _REENTRANT should be defined during compilation.
4) Dlopen() disables collection during its execution. (It can't run
concurrently with the collector, since the collector looks at its
data structures. It can't acquire the allocator lock, since arbitrary
user startup code may run as part of dlopen().) Under unusual
conditions, this may cause unexpected heap growth.
5) The combination of LINUX_THREADS, REDIRECT_MALLOC, and incremental
collection fails in seemingly random places. This hasn't been tracked
......@@ -48,3 +60,9 @@ To use threads, you need to abide by the following requirements:
uses malloc, and thus can presumably get SIGSEGVs while inside the
package. There is no real guarantee that signals are handled properly
at that point.
6) Thread local storage may not be viewed as part of the root set by the
collector. This probably depends on the linuxthreads version. For the
time being, any collectable memory referenced by thread local storage should
also be referenced from elsewhere, or be allocated as uncollectable.
(This is really a bug that should be fixed somehow.)
......@@ -37,13 +37,10 @@ a thread stack. If you know that you will only be running Solaris2.5
or later, it should be possible to fix this by compiling the collector
with -DSOLARIS23_MPROTECT_BUG_FIXED.
Jeremy Fitzhardinge points out that there is a problem with the dlopen
replacement, in that startup code in the library is run while the allocation
lock is held. This appears to be difficult to fix, since the collector does
look at data structures maintained by dlopen, and hence some locking is needed
around the dlopen call. Defining USE_PROC_FOR_LIBRARIES will get address
space layout information from /proc avoiding the dlopen lock. But this has
other disadvanatages, e.g. mmapped files may be scanned.
Since 5.0 alpha5, dlopen disables collection temporarily,
unless USE_PROC_FOR_LIBRARIES is defined. In some unlikely cases, this
can result in unpleasant heap growth. But it seems better than the
race/deadlock issues we had before.
If solaris_threads are used on an X86 processor with malloc redirected to
GC_malloc, it is necessary to call GC_thr_init explicitly before forking the
......
......@@ -31,7 +31,7 @@ AC_SUBST(boehm_gc_basedir)
AC_CANONICAL_HOST
AM_INIT_AUTOMAKE(boehm-gc, 5.0a4, no-define)
AM_INIT_AUTOMAKE(boehm-gc, 5.0a6, no-define)
# FIXME: We temporarily define our own version of AC_PROG_CC. This is
# copied from autoconf 2.12, but does not call AC_PROG_CC_WORKS. We
......
......@@ -43,7 +43,7 @@ AC_SUBST(boehm_gc_basedir)
AC_CANONICAL_HOST
AM_INIT_AUTOMAKE(boehm-gc, 5.0a4, no-define)
AM_INIT_AUTOMAKE(boehm-gc, 5.0a6, no-define)
# FIXME: We temporarily define our own version of AC_PROG_CC. This is
# copied from autoconf 2.12, but does not call AC_PROG_CC_WORKS. We
......
......@@ -19,6 +19,7 @@
#include <stdio.h>
#include "gc_priv.h"
GC_bool GC_use_entire_heap = 0;
/*
* Free heap blocks are kept on one of several free lists,
......@@ -229,11 +230,15 @@ int n;
GC_ASSERT(HDR(GC_hblkfreelist[index]) == hhdr);
GC_hblkfreelist[index] = hhdr -> hb_next;
} else {
PHDR(hhdr) -> hb_next = hhdr -> hb_next;
hdr *phdr;
GET_HDR(hhdr -> hb_prev, phdr);
phdr -> hb_next = hhdr -> hb_next;
}
if (0 != hhdr -> hb_next) {
hdr * nhdr;
GC_ASSERT(!IS_FORWARDING_ADDR_OR_NIL(NHDR(hhdr)));
NHDR(hhdr) -> hb_prev = hhdr -> hb_prev;
GET_HDR(hhdr -> hb_next, nhdr);
nhdr -> hb_prev = hhdr -> hb_prev;
}
}
......@@ -244,13 +249,20 @@ struct hblk * GC_free_block_ending_at(h)
struct hblk *h;
{
struct hblk * p = h - 1;
hdr * phdr = HDR(p);
hdr * phdr;
GET_HDR(p, phdr);
while (0 != phdr && IS_FORWARDING_ADDR_OR_NIL(phdr)) {
p = FORWARDED_ADDR(p,phdr);
phdr = HDR(p);
}
if (0 != phdr && HBLK_IS_FREE(phdr)) return p;
if (0 != phdr) {
if(HBLK_IS_FREE(phdr)) {
return p;
} else {
return 0;
}
}
p = GC_prev_block(h - 1);
if (0 != p) {
phdr = HDR(p);
......@@ -271,6 +283,7 @@ hdr * hhdr;
{
int index = GC_hblk_fl_from_blocks(divHBLKSZ(hhdr -> hb_sz));
struct hblk *second = GC_hblkfreelist[index];
hdr * second_hdr;
# ifdef GC_ASSERTIONS
struct hblk *next = (struct hblk *)((word)h + hhdr -> hb_sz);
hdr * nexthdr = HDR(next);
......@@ -283,7 +296,10 @@ hdr * hhdr;
GC_hblkfreelist[index] = h;
hhdr -> hb_next = second;
hhdr -> hb_prev = 0;
if (0 != second) HDR(second) -> hb_prev = h;
if (0 != second) {
GET_HDR(second, second_hdr);
second_hdr -> hb_prev = h;
}
GC_invalidate_map(hhdr);
}
......@@ -330,10 +346,10 @@ void GC_merge_unmapped(void)
for (i = 0; i <= N_HBLK_FLS; ++i) {
h = GC_hblkfreelist[i];
while (h != 0) {
hhdr = HDR(h);
GET_HDR(h, hhdr);
size = hhdr->hb_sz;
next = (struct hblk *)((word)h + size);
nexthdr = HDR(next);
GET_HDR(next, nexthdr);
/* Coalesce with successor, if possible */
if (0 != nexthdr && HBLK_IS_FREE(nexthdr)) {
nextsize = nexthdr -> hb_sz;
......@@ -398,8 +414,8 @@ int index;
GC_remove_from_fl(hhdr, index);
if (total_size == bytes) return h;
rest = (struct hblk *)((word)h + bytes);
if (!GC_install_header(rest)) return(0);
rest_hdr = HDR(rest);
rest_hdr = GC_install_header(rest);
if (0 == rest_hdr) return(0);
rest_hdr -> hb_sz = total_size - bytes;
rest_hdr -> hb_flags = 0;
# ifdef GC_ASSERTIONS
......@@ -506,16 +522,17 @@ int n;
/* search for a big enough block in free list */
hbp = GC_hblkfreelist[n];
hhdr = HDR(hbp);
for(; 0 != hbp; hbp = hhdr -> hb_next, hhdr = HDR(hbp)) {
for(; 0 != hbp; hbp = hhdr -> hb_next) {
GET_HDR(hbp, hhdr);
size_avail = hhdr->hb_sz;
if (size_avail < size_needed) continue;
# ifdef PRESERVE_LAST
if (!GC_use_entire_heap) {
if (size_avail != size_needed
&& USED_HEAP_SIZE >= GC_requested_heapsize
&& !GC_incremental && GC_should_collect()) {
continue;
}
# endif
}
/* If the next heap block is obviously better, go on. */
/* This prevents us from disassembling a single large block */
/* to get tiny blocks. */
......@@ -524,7 +541,7 @@ int n;
thishbp = hhdr -> hb_next;
if (thishbp != 0) {
thishdr = HDR(thishbp);
GET_HDR(thishbp, thishdr);
next_size = (signed_word)(thishdr -> hb_sz);
if (next_size < size_avail
&& next_size >= size_needed
......@@ -551,7 +568,8 @@ int n;
size_avail -= (ptr_t)lasthbp - (ptr_t)hbp;
thishbp = lasthbp;
if (size_avail >= size_needed) {
if (thishbp != hbp && GC_install_header(thishbp)) {
if (thishbp != hbp &&
0 != (thishdr = GC_install_header(thishbp))) {
/* Make sure it's mapped before we mangle it. */
# ifdef USE_MUNMAP
if (!IS_MAPPED(hhdr)) {
......@@ -560,7 +578,6 @@ int n;
}
# endif
/* Split the block at thishbp */
thishdr = HDR(thishbp);
GC_split_block(hbp, hhdr, thishbp, thishdr, n);
/* Advance to thishbp */
hbp = thishbp;
......@@ -598,8 +615,7 @@ int n;
GC_large_free_bytes -= total_size;
GC_remove_from_fl(hhdr, n);
for (h = hbp; h < limit; h++) {
if (h == hbp || GC_install_header(h)) {
hhdr = HDR(h);
if (h == hbp || 0 != (hhdr = GC_install_header(h))) {
(void) setup_header(
hhdr,
BYTES_TO_WORDS(HBLKSIZE - HDR_BYTES),
......@@ -686,7 +702,7 @@ hdr *hhdr, *prevhdr, *nexthdr;
signed_word size;
hhdr = HDR(hbp);
GET_HDR(hbp, hhdr);
size = hhdr->hb_sz;
size = HBLKSIZE * OBJ_SZ_TO_BLOCKS(size);
GC_remove_counts(hbp, (word)size);
......@@ -701,7 +717,7 @@ signed_word size;
GC_ASSERT(IS_MAPPED(hhdr));
GC_invalidate_map(hhdr);
next = (struct hblk *)((word)hbp + size);
nexthdr = HDR(next);
GET_HDR(next, nexthdr);
prev = GC_free_block_ending_at(hbp);
/* Coalesce with successor, if possible */
if(0 != nexthdr && HBLK_IS_FREE(nexthdr) && IS_MAPPED(nexthdr)) {
......
......@@ -70,8 +70,6 @@ int GC_full_freq = 19; /* Every 20th collection is a full */
GC_bool GC_need_full_gc = FALSE;
/* Need full GC do to heap growth. */
#define USED_HEAP_SIZE (GC_heapsize - GC_large_free_bytes)
word GC_used_heap_size_after_full = 0;
char * GC_copyright[] =
......@@ -655,7 +653,8 @@ word bytes;
if (GC_n_heap_sects >= MAX_HEAP_SECTS) {
ABORT("Too many heap sections: Increase MAXHINCR or MAX_HEAP_SECTS");
}
if (!GC_install_header(p)) {
phdr = GC_install_header(p);
if (0 == phdr) {
/* This is extremely unlikely. Can't add it. This will */
/* almost certainly result in a 0 return from the allocator, */
/* which is entirely appropriate. */
......@@ -665,7 +664,6 @@ word bytes;
GC_heap_sects[GC_n_heap_sects].hs_bytes = bytes;
GC_n_heap_sects++;
words = BYTES_TO_WORDS(bytes - HDR_BYTES);
phdr = HDR(p);
phdr -> hb_sz = words;
phdr -> hb_map = (char *)1; /* A value != GC_invalid_map */
phdr -> hb_flags = 0;
......@@ -814,6 +812,7 @@ word n;
LOCK();
if (!GC_is_initialized) GC_init_inner();
result = (int)GC_expand_hp_inner(divHBLKSZ((word)bytes));
if (result) GC_requested_heapsize += bytes;
UNLOCK();
ENABLE_SIGNALS();
return(result);
......@@ -827,7 +826,8 @@ GC_bool GC_collect_or_expand(needed_blocks, ignore_off_page)
word needed_blocks;
GC_bool ignore_off_page;
{
if (!GC_incremental && !GC_dont_gc && GC_should_collect()) {
if (!GC_incremental && !GC_dont_gc &&
(GC_dont_expand && GC_words_allocd > 0 || GC_should_collect())) {
GC_notify_full_gc();
GC_gcollect_inner();
} else {
......
# This is BROKEN on a 21264 running gcc, and probably in other cases.
# The compiler may spill pointers to fp registers, and this code doesn't
# scan those.
# define call_push(x) \
lda $16, 0(x); /* copy x to first argument register */ \
jsr $26, GC_push_one; /* call GC_push_one, ret addr in $26 */ \
......
......@@ -2168,9 +2168,11 @@ esac
machdep=
case "$host" in
alpha*-*-*)
machdep="alpha_mach_dep.lo"
;;
# alpha_mach_dep.s assumes that pointers are not saved in fp registers.
# Gcc on a 21264 can spill pointers to fp registers. Oops.
# alpha*-*-*)
# machdep="alpha_mach_dep.lo"
# ;;
mipstx39-*-elf*)
machdep="mips_ultrix_mach_dep.lo"
cat >> confdefs.h <<\EOF
......
......@@ -134,9 +134,11 @@ AC_SUBST(CXXINCLUDES)
machdep=
case "$host" in
alpha*-*-*)
machdep="alpha_mach_dep.lo"
;;
# alpha_mach_dep.s assumes that pointers are not saved in fp registers.
# Gcc on a 21264 can spill pointers to fp registers. Oops.
# alpha*-*-*)
# machdep="alpha_mach_dep.lo"
# ;;
mipstx39-*-elf*)
machdep="mips_ultrix_mach_dep.lo"
AC_DEFINE(STACKBASE, __stackbase)
......
......@@ -2,6 +2,7 @@
* Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
* Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved.
* Copyright (c) 1997 by Silicon Graphics. All rights reserved.
* Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved.
*
* THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
* OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
......@@ -12,64 +13,14 @@
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*/
# define I_HIDE_POINTERS
# include "gc_priv.h"
# ifdef KEEP_BACK_PTRS
# include "backptr.h"
# endif
#include "dbg_mlc.h"
void GC_default_print_heap_obj_proc();
GC_API void GC_register_finalizer_no_order
GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd,
GC_finalization_proc *ofn, GC_PTR *ocd));
/* Do we want to and know how to save the call stack at the time of */
/* an allocation? How much space do we want to use in each object? */
# define START_FLAG ((word)0xfedcedcb)
# define END_FLAG ((word)0xbcdecdef)
/* Stored both one past the end of user object, and one before */
/* the end of the object as seen by the allocator. */
/* Object header */
typedef struct {
# ifdef KEEP_BACK_PTRS
ptr_t oh_back_ptr;
# define MARKED_FOR_FINALIZATION (ptr_t)(-1)
/* Object was marked because it is finalizable. */
# ifdef ALIGN_DOUBLE
word oh_dummy;
# endif
# endif
char * oh_string; /* object descriptor string */
word oh_int; /* object descriptor integers */
# ifdef NEED_CALLINFO
struct callinfo oh_ci[NFRAMES];
# endif
word oh_sz; /* Original malloc arg. */
word oh_sf; /* start flag */
} oh;
/* The size of the above structure is assumed not to dealign things, */
/* and to be a multiple of the word length. */
#define DEBUG_BYTES (sizeof (oh) + sizeof (word))
#undef ROUNDED_UP_WORDS
#define ROUNDED_UP_WORDS(n) BYTES_TO_WORDS((n) + WORDS_TO_BYTES(1) - 1)
#ifdef SAVE_CALL_CHAIN
# define ADD_CALL_CHAIN(base, ra) GC_save_callers(((oh *)(base)) -> oh_ci)
# define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci)
#else
# ifdef GC_ADD_CALLER
# define ADD_CALL_CHAIN(base, ra) ((oh *)(base)) -> oh_ci[0].ci_pc = (ra)
# define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci)
# else
# define ADD_CALL_CHAIN(base, ra)
# define PRINT_CALL_CHAIN(base)
# endif
#endif
/* Check whether object with base pointer p has debugging info */
/* p is assumed to point to a legitimate object in our part */
......@@ -116,7 +67,7 @@ ptr_t p;
/* Store information about the object referencing dest in *base_p */
/* and *offset_p. */
/* source is root ==> *base_p = 0, *offset_p = address */
/* source is root ==> *base_p = address, *offset_p = 0 */
/* source is heap object ==> *base_p != 0, *offset_p = offset */
/* Returns 1 on success, 0 if source couldn't be determined. */
/* Dest can be any address within a heap object. */
......@@ -128,6 +79,7 @@ ptr_t p;
if (!GC_has_debug_info((ptr_t) hdr)) return GC_NO_SPACE;
bp = hdr -> oh_back_ptr;
if (MARKED_FOR_FINALIZATION == bp) return GC_FINALIZER_REFD;
if (MARKED_FROM_REGISTER == bp) return GC_REFD_FROM_REG;
if (0 == bp) return GC_UNREFERENCED;
bp = REVEAL_POINTER(bp);
bp_base = GC_base(bp);
......@@ -177,18 +129,15 @@ ptr_t p;
}
}
/* Force a garbage collection and generate a backtrace from a */
/* random heap address. */
void GC_generate_random_backtrace(void)
/* Print back trace for p */
void GC_print_backtrace(void *p)
{
void * current;
void *current = p;
int i;
void * base;
size_t offset;
GC_ref_kind source;
GC_gcollect();
current = GC_generate_random_valid_address();
GC_printf1("Chose address 0x%lx in object\n", (unsigned long)current);
size_t offset;
void *base;
GC_print_heap_obj(GC_base(current));
GC_err_printf0("\n");
for (i = 0; ; ++i) {
......@@ -207,6 +156,9 @@ ptr_t p;
case GC_REFD_FROM_ROOT:
GC_err_printf1("root at 0x%lx\n", (unsigned long)base);
goto out;
case GC_REFD_FROM_REG:
GC_err_printf0("root in register\n");
goto out;
case GC_FINALIZER_REFD:
GC_err_printf0("list of finalizable objects\n");
goto out;
......@@ -221,6 +173,17 @@ ptr_t p;
}
out:;
}
/* Force a garbage collection and generate a backtrace from a */
/* random heap address. */
void GC_generate_random_backtrace(void)
{
void * current;
GC_gcollect();
current = GC_generate_random_valid_address();
GC_printf1("Chose address 0x%lx in object\n", (unsigned long)current);
GC_print_backtrace(current);
}
#endif /* KEEP_BACK_PTRS */
......@@ -342,16 +305,8 @@ void GC_start_debugging()
GC_register_displacement((word)sizeof(oh) + offset);
}
# ifdef GC_ADD_CALLER
# define EXTRA_ARGS word ra, CONST char * s, int i
# define OPT_RA ra,
# else
# define EXTRA_ARGS CONST char * s, int i
# define OPT_RA
# endif
# ifdef __STDC__
GC_PTR GC_debug_malloc(size_t lb, EXTRA_ARGS)
GC_PTR GC_debug_malloc(size_t lb, GC_EXTRA_PARAMS)
# else
GC_PTR GC_debug_malloc(lb, s, i)
size_t lb;
......@@ -379,7 +334,7 @@ void GC_start_debugging()
}
# ifdef __STDC__
GC_PTR GC_debug_generic_malloc(size_t lb, int k, EXTRA_ARGS)
GC_PTR GC_debug_generic_malloc(size_t lb, int k, GC_EXTRA_PARAMS)
# else
GC_PTR GC_debug_malloc(lb, k, s, i)
size_t lb;
......@@ -409,7 +364,7 @@ void GC_start_debugging()
#ifdef STUBBORN_ALLOC
# ifdef __STDC__
GC_PTR GC_debug_malloc_stubborn(size_t lb, EXTRA_ARGS)
GC_PTR GC_debug_malloc_stubborn(size_t lb, GC_EXTRA_PARAMS)
# else
GC_PTR GC_debug_malloc_stubborn(lb, s, i)
size_t lb;
......@@ -476,7 +431,7 @@ GC_PTR p;
#endif /* STUBBORN_ALLOC */
# ifdef __STDC__
GC_PTR GC_debug_malloc_atomic(size_t lb, EXTRA_ARGS)
GC_PTR GC_debug_malloc_atomic(size_t lb, GC_EXTRA_PARAMS)
# else
GC_PTR GC_debug_malloc_atomic(lb, s, i)
size_t lb;
......@@ -501,7 +456,7 @@ GC_PTR p;
}
# ifdef __STDC__
GC_PTR GC_debug_malloc_uncollectable(size_t lb, EXTRA_ARGS)
GC_PTR GC_debug_malloc_uncollectable(size_t lb, GC_EXTRA_PARAMS)
# else
GC_PTR GC_debug_malloc_uncollectable(lb, s, i)
size_t lb;
......@@ -527,7 +482,7 @@ GC_PTR p;
#ifdef ATOMIC_UNCOLLECTABLE
# ifdef __STDC__
GC_PTR GC_debug_malloc_atomic_uncollectable(size_t lb, EXTRA_ARGS)
GC_PTR GC_debug_malloc_atomic_uncollectable(size_t lb, GC_EXTRA_PARAMS)
# else
GC_PTR GC_debug_malloc_atomic_uncollectable(lb, s, i)
size_t lb;
......@@ -607,7 +562,7 @@ GC_PTR p;
}
# ifdef __STDC__
GC_PTR GC_debug_realloc(GC_PTR p, size_t lb, EXTRA_ARGS)
GC_PTR GC_debug_realloc(GC_PTR p, size_t lb, GC_EXTRA_PARAMS)
# else
GC_PTR GC_debug_realloc(p, lb, s, i)
GC_PTR p;
......@@ -810,7 +765,7 @@ struct closure {
GC_PTR cd, GC_finalization_proc *ofn,
GC_PTR *ocd)
# else
void GC_debug_register_finalizer_no_order
void GC_debug_register_finalizer_ignore_self
(obj, fn, cd, ofn, ocd)
GC_PTR obj;
GC_finalization_proc fn;
......@@ -822,9 +777,9 @@ struct closure {
ptr_t base = GC_base(obj);
if (0 == base || (ptr_t)obj - base != sizeof(oh)) {
GC_err_printf1(
"GC_register_finalizer_no_order called with non-base-pointer 0x%lx\n",
"GC_register_finalizer_ignore_self called with non-base-pointer 0x%lx\n",
obj);
}
GC_register_finalizer_no_order(base, GC_debug_invoke_finalizer,
GC_register_finalizer_ignore_self(base, GC_debug_invoke_finalizer,
GC_make_closure(fn,cd), ofn, ocd);
}
......@@ -32,7 +32,9 @@
#include "gc_priv.h"
/* BTL: avoid circular redefinition of dlopen if SOLARIS_THREADS defined */
# if (defined(SOLARIS_THREADS) || defined(LINUX_THREADS)) && defined(dlopen)
# if (defined(LINUX_THREADS) || defined(SOLARIS_THREADS) \
|| defined(HPUX_THREADS) || defined(IRIX_THREADS)) && defined(dlopen) \
&& !defined(USE_LD_WRAP)
/* To support threads in Solaris, gc.h interposes on dlopen by */
/* defining "dlopen" to be "GC_dlopen", which is implemented below. */
/* However, both GC_FirstDLOpenedLinkMap() and GC_dlopen() use the */
......@@ -159,37 +161,77 @@ static ptr_t GC_first_common()
#endif /* SUNOS4 ... */
# if defined(SUNOS4) || defined(SUNOS5DL)
/* Add dynamic library data sections to the root set. */
# if !defined(PCR) && !defined(SOLARIS_THREADS) && defined(THREADS)
# ifndef SRC_M3
--> fix mutual exclusion with dlopen
# endif /* We assume M3 programs don't call dlopen for now */
# endif
# if defined(LINUX_THREADS) || defined(SOLARIS_THREADS) \
|| defined(HPUX_THREADS) || defined(IRIX_THREADS)
/* Make sure we're not in the middle of a collection, and make */
/* sure we don't start any. Returns previous value of GC_dont_gc. */
/* This is invoked prior to a dlopen call to avoid synchronization */
/* issues. We can't just acquire the allocation lock, since startup */
/* code in dlopen may try to allocate. */
/* This solution risks heap growth in the presence of many dlopen */
/* calls in either a multithreaded environment, or if the library */
/* initialization code allocates substantial amounts of GC'ed memory. */
/* But I don't know of a better solution. */
/* This can still deadlock if the client explicitly starts a GC */
/* during the dlopen. He shouldn't do that. */
static GC_bool disable_gc_for_dlopen()
{
GC_bool result;
LOCK();
result = GC_dont_gc;
while (GC_incremental && GC_collection_in_progress()) {
GC_collect_a_little_inner(1000);
}
GC_dont_gc = TRUE;
UNLOCK();
return(result);
}
# ifdef SOLARIS_THREADS
/* Redefine dlopen to guarantee mutual exclusion with */
/* GC_register_dynamic_libraries. */
/* assumes that dlopen doesn't need to call GC_malloc */
/* and friends. */
# include <thread.h>
# include <synch.h>
/* Should probably happen for other operating systems, too. */
void * GC_dlopen(const char *path, int mode)
#include <dlfcn.h>
#ifdef USE_LD_WRAP
void * __wrap_dlopen(const char *path, int mode)
#else
void * GC_dlopen(path, mode)
GC_CONST char * path;
int mode;
#endif
{
void * result;
GC_bool dont_gc_save;
# ifndef USE_PROC_FOR_LIBRARIES
mutex_lock(&GC_allocate_ml);
dont_gc_save = disable_gc_for_dlopen();
# endif
# ifdef USE_LD_WRAP
result = __real_dlopen(path, mode);
# else
result = dlopen(path, mode);
# endif
result = dlopen(path, mode);
# ifndef USE_PROC_FOR_LIBRARIES
mutex_unlock(&GC_allocate_ml);
GC_dont_gc = dont_gc_save;
# endif
return(result);
}
# endif /* SOLARIS_THREADS */
/* BTL: added to fix circular dlopen definition if SOLARIS_THREADS defined */
# if defined(GC_must_restore_redefined_dlopen)
# define dlopen GC_dlopen
# endif
# if defined(SUNOS4) || defined(SUNOS5DL)
/* Add dynamic library data sections to the root set. */
# if !defined(PCR) && !defined(SOLARIS_THREADS) && defined(THREADS)
# ifndef SRC_M3
--> fix mutual exclusion with dlopen
# endif /* We assume M3 programs don't call dlopen for now */
# endif
# ifndef USE_PROC_FOR_LIBRARIES
void GC_register_dynamic_libraries()
{
......@@ -255,25 +297,6 @@ void GC_register_dynamic_libraries()
# endif /* !USE_PROC ... */
# endif /* SUNOS */
#ifdef LINUX_THREADS
#include <dlfcn.h>
void * GC_dlopen(const char *path, int mode)
{
void * result;
LOCK();
result = dlopen(path, mode);
UNLOCK();
return(result);
}
#endif /* LINUX_THREADS */
/* BTL: added to fix circular dlopen definition if SOLARIS_THREADS defined */
#if defined(GC_must_restore_redefined_dlopen)
# define dlopen GC_dlopen
#endif
#if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF)
/* Dynamic loading code for Linux running ELF. Somewhat tested on
......
......@@ -694,6 +694,14 @@ GC_API void GC_finalize_all()
}
#endif
/* Returns true if it is worth calling GC_invoke_finalizers. (Useful if */
/* finalizers can only be called from some kind of `safe state' and */
/* getting into that safe state is expensive.) */
int GC_should_invoke_finalizers GC_PROTO((void))
{
return GC_finalize_now != 0;
}
/* Invoke finalizers for all objects that are ready to be finalized. */
/* Should be called without allocation lock. */
int GC_invoke_finalizers()
......
/*
* Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
* Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved.
* Copyright 1996 by Silicon Graphics. All rights reserved.
* Copyright 1996-1999 by Silicon Graphics. All rights reserved.
* Copyright 1999 by Hewlett-Packard Company. All rights reserved.
*
* THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
* OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
......@@ -35,6 +36,14 @@
#include "libgc_globals.h"
#endif
#if defined(__MINGW32__) && defined(WIN32_THREADS)
# ifdef GC_BUILD
# define GC_API __declspec(dllexport)
# else
# define GC_API __declspec(dllimport)
# endif
#endif
#if defined(_MSC_VER) && defined(_DLL)
# ifdef GC_BUILD
# define GC_API __declspec(dllexport)
......@@ -130,6 +139,17 @@ GC_API int GC_dont_expand;
/* Dont expand heap unless explicitly requested */
/* or forced to. */
GC_API int GC_use_entire_heap;
/* Causes the nonincremental collector to use the */
/* entire heap before collecting. This was the only */
/* option for GC versions < 5.0. This sometimes */
/* results in more large block fragmentation, since */
/* very larg blocks will tend to get broken up */
/* during each GC cycle. It is likely to result in a */
/* larger working set, but lower collection */
/* frequencies, and hence fewer instructions executed */
/* in the collector. */
GC_API int GC_full_freq; /* Number of partial collections between */
/* full collections. Matters only if */
/* GC_incremental is set. */
......@@ -352,11 +372,11 @@ GC_API GC_PTR GC_malloc_atomic_ignore_off_page GC_PROTO((size_t lb));
#ifdef GC_ADD_CALLER
# define GC_EXTRAS GC_RETURN_ADDR, __FILE__, __LINE__
# define GC_EXTRA_PARAMS GC_word ra, GC_CONST char * descr_string,
int descr_int
# define GC_EXTRA_PARAMS GC_word ra, GC_CONST char * s,
int i
#else
# define GC_EXTRAS __FILE__, __LINE__
# define GC_EXTRA_PARAMS GC_CONST char * descr_string, int descr_int
# define GC_EXTRA_PARAMS GC_CONST char * s, int i
#endif
/* Debugging (annotated) allocation. GC_gcollect will check */
......@@ -494,6 +514,7 @@ GC_API void GC_debug_register_finalizer_no_order
GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd,
GC_finalization_proc *ofn, GC_PTR *ocd));
/* The following routine may be used to break cycles between */
/* finalizable objects, thus causing cyclic finalizable */
/* objects to be finalized in the correct order. Standard */
......@@ -550,6 +571,9 @@ GC_API int GC_unregister_disappearing_link GC_PROTO((GC_PTR * /* link */));
GC_API GC_PTR GC_make_closure GC_PROTO((GC_finalization_proc fn, GC_PTR data));
GC_API void GC_debug_invoke_finalizer GC_PROTO((GC_PTR obj, GC_PTR data));
/* Returns !=0 if GC_invoke_finalizers has something to do. */
GC_API int GC_should_invoke_finalizers GC_PROTO((void));
GC_API int GC_invoke_finalizers GC_PROTO((void));
/* Run finalizers for all objects that are ready to */
/* be finalized. Return the number of finalizers */
......@@ -712,12 +736,9 @@ GC_API void (*GC_is_visible_print_proc)
# endif /* SOLARIS_THREADS */
#if defined(LINUX_THREADS)
void * GC_dlopen(const char *path, int mode);
# define dlopen GC_dlopen
#endif
#if defined(IRIX_THREADS) || defined(LINUX_THREADS) || defined(HPUX_THREADS)
#if !defined(USE_LD_WRAP) && \
(defined(IRIX_THREADS) || defined(LINUX_THREADS) || defined(HPUX_THREADS))
/* We treat these similarly. */
# include <pthread.h>
# include <signal.h>
......@@ -731,8 +752,9 @@ GC_API void (*GC_is_visible_print_proc)
# define pthread_create GC_pthread_create
# define pthread_sigmask GC_pthread_sigmask
# define pthread_join GC_pthread_join
# define dlopen GC_dlopen
#endif /* IRIX_THREADS || LINUX_THREADS */
#endif /* xxxxx_THREADS */
# if defined(PCR) || defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || \
defined(IRIX_THREADS) || defined(LINUX_THREADS) || \
......
......@@ -16,12 +16,11 @@ the code was modified is included with the above copyright notice.
C++ Interface to the Boehm Collector
John R. Ellis and Jesse Hull
Last modified on Mon Jul 24 15:43:42 PDT 1995 by ellis
This interface provides access to the Boehm collector. It provides
basic facilities similar to those described in "Safe, Efficient
Garbage Collection for C++", by John R. Elis and David L. Detlefs
(ftp.parc.xerox.com:/pub/ellis/gc).
(ftp://ftp.parc.xerox.com/pub/ellis/gc).
All heap-allocated objects are either "collectable" or
"uncollectable". Programs must explicitly delete uncollectable
......@@ -38,7 +37,7 @@ Objects derived from class "gc" are collectable. For example:
A* a = new A; // a is collectable.
Collectable instances of non-class types can be allocated using the GC
placement:
(or UseGC) placement:
typedef int A[ 10 ];
A* a = new (GC) A;
......@@ -124,6 +123,12 @@ invoked using the ANSI-conforming syntax t->~T(). If you're using
cfront 3.0, you'll have to comment out the class gc_cleanup, which
uses explicit invocation.
5. GC name conflicts:
Many other systems seem to use the identifier "GC" as an abbreviation
for "Graphics Context". Since version 5.0, GC placement has been replaced
by UseGC. GC is an alias for UseGC, unless GC_NAME_CONFLICT is defined.
****************************************************************************/
#include "gc.h"
......@@ -138,7 +143,11 @@ uses explicit invocation.
# define OPERATOR_NEW_ARRAY
#endif
enum GCPlacement {GC, NoGC, PointerFreeGC};
enum GCPlacement {UseGC,
#ifndef GC_NAME_CONFLICT
GC=UseGC,
#endif
NoGC, PointerFreeGC};
class gc {public:
inline void* operator new( size_t size );
......@@ -211,7 +220,7 @@ inline void* gc::operator new( size_t size ) {
return GC_MALLOC( size );}
inline void* gc::operator new( size_t size, GCPlacement gcp ) {
if (gcp == GC)
if (gcp == UseGC)
return GC_MALLOC( size );
else if (gcp == PointerFreeGC)
return GC_MALLOC_ATOMIC( size );
......@@ -261,7 +270,7 @@ inline void* operator new(
{
void* obj;
if (gcp == GC) {
if (gcp == UseGC) {
obj = GC_MALLOC( size );
if (cleanup != 0)
GC_REGISTER_FINALIZER_IGNORE_SELF(
......
......@@ -24,6 +24,17 @@ typedef struct hblkhdr hdr;
* The 2 level tree data structure that is used to find block headers.
* If there are more than 32 bits in a pointer, the top level is a hash
* table.
*
* This defines HDR, GET_HDR, and SET_HDR, the main macros used to
* retrieve and set object headers. We also define some variants to
* retrieve 2 unrelated headers in interleaved fashion. This
* slightly improves scheduling.
*
* Since 5.0 alpha 5, we can also take advantage of a header lookup
* cache. This is a locally declared direct mapped cache, used inside
* the marker. The HC_GET_HDR and HC_GET_HDR2 macros use and maintain this
* cache. Assuming we get reasonable hit rates, this shaves a few
* memory references from each pointer validation.
*/
# if CPP_WORDSZ > 32
......@@ -45,6 +56,127 @@ typedef struct hblkhdr hdr;
# define TOP_SZ (1 << LOG_TOP_SZ)
# define BOTTOM_SZ (1 << LOG_BOTTOM_SZ)
#ifndef SMALL_CONFIG
# define USE_HDR_CACHE
#endif
/* #define COUNT_HDR_CACHE_HITS */
extern hdr * GC_invalid_header; /* header for an imaginary block */
/* containing no objects. */
/* Check whether p and corresponding hhdr point to long or invalid */
/* object. If so, advance them to */
/* beginning of block, or set hhdr to GC_invalid_header. */
#define ADVANCE(p, hhdr, source) \
if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { \
p = GC_FIND_START(p, hhdr, (word)source); \
if (p == 0) { \
hhdr = GC_invalid_header; \
} else { \
hhdr = GC_find_header(p); \
} \
}
#ifdef USE_HDR_CACHE
# ifdef COUNT_HDR_CACHE_HITS
extern word GC_hdr_cache_hits;
extern word GC_hdr_cache_misses;
# define HC_HIT() ++GC_hdr_cache_hits
# define HC_MISS() ++GC_hdr_cache_misses
# else
# define HC_HIT()
# define HC_MISS()
# endif
typedef struct hce {
word block_addr; /* right shifted by LOG_HBLKSIZE */
hdr * hce_hdr;
} hdr_cache_entry;
# define HDR_CACHE_SIZE 8 /* power of 2 */
# define DECLARE_HDR_CACHE \
hdr_cache_entry hdr_cache[HDR_CACHE_SIZE]
# define INIT_HDR_CACHE BZERO(hdr_cache, sizeof(hdr_cache));
# define HCE(h) hdr_cache + (((word)(h) >> LOG_HBLKSIZE) & (HDR_CACHE_SIZE-1))
# define HCE_VALID_FOR(hce,h) ((hce) -> block_addr == \
((word)(h) >> LOG_HBLKSIZE))
# define HCE_HDR(h) ((hce) -> hce_hdr)
/* Analogous to GET_HDR, except that in the case of large objects, it */
/* Returns the header for the object beginning, and updates p. */
/* Returns &GC_bad_header instead of 0. All of this saves a branch */
/* in the fast path. */
# define HC_GET_HDR(p, hhdr, source) \
{ \
hdr_cache_entry * hce = HCE(p); \
if (HCE_VALID_FOR(hce, p)) { \
HC_HIT(); \
hhdr = hce -> hce_hdr; \
} else { \
HC_MISS(); \
GET_HDR(p, hhdr); \
ADVANCE(p, hhdr, source); \
hce -> block_addr = (word)(p) >> LOG_HBLKSIZE; \
hce -> hce_hdr = hhdr; \
} \
}
# define HC_GET_HDR2(p1, hhdr1, source1, p2, hhdr2, source2) \
{ \
hdr_cache_entry * hce1 = HCE(p1); \
hdr_cache_entry * hce2 = HCE(p2); \
if (HCE_VALID_FOR(hce1, p1)) { \
HC_HIT(); \
hhdr1 = hce1 -> hce_hdr; \
} else { \
HC_MISS(); \
GET_HDR(p1, hhdr1); \
ADVANCE(p1, hhdr1, source1); \
hce1 -> block_addr = (word)(p1) >> LOG_HBLKSIZE; \
hce1 -> hce_hdr = hhdr1; \
} \
if (HCE_VALID_FOR(hce2, p2)) { \
HC_HIT(); \
hhdr2 = hce2 -> hce_hdr; \
} else { \
HC_MISS(); \
GET_HDR(p2, hhdr2); \
ADVANCE(p2, hhdr2, source2); \
hce2 -> block_addr = (word)(p2) >> LOG_HBLKSIZE; \
hce2 -> hce_hdr = hhdr2; \
} \
}
#else /* !USE_HDR_CACHE */
# define DECLARE_HDR_CACHE
# define INIT_HDR_CACHE
# define HC_GET_HDR(p, hhdr, source) \
{ \
GET_HDR(p, hhdr); \
ADVANCE(p, hhdr, source); \
}
# define HC_GET_HDR2(p1, hhdr1, source1, p2, hhdr2, source2) \
{ \
GET_HDR2(p1, hhdr1, p2, hhdr2); \
ADVANCE(p1, hhdr1, source1); \
ADVANCE(p2, hhdr2, source2); \
}
#endif
typedef struct bi {
hdr * index[BOTTOM_SZ];
/*
......@@ -97,6 +229,8 @@ typedef struct bi {
# define GET_HDR(p, hhdr) (hhdr) = HDR(p)
# define SET_HDR(p, hhdr) HDR_INNER(p) = (hhdr)
# define GET_HDR_ADDR(p, ha) (ha) = &(HDR_INNER(p))
# define GET_HDR2(p1, hhdr1, p2, hhdr2) \
{ GET_HDR(p1, hhdr1); GET_HDR(p2, hhdr2); }
# else /* hash */
/* Hash function for tree top level */
# define TL_HASH(hi) ((hi) & (TOP_SZ - 1))
......@@ -123,6 +257,40 @@ typedef struct bi {
# define SET_HDR(p, hhdr) { register hdr ** _ha; GET_HDR_ADDR(p, _ha); \
*_ha = (hhdr); }
# define HDR(p) GC_find_header((ptr_t)(p))
/* And some interleaved versions for two pointers at once. */
/* This hopefully helps scheduling on processors like IA64. */
# define GET_BI2(p1, bottom_indx1, p2, bottom_indx2) \
{ \
register word hi1 = \
(word)(p1) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE); \
register word hi2 = \
(word)(p2) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE); \
register bottom_index * _bi1 = GC_top_index[TL_HASH(hi1)]; \
register bottom_index * _bi2 = GC_top_index[TL_HASH(hi2)]; \
\
while (_bi1 -> key != hi1 && _bi1 != GC_all_nils) \
_bi1 = _bi1 -> hash_link; \
while (_bi2 -> key != hi2 && _bi2 != GC_all_nils) \
_bi2 = _bi2 -> hash_link; \
(bottom_indx1) = _bi1; \
(bottom_indx2) = _bi2; \
}
# define GET_HDR_ADDR2(p1, ha1, p2, ha2) \
{ \
register bottom_index * bi1; \
register bottom_index * bi2; \
\
GET_BI2(p1, bi1, p2, bi2); \
(ha1) = &(HDR_FROM_BI(bi1, p1)); \
(ha2) = &(HDR_FROM_BI(bi2, p2)); \
}
# define GET_HDR2(p1, hhdr1, p2, hhdr2) \
{ register hdr ** _ha1; \
register hdr ** _ha2; \
GET_HDR_ADDR2(p1, _ha1, p2, _ha2); \
(hhdr1) = *_ha1; \
(hhdr2) = *_ha2; \
}
# endif
/* Is the result a forwarding address to someplace closer to the */
......
......@@ -20,6 +20,10 @@
#ifndef GC_MARK_H
# define GC_MARK_H
# ifdef KEEP_BACK_PTRS
# include "dbg_mlc.h"
# endif
/* A client supplied mark procedure. Returns new mark stack pointer. */
/* Primary effect should be to push new entries on the mark stack. */
/* Mark stack pointer values are passed and returned explicitly. */
......@@ -41,8 +45,10 @@
/* The real declarations of the following are in gc_priv.h, so that */
/* we can avoid scanning the following table. */
/*
typedef struct ms_entry * (*mark_proc)( word * addr, mark_stack_ptr,
mark_stack_limit, env );
typedef struct ms_entry * (*mark_proc)( word * addr,
struct ms_entry *mark_stack_ptr,
struct ms_entry *mark_stack_limit,
word env );
# define LOG_MAX_MARK_PROCS 6
# define MAX_MARK_PROCS (1 << LOG_MAX_MARK_PROCS)
......@@ -51,6 +57,12 @@ extern mark_proc GC_mark_procs[MAX_MARK_PROCS];
extern word GC_n_mark_procs;
/* In a few cases it's necessary to assign statically known indices to */
/* certain mark procs. Thus we reserve a few for well known clients. */
/* (This is necessary if mark descriptors are compiler generated.) */
#define GC_RESERVED_MARK_PROCS 8
# define GCJ_RESERVED_MARK_PROC_INDEX 0
/* Object descriptors on mark stack or in objects. Low order two */
/* bits are tags distinguishing among the following 4 possibilities */
/* for the high order 30 bits. */
......@@ -84,6 +96,13 @@ extern word GC_n_mark_procs;
#define DS_PER_OBJECT 3 /* The real descriptor is at the */
/* byte displacement from the beginning of the */
/* object given by descr & ~DS_TAGS */
/* If the descriptor is negative, the real */
/* descriptor is at (*<object_start>) - */
/* (descr & ~DS_TAGS) - INDIR_PER_OBJ_BIAS */
/* The latter alternative can be used if each */
/* object contains a type descriptor in the */
/* first word. */
#define INDIR_PER_OBJ_BIAS 0x10
typedef struct ms_entry {
word * mse_start; /* First word of object */
......@@ -98,7 +117,7 @@ extern mse * GC_mark_stack_top;
extern mse * GC_mark_stack;
word GC_find_start();
ptr_t GC_find_start();
mse * GC_signal_mark_stack_overflow();
......@@ -144,16 +163,60 @@ mse * GC_signal_mark_stack_overflow();
# define PUSH_CONTENTS(current, mark_stack_top, mark_stack_limit, \
source, exit_label) \
{ \
register int displ; /* Displacement in block; first bytes, then words */ \
register hdr * hhdr; \
register map_entry_type map_entry; \
\
GET_HDR(current,hhdr); \
if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { \
current = GC_FIND_START(current, hhdr, (word)source); \
if (current == 0) goto exit_label; \
hhdr = HDR(current); \
hdr * my_hhdr; \
ptr_t my_current = current; \
\
GET_HDR(my_current, my_hhdr); \
if (IS_FORWARDING_ADDR_OR_NIL(my_hhdr)) { \
my_current = GC_FIND_START(my_current, my_hhdr, (word)source); \
if (my_current == 0) goto exit_label; \
my_hhdr = GC_find_header(my_current); \
} \
PUSH_CONTENTS_HDR(my_current, mark_stack_top, mark_stack_limit, \
source, exit_label, my_hhdr); \
exit_label: ; \
}
/* As above, but use header cache for header lookup. */
# define HC_PUSH_CONTENTS(current, mark_stack_top, mark_stack_limit, \
source, exit_label) \
{ \
hdr * my_hhdr; \
ptr_t my_current = current; \
\
HC_GET_HDR(my_current, my_hhdr, source); \
PUSH_CONTENTS_HDR(my_current, mark_stack_top, mark_stack_limit, \
source, exit_label, my_hhdr); \
exit_label: ; \
}
/* As above, but deal with two pointers in interleaved fashion. */
# define HC_PUSH_CONTENTS2(current1, current2, mark_stack_top, \
mark_stack_limit, \
source1, source2, exit_label1, exit_label2) \
{ \
hdr * hhdr1; \
ptr_t my_current1 = current1; \
hdr * hhdr2; \
ptr_t my_current2 = current2; \
\
HC_GET_HDR2(my_current1, hhdr1, source1, my_current2, hhdr2, source2); \
PUSH_CONTENTS_HDR(my_current1, mark_stack_top, mark_stack_limit, \
source1, exit_label1, hhdr1); \
exit_label1: ; \
if (0 != hhdr2) { \
PUSH_CONTENTS_HDR(my_current2, mark_stack_top, mark_stack_limit, \
source2, exit_label2, hhdr2); \
} \
exit_label2: ; \
}
# define PUSH_CONTENTS_HDR(current, mark_stack_top, mark_stack_limit, \
source, exit_label, hhdr) \
{ \
int displ; /* Displacement in block; first bytes, then words */ \
map_entry_type map_entry; \
\
displ = HBLKDISPL(current); \
map_entry = MAP_ENTRY((hhdr -> hb_map), displ); \
if (map_entry == OBJ_INVALID) { \
......@@ -177,10 +240,9 @@ mse * GC_signal_mark_stack_overflow();
} \
PUSH_OBJ(((word *)(HBLKPTR(current)) + displ), hhdr, \
mark_stack_top, mark_stack_limit) \
exit_label: ; \
}
#ifdef PRINT_BLACK_LIST
#if defined(PRINT_BLACK_LIST) || defined(KEEP_BACK_PTRS)
# define PUSH_ONE_CHECKED(p, ip, source) \
GC_push_one_checked(p, ip, (ptr_t)(source))
#else
......
......@@ -61,6 +61,7 @@ GC_API GC_PTR GC_malloc_explicitly_typed
GC_PROTO((size_t size_in_bytes, GC_descr d));
/* Allocate an object whose layout is described by d. */
/* The resulting object MAY NOT BE PASSED TO REALLOC. */
/* The returned object is cleared. */
GC_API GC_PTR GC_malloc_explicitly_typed_ignore_off_page
GC_PROTO((size_t size_in_bytes, GC_descr d));
......@@ -75,6 +76,7 @@ GC_API GC_PTR GC_calloc_explicitly_typed
/* alignment required for pointers. E.g. on a 32-bit */
/* machine with 16-bit aligned pointers, size_in_bytes */
/* must be a multiple of 2. */
/* Returned object is cleared. */
#ifdef GC_DEBUG
# define GC_MALLOC_EXPLICTLY_TYPED(bytes, d) GC_MALLOC(bytes)
......
name gc_watcom
.386p
extrn _edata : byte ; end of DATA (start of BSS)
extrn _end : byte ; end of BSS (start of STACK)
extrn __nullarea : word
extrn "C",_STACKLOW : dword
extrn "C",_STACKTOP : dword
DGROUP group _DATA
_DATA segment dword public 'DATA'
_DATA ends
_TEXT segment para public use32 'CODE'
assume cs:_TEXT, ds:DGROUP, ss:DGROUP
public Get_DATASTART
align 4
Get_DATASTART proc near
mov eax,offset DGROUP:__nullarea
ret
Get_DATASTART endp
public Get_DATAEND
align 4
Get_DATAEND proc near
mov eax,offset DGROUP:_end
ret
Get_DATAEND endp
public Get_STACKBOTTOM
align 4
Get_STACKBOTTOM proc near
mov eax,_STACKTOP
ret
Get_STACKBOTTOM endp
_TEXT ends
end
......@@ -50,10 +50,8 @@ ptr_t h;
static ptr_t scratch_free_ptr = 0;
ptr_t GC_scratch_end_ptr = 0;
ptr_t GC_scratch_last_end_ptr = 0;
/* End point of last obtained scratch area */
/* GC_scratch_last_end_ptr is end point of last obtained scratch area. */
/* GC_scratch_end_ptr is end point of current scratch area. */
ptr_t GC_scratch_alloc(bytes)
register word bytes;
......@@ -128,6 +126,13 @@ hdr * hhdr;
hhdr -> hb_next = (struct hblk *) hdr_free_list;
hdr_free_list = hhdr;
}
hdr * GC_invalid_header;
#ifdef USE_HDR_CACHE
word GC_hdr_cache_hits = 0;
word GC_hdr_cache_misses = 0;
#endif
void GC_init_headers()
{
......@@ -138,6 +143,8 @@ void GC_init_headers()
for (i = 0; i < TOP_SZ; i++) {
GC_top_index[i] = GC_all_nils;
}
GC_invalid_header = alloc_hdr();
GC_invalidate_map(GC_invalid_header);
}
/* Make sure that there is a bottom level index block for address addr */
......@@ -191,10 +198,10 @@ word addr;
return(TRUE);
}
/* Install a header for block h. */
/* The header is uninitialized. */
/* Returns FALSE on failure. */
GC_bool GC_install_header(h)
/* Install a header for block h. */
/* The header is uninitialized. */
/* Returns the header or 0 on failure. */
struct hblkhdr * GC_install_header(h)
register struct hblk * h;
{
hdr * result;
......@@ -205,7 +212,7 @@ register struct hblk * h;
# ifdef USE_MUNMAP
result -> hb_last_reclaimed = GC_gc_no;
# endif
return(result != 0);
return(result);
}
/* Set up forwarding counts for block h of size sz */
......
......@@ -80,6 +80,24 @@ void GC_push_regs()
# ifdef RT
register long TMP_SP; /* must be bound to r11 */
# endif
# if defined(MIPS) && defined(LINUX)
/* I'm not sure whether this has actually been tested. */
# define call_push(x) asm("move $4," x ";"); asm("jal GC_push_one")
call_push("$2");
call_push("$3");
call_push("$16");
call_push("$17");
call_push("$18");
call_push("$19");
call_push("$20");
call_push("$21");
call_push("$22");
call_push("$23");
call_push("$30");
# undef call_push
# endif /* MIPS && LINUX */
# ifdef VAX
/* VAX - generic code below does not work under 4.2 */
/* r1 through r5 are caller save, and therefore */
......@@ -199,10 +217,11 @@ void GC_push_regs()
# endif /* __MWERKS__ */
# endif /* MACOS */
# if defined(I386) &&!defined(OS2) &&!defined(SVR4) &&!defined(MSWIN32) \
# if defined(I386) &&!defined(OS2) &&!defined(SVR4) \
&& (defined(__MINGW32__) || !defined(MSWIN32)) \
&& !defined(SCO) && !defined(SCO_ELF) \
&& !(defined(LINUX) && defined(__ELF__)) \
&& !(defined(__FreeBSD__) && defined(__ELF__)) \
&& !(defined(FREEBSD) && defined(__ELF__)) \
&& !defined(DOS4GW)
/* I386 code, generic code does not appear to work */
/* It does appear to work under OS2, and asms dont */
......@@ -217,20 +236,25 @@ void GC_push_regs()
# endif
# if ( defined(I386) && defined(LINUX) && defined(__ELF__) ) \
|| ( defined(I386) && defined(__FreeBSD__) && defined(__ELF__) )
|| ( defined(I386) && defined(FREEBSD) && defined(__ELF__) )
/* This is modified for Linux with ELF (Note: _ELF_ only) */
/* This section handles FreeBSD with ELF. */
asm("pushl %eax"); asm("call GC_push_one"); asm("addl $4,%esp");
asm("pushl %ecx"); asm("call GC_push_one"); asm("addl $4,%esp");
asm("pushl %edx"); asm("call GC_push_one"); asm("addl $4,%esp");
asm("pushl %ebp"); asm("call GC_push_one"); asm("addl $4,%esp");
asm("pushl %esi"); asm("call GC_push_one"); asm("addl $4,%esp");
asm("pushl %edi"); asm("call GC_push_one"); asm("addl $4,%esp");
asm("pushl %ebx"); asm("call GC_push_one"); asm("addl $4,%esp");
/* Eax is caller-save and dead here. Other caller-save */
/* registers could also be skipped. We assume there are no */
/* pointers in MMX registers, etc. */
/* We combine instructions in a single asm to prevent gcc from */
/* inserting code in the middle. */
asm("pushl %ecx; call GC_push_one; addl $4,%esp");
asm("pushl %edx; call GC_push_one; addl $4,%esp");
asm("pushl %ebp; call GC_push_one; addl $4,%esp");
asm("pushl %esi; call GC_push_one; addl $4,%esp");
asm("pushl %edi; call GC_push_one; addl $4,%esp");
asm("pushl %ebx; call GC_push_one; addl $4,%esp");
# endif
# if defined(I386) && defined(MSWIN32) && !defined(USE_GENERIC)
# if defined(I386) && defined(MSWIN32) && !defined(__MINGW32__) \
&& !defined(USE_GENERIC)
/* I386 code, Microsoft variant */
__asm push eax
__asm call GC_push_one
......@@ -274,11 +298,10 @@ void GC_push_regs()
asm ("movd r7, tos"); asm ("bsr ?_GC_push_one"); asm ("adjspb $-4");
# endif
# if defined(SPARC) || defined(IA64)
# if defined(SPARC)
{
word GC_save_regs_in_stack();
/* generic code will not work */
GC_save_regs_ret_val = GC_save_regs_in_stack();
}
# endif
......@@ -351,8 +374,8 @@ void GC_push_regs()
/* other machines... */
# if !(defined M68K) && !(defined VAX) && !(defined RT)
# if !(defined SPARC) && !(defined I386) && !(defined NS32K)
# if !defined(POWERPC) && !defined(UTS4) && !defined(IA64)
# if !defined(PJ)
# if !defined(POWERPC) && !defined(UTS4)
# if !defined(PJ) && !(defined(MIPS) && defined(LINUX))
--> bad news <--
# endif
# endif
......@@ -379,11 +402,24 @@ ptr_t cold_gc_frame;
for (; (char *)i < lim; i++) {
*i = 0;
}
# if defined(POWERPC) || defined(MSWIN32) || defined(UTS4)
# if defined(POWERPC) || defined(MSWIN32) || defined(UTS4) || defined(LINUX)
(void) setjmp(regs);
# else
(void) _setjmp(regs);
# endif
# if defined(SPARC) || defined(IA64)
/* On a register window machine, we need to save register */
/* contents on the stack for this to work. The setjmp */
/* is probably not needed on SPARC, since pointers are */
/* only stored in windowed or scratch registers. It is */
/* needed on IA64, since some non-windowed registers are */
/* preserved. */
{
word GC_save_regs_in_stack();
GC_save_regs_ret_val = GC_save_regs_in_stack();
}
# endif
GC_push_current_stack(cold_gc_frame);
}
}
......
......@@ -81,6 +81,10 @@ register ptr_t *opp;
/* but that's benign. */
/* Volatile declarations may need to be added */
/* to prevent the compiler from breaking things.*/
/* If we only execute the second of the */
/* following assignments, we lose the free */
/* list, but that should still be OK, at least */
/* for garbage collected memory. */
*opp = obj_link(op);
obj_link(op) = 0;
} else {
......
......@@ -134,22 +134,14 @@ void GC_incr_mem_freed(size_t n)
/* Analogous to the above, but assumes a small object size, and */
/* bypasses MERGE_SIZES mechanism. Used by gc_inline.h. */
#ifdef __STDC__
ptr_t GC_generic_malloc_words_small(size_t lw, int k)
#else
ptr_t GC_generic_malloc_words_small(lw, k)
register word lw;
register int k;
#endif
ptr_t GC_generic_malloc_words_small_inner(lw, k)
register word lw;
register int k;
{
register ptr_t op;
register ptr_t *opp;
register struct obj_kind * kind = GC_obj_kinds + k;
DCL_LOCK_STATE;
GC_INVOKE_FINALIZERS();
DISABLE_SIGNALS();
LOCK();
opp = &(kind -> ok_freelist[lw]);
if( (op = *opp) == 0 ) {
if (!GC_is_initialized) {
......@@ -167,6 +159,26 @@ DCL_LOCK_STATE;
*opp = obj_link(op);
obj_link(op) = 0;
GC_words_allocd += lw;
return((ptr_t)op);
}
/* Analogous to the above, but assumes a small object size, and */
/* bypasses MERGE_SIZES mechanism. Used by gc_inline.h. */
#ifdef __STDC__
ptr_t GC_generic_malloc_words_small(size_t lw, int k)
#else
ptr_t GC_generic_malloc_words_small(lw, k)
register word lw;
register int k;
#endif
{
register ptr_t op;
DCL_LOCK_STATE;
GC_INVOKE_FINALIZERS();
DISABLE_SIGNALS();
LOCK();
op = GC_generic_malloc_words_small_inner(lw, k);
UNLOCK();
ENABLE_SIGNALS();
return((ptr_t)op);
......
......@@ -38,7 +38,7 @@ word x;
/* mark_proc GC_mark_procs[MAX_MARK_PROCS] = {0} -- declared in gc_priv.h */
word GC_n_mark_procs = 0;
word GC_n_mark_procs = GC_RESERVED_MARK_PROCS;
/* Initialize GC_obj_kinds properly and standard free lists properly. */
/* This must be done statically since they may be accessed before */
......@@ -365,20 +365,20 @@ GC_bool GC_mark_stack_empty()
/* with IGNORE_OFF_PAGE set. */
/*ARGSUSED*/
# ifdef PRINT_BLACK_LIST
word GC_find_start(current, hhdr, source)
ptr_t GC_find_start(current, hhdr, source)
word source;
# else
word GC_find_start(current, hhdr)
ptr_t GC_find_start(current, hhdr)
# define source 0
# endif
register word current;
register ptr_t current;
register hdr * hhdr;
{
# ifdef ALL_INTERIOR_POINTERS
if (hhdr != 0) {
register word orig = current;
register ptr_t orig = current;
current = (word)HBLKPTR(current) + HDR_BYTES;
current = (ptr_t)HBLKPTR(current) + HDR_BYTES;
do {
current = current - HBLKSIZE*(word)hhdr;
hhdr = HDR(current);
......@@ -429,6 +429,12 @@ mse * msp;
* is never 0. A mark stack entry never has size 0.
* We try to traverse on the order of a hblk of memory before we return.
* Caller is responsible for calling this until the mark stack is empty.
* Note that this is the most performance critical routine in the
* collector. Hence it contains all sorts of ugly hacks to speed
* things up. In particular, we avoid procedure calls on the common
* path, we take advantage of peculiarities of the mark descriptor
* encoding, we optionally maintain a cache for the block address to
* header mapping, we prefetch when an object is "grayed", etc.
*/
void GC_mark_from_mark_stack()
{
......@@ -443,9 +449,12 @@ void GC_mark_from_mark_stack()
register word descr;
register ptr_t greatest_ha = GC_greatest_plausible_heap_addr;
register ptr_t least_ha = GC_least_plausible_heap_addr;
DECLARE_HDR_CACHE;
# define SPLIT_RANGE_WORDS 128 /* Must be power of 2. */
GC_objects_are_marked = TRUE;
INIT_HDR_CACHE;
# ifdef OS2 /* Use untweaked version to circumvent compiler problem */
while (GC_mark_stack_top_reg >= GC_mark_stack_reg && credit >= 0) {
# else
......@@ -453,8 +462,13 @@ void GC_mark_from_mark_stack()
>= 0) {
# endif
current_p = GC_mark_stack_top_reg -> mse_start;
retry:
descr = GC_mark_stack_top_reg -> mse_descr;
retry:
/* current_p and descr describe the current object. */
/* *GC_mark_stack_top_reg is vacant. */
/* The following is 0 only for small objects described by a simple */
/* length descriptor. For many applications this is the common */
/* case, so we try to detect it quickly. */
if (descr & ((~(WORDS_TO_BYTES(SPLIT_RANGE_WORDS) - 1)) | DS_TAGS)) {
word tag = descr & DS_TAGS;
......@@ -465,8 +479,8 @@ void GC_mark_from_mark_stack()
/* stack. */
GC_mark_stack_top_reg -> mse_start =
limit = current_p + SPLIT_RANGE_WORDS-1;
GC_mark_stack_top_reg -> mse_descr -=
WORDS_TO_BYTES(SPLIT_RANGE_WORDS-1);
GC_mark_stack_top_reg -> mse_descr =
descr - WORDS_TO_BYTES(SPLIT_RANGE_WORDS-1);
/* Make sure that pointers overlapping the two ranges are */
/* considered. */
limit = (word *)((char *)limit + sizeof(word) - ALIGNMENT);
......@@ -479,8 +493,8 @@ void GC_mark_from_mark_stack()
if ((signed_word)descr < 0) {
current = *current_p;
if ((ptr_t)current >= least_ha && (ptr_t)current < greatest_ha) {
PUSH_CONTENTS(current, GC_mark_stack_top_reg, mark_stack_limit,
current_p, exit1);
PUSH_CONTENTS((ptr_t)current, GC_mark_stack_top_reg,
mark_stack_limit, current_p, exit1);
}
}
descr <<= 1;
......@@ -499,24 +513,94 @@ void GC_mark_from_mark_stack()
mark_stack_limit, ENV(descr));
continue;
case DS_PER_OBJECT:
GC_mark_stack_top_reg -> mse_descr =
*(word *)((ptr_t)current_p + descr - tag);
if ((signed_word)descr >= 0) {
/* Descriptor is in the object. */
descr = *(word *)((ptr_t)current_p + descr - DS_PER_OBJECT);
} else {
/* Descriptor is in type descriptor pointed to by first */
/* word in object. */
ptr_t type_descr = *(ptr_t *)current_p;
/* type_descr is either a valid pointer to the descriptor */
/* structure, or this object was on a free list. If it */
/* it was anything but the last object on the free list, */
/* we will misinterpret the next object on the free list as */
/* the type descriptor, and get a 0 GC descriptor, which */
/* is ideal. Unfortunately, we need to check for the last */
/* object case explicitly. */
if (0 == type_descr) {
/* Rarely executed. */
GC_mark_stack_top_reg--;
continue;
}
descr = *(word *)(type_descr
- (descr - (DS_PER_OBJECT - INDIR_PER_OBJ_BIAS)));
}
goto retry;
}
} else {
} else /* Small object with length descriptor */ {
GC_mark_stack_top_reg--;
limit = (word *)(((ptr_t)current_p) + (word)descr);
}
/* The simple case in which we're scanning a range. */
credit -= (ptr_t)limit - (ptr_t)current_p;
limit -= 1;
while (current_p <= limit) {
current = *current_p;
if ((ptr_t)current >= least_ha && (ptr_t)current < greatest_ha) {
PUSH_CONTENTS(current, GC_mark_stack_top_reg,
mark_stack_limit, current_p, exit2);
{
# define PREF_DIST 4
# ifndef SMALL_CONFIG
word deferred;
/* Try to prefetch the next pointer to be examined asap. */
/* Empirically, this also seems to help slightly without */
/* prefetches, at least on linux/X86. Presumably this loop */
/* ends up with less register pressure, and gcc thus ends up */
/* generating slightly better code. Overall gcc code quality */
/* for this loop is still not great. */
for(;;) {
PREFETCH((ptr_t)limit - PREF_DIST*CACHE_LINE_SIZE);
deferred = *limit;
limit = (word *)((char *)limit - ALIGNMENT);
if ((ptr_t)deferred >= least_ha && (ptr_t)deferred < greatest_ha) {
PREFETCH(deferred);
break;
}
if (current_p > limit) goto next_object;
/* Unroll once, so we don't do too many of the prefetches */
/* based on limit. */
deferred = *limit;
limit = (word *)((char *)limit - ALIGNMENT);
if ((ptr_t)deferred >= least_ha && (ptr_t)deferred < greatest_ha) {
PREFETCH(deferred);
break;
}
if (current_p > limit) goto next_object;
}
# endif
while (current_p <= limit) {
/* Empirically, unrolling this loop doesn't help a lot. */
/* Since HC_PUSH_CONTENTS expands to a lot of code, */
/* we don't. */
current = *current_p;
PREFETCH((ptr_t)current_p + PREF_DIST*CACHE_LINE_SIZE);
if ((ptr_t)current >= least_ha && (ptr_t)current < greatest_ha) {
/* Prefetch the contents of the object we just pushed. It's */
/* likely we will need them soon. */
PREFETCH(current);
HC_PUSH_CONTENTS((ptr_t)current, GC_mark_stack_top_reg,
mark_stack_limit, current_p, exit2);
}
current_p = (word *)((char *)current_p + ALIGNMENT);
}
current_p = (word *)((char *)current_p + ALIGNMENT);
# ifndef SMALL_CONFIG
/* We still need to mark the entry we previously prefetched. */
/* We alrady know that it passes the preliminary pointer */
/* validity test. */
HC_PUSH_CONTENTS((ptr_t)deferred, GC_mark_stack_top_reg,
mark_stack_limit, current_p, exit4);
next_object:;
# endif
}
}
GC_mark_stack_top = GC_mark_stack_top_reg;
......@@ -689,7 +773,7 @@ word p;
return;
}
# endif
GC_PUSH_ONE_STACK(p, 0);
GC_PUSH_ONE_STACK(p, MARKED_FROM_REGISTER);
}
# ifdef __STDC__
......
......@@ -412,9 +412,8 @@ ptr_t cold_gc_frame;
if (0 == cold_gc_frame) return;
# ifdef STACK_GROWS_DOWN
GC_push_all_eager(GC_approx_sp(), cold_gc_frame);
# ifdef IA64
--> fix this
# endif
/* For IA64, the register stack backing store is handled */
/* in the thread-specific code. */
# else
GC_push_all_eager( cold_gc_frame, GC_approx_sp() );
# endif
......@@ -505,6 +504,9 @@ ptr_t cold_gc_frame;
/* In the USE_GENERIC_PUSH_REGS case, this is done inside */
/* GC_push_regs, so that we catch callee-save registers saved */
/* inside the GC_push_regs frame. */
/* In the case of linux threads on Ia64, the hot section of */
/* the main stack is marked here, but the register stack */
/* backing store is handled in the threads-specific code. */
# endif
if (GC_push_other_roots != 0) (*GC_push_other_roots)();
/* In the threads case, this also pushes thread stacks. */
......
......@@ -42,11 +42,12 @@
# ifdef WIN32_THREADS
GC_API CRITICAL_SECTION GC_allocate_ml;
# else
# if defined(IRIX_THREADS) || defined(LINUX_THREADS) \
|| defined(IRIX_JDK_THREADS)
# if defined(IRIX_THREADS) || defined(IRIX_JDK_THREADS) \
|| (defined(LINUX_THREADS) && defined(USE_SPIN_LOCK))
pthread_t GC_lock_holder = NO_THREAD;
# else
# if defined(HPUX_THREADS)
# if defined(HPUX_THREADS) \
|| defined(LINUX_THREADS) && !defined(USE_SPIN_LOCK)
pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER;
# else
--> declare allocator lock here
......@@ -123,6 +124,15 @@ extern signed_word GC_mem_found;
for (i = 8*sizeof(word) + 1; i <= 16 * sizeof(word); i++) {
GC_size_map[i] = (ROUNDED_UP_WORDS(i) + 1) & (~1);
}
# ifdef GC_GCJ_SUPPORT
/* Make all sizes up to 32 words predictable, so that a */
/* compiler can statically perform the same computation, */
/* or at least a computation that results in similar size */
/* classes. */
for (i = 16*sizeof(word) + 1; i <= 32 * sizeof(word); i++) {
GC_size_map[i] = (ROUNDED_UP_WORDS(i) + 3) & (~3);
}
# endif
/* We leave the rest of the array to be filled in on demand. */
}
......@@ -443,7 +453,8 @@ void GC_init_inner()
# ifdef MSWIN32
GC_init_win32();
# endif
# if defined(LINUX) && (defined(SPARC) || defined(IA64))
# if defined(SEARCH_FOR_DATA_START)
/* This doesn't really work if the collector is in a shared library. */
GC_init_linux_data_start();
# endif
# ifdef SOLARIS_THREADS
......@@ -819,6 +830,8 @@ struct callinfo info[NFRAMES];
#endif /* SAVE_CALL_CHAIN */
/* Needed by SRC_M3, gcj, and should perhaps be the official interface */
/* to GC_dont_gc. */
void GC_enable()
{
GC_dont_gc--;
......
......@@ -103,10 +103,10 @@ ptr_t ofl;
p[3] = 0;
p += 4;
for (; p < lim; p += 4) {
PREFETCH_FOR_WRITE(p+64);
p[0] = (word)(p-4);
p[1] = 0;
p[2] = 0;
p[3] = 0;
CLEAR_DOUBLE(p+2);
};
return((ptr_t)(p-4));
}
......@@ -141,6 +141,7 @@ ptr_t ofl;
p[4] = (word)p;
p += 8;
for (; p < lim; p += 8) {
PREFETCH_FOR_WRITE(p+64);
p[0] = (word)(p-4);
p[4] = (word)p;
};
......@@ -179,6 +180,10 @@ int kind;
/* Mark all objects if appropriate. */
if (IS_UNCOLLECTABLE(kind)) GC_set_hdr_marks(HDR(h));
PREFETCH_FOR_WRITE((char *)h);
PREFETCH_FOR_WRITE((char *)h + 128);
PREFETCH_FOR_WRITE((char *)h + 256);
PREFETCH_FOR_WRITE((char *)h + 378);
/* Handle small objects sizes more efficiently. For larger objects */
/* the difference is less significant. */
# ifndef SMALL_CONFIG
......
......@@ -66,7 +66,7 @@
# define NEED_FIND_LIMIT
# endif
# if (defined(SUNOS4) & defined(DYNAMIC_LOADING)) && !defined(PCR)
# if (defined(SUNOS4) && defined(DYNAMIC_LOADING)) && !defined(PCR)
# define NEED_FIND_LIMIT
# endif
......@@ -75,7 +75,8 @@
# endif
# if defined(LINUX) && \
(defined(SPARC) || defined(IA64))
(defined(POWERPC) || defined(SPARC) || defined(ALPHA) || defined(IA64) \
|| defined(MIPS))
# define NEED_FIND_LIMIT
# endif
......@@ -142,7 +143,8 @@
# define OPT_PROT_EXEC 0
#endif
#if defined(LINUX) && (defined(SPARC) || defined(IA64))
#if defined(SEARCH_FOR_DATA_START)
/* The following doesn't work if the GC is in a dynamic library. */
/* The I386 case can be handled without a search. The Alpha case */
/* used to be handled differently as well, but the rules changed */
/* for recent Linux versions. This seems to be the easiest way to */
......@@ -641,19 +643,17 @@ ptr_t GC_get_stack_base()
#ifdef LINUX_STACKBOTTOM
# define STAT_SKIP 27 /* Number of fields preceding startstack */
/* field in /proc/<pid>/stat */
/* field in /proc/self/stat */
ptr_t GC_linux_stack_base(void)
{
char buf[50];
FILE *f;
char c;
word result = 0;
int i;
sprintf(buf, "/proc/%d/stat", getpid());
f = fopen(buf, "r");
if (NULL == f) ABORT("Couldn't open /proc/<pid>/stat");
f = fopen("/proc/self/stat", "r");
if (NULL == f) ABORT("Couldn't open /proc/self/stat");
c = getc(f);
/* Skip the required number of fields. This number is hopefully */
/* constant across all Linux implementations. */
......@@ -1874,6 +1874,9 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */
# else
# ifdef IA64
char * addr = si -> si_addr;
/* I believe this is claimed to work on all platforms for */
/* Linux 2.3.47 and later. Hopefully we don't have to */
/* worry about earlier kernels on IA64. */
# else
# if defined(POWERPC)
char * addr = (char *) (sc.regs->dar);
......@@ -2178,12 +2181,13 @@ word len;
((ptr_t)end_block - (ptr_t)start_block) + HBLKSIZE);
}
#ifndef MSWIN32
#if !defined(MSWIN32) && !defined(LINUX_THREADS)
/* Replacement for UNIX system call. */
/* Other calls that write to the heap */
/* should be handled similarly. */
# if defined(__STDC__) && !defined(SUNOS4)
# include <unistd.h>
# include <sys/uio.h>
ssize_t read(int fd, void *buf, size_t nbyte)
# else
# ifndef LINT
......@@ -2200,10 +2204,12 @@ word len;
GC_begin_syscall();
GC_unprotect_range(buf, (word)nbyte);
# ifdef IRIX5
# if defined(IRIX5) || defined(LINUX_THREADS)
/* Indirect system call may not always be easily available. */
/* We could call _read, but that would interfere with the */
/* libpthread interception of read. */
/* On Linux, we have to be careful with the linuxthreads */
/* read interception. */
{
struct iovec iov;
......@@ -2217,7 +2223,29 @@ word len;
GC_end_syscall();
return(result);
}
#endif /* !MSWIN32 */
#endif /* !MSWIN32 && !LINUX */
#ifdef USE_LD_WRAP
/* We use the GNU ld call wrapping facility. */
/* This requires that the linker be invoked with "--wrap read". */
/* This can be done by passing -Wl,"--wrap read" to gcc. */
/* I'm not sure that this actually wraps whatever version of read */
/* is called by stdio. That code also mentions __read. */
# include <unistd.h>
ssize_t __wrap_read(int fd, void *buf, size_t nbyte)
{
int result;
GC_begin_syscall();
GC_unprotect_range(buf, (word)nbyte);
result = __real_read(fd, buf, nbyte);
GC_end_syscall();
return(result);
}
/* We should probably also do this for __read, or whatever stdio */
/* actually calls. */
#endif
/*ARGSUSED*/
GC_bool GC_page_was_ever_dirty(h)
......
......@@ -241,9 +241,18 @@ register word sz;
/* Clear object, advance p to next object in the process */
q = p + sz;
p++; /* Skip link field */
while (p < q) {
# if defined(SMALL_CONFIG) && defined(ALIGN_DOUBLE)
/* We assert that sz must be even */
*p++ = 0;
while (p < q) {
CLEAR_DOUBLE(p);
p += 2;
}
# else
while (p < q) {
*p++ = 0;
}
}
# endif
}
word_no += sz;
}
......@@ -321,8 +330,7 @@ register ptr_t list;
p[start_displ] = (word)list; \
list = (ptr_t)(p+start_displ); \
p[start_displ+1] = 0; \
p[start_displ+2] = 0; \
p[start_displ+3] = 0; \
CLEAR_DOUBLE(p + start_displ + 2); \
INCR_WORDS(4); \
}
......@@ -814,6 +822,12 @@ int report_if_found; /* Abort if a GC_reclaimable object is found */
/* Go through all heap blocks (in hblklist) and reclaim unmarked objects */
/* or enqueue the block for later processing. */
GC_apply_to_all_blocks(GC_reclaim_block, (word)report_if_found);
# ifdef EAGER_SWEEP
/* This is a very stupid thing to do. We make it possible anyway, */
/* so that you can convince yourself that it really is very stupid. */
GC_reclaim_all((GC_stop_func)0, FALSE);
# endif
}
......@@ -847,7 +861,7 @@ int kind;
* Abort and return FALSE when/if (*stop_func)() returns TRUE.
* If this returns TRUE, then it's safe to restart the world
* with incorrectly cleared mark bits.
* If ignore_old is TRUE, then reclain only blocks that have been
* If ignore_old is TRUE, then reclaim only blocks that have been
* recently reclaimed, and discard the rest.
* Stop_func may be 0.
*/
......
......@@ -76,14 +76,16 @@ GC_pthread_create(pthread_t *new_thread,
pthread_attr_t attr;
word my_flags = 0;
int flag;
void * stack;
size_t stack_size;
void * stack = 0;
size_t stack_size = 0;
int n;
struct sched_param schedparam;
(void)pthread_attr_getstacksize(attr_in, &stack_size);
(void)pthread_attr_getstackaddr(attr_in, &stack);
(void)pthread_attr_init(&attr);
if (attr_in != 0) {
(void)pthread_attr_getstacksize(attr_in, &stack_size);
(void)pthread_attr_getstackaddr(attr_in, &stack);
}
LOCK();
if (!GC_thr_initialized) {
......@@ -93,7 +95,11 @@ GC_pthread_create(pthread_t *new_thread,
if (stack == 0) {
if (stack_size == 0)
stack_size = GC_min_stack_sz;
stack_size = 1048576;
/* ^-- 1 MB (this was GC_min_stack_sz, but that
* violates the pthread_create documentation which
* says the default value if none is supplied is
* 1MB) */
else
stack_size += thr_min_stack();
......@@ -109,20 +115,22 @@ GC_pthread_create(pthread_t *new_thread,
}
(void)pthread_attr_setstacksize(&attr, stack_size);
(void)pthread_attr_setstackaddr(&attr, stack);
(void)pthread_attr_getscope(attr_in, &n);
(void)pthread_attr_setscope(&attr, n);
(void)pthread_attr_getschedparam(attr_in, &schedparam);
(void)pthread_attr_setschedparam(&attr, &schedparam);
(void)pthread_attr_getschedpolicy(attr_in, &n);
(void)pthread_attr_setschedpolicy(&attr, n);
(void)pthread_attr_getinheritsched(attr_in, &n);
(void)pthread_attr_setinheritsched(&attr, n);
(void)pthread_attr_getdetachstate(attr_in, &flag);
if (flag == PTHREAD_CREATE_DETACHED) {
my_flags |= DETACHED;
if (attr_in != 0) {
(void)pthread_attr_getscope(attr_in, &n);
(void)pthread_attr_setscope(&attr, n);
(void)pthread_attr_getschedparam(attr_in, &schedparam);
(void)pthread_attr_setschedparam(&attr, &schedparam);
(void)pthread_attr_getschedpolicy(attr_in, &n);
(void)pthread_attr_setschedpolicy(&attr, n);
(void)pthread_attr_getinheritsched(attr_in, &n);
(void)pthread_attr_setinheritsched(&attr, n);
(void)pthread_attr_getdetachstate(attr_in, &flag);
if (flag == PTHREAD_CREATE_DETACHED) {
my_flags |= DETACHED;
}
(void)pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
}
(void)pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
/*
* thr_create can call malloc(), which if redirected will
* attempt to acquire the allocation lock.
......
......@@ -661,7 +661,8 @@ void GC_my_stack_limits()
}
/* We hold allocation lock. We assume the world is stopped. */
/* We hold allocation lock. Should do exactly the right thing if the */
/* world is stopped. Should not fail if it isn't. */
void GC_push_all_stacks()
{
register int i;
......@@ -900,7 +901,7 @@ GC_thr_create(void *stack_base, size_t stack_size,
}
GC_multithreaded++;
if (stack == 0) {
if (stack_size == 0) stack_size = GC_min_stack_sz;
if (stack_size == 0) stack_size = 1024*1024;
stack = (void *)GC_stack_alloc(&stack_size);
if (stack == 0) {
GC_multithreaded--;
......
......@@ -15,6 +15,8 @@
/* An incomplete test for the garbage collector. */
/* Some more obscure entry points are not tested at all. */
# undef GC_BUILD
# if defined(mips) && defined(SYSTYPE_BSD43)
/* MIPS RISCOS 4 */
# else
......@@ -147,7 +149,6 @@ sexpr y;
register sexpr r;
r = (sexpr) GC_MALLOC_UNCOLLECTABLE(sizeof(struct SEXPR));
assert(GC_is_marked(r));
if (r == 0) {
(void)GC_printf0("Out of memory\n");
exit(1);
......@@ -157,6 +158,76 @@ assert(GC_is_marked(r));
return(r);
}
#ifdef GC_GCJ_SUPPORT
#include "gc_mark.h"
#include "dbg_mlc.h"
#include "include/gc_gcj.h"
/* The following struct emulates the vtable in gcj. */
/* This assumes the default value of MARK_DESCR_OFFSET. */
struct fake_vtable {
void * dummy; /* class pointer in real gcj. */
size_t descr;
};
struct fake_vtable gcj_class_struct1 = { 0, sizeof(struct SEXPR)
+ sizeof(struct fake_vtable *) };
/* length based descriptor. */
struct fake_vtable gcj_class_struct2 =
{ 0, (3l << (CPP_WORDSZ - 3)) | DS_BITMAP};
/* Bitmap based descriptor. */
struct ms_entry * fake_gcj_mark_proc(word * addr,
struct ms_entry *mark_stack_ptr,
struct ms_entry *mark_stack_limit,
word env )
{
sexpr x;
if (1 == env) {
/* Object allocated with debug allocator. */
addr = (word *)USR_PTR_FROM_BASE(addr);
}
x = (sexpr)(addr + 1); /* Skip the vtable pointer. */
/* We could just call PUSH_CONTENTS directly here. But any real */
/* real client would try to filter out the obvious misses. */
if (0 != x -> sexpr_cdr) {
PUSH_CONTENTS((ptr_t)(x -> sexpr_cdr), mark_stack_ptr,
mark_stack_limit, &(x -> sexpr_cdr), exit1);
}
if ((ptr_t)(x -> sexpr_car) > GC_least_plausible_heap_addr) {
PUSH_CONTENTS((ptr_t)(x -> sexpr_car), mark_stack_ptr,
mark_stack_limit, &(x -> sexpr_car), exit2);
}
return(mark_stack_ptr);
}
sexpr gcj_cons(x, y)
sexpr x;
sexpr y;
{
GC_word * r;
sexpr result;
static int count = 0;
if (++count & 1) {
r = (GC_word *) GC_GCJ_FAST_MALLOC(3, &gcj_class_struct1);
} else {
r = (GC_word *) GC_GCJ_MALLOC(sizeof(struct SEXPR)
+ sizeof(struct fake_vtable*),
&gcj_class_struct2);
}
if (r == 0) {
(void)GC_printf0("Out of memory\n");
exit(1);
}
result = (sexpr)(r + 1);
result -> sexpr_car = x;
result -> sexpr_cdr = y;
return(result);
}
#endif
/* Return reverse(x) concatenated with y */
sexpr reverse1(x, y)
sexpr x, y;
......@@ -184,6 +255,35 @@ int low, up;
}
}
#ifdef GC_GCJ_SUPPORT
/* Return reverse(x) concatenated with y */
sexpr gcj_reverse1(x, y)
sexpr x, y;
{
if (is_nil(x)) {
return(y);
} else {
return( gcj_reverse1(cdr(x), gcj_cons(car(x), y)) );
}
}
sexpr gcj_reverse(x)
sexpr x;
{
return( gcj_reverse1(x, nil) );
}
sexpr gcj_ints(low, up)
int low, up;
{
if (low > up) {
return(nil);
} else {
return(gcj_cons(gcj_cons(INT_TO_SEXPR(low), nil), gcj_ints(low+1, up)));
}
}
#endif /* GC_GCJ_SUPPORT */
/* To check uncollectable allocation we build lists with disguised cdr */
/* pointers, and make sure they don't go away. */
sexpr uncollectable_ints(low, up)
......@@ -367,7 +467,12 @@ void reverse_test()
g[799] = ints(1,18);
h = (sexpr *)GC_MALLOC(1025 * sizeof(sexpr));
h = (sexpr *)GC_REALLOC((GC_PTR)h, 2000 * sizeof(sexpr));
h[1999] = ints(1,19);
# ifdef GC_GCJ_SUPPORT
h[1999] = gcj_ints(1,200);
h[1999] = gcj_reverse(h[1999]);
# else
h[1999] = ints(1,200);
# endif
/* Try to force some collections and reuse of small list elements */
for (i = 0; i < 10; i++) {
(void)ints(1, BIG);
......@@ -412,7 +517,10 @@ void reverse_test()
check_uncollectable_ints(d, 1, 100);
check_ints(f[5], 1,17);
check_ints(g[799], 1,18);
check_ints(h[1999], 1,19);
# ifdef GC_GCJ_SUPPORT
h[1999] = gcj_reverse(h[1999]);
# endif
check_ints(h[1999], 1,200);
# ifndef THREADS
a = 0;
# endif
......@@ -759,6 +867,10 @@ void typed_test()
old = 0;
for (i = 0; i < 4000; i++) {
new = (GC_word *) GC_malloc_explicitly_typed(4 * sizeof(GC_word), d1);
if (0 != new[0] || 0 != new[1]) {
GC_printf0("Bad initialization by GC_malloc_explicitly_typed\n");
FAIL;
}
new[0] = 17;
new[1] = (GC_word)old;
old = new;
......@@ -782,6 +894,10 @@ void typed_test()
new = (GC_word *) GC_calloc_explicitly_typed(1001,
3 * sizeof(GC_word),
d2);
if (0 != new[0] || 0 != new[1]) {
GC_printf0("Bad initialization by GC_malloc_explicitly_typed\n");
FAIL;
}
}
new[0] = 17;
new[1] = (GC_word)old;
......@@ -906,6 +1022,10 @@ void run_one_test()
/* Test floating point alignment */
*(double *)GC_MALLOC(sizeof(double)) = 1.0;
*(double *)GC_MALLOC(sizeof(double)) = 1.0;
# ifdef GC_GCJ_SUPPORT
GC_REGISTER_DISPLACEMENT(sizeof(struct fake_vtable *));
GC_init_gcj_malloc(0, (void *)fake_gcj_mark_proc);
# endif
/* Repeated list reversal test. */
reverse_test();
# ifdef PRINTSTATS
......@@ -1032,7 +1152,7 @@ void SetMinimumStack(long minSize)
#if !defined(PCR) && !defined(SOLARIS_THREADS) && !defined(WIN32_THREADS) \
&& !defined(IRIX_THREADS) && !defined(LINUX_THREADS) \
&& !defined(HPUX_THREADS) || defined(LINT)
#ifdef MSWIN32
#if defined(MSWIN32) && !defined(__MINGW32__)
int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, LPSTR cmd, int n)
#else
int main()
......@@ -1114,19 +1234,24 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, LPSTR cmd, int n)
# endif
InitializeCriticalSection(&incr_cs);
(void) GC_set_warn_proc(warn_proc);
for (i = 0; i < NTEST; i++) {
# if NTEST > 0
for (i = 0; i < NTEST; i++) {
h[i] = (HANDLE)_beginthreadex(NULL, 0, thr_run_one_test, 0, 0, &thread_id);
if (h[i] == (HANDLE)-1) {
(void)GC_printf1("Thread creation failed %lu\n", (unsigned long)GetLastError());
FAIL;
}
}
}
# endif /* NTEST > 0 */
run_one_test();
for (i = 0; i < NTEST; i++)
# if NTEST > 0
for (i = 0; i < NTEST; i++) {
if (WaitForSingleObject(h[i], INFINITE) != WAIT_OBJECT_0) {
(void)GC_printf1("Thread wait failed %lu\n", (unsigned long)GetLastError());
FAIL;
}
}
# endif /* NTEST > 0 */
check_heap_stats();
(void)fflush(stdout);
return(0);
......
......@@ -37,6 +37,12 @@ extern "C" {
#ifdef MSWIN32
# include <windows.h>
#endif
#ifdef GC_NAME_CONFLICT
# define USE_GC UseGC
struct foo * GC;
#else
# define USE_GC GC
#endif
#define my_assert( e ) \
......@@ -214,7 +220,7 @@ int APIENTRY WinMain(
for (i = 0; i < 1000; i++) {
C* c = new C( 2 );
C c1( 2 ); /* stack allocation should work too */
D* d = ::new (GC, D::CleanUp, (void*) i) D( i );
D* d = ::new (USE_GC, D::CleanUp, (void*) i) D( i );
F* f = new F;
if (0 == i % 10) delete c;}
......@@ -222,9 +228,9 @@ int APIENTRY WinMain(
drop the references to them immediately, forcing many
collections. */
for (i = 0; i < 1000000; i++) {
A* a = new (GC) A( i );
A* a = new (USE_GC) A( i );
B* b = new B( i );
b = new (GC) B( i );
b = new (USE_GC) B( i );
if (0 == i % 10) {
B::Deleting( 1 );
delete b;
......
......@@ -3,7 +3,16 @@
int main()
{
# if defined(IRIX_THREADS) || defined(LINUX_THREADS)
# if defined(LINUX_THREADS)
# ifdef USE_LD_WRAP
printf("-Wl,\"--wrap read\" -Wl,\"--wrap dlopen\" "
"-Wl,\"--wrap pthread_create\" -Wl,\"--wrap pthread_join\" "
"-Wl,\"--wrap pthread_sigmask\" -lpthread\n");
# else
printf("-lpthread\n");
# endif
# endif
# if defined(IRIX_THREADS)
printf("-lpthread\n");
# endif
# if defined(HPUX_THREADS)
......
......@@ -430,7 +430,7 @@ word env;
if (bm & 1) {
current = *current_p;
if ((ptr_t)current >= least_ha && (ptr_t)current <= greatest_ha) {
PUSH_CONTENTS(current, mark_stack_ptr,
PUSH_CONTENTS((ptr_t)current, mark_stack_ptr,
mark_stack_limit, current_p, exit1);
}
}
......@@ -665,6 +665,7 @@ DCL_LOCK_STATE;
# endif
} else {
*opp = obj_link(op);
obj_link(op) = 0;
GC_words_allocd += lw;
FASTUNLOCK();
}
......@@ -708,6 +709,7 @@ DCL_LOCK_STATE;
# endif
} else {
*opp = obj_link(op);
obj_link(op) = 0;
GC_words_allocd += lw;
FASTUNLOCK();
}
......@@ -717,7 +719,7 @@ DCL_LOCK_STATE;
lw = BYTES_TO_WORDS(GC_size(op));
}
if (op != NULL)
((word *)op)[lw - 1] = d;
((word *)op)[lw - 1] = d;
return((GC_PTR) op);
}
......@@ -772,6 +774,7 @@ DCL_LOCK_STATE;
# endif
} else {
*opp = obj_link(op);
obj_link(op) = 0;
GC_words_allocd += lw;
FASTUNLOCK();
}
......
#define GC_VERSION_MAJOR 5
#define GC_VERSION_MINOR 0
#define GC_ALPHA_VERSION 4
#define GC_ALPHA_VERSION 6
# define GC_NOT_ALPHA 0xff
/* This is really an unreleased version which doesn't have a real version */
/* number. */
#ifndef GC_NO_VERSION_VAR
unsigned GC_version = ((GC_VERSION_MAJOR << 16) | (GC_VERSION_MINOR << 8) | GC_ALPHA_VERSION);
......
......@@ -2,8 +2,10 @@
#include "gc_priv.h"
#if 0
#define STRICT
#include <windows.h>
#endif
#define MAX_THREADS 64
......@@ -61,7 +63,7 @@ ptr_t GC_current_stackbottom()
ABORT("no thread table entry for current thread");
}
ptr_t GC_get_lo_stack_addr(ptr_t s)
static ptr_t GC_get_lo_stack_addr(ptr_t s)
{
ptr_t bottom;
MEMORY_BASIC_INFORMATION info;
......@@ -81,7 +83,7 @@ void GC_push_all_stacks()
if (thread_table[i].stack) {
ptr_t bottom = GC_get_lo_stack_addr(thread_table[i].stack);
if (thread_table[i].id == thread_id)
GC_push_all(&i, thread_table[i].stack);
GC_push_all_stack(&i, thread_table[i].stack);
else {
thread_table[i].context.ContextFlags
= (CONTEXT_INTEGER|CONTEXT_CONTROL);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment