Selaa lähdekoodia

Initial Import.

woollybah 11 vuotta sitten
vanhempi
commit
d2ca3911f6
100 muutettua tiedostoa jossa 26423 lisäystä ja 0 poistoa
  1. 6 0
      .gitignore
  2. 71 0
      appstub.mod/appstub.bmx
  3. 17 0
      appstub.mod/appstub.linux.c
  4. 139 0
      appstub.mod/appstub.macos.m
  5. 70 0
      appstub.mod/appstub.win32.c
  6. 711 0
      appstub.mod/debugger.stdio.bmx
  7. 725 0
      appstub.mod/debugger_mt.stdio.bmx
  8. 381 0
      audio.mod/audio.bmx
  9. 11 0
      audio.mod/doc/allocchannel.bmx
  10. 10 0
      audio.mod/doc/channelplaying.bmx
  11. 12 0
      audio.mod/doc/cuesound.bmx
  12. 14 0
      audio.mod/doc/intro.bbdoc
  13. 8 0
      audio.mod/doc/loadsound.bmx
  14. 8 0
      audio.mod/doc/playsound.bmx
  15. 25 0
      audio.mod/doc/setchanneldepth.bmx
  16. 24 0
      audio.mod/doc/setchannelpan.bmx
  17. 12 0
      audio.mod/doc/setchannelrate.bmx
  18. 12 0
      audio.mod/doc/setchannelvolume.bmx
  19. BIN
      audio.mod/doc/shoot.wav
  20. 14 0
      audio.mod/doc/stopchannel.bmx
  21. 203 0
      audiosample.mod/audiosample.bmx
  22. 13 0
      audiosample.mod/doc/createaudiosample.bmx
  23. 2 0
      audiosample.mod/doc/intro.bbdoc
  24. 161 0
      audiosample.mod/sample.bmx
  25. 531 0
      bank.mod/bank.bmx
  26. 12 0
      bank.mod/doc/intro.bbdoc
  27. 95 0
      bankstream.mod/bankstream.bmx
  28. 6 0
      bankstream.mod/doc/intro.bbdoc
  29. 27 0
      basic.mod/basic.bmx
  30. 884 0
      blitz.mod/bdwgc/allchblk.c
  31. 1358 0
      blitz.mod/bdwgc/alloc.c
  32. 475 0
      blitz.mod/bdwgc/backgraph.c
  33. 289 0
      blitz.mod/bdwgc/blacklst.c
  34. 224 0
      blitz.mod/bdwgc/checksums.c
  35. 666 0
      blitz.mod/bdwgc/darwin_stop_world.c
  36. 1218 0
      blitz.mod/bdwgc/dbg_mlc.c
  37. 1507 0
      blitz.mod/bdwgc/dyn_load.c
  38. 1114 0
      blitz.mod/bdwgc/finalize.c
  39. 172 0
      blitz.mod/bdwgc/fnlz_mlc.c
  40. 88 0
      blitz.mod/bdwgc/gc_cpp.cc
  41. 2 0
      blitz.mod/bdwgc/gc_cpp.cpp
  42. 100 0
      blitz.mod/bdwgc/gc_dlopen.c
  43. 278 0
      blitz.mod/bdwgc/gcj_mlc.c
  44. 406 0
      blitz.mod/bdwgc/headers.c
  45. 354 0
      blitz.mod/bdwgc/include/cord.h
  46. 120 0
      blitz.mod/bdwgc/include/cord_pos.h
  47. 68 0
      blitz.mod/bdwgc/include/ec.h
  48. 2 0
      blitz.mod/bdwgc/include/extra/gc.h
  49. 2 0
      blitz.mod/bdwgc/include/extra/gc_cpp.h
  50. 1763 0
      blitz.mod/bdwgc/include/gc.h
  51. 325 0
      blitz.mod/bdwgc/include/gc_allocator.h
  52. 98 0
      blitz.mod/bdwgc/include/gc_backptr.h
  53. 386 0
      blitz.mod/bdwgc/include/gc_config_macros.h
  54. 436 0
      blitz.mod/bdwgc/include/gc_cpp.h
  55. 55 0
      blitz.mod/bdwgc/include/gc_disclaim.h
  56. 110 0
      blitz.mod/bdwgc/include/gc_gcj.h
  57. 146 0
      blitz.mod/bdwgc/include/gc_inline.h
  58. 268 0
      blitz.mod/bdwgc/include/gc_mark.h
  59. 95 0
      blitz.mod/bdwgc/include/gc_pthread_redirects.h
  60. 90 0
      blitz.mod/bdwgc/include/gc_tiny_fl.h
  61. 117 0
      blitz.mod/bdwgc/include/gc_typed.h
  62. 47 0
      blitz.mod/bdwgc/include/gc_version.h
  63. 54 0
      blitz.mod/bdwgc/include/include.am
  64. 45 0
      blitz.mod/bdwgc/include/javaxfc.h
  65. 68 0
      blitz.mod/bdwgc/include/leak_detector.h
  66. 480 0
      blitz.mod/bdwgc/include/new_gc_alloc.h
  67. 83 0
      blitz.mod/bdwgc/include/private/darwin_semaphore.h
  68. 46 0
      blitz.mod/bdwgc/include/private/darwin_stop_world.h
  69. 166 0
      blitz.mod/bdwgc/include/private/dbg_mlc.h
  70. 212 0
      blitz.mod/bdwgc/include/private/gc_hdrs.h
  71. 220 0
      blitz.mod/bdwgc/include/private/gc_locks.h
  72. 473 0
      blitz.mod/bdwgc/include/private/gc_pmark.h
  73. 2511 0
      blitz.mod/bdwgc/include/private/gc_priv.h
  74. 2940 0
      blitz.mod/bdwgc/include/private/gcconfig.h
  75. 69 0
      blitz.mod/bdwgc/include/private/msvc_dbg.h
  76. 44 0
      blitz.mod/bdwgc/include/private/pthread_stop_world.h
  77. 153 0
      blitz.mod/bdwgc/include/private/pthread_support.h
  78. 98 0
      blitz.mod/bdwgc/include/private/specific.h
  79. 175 0
      blitz.mod/bdwgc/include/private/thread_local_alloc.h
  80. 217 0
      blitz.mod/bdwgc/include/weakpointer.h
  81. 7 0
      blitz.mod/bdwgc/libatomic_ops/.gitattributes
  82. 66 0
      blitz.mod/bdwgc/libatomic_ops/.gitignore
  83. 40 0
      blitz.mod/bdwgc/libatomic_ops/AUTHORS
  84. 340 0
      blitz.mod/bdwgc/libatomic_ops/COPYING
  85. 312 0
      blitz.mod/bdwgc/libatomic_ops/ChangeLog
  86. 13 0
      blitz.mod/bdwgc/libatomic_ops/Makefile.am
  87. 63 0
      blitz.mod/bdwgc/libatomic_ops/README.md
  88. 13 0
      blitz.mod/bdwgc/libatomic_ops/TODO
  89. 10 0
      blitz.mod/bdwgc/libatomic_ops/autogen.sh
  90. 191 0
      blitz.mod/bdwgc/libatomic_ops/configure.ac
  91. 63 0
      blitz.mod/bdwgc/libatomic_ops/doc/LICENSING.txt
  92. 4 0
      blitz.mod/bdwgc/libatomic_ops/doc/Makefile.am
  93. 246 0
      blitz.mod/bdwgc/libatomic_ops/doc/README.txt
  94. 57 0
      blitz.mod/bdwgc/libatomic_ops/doc/README_malloc.txt
  95. 78 0
      blitz.mod/bdwgc/libatomic_ops/doc/README_stack.txt
  96. 31 0
      blitz.mod/bdwgc/libatomic_ops/doc/README_win32.txt
  97. 3 0
      blitz.mod/bdwgc/libatomic_ops/m4/.gitignore
  98. 10 0
      blitz.mod/bdwgc/libatomic_ops/pkgconfig/atomic_ops-uninstalled.pc.in
  99. 10 0
      blitz.mod/bdwgc/libatomic_ops/pkgconfig/atomic_ops.pc.in
  100. 229 0
      blitz.mod/bdwgc/libatomic_ops/src/Makefile.am

+ 6 - 0
.gitignore

@@ -0,0 +1,6 @@
+
+*.x86.a
+*.x86.i
+*.bak
+
+.bmx/

+ 71 - 0
appstub.mod/appstub.bmx

@@ -0,0 +1,71 @@
+
+Strict
+
+NoDebug
+
+Module BRL.AppStub
+
+ModuleInfo "Version: 1.20"
+ModuleInfo "Authors: Mark Sibly, Simon Armstrong"
+ModuleInfo "License: zlib/libpng"
+ModuleInfo "Copyright: Blitz Research Ltd"
+ModuleInfo "Modserver: BRL"
+
+ModuleInfo "History: 1.20 Release"
+ModuleInfo "History: Fixed 'invalid typetag' issue"
+ModuleInfo "History: 1.19 Release"
+ModuleInfo "History: Removed some debug output"
+ModuleInfo "History: 1.18 Release"
+ModuleInfo "History: Changed debugger.stdio.bmx so it handles deep stacks better"
+ModuleInfo "History: 1.17 Release"
+ModuleInfo "History: Added Brucey's SIGPIPE fix for GTK"
+ModuleInfo "History: 1.16 Release"
+ModuleInfo "History: Added experimental dll support"
+ModuleInfo "History: 1.15 Release"
+ModuleInfo "History: Fixed Const string reporting not being escaped"
+ModuleInfo "History: 1.14 Release"
+ModuleInfo "History: Debug output lines now prepended with ~>"
+ModuleInfo "History: 1.13 Release"
+ModuleInfo "History: Removed unused debugger sources"
+ModuleInfo "History: 1.12 Release"
+ModuleInfo "History: Now manually builds app menu for MacOS Tiger"
+ModuleInfo "History: 1.11 Release"
+ModuleInfo "History: Fixed MacOS debug string overflow"
+ModuleInfo "History: 1.10 Release"
+ModuleInfo "History: Added kludge for MacOS Menu Quit generating errors"
+ModuleInfo "History: 1.09 Release"
+ModuleInfo "History: Fixed MacOS AutoreleasePool problem"
+ModuleInfo "History: 1.08 Release"
+ModuleInfo "History: Fixed Tiger build warnings in debugger.macos.m"
+ModuleInfo "History: 1.07 Release"
+ModuleInfo "History: Improved Win32 debugger switching"
+ModuleInfo "History: Fixed macos debugger dying with extern types"
+ModuleInfo "History: 1.06 Release"
+ModuleInfo "History: Tweaked MacOS debugger"
+ModuleInfo "History: 1.05 Release"
+ModuleInfo "History: Fixed C Compiler warnings"
+ModuleInfo "History: Fixed multidim arrays hanging debugger"
+ModuleInfo "History: 1.04 Release"
+ModuleInfo "History: Fixed buffer overflow in debug strings"
+ModuleInfo "History: Modified float and double debug output to match compiler precision"
+
+?Debug
+'Import "debugger.stdio.bmx"
+'Import "debugger_mt.stdio.bmx"	'Let's give Otus's new MT friendly debugger a whirl!
+?
+
+?MacOS
+Import "appstub.macos.m"
+Import "-framework Cocoa"
+Import "-framework Carbon"
+?Win32
+Import "appstub.win32.c"
+?Linux
+Import "appstub.linux.c"
+?
+
+Extern
+Function _bb_main()
+End Extern
+
+_bb_main

+ 17 - 0
appstub.mod/appstub.linux.c

@@ -0,0 +1,17 @@
+
+#include <brl.mod/blitz.mod/blitz.h>
+
+#include <signal.h>
+
+void __bb_appstub_appstub();
+
+int main( int argc,char *argv[] ){
+
+	signal( SIGPIPE,SIG_IGN );
+	
+	bbStartup( argc,argv,0,0 );
+	
+	__bb_appstub_appstub();
+
+	return 0;
+}

+ 139 - 0
appstub.mod/appstub.macos.m

@@ -0,0 +1,139 @@
+
+#include <brl.mod/blitz.mod/blitz.h>
+
+#import <AppKit/AppKit.h>
+
+void __bb_brl_appstub_appstub();
+
+static int app_argc;
+static char **app_argv;
+
+static NSMutableArray *_appArgs;
+static NSAutoreleasePool *_globalPool;
+
+static void createAppMenu( NSString *appName ){
+
+	NSMenu *appMenu;
+	NSMenuItem *item;
+	NSString *title;
+	
+	[NSApp setMainMenu:[NSMenu new]];
+	
+	appMenu=[NSMenu new];
+	
+	title=[@"Hide " stringByAppendingString:appName];
+	[appMenu addItemWithTitle:@"Hide" action:@selector(hide:) keyEquivalent:@"h"];
+
+	item=(NSMenuItem*)[appMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
+	[item setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
+	
+	[appMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
+	
+	[appMenu addItem:[NSMenuItem separatorItem]];
+
+	title=[@"Quit " stringByAppendingString:appName];
+	[appMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
+	
+	item=[NSMenuItem new];
+	[item setSubmenu:appMenu];
+	[[NSApp mainMenu] addItem:item];
+	
+	[NSApp performSelector:NSSelectorFromString(@"setAppleMenu:") withObject:appMenu];
+}
+
+static void run(){
+
+	signal( SIGPIPE,SIG_IGN );
+	
+	bbStartup( app_argc,app_argv,0,0 );
+
+	__bb_brl_appstub_appstub();
+	
+	exit( 0 );
+}
+
+void bbFlushAutoreleasePool(){
+	[_globalPool release];
+	_globalPool=[[NSAutoreleasePool alloc] init];
+}
+
+@interface BlitzMaxAppDelegate : NSObject{
+}
+@end
+
+@implementation BlitzMaxAppDelegate
+-(void)applicationWillTerminate:(NSNotification*)notification{
+	exit(0);
+}
+
+-(NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender{
+	return NSTerminateCancel;
+}
+
+-(BOOL)application:(NSApplication*)app openFile:(NSString*)path{
+	[_appArgs addObject:path];
+	return YES;
+}
+
+-(void)applicationDidFinishLaunching:(NSNotification*)notification{
+	int i;
+	app_argc=[_appArgs count];
+	app_argv=(char**)malloc( (app_argc+1)*sizeof(char*) );
+	for( i=0;i<app_argc;++i ){
+		NSString *t=[_appArgs objectAtIndex:i];
+		char *p=(char*)malloc( [t length]+1 );
+		strcpy( p,[t cString] );
+		app_argv[i]=p;
+	}
+	app_argv[i]=0;
+	[_appArgs release];
+
+	[NSApp activateIgnoringOtherApps:YES];
+	
+	run();
+}
+@end
+
+int main( int argc,char *argv[] ){
+	int i;
+	CFURLRef url;
+	char *app_file,*p;
+	
+	_globalPool=[[NSAutoreleasePool alloc] init];
+
+	[NSApplication sharedApplication];
+	
+	app_argc=argc;
+	app_argv=argv;
+	
+	url=CFBundleCopyExecutableURL( CFBundleGetMainBundle() );
+
+	app_file=malloc( 4096 );
+	CFURLGetFileSystemRepresentation( url,true,(UInt8*)app_file,4096 );
+	
+	if( strstr( app_file,".app/Contents/MacOS/" ) ){
+		//GUI app!
+		//
+		p=strrchr( app_file,'/' );
+		if( p ){
+			++p;
+		}else{
+			 p=app_file;
+		}
+		createAppMenu( [NSString stringWithCString:p] );
+		free( app_file );
+	
+		[NSApp setDelegate:[[BlitzMaxAppDelegate alloc] init]];
+		
+		_appArgs=[[NSMutableArray arrayWithCapacity:10] retain];
+		[_appArgs addObject:[NSString stringWithCString:argv[0]] ];
+			
+		[NSApp run];
+	}else{
+		//Console app!
+		//
+		free( app_file );
+
+		run();
+	}
+}

+ 70 - 0
appstub.mod/appstub.win32.c

@@ -0,0 +1,70 @@
+
+#include <brl.mod/blitz.mod/blitz.h>
+
+#include <windows.h>
+
+//Enable exception debugging
+#define BB_DEBUG_EXCEPTIONS
+
+#ifdef BB_DEBUG_EXCEPTIONS
+
+static LONG WINAPI unhandledExceptionFilter( EXCEPTION_POINTERS *xinfo ){
+	const char *p="EXCEPTION_UNKNOWN";
+	switch( xinfo->ExceptionRecord->ExceptionCode ){
+	case EXCEPTION_ACCESS_VIOLATION:p="EXCEPTION_ACCESS_VIOLATION";break;
+	case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:p="EXCEPTION_ARRAY_BOUNDS_EXCEEDED";break;
+	case EXCEPTION_BREAKPOINT:p="EXCEPTION_BREAKPOINT";break;
+	case EXCEPTION_DATATYPE_MISALIGNMENT:p="EXCEPTION_DATATYPE_MISALIGNMENT";break;
+	case EXCEPTION_FLT_DENORMAL_OPERAND:p="EXCEPTION_FLT_DENORMAL_OPERAND";break;
+	case EXCEPTION_FLT_DIVIDE_BY_ZERO:p="EXCEPTION_FLT_DIVIDE_BY_ZERO";break;
+	case EXCEPTION_FLT_INEXACT_RESULT:p="EXCEPTION_FLT_INEXACT_RESULT";break;
+	case EXCEPTION_FLT_INVALID_OPERATION:p="EXCEPTION_FLT_INVALID_OPERATION";break;
+	case EXCEPTION_FLT_OVERFLOW:p="EXCEPTION_FLT_OVERFLOW";break;
+	case EXCEPTION_FLT_STACK_CHECK:p="EXCEPTION_FLT_STACK_CHECK";break;
+	case EXCEPTION_FLT_UNDERFLOW:p="EXCEPTION_FLT_UNDERFLOW";break;
+	case EXCEPTION_ILLEGAL_INSTRUCTION:p="EXCEPTION_ILLEGAL_INSTRUCTION";break;
+	case EXCEPTION_IN_PAGE_ERROR:p="EXCEPTION_IN_PAGE_ERROR";break;
+	case EXCEPTION_INT_DIVIDE_BY_ZERO:p="EXCEPTION_INT_DIVIDE_BY_ZERO";break;
+	case EXCEPTION_INT_OVERFLOW:p="EXCEPTION_INT_OVERFLOW";break;
+	case EXCEPTION_INVALID_DISPOSITION:p="EXCEPTION_INVALID_DISPOSITION";break;
+	case EXCEPTION_NONCONTINUABLE_EXCEPTION:p="EXCEPTION_NONCONTINUABLE_EXCEPTION";break;
+	case EXCEPTION_PRIV_INSTRUCTION:p="EXCEPTION_PRIV_INSTRUCTION";break;
+	case EXCEPTION_SINGLE_STEP:p="EXCEPTION_SINGLE_STEP";break;
+	case EXCEPTION_STACK_OVERFLOW:p="EXCEPTION_STACK_OVERFLOW";break;
+	}
+	MessageBoxA( GetActiveWindow(),p,"Windows exception",MB_OK );
+	bbOnDebugStop();
+	exit( 0 );
+}
+
+#endif
+
+void bbLibStartup();
+
+void __bb_appstub_appstub();
+
+int main( int argc,char *argv[] ){
+
+#ifdef BB_DEBUG_EXCEPTIONS
+
+	SetUnhandledExceptionFilter( unhandledExceptionFilter );
+
+#endif
+
+	bbStartup( argc,argv,0,0 );
+
+	__bb_appstub_appstub();
+
+	return 0;
+}
+
+BOOL WINAPI DllMain( HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved ){
+
+	if( fdwReason!=DLL_PROCESS_ATTACH ) return 1;
+
+	bbLibStartup();
+	
+	__bb_appstub_appstub();
+
+	return 1;
+}

+ 711 - 0
appstub.mod/debugger.stdio.bmx

@@ -0,0 +1,711 @@
+
+Strict
+
+NoDebug
+
+Private
+
+?Win32
+Extern "Win32"
+Const SW_SHOW=5
+Const SW_RESTORE=9
+Function IsIconic( hwnd )
+Function GetForegroundWindow()
+Function SetForegroundWindow( hwnd )
+Function ShowWindow( hwnd,cmdShow )
+Function GetCurrentThreadId()
+End Extern
+?
+
+?MacOS
+Extern
+Function CGDisplayIsCaptured( displayId )
+End Extern
+?
+
+Extern
+Global bbStringClass:Byte
+Global bbArrayClass:Byte
+Global bbNullObject:Byte
+Global bbEmptyArray:Byte
+Global bbEmptyString:Byte
+Global brl_blitz_NullFunctionError:Byte Ptr
+Function bbIsMainThread()="bbIsMainThread"
+Function bbGCValidate:Int( mem:Int ) = "bbGCValidate"
+End Extern
+
+Function ToHex$( val )
+	Local buf:Short[8]
+	For Local k=7 To 0 Step -1
+		Local n=(val&15)+Asc("0")
+		If n>Asc("9") n=n+(Asc("A")-Asc("9")-1)
+		buf[k]=n
+		val:Shr 4
+	Next
+	Return String.FromShorts( buf,8 ).ToLower()
+End Function
+
+Function IsAlpha( ch )
+	Return (ch>=Asc("a") And ch<=Asc("z")) Or (ch>=Asc("A") And ch<=Asc("Z"))
+End Function
+
+Function IsNumeric( ch )
+	Return ch>=Asc("0") And ch<=Asc("9")
+End Function
+
+Function IsAlphaNumeric( ch )
+	Return IsAlpha(ch) Or IsNumeric(ch)
+End Function
+
+Function IsUnderscore( ch )
+	Return ch=Asc("_")
+End Function
+
+Function Ident$( tag$ Var )
+	If Not tag Return ""
+	If Not IsAlpha( tag[0] ) And Not IsUnderscore( tag[0] ) Return ""
+	Local i=1
+	While i<tag.length And (IsAlphaNumeric(tag[i]) Or IsUnderscore(tag[i]))
+		i:+1
+	Wend
+	Local id$=tag[..i]
+	tag=tag[i..]
+	Return id
+End Function
+
+Function TypeName$( tag$ Var )
+
+	Local t$=tag[..1]
+	tag=tag[1..]
+
+	Select t
+	Case "b"
+		Return "Byte"
+	Case "s"
+		Return "Short"
+	Case "i"
+		Return "Int"
+	Case "l"
+		Return "Long"
+	Case "f"
+		Return "Float"
+	Case "d"
+		Return "Double"
+	Case "$"
+		Return "String"
+	Case "z"
+		Return "CString"
+	Case "w"
+		Return "WString"
+	Case ":","?"
+		Local id$=Ident( tag )
+		While tag And tag[0]=Asc(".")
+			tag=tag[1..]
+			id=Ident( tag )
+		Wend
+		If Not id DebugError "Invalid object typetag"
+		Return id
+	Case "*"
+		Return TypeName( tag )+" Ptr"
+	Case "["
+		While tag[..1]=","
+			tag=tag[1..]
+			t:+","
+		Wend
+		If tag[..1]<>"]" DebugError "Invalid array typetag"
+		tag=tag[1..]
+		Return TypeName( tag )+t+"]"
+	Case "("
+		If tag[..1]<>")"
+			t:+TypeName( tag )
+			While tag[..1]=","
+				tag=tag[1..]
+				t:+","+TypeName( tag )
+			Wend
+			If tag[..1]<>")" DebugError "Invalid function typetag"
+		EndIf
+		tag=tag[1..]
+		Return TypeName( tag )+t+")"
+	End Select
+
+	DebugError "Invalid debug typetag:"+t
+
+End Function
+
+'int offsets into 12 byte DebugStm struct
+Const DEBUGSTM_FILE=0
+Const DEBUGSTM_LINE=1
+Const DEBUGSTM_CHAR=2
+
+'int offsets into 16 byte DebugDecl struct
+Const DEBUGDECL_KIND=0
+Const DEBUGDECL_NAME=1
+Const DEBUGDECL_TYPE=2
+Const DEBUGDECL_ADDR=3
+
+'DEBUGDECL_KIND values
+Const DEBUGDECLKIND_END=0
+Const DEBUGDECLKIND_CONST=1
+Const DEBUGDECLKIND_LOCAL=2
+Const DEBUGDECLKIND_FIELD=3
+Const DEBUGDECLKIND_GLOBAL=4
+Const DEBUGDECLKIND_VARPARAM=5
+Const DEBUGDECLKIND_TYPEMETHOD=6
+Const DEBUGDECLKIND_TYPEFUNCTION=7
+
+'int offsets into 12+n_decls*4 byte DebugScope struct
+Const DEBUGSCOPE_KIND=0
+Const DEBUGSCOPE_NAME=1
+Const DEBUGSCOPE_DECLS=2
+
+'DEBUGSCOPE_KIND values
+Const DEBUGSCOPEKIND_FUNCTION=1
+Const DEBUGSCOPEKIND_TYPE=2
+Const DEBUGSCOPEKIND_LOCAL=3
+
+Function DebugError( t$ )
+	WriteStderr "Debugger Error:"+t+"~n"
+	End
+End Function
+
+Function DebugStmFile$( stm:Int Ptr )
+	Return String.FromCString( Byte Ptr stm[DEBUGSTM_FILE] )
+End Function
+
+Function DebugStmLine( stm:Int Ptr )
+	Return stm[DEBUGSTM_LINE]
+End Function
+
+Function DebugStmChar( stm:Int Ptr )
+	Return stm[DEBUGSTM_CHAR]
+End Function
+
+Function DebugDeclKind$( decl:Int Ptr )
+	Select decl[DEBUGDECL_KIND]
+	Case DEBUGDECLKIND_CONST Return "Const"
+	Case DEBUGDECLKIND_LOCAL Return "Local"
+	Case DEBUGDECLKIND_FIELD Return "Field"
+	Case DEBUGDECLKIND_GLOBAL Return "Global"
+	Case DEBUGDECLKIND_VARPARAM Return "Local"
+	End Select
+	DebugError "Invalid decl kind"
+End Function
+
+Function DebugDeclName$( decl:Int Ptr )
+	Return String.FromCString( Byte Ptr decl[DEBUGDECL_NAME] )
+End Function
+
+Function DebugDeclType$( decl:Int Ptr )
+	Local t$=String.FromCString( Byte Ptr decl[DEBUGDECL_TYPE] )
+	Local ty$=TypeName( t )
+	Return ty
+End Function
+
+Function DebugDeclSize( decl:Int Ptr )
+
+	Local tag=(Byte Ptr Ptr(decl+DEBUGDECL_TYPE))[0][0]
+
+	Select tag
+	Case Asc("b") Return 1
+	Case Asc("s") Return 2
+	Case Asc("l") Return 8
+	Case Asc("d") Return 8
+	End Select
+	
+	Return 4
+
+End Function
+
+Function DebugEscapeString$( s$ )
+	s=s.Replace( "~~","~~~~")
+	s=s.Replace( "~0","~~0" )
+	s=s.Replace( "~t","~~t" )
+	s=s.Replace( "~n","~~n" )
+	s=s.Replace( "~r","~~r" )
+	s=s.Replace( "~q","~~q" )
+	Return "~q"+s+"~q"
+End Function
+
+Function DebugDeclValue$( decl:Int Ptr,inst:Byte Ptr )
+	If decl[DEBUGDECL_KIND]=DEBUGDECLKIND_CONST
+		Local p:Byte Ptr=Byte Ptr decl[DEBUGDECL_ADDR]
+		Return DebugEscapeString(String.FromShorts( Short Ptr(p+12),(Int Ptr (p+8))[0] ))
+	EndIf
+
+	Local p:Byte Ptr
+	Select decl[DEBUGDECL_KIND]
+	Case DEBUGDECLKIND_GLOBAL
+		p=Byte Ptr decl[DEBUGDECL_ADDR]
+	Case DEBUGDECLKIND_LOCAL,DEBUGDECLKIND_FIELD
+		p=Byte Ptr (inst+decl[DEBUGDECL_ADDR])
+	Case DEBUGDECLKIND_VARPARAM
+		p=Byte Ptr (inst+decl[DEBUGDECL_ADDR])
+		p=Byte Ptr ( (Int Ptr p)[0] )
+	Default
+		DebugError "Invalid decl kind"
+	End Select
+	
+	Local tag=(Byte Ptr Ptr(decl+DEBUGDECL_TYPE))[0][0]
+	
+	Select tag
+	Case Asc("b")
+		Return String.FromInt( (Byte Ptr p)[0] )
+	Case Asc("s")
+		Return String.FromInt( (Short Ptr p)[0] )
+	Case Asc("i")
+		Return String.FromInt( (Int Ptr p)[0] )
+	Case Asc("l")
+		Return String.FromLong( (Long Ptr p)[0] )
+	Case Asc("f")
+		Return String.FromFloat( (Float Ptr p)[0] )
+	Case Asc("d")
+		Return String.FromDouble( (Double Ptr p)[0] )
+	Case Asc("$")
+		p=(Byte Ptr Ptr p)[0]
+		Local sz=Int Ptr(p+8)[0]
+		Local s$=String.FromShorts( Short Ptr(p+12),sz )
+		Return DebugEscapeString( s )
+	Case Asc("z")
+		p=(Byte Ptr Ptr p)[0]
+		If Not p Return "Null"
+		Local s$=String.FromCString( p )
+		Return DebugEscapeString( s )
+	Case Asc("w")
+		p=(Byte Ptr Ptr p)[0]
+		If Not p Return "Null"
+		Local s$=String.FromWString( Short Ptr p )
+		Return DebugEscapeString( s )
+	Case Asc("*"),Asc("?")
+		Return "$"+ToHex( (Int Ptr p)[0] )
+	Case Asc("(")
+		p=(Byte Ptr Ptr p)[0]
+		If p=brl_blitz_NullFunctionError Return "Null"
+	Case Asc(":")
+		p=(Byte Ptr Ptr p)[0]
+		If p=Varptr bbNullObject Return "Null"
+		If p=Varptr bbEmptyArray Return "Null[]"
+		If p=Varptr bbEmptyString Return "Null$"
+	Case Asc("[")
+		p=(Byte Ptr Ptr p)[0]
+		If Not p Return "Null"
+		If Not (Int Ptr (p+20))[0] Return "Null"
+	Default
+		DebugError "Invalid decl typetag:"+Chr(tag)
+	End Select
+	
+	Return "$"+ToHex( Int p )
+
+End Function
+
+Function DebugScopeKind$( scope:Int Ptr )
+	Select scope[DEBUGSCOPE_KIND]
+	Case DEBUGSCOPEKIND_FUNCTION Return "Function"
+	Case DEBUGSCOPEKIND_TYPE Return "Type"
+	Case DEBUGSCOPEKIND_LOCAL Return "Local"
+	End Select
+	DebugError "Invalid scope kind"
+End Function
+
+Function DebugScopeName$( scope:Int Ptr )
+	Return String.FromCString( Byte Ptr scope[DEBUGSCOPE_NAME] )
+End Function
+
+Function DebugScopeDecls:Int Ptr[]( scope:Int Ptr )
+	Local n,p:Int Ptr=scope+DEBUGSCOPE_DECLS
+	While p[n]<>DEBUGDECLKIND_END
+		n:+1
+	Wend
+	Local decls:Int Ptr[n]
+	For Local i=0 Until n
+		decls[i]=p+i*4
+	Next
+	Return decls
+End Function
+
+Function DebugObjectScope:Int Ptr( inst:Byte Ptr )
+	Local clas:Int Ptr Ptr=(Int Ptr Ptr Ptr inst)[0]
+	Return clas[2]
+End Function
+
+Extern
+Global bbOnDebugStop()
+Global bbOnDebugLog( message$ )
+Global bbOnDebugEnterStm( stm:Int Ptr )
+Global bbOnDebugEnterScope( scope:Int Ptr,inst:Byte Ptr )
+Global bbOnDebugLeaveScope()
+Global bbOnDebugPushExState()
+Global bbOnDebugPopExState()
+Global bbOnDebugUnhandledEx( ex:Object )
+End Extern
+
+bbOnDebugStop=OnDebugStop
+bbOnDebugLog=OnDebugLog
+bbOnDebugEnterStm=OnDebugEnterStm
+bbOnDebugEnterScope=OnDebugEnterScope
+bbOnDebugLeaveScope=OnDebugLeaveScope
+bbOnDebugPushExState=OnDebugPushExState
+bbOnDebugPopExState=OnDebugPopExState
+bbOnDebugUnhandledEx=OnDebugUnhandledEx
+
+?Win32
+Global _ideHwnd=GetForegroundWindow();
+Global _appHwnd
+?
+
+'********** Debugger code here **********
+
+Const MODE_RUN=0
+Const MODE_STEP=1
+Const MODE_STEPIN=2
+Const MODE_STEPOUT=3
+
+Type TScope
+	Field scope:Int Ptr,inst:Byte Ptr,stm:Int Ptr
+End Type
+
+Type TExState
+	Field scopeStackTop
+End Type
+
+Global mode,debugLevel,funcLevel
+Global currentScope:TScope=New TScope
+Global scopeStack:TScope[],scopeStackTop
+Global exStateStack:TExState[],exStateStackTop
+
+Function ReadDebug$()
+	Return ReadStdin()
+End Function
+
+Function WriteDebug( t$ )
+	WriteStderr "~~>"+t
+End Function
+
+Function DumpScope( scope:Int Ptr,inst:Byte Ptr )
+
+	Local decl:Int Ptr=scope+DEBUGSCOPE_DECLS
+	
+	Local kind$=DebugScopeKind( scope ),name$=DebugScopeName( scope )
+	
+	If Not name name="<local>"
+	
+	WriteDebug kind+" "+name+"~n"
+	
+	While decl[DEBUGDECL_KIND]<>DEBUGDECLKIND_END
+	
+		Select decl[DEBUGDECL_KIND]
+		Case DEBUGDECLKIND_TYPEMETHOD,DEBUGDECLKIND_TYPEFUNCTION
+			decl:+4
+			Continue
+		End Select
+
+		Local kind$=DebugDeclKind( decl )
+		Local name$=DebugDeclname( decl )
+		Local tipe$=DebugDeclType( decl )
+		Local value$=DebugDeclValue( decl,inst )
+		
+		WriteDebug kind+" "+name+":"+tipe+"="+value+"~n"
+
+		decl:+4	
+	Wend
+End Function
+
+Function DumpClassScope( clas:Int Ptr,inst:Byte Ptr )
+
+	Local supa:Int Ptr=Int Ptr clas[0]
+	
+	If Not supa Return
+	
+	DumpClassScope supa,inst
+	
+	DumpScope Int Ptr clas[2],inst
+
+End Function
+
+Function DumpObject( inst:Byte Ptr,index )
+
+	Local clas:Int Ptr=(Int Ptr Ptr inst)[0]
+	
+	If clas=Int Ptr Varptr bbStringClass
+
+		WriteDebug DebugEscapeString(String.FromShorts( Short Ptr(inst+12),(Int Ptr (inst+8))[0] ))+"~n"
+
+		Return
+
+	Else If clas=Int Ptr Varptr bbArrayClass
+	
+		Local length=(Int Ptr (inst+20))[0]
+		
+		If Not length Return
+		
+		Local decl:Int[3]
+		decl[0]=DEBUGDECLKIND_LOCAL
+		decl[2]=(Int Ptr (inst+8))[0]
+		
+		Local sz=DebugDeclSize( decl )
+		
+		Local p:Byte Ptr=Byte Ptr(20+(Int Ptr (inst+12))[0]*4)
+
+		For Local i=1 To 10
+
+			If index>=length Exit
+			
+			decl[3]=Int(p+index*sz)
+		
+			Local value$=DebugDeclValue( decl,inst )
+			
+			WriteDebug "["+index+"]="+value+"~n"
+			
+			index:+1
+			
+		Next
+		
+		If index<length
+
+			WriteDebug "...=$"+ToHex(Int inst)+":"+index+"~n"
+	
+		EndIf
+		
+	Else
+			
+		If Not clas[0]
+			WriteDebug "Object~n"
+			Return
+		EndIf
+	
+		DumpClassScope clas,inst
+	
+	EndIf
+	
+End Function
+
+Function DumpScopeStack()
+	For Local i=Max(scopeStackTop-100,0) Until scopeStackTop
+		Local t:TScope=scopeStack[i]
+		Local stm:Int Ptr=t.stm
+		If Not stm Continue
+		WriteDebug "@"+DebugStmFile(stm)+"<"+DebugStmLine(stm)+","+DebugStmChar(stm)+">~n"
+		DumpScope t.scope,t.inst
+	Next
+End Function
+
+Function UpdateDebug( msg$ )
+	Global indebug
+	If indebug Return
+	indebug=True
+	
+?Win32
+	_appHwnd=GetForegroundWindow();
+	'SetForegroundWindow( _ideHwnd );
+?
+?MacOs
+	'fullscreen debug too hard in MacOS!
+	If CGDisplayIsCaptured( 0 )
+		WriteStdout msg
+		End
+	EndIf
+?
+	WriteDebug msg
+	Repeat
+		WriteDebug "~n"
+		Local line$=ReadDebug()
+
+		Select line[..1].ToLower()
+		Case "r"
+			mode=MODE_RUN
+			Exit
+		Case "s"
+			mode=MODE_STEP
+			debugLevel=funcLevel
+			Exit
+		Case "e"
+			mode=MODE_STEPIN
+			Exit
+		Case "l"
+			mode=MODE_STEPOUT
+			debugLevel=scopeStackTop-1
+			Exit
+		Case "t"
+			WriteDebug "StackTrace{~n"
+			DumpScopeStack
+			WriteDebug "}~n"
+		Case "d"
+			Local t$=line[1..].Trim()
+			Local index
+			Local i=t.Find(":")
+			If i<>-1
+				index=Int( t[i+1..] )
+				t=t[..i]
+			EndIf
+			If t[..1]="$" t=t[1..].Trim()
+			If t[..2].ToLower()="0x" t=t[2..].Trim()
+			Local pointer = Int( "$"+t )
+			If Not (pointer And bbGCValidate(pointer)) Then Continue
+			Local inst:Int Ptr=Int Ptr pointer
+			
+			Local cmd$="ObjectDump@"+ToHex( Int inst )
+			If i<>-1 cmd:+":"+index
+			WriteDebug cmd$+"{~n"
+
+			DumpObject inst,index
+			WriteDebug "}~n"
+		Case "h"
+			WriteDebug "T - Stack trace~n"
+			WriteDebug "R - Run from here~n"
+			WriteDebug "S - Step through source code~n"
+			WriteDebug "E - Step into function call~n"
+			WriteDebug "L - Leave function or local block~n"
+			WriteDebug "Q - Quit~n"
+			WriteDebug "H - This text~n"
+			WriteDebug "Dxxxxxxxx - Dump object at hex address xxxxxxxx~n"
+		Case "q"
+			End
+		End Select
+	Forever
+
+?Win32
+	If _appHwnd And _appHwnd<>_ideHwnd 
+		If IsIconic(_apphwnd)
+			ShowWindow _appHwnd,SW_RESTORE
+		Else
+			ShowWindow _appHwnd,SW_SHOW
+		EndIf		
+		_apphwnd=0
+	EndIf
+?
+	indebug=False
+End Function
+
+Function OnDebugStop()
+?Threaded
+	If Not bbIsMainThread() Return
+?
+	UpdateDebug "DebugStop:~n"
+End Function
+
+Function OnDebugLog( message$ )
+?Threaded
+	If Not bbIsMainThread() Return
+?
+	WriteStdout "DebugLog:"+message+"~n"
+End Function
+
+Function OnDebugEnterStm( stm:Int Ptr )
+?Threaded
+	If Not bbIsMainThread() Return
+?
+	currentScope.stm=stm
+	
+	Select mode
+	Case MODE_RUN
+		Return
+	Case MODE_STEP
+		If funcLevel>debugLevel 
+			Return
+		EndIf
+	Case MODE_STEPOUT
+		If scopeStackTop>debugLevel
+			Return
+		EndIf
+	End Select
+	
+	UpdateDebug "Debug:~n"
+End Function
+
+Function OnDebugEnterScope( scope:Int Ptr,inst:Byte Ptr )
+?Threaded
+	If Not bbIsMainThread() Return
+?
+	GCSuspend
+
+	If scopeStackTop=scopeStack.length 
+		scopeStack=scopeStack[..scopeStackTop * 2 + 32]
+		For Local i=scopeStackTop Until scopeStack.length
+			scopeStack[i]=New TScope
+		Next
+	EndIf
+	
+	currentScope=scopeStack[scopeStackTop]
+
+	currentScope.scope=scope
+	currentScope.inst=inst
+
+	scopeStackTop:+1
+
+	If currentScope.scope[DEBUGSCOPE_KIND]=DEBUGSCOPEKIND_FUNCTION funcLevel:+1
+
+	GCResume	
+End Function
+
+Function OnDebugLeaveScope()
+?Threaded
+	If Not bbIsMainThread() Return
+?
+	GCSuspend
+
+	If Not scopeStackTop DebugError "scope stack underflow"
+
+	If currentScope.scope[DEBUGSCOPE_KIND]=DEBUGSCOPEKIND_FUNCTION funcLevel:-1
+	
+	scopeStackTop:-1
+
+	If scopeStackTop
+		currentScope=scopeStack[scopeStackTop-1]
+	Else
+		currentScope=New TScope
+	EndIf
+
+	GCResume	
+End Function
+
+Function OnDebugPushExState()
+?Threaded
+	If Not bbIsMainThread() Return
+?
+	GCSuspend
+
+	If exStateStackTop=exStateStack.length 
+		exStateStack=exStateStack[..exStateStackTop * 2 + 32]
+		For Local i=exStateStackTop Until exStateStack.length
+			exStateStack[i]=New TExState
+		Next
+	EndIf
+	
+	exStateStack[exStateStackTop].scopeStackTop=scopeStackTop
+	
+	exStateStackTop:+1
+
+	GCResume	
+End Function
+
+Function OnDebugPopExState()
+?Threaded
+	If Not bbIsMainThread() Return
+?
+	GCSuspend
+
+	If Not exStateStackTop DebugError "exception stack underflow"
+
+	exStateStackTop:-1
+
+	scopeStackTop=exStateStack[exStateStackTop].scopeStackTop
+	
+	If scopeStackTop
+		currentScope=scopeStack[scopeStackTop-1]
+	Else
+		currentScope=New TScope
+	EndIf
+
+	GCResume	
+End Function
+
+Function OnDebugUnhandledEx( ex:Object )
+?Threaded
+	If Not bbIsMainThread() Return
+?
+	GCSuspend
+	
+	UpdateDebug "Unhandled Exception:"+ex.ToString()+"~n"
+
+	GCResume	
+End Function
+

+ 725 - 0
appstub.mod/debugger_mt.stdio.bmx

@@ -0,0 +1,725 @@
+Strict
+
+NoDebug
+
+Private
+
+?Win32
+Extern "Win32"
+Const SW_SHOW=5
+Const SW_RESTORE=9
+Function IsIconic( hwnd )
+Function GetForegroundWindow()
+Function SetForegroundWindow( hwnd )
+Function ShowWindow( hwnd,cmdShow )
+Function GetCurrentThreadId()
+End Extern
+?
+
+?MacOS
+Extern
+Function CGDisplayIsCaptured( displayId )
+End Extern
+?
+
+Extern
+Global bbStringClass:Byte
+Global bbArrayClass:Byte
+Global bbNullObject:Byte
+Global bbEmptyArray:Byte
+Global bbEmptyString:Byte
+Global brl_blitz_NullFunctionError:Byte Ptr
+Function bbIsMainThread()="bbIsMainThread"
+Function bbGCValidate:Int( mem:Int ) = "bbGCValidate"
+End Extern
+
+Function ToHex$( val )
+	Local buf:Short[8]
+	For Local k=7 To 0 Step -1
+		Local n=(val&15)+Asc("0")
+		If n>Asc("9") n=n+(Asc("A")-Asc("9")-1)
+		buf[k]=n
+		val:Shr 4
+	Next
+	Return String.FromShorts( buf,8 ).ToLower()
+End Function
+
+Function IsAlpha( ch )
+	Return (ch>=Asc("a") And ch<=Asc("z")) Or (ch>=Asc("A") And ch<=Asc("Z"))
+End Function
+
+Function IsNumeric( ch )
+	Return ch>=Asc("0") And ch<=Asc("9")
+End Function
+
+Function IsAlphaNumeric( ch )
+	Return IsAlpha(ch) Or IsNumeric(ch)
+End Function
+
+Function IsUnderscore( ch )
+	Return ch=Asc("_")
+End Function
+
+Function Ident$( tag$ Var )
+	If Not tag Return ""
+	If Not IsAlpha( tag[0] ) And Not IsUnderscore( tag[0] ) Return ""
+	Local i=1
+	While i<tag.length And (IsAlphaNumeric(tag[i]) Or IsUnderscore(tag[i]))
+		i:+1
+	Wend
+	Local id$=tag[..i]
+	tag=tag[i..]
+	Return id
+End Function
+
+Function TypeName$( tag$ Var )
+
+	Local t$=tag[..1]
+	tag=tag[1..]
+
+	Select t
+	Case "b"
+		Return "Byte"
+	Case "s"
+		Return "Short"
+	Case "i"
+		Return "Int"
+	Case "l"
+		Return "Long"
+	Case "f"
+		Return "Float"
+	Case "d"
+		Return "Double"
+	Case "$"
+		Return "String"
+	Case "z"
+		Return "CString"
+	Case "w"
+		Return "WString"
+	Case ":","?"
+		Local id$=Ident( tag )
+		While tag And tag[0]=Asc(".")
+			tag=tag[1..]
+			id=Ident( tag )
+		Wend
+		If Not id DebugError "Invalid object typetag"
+		Return id
+	Case "*"
+		Return TypeName( tag )+" Ptr"
+	Case "["
+		While tag[..1]=","
+			tag=tag[1..]
+			t:+","
+		Wend
+		If tag[..1]<>"]" DebugError "Invalid array typetag"
+		tag=tag[1..]
+		Return TypeName( tag )+t+"]"
+	Case "("
+		If tag[..1]<>")"
+			t:+TypeName( tag )
+			While tag[..1]=","
+				tag=tag[1..]
+				t:+","+TypeName( tag )
+			Wend
+			If tag[..1]<>")" DebugError "Invalid function typetag"
+		EndIf
+		tag=tag[1..]
+		Return TypeName( tag )+t+")"
+	End Select
+
+	DebugError "Invalid debug typetag:"+t
+
+End Function
+
+'int offsets into 12 byte DebugStm struct
+Const DEBUGSTM_FILE=0
+Const DEBUGSTM_LINE=1
+Const DEBUGSTM_CHAR=2
+
+'int offsets into 16 byte DebugDecl struct
+Const DEBUGDECL_KIND=0
+Const DEBUGDECL_NAME=1
+Const DEBUGDECL_TYPE=2
+Const DEBUGDECL_ADDR=3
+
+'DEBUGDECL_KIND values
+Const DEBUGDECLKIND_END=0
+Const DEBUGDECLKIND_CONST=1
+Const DEBUGDECLKIND_LOCAL=2
+Const DEBUGDECLKIND_FIELD=3
+Const DEBUGDECLKIND_GLOBAL=4
+Const DEBUGDECLKIND_VARPARAM=5
+Const DEBUGDECLKIND_TYPEMETHOD=6
+Const DEBUGDECLKIND_TYPEFUNCTION=7
+
+'int offsets into 12+n_decls*4 byte DebugScope struct
+Const DEBUGSCOPE_KIND=0
+Const DEBUGSCOPE_NAME=1
+Const DEBUGSCOPE_DECLS=2
+
+'DEBUGSCOPE_KIND values
+Const DEBUGSCOPEKIND_FUNCTION=1
+Const DEBUGSCOPEKIND_TYPE=2
+Const DEBUGSCOPEKIND_LOCAL=3
+
+Function DebugError( t$ )
+	WriteStderr "Debugger Error:"+t+"~n"
+	End
+End Function
+
+Function DebugStmFile$( stm:Int Ptr )
+	Return String.FromCString( Byte Ptr stm[DEBUGSTM_FILE] )
+End Function
+
+Function DebugStmLine( stm:Int Ptr )
+	Return stm[DEBUGSTM_LINE]
+End Function
+
+Function DebugStmChar( stm:Int Ptr )
+	Return stm[DEBUGSTM_CHAR]
+End Function
+
+Function DebugDeclKind$( decl:Int Ptr )
+	Select decl[DEBUGDECL_KIND]
+	Case DEBUGDECLKIND_CONST Return "Const"
+	Case DEBUGDECLKIND_LOCAL Return "Local"
+	Case DEBUGDECLKIND_FIELD Return "Field"
+	Case DEBUGDECLKIND_GLOBAL Return "Global"
+	Case DEBUGDECLKIND_VARPARAM Return "Local"
+	End Select
+	DebugError "Invalid decl kind"
+End Function
+
+Function DebugDeclName$( decl:Int Ptr )
+	Return String.FromCString( Byte Ptr decl[DEBUGDECL_NAME] )
+End Function
+
+Function DebugDeclType$( decl:Int Ptr )
+	Local t$=String.FromCString( Byte Ptr decl[DEBUGDECL_TYPE] )
+	Local ty$=TypeName( t )
+	Return ty
+End Function
+
+Function DebugDeclSize( decl:Int Ptr )
+
+	Local tag=(Byte Ptr Ptr(decl+DEBUGDECL_TYPE))[0][0]
+
+	Select tag
+	Case Asc("b") Return 1
+	Case Asc("s") Return 2
+	Case Asc("l") Return 8
+	Case Asc("d") Return 8
+	End Select
+	
+	Return 4
+
+End Function
+
+Function DebugEscapeString$( s$ )
+	If s.length>4096 s=s[..4096]
+	s=s.Replace( "~~","~~~~")
+	s=s.Replace( "~0","~~0" )
+	s=s.Replace( "~t","~~t" )
+	s=s.Replace( "~n","~~n" )
+	s=s.Replace( "~r","~~r" )
+	s=s.Replace( "~q","~~q" )
+	Return "~q"+s+"~q"
+End Function
+
+Function DebugDeclValue$( decl:Int Ptr,inst:Byte Ptr )
+	If decl[DEBUGDECL_KIND]=DEBUGDECLKIND_CONST
+		Local p:Byte Ptr=Byte Ptr decl[DEBUGDECL_ADDR]
+		Return DebugEscapeString(String.FromShorts( Short Ptr(p+12),(Int Ptr (p+8))[0] ))
+	EndIf
+
+	Local p:Byte Ptr
+	Select decl[DEBUGDECL_KIND]
+	Case DEBUGDECLKIND_GLOBAL
+		p=Byte Ptr decl[DEBUGDECL_ADDR]
+	Case DEBUGDECLKIND_LOCAL,DEBUGDECLKIND_FIELD
+		p=Byte Ptr (inst+decl[DEBUGDECL_ADDR])
+	Case DEBUGDECLKIND_VARPARAM
+		p=Byte Ptr (inst+decl[DEBUGDECL_ADDR])
+		p=Byte Ptr ( (Int Ptr p)[0] )
+	Default
+		DebugError "Invalid decl kind"
+	End Select
+	
+	Local tag=(Byte Ptr Ptr(decl+DEBUGDECL_TYPE))[0][0]
+	
+	Select tag
+	Case Asc("b")
+		Return String.FromInt( (Byte Ptr p)[0] )
+	Case Asc("s")
+		Return String.FromInt( (Short Ptr p)[0] )
+	Case Asc("i")
+		Return String.FromInt( (Int Ptr p)[0] )
+	Case Asc("l")
+		Return String.FromLong( (Long Ptr p)[0] )
+	Case Asc("f")
+		Return String.FromFloat( (Float Ptr p)[0] )
+	Case Asc("d")
+		Return String.FromDouble( (Double Ptr p)[0] )
+	Case Asc("$")
+		p=(Byte Ptr Ptr p)[0]
+		Local sz=Int Ptr(p+8)[0]
+		Local s$=String.FromShorts( Short Ptr(p+12),sz )
+		Return DebugEscapeString( s )
+	Case Asc("z")
+		p=(Byte Ptr Ptr p)[0]
+		If Not p Return "Null"
+		Local s$=String.FromCString( p )
+		Return DebugEscapeString( s )
+	Case Asc("w")
+		p=(Byte Ptr Ptr p)[0]
+		If Not p Return "Null"
+		Local s$=String.FromWString( Short Ptr p )
+		Return DebugEscapeString( s )
+	Case Asc("*"),Asc("?")
+		Return "$"+ToHex( (Int Ptr p)[0] )
+	Case Asc("(")
+		p=(Byte Ptr Ptr p)[0]
+		If p=brl_blitz_NullFunctionError Return "Null"
+	Case Asc(":")
+		p=(Byte Ptr Ptr p)[0]
+		If p=Varptr bbNullObject Return "Null"
+		If p=Varptr bbEmptyArray Return "Null[]"
+		If p=Varptr bbEmptyString Return "Null$"
+	Case Asc("[")
+		p=(Byte Ptr Ptr p)[0]
+		If Not p Return "Null"
+		If Not (Int Ptr (p+20))[0] Return "Null"
+	Default
+		DebugError "Invalid decl typetag:"+Chr(tag)
+	End Select
+	
+	Return "$"+ToHex( Int p )
+
+End Function
+
+Function DebugScopeKind$( scope:Int Ptr )
+	Select scope[DEBUGSCOPE_KIND]
+	Case DEBUGSCOPEKIND_FUNCTION Return "Function"
+	Case DEBUGSCOPEKIND_TYPE Return "Type"
+	Case DEBUGSCOPEKIND_LOCAL Return "Local"
+	End Select
+	DebugError "Invalid scope kind"
+End Function
+
+Function DebugScopeName$( scope:Int Ptr )
+	Return String.FromCString( Byte Ptr scope[DEBUGSCOPE_NAME] )
+End Function
+
+Function DebugScopeDecls:Int Ptr[]( scope:Int Ptr )
+	Local n,p:Int Ptr=scope+DEBUGSCOPE_DECLS
+	While p[n]<>DEBUGDECLKIND_END
+		n:+1
+	Wend
+	Local decls:Int Ptr[n]
+	For Local i=0 Until n
+		decls[i]=p+i*4
+	Next
+	Return decls
+End Function
+
+Function DebugObjectScope:Int Ptr( inst:Byte Ptr )
+	Local clas:Int Ptr Ptr=(Int Ptr Ptr Ptr inst)[0]
+	Return clas[2]
+End Function
+
+Extern
+Global bbOnDebugStop()
+Global bbOnDebugLog( message$ )
+Global bbOnDebugEnterStm( stm:Int Ptr )
+Global bbOnDebugEnterScope( scope:Int Ptr,inst:Byte Ptr )
+Global bbOnDebugLeaveScope()
+Global bbOnDebugPushExState()
+Global bbOnDebugPopExState()
+Global bbOnDebugUnhandledEx( ex:Object )
+End Extern
+
+bbOnDebugStop=OnDebugStop
+bbOnDebugLog=OnDebugLog
+bbOnDebugEnterStm=OnDebugEnterStm
+bbOnDebugEnterScope=OnDebugEnterScope
+bbOnDebugLeaveScope=OnDebugLeaveScope
+bbOnDebugPushExState=OnDebugPushExState
+bbOnDebugPopExState=OnDebugPopExState
+bbOnDebugUnhandledEx=OnDebugUnhandledEx
+
+?Win32
+Global _ideHwnd=GetForegroundWindow();
+Global _appHwnd
+?
+
+'********** Debugger code here **********
+
+Const MODE_RUN=0
+Const MODE_STEP=1
+Const MODE_STEPIN=2
+Const MODE_STEPOUT=3
+
+Type TScope
+	Field scope:Int Ptr,inst:Byte Ptr,stm:Int Ptr
+End Type
+
+Type TExState
+	Field scopeStackTop
+End Type
+
+Type TDbgState
+	Field mode,debugLevel,funcLevel
+	Field currentScope:TScope=New TScope
+	Field scopeStack:TScope[],scopeStackTop
+	Field exStateStack:TExState[],exStateStackTop
+End Type
+
+?Threaded
+Extern
+Function bbThreadAllocData()
+Function bbThreadSetData( index,data:Object )
+Function bbThreadGetData:TDbgState( index )
+End Extern
+?
+
+Function GetDbgState:TDbgState()
+	Global dbgStateMain:TDbgState=New TDbgState
+?Threaded
+	If bbIsMainThread() Return dbgStateMain
+	Global dbgStateId:Int=bbThreadAllocData()
+	Local dbgState:TDbgState=bbThreadGetData( dbgStateId )
+	If Not dbgState
+		dbgState = New TDbgState
+		bbThreadSetData( dbgStateId,dbgState )
+	End If
+	Return dbgState
+?Not Threaded
+	Return dbgStateMain
+?
+End Function
+
+Function ReadDebug$()
+	Return ReadStdin()
+End Function
+
+Function WriteDebug( t$ )
+	WriteStderr "~~>"+t
+End Function
+
+Function DumpScope( scope:Int Ptr,inst:Byte Ptr )
+
+	Local decl:Int Ptr=scope+DEBUGSCOPE_DECLS
+	
+	Local kind$=DebugScopeKind( scope ),name$=DebugScopeName( scope )
+	
+	If Not name name="<local>"
+	
+	WriteDebug kind+" "+name+"~n"
+	
+	While decl[DEBUGDECL_KIND]<>DEBUGDECLKIND_END
+	
+		Select decl[DEBUGDECL_KIND]
+		Case DEBUGDECLKIND_TYPEMETHOD,DEBUGDECLKIND_TYPEFUNCTION
+			decl:+4
+			Continue
+		End Select
+
+		Local kind$=DebugDeclKind( decl )
+		Local name$=DebugDeclname( decl )
+		Local tipe$=DebugDeclType( decl )
+		Local value$=DebugDeclValue( decl,inst )
+		
+		WriteDebug kind+" "+name+":"+tipe+"="+value+"~n"
+
+		decl:+4	
+	Wend
+End Function
+
+Function DumpClassScope( clas:Int Ptr,inst:Byte Ptr )
+
+	Local supa:Int Ptr=Int Ptr clas[0]
+	
+	If Not supa Return
+	
+	DumpClassScope supa,inst
+	
+	DumpScope Int Ptr clas[2],inst
+
+End Function
+
+Function DumpObject( inst:Byte Ptr,index )
+
+	Local clas:Int Ptr=(Int Ptr Ptr inst)[0]
+	
+	If clas=Int Ptr Varptr bbStringClass
+
+		WriteDebug DebugEscapeString(String.FromShorts( Short Ptr(inst+12),(Int Ptr (inst+8))[0] ))+"~n"
+
+		Return
+
+	Else If clas=Int Ptr Varptr bbArrayClass
+	
+		Local length=(Int Ptr (inst+20))[0]
+
+		If Not length Return
+		
+		Local decl:Int[3]
+		decl[0]=DEBUGDECLKIND_LOCAL
+		decl[2]=(Int Ptr (inst+8))[0]
+		
+		Local sz=DebugDeclSize( decl )
+		
+		Local p:Byte Ptr=Byte Ptr(20+(Int Ptr (inst+12))[0]*4)
+
+		For Local i=1 To 10
+
+			If index>=length Exit
+			
+			decl[3]=Int(p+index*sz)
+		
+			Local value$=DebugDeclValue( decl,inst )
+			
+			WriteDebug "["+index+"]="+value+"~n"
+			
+			index:+1
+			
+		Next
+		
+		If index<length
+
+			WriteDebug "...=$"+ToHex(Int inst)+":"+index+"~n"
+	
+		EndIf
+		
+	Else
+			
+		If Not clas[0]
+			WriteDebug "Object~n"
+			Return
+		EndIf
+	
+		DumpClassScope clas,inst
+	
+	EndIf
+	
+End Function
+
+Function DumpScopeStack()
+	Local dbgState:TDbgState = GetDbgState()
+	For Local i=Max(dbgState.scopeStackTop-100,0) Until dbgState.scopeStackTop
+		Local t:TScope=dbgState.scopeStack[i]
+		Local stm:Int Ptr=t.stm
+		If Not stm Continue
+		WriteDebug "@"+DebugStmFile(stm)+"<"+DebugStmLine(stm)+","+DebugStmChar(stm)+">~n"
+		DumpScope t.scope,t.inst
+	Next
+End Function
+
+Function UpdateDebug( msg$ )
+	Global indebug
+	If indebug Return
+	indebug=True
+	
+	Local dbgState:TDbgState = GetDbgState()
+	
+?Win32
+	_appHwnd=GetForegroundWindow();
+	'SetForegroundWindow( _ideHwnd );
+?
+?MacOs
+	'fullscreen debug too hard in MacOS!
+	If CGDisplayIsCaptured( 0 )
+		WriteStdout msg
+		End
+	EndIf
+?
+	WriteDebug msg
+	Repeat
+		WriteDebug "~n"
+		Local line$=ReadDebug()
+
+		Select line[..1].ToLower()
+		Case "r"
+			dbgState.mode=MODE_RUN
+			Exit
+		Case "s"
+			dbgState.mode=MODE_STEP
+			dbgState.debugLevel=dbgState.funcLevel
+			Exit
+		Case "e"
+			dbgState.mode=MODE_STEPIN
+			Exit
+		Case "l"
+			dbgState.mode=MODE_STEPOUT
+			dbgState.debugLevel=dbgState.scopeStackTop-1
+			Exit
+		Case "t"
+			WriteDebug "StackTrace{~n"
+			DumpScopeStack
+			WriteDebug "}~n"
+		Case "d"
+			Local t$=line[1..].Trim()
+			Local index
+			Local i=t.Find(":")
+			If i<>-1
+				index=Int( t[i+1..] )
+				t=t[..i]
+			EndIf
+			If t[..1]="$" t=t[1..].Trim()
+			If t[..2].ToLower()="0x" t=t[2..].Trim()
+			Local pointer = Int( "$"+t )
+			If Not (pointer And bbGCValidate(pointer)) Then Continue
+			Local inst:Int Ptr=Int Ptr pointer
+			
+			Local cmd$="ObjectDump@"+ToHex( Int inst )
+			If i<>-1 cmd:+":"+index
+			WriteDebug cmd$+"{~n"
+
+			DumpObject inst,index
+			WriteDebug "}~n"
+		Case "h"
+			WriteDebug "T - Stack trace~n"
+			WriteDebug "R - Run from here~n"
+			WriteDebug "S - Step through source code~n"
+			WriteDebug "E - Step into function call~n"
+			WriteDebug "L - Leave function or local block~n"
+			WriteDebug "Q - Quit~n"
+			WriteDebug "H - This text~n"
+			WriteDebug "Dxxxxxxxx - Dump object at hex address xxxxxxxx~n"
+		Case "q"
+			End
+		End Select
+	Forever
+
+?Win32
+	If _appHwnd And _appHwnd<>_ideHwnd 
+		If IsIconic(_apphwnd)
+			ShowWindow _appHwnd,SW_RESTORE
+		Else
+			ShowWindow _appHwnd,SW_SHOW
+		EndIf		
+		_apphwnd=0
+	EndIf
+?
+	indebug=False
+End Function
+
+Function OnDebugStop()
+	UpdateDebug "DebugStop:~n"
+End Function
+
+Function OnDebugLog( message$ )
+	WriteStdout "DebugLog:"+message+"~n"
+End Function
+
+Function OnDebugEnterStm( stm:Int Ptr )
+	Local dbgState:TDbgState = GetDbgState()
+	dbgState.currentScope.stm=stm
+	
+	Select dbgState.mode
+	Case MODE_RUN
+		Return
+	Case MODE_STEP
+		If dbgState.funcLevel>dbgState.debugLevel 
+			Return
+		EndIf
+	Case MODE_STEPOUT
+		If dbgState.scopeStackTop>dbgState.debugLevel
+			Return
+		EndIf
+	End Select
+	
+	UpdateDebug "Debug:~n"
+End Function
+
+Function OnDebugEnterScope( scope:Int Ptr,inst:Byte Ptr )
+
+	Local dbgState:TDbgState = GetDbgState()
+	GCSuspend
+
+	If dbgState.scopeStackTop=dbgState.scopeStack.length 
+		dbgState.scopeStack=dbgState.scopeStack[..dbgState.scopeStackTop * 2 + 32]
+		For Local i=dbgState.scopeStackTop Until dbgState.scopeStack.length
+			dbgState.scopeStack[i]=New TScope
+		Next
+	EndIf
+	
+	dbgState.currentScope=dbgState.scopeStack[dbgState.scopeStackTop]
+
+	dbgState.currentScope.scope=scope
+	dbgState.currentScope.inst=inst
+
+	dbgState.scopeStackTop:+1
+
+	If dbgState.currentScope.scope[DEBUGSCOPE_KIND]=DEBUGSCOPEKIND_FUNCTION dbgState.funcLevel:+1
+
+	GCResume	
+End Function
+
+Function OnDebugLeaveScope()
+
+	Local dbgState:TDbgState = GetDbgState()
+	GCSuspend
+
+	If Not dbgState.scopeStackTop DebugError "scope stack underflow"
+
+	If dbgState.currentScope.scope[DEBUGSCOPE_KIND]=DEBUGSCOPEKIND_FUNCTION dbgState.funcLevel:-1
+	
+	dbgState.scopeStackTop:-1
+
+	If dbgState.scopeStackTop
+		dbgState.currentScope=dbgState.scopeStack[dbgState.scopeStackTop-1]
+	Else
+		dbgState.currentScope=New TScope
+	EndIf
+
+	GCResume	
+End Function
+
+Function OnDebugPushExState()
+
+	Local dbgState:TDbgState = GetDbgState()
+	GCSuspend
+
+	If dbgState.exStateStackTop=dbgState.exStateStack.length 
+		dbgState.exStateStack=dbgState.exStateStack[..dbgState.exStateStackTop * 2 + 32]
+		For Local i=dbgState.exStateStackTop Until dbgState.exStateStack.length
+			dbgState.exStateStack[i]=New TExState
+		Next
+	EndIf
+	
+	dbgState.exStateStack[dbgState.exStateStackTop].scopeStackTop=dbgState.scopeStackTop
+	
+	dbgState.exStateStackTop:+1
+
+	GCResume	
+End Function
+
+Function OnDebugPopExState()
+
+	Local dbgState:TDbgState = GetDbgState()
+	GCSuspend
+
+	If Not dbgState.exStateStackTop DebugError "exception stack underflow"
+
+	dbgState.exStateStackTop:-1
+
+	dbgState.scopeStackTop=dbgState.exStateStack[dbgState.exStateStackTop].scopeStackTop
+	
+	If dbgState.scopeStackTop
+		dbgState.currentScope=dbgState.scopeStack[dbgState.scopeStackTop-1]
+	Else
+		dbgState.currentScope=New TScope
+	EndIf
+
+	GCResume	
+End Function
+
+Function OnDebugUnhandledEx( ex:Object )
+
+	GCSuspend
+	
+	UpdateDebug "Unhandled Exception:"+ex.ToString()+"~n"
+
+	GCResume	
+End Function

+ 381 - 0
audio.mod/audio.bmx

@@ -0,0 +1,381 @@
+
+Strict
+
+Rem
+bbdoc: Audio/Audio playback
+End Rem
+Module BRL.Audio
+
+ModuleInfo "Version: 1.07"
+ModuleInfo "Author: Mark Sibly"
+ModuleInfo "License: zlib/libpng"
+ModuleInfo "Copyright: Blitz Research Ltd"
+ModuleInfo "Modserver: BRL"
+
+ModuleInfo "History: 1.07 Release"
+ModuleInfo "History: Added flags to LoadSound"
+ModuleInfo "History: Driver now set to Null if SetAudioDriver fails"
+ModuleInfo "History: 1.06 Release"
+ModuleInfo "History: Changed default device to FreeAudio"
+ModuleInfo "History: 1.05 Release"
+ModuleInfo "History: Added driver list and SetAudioDriver"
+
+Import BRL.AudioSample
+
+Private
+
+Global _nullDriver:TAudioDriver=New TAudioDriver
+
+Global _driver:TAudioDriver,_drivers:TAudioDriver
+
+Function Driver:TAudioDriver()
+	If _driver Return _driver
+?Win32
+	If SetAudioDriver( "DirectSound" ) Return _driver
+?Not Win32
+	If SetAudioDriver( "FreeAudio" ) Return _driver
+?
+	SetAudioDriver "Null"
+	Return _driver
+End Function
+
+Function Shutdown()
+	If Not _driver Return
+	_driver.Shutdown
+	_driver=Null
+End Function
+
+atexit_ Shutdown
+
+Public
+
+Const SOUND_LOOP=1
+Const SOUND_HARDWARE=2
+
+Rem
+bbdoc: Audio sound type
+End Rem
+Type TSound
+
+	Rem 
+	bbdoc: Play the sound
+	returns: An audio channel object
+	about:
+	Starts a sound playing through an audio channel.
+	If no channel is specified, #Play automatically allocates a channel for you.
+	End Rem
+	Method Play:TChannel( alloced_channel:TChannel=Null )
+		Return New TChannel
+	End Method
+	
+	Rem 
+	bbdoc: Cue the sound for playback
+	returns: An audio channel object
+	about:
+	Prepares an audio channel for playback of a sound. 
+	To actually start the sound, you must use the channel's #SetPaused method.
+	If no channel is specified, #Cue automatically allocates a channel for you.
+
+	#Cue allows you to setup various audio channel states such as volume, pan, depth and rate before a sound
+	actually starts playing.
+	End Rem
+	Method Cue:TChannel( alloced_channel:TChannel=Null )
+		Return New TChannel
+	End Method
+	
+	Rem
+	bbdoc: Load sound
+	returns: A sound object
+	about:
+	@url can be either a string, a stream or an audio sample object.
+	The returned sound object can be played using #Play or #Cue.
+	End Rem
+	Function Load:TSound( url:Object,loop_flag )
+		Local sample:TAudioSample
+		sample=TAudioSample( url )
+		If Not sample sample=LoadAudioSample( url )
+		If sample Return Driver().CreateSound( sample,loop_flag )
+	End Function
+
+End Type
+
+Rem
+bbdoc: Audio channel Type
+End Rem
+Type TChannel
+	Rem
+	bbdoc: Stop audio channel playback
+	about:
+	Shuts down the audio channel. Further commands on this audio channel will have no effect.
+	End Rem
+	Method Stop()
+	End Method
+	Rem
+	bbdoc: Pause or unpause audio channel playback
+	about:
+	If @paused is True, the audio channel is paused. Otherwise, the audio channel is unpaused.
+	End Rem
+	Method SetPaused( paused )
+	End Method
+	Rem
+	bbdoc: Set audio channel volume
+	about:
+	@volume should be in the range 0 (silence) to 1 (full volume).
+	End Rem
+	Method SetVolume( volume# )
+	End Method
+	Rem
+	bbdoc: Set audio channel stereo pan
+	about:
+	@pan should be in the range -1 (full left) to 1 (full right).
+	End Rem
+	Method SetPan( pan# ) 
+	End Method
+	Rem
+	bbdoc: Set audio channel depth
+	about: 
+	@depth should be in the range -1 (back) to 1 (front).
+	End Rem
+	Method SetDepth( depth# )
+	End Method
+	Rem
+	bbdoc: Set audio channel playback rate
+	about:
+	@rate is a multiplier used to modify the audio channel's frequency.
+	For example, a rate of .5 will cause the audio channel
+	to play at half speed (ie: an octave down) while a rate of 2 will
+	cause the audio channel to play at double speed (ie: an octave up).
+	End Rem
+	Method SetRate( rate# )
+	End Method
+	Rem
+	bbdoc: Determine whether audio channel is playing
+	returns: True if @channel is currently playing
+	about:
+	#Playing will return False if the audio channel is either paused, or has been stopped
+	using #Stop.
+	End Rem
+	Method Playing()
+	End Method
+
+End Type
+
+Type TAudioDriver
+
+	Method New()
+		_succ=_drivers
+		_drivers=Self
+	End Method
+	
+	Method Name$()
+		Return "Null"
+	End Method
+	
+	Method Startup()
+		Return True
+	End Method
+	
+	Method Shutdown()
+	End Method
+
+	Method CreateSound:TSound( sample:TAudioSample,loop_flag )
+		Return New TSound
+	End Method
+	
+	Method AllocChannel:TChannel() 
+		Return New TChannel
+	End Method
+
+	Method LoadSound:TSound( url:Object, flags:Int = 0)
+		Return TSound.Load(url, flags)
+	End Method
+	
+	Field _succ:TAudioDriver
+
+End Type
+
+Rem
+bbdoc: Load a sound
+returns: A sound object
+about:
+@url can be either a string, a stream or a #TAudioSample object.
+The returned sound can be played using #PlaySound or #CueSound.
+
+The @flags parameter can be any combination of:
+
+[ @{Flag value} | @Effect
+* SOUND_LOOP | The sound should loop when played back.
+* SOUND_HARDWARE | The sound should be placed in onboard soundcard memory if possible.
+]
+
+To combine flags, use the binary 'or' operator: '|'.
+End Rem
+Function LoadSound:TSound( url:Object,flags=0 )
+	Return Driver().LoadSound( url,flags )
+End Function
+
+Rem
+bbdoc: Play a sound
+returns: An audio channel object
+about:
+#PlaySound starts a sound playing through an audio channel.
+If no @channel is specified, #PlaySound automatically allocates a channel for you.
+end rem
+Function PlaySound:TChannel( sound:TSound,channel:TChannel=Null )
+	Return sound.Play( channel )
+End Function
+
+Rem
+bbdoc: Cue a sound
+returns: An audio channel object
+about:
+Prepares a sound for playback through an audio channel. 
+To actually start the sound, you must use #ResumeChannel.
+If no @channel is specified, #CueSound automatically allocates a channel for you.
+
+#CueSound allows you to setup various audio channel states such as volume, pan, depth 
+and rate before a sound actually starts playing.
+End Rem
+Function CueSound:TChannel( sound:TSound,channel:TChannel=Null )
+	Return sound.Cue( channel )
+End Function
+
+Rem
+bbdoc: Allocate audio channel
+returns: An audio channel object
+about: 
+Allocates an audio channel for use with #PlaySound and #CueSound.
+Once you are finished with an audio channel, you should use #StopChannel.
+end rem
+Function AllocChannel:TChannel()
+	Return Driver().AllocChannel()
+End Function
+
+Rem
+bbdoc: Stop an audio channel
+about:
+Shuts down an audio channel. Further commands using this channel will have no effect.
+end rem
+Function StopChannel( channel:TChannel )
+	channel.Stop
+End Function
+
+Rem
+bbdoc: Determine whether an audio channel is playing
+returns: #True if @channel is currently playing
+about:
+#ChannelPlaying will return #False if either the channel has been paused using #PauseChannel,
+or stopped using #StopChannel.
+end rem
+Function ChannelPlaying( channel:TChannel )
+	Return channel.Playing()
+End Function
+
+Rem
+bbdoc: Set playback volume of an audio channel
+about:
+@volume should be in the range 0 (silent) to 1 (full volume)
+end rem
+Function SetChannelVolume( channel:TChannel,volume# )
+	channel.SetVolume volume
+End Function
+
+Rem
+bbdoc: Set stereo balance of an audio channel
+about: 
+@pan should be in the range -1 (left) to 1 (right)
+end rem
+Function SetChannelPan( channel:TChannel,pan# )
+	channel.SetPan pan
+End Function
+
+Rem
+bbdoc: Set surround sound depth of an audio channel
+about: 
+@depth should be in the range -1 (back) to 1 (front)
+end rem
+Function SetChannelDepth( channel:TChannel,depth# )
+	channel.SetDepth depth
+End Function
+
+Rem
+bbdoc: Set playback rate of an audio channel
+about:
+@rate is a multiplier used to modify the audio channel's frequency.
+For example, a rate of .5 will cause the audio channel
+to play at half speed (ie: an octave down) while a rate of 2 will
+cause the audio channel to play at double speed (ie: an octave up).
+end rem
+Function SetChannelRate( channel:TChannel,rate# )
+	channel.SetRate rate
+End Function
+
+Rem
+bbdoc: Pause audio channel playback
+about:
+Pauses audio channel playback.
+end rem
+Function PauseChannel( channel:TChannel )
+	channel.SetPaused True
+End Function
+
+Rem
+bbdoc: Resume audio channel playback
+about:
+Resumes audio channel playback after it has been paused by #CueSound or #PauseChannel.
+end rem
+Function ResumeChannel( channel:TChannel )
+	channel.SetPaused False
+End Function
+
+Rem
+bbdoc: Get audio drivers
+about:
+Returns an array of strings, where each string describes an audio driver.
+End Rem
+Function AudioDrivers$[]()
+	Local devs$[100],n
+	Local t:TAudioDriver=_drivers
+	While t And n<100
+		devs[n]=t.Name()
+		n:+1
+		t=t._succ
+	Wend
+	Return devs[..n]
+End Function
+
+Rem
+bbdoc: Determine if an audio driver exists
+about:
+Returns True if the audio drvier specified by @driver exists.
+End Rem
+Function AudioDriverExists( name$ )
+	name=name.ToLower()
+	Local t:TAudioDriver=_drivers
+	While t
+		If t.Name().ToLower()=name Return True
+		t=t._succ
+	Wend
+End Function
+
+Rem
+bbdoc: Set current audio driver
+about:
+Returns true if the audio driver was successfully set.
+End Rem
+Function SetAudioDriver( name$ )
+	name=name.ToLower()
+	Shutdown
+	_driver=_nullDriver
+	Local t:TAudioDriver=_drivers
+	While t
+		If t.Name().ToLower()=name
+			If t.Startup()
+				_driver=t
+				Return True
+			EndIf
+			Return False
+		EndIf
+		t=t._succ
+	Wend
+End Function

+ 11 - 0
audio.mod/doc/allocchannel.bmx

@@ -0,0 +1,11 @@
+'AllocChannel.bmx
+
+timer=createtimer(20)
+
+sound=LoadSound ("shoot.wav")
+channel=AllocChannel()
+
+for i=1 to 20
+	waittimer timer
+	playsound sound,channel
+next

+ 10 - 0
audio.mod/doc/channelplaying.bmx

@@ -0,0 +1,10 @@
+' channelplaying.bmx
+
+sound = LoadSound ("shoot.wav")
+
+Input "Hit return to begin channelplaying test, use ctrl-C to exit"
+
+channel=playsound (sound)
+while true
+	print "ChannelPlaying(channel)="+ChannelPlaying(channel)
+wend

+ 12 - 0
audio.mod/doc/cuesound.bmx

@@ -0,0 +1,12 @@
+Rem
+CueSound example
+End Rem
+
+sound=LoadSound("shoot.wav")
+channel=CueSound(sound)
+
+Input "Press return key to play cued sound"
+
+ResumeChannel channel
+
+Input "Press return key to quit"

+ 14 - 0
audio.mod/doc/intro.bbdoc

@@ -0,0 +1,14 @@
+
+The BlitzMax audio module contains commands to load and play sounds.
+
+A sound file can be played in BlitzMax with a combination of #LoadSound that loads a sound file
+and #PlaySound which plays the sound through the systems audio system if available.
+
+BlitzMax contains native support for sound files in both .wav (uncompressed)
+and .ogg (compressed) file formats.
+
+Playback of sounds can be controlled with various audio channel
+operators including #SetChannelVolume, #SetChannelPan, #SetChannelDepth and #SetChannelRate.
+
+A channel handle is obtained from either the return value of #PlaySound and #CueSound or from
+reserving a channel with #AllocChannel.

+ 8 - 0
audio.mod/doc/loadsound.bmx

@@ -0,0 +1,8 @@
+Rem
+Load and Play a small example wav file.
+End Rem
+
+sound=LoadSound("shoot.wav")
+PlaySound sound
+
+Input "Press any key to continue"

+ 8 - 0
audio.mod/doc/playsound.bmx

@@ -0,0 +1,8 @@
+Rem
+Load and Play a small example wav file with looping.
+End Rem
+
+sound=LoadSound("shoot.wav",true)
+PlaySound sound
+
+Input "Press any key to continue"

+ 25 - 0
audio.mod/doc/setchanneldepth.bmx

@@ -0,0 +1,25 @@
+' setchanneldepth.bmx
+
+Graphics 640, 480
+
+channel = AllocChannel ()
+sound = LoadSound ("shoot.wav") ' Use a short sample...
+
+Repeat
+	If MouseHit(1) PlaySound sound,channel
+	
+	pan# = MouseX () / (640 / 2.0) - 1
+	depth# = MouseY () / (480 /2.0) -1
+	
+	SetChannelPan channel,pan
+	SetChannelDepth channel,depth
+
+	Cls
+	DrawText "Click to play...", 240, 200
+	DrawText "Pan   : " + pan, 240, 220
+	DrawText "Depth : " + depth, 240, 240
+
+	Flip
+Until KeyHit (KEY_ESCAPE)
+
+End

+ 24 - 0
audio.mod/doc/setchannelpan.bmx

@@ -0,0 +1,24 @@
+' setchannelpan.bmx
+
+Graphics 640, 480
+
+channel = AllocChannel ()
+sound = LoadSound ("shoot.wav") ' Use a short sample...
+
+Repeat
+	If MouseHit(1) PlaySound sound,channel
+	
+	pan# = MouseX () / (GraphicsWidth () / 2.0) - 1
+	vol# = 1 - MouseY () / 480.0
+	SetChannelPan channel, pan
+	SetChannelVolume channel, vol*2
+
+	Cls
+	DrawText "Click to play...", 240, 200
+	DrawText "Pan   : " + pan, 240, 220
+	DrawText "Volume: " + vol, 240, 240
+
+	Flip
+Until KeyHit (KEY_ESCAPE)
+
+End

+ 12 - 0
audio.mod/doc/setchannelrate.bmx

@@ -0,0 +1,12 @@
+' setchannelrate.bmx
+
+timer=CreateTimer(20)
+
+sound = LoadSound ("shoot.wav",True)
+channel=CueSound(sound)
+ResumeChannel channel
+
+For rate#=1.0 To 4 Step 0.01
+	WaitTimer timer
+	SetChannelRate channel,rate
+Next

+ 12 - 0
audio.mod/doc/setchannelvolume.bmx

@@ -0,0 +1,12 @@
+' setchannelvolume.bmx
+
+timer=CreateTimer(20)
+
+sound = LoadSound ("shoot.wav")
+
+For volume#=.1 To 2 Step .05
+	WaitTimer timer
+	channel=CueSound(sound)
+	SetChannelVolume channel,volume
+	ResumeChannel channel
+Next

BIN
audio.mod/doc/shoot.wav


+ 14 - 0
audio.mod/doc/stopchannel.bmx

@@ -0,0 +1,14 @@
+Rem
+StopChannel example
+End Rem
+
+sound=LoadSound("shoot.wav",true)
+channel=PlaySound(sound)
+
+print "channel="+channel
+
+Input "Press return key to stop sound"
+
+StopChannel channel
+
+Input "Press return key to quit"

+ 203 - 0
audiosample.mod/audiosample.bmx

@@ -0,0 +1,203 @@
+
+Strict
+
+Rem
+bbdoc: Audio/Audio samples
+End Rem
+Module BRL.AudioSample
+
+ModuleInfo "Version: 1.04"
+ModuleInfo "Author: Mark Sibly"
+ModuleInfo "License: zlib/libpng"
+ModuleInfo "Copyright: Blitz Research Ltd"
+ModuleInfo "Modserver: BRL"
+
+ModuleInfo "History: 1.04 Release"
+ModuleInfo "History: ChannelsPerSample array added"
+
+Import BRL.Stream
+
+Import "sample.bmx"
+
+Rem
+bbdoc: Audio sample type
+end rem
+Type TAudioSample
+
+	Rem
+	bbdoc: Byte pointer to sample data
+	end rem
+	Field samples:Byte Ptr
+	
+	Rem
+	bbdoc: Length, in samples, of the sample data
+	end rem
+	Field length
+	
+	Rem
+	bbdoc: Sample rate
+	end rem
+	Field hertz
+	
+	Rem
+	bbdoc: Sample format
+	end rem
+	Field format
+	
+	Field capacity
+
+	Method Delete()
+		If capacity>=0 MemFree samples
+	End Method
+
+	Rem
+	bbdoc: Copy audio sample
+	returns: A new audio sample object
+	end rem
+	Method Copy:TAudioSample()
+		Local t:TAudioSample=Create( length,hertz,format )
+		CopySamples samples,t.samples,format,length
+		Return t
+	End Method
+
+	Rem
+	bbdoc: Convert audio sample
+	returns: A new audio sample object in the specified format
+	end rem
+	Method Convert:TAudioSample( to_format )
+		Local t:TAudioSample=Create( length,hertz,to_format )
+		ConvertSamples samples,format,t.samples,to_format,length
+		Return t
+	End Method
+
+	Rem
+	bbdoc: Create an audio sample
+	returns: A new audio sample object
+	end rem
+	Function Create:TAudioSample( length,hertz,format )
+		Local t:TAudioSample=New TAudioSample
+		Local capacity=length*BytesPerSample[format]
+		t.samples=MemAlloc( capacity )
+		t.length=length
+		t.hertz=hertz
+		t.format=format
+		t.capacity=capacity
+		Return t
+	End Function
+
+	Rem
+	bbdoc: Create a static audio sample
+	returns: A new audio sample object that references an existing block of memory
+	end rem
+	Function CreateStatic:TAudioSample( samples:Byte Ptr,length,hertz,format )
+		Local t:TAudioSample=New TAudioSample
+		t.samples=samples
+		t.length=length
+		t.hertz=hertz
+		t.format=format
+		t.capacity=-1
+		Return t
+	End Function
+
+End Type
+
+Private
+Global sample_loaders:TAudioSampleLoader
+Public
+
+'deprecated
+Function AddAudioSampleLoader( loader:TAudioSampleLoader )
+'	If( loader._succ ) Return
+'	loader._succ=sample_loaders
+'	sample_loaders=loader
+End Function
+
+Rem
+bbdoc: Audio sample loader type
+about: To create your own audio sample loaders, you should extend this type and
+provide a @LoadAudioSample method. To add your audio sample loader to the system,
+simply create an instance of it using @New.
+end rem
+Type TAudioSampleLoader
+	Field _succ:TAudioSampleLoader
+	
+	Method New()
+		_succ=sample_loaders
+		sample_loaders=Self
+	End Method
+	
+	Rem
+	bbdoc: Load an audio sample
+	returns: A new audio sample object, or Null if sample could not be loaded
+	about: Extending types must implement this method.
+	end rem
+	Method LoadAudioSample:TAudioSample( stream:TStream ) Abstract
+
+End Type
+
+Rem
+bbdoc: Create an audio sample
+returns: An audio sample object
+about:
+@length is the number of samples to allocate for the sample. @hertz is the frequency in samples per second (hz)
+the audio sample will be played. @format should be one of:
+
+[ @Format | @Description
+
+* &SF_MONO8 | Mono unsigned 8 bit
+
+* &SF_MONO16LE | Mono signed 16 bit little endian
+
+* &SF_MONO16BE | Mono signed 16 bit big endian
+
+* &SF_STEREO8 | Stereo unsigned 8 bit
+
+* &SF_STEREO16LE | Stereo signed 16 bit little endian
+
+* &SF_STEREO16BE | Stereo signed 16 bit big endian
+]
+End Rem
+Function CreateAudioSample:TAudioSample( length,hertz,format )
+	Return TAudioSample.Create( length,hertz,format )
+End Function
+
+Rem
+bbdoc: Create an audio sample with existing data
+returns: An audio sample object that references an existing block of memory
+about:
+The memory referenced by a static audio sample is not released when the audio sample is 
+deleted.
+
+See #CreateAudioSample for possile @format values.
+End Rem
+Function CreateStaticAudioSample:TAudioSample( samples:Byte Ptr,length,hertz,format )
+	Return TAudioSample.CreateStatic( samples,length,hertz,format )
+End Function
+
+Rem
+bbdoc: Load an audio sample
+returns: An audio sample object
+end rem
+Function LoadAudioSample:TAudioSample( url:Object )
+
+	Local stream:TStream=ReadStream( url )
+	If Not stream Return
+
+	Local pos=stream.Pos()
+	If pos=-1 RuntimeError "Stream is not seekable"
+
+	Local sample:TAudioSample
+	Local loader:TAudioSampleLoader=sample_loaders
+	
+	While loader
+		stream.Seek pos
+		Try
+			sample=loader.LoadAudioSample( stream )
+		Catch ex:TStreamException
+		End Try
+		If sample Exit
+		loader=loader._succ
+	Wend
+	stream.Close
+	Return sample
+End Function

+ 13 - 0
audiosample.mod/doc/createaudiosample.bmx

@@ -0,0 +1,13 @@
+' createaudiosample.bmx
+
+Local sample:TAudioSample=CreateAudioSample( 32,11025,SF_MONO8 )
+
+For Local k=0 Until 32
+        sample.samples[k]=Sin(k*360/32)*127.5+127.5
+Next
+
+Local sound:TSound=LoadSound( sample,True )
+
+PlaySound(sound)
+
+Input

+ 2 - 0
audiosample.mod/doc/intro.bbdoc

@@ -0,0 +1,2 @@
+The BlitzMax audiosample module contains commands to create and load audio samples for
+use with the BlitzMax #BRL.Audio module.

+ 161 - 0
audiosample.mod/sample.bmx

@@ -0,0 +1,161 @@
+
+Strict
+
+Const SF_MONO8=1
+Const SF_MONO16LE=2
+Const SF_MONO16BE=3
+
+Const SF_STEREO8=4
+Const SF_STEREO16LE=5
+Const SF_STEREO16BE=6
+
+Const SF_STDFORMAT=SF_STEREO16BE
+
+Global BytesPerSample[]=[0,1,2,2,2,4,4]
+Global ChannelsPerSample[]=[0,1,1,1,2,2,2]
+
+Function CopySamples( in_buf:Byte Ptr,out_buf:Byte Ptr,format,count )
+	MemCopy out_buf,in_buf,count*BytesPerSample[format]
+End Function
+
+Function ConvertSamples( in_buf:Byte Ptr,in_format,out_buf:Byte Ptr,out_format,count )
+	If in_format=out_format
+		CopySamples in_buf,out_buf,out_format,count
+	Else If in_format=SF_STDFORMAT
+		ConvertSamplesFromStdFormat in_buf,out_buf,out_format,count
+	Else If out_format=SF_STDFORMAT
+		ConvertSamplesToStdFormat in_buf,out_buf,in_format,count
+	Else
+		Local tmp_buf:Byte[count*BytesPerSample[SF_STDFORMAT]]
+		ConvertSamplesToStdFormat in_buf,tmp_buf,in_format,count
+		ConvertSamplesFromStdFormat tmp_buf,out_buf,out_format,count
+	EndIf
+End Function
+
+Function ConvertSamplesToStdFormat( in_buf:Byte Ptr,out_buf:Byte Ptr,format,count )
+
+	If format=SF_STDFORMAT
+		CopySamples in_buf,out_buf,format,count
+		Return
+	EndIf
+
+	Local in:Byte Ptr=in_buf,out:Byte Ptr=out_buf
+	Local out_end:Byte Ptr=out+count*BytesPerSample[SF_STDFORMAT]
+
+	Select format
+	Case SF_MONO8
+		While out<>out_end
+			Local t=in[0]*257-$8000
+			out[0]=t Shr 8
+			out[1]=t
+			out[2]=t Shr 8
+			out[3]=t
+			in:+1;out:+4
+		Wend
+	Case SF_MONO16LE
+		While out<>out_end
+			Local t=in[1] Shl 8 | in[0]
+			out[0]=in[1]
+			out[1]=in[0]
+			out[2]=in[1]
+			out[3]=in[0]
+			in:+2;out:+4
+		Wend
+	Case SF_MONO16BE
+		While out<>out_end
+			out[0]=in[0]
+			out[1]=in[1]
+			out[2]=in[0]
+			out[3]=in[1]
+			in:+2;out:+4
+		Wend
+	Case SF_STEREO8
+		While out<>out_end
+			Local x=in[0]*257-$8000
+			Local y=in[1]*257-$8000
+			out[0]=x Shr 8
+			out[1]=x
+			out[2]=y Shr 8
+			out[3]=y
+			in:+2;out:+4
+		Wend
+	Case SF_STEREO16LE
+		While out<>out_end
+			out[0]=in[1]
+			out[1]=in[0]
+			out[2]=in[3]
+			out[3]=in[2]
+			in:+4;out:+4
+		Wend
+	Default
+		RuntimeError "Unimplemented sample format conversion"
+	End Select
+
+End Function
+
+Function ConvertSamplesFromStdFormat( in_buf:Byte Ptr,out_buf:Byte Ptr,format,count )
+
+	If format=SF_STDFORMAT
+		CopySamples in_buf,out_buf,format,count
+		Return
+	EndIf
+
+	Local out:Byte Ptr=out_buf,in:Byte Ptr=in_buf
+	Local in_end:Byte Ptr=in+count*BytesPerSample[SF_STDFORMAT]
+
+	Select format
+	Case SF_MONO8
+		While in<>in_end
+			Local x=in[0] Shl 8 | in[1]
+			Local y=in[2] Shl 8 | in[3]
+			If x & $8000 x:|$ffff0000
+			If y & $8000 y:|$ffff0000
+			Local t=(x+y)/2
+			out[0]=(t+$8000)/257
+			in:+4;out:+1
+		Wend
+	Case SF_MONO16LE
+		While in<>in_end
+			Local x=in[0] Shl 8 | in[1]
+			Local y=in[2] Shl 8 | in[3]
+			If x & $8000 x:|$ffff0000
+			If y & $8000 y:|$ffff0000
+			Local t=(x+y)/2
+			out[0]=t
+			out[1]=t Shr 8
+			in:+4;out:+2
+		Wend
+	Case SF_MONO16BE
+		While in<>in_end
+			Local x=in[0] Shl 8 | in[1]
+			Local y=in[2] Shl 8 | in[3]
+			If x & $8000 x:|$ffff0000
+			If y & $8000 y:|$ffff0000
+			Local t=(x+y)/2
+			out[0]=t Shr 8
+			out[1]=t
+			in:+4;out:+2
+		Wend
+	Case SF_STEREO8
+		While in<>in_end
+			Local x=in[0] Shl 8 | in[1]
+			Local y=in[2] Shl 8 | in[3]
+			If x & $8000 x:|$ffff0000
+			If y & $8000 y:|$ffff0000
+			out[0]=(x+$8000)/257
+			out[1]=(y+$8000)/257
+			in:+4;out:+2
+		Wend
+	Case SF_STEREO16LE
+		While in<>in_end
+			out[0]=in[1]
+			out[1]=in[0]
+			out[2]=in[3]
+			out[3]=in[2]
+			in:+4;out:+4
+		Wend
+	Default
+		RuntimeError "Unimplemented sample format conversion"
+	End Select
+
+End Function

+ 531 - 0
bank.mod/bank.bmx

@@ -0,0 +1,531 @@
+
+Strict
+
+Rem
+bbdoc: Miscellaneous/Banks
+End Rem
+Module BRL.Bank
+
+ModuleInfo "Version: 1.06"
+ModuleInfo "Author: Mark Sibly"
+ModuleInfo "License: zlib/libpng"
+ModuleInfo "Copyright: Blitz Research Ltd"
+ModuleInfo "Modserver: BRL"
+
+ModuleInfo "History: 1.06 Release"
+ModuleInfo "History: Added Lock/Unlock to replace Buf"
+ModuleInfo "History: 1.05 Release"
+ModuleInfo "History: Fixed Read/Write using ReadBytes/WriteBytes"
+
+Import BRL.Stream
+
+Rem
+bbdoc: Memory bank
+end rem
+Type TBank
+
+	Field _buf:Byte Ptr,_size,_capacity,_locked
+	
+	Method _pad()
+	End Method
+	
+	Method Delete()
+		Assert Not _locked
+		If _capacity>=0 MemFree _buf
+	End Method
+
+	Rem
+	bbdoc: Get a bank's memory pointer
+	returns: A byte pointer to the memory block controlled by the bank
+	about:
+	Please use #Lock and #Unlock instead of this method.
+	End Rem
+	Method Buf:Byte Ptr()
+		Return _buf
+	End Method
+	
+	Rem
+	bbdoc: Lock a bank's memory block
+	returns: A byte pointer to the memory block controlled by the bank
+	about:
+	While locked, a bank cannot be resized.
+
+	After you have finished with a bank's memory block, you must use #Unlock
+	to return it to the bank.
+	End Rem
+	Method Lock:Byte Ptr()
+		_locked:+1
+		Return _buf
+	End Method
+	
+	Rem
+	bbdoc: Unlock a bank's memory pointer
+	about:
+	After you have finished with a bank's memory block, you must use #Unlock
+	to return it to the bank.
+	End Rem
+	Method Unlock()
+		_locked:-1
+	End Method
+
+	Rem
+	bbdoc: Get a bank's size
+	returns: The size, in bytes, of the memory block controlled by the bank
+	End Rem
+	Method Size()
+		Return _size
+	End Method
+
+	Rem
+	bbdoc: Get capacity of bank
+	returns: The capacity, in bytes, of the bank's internal memory buffer
+	end rem
+	Method Capacity()
+		Return _capacity
+	End Method
+
+	Rem
+	bbdoc: Resize a bank
+	end rem
+	Method Resize( size )
+		Assert _locked=0 Else "Locked banks cannot be resize"
+		Assert _capacity>=0 Else "Static banks cannot be resized"
+		If size>_capacity
+			Local n=_capacity*3/2
+			If n<size n=size
+			Local tmp:Byte Ptr=MemAlloc(n)
+			MemCopy tmp,_buf,_size
+			MemFree _buf
+			_capacity=n
+			_buf=tmp
+		EndIf
+		_size=size
+	End Method
+	
+	Rem
+	bbdoc: Read bytes from a stream into a bank
+	end rem
+	Method Read( stream:TStream,offset,count )
+		Assert offset>=0 And offset<=_size-count Else "Illegal bank offset"
+		Return stream.Read( _buf+offset,count )
+	End Method
+	
+	Rem
+	bbdoc: Write bytes in a bank to a stream
+	end rem
+	Method Write( stream:TStream,offset,count )
+		Assert offset>=0 And offset<=_size-count Else "Illegal bank offset"
+		Return stream.Write( _buf+offset,count )
+	End Method
+
+	Rem
+	bbdoc: Peek a byte from a bank
+	returns: The byte value at the specified byte offset within the bank
+	end rem
+	Method PeekByte( offset )
+		Assert offset>=0 And offset<_size Else "Illegal bank offset"
+		Return _buf[offset]
+	End Method
+
+	Rem
+	bbdoc: Poke a byte into a bank
+	end rem
+	Method PokeByte( offset,value )
+		Assert offset>=0 And offset<_size Else "Illegal bank offset"
+		_buf[offset]=value
+	End Method
+
+	Rem
+	bbdoc: Peek a short from a bank
+	returns: The short value at the specified byte offset within the bank
+	end rem
+	Method PeekShort( offset )
+		Assert offset>=0 And offset<_size-1 Else "Illegal bank offset"
+		Return (Short Ptr(_buf+offset))[0]
+	End Method
+
+	Rem
+	bbdoc: Poke a short into a bank
+	end rem
+	Method PokeShort( offset,value )
+		Assert offset>=0 And offset<_size-1 Else "Illegal bank offset"
+		(Short Ptr(_buf+offset))[0]=value
+	End Method
+
+	Rem
+	bbdoc: Peek an int from a bank
+	returns: The int value at the specified byte offset within the bank
+	end rem
+	Method PeekInt( offset )
+		Assert offset>=0 And offset<_size-3 Else "Illegal bank offset"
+		Return (Int Ptr(_buf+offset))[0]
+	End Method
+
+	Rem
+	bbdoc: Poke an int into a bank
+	end rem
+	Method PokeInt( offset,value )
+		Assert offset>=0 And offset<_size-3 Else "Illegal bank offset"
+		(Int Ptr(_buf+offset))[0]=value
+	End Method
+
+	Rem
+	bbdoc: Peek a long from a bank
+	returns: The long value at the specified byte offset within the bank
+	end rem
+	Method PeekLong:Long( offset )
+		Assert offset>=0 And offset<_size-7 Else "Illegal bank offset"
+		Return (Long Ptr(_buf+offset))[0]
+	End Method
+
+	Rem
+	bbdoc: Poke a long value into a bank
+	end rem
+	Method PokeLong( offset,value:Long )
+		Assert offset>=0 And offset<_size-7 Else "Illegal bank offset"
+		(Long Ptr(_buf+offset))[0]=value
+	End Method
+	
+	Rem
+	bbdoc: Peek a float from a bank
+	returns: The float value at the specified byte offset within the bank
+	end rem
+	Method PeekFloat#( offset )
+		Assert offset>=0 And offset<_size-3 Else "Illegal bank offset"
+		Return (Float Ptr(_buf+offset))[0]
+	End Method
+
+	Rem
+	bbdoc: Poke a float value into a bank
+	end rem
+	Method PokeFloat( offset,value# )
+		Assert offset>=0 And offset<_size-3 Else "Illegal bank offset"
+		(Float Ptr(_buf+offset))[0]=value
+	End Method
+
+	Rem
+	bbdoc: Peek a double from a bank
+	returns: The double value at the specified byte offset within the bank
+	end rem
+	Method PeekDouble!( offset )
+		Assert offset>=0 And offset<_size-7 Else "Illegal bank offset"
+		Return (Double Ptr(_buf+offset))[0]
+	End Method
+
+	Rem
+	bbdoc: Poke a double value into a bank
+	end rem
+	Method PokeDouble( offset,value! )
+		Assert offset>=0 And offset<_size-7 Else "Illegal bank offset"
+		(Double Ptr(_buf+offset))[0]=value
+	End Method
+	
+	Rem
+	bbdoc: Save a bank to a stream
+	about:
+	Return True if successful, otherwise False.
+	end rem
+	Method Save( url:Object )
+		Local stream:TStream=WriteStream( url )
+		If Not stream Return
+		Local n=stream.WriteBytes( _buf,_size )
+		stream.Close
+		Return True
+	End Method
+
+	Rem
+	bbdoc: Load a bank from a stream
+	returns: A new TBank object
+	about:
+	Returns a new TBank object if successfull, otherwise Null.
+	end rem
+	Function Load:TBank( url:Object )
+		Local stream:TStream=ReadStream( url )
+		If Not stream Return
+		Local data:Byte[]=LoadByteArray( stream )
+		Local bank:TBank=Create( data.length )
+		MemCopy bank.Buf(),data,data.length 
+		stream.Close
+		Return bank
+	End Function
+
+	Rem
+	bbdoc: Create a bank
+	returns: A new TBank object with an initial size of @size
+	end rem
+	Function Create:TBank( size )
+		Assert size>=0 Else "Illegal bank size"
+		Local bank:TBank=New TBank
+		bank._buf=MemAlloc( size )
+		bank._size=size
+		bank._capacity=size
+		Return bank
+	End Function
+	
+	Rem
+	bbdoc: Create a bank from an existing block of memory
+	end rem
+	Function CreateStatic:TBank( buf:Byte Ptr,size )
+		Assert size>=0 Else "Illegal bank size"
+		Local bank:TBank=New TBank
+		bank._buf=buf
+		bank._size=size
+		bank._capacity=-1
+		Return bank
+	End Function
+
+End Type
+
+Rem
+bbdoc: Create a bank
+returns: A bank object with an initial size of @size bytes
+about:
+#CreateBank creates a Bank allocating a specified amount of memory that
+can be used for storage of binary data using the various Poke and
+Peek commands. 
+End Rem
+Function CreateBank:TBank( size=0 )
+	Return TBank.Create( size )
+End Function
+
+Rem
+bbdoc: Create a bank with existing data
+returns: A bank object that references an existing block of memory
+about:
+The memory referenced by a static bank is not released when the bank is deleted.
+A static bank cannot be resized.
+End Rem
+Function CreateStaticBank:TBank( buf:Byte Ptr,size )
+	Return TBank.CreateStatic( buf,size )
+End Function
+
+Rem
+bbdoc: Load a bank
+returns: A bank containing the binary contents of @url, or null if @url could not be opened
+about:
+#LoadBank reads the entire contents of a binary file from a specified @url into a newly
+created bank with a size matching that of the file.
+end rem
+Function LoadBank:TBank( url:Object )
+	Return TBank.Load( url )
+End Function
+
+Rem
+bbdoc: Save a bank
+returns: True if successful.
+about:
+#SaveBank writes it's entire contents to a @url. If the @url is a file path a new
+file is created.
+end rem
+Function SaveBank( bank:TBank,url:Object )
+	Return bank.Save( url )
+End Function
+
+Rem
+bbdoc: Get bank's memory buffer
+returns: A byte pointer to the bank's internal memory buffer
+about:
+Please use #LockBank and #UnlockBank instead of this method.
+End Rem
+Function BankBuf:Byte Ptr( bank:TBank )
+	Return bank.Buf()
+End Function
+
+Rem
+bbdoc: Lock a bank's memory block
+returns: A byte pointer to the memory block controlled by the bank.
+about:
+While locked, a bank cannot be resized.
+
+After you have finished with a bank's memory block, you must use #UnlockBank
+to return it to the bank.
+End Rem
+Function LockBank:Byte Ptr( bank:TBank )
+	Return bank.Lock()
+End Function
+
+Rem
+bbdoc: Unlock a bank's memory block
+about:
+After you have finished with a bank's memory block, you must use #UnlockBank
+to return it to the bank.
+End Rem
+Function UnlockBank( bank:TBank )
+	bank.Unlock
+End Function
+
+Rem
+bbdoc: Get size of bank
+returns: The size, in bytes, of the bank's internal memory buffer
+end rem
+Function BankSize( bank:TBank )
+	Return bank.Size()
+End Function
+
+Rem
+bbdoc: Get capacity of bank
+returns: The capacity, in bytes, of the bank's internal memory buffer
+about:
+The capacity of a bank is the size limit before a bank must allocate
+more memory due to a resize. Bank capacity may be increased due to a call
+to #ResizeBank by either 50% or the requested amount, whichever is greater.
+Capacity never decreases.
+end rem
+Function BankCapacity( bank:TBank )
+	Return bank.Capacity()
+End Function
+
+Rem
+bbdoc: Resize a bank
+about:
+#ResizeBank modifies the size limit of a bank. This may cause memory to be
+allocated if the requested size is greater than the bank's current capacity,
+see #BankCapacity for more information.
+end rem
+Function ResizeBank( bank:TBank,size )
+	bank.Resize size
+End Function
+
+Rem
+bbdoc: Copy bank contents
+about:
+#CopyBank copies @count bytes from @src_offset in @src_bank to @dst_offset
+in @dst_bank.
+end rem
+Function CopyBank( src_bank:TBank,src_offset,dst_bank:TBank,dst_offset,count )
+	Assert..
+	count>=0 And..
+	src_offset>=0 And..
+	dst_offset>=0 And..
+	src_offset+count<=src_bank.Size() And..
+	dst_offset+count<=dst_bank.size() Else "Illegal range for CopyBank"
+	MemCopy( dst_bank.Buf()+dst_offset,src_bank.Buf()+src_offset,count )
+End Function
+
+Rem
+bbdoc: Peek a byte from a bank
+returns: The byte value at the specified byte offset within the bank
+about:
+A byte is an unsigned 8 bit value with a range of 0..255.
+end rem
+Function PeekByte( bank:TBank,offset )
+	Return bank.PeekByte( offset )
+End Function
+
+Rem
+bbdoc: Poke a byte into a bank
+end rem
+Function PokeByte( bank:TBank,offset,value )
+	bank.PokeByte offset,value
+End Function
+
+Rem
+bbdoc: Peek a short from a bank
+returns: The short value at the specified byte offset within the bank
+about:
+A short is an unsigned 16 bit (2 bytes) value with a range of 0..65535.
+end rem
+Function PeekShort( bank:TBank,offset )
+	Return bank.PeekShort( offset )
+End Function
+
+Rem
+bbdoc: Poke a short into a bank
+about:
+An short is an unsigned 16 bit value that requires 2 bytes of storage.
+end rem
+Function PokeShort( bank:TBank,offset,value )
+	bank.PokeShort offset,value
+End Function
+
+Rem
+bbdoc: Peek an int from a bank
+returns: The int value at the specified byte offset within the bank
+about:
+An int is a signed 32 bit value (4 bytes).
+end rem
+Function PeekInt( bank:TBank,offset )
+	Return bank.PeekInt( offset )
+End Function
+
+Rem
+bbdoc: Poke an int into a bank
+about:
+An int is a signed 32 bit value that requires 4 bytes of storage.
+end rem
+Function PokeInt( bank:TBank,offset,value )
+	bank.PokeInt offset,value
+End Function
+
+Rem
+bbdoc: Peek a long integer from a bank
+returns: The long integer value at the specified byte offset within the bank
+about:
+A long is a 64 bit integer that requires 8 bytes of memory.
+end rem
+Function PeekLong:Long( bank:TBank,offset )
+	Return bank.PeekLong( offset )
+End Function
+
+Rem
+bbdoc: Poke a long integer int into a bank
+about:
+A long is a 64 bit integer that requires 8 bytes of storage.
+end rem
+Function PokeLong( bank:TBank,offset,value:Long )
+	bank.PokeLong offset,value
+End Function
+
+Rem
+bbdoc: Peek a float from a bank
+returns: The float value at the specified byte offset within the bank
+about:
+A float requires 4 bytes of storage
+end rem
+Function PeekFloat#( bank:TBank,offset )
+	Return bank.PeekFloat( offset )
+End Function
+
+Rem
+bbdoc: Poke a float into a bank
+about:
+A float requires 4 bytes of storage
+end rem
+Function PokeFloat( bank:TBank,offset,value# )
+	bank.PokeFloat offset,value
+End Function
+
+Rem
+bbdoc: Peek a double from a bank
+returns: The double value at the specified byte offset within the bank
+about:
+A double requires 8 bytes of storage
+end rem
+Function PeekDouble!( bank:TBank,offset )
+	Return bank.PeekDouble( offset )
+End Function
+
+Rem
+bbdoc: Poke a double into a bank
+about:
+A double requires 8 bytes of storage
+end rem
+Function PokeDouble( bank:TBank,offset,value! )
+	bank.PokeDouble offset,value
+End Function
+
+Rem
+bbdoc: Read bytes from a Stream to a Bank
+returns: The number of bytes successfully read from the Stream
+end rem
+Function ReadBank( bank:TBank,stream:TStream,offset,count )
+	Return bank.Read( stream,offset,count )
+End Function
+
+Rem
+bbdoc: Write bytes from a Bank to a Stream
+returns: The number of bytes successfully written to the Stream
+end rem
+Function WriteBank( bank:TBank,stream:TStream,offset,count )
+	Return bank.Write( stream,offset,count )
+End Function

+ 12 - 0
bank.mod/doc/intro.bbdoc

@@ -0,0 +1,12 @@
+A bank object encapsulates a block of memory you can use to store arbitrary data.
+
+Banks are useful for storing data that is of no fixed type - for example, the contents
+of a binary file.
+
+To create a bank, use the #CreateBank command.
+
+To write data to a bank, use one of 'Poke' style commands, such as #PokeByte.
+
+To read data from a bank, use one of the 'Peek' style commands, such as #PeekByte.
+
+In addition, banks can be loaded or saved using #LoadBank or #SaveBank.

+ 95 - 0
bankstream.mod/bankstream.bmx

@@ -0,0 +1,95 @@
+
+Strict
+
+Rem
+bbdoc: Streams/Bank streams
+End Rem
+Module BRL.BankStream
+
+ModuleInfo "Version: 1.01"
+ModuleInfo "Author: Mark Sibly"
+ModuleInfo "License: zlib/libpng"
+ModuleInfo "Copyright: Blitz Research Ltd"
+ModuleInfo "Modserver: BRL"
+
+ModuleInfo "History: Added TBankStreamFactory"
+
+Import BRL.Bank
+Import BRL.Stream
+
+Rem
+bbdoc: BankStream Object
+End Rem
+Type TBankStream Extends TStream
+
+	Field _pos,_bank:TBank
+
+	Method Pos()
+		Return _pos
+	End Method
+
+	Method Size()
+		Return _bank.Size()
+	End Method
+
+	Method Seek( pos )
+		If pos<0 pos=0 Else If pos>_bank.Size() pos=_bank.Size()
+		_pos=pos
+		Return _pos
+	End Method
+	
+	Method Read( buf:Byte Ptr,count )
+		If count<=0 Or _pos>=_bank.Size() Return 0
+		If _pos+count>_bank.Size() count=_bank.Size()-_pos
+		MemCopy buf,_bank.Buf()+_pos,count
+		_pos:+count
+		Return count
+	End Method
+
+	Method Write( buf:Byte Ptr,count )
+		If count<=0 Or _pos>_bank.Size() Return 0
+		If _pos+count>_bank.Size() _bank.Resize _pos+count
+		MemCopy _bank.Buf()+_pos,buf,count
+		_pos:+count
+		Return count
+	End Method
+
+	Rem
+	bbdoc: Create a bank stream
+	returns: A bank stream object
+	about:
+	A bank stream allows you to read data into or out of a bank. A bank stream extends a stream so
+	can be used in place of a stream.
+	end rem
+	Function Create:TBankStream( bank:TBank )
+		Local stream:TBankStream=New TBankStream
+		stream._bank=bank
+		Return stream
+	End Function
+	
+End Type
+
+Rem
+bbdoc: Create a bank stream
+returns: A bank stream object
+about:
+A bank stream allows you to read data into or out of a bank. A bank stream extends a stream so
+can be used in place of a stream.
+end rem
+Function CreateBankStream:TBankStream( bank:TBank )
+	If Not bank bank=TBank.Create(0)
+	Return TBankStream.Create( bank )
+End Function
+
+Type TBankStreamFactory Extends TStreamFactory
+
+	Method CreateStream:TBankStream( url:Object,proto$,path$,readable,writeable )
+		Local bank:TBank=TBank(url)
+		If bank Return CreateBankStream( bank )
+	End Method
+	
+End Type
+
+New TBankStreamFactory
+
+

+ 6 - 0
bankstream.mod/doc/intro.bbdoc

@@ -0,0 +1,6 @@
+A bank stream allows you to read data into or out of a bank. A bank stream can be used with
+any of the stream commands, or anywhere a #TStream object is expected.
+
+To create a bank stream, use the #CreateBankStream command.
+
+As with all streams, you should use #CloseStream when you are finished with the bank stream.

+ 27 - 0
basic.mod/basic.bmx

@@ -0,0 +1,27 @@
+
+Strict
+
+Module BRL.Basic
+
+ModuleInfo "Version: 1.02"
+ModuleInfo "Author: Mark Sibly"
+ModuleInfo "License: zlib/libpng"
+ModuleInfo "Copyright: Blitz Research Ltd"
+ModuleInfo "Modserver: BRL"
+
+ModuleInfo "History: 1.02 Release"
+ModuleInfo "History: Added BRL.Socket"
+
+Import BRL.Math
+Import BRL.Bank
+Import BRL.Random
+Import BRL.Socket
+Import BRL.Stream
+Import BRL.RamStream
+Import BRL.BankStream
+Import BRL.EndianStream
+Import BRL.SocketStream
+Import BRL.HTTPStream
+Import BRL.StandardIO
+Import BRL.LinkedList
+Import BRL.FileSystem

+ 884 - 0
blitz.mod/bdwgc/allchblk.c

@@ -0,0 +1,884 @@
+/*
+ * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
+ * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1998-1999 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.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+#include "private/gc_priv.h"
+
+#include <stdio.h>
+
+#ifdef GC_USE_ENTIRE_HEAP
+  int GC_use_entire_heap = TRUE;
+#else
+  int GC_use_entire_heap = FALSE;
+#endif
+
+/*
+ * Free heap blocks are kept on one of several free lists,
+ * depending on the size of the block.  Each free list is doubly linked.
+ * Adjacent free blocks are coalesced.
+ */
+
+
+# define MAX_BLACK_LIST_ALLOC (2*HBLKSIZE)
+                /* largest block we will allocate starting on a black   */
+                /* listed block.  Must be >= HBLKSIZE.                  */
+
+
+# define UNIQUE_THRESHOLD 32
+        /* Sizes up to this many HBLKs each have their own free list    */
+# define HUGE_THRESHOLD 256
+        /* Sizes of at least this many heap blocks are mapped to a      */
+        /* single free list.                                            */
+# define FL_COMPRESSION 8
+        /* In between sizes map this many distinct sizes to a single    */
+        /* bin.                                                         */
+
+# define N_HBLK_FLS ((HUGE_THRESHOLD - UNIQUE_THRESHOLD) / FL_COMPRESSION \
+                     + UNIQUE_THRESHOLD)
+
+#ifndef GC_GCJ_SUPPORT
+  STATIC
+#endif
+  struct hblk * GC_hblkfreelist[N_HBLK_FLS+1] = { 0 };
+                                /* List of completely empty heap blocks */
+                                /* Linked through hb_next field of      */
+                                /* header structure associated with     */
+                                /* block.  Remains externally visible   */
+                                /* as used by GNU GCJ currently.        */
+
+#ifndef GC_GCJ_SUPPORT
+  STATIC
+#endif
+  word GC_free_bytes[N_HBLK_FLS+1] = { 0 };
+        /* Number of free bytes on each list.  Remains visible to GCJ.  */
+
+/* Return the largest n such that the number of free bytes on lists     */
+/* n .. N_HBLK_FLS is greater or equal to GC_max_large_allocd_bytes     */
+/* minus GC_large_allocd_bytes.  If there is no such n, return 0.       */
+GC_INLINE int GC_enough_large_bytes_left(void)
+{
+    int n;
+    word bytes = GC_large_allocd_bytes;
+
+    GC_ASSERT(GC_max_large_allocd_bytes <= GC_heapsize);
+    for (n = N_HBLK_FLS; n >= 0; --n) {
+        bytes += GC_free_bytes[n];
+        if (bytes >= GC_max_large_allocd_bytes) return n;
+    }
+    return 0;
+}
+
+/* Map a number of blocks to the appropriate large block free list index. */
+STATIC int GC_hblk_fl_from_blocks(word blocks_needed)
+{
+    if (blocks_needed <= UNIQUE_THRESHOLD) return (int)blocks_needed;
+    if (blocks_needed >= HUGE_THRESHOLD) return N_HBLK_FLS;
+    return (int)(blocks_needed - UNIQUE_THRESHOLD)/FL_COMPRESSION
+                                        + UNIQUE_THRESHOLD;
+
+}
+
+# define PHDR(hhdr) HDR((hhdr) -> hb_prev)
+# define NHDR(hhdr) HDR((hhdr) -> hb_next)
+
+# ifdef USE_MUNMAP
+#   define IS_MAPPED(hhdr) (((hhdr) -> hb_flags & WAS_UNMAPPED) == 0)
+# else
+#   define IS_MAPPED(hhdr) TRUE
+# endif /* !USE_MUNMAP */
+
+#if !defined(NO_DEBUGGING) || defined(GC_ASSERTIONS)
+  /* Should return the same value as GC_large_free_bytes.       */
+  GC_INNER word GC_compute_large_free_bytes(void)
+  {
+      struct hblk * h;
+      hdr * hhdr;
+      word total_free = 0;
+      unsigned i;
+
+      for (i = 0; i <= N_HBLK_FLS; ++i) {
+        for (h = GC_hblkfreelist[i]; h != 0; h = hhdr->hb_next) {
+          hhdr = HDR(h);
+          total_free += hhdr->hb_sz;
+        }
+      }
+      return total_free;
+  }
+#endif /* !NO_DEBUGGING || GC_ASSERTIONS */
+
+# if !defined(NO_DEBUGGING)
+void GC_print_hblkfreelist(void)
+{
+    struct hblk * h;
+    hdr * hhdr;
+    unsigned i;
+    word total;
+
+    for (i = 0; i <= N_HBLK_FLS; ++i) {
+      h = GC_hblkfreelist[i];
+      if (0 != h) GC_printf("Free list %u (total size %lu):\n",
+                            i, (unsigned long)GC_free_bytes[i]);
+      while (h != 0) {
+        hhdr = HDR(h);
+        GC_printf("\t%p size %lu %s black listed\n",
+                (void *)h, (unsigned long) hhdr -> hb_sz,
+                GC_is_black_listed(h, HBLKSIZE) != 0 ? "start" :
+                GC_is_black_listed(h, hhdr -> hb_sz) != 0 ? "partially" :
+                                                        "not");
+        h = hhdr -> hb_next;
+      }
+    }
+    GC_printf("GC_large_free_bytes: %lu\n",
+              (unsigned long)GC_large_free_bytes);
+
+    if ((total = GC_compute_large_free_bytes()) != GC_large_free_bytes)
+          GC_err_printf("GC_large_free_bytes INCONSISTENT!! Should be: %lu\n",
+                        (unsigned long)total);
+}
+
+/* Return the free list index on which the block described by the header */
+/* appears, or -1 if it appears nowhere.                                 */
+static int free_list_index_of(hdr *wanted)
+{
+    struct hblk * h;
+    hdr * hhdr;
+    int i;
+
+    for (i = 0; i <= N_HBLK_FLS; ++i) {
+      h = GC_hblkfreelist[i];
+      while (h != 0) {
+        hhdr = HDR(h);
+        if (hhdr == wanted) return i;
+        h = hhdr -> hb_next;
+      }
+    }
+    return -1;
+}
+
+void GC_dump_regions(void)
+{
+    unsigned i;
+    ptr_t start, end;
+    ptr_t p;
+    size_t bytes;
+    hdr *hhdr;
+    for (i = 0; i < GC_n_heap_sects; ++i) {
+        start = GC_heap_sects[i].hs_start;
+        bytes = GC_heap_sects[i].hs_bytes;
+        end = start + bytes;
+        /* Merge in contiguous sections.        */
+          while (i+1 < GC_n_heap_sects && GC_heap_sects[i+1].hs_start == end) {
+            ++i;
+            end = GC_heap_sects[i].hs_start + GC_heap_sects[i].hs_bytes;
+          }
+        GC_printf("***Section from %p to %p\n", start, end);
+        for (p = start; (word)p < (word)end; ) {
+            hhdr = HDR(p);
+            if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) {
+                GC_printf("\t%p Missing header!!(%p)\n", p, (void *)hhdr);
+                p += HBLKSIZE;
+                continue;
+            }
+            if (HBLK_IS_FREE(hhdr)) {
+                int correct_index = GC_hblk_fl_from_blocks(
+                                        divHBLKSZ(hhdr -> hb_sz));
+                int actual_index;
+
+                GC_printf("\t%p\tfree block of size 0x%lx bytes%s\n", p,
+                          (unsigned long)(hhdr -> hb_sz),
+                          IS_MAPPED(hhdr) ? "" : " (unmapped)");
+                actual_index = free_list_index_of(hhdr);
+                if (-1 == actual_index) {
+                    GC_printf("\t\tBlock not on free list %d!!\n",
+                              correct_index);
+                } else if (correct_index != actual_index) {
+                    GC_printf("\t\tBlock on list %d, should be on %d!!\n",
+                              actual_index, correct_index);
+                }
+                p += hhdr -> hb_sz;
+            } else {
+                GC_printf("\t%p\tused for blocks of size 0x%lx bytes\n", p,
+                          (unsigned long)(hhdr -> hb_sz));
+                p += HBLKSIZE * OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz);
+            }
+        }
+    }
+}
+
+# endif /* NO_DEBUGGING */
+
+/* Initialize hdr for a block containing the indicated size and         */
+/* kind of objects.                                                     */
+/* Return FALSE on failure.                                             */
+static GC_bool setup_header(hdr * hhdr, struct hblk *block, size_t byte_sz,
+                            int kind, unsigned flags)
+{
+    word descr;
+#   ifndef MARK_BIT_PER_OBJ
+      size_t granules;
+#   endif
+
+#   ifdef ENABLE_DISCLAIM
+      if (GC_obj_kinds[kind].ok_disclaim_proc)
+        flags |= HAS_DISCLAIM;
+      if (GC_obj_kinds[kind].ok_mark_unconditionally)
+        flags |= MARK_UNCONDITIONALLY;
+#   endif
+
+    /* Set size, kind and mark proc fields */
+      hhdr -> hb_sz = byte_sz;
+      hhdr -> hb_obj_kind = (unsigned char)kind;
+      hhdr -> hb_flags = (unsigned char)flags;
+      hhdr -> hb_block = block;
+      descr = GC_obj_kinds[kind].ok_descriptor;
+      if (GC_obj_kinds[kind].ok_relocate_descr) descr += byte_sz;
+      hhdr -> hb_descr = descr;
+
+#   ifdef MARK_BIT_PER_OBJ
+     /* Set hb_inv_sz as portably as possible.                          */
+     /* We set it to the smallest value such that sz * inv_sz > 2**32    */
+     /* This may be more precision than necessary.                      */
+      if (byte_sz > MAXOBJBYTES) {
+         hhdr -> hb_inv_sz = LARGE_INV_SZ;
+      } else {
+        word inv_sz;
+
+#       if CPP_WORDSZ == 64
+          inv_sz = ((word)1 << 32)/byte_sz;
+          if (((inv_sz*byte_sz) >> 32) == 0) ++inv_sz;
+#       else  /* 32 bit words */
+          GC_ASSERT(byte_sz >= 4);
+          inv_sz = ((unsigned)1 << 31)/byte_sz;
+          inv_sz *= 2;
+          while (inv_sz*byte_sz > byte_sz) ++inv_sz;
+#       endif
+        hhdr -> hb_inv_sz = inv_sz;
+      }
+#   else /* MARK_BIT_PER_GRANULE */
+      hhdr -> hb_large_block = (unsigned char)(byte_sz > MAXOBJBYTES);
+      granules = BYTES_TO_GRANULES(byte_sz);
+      if (EXPECT(!GC_add_map_entry(granules), FALSE)) {
+        /* Make it look like a valid block. */
+        hhdr -> hb_sz = HBLKSIZE;
+        hhdr -> hb_descr = 0;
+        hhdr -> hb_large_block = TRUE;
+        hhdr -> hb_map = 0;
+        return FALSE;
+      } else {
+        size_t index = (hhdr -> hb_large_block? 0 : granules);
+        hhdr -> hb_map = GC_obj_map[index];
+      }
+#   endif /* MARK_BIT_PER_GRANULE */
+
+    /* Clear mark bits */
+    GC_clear_hdr_marks(hhdr);
+
+    hhdr -> hb_last_reclaimed = (unsigned short)GC_gc_no;
+    return(TRUE);
+}
+
+/* Remove hhdr from the free list (it is assumed to specified by index). */
+STATIC void GC_remove_from_fl_at(hdr *hhdr, int index)
+{
+    GC_ASSERT(((hhdr -> hb_sz) & (HBLKSIZE-1)) == 0);
+    if (hhdr -> hb_prev == 0) {
+        GC_ASSERT(HDR(GC_hblkfreelist[index]) == hhdr);
+        GC_hblkfreelist[index] = hhdr -> hb_next;
+    } else {
+        hdr *phdr;
+        GET_HDR(hhdr -> hb_prev, phdr);
+        phdr -> hb_next = hhdr -> hb_next;
+    }
+    /* We always need index to maintain free counts.    */
+    GC_ASSERT(GC_free_bytes[index] >= hhdr -> hb_sz);
+    GC_free_bytes[index] -= hhdr -> hb_sz;
+    if (0 != hhdr -> hb_next) {
+        hdr * nhdr;
+        GC_ASSERT(!IS_FORWARDING_ADDR_OR_NIL(NHDR(hhdr)));
+        GET_HDR(hhdr -> hb_next, nhdr);
+        nhdr -> hb_prev = hhdr -> hb_prev;
+    }
+}
+
+/* Remove hhdr from the appropriate free list (we assume it is on the   */
+/* size-appropriate free list).                                         */
+GC_INLINE void GC_remove_from_fl(hdr *hhdr)
+{
+  GC_remove_from_fl_at(hhdr, GC_hblk_fl_from_blocks(divHBLKSZ(hhdr->hb_sz)));
+}
+
+/* Return a pointer to the free block ending just before h, if any.     */
+STATIC struct hblk * GC_free_block_ending_at(struct hblk *h)
+{
+    struct hblk * p = h - 1;
+    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) {
+        if(HBLK_IS_FREE(phdr)) {
+            return p;
+        } else {
+            return 0;
+        }
+    }
+    p = GC_prev_block(h - 1);
+    if (0 != p) {
+      phdr = HDR(p);
+      if (HBLK_IS_FREE(phdr) && (ptr_t)p + phdr -> hb_sz == (ptr_t)h) {
+        return p;
+      }
+    }
+    return 0;
+}
+
+/* Add hhdr to the appropriate free list.               */
+/* We maintain individual free lists sorted by address. */
+STATIC void GC_add_to_fl(struct hblk *h, hdr *hhdr)
+{
+    int index = GC_hblk_fl_from_blocks(divHBLKSZ(hhdr -> hb_sz));
+    struct hblk *second = GC_hblkfreelist[index];
+    hdr * second_hdr;
+#   if defined(GC_ASSERTIONS) && !defined(USE_MUNMAP)
+      struct hblk *next = (struct hblk *)((word)h + hhdr -> hb_sz);
+      hdr * nexthdr = HDR(next);
+      struct hblk *prev = GC_free_block_ending_at(h);
+      hdr * prevhdr = HDR(prev);
+      GC_ASSERT(nexthdr == 0 || !HBLK_IS_FREE(nexthdr)
+                || (signed_word)GC_heapsize < 0);
+                /* In the last case, blocks may be too large to merge. */
+      GC_ASSERT(prev == 0 || !HBLK_IS_FREE(prevhdr)
+                || (signed_word)GC_heapsize < 0);
+#   endif
+
+    GC_ASSERT(((hhdr -> hb_sz) & (HBLKSIZE-1)) == 0);
+    GC_hblkfreelist[index] = h;
+    GC_free_bytes[index] += hhdr -> hb_sz;
+    GC_ASSERT(GC_free_bytes[index] <= GC_large_free_bytes);
+    hhdr -> hb_next = second;
+    hhdr -> hb_prev = 0;
+    if (0 != second) {
+      GET_HDR(second, second_hdr);
+      second_hdr -> hb_prev = h;
+    }
+    hhdr -> hb_flags |= FREE_BLK;
+}
+
+#ifdef USE_MUNMAP
+
+#   ifndef MUNMAP_THRESHOLD
+#     define MUNMAP_THRESHOLD 6
+#   endif
+
+GC_INNER int GC_unmap_threshold = MUNMAP_THRESHOLD;
+
+/* Unmap blocks that haven't been recently touched.  This is the only way */
+/* way blocks are ever unmapped.                                          */
+GC_INNER void GC_unmap_old(void)
+{
+    struct hblk * h;
+    hdr * hhdr;
+    int i;
+
+    if (GC_unmap_threshold == 0)
+      return; /* unmapping disabled */
+
+    for (i = 0; i <= N_HBLK_FLS; ++i) {
+      for (h = GC_hblkfreelist[i]; 0 != h; h = hhdr -> hb_next) {
+        hhdr = HDR(h);
+        if (!IS_MAPPED(hhdr)) continue;
+
+        if ((unsigned short)GC_gc_no - hhdr -> hb_last_reclaimed >
+                (unsigned short)GC_unmap_threshold) {
+          GC_unmap((ptr_t)h, hhdr -> hb_sz);
+          hhdr -> hb_flags |= WAS_UNMAPPED;
+        }
+      }
+    }
+}
+
+/* Merge all unmapped blocks that are adjacent to other free            */
+/* blocks.  This may involve remapping, since all blocks are either     */
+/* fully mapped or fully unmapped.                                      */
+GC_INNER void GC_merge_unmapped(void)
+{
+    struct hblk * h, *next;
+    hdr * hhdr, *nexthdr;
+    word size, nextsize;
+    int i;
+
+    for (i = 0; i <= N_HBLK_FLS; ++i) {
+      h = GC_hblkfreelist[i];
+      while (h != 0) {
+        GET_HDR(h, hhdr);
+        size = hhdr->hb_sz;
+        next = (struct hblk *)((word)h + size);
+        GET_HDR(next, nexthdr);
+        /* Coalesce with successor, if possible */
+          if (0 != nexthdr && HBLK_IS_FREE(nexthdr)
+              && (signed_word) (size + (nextsize = nexthdr->hb_sz)) > 0
+                 /* no pot. overflow */) {
+            /* Note that we usually try to avoid adjacent free blocks   */
+            /* that are either both mapped or both unmapped.  But that  */
+            /* isn't guaranteed to hold since we remap blocks when we   */
+            /* split them, and don't merge at that point.  It may also  */
+            /* not hold if the merged block would be too big.           */
+            if (IS_MAPPED(hhdr) && !IS_MAPPED(nexthdr)) {
+              /* make both consistent, so that we can merge */
+                if (size > nextsize) {
+                  GC_remap((ptr_t)next, nextsize);
+                } else {
+                  GC_unmap((ptr_t)h, size);
+                  GC_unmap_gap((ptr_t)h, size, (ptr_t)next, nextsize);
+                  hhdr -> hb_flags |= WAS_UNMAPPED;
+                }
+            } else if (IS_MAPPED(nexthdr) && !IS_MAPPED(hhdr)) {
+              if (size > nextsize) {
+                GC_unmap((ptr_t)next, nextsize);
+                GC_unmap_gap((ptr_t)h, size, (ptr_t)next, nextsize);
+              } else {
+                GC_remap((ptr_t)h, size);
+                hhdr -> hb_flags &= ~WAS_UNMAPPED;
+                hhdr -> hb_last_reclaimed = nexthdr -> hb_last_reclaimed;
+              }
+            } else if (!IS_MAPPED(hhdr) && !IS_MAPPED(nexthdr)) {
+              /* Unmap any gap in the middle */
+                GC_unmap_gap((ptr_t)h, size, (ptr_t)next, nextsize);
+            }
+            /* If they are both unmapped, we merge, but leave unmapped. */
+            GC_remove_from_fl_at(hhdr, i);
+            GC_remove_from_fl(nexthdr);
+            hhdr -> hb_sz += nexthdr -> hb_sz;
+            GC_remove_header(next);
+            GC_add_to_fl(h, hhdr);
+            /* Start over at beginning of list */
+            h = GC_hblkfreelist[i];
+          } else /* not mergable with successor */ {
+            h = hhdr -> hb_next;
+          }
+      } /* while (h != 0) ... */
+    } /* for ... */
+}
+
+#endif /* USE_MUNMAP */
+
+/*
+ * Return a pointer to a block starting at h of length bytes.
+ * Memory for the block is mapped.
+ * Remove the block from its free list, and return the remainder (if any)
+ * to its appropriate free list.
+ * May fail by returning 0.
+ * The header for the returned block must be set up by the caller.
+ * If the return value is not 0, then hhdr is the header for it.
+ */
+STATIC struct hblk * GC_get_first_part(struct hblk *h, hdr *hhdr,
+                                       size_t bytes, int index)
+{
+    word total_size = hhdr -> hb_sz;
+    struct hblk * rest;
+    hdr * rest_hdr;
+
+    GC_ASSERT((total_size & (HBLKSIZE-1)) == 0);
+    GC_remove_from_fl_at(hhdr, index);
+    if (total_size == bytes) return h;
+    rest = (struct hblk *)((word)h + bytes);
+    rest_hdr = GC_install_header(rest);
+    if (0 == rest_hdr) {
+        /* FIXME: This is likely to be very bad news ... */
+        WARN("Header allocation failed: Dropping block.\n", 0);
+        return(0);
+    }
+    rest_hdr -> hb_sz = total_size - bytes;
+    rest_hdr -> hb_flags = 0;
+#   ifdef GC_ASSERTIONS
+      /* Mark h not free, to avoid assertion about adjacent free blocks. */
+        hhdr -> hb_flags &= ~FREE_BLK;
+#   endif
+    GC_add_to_fl(rest, rest_hdr);
+    return h;
+}
+
+/*
+ * H is a free block.  N points at an address inside it.
+ * A new header for n has already been set up.  Fix up h's header
+ * to reflect the fact that it is being split, move it to the
+ * appropriate free list.
+ * N replaces h in the original free list.
+ *
+ * Nhdr is not completely filled in, since it is about to allocated.
+ * It may in fact end up on the wrong free list for its size.
+ * That's not a disaster, since n is about to be allocated
+ * by our caller.
+ * (Hence adding it to a free list is silly.  But this path is hopefully
+ * rare enough that it doesn't matter.  The code is cleaner this way.)
+ */
+STATIC void GC_split_block(struct hblk *h, hdr *hhdr, struct hblk *n,
+                           hdr *nhdr, int index /* Index of free list */)
+{
+    word total_size = hhdr -> hb_sz;
+    word h_size = (word)n - (word)h;
+    struct hblk *prev = hhdr -> hb_prev;
+    struct hblk *next = hhdr -> hb_next;
+
+    /* Replace h with n on its freelist */
+      nhdr -> hb_prev = prev;
+      nhdr -> hb_next = next;
+      nhdr -> hb_sz = total_size - h_size;
+      nhdr -> hb_flags = 0;
+      if (0 != prev) {
+        HDR(prev) -> hb_next = n;
+      } else {
+        GC_hblkfreelist[index] = n;
+      }
+      if (0 != next) {
+        HDR(next) -> hb_prev = n;
+      }
+      GC_ASSERT(GC_free_bytes[index] > h_size);
+      GC_free_bytes[index] -= h_size;
+#   ifdef USE_MUNMAP
+      hhdr -> hb_last_reclaimed = (unsigned short)GC_gc_no;
+#   endif
+    hhdr -> hb_sz = h_size;
+    GC_add_to_fl(h, hhdr);
+    nhdr -> hb_flags |= FREE_BLK;
+}
+
+STATIC struct hblk *
+GC_allochblk_nth(size_t sz /* bytes */, int kind, unsigned flags, int n,
+                 int may_split);
+#define AVOID_SPLIT_REMAPPED 2
+
+/*
+ * Allocate (and return pointer to) a heap block
+ *   for objects of size sz bytes, searching the nth free list.
+ *
+ * NOTE: We set obj_map field in header correctly.
+ *       Caller is responsible for building an object freelist in block.
+ *
+ * The client is responsible for clearing the block, if necessary.
+ */
+GC_INNER struct hblk *
+GC_allochblk(size_t sz, int kind, unsigned flags/* IGNORE_OFF_PAGE or 0 */)
+{
+    word blocks;
+    int start_list;
+    struct hblk *result;
+    int may_split;
+    int split_limit; /* Highest index of free list whose blocks we      */
+                     /* split.                                          */
+
+    GC_ASSERT((sz & (GRANULE_BYTES - 1)) == 0);
+    blocks = OBJ_SZ_TO_BLOCKS(sz);
+    if ((signed_word)(blocks * HBLKSIZE) < 0) {
+      return 0;
+    }
+    start_list = GC_hblk_fl_from_blocks(blocks);
+    /* Try for an exact match first. */
+    result = GC_allochblk_nth(sz, kind, flags, start_list, FALSE);
+    if (0 != result) return result;
+
+    may_split = TRUE;
+    if (GC_use_entire_heap || GC_dont_gc
+        || USED_HEAP_SIZE < GC_requested_heapsize
+        || GC_incremental || !GC_should_collect()) {
+        /* Should use more of the heap, even if it requires splitting. */
+        split_limit = N_HBLK_FLS;
+    } else if (GC_finalizer_bytes_freed > (GC_heapsize >> 4)) {
+          /* If we are deallocating lots of memory from         */
+          /* finalizers, fail and collect sooner rather         */
+          /* than later.                                        */
+          split_limit = 0;
+    } else {
+          /* If we have enough large blocks left to cover any   */
+          /* previous request for large blocks, we go ahead     */
+          /* and split.  Assuming a steady state, that should   */
+          /* be safe.  It means that we can use the full        */
+          /* heap if we allocate only small objects.            */
+          split_limit = GC_enough_large_bytes_left();
+#         ifdef USE_MUNMAP
+            if (split_limit > 0)
+              may_split = AVOID_SPLIT_REMAPPED;
+#         endif
+    }
+    if (start_list < UNIQUE_THRESHOLD) {
+      /* No reason to try start_list again, since all blocks are exact  */
+      /* matches.                                                       */
+      ++start_list;
+    }
+    for (; start_list <= split_limit; ++start_list) {
+        result = GC_allochblk_nth(sz, kind, flags, start_list, may_split);
+        if (0 != result)
+            break;
+    }
+    return result;
+}
+
+STATIC long GC_large_alloc_warn_suppressed = 0;
+                        /* Number of warnings suppressed so far.        */
+
+/* The same, but with search restricted to nth free list.  Flags is     */
+/* IGNORE_OFF_PAGE or zero.  sz is in bytes.  The may_split flag        */
+/* indicates whether it is OK to split larger blocks (if set to         */
+/* AVOID_SPLIT_REMAPPED then memory remapping followed by splitting     */
+/* should be generally avoided).                                        */
+STATIC struct hblk *
+GC_allochblk_nth(size_t sz, int kind, unsigned flags, int n, int may_split)
+{
+    struct hblk *hbp;
+    hdr * hhdr;                 /* Header corr. to hbp */
+    struct hblk *thishbp;
+    hdr * thishdr;              /* Header corr. to thishbp */
+    signed_word size_needed;    /* number of bytes in requested objects */
+    signed_word size_avail;     /* bytes available in this block        */
+
+    size_needed = HBLKSIZE * OBJ_SZ_TO_BLOCKS(sz);
+
+    /* search for a big enough block in free list */
+        for (hbp = GC_hblkfreelist[n];; hbp = hhdr -> hb_next) {
+            if (NULL == hbp) return NULL;
+            GET_HDR(hbp, hhdr); /* set hhdr value */
+            size_avail = hhdr->hb_sz;
+            if (size_avail < size_needed) continue;
+            if (size_avail != size_needed) {
+              signed_word next_size;
+
+              if (!may_split) continue;
+              /* If the next heap block is obviously better, go on.     */
+              /* This prevents us from disassembling a single large     */
+              /* block to get tiny blocks.                              */
+              thishbp = hhdr -> hb_next;
+              if (thishbp != 0) {
+                GET_HDR(thishbp, thishdr);
+                next_size = (signed_word)(thishdr -> hb_sz);
+                if (next_size < size_avail
+                    && next_size >= size_needed
+                    && !GC_is_black_listed(thishbp, (word)size_needed)) {
+                    continue;
+                }
+              }
+            }
+            if (!IS_UNCOLLECTABLE(kind) && (kind != PTRFREE
+                        || size_needed > (signed_word)MAX_BLACK_LIST_ALLOC)) {
+              struct hblk * lasthbp = hbp;
+              ptr_t search_end = (ptr_t)hbp + size_avail - size_needed;
+              signed_word orig_avail = size_avail;
+              signed_word eff_size_needed = (flags & IGNORE_OFF_PAGE) != 0 ?
+                                                (signed_word)HBLKSIZE
+                                                : size_needed;
+
+              while ((word)lasthbp <= (word)search_end
+                     && (thishbp = GC_is_black_listed(lasthbp,
+                                            (word)eff_size_needed)) != 0) {
+                lasthbp = thishbp;
+              }
+              size_avail -= (ptr_t)lasthbp - (ptr_t)hbp;
+              thishbp = lasthbp;
+              if (size_avail >= size_needed) {
+                if (thishbp != hbp) {
+#                 ifdef USE_MUNMAP
+                    /* Avoid remapping followed by splitting.   */
+                    if (may_split == AVOID_SPLIT_REMAPPED && !IS_MAPPED(hhdr))
+                      continue;
+#                 endif
+                  thishdr = GC_install_header(thishbp);
+                  if (0 != thishdr) {
+                  /* Make sure it's mapped before we mangle it. */
+#                   ifdef USE_MUNMAP
+                      if (!IS_MAPPED(hhdr)) {
+                        GC_remap((ptr_t)hbp, hhdr -> hb_sz);
+                        hhdr -> hb_flags &= ~WAS_UNMAPPED;
+                      }
+#                   endif
+                  /* Split the block at thishbp */
+                      GC_split_block(hbp, hhdr, thishbp, thishdr, n);
+                  /* Advance to thishbp */
+                      hbp = thishbp;
+                      hhdr = thishdr;
+                      /* We must now allocate thishbp, since it may     */
+                      /* be on the wrong free list.                     */
+                  }
+                }
+              } else if (size_needed > (signed_word)BL_LIMIT
+                         && orig_avail - size_needed
+                            > (signed_word)BL_LIMIT) {
+                /* Punt, since anything else risks unreasonable heap growth. */
+                if (++GC_large_alloc_warn_suppressed
+                    >= GC_large_alloc_warn_interval) {
+                  WARN("Repeated allocation of very large block "
+                       "(appr. size %" WARN_PRIdPTR "):\n"
+                       "\tMay lead to memory leak and poor performance.\n",
+                       size_needed);
+                  GC_large_alloc_warn_suppressed = 0;
+                }
+                size_avail = orig_avail;
+              } else if (size_avail == 0 && size_needed == HBLKSIZE
+                         && IS_MAPPED(hhdr)) {
+                if (!GC_find_leak) {
+                  static unsigned count = 0;
+
+                  /* The block is completely blacklisted.  We need      */
+                  /* to drop some such blocks, since otherwise we spend */
+                  /* all our time traversing them if pointer-free       */
+                  /* blocks are unpopular.                              */
+                  /* A dropped block will be reconsidered at next GC.   */
+                  if ((++count & 3) == 0) {
+                    /* Allocate and drop the block in small chunks, to  */
+                    /* maximize the chance that we will recover some    */
+                    /* later.                                           */
+                      word total_size = hhdr -> hb_sz;
+                      struct hblk * limit = hbp + divHBLKSZ(total_size);
+                      struct hblk * h;
+                      struct hblk * prev = hhdr -> hb_prev;
+
+                      GC_large_free_bytes -= total_size;
+                      GC_bytes_dropped += total_size;
+                      GC_remove_from_fl_at(hhdr, n);
+                      for (h = hbp; (word)h < (word)limit; h++) {
+                        if (h != hbp) {
+                          hhdr = GC_install_header(h);
+                        }
+                        if (NULL != hhdr) {
+                          (void)setup_header(hhdr, h, HBLKSIZE, PTRFREE, 0);
+                                                    /* Can't fail. */
+                          if (GC_debugging_started) {
+                            BZERO(h, HBLKSIZE);
+                          }
+                        }
+                      }
+                    /* Restore hbp to point at free block */
+                      hbp = prev;
+                      if (0 == hbp) {
+                        return GC_allochblk_nth(sz, kind, flags, n, may_split);
+                      }
+                      hhdr = HDR(hbp);
+                  }
+                }
+              }
+            }
+            if( size_avail >= size_needed ) {
+#               ifdef USE_MUNMAP
+                  if (!IS_MAPPED(hhdr)) {
+                    GC_remap((ptr_t)hbp, hhdr -> hb_sz);
+                    hhdr -> hb_flags &= ~WAS_UNMAPPED;
+                    /* Note: This may leave adjacent, mapped free blocks. */
+                  }
+#               endif
+                /* hbp may be on the wrong freelist; the parameter n    */
+                /* is important.                                        */
+                hbp = GC_get_first_part(hbp, hhdr, size_needed, n);
+                break;
+            }
+        }
+
+    if (0 == hbp) return 0;
+
+    /* Add it to map of valid blocks */
+        if (!GC_install_counts(hbp, (word)size_needed)) return(0);
+        /* This leaks memory under very rare conditions. */
+
+    /* Set up header */
+        if (!setup_header(hhdr, hbp, sz, kind, flags)) {
+            GC_remove_counts(hbp, (word)size_needed);
+            return(0); /* ditto */
+        }
+#   ifndef GC_DISABLE_INCREMENTAL
+        /* Notify virtual dirty bit implementation that we are about to */
+        /* write.  Ensure that pointer-free objects are not protected   */
+        /* if it is avoidable.  This also ensures that newly allocated  */
+        /* blocks are treated as dirty.  Necessary since we don't       */
+        /* protect free blocks.                                         */
+        GC_ASSERT((size_needed & (HBLKSIZE-1)) == 0);
+        GC_remove_protection(hbp, divHBLKSZ(size_needed),
+                             (hhdr -> hb_descr == 0) /* pointer-free */);
+#   endif
+    /* We just successfully allocated a block.  Restart count of        */
+    /* consecutive failures.                                            */
+    GC_fail_count = 0;
+
+    GC_large_free_bytes -= size_needed;
+    GC_ASSERT(IS_MAPPED(hhdr));
+    return( hbp );
+}
+
+/*
+ * Free a heap block.
+ *
+ * Coalesce the block with its neighbors if possible.
+ *
+ * All mark words are assumed to be cleared.
+ */
+GC_INNER void GC_freehblk(struct hblk *hbp)
+{
+    struct hblk *next, *prev;
+    hdr *hhdr, *prevhdr, *nexthdr;
+    word size;
+
+    GET_HDR(hbp, hhdr);
+    size = HBLKSIZE * OBJ_SZ_TO_BLOCKS(hhdr->hb_sz);
+    if ((signed_word)size <= 0)
+      ABORT("Deallocating excessively large block.  Too large an allocation?");
+      /* Probably possible if we try to allocate more than half the address */
+      /* space at once.  If we don't catch it here, strange things happen   */
+      /* later.                                                             */
+    GC_remove_counts(hbp, size);
+    hhdr->hb_sz = size;
+#   ifdef USE_MUNMAP
+      hhdr -> hb_last_reclaimed = (unsigned short)GC_gc_no;
+#   endif
+
+    /* Check for duplicate deallocation in the easy case */
+      if (HBLK_IS_FREE(hhdr)) {
+        ABORT_ARG1("Duplicate large block deallocation",
+                   " of %p", (void *)hbp);
+      }
+
+    GC_ASSERT(IS_MAPPED(hhdr));
+    hhdr -> hb_flags |= FREE_BLK;
+    next = (struct hblk *)((ptr_t)hbp + size);
+    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)
+         && (signed_word)(hhdr -> hb_sz + nexthdr -> hb_sz) > 0
+         /* no overflow */) {
+        GC_remove_from_fl(nexthdr);
+        hhdr -> hb_sz += nexthdr -> hb_sz;
+        GC_remove_header(next);
+      }
+    /* Coalesce with predecessor, if possible. */
+      if (0 != prev) {
+        prevhdr = HDR(prev);
+        if (IS_MAPPED(prevhdr)
+            && (signed_word)(hhdr -> hb_sz + prevhdr -> hb_sz) > 0) {
+          GC_remove_from_fl(prevhdr);
+          prevhdr -> hb_sz += hhdr -> hb_sz;
+#         ifdef USE_MUNMAP
+            prevhdr -> hb_last_reclaimed = (unsigned short)GC_gc_no;
+#         endif
+          GC_remove_header(hbp);
+          hbp = prev;
+          hhdr = prevhdr;
+        }
+      }
+    /* FIXME: It is not clear we really always want to do these merges  */
+    /* with USE_MUNMAP, since it updates ages and hence prevents        */
+    /* unmapping.                                                       */
+
+    GC_large_free_bytes += size;
+    GC_add_to_fl(hbp, hhdr);
+}

+ 1358 - 0
blitz.mod/bdwgc/alloc.c

@@ -0,0 +1,1358 @@
+/*
+ * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
+ * Copyright (c) 1991-1996 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1998 by Silicon Graphics.  All rights reserved.
+ * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ *
+ */
+
+#include "private/gc_priv.h"
+
+#include <stdio.h>
+#if !defined(MACOS) && !defined(MSWINCE)
+# include <signal.h>
+# if !defined(__CC_ARM)
+#   include <sys/types.h>
+# endif
+#endif
+
+/*
+ * Separate free lists are maintained for different sized objects
+ * up to MAXOBJBYTES.
+ * The call GC_allocobj(i,k) ensures that the freelist for
+ * kind k objects of size i points to a non-empty
+ * free list. It returns a pointer to the first entry on the free list.
+ * In a single-threaded world, GC_allocobj may be called to allocate
+ * an object of (small) size i as follows:
+ *
+ *            opp = &(GC_objfreelist[i]);
+ *            if (*opp == 0) GC_allocobj(i, NORMAL);
+ *            ptr = *opp;
+ *            *opp = obj_link(ptr);
+ *
+ * Note that this is very fast if the free list is non-empty; it should
+ * only involve the execution of 4 or 5 simple instructions.
+ * All composite objects on freelists are cleared, except for
+ * their first word.
+ */
+
+/*
+ * The allocator uses GC_allochblk to allocate large chunks of objects.
+ * These chunks all start on addresses which are multiples of
+ * HBLKSZ.   Each allocated chunk has an associated header,
+ * which can be located quickly based on the address of the chunk.
+ * (See headers.c for details.)
+ * This makes it possible to check quickly whether an
+ * arbitrary address corresponds to an object administered by the
+ * allocator.
+ */
+
+word GC_non_gc_bytes = 0;  /* Number of bytes not intended to be collected */
+
+word GC_gc_no = 0;
+
+#ifndef GC_DISABLE_INCREMENTAL
+  GC_INNER int GC_incremental = 0;      /* By default, stop the world.  */
+#endif
+
+#ifdef THREADS
+  int GC_parallel = FALSE;      /* By default, parallel GC is off.      */
+#endif
+
+#ifndef GC_FULL_FREQ
+# define GC_FULL_FREQ 19   /* Every 20th collection is a full   */
+                           /* collection, whether we need it    */
+                           /* or not.                           */
+#endif
+
+int GC_full_freq = GC_FULL_FREQ;
+
+STATIC GC_bool GC_need_full_gc = FALSE;
+                           /* Need full GC do to heap growth.   */
+
+#ifdef THREAD_LOCAL_ALLOC
+  GC_INNER GC_bool GC_world_stopped = FALSE;
+#endif
+
+STATIC word GC_used_heap_size_after_full = 0;
+
+/* GC_copyright symbol is externally visible. */
+char * const GC_copyright[] =
+{"Copyright 1988,1989 Hans-J. Boehm and Alan J. Demers ",
+"Copyright (c) 1991-1995 by Xerox Corporation.  All rights reserved. ",
+"Copyright (c) 1996-1998 by Silicon Graphics.  All rights reserved. ",
+"Copyright (c) 1999-2009 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.",
+"See source code for details." };
+
+/* Version macros are now defined in gc_version.h, which is included by */
+/* gc.h, which is included by gc_priv.h.                                */
+#ifndef GC_NO_VERSION_VAR
+  const unsigned GC_version = ((GC_VERSION_MAJOR << 16) |
+                        (GC_VERSION_MINOR << 8) | GC_VERSION_MICRO);
+#endif
+
+GC_API unsigned GC_CALL GC_get_version(void)
+{
+  return (GC_VERSION_MAJOR << 16) | (GC_VERSION_MINOR << 8) |
+          GC_VERSION_MICRO;
+}
+
+/* some more variables */
+
+#ifdef GC_DONT_EXPAND
+  int GC_dont_expand = TRUE;
+#else
+  int GC_dont_expand = FALSE;
+#endif
+
+#ifndef GC_FREE_SPACE_DIVISOR
+# define GC_FREE_SPACE_DIVISOR 3 /* must be > 0 */
+#endif
+
+word GC_free_space_divisor = GC_FREE_SPACE_DIVISOR;
+
+GC_INNER int GC_CALLBACK GC_never_stop_func(void)
+{
+  return(0);
+}
+
+#ifndef GC_TIME_LIMIT
+# define GC_TIME_LIMIT 50  /* We try to keep pause times from exceeding  */
+                           /* this by much. In milliseconds.             */
+#endif
+
+unsigned long GC_time_limit = GC_TIME_LIMIT;
+
+#ifndef NO_CLOCK
+  STATIC CLOCK_TYPE GC_start_time = 0;
+                                /* Time at which we stopped world.      */
+                                /* used only in GC_timeout_stop_func.   */
+#endif
+
+STATIC int GC_n_attempts = 0;   /* Number of attempts at finishing      */
+                                /* collection within GC_time_limit.     */
+
+STATIC GC_stop_func GC_default_stop_func = GC_never_stop_func;
+                                /* accessed holding the lock.           */
+
+GC_API void GC_CALL GC_set_stop_func(GC_stop_func stop_func)
+{
+  DCL_LOCK_STATE;
+  GC_ASSERT(stop_func != 0);
+  LOCK();
+  GC_default_stop_func = stop_func;
+  UNLOCK();
+}
+
+GC_API GC_stop_func GC_CALL GC_get_stop_func(void)
+{
+  GC_stop_func stop_func;
+  DCL_LOCK_STATE;
+  LOCK();
+  stop_func = GC_default_stop_func;
+  UNLOCK();
+  return stop_func;
+}
+
+#if defined(GC_DISABLE_INCREMENTAL) || defined(NO_CLOCK)
+# define GC_timeout_stop_func GC_default_stop_func
+#else
+  STATIC int GC_CALLBACK GC_timeout_stop_func (void)
+  {
+    CLOCK_TYPE current_time;
+    static unsigned count = 0;
+    unsigned long time_diff;
+
+    if ((*GC_default_stop_func)())
+      return(1);
+
+    if ((count++ & 3) != 0) return(0);
+    GET_TIME(current_time);
+    time_diff = MS_TIME_DIFF(current_time,GC_start_time);
+    if (time_diff >= GC_time_limit) {
+        GC_COND_LOG_PRINTF(
+                "Abandoning stopped marking after %lu msecs (attempt %d)\n",
+                time_diff, GC_n_attempts);
+        return(1);
+    }
+    return(0);
+  }
+#endif /* !GC_DISABLE_INCREMENTAL */
+
+#ifdef THREADS
+  GC_INNER word GC_total_stacksize = 0; /* updated on every push_all_stacks */
+#endif
+
+/* Return the minimum number of bytes that must be allocated between    */
+/* collections to amortize the collection cost.  Should be non-zero.    */
+static word min_bytes_allocd(void)
+{
+    word result;
+#   ifdef STACK_GROWS_UP
+      word stack_size = GC_approx_sp() - GC_stackbottom;
+            /* GC_stackbottom is used only for a single-threaded case.  */
+#   else
+      word stack_size = GC_stackbottom - GC_approx_sp();
+#   endif
+
+    word total_root_size;       /* includes double stack size,  */
+                                /* since the stack is expensive */
+                                /* to scan.                     */
+    word scan_size;             /* Estimate of memory to be scanned     */
+                                /* during normal GC.                    */
+
+#   ifdef THREADS
+      if (GC_need_to_lock) {
+        /* We are multi-threaded... */
+        stack_size = GC_total_stacksize;
+        /* For now, we just use the value computed during the latest GC. */
+#       ifdef DEBUG_THREADS
+          GC_log_printf("Total stacks size: %lu\n",
+                        (unsigned long)stack_size);
+#       endif
+      }
+#   endif
+
+    total_root_size = 2 * stack_size + GC_root_size;
+    scan_size = 2 * GC_composite_in_use + GC_atomic_in_use / 4
+                + total_root_size;
+    result = scan_size / GC_free_space_divisor;
+    if (GC_incremental) {
+      result /= 2;
+    }
+    return result > 0 ? result : 1;
+}
+
+STATIC word GC_non_gc_bytes_at_gc = 0;
+                /* Number of explicitly managed bytes of storage        */
+                /* at last collection.                                  */
+
+/* Return the number of bytes allocated, adjusted for explicit storage  */
+/* management, etc..  This number is used in deciding when to trigger   */
+/* collections.                                                         */
+STATIC word GC_adj_bytes_allocd(void)
+{
+    signed_word result;
+    signed_word expl_managed = (signed_word)GC_non_gc_bytes
+                                - (signed_word)GC_non_gc_bytes_at_gc;
+
+    /* Don't count what was explicitly freed, or newly allocated for    */
+    /* explicit management.  Note that deallocating an explicitly       */
+    /* managed object should not alter result, assuming the client      */
+    /* is playing by the rules.                                         */
+    result = (signed_word)GC_bytes_allocd
+             + (signed_word)GC_bytes_dropped
+             - (signed_word)GC_bytes_freed
+             + (signed_word)GC_finalizer_bytes_freed
+             - expl_managed;
+    if (result > (signed_word)GC_bytes_allocd) {
+        result = GC_bytes_allocd;
+        /* probably client bug or unfortunate scheduling */
+    }
+    result += GC_bytes_finalized;
+        /* We count objects enqueued for finalization as though they    */
+        /* had been reallocated this round. Finalization is user        */
+        /* visible progress.  And if we don't count this, we have       */
+        /* stability problems for programs that finalize all objects.   */
+    if (result < (signed_word)(GC_bytes_allocd >> 3)) {
+        /* Always count at least 1/8 of the allocations.  We don't want */
+        /* to collect too infrequently, since that would inhibit        */
+        /* coalescing of free storage blocks.                           */
+        /* This also makes us partially robust against client bugs.     */
+        return(GC_bytes_allocd >> 3);
+    } else {
+        return(result);
+    }
+}
+
+
+/* Clear up a few frames worth of garbage left at the top of the stack. */
+/* This is used to prevent us from accidentally treating garbage left   */
+/* on the stack by other parts of the collector as roots.  This         */
+/* differs from the code in misc.c, which actually tries to keep the    */
+/* stack clear of long-lived, client-generated garbage.                 */
+STATIC void GC_clear_a_few_frames(void)
+{
+#   ifndef CLEAR_NWORDS
+#     define CLEAR_NWORDS 64
+#   endif
+    volatile word frames[CLEAR_NWORDS];
+    BZERO((word *)frames, CLEAR_NWORDS * sizeof(word));
+}
+
+/* Heap size at which we need a collection to avoid expanding past      */
+/* limits used by blacklisting.                                         */
+STATIC word GC_collect_at_heapsize = (word)(-1);
+
+/* Have we allocated enough to amortize a collection? */
+GC_INNER GC_bool GC_should_collect(void)
+{
+    static word last_min_bytes_allocd;
+    static word last_gc_no;
+    if (last_gc_no != GC_gc_no) {
+      last_gc_no = GC_gc_no;
+      last_min_bytes_allocd = min_bytes_allocd();
+    }
+    return(GC_adj_bytes_allocd() >= last_min_bytes_allocd
+           || GC_heapsize >= GC_collect_at_heapsize);
+}
+
+/* STATIC */ GC_start_callback_proc GC_start_call_back = 0;
+                        /* Called at start of full collections.         */
+                        /* Not called if 0.  Called with the allocation */
+                        /* lock held.  Not used by GC itself.           */
+
+GC_API void GC_CALL GC_set_start_callback(GC_start_callback_proc fn)
+{
+    DCL_LOCK_STATE;
+    LOCK();
+    GC_start_call_back = fn;
+    UNLOCK();
+}
+
+GC_API GC_start_callback_proc GC_CALL GC_get_start_callback(void)
+{
+    GC_start_callback_proc fn;
+    DCL_LOCK_STATE;
+    LOCK();
+    fn = GC_start_call_back;
+    UNLOCK();
+    return fn;
+}
+
+GC_INLINE void GC_notify_full_gc(void)
+{
+    if (GC_start_call_back != 0) {
+        (*GC_start_call_back)();
+    }
+}
+
+STATIC GC_bool GC_is_full_gc = FALSE;
+
+STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func);
+STATIC void GC_finish_collection(void);
+
+/*
+ * Initiate a garbage collection if appropriate.
+ * Choose judiciously
+ * between partial, full, and stop-world collections.
+ */
+STATIC void GC_maybe_gc(void)
+{
+    static int n_partial_gcs = 0;
+
+    GC_ASSERT(I_HOLD_LOCK());
+    ASSERT_CANCEL_DISABLED();
+    if (GC_should_collect()) {
+        if (!GC_incremental) {
+            /* FIXME: If possible, GC_default_stop_func should be used here */
+            GC_try_to_collect_inner(GC_never_stop_func);
+            n_partial_gcs = 0;
+            return;
+        } else {
+#         ifdef PARALLEL_MARK
+            if (GC_parallel)
+              GC_wait_for_reclaim();
+#         endif
+          if (GC_need_full_gc || n_partial_gcs >= GC_full_freq) {
+            GC_COND_LOG_PRINTF(
+                "***>Full mark for collection #%lu after %lu allocd bytes\n",
+                (unsigned long)GC_gc_no + 1, (unsigned long)GC_bytes_allocd);
+            GC_promote_black_lists();
+            (void)GC_reclaim_all((GC_stop_func)0, TRUE);
+            GC_notify_full_gc();
+            GC_clear_marks();
+            n_partial_gcs = 0;
+            GC_is_full_gc = TRUE;
+          } else {
+            n_partial_gcs++;
+          }
+        }
+        /* We try to mark with the world stopped.       */
+        /* If we run out of time, this turns into       */
+        /* incremental marking.                 */
+#       ifndef NO_CLOCK
+          if (GC_time_limit != GC_TIME_UNLIMITED) { GET_TIME(GC_start_time); }
+#       endif
+        /* FIXME: If possible, GC_default_stop_func should be   */
+        /* used instead of GC_never_stop_func here.             */
+        if (GC_stopped_mark(GC_time_limit == GC_TIME_UNLIMITED?
+                            GC_never_stop_func : GC_timeout_stop_func)) {
+#           ifdef SAVE_CALL_CHAIN
+                GC_save_callers(GC_last_stack);
+#           endif
+            GC_finish_collection();
+        } else {
+            if (!GC_is_full_gc) {
+                /* Count this as the first attempt */
+                GC_n_attempts++;
+            }
+        }
+    }
+}
+
+
+/*
+ * Stop the world garbage collection.  Assumes lock held. If stop_func is
+ * not GC_never_stop_func then abort if stop_func returns TRUE.
+ * Return TRUE if we successfully completed the collection.
+ */
+GC_INNER GC_bool GC_try_to_collect_inner(GC_stop_func stop_func)
+{
+#   ifndef SMALL_CONFIG
+      CLOCK_TYPE start_time = 0; /* initialized to prevent warning. */
+      CLOCK_TYPE current_time;
+#   endif
+    ASSERT_CANCEL_DISABLED();
+    if (GC_dont_gc || (*stop_func)()) return FALSE;
+    if (GC_incremental && GC_collection_in_progress()) {
+      GC_COND_LOG_PRINTF(
+            "GC_try_to_collect_inner: finishing collection in progress\n");
+      /* Just finish collection already in progress.    */
+        while(GC_collection_in_progress()) {
+            if ((*stop_func)()) return(FALSE);
+            GC_collect_a_little_inner(1);
+        }
+    }
+    GC_notify_full_gc();
+#   ifndef SMALL_CONFIG
+      if (GC_print_stats) {
+        GET_TIME(start_time);
+        GC_log_printf("Initiating full world-stop collection!\n");
+      }
+#   endif
+    GC_promote_black_lists();
+    /* Make sure all blocks have been reclaimed, so sweep routines      */
+    /* don't see cleared mark bits.                                     */
+    /* If we're guaranteed to finish, then this is unnecessary.         */
+    /* In the find_leak case, we have to finish to guarantee that       */
+    /* previously unmarked objects are not reported as leaks.           */
+#       ifdef PARALLEL_MARK
+          if (GC_parallel)
+            GC_wait_for_reclaim();
+#       endif
+        if ((GC_find_leak || stop_func != GC_never_stop_func)
+            && !GC_reclaim_all(stop_func, FALSE)) {
+            /* Aborted.  So far everything is still consistent. */
+            return(FALSE);
+        }
+    GC_invalidate_mark_state();  /* Flush mark stack.   */
+    GC_clear_marks();
+#   ifdef SAVE_CALL_CHAIN
+        GC_save_callers(GC_last_stack);
+#   endif
+    GC_is_full_gc = TRUE;
+    if (!GC_stopped_mark(stop_func)) {
+      if (!GC_incremental) {
+        /* We're partially done and have no way to complete or use      */
+        /* current work.  Reestablish invariants as cheaply as          */
+        /* possible.                                                    */
+        GC_invalidate_mark_state();
+        GC_unpromote_black_lists();
+      } /* else we claim the world is already still consistent.  We'll  */
+        /* finish incrementally.                                        */
+      return(FALSE);
+    }
+    GC_finish_collection();
+#   ifndef SMALL_CONFIG
+      if (GC_print_stats) {
+        GET_TIME(current_time);
+        GC_log_printf("Complete collection took %lu msecs\n",
+                      MS_TIME_DIFF(current_time,start_time));
+      }
+#   endif
+    return(TRUE);
+}
+
+/*
+ * Perform n units of garbage collection work.  A unit is intended to touch
+ * roughly GC_RATE pages.  Every once in a while, we do more than that.
+ * This needs to be a fairly large number with our current incremental
+ * GC strategy, since otherwise we allocate too much during GC, and the
+ * cleanup gets expensive.
+ */
+#ifndef GC_RATE
+# define GC_RATE 10
+#endif
+#ifndef MAX_PRIOR_ATTEMPTS
+# define MAX_PRIOR_ATTEMPTS 1
+#endif
+        /* Maximum number of prior attempts at world stop marking       */
+        /* A value of 1 means that we finish the second time, no matter */
+        /* how long it takes.  Doesn't count the initial root scan      */
+        /* for a full GC.                                               */
+
+STATIC int GC_deficit = 0;/* The number of extra calls to GC_mark_some  */
+                          /* that we have made.                         */
+
+GC_INNER void GC_collect_a_little_inner(int n)
+{
+    int i;
+    IF_CANCEL(int cancel_state;)
+
+    if (GC_dont_gc) return;
+    DISABLE_CANCEL(cancel_state);
+    if (GC_incremental && GC_collection_in_progress()) {
+        for (i = GC_deficit; i < GC_RATE*n; i++) {
+            if (GC_mark_some((ptr_t)0)) {
+                /* Need to finish a collection */
+#               ifdef SAVE_CALL_CHAIN
+                    GC_save_callers(GC_last_stack);
+#               endif
+#               ifdef PARALLEL_MARK
+                    if (GC_parallel)
+                      GC_wait_for_reclaim();
+#               endif
+                if (GC_n_attempts < MAX_PRIOR_ATTEMPTS
+                    && GC_time_limit != GC_TIME_UNLIMITED) {
+#                 ifndef NO_CLOCK
+                    GET_TIME(GC_start_time);
+#                 endif
+                  if (!GC_stopped_mark(GC_timeout_stop_func)) {
+                    GC_n_attempts++;
+                    break;
+                  }
+                } else {
+                  /* FIXME: If possible, GC_default_stop_func should be */
+                  /* used here.                                         */
+                  (void)GC_stopped_mark(GC_never_stop_func);
+                }
+                GC_finish_collection();
+                break;
+            }
+        }
+        if (GC_deficit > 0) GC_deficit -= GC_RATE*n;
+        if (GC_deficit < 0) GC_deficit = 0;
+    } else {
+        GC_maybe_gc();
+    }
+    RESTORE_CANCEL(cancel_state);
+}
+
+GC_INNER void (*GC_check_heap)(void) = 0;
+GC_INNER void (*GC_print_all_smashed)(void) = 0;
+
+GC_API int GC_CALL GC_collect_a_little(void)
+{
+    int result;
+    DCL_LOCK_STATE;
+
+    LOCK();
+    GC_collect_a_little_inner(1);
+    result = (int)GC_collection_in_progress();
+    UNLOCK();
+    if (!result && GC_debugging_started) GC_print_all_smashed();
+    return(result);
+}
+
+#ifndef SMALL_CONFIG
+  /* Variables for world-stop average delay time statistic computation. */
+  /* "divisor" is incremented every world-stop and halved when reached  */
+  /* its maximum (or upon "total_time" overflow).                       */
+  static unsigned world_stopped_total_time = 0;
+  static unsigned world_stopped_total_divisor = 0;
+# ifndef MAX_TOTAL_TIME_DIVISOR
+    /* We shall not use big values here (so "outdated" delay time       */
+    /* values would have less impact on "average" delay time value than */
+    /* newer ones).                                                     */
+#   define MAX_TOTAL_TIME_DIVISOR 1000
+# endif
+#endif
+
+#ifdef USE_MUNMAP
+# define IF_USE_MUNMAP(x) x
+# define COMMA_IF_USE_MUNMAP(x) /* comma */, x
+#else
+# define IF_USE_MUNMAP(x) /* empty */
+# define COMMA_IF_USE_MUNMAP(x) /* empty */
+#endif
+
+/*
+ * Assumes lock is held.  We stop the world and mark from all roots.
+ * If stop_func() ever returns TRUE, we may fail and return FALSE.
+ * Increment GC_gc_no if we succeed.
+ */
+STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func)
+{
+    unsigned i;
+#   ifndef SMALL_CONFIG
+      CLOCK_TYPE start_time = 0; /* initialized to prevent warning. */
+      CLOCK_TYPE current_time;
+#   endif
+
+#   if !defined(REDIRECT_MALLOC) && defined(USE_WINALLOC)
+        GC_add_current_malloc_heap();
+#   endif
+#   if defined(REGISTER_LIBRARIES_EARLY)
+        GC_cond_register_dynamic_libraries();
+#   endif
+
+#   ifndef SMALL_CONFIG
+      if (GC_PRINT_STATS_FLAG)
+        GET_TIME(start_time);
+#   endif
+
+    STOP_WORLD();
+#   ifdef THREAD_LOCAL_ALLOC
+      GC_world_stopped = TRUE;
+#   endif
+        /* Output blank line for convenience here */
+    GC_COND_LOG_PRINTF(
+              "\n--> Marking for collection #%lu after %lu allocated bytes\n",
+              (unsigned long)GC_gc_no + 1, (unsigned long) GC_bytes_allocd);
+#   ifdef MAKE_BACK_GRAPH
+      if (GC_print_back_height) {
+        GC_build_back_graph();
+      }
+#   endif
+
+    /* Mark from all roots.  */
+        /* Minimize junk left in my registers and on the stack */
+            GC_clear_a_few_frames();
+            GC_noop6(0,0,0,0,0,0);
+
+        GC_initiate_gc();
+        for (i = 0;;i++) {
+          if ((*stop_func)()) {
+            GC_COND_LOG_PRINTF("Abandoned stopped marking after"
+                               " %u iterations\n", i);
+            GC_deficit = i;     /* Give the mutator a chance.   */
+#           ifdef THREAD_LOCAL_ALLOC
+              GC_world_stopped = FALSE;
+#           endif
+            START_WORLD();
+            return(FALSE);
+          }
+          if (GC_mark_some(GC_approx_sp())) break;
+        }
+
+    GC_gc_no++;
+    GC_DBGLOG_PRINTF("GC #%lu freed %ld bytes, heap %lu KiB"
+                     IF_USE_MUNMAP(" (+ %lu KiB unmapped)") "\n",
+                     (unsigned long)GC_gc_no, (long)GC_bytes_found,
+                     TO_KiB_UL(GC_heapsize - GC_unmapped_bytes) /*, */
+                     COMMA_IF_USE_MUNMAP(TO_KiB_UL(GC_unmapped_bytes)));
+
+    /* Check all debugged objects for consistency */
+    if (GC_debugging_started) {
+      (*GC_check_heap)();
+    }
+
+#   ifdef THREAD_LOCAL_ALLOC
+      GC_world_stopped = FALSE;
+#   endif
+    START_WORLD();
+#   ifndef SMALL_CONFIG
+      if (GC_PRINT_STATS_FLAG) {
+        unsigned long time_diff;
+        unsigned total_time, divisor;
+        GET_TIME(current_time);
+        time_diff = MS_TIME_DIFF(current_time,start_time);
+
+        /* Compute new world-stop delay total time */
+        total_time = world_stopped_total_time;
+        divisor = world_stopped_total_divisor;
+        if ((int)total_time < 0 || divisor >= MAX_TOTAL_TIME_DIVISOR) {
+          /* Halve values if overflow occurs */
+          total_time >>= 1;
+          divisor >>= 1;
+        }
+        total_time += time_diff < (((unsigned)-1) >> 1) ?
+                        (unsigned)time_diff : ((unsigned)-1) >> 1;
+        /* Update old world_stopped_total_time and its divisor */
+        world_stopped_total_time = total_time;
+        world_stopped_total_divisor = ++divisor;
+
+        GC_ASSERT(divisor != 0);
+        GC_log_printf(
+                "World-stopped marking took %lu msecs (%u in average)\n",
+                time_diff, total_time / divisor);
+      }
+#   endif
+    return(TRUE);
+}
+
+/* Set all mark bits for the free list whose first entry is q   */
+GC_INNER void GC_set_fl_marks(ptr_t q)
+{
+   struct hblk *h, *last_h;
+   hdr *hhdr;
+   IF_PER_OBJ(size_t sz;)
+   unsigned bit_no;
+
+   if (q != NULL) {
+     h = HBLKPTR(q);
+     last_h = h;
+     hhdr = HDR(h);
+     IF_PER_OBJ(sz = hhdr->hb_sz;)
+
+     for (;;) {
+        bit_no = MARK_BIT_NO((ptr_t)q - (ptr_t)h, sz);
+        if (!mark_bit_from_hdr(hhdr, bit_no)) {
+          set_mark_bit_from_hdr(hhdr, bit_no);
+          ++hhdr -> hb_n_marks;
+        }
+
+        q = obj_link(q);
+        if (q == NULL)
+          break;
+
+        h = HBLKPTR(q);
+        if (h != last_h) {
+          last_h = h;
+          hhdr = HDR(h);
+          IF_PER_OBJ(sz = hhdr->hb_sz;)
+        }
+     }
+   }
+}
+
+#if defined(GC_ASSERTIONS) && defined(THREADS) && defined(THREAD_LOCAL_ALLOC)
+  /* Check that all mark bits for the free list whose first entry is    */
+  /* (*pfreelist) are set.  Check skipped if points to a special value. */
+  void GC_check_fl_marks(void **pfreelist)
+  {
+#   ifdef AO_HAVE_load_acquire_read
+      AO_t *list = (AO_t *)AO_load_acquire_read((AO_t *)pfreelist);
+                /* Atomic operations are used because the world is running. */
+      AO_t *prev;
+      AO_t *p;
+
+      if ((word)list <= HBLKSIZE) return;
+
+      prev = (AO_t *)pfreelist;
+      for (p = list; p != NULL;) {
+        AO_t *next;
+
+        if (!GC_is_marked(p)) {
+          ABORT_ARG2("Unmarked local free list entry",
+                     ": object %p on list %p", (void *)p, (void *)list);
+        }
+
+        /* While traversing the free-list, it re-reads the pointer to   */
+        /* the current node before accepting its next pointer and       */
+        /* bails out if the latter has changed.  That way, it won't     */
+        /* try to follow the pointer which might be been modified       */
+        /* after the object was returned to the client.  It might       */
+        /* perform the mark-check on the just allocated object but      */
+        /* that should be harmless.                                     */
+        next = (AO_t *)AO_load_acquire_read(p);
+        if (AO_load(prev) != (AO_t)p)
+          break;
+        prev = p;
+        p = next;
+      }
+#   else
+      /* FIXME: Not implemented (just skipped). */
+      (void)pfreelist;
+#   endif
+  }
+#endif /* GC_ASSERTIONS && THREAD_LOCAL_ALLOC */
+
+/* Clear all mark bits for the free list whose first entry is q */
+/* Decrement GC_bytes_found by number of bytes on free list.    */
+STATIC void GC_clear_fl_marks(ptr_t q)
+{
+   struct hblk *h, *last_h;
+   hdr *hhdr;
+   size_t sz;
+   unsigned bit_no;
+
+   if (q != NULL) {
+     h = HBLKPTR(q);
+     last_h = h;
+     hhdr = HDR(h);
+     sz = hhdr->hb_sz;  /* Normally set only once. */
+
+     for (;;) {
+        bit_no = MARK_BIT_NO((ptr_t)q - (ptr_t)h, sz);
+        if (mark_bit_from_hdr(hhdr, bit_no)) {
+          size_t n_marks = hhdr -> hb_n_marks - 1;
+          clear_mark_bit_from_hdr(hhdr, bit_no);
+#         ifdef PARALLEL_MARK
+            /* Appr. count, don't decrement to zero! */
+            if (0 != n_marks || !GC_parallel) {
+              hhdr -> hb_n_marks = n_marks;
+            }
+#         else
+            hhdr -> hb_n_marks = n_marks;
+#         endif
+        }
+        GC_bytes_found -= sz;
+
+        q = obj_link(q);
+        if (q == NULL)
+          break;
+
+        h = HBLKPTR(q);
+        if (h != last_h) {
+          last_h = h;
+          hhdr = HDR(h);
+          sz = hhdr->hb_sz;
+        }
+     }
+   }
+}
+
+#if defined(GC_ASSERTIONS) && defined(THREADS) && defined(THREAD_LOCAL_ALLOC)
+  void GC_check_tls(void);
+#endif
+
+GC_on_heap_resize_proc GC_on_heap_resize = 0;
+
+/* Used for logging only. */
+GC_INLINE int GC_compute_heap_usage_percent(void)
+{
+  word used = GC_composite_in_use + GC_atomic_in_use;
+  word heap_sz = GC_heapsize - GC_unmapped_bytes;
+  return used >= heap_sz ? 0 : used < ((word)-1) / 100 ?
+                (int)((used * 100) / heap_sz) : (int)(used / (heap_sz / 100));
+}
+
+/* Finish up a collection.  Assumes mark bits are consistent, lock is   */
+/* held, but the world is otherwise running.                            */
+STATIC void GC_finish_collection(void)
+{
+#   ifndef SMALL_CONFIG
+      CLOCK_TYPE start_time = 0; /* initialized to prevent warning. */
+      CLOCK_TYPE finalize_time = 0;
+      CLOCK_TYPE done_time;
+#   endif
+
+#   if defined(GC_ASSERTIONS) && defined(THREADS) \
+       && defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
+        /* Check that we marked some of our own data.           */
+        /* FIXME: Add more checks.                              */
+        GC_check_tls();
+#   endif
+
+#   ifndef SMALL_CONFIG
+      if (GC_print_stats)
+        GET_TIME(start_time);
+#   endif
+
+#   ifndef GC_GET_HEAP_USAGE_NOT_NEEDED
+      if (GC_bytes_found > 0)
+        GC_reclaimed_bytes_before_gc += (word)GC_bytes_found;
+#   endif
+    GC_bytes_found = 0;
+#   if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG)
+        if (GETENV("GC_PRINT_ADDRESS_MAP") != 0) {
+          GC_print_address_map();
+        }
+#   endif
+    COND_DUMP;
+    if (GC_find_leak) {
+      /* Mark all objects on the free list.  All objects should be      */
+      /* marked when we're done.                                        */
+      word size;        /* current object size  */
+      unsigned kind;
+      ptr_t q;
+
+      for (kind = 0; kind < GC_n_kinds; kind++) {
+        for (size = 1; size <= MAXOBJGRANULES; size++) {
+          q = GC_obj_kinds[kind].ok_freelist[size];
+          if (q != 0) GC_set_fl_marks(q);
+        }
+      }
+      GC_start_reclaim(TRUE);
+        /* The above just checks; it doesn't really reclaim anything.   */
+    }
+
+#   ifndef GC_NO_FINALIZATION
+      GC_finalize();
+#   endif
+#   ifdef STUBBORN_ALLOC
+      GC_clean_changing_list();
+#   endif
+
+#   ifndef SMALL_CONFIG
+      if (GC_print_stats)
+        GET_TIME(finalize_time);
+#   endif
+
+    if (GC_print_back_height) {
+#     ifdef MAKE_BACK_GRAPH
+        GC_traverse_back_graph();
+#     elif !defined(SMALL_CONFIG)
+        GC_err_printf("Back height not available: "
+                      "Rebuild collector with -DMAKE_BACK_GRAPH\n");
+#     endif
+    }
+
+    /* Clear free list mark bits, in case they got accidentally marked   */
+    /* (or GC_find_leak is set and they were intentionally marked).      */
+    /* Also subtract memory remaining from GC_bytes_found count.         */
+    /* Note that composite objects on free list are cleared.             */
+    /* Thus accidentally marking a free list is not a problem;  only     */
+    /* objects on the list itself will be marked, and that's fixed here. */
+    {
+      word size;        /* current object size          */
+      ptr_t q;          /* pointer to current object    */
+      unsigned kind;
+
+      for (kind = 0; kind < GC_n_kinds; kind++) {
+        for (size = 1; size <= MAXOBJGRANULES; size++) {
+          q = GC_obj_kinds[kind].ok_freelist[size];
+          if (q != 0) GC_clear_fl_marks(q);
+        }
+      }
+    }
+
+    GC_VERBOSE_LOG_PRINTF("Bytes recovered before sweep - f.l. count = %ld\n",
+                          (long)GC_bytes_found);
+
+    /* Reconstruct free lists to contain everything not marked */
+    GC_start_reclaim(FALSE);
+    GC_DBGLOG_PRINTF("In-use heap: %d%% (%lu KiB pointers + %lu KiB other)\n",
+                     GC_compute_heap_usage_percent(),
+                     TO_KiB_UL(GC_composite_in_use),
+                     TO_KiB_UL(GC_atomic_in_use));
+    if (GC_is_full_gc) {
+        GC_used_heap_size_after_full = USED_HEAP_SIZE;
+        GC_need_full_gc = FALSE;
+    } else {
+        GC_need_full_gc = USED_HEAP_SIZE - GC_used_heap_size_after_full
+                            > min_bytes_allocd();
+    }
+
+    GC_VERBOSE_LOG_PRINTF("Immediately reclaimed %ld bytes, heapsize:"
+                          " %lu bytes" IF_USE_MUNMAP(" (%lu unmapped)") "\n",
+                          (long)GC_bytes_found,
+                          (unsigned long)GC_heapsize /*, */
+                          COMMA_IF_USE_MUNMAP((unsigned long)
+                                              GC_unmapped_bytes));
+
+    /* Reset or increment counters for next cycle */
+    GC_n_attempts = 0;
+    GC_is_full_gc = FALSE;
+    GC_bytes_allocd_before_gc += GC_bytes_allocd;
+    GC_non_gc_bytes_at_gc = GC_non_gc_bytes;
+    GC_bytes_allocd = 0;
+    GC_bytes_dropped = 0;
+    GC_bytes_freed = 0;
+    GC_finalizer_bytes_freed = 0;
+
+    IF_USE_MUNMAP(GC_unmap_old());
+
+#   ifndef SMALL_CONFIG
+      if (GC_print_stats) {
+        GET_TIME(done_time);
+#       ifndef GC_NO_FINALIZATION
+          /* A convenient place to output finalization statistics.      */
+          GC_print_finalization_stats();
+#       endif
+        GC_log_printf("Finalize plus initiate sweep took %lu + %lu msecs\n",
+                      MS_TIME_DIFF(finalize_time,start_time),
+                      MS_TIME_DIFF(done_time,finalize_time));
+      }
+#   endif
+}
+
+/* If stop_func == 0 then GC_default_stop_func is used instead.         */
+STATIC GC_bool GC_try_to_collect_general(GC_stop_func stop_func,
+                                         GC_bool force_unmap GC_ATTR_UNUSED)
+{
+    GC_bool result;
+    IF_USE_MUNMAP(int old_unmap_threshold;)
+    IF_CANCEL(int cancel_state;)
+    DCL_LOCK_STATE;
+
+    if (!EXPECT(GC_is_initialized, TRUE)) GC_init();
+    if (GC_debugging_started) GC_print_all_smashed();
+    GC_INVOKE_FINALIZERS();
+    LOCK();
+    DISABLE_CANCEL(cancel_state);
+#   ifdef USE_MUNMAP
+      old_unmap_threshold = GC_unmap_threshold;
+      if (force_unmap ||
+          (GC_force_unmap_on_gcollect && old_unmap_threshold > 0))
+        GC_unmap_threshold = 1; /* unmap as much as possible */
+#   endif
+    ENTER_GC();
+    /* Minimize junk left in my registers */
+      GC_noop6(0,0,0,0,0,0);
+    result = GC_try_to_collect_inner(stop_func != 0 ? stop_func :
+                                     GC_default_stop_func);
+    EXIT_GC();
+    IF_USE_MUNMAP(GC_unmap_threshold = old_unmap_threshold); /* restore */
+    RESTORE_CANCEL(cancel_state);
+    UNLOCK();
+    if (result) {
+        if (GC_debugging_started) GC_print_all_smashed();
+        GC_INVOKE_FINALIZERS();
+    }
+    return(result);
+}
+
+/* Externally callable routines to invoke full, stop-the-world collection. */
+GC_API int GC_CALL GC_try_to_collect(GC_stop_func stop_func)
+{
+    GC_ASSERT(stop_func != 0);
+    return (int)GC_try_to_collect_general(stop_func, FALSE);
+}
+
+GC_API void GC_CALL GC_gcollect(void)
+{
+    /* 0 is passed as stop_func to get GC_default_stop_func value       */
+    /* while holding the allocation lock (to prevent data races).       */
+    (void)GC_try_to_collect_general(0, FALSE);
+    if (GC_have_errors) GC_print_all_errors();
+}
+
+GC_API void GC_CALL GC_gcollect_and_unmap(void)
+{
+    (void)GC_try_to_collect_general(GC_never_stop_func, TRUE);
+}
+
+GC_INNER word GC_n_heap_sects = 0;
+                        /* Number of sections currently in heap. */
+
+#ifdef USE_PROC_FOR_LIBRARIES
+  GC_INNER word GC_n_memory = 0;
+                        /* Number of GET_MEM allocated memory sections. */
+#endif
+
+#ifdef USE_PROC_FOR_LIBRARIES
+  /* Add HBLKSIZE aligned, GET_MEM-generated block to GC_our_memory. */
+  /* Defined to do nothing if USE_PROC_FOR_LIBRARIES not set.       */
+  GC_INNER void GC_add_to_our_memory(ptr_t p, size_t bytes)
+  {
+    if (0 == p) return;
+    if (GC_n_memory >= MAX_HEAP_SECTS)
+      ABORT("Too many GC-allocated memory sections: Increase MAX_HEAP_SECTS");
+    GC_our_memory[GC_n_memory].hs_start = p;
+    GC_our_memory[GC_n_memory].hs_bytes = bytes;
+    GC_n_memory++;
+  }
+#endif
+
+/*
+ * Use the chunk of memory starting at p of size bytes as part of the heap.
+ * Assumes p is HBLKSIZE aligned, and bytes is a multiple of HBLKSIZE.
+ */
+GC_INNER void GC_add_to_heap(struct hblk *p, size_t bytes)
+{
+    hdr * phdr;
+    word endp;
+
+    if (GC_n_heap_sects >= MAX_HEAP_SECTS) {
+        ABORT("Too many heap sections: Increase MAXHINCR or MAX_HEAP_SECTS");
+    }
+    while ((word)p <= HBLKSIZE) {
+        /* Can't handle memory near address zero. */
+        ++p;
+        bytes -= HBLKSIZE;
+        if (0 == bytes) return;
+    }
+    endp = (word)p + bytes;
+    if (endp <= (word)p) {
+        /* Address wrapped. */
+        bytes -= HBLKSIZE;
+        if (0 == bytes) return;
+        endp -= HBLKSIZE;
+    }
+    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.                               */
+        return;
+    }
+    GC_ASSERT(endp > (word)p && endp == (word)p + bytes);
+    GC_heap_sects[GC_n_heap_sects].hs_start = (ptr_t)p;
+    GC_heap_sects[GC_n_heap_sects].hs_bytes = bytes;
+    GC_n_heap_sects++;
+    phdr -> hb_sz = bytes;
+    phdr -> hb_flags = 0;
+    GC_freehblk(p);
+    GC_heapsize += bytes;
+    if ((word)p <= (word)GC_least_plausible_heap_addr
+        || GC_least_plausible_heap_addr == 0) {
+        GC_least_plausible_heap_addr = (void *)((ptr_t)p - sizeof(word));
+                /* Making it a little smaller than necessary prevents   */
+                /* us from getting a false hit from the variable        */
+                /* itself.  There's some unintentional reflection       */
+                /* here.                                                */
+    }
+    if ((word)p + bytes >= (word)GC_greatest_plausible_heap_addr) {
+        GC_greatest_plausible_heap_addr = (void *)endp;
+    }
+}
+
+#if !defined(NO_DEBUGGING)
+  void GC_print_heap_sects(void)
+  {
+    unsigned i;
+
+    GC_printf("Total heap size: %lu" IF_USE_MUNMAP(" (%lu unmapped)") "\n",
+              (unsigned long)GC_heapsize /*, */
+              COMMA_IF_USE_MUNMAP((unsigned long)GC_unmapped_bytes));
+
+    for (i = 0; i < GC_n_heap_sects; i++) {
+      ptr_t start = GC_heap_sects[i].hs_start;
+      size_t len = GC_heap_sects[i].hs_bytes;
+      struct hblk *h;
+      unsigned nbl = 0;
+
+      for (h = (struct hblk *)start; (word)h < (word)(start + len); h++) {
+        if (GC_is_black_listed(h, HBLKSIZE)) nbl++;
+      }
+      GC_printf("Section %d from %p to %p %lu/%lu blacklisted\n",
+                i, start, start + len,
+                (unsigned long)nbl, (unsigned long)(len/HBLKSIZE));
+    }
+  }
+#endif
+
+void * GC_least_plausible_heap_addr = (void *)ONES;
+void * GC_greatest_plausible_heap_addr = 0;
+
+GC_INLINE word GC_max(word x, word y)
+{
+    return(x > y? x : y);
+}
+
+GC_INLINE word GC_min(word x, word y)
+{
+    return(x < y? x : y);
+}
+
+STATIC word GC_max_heapsize = 0;
+
+GC_API void GC_CALL GC_set_max_heap_size(GC_word n)
+{
+    GC_max_heapsize = n;
+}
+
+GC_word GC_max_retries = 0;
+
+/* This explicitly increases the size of the heap.  It is used          */
+/* internally, but may also be invoked from GC_expand_hp by the user.   */
+/* The argument is in units of HBLKSIZE (tiny values are rounded up).   */
+/* Returns FALSE on failure.                                            */
+GC_INNER GC_bool GC_expand_hp_inner(word n)
+{
+    word bytes;
+    struct hblk * space;
+    word expansion_slop;        /* Number of bytes by which we expect the */
+                                /* heap to expand soon.                   */
+
+    if (n < MINHINCR) n = MINHINCR;
+    bytes = n * HBLKSIZE;
+    /* Make sure bytes is a multiple of GC_page_size */
+      {
+        word mask = GC_page_size - 1;
+        bytes += mask;
+        bytes &= ~mask;
+      }
+
+    if (GC_max_heapsize != 0 && GC_heapsize + bytes > GC_max_heapsize) {
+        /* Exceeded self-imposed limit */
+        return(FALSE);
+    }
+    space = GET_MEM(bytes);
+    GC_add_to_our_memory((ptr_t)space, bytes);
+    if (space == 0) {
+        WARN("Failed to expand heap by %" WARN_PRIdPTR " bytes\n", bytes);
+        return(FALSE);
+    }
+    GC_INFOLOG_PRINTF("Grow heap to %lu KiB after %lu bytes allocated\n",
+                      TO_KiB_UL(GC_heapsize + bytes),
+                      (unsigned long)GC_bytes_allocd);
+    /* Adjust heap limits generously for blacklisting to work better.   */
+    /* GC_add_to_heap performs minimal adjustment needed for            */
+    /* correctness.                                                     */
+    expansion_slop = min_bytes_allocd() + 4*MAXHINCR*HBLKSIZE;
+    if ((GC_last_heap_addr == 0 && !((word)space & SIGNB))
+        || (GC_last_heap_addr != 0
+            && (word)GC_last_heap_addr < (word)space)) {
+        /* Assume the heap is growing up */
+        word new_limit = (word)space + bytes + expansion_slop;
+        if (new_limit > (word)space) {
+          GC_greatest_plausible_heap_addr =
+            (void *)GC_max((word)GC_greatest_plausible_heap_addr,
+                           (word)new_limit);
+        }
+    } else {
+        /* Heap is growing down */
+        word new_limit = (word)space - expansion_slop;
+        if (new_limit < (word)space) {
+          GC_least_plausible_heap_addr =
+            (void *)GC_min((word)GC_least_plausible_heap_addr,
+                           (word)space - expansion_slop);
+        }
+    }
+    GC_prev_heap_addr = GC_last_heap_addr;
+    GC_last_heap_addr = (ptr_t)space;
+    GC_add_to_heap(space, bytes);
+    /* Force GC before we are likely to allocate past expansion_slop */
+      GC_collect_at_heapsize =
+         GC_heapsize + expansion_slop - 2*MAXHINCR*HBLKSIZE;
+      if (GC_collect_at_heapsize < GC_heapsize /* wrapped */)
+         GC_collect_at_heapsize = (word)(-1);
+    if (GC_on_heap_resize)
+      (*GC_on_heap_resize)(GC_heapsize);
+
+    return(TRUE);
+}
+
+/* Really returns a bool, but it's externally visible, so that's clumsy. */
+/* Arguments is in bytes.  Includes GC_init() call.                      */
+GC_API int GC_CALL GC_expand_hp(size_t bytes)
+{
+    int result;
+    DCL_LOCK_STATE;
+
+    LOCK();
+    if (!EXPECT(GC_is_initialized, TRUE)) GC_init();
+    result = (int)GC_expand_hp_inner(divHBLKSZ((word)bytes));
+    if (result) GC_requested_heapsize += bytes;
+    UNLOCK();
+    return(result);
+}
+
+word GC_fo_entries = 0; /* used also in extra/MacOS.c */
+
+GC_INNER unsigned GC_fail_count = 0;
+                        /* How many consecutive GC/expansion failures?  */
+                        /* Reset by GC_allochblk.                       */
+
+static word last_fo_entries = 0;
+static word last_bytes_finalized = 0;
+
+/* Collect or expand heap in an attempt make the indicated number of    */
+/* free blocks available.  Should be called until the blocks are        */
+/* available (seting retry value to TRUE unless this is the first call  */
+/* in a loop) or until it fails by returning FALSE.                     */
+GC_INNER GC_bool GC_collect_or_expand(word needed_blocks,
+                                      GC_bool ignore_off_page,
+                                      GC_bool retry)
+{
+    GC_bool gc_not_stopped = TRUE;
+    word blocks_to_get;
+    IF_CANCEL(int cancel_state;)
+
+    DISABLE_CANCEL(cancel_state);
+    if (!GC_incremental && !GC_dont_gc &&
+        ((GC_dont_expand && GC_bytes_allocd > 0)
+         || (GC_fo_entries > (last_fo_entries + 500)
+             && (last_bytes_finalized | GC_bytes_finalized) != 0)
+         || GC_should_collect())) {
+      /* Try to do a full collection using 'default' stop_func (unless  */
+      /* nothing has been allocated since the latest collection or heap */
+      /* expansion is disabled).                                        */
+      gc_not_stopped = GC_try_to_collect_inner(
+                        GC_bytes_allocd > 0 && (!GC_dont_expand || !retry) ?
+                        GC_default_stop_func : GC_never_stop_func);
+      if (gc_not_stopped == TRUE || !retry) {
+        /* Either the collection hasn't been aborted or this is the     */
+        /* first attempt (in a loop).                                   */
+        last_fo_entries = GC_fo_entries;
+        last_bytes_finalized = GC_bytes_finalized;
+        RESTORE_CANCEL(cancel_state);
+        return(TRUE);
+      }
+    }
+
+    blocks_to_get = GC_heapsize/(HBLKSIZE*GC_free_space_divisor)
+                        + needed_blocks;
+    if (blocks_to_get > MAXHINCR) {
+      word slop;
+
+      /* Get the minimum required to make it likely that we can satisfy */
+      /* the current request in the presence of black-listing.          */
+      /* This will probably be more than MAXHINCR.                      */
+      if (ignore_off_page) {
+        slop = 4;
+      } else {
+        slop = 2 * divHBLKSZ(BL_LIMIT);
+        if (slop > needed_blocks) slop = needed_blocks;
+      }
+      if (needed_blocks + slop > MAXHINCR) {
+        blocks_to_get = needed_blocks + slop;
+      } else {
+        blocks_to_get = MAXHINCR;
+      }
+    }
+
+    if (!GC_expand_hp_inner(blocks_to_get)
+        && !GC_expand_hp_inner(needed_blocks)) {
+      if (gc_not_stopped == FALSE) {
+        /* Don't increment GC_fail_count here (and no warning).     */
+        GC_gcollect_inner();
+        GC_ASSERT(GC_bytes_allocd == 0);
+      } else if (GC_fail_count++ < GC_max_retries) {
+        WARN("Out of Memory!  Trying to continue ...\n", 0);
+        GC_gcollect_inner();
+      } else {
+#       if !defined(AMIGA) || !defined(GC_AMIGA_FASTALLOC)
+          WARN("Out of Memory! Heap size: %" WARN_PRIdPTR " MiB."
+               " Returning NULL!\n", (GC_heapsize - GC_unmapped_bytes) >> 20);
+#       endif
+        RESTORE_CANCEL(cancel_state);
+        return(FALSE);
+      }
+    } else if (GC_fail_count) {
+      GC_COND_LOG_PRINTF("Memory available again...\n");
+    }
+    RESTORE_CANCEL(cancel_state);
+    return(TRUE);
+}
+
+/*
+ * Make sure the object free list for size gran (in granules) is not empty.
+ * Return a pointer to the first object on the free list.
+ * The object MUST BE REMOVED FROM THE FREE LIST BY THE CALLER.
+ * Assumes we hold the allocator lock.
+ */
+GC_INNER ptr_t GC_allocobj(size_t gran, int kind)
+{
+    void ** flh = &(GC_obj_kinds[kind].ok_freelist[gran]);
+    GC_bool tried_minor = FALSE;
+    GC_bool retry = FALSE;
+
+    if (gran == 0) return(0);
+
+    while (*flh == 0) {
+      ENTER_GC();
+      /* Do our share of marking work */
+        if(TRUE_INCREMENTAL) GC_collect_a_little_inner(1);
+      /* Sweep blocks for objects of this size */
+        GC_continue_reclaim(gran, kind);
+      EXIT_GC();
+      if (*flh == 0) {
+        GC_new_hblk(gran, kind);
+      }
+      if (*flh == 0) {
+        ENTER_GC();
+        if (GC_incremental && GC_time_limit == GC_TIME_UNLIMITED
+            && !tried_minor) {
+          GC_collect_a_little_inner(1);
+          tried_minor = TRUE;
+        } else {
+          if (!GC_collect_or_expand(1, FALSE, retry)) {
+            EXIT_GC();
+            return(0);
+          }
+          retry = TRUE;
+        }
+        EXIT_GC();
+      }
+    }
+    /* Successful allocation; reset failure count.      */
+    GC_fail_count = 0;
+
+    return(*flh);
+}

+ 475 - 0
blitz.mod/bdwgc/backgraph.c

@@ -0,0 +1,475 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ *
+ */
+
+#include "private/dbg_mlc.h"
+
+/*
+ * This implements a full, though not well-tuned, representation of the
+ * backwards points-to graph.  This is used to test for non-GC-robust
+ * data structures; the code is not used during normal garbage collection.
+ *
+ * One restriction is that we drop all back-edges from nodes with very
+ * high in-degree, and simply add them add them to a list of such
+ * nodes.  They are then treated as permanent roots.  Id this by itself
+ * doesn't introduce a space leak, then such nodes can't contribute to
+ * a growing space leak.
+ */
+
+#ifdef MAKE_BACK_GRAPH
+
+#define MAX_IN  10      /* Maximum in-degree we handle directly */
+
+/* #include <unistd.h> */
+
+#if !defined(DBG_HDRS_ALL) || (ALIGNMENT != CPP_WORDSZ/8) /* || !defined(UNIX_LIKE) */
+# error The configuration does not support MAKE_BACK_GRAPH
+#endif
+
+/* We store single back pointers directly in the object's oh_bg_ptr field. */
+/* If there is more than one ptr to an object, we store q | FLAG_MANY,     */
+/* where q is a pointer to a back_edges object.                            */
+/* Every once in a while we use a back_edges object even for a single      */
+/* pointer, since we need the other fields in the back_edges structure to  */
+/* be present in some fraction of the objects.  Otherwise we get serious   */
+/* performance issues.                                                     */
+#define FLAG_MANY 2
+
+typedef struct back_edges_struct {
+  word n_edges; /* Number of edges, including those in continuation     */
+                /* structures.                                          */
+  unsigned short flags;
+#       define RETAIN 1 /* Directly points to a reachable object;       */
+                        /* retain for next GC.                          */
+  unsigned short height_gc_no;
+                /* If height > 0, then the GC_gc_no value when it       */
+                /* was computed.  If it was computed this cycle, then   */
+                /* it is current.  If it was computed during the        */
+                /* last cycle, then it represents the old height,       */
+                /* which is only saved for live objects referenced by   */
+                /* dead ones.  This may grow due to refs from newly     */
+                /* dead objects.                                        */
+  signed_word height;
+                /* Longest path through unreachable nodes to this node  */
+                /* that we found using depth first search.              */
+
+#   define HEIGHT_UNKNOWN ((signed_word)(-2))
+#   define HEIGHT_IN_PROGRESS ((signed_word)(-1))
+  ptr_t edges[MAX_IN];
+  struct back_edges_struct *cont;
+                /* Pointer to continuation structure; we use only the   */
+                /* edges field in the continuation.                     */
+                /* also used as free list link.                         */
+} back_edges;
+
+/* Allocate a new back edge structure.  Should be more sophisticated    */
+/* if this were production code.                                        */
+#define MAX_BACK_EDGE_STRUCTS 100000
+static back_edges *back_edge_space = 0;
+STATIC int GC_n_back_edge_structs = 0;
+                                /* Serves as pointer to never used      */
+                                /* back_edges space.                    */
+static back_edges *avail_back_edges = 0;
+                                /* Pointer to free list of deallocated  */
+                                /* back_edges structures.               */
+
+static back_edges * new_back_edges(void)
+{
+  if (0 == back_edge_space) {
+    back_edge_space = (back_edges *)
+                        GET_MEM(MAX_BACK_EDGE_STRUCTS*sizeof(back_edges));
+    GC_add_to_our_memory((ptr_t)back_edge_space,
+                         MAX_BACK_EDGE_STRUCTS*sizeof(back_edges));
+  }
+  if (0 != avail_back_edges) {
+    back_edges * result = avail_back_edges;
+    avail_back_edges = result -> cont;
+    result -> cont = 0;
+    return result;
+  }
+  if (GC_n_back_edge_structs >= MAX_BACK_EDGE_STRUCTS - 1) {
+    ABORT("Needed too much space for back edges: adjust "
+          "MAX_BACK_EDGE_STRUCTS");
+  }
+  return back_edge_space + (GC_n_back_edge_structs++);
+}
+
+/* Deallocate p and its associated continuation structures.     */
+static void deallocate_back_edges(back_edges *p)
+{
+   back_edges *last = p;
+
+   while (0 != last -> cont) last = last -> cont;
+   last -> cont = avail_back_edges;
+   avail_back_edges = p;
+}
+
+/* Table of objects that are currently on the depth-first search        */
+/* stack.  Only objects with in-degree one are in this table.           */
+/* Other objects are identified using HEIGHT_IN_PROGRESS.               */
+/* FIXME: This data structure NEEDS IMPROVEMENT.                        */
+#define INITIAL_IN_PROGRESS 10000
+static ptr_t * in_progress_space = 0;
+static size_t in_progress_size = 0;
+static size_t n_in_progress = 0;
+
+static void push_in_progress(ptr_t p)
+{
+  if (n_in_progress >= in_progress_size) {
+    if (in_progress_size == 0) {
+      in_progress_size = INITIAL_IN_PROGRESS;
+      in_progress_space = (ptr_t *)GET_MEM(in_progress_size * sizeof(ptr_t));
+      GC_add_to_our_memory((ptr_t)in_progress_space,
+                           in_progress_size * sizeof(ptr_t));
+    } else {
+      ptr_t * new_in_progress_space;
+      in_progress_size *= 2;
+      new_in_progress_space = (ptr_t *)
+                                GET_MEM(in_progress_size * sizeof(ptr_t));
+      GC_add_to_our_memory((ptr_t)new_in_progress_space,
+                           in_progress_size * sizeof(ptr_t));
+      BCOPY(in_progress_space, new_in_progress_space,
+            n_in_progress * sizeof(ptr_t));
+      in_progress_space = new_in_progress_space;
+      /* FIXME: This just drops the old space.  */
+    }
+  }
+  if (in_progress_space == 0)
+      ABORT("MAKE_BACK_GRAPH: Out of in-progress space: "
+            "Huge linear data structure?");
+  in_progress_space[n_in_progress++] = p;
+}
+
+static GC_bool is_in_progress(ptr_t p)
+{
+  size_t i;
+  for (i = 0; i < n_in_progress; ++i) {
+    if (in_progress_space[i] == p) return TRUE;
+  }
+  return FALSE;
+}
+
+GC_INLINE void pop_in_progress(ptr_t p GC_ATTR_UNUSED)
+{
+  --n_in_progress;
+  GC_ASSERT(in_progress_space[n_in_progress] == p);
+}
+
+#define GET_OH_BG_PTR(p) \
+                (ptr_t)GC_REVEAL_POINTER(((oh *)(p)) -> oh_bg_ptr)
+#define SET_OH_BG_PTR(p,q) (((oh *)(p)) -> oh_bg_ptr = GC_HIDE_POINTER(q))
+
+/* Execute s once for each predecessor q of p in the points-to graph.   */
+/* s should be a bracketed statement.  We declare q.                    */
+#define FOR_EACH_PRED(q, p, s) \
+  do { \
+    ptr_t q = GET_OH_BG_PTR(p); \
+    if (!((word)q & FLAG_MANY)) { \
+      if (q && !((word)q & 1)) s \
+              /* !((word)q & 1) checks for a misinterpreted freelist link */ \
+    } else { \
+      back_edges *orig_be_ = (back_edges *)((word)q & ~FLAG_MANY); \
+      back_edges *be_ = orig_be_; \
+      int local_; \
+      word total_; \
+      word n_edges_ = be_ -> n_edges; \
+      for (total_ = 0, local_ = 0; total_ < n_edges_; ++local_, ++total_) { \
+          if (local_ == MAX_IN) { \
+              be_ = be_ -> cont; \
+              local_ = 0; \
+          } \
+          q = be_ -> edges[local_]; s \
+      } \
+    } \
+  } while (0)
+
+/* Ensure that p has a back_edges structure associated with it. */
+static void ensure_struct(ptr_t p)
+{
+  ptr_t old_back_ptr = GET_OH_BG_PTR(p);
+
+  if (!((word)old_back_ptr & FLAG_MANY)) {
+    back_edges *be = new_back_edges();
+    be -> flags = 0;
+    if (0 == old_back_ptr) {
+      be -> n_edges = 0;
+    } else {
+      be -> n_edges = 1;
+      be -> edges[0] = old_back_ptr;
+    }
+    be -> height = HEIGHT_UNKNOWN;
+    be -> height_gc_no = (unsigned short)(GC_gc_no - 1);
+    GC_ASSERT((word)be >= (word)back_edge_space);
+    SET_OH_BG_PTR(p, (word)be | FLAG_MANY);
+  }
+}
+
+/* Add the (forward) edge from p to q to the backward graph.  Both p    */
+/* q are pointers to the object base, i.e. pointers to an oh.           */
+static void add_edge(ptr_t p, ptr_t q)
+{
+    ptr_t old_back_ptr = GET_OH_BG_PTR(q);
+    back_edges * be, *be_cont;
+    word i;
+    static unsigned random_number = 13;
+#   define GOT_LUCKY_NUMBER (((++random_number) & 0x7f) == 0)
+      /* A not very random number we use to occasionally allocate a     */
+      /* back_edges structure even for a single backward edge.  This    */
+      /* prevents us from repeatedly tracing back through very long     */
+      /* chains, since we will have some place to store height and      */
+      /* in_progress flags along the way.                               */
+
+    GC_ASSERT(p == GC_base(p) && q == GC_base(q));
+    if (!GC_HAS_DEBUG_INFO(q) || !GC_HAS_DEBUG_INFO(p)) {
+      /* This is really a misinterpreted free list link, since we saw   */
+      /* a pointer to a free list.  Don't overwrite it!                 */
+      return;
+    }
+    if (0 == old_back_ptr) {
+        SET_OH_BG_PTR(q, p);
+        if (GOT_LUCKY_NUMBER) ensure_struct(q);
+        return;
+    }
+    /* Check whether it was already in the list of predecessors. */
+      FOR_EACH_PRED(pred, q, { if (p == pred) return; });
+    ensure_struct(q);
+    old_back_ptr = GET_OH_BG_PTR(q);
+    be = (back_edges *)((word)old_back_ptr & ~FLAG_MANY);
+    for (i = be -> n_edges, be_cont = be; i > MAX_IN; i -= MAX_IN)
+        be_cont = be_cont -> cont;
+    if (i == MAX_IN) {
+        be_cont -> cont = new_back_edges();
+        be_cont = be_cont -> cont;
+        i = 0;
+    }
+    be_cont -> edges[i] = p;
+    be -> n_edges++;
+#   ifdef DEBUG_PRINT_BIG_N_EDGES
+      if (GC_print_stats == VERBOSE && be -> n_edges == 100) {
+        GC_err_printf("The following object has big in-degree:\n");
+        GC_print_heap_obj(q);
+      }
+#   endif
+}
+
+typedef void (*per_object_func)(ptr_t p, size_t n_bytes, word gc_descr);
+
+static void per_object_helper(struct hblk *h, word fn)
+{
+  hdr * hhdr = HDR(h);
+  size_t sz = hhdr -> hb_sz;
+  word descr = hhdr -> hb_descr;
+  per_object_func f = (per_object_func)fn;
+  int i = 0;
+
+  do {
+    f((ptr_t)(h -> hb_body + i), sz, descr);
+    i += (int)sz;
+  } while ((word)i + sz <= BYTES_TO_WORDS(HBLKSIZE));
+}
+
+GC_INLINE void GC_apply_to_each_object(per_object_func f)
+{
+  GC_apply_to_all_blocks(per_object_helper, (word)f);
+}
+
+static void reset_back_edge(ptr_t p, size_t n_bytes GC_ATTR_UNUSED,
+                            word gc_descr GC_ATTR_UNUSED)
+{
+  /* Skip any free list links, or dropped blocks */
+  if (GC_HAS_DEBUG_INFO(p)) {
+    ptr_t old_back_ptr = GET_OH_BG_PTR(p);
+    if ((word)old_back_ptr & FLAG_MANY) {
+      back_edges *be = (back_edges *)((word)old_back_ptr & ~FLAG_MANY);
+      if (!(be -> flags & RETAIN)) {
+        deallocate_back_edges(be);
+        SET_OH_BG_PTR(p, 0);
+      } else {
+
+        GC_ASSERT(GC_is_marked(p));
+
+        /* Back edges may point to objects that will not be retained.   */
+        /* Delete them for now, but remember the height.                */
+        /* Some will be added back at next GC.                          */
+          be -> n_edges = 0;
+          if (0 != be -> cont) {
+            deallocate_back_edges(be -> cont);
+            be -> cont = 0;
+          }
+
+        GC_ASSERT(GC_is_marked(p));
+
+        /* We only retain things for one GC cycle at a time.            */
+          be -> flags &= ~RETAIN;
+      }
+    } else /* Simple back pointer */ {
+      /* Clear to avoid dangling pointer. */
+      SET_OH_BG_PTR(p, 0);
+    }
+  }
+}
+
+static void add_back_edges(ptr_t p, size_t n_bytes, word gc_descr)
+{
+  word *currentp = (word *)(p + sizeof(oh));
+
+  /* For now, fix up non-length descriptors conservatively.     */
+    if((gc_descr & GC_DS_TAGS) != GC_DS_LENGTH) {
+      gc_descr = n_bytes;
+    }
+  while ((word)currentp < (word)(p + gc_descr)) {
+    word current = *currentp++;
+    FIXUP_POINTER(current);
+    if (current >= (word)GC_least_plausible_heap_addr &&
+        current <= (word)GC_greatest_plausible_heap_addr) {
+       ptr_t target = GC_base((void *)current);
+       if (0 != target) {
+         add_edge(p, target);
+       }
+    }
+  }
+}
+
+/* Rebuild the representation of the backward reachability graph.       */
+/* Does not examine mark bits.  Can be called before GC.                */
+GC_INNER void GC_build_back_graph(void)
+{
+  GC_apply_to_each_object(add_back_edges);
+}
+
+/* Return an approximation to the length of the longest simple path     */
+/* through unreachable objects to p.  We refer to this as the height    */
+/* of p.                                                                */
+static word backwards_height(ptr_t p)
+{
+  word result;
+  ptr_t back_ptr = GET_OH_BG_PTR(p);
+  back_edges *be;
+
+  if (0 == back_ptr) return 1;
+  if (!((word)back_ptr & FLAG_MANY)) {
+    if (is_in_progress(p)) return 0; /* DFS back edge, i.e. we followed */
+                                     /* an edge to an object already    */
+                                     /* on our stack: ignore            */
+    push_in_progress(p);
+    result = backwards_height(back_ptr)+1;
+    pop_in_progress(p);
+    return result;
+  }
+  be = (back_edges *)((word)back_ptr & ~FLAG_MANY);
+  if (be -> height >= 0 && be -> height_gc_no == (unsigned short)GC_gc_no)
+      return be -> height;
+  /* Ignore back edges in DFS */
+    if (be -> height == HEIGHT_IN_PROGRESS) return 0;
+  result = (be -> height > 0? be -> height : 1);
+  be -> height = HEIGHT_IN_PROGRESS;
+  FOR_EACH_PRED(q, p, {
+    word this_height;
+    if (GC_is_marked(q) && !(FLAG_MANY & (word)GET_OH_BG_PTR(p))) {
+      GC_COND_LOG_PRINTF("Found bogus pointer from %p to %p\n", q, p);
+        /* Reachable object "points to" unreachable one.                */
+        /* Could be caused by our lax treatment of GC descriptors.      */
+      this_height = 1;
+    } else {
+        this_height = backwards_height(q);
+    }
+    if (this_height >= result) result = this_height + 1;
+  });
+  be -> height = result;
+  be -> height_gc_no = (unsigned short)GC_gc_no;
+  return result;
+}
+
+STATIC word GC_max_height = 0;
+STATIC ptr_t GC_deepest_obj = NULL;
+
+/* Compute the maximum height of every unreachable predecessor p of a   */
+/* reachable object.  Arrange to save the heights of all such objects p */
+/* so that they can be used in calculating the height of objects in the */
+/* next GC.                                                             */
+/* Set GC_max_height to be the maximum height we encounter, and         */
+/* GC_deepest_obj to be the corresponding object.                       */
+static void update_max_height(ptr_t p, size_t n_bytes GC_ATTR_UNUSED,
+                              word gc_descr GC_ATTR_UNUSED)
+{
+  if (GC_is_marked(p) && GC_HAS_DEBUG_INFO(p)) {
+    word p_height = 0;
+    ptr_t p_deepest_obj = 0;
+    ptr_t back_ptr;
+    back_edges *be = 0;
+
+    /* If we remembered a height last time, use it as a minimum.        */
+    /* It may have increased due to newly unreachable chains pointing   */
+    /* to p, but it can't have decreased.                               */
+    back_ptr = GET_OH_BG_PTR(p);
+    if (0 != back_ptr && ((word)back_ptr & FLAG_MANY)) {
+      be = (back_edges *)((word)back_ptr & ~FLAG_MANY);
+      if (be -> height != HEIGHT_UNKNOWN) p_height = be -> height;
+    }
+    FOR_EACH_PRED(q, p, {
+      if (!GC_is_marked(q) && GC_HAS_DEBUG_INFO(q)) {
+        word q_height;
+
+        q_height = backwards_height(q);
+        if (q_height > p_height) {
+          p_height = q_height;
+          p_deepest_obj = q;
+        }
+      }
+    });
+    if (p_height > 0) {
+      /* Remember the height for next time. */
+        if (be == 0) {
+          ensure_struct(p);
+          back_ptr = GET_OH_BG_PTR(p);
+          be = (back_edges *)((word)back_ptr & ~FLAG_MANY);
+        }
+        be -> flags |= RETAIN;
+        be -> height = p_height;
+        be -> height_gc_no = (unsigned short)GC_gc_no;
+    }
+    if (p_height > GC_max_height) {
+        GC_max_height = p_height;
+        GC_deepest_obj = p_deepest_obj;
+    }
+  }
+}
+
+STATIC word GC_max_max_height = 0;
+
+GC_INNER void GC_traverse_back_graph(void)
+{
+  GC_max_height = 0;
+  GC_apply_to_each_object(update_max_height);
+  if (0 != GC_deepest_obj)
+    GC_set_mark_bit(GC_deepest_obj);  /* Keep it until we can print it. */
+}
+
+void GC_print_back_graph_stats(void)
+{
+  GC_printf("Maximum backwards height of reachable objects at GC %lu is %lu\n",
+            (unsigned long) GC_gc_no, (unsigned long)GC_max_height);
+  if (GC_max_height > GC_max_max_height) {
+    GC_max_max_height = GC_max_height;
+    GC_err_printf(
+            "The following unreachable object is last in a longest chain "
+            "of unreachable objects:\n");
+    GC_print_heap_obj(GC_deepest_obj);
+  }
+  GC_COND_LOG_PRINTF("Needed max total of %d back-edge structs\n",
+                     GC_n_back_edge_structs);
+  GC_apply_to_each_object(reset_back_edge);
+  GC_deepest_obj = 0;
+}
+
+#endif /* MAKE_BACK_GRAPH */

+ 289 - 0
blitz.mod/bdwgc/blacklst.c

@@ -0,0 +1,289 @@
+/*
+ * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
+ * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+#include "private/gc_priv.h"
+
+/*
+ * We maintain several hash tables of hblks that have had false hits.
+ * Each contains one bit per hash bucket;  If any page in the bucket
+ * has had a false hit, we assume that all of them have.
+ * See the definition of page_hash_table in gc_private.h.
+ * False hits from the stack(s) are much more dangerous than false hits
+ * from elsewhere, since the former can pin a large object that spans the
+ * block, even though it does not start on the dangerous block.
+ */
+
+/*
+ * Externally callable routines are:
+
+ * GC_add_to_black_list_normal
+ * GC_add_to_black_list_stack
+ * GC_promote_black_lists
+ * GC_is_black_listed
+ *
+ * All require that the allocator lock is held.
+ */
+
+/* Pointers to individual tables.  We replace one table by another by   */
+/* switching these pointers.                                            */
+STATIC word * GC_old_normal_bl = NULL;
+                /* Nonstack false references seen at last full          */
+                /* collection.                                          */
+STATIC word * GC_incomplete_normal_bl = NULL;
+                /* Nonstack false references seen since last            */
+                /* full collection.                                     */
+STATIC word * GC_old_stack_bl = NULL;
+STATIC word * GC_incomplete_stack_bl = NULL;
+
+STATIC word GC_total_stack_black_listed = 0;
+                        /* Number of bytes on stack blacklist.  */
+
+GC_INNER word GC_black_list_spacing = MINHINCR * HBLKSIZE;
+                        /* Initial rough guess. */
+
+STATIC void GC_clear_bl(word *);
+
+GC_INNER void GC_default_print_heap_obj_proc(ptr_t p)
+{
+    ptr_t base = GC_base(p);
+    int kind = HDR(base)->hb_obj_kind;
+
+    GC_err_printf("object at %p of appr. %lu bytes (%s)\n",
+                  base, (unsigned long)GC_size(base),
+                  kind == PTRFREE ? "atomic" :
+                    IS_UNCOLLECTABLE(kind) ? "uncollectable" : "composite");
+}
+
+GC_INNER void (*GC_print_heap_obj)(ptr_t p) = GC_default_print_heap_obj_proc;
+
+#ifdef PRINT_BLACK_LIST
+  STATIC void GC_print_blacklisted_ptr(word p, ptr_t source,
+                                       const char *kind_str)
+  {
+    ptr_t base = GC_base(source);
+
+    if (0 == base) {
+        GC_err_printf("Black listing (%s) %p referenced from %p in %s\n",
+                      kind_str, (ptr_t)p, source,
+                      NULL != source ? "root set" : "register");
+    } else {
+        /* FIXME: We can't call the debug version of GC_print_heap_obj  */
+        /* (with PRINT_CALL_CHAIN) here because the lock is held and    */
+        /* the world is stopped.                                        */
+        GC_err_printf("Black listing (%s) %p referenced from %p in"
+                      " object at %p of appr. %lu bytes\n",
+                      kind_str, (ptr_t)p, source,
+                      base, (unsigned long)GC_size(base));
+    }
+  }
+#endif /* PRINT_BLACK_LIST */
+
+GC_INNER void GC_bl_init_no_interiors(void)
+{
+  if (GC_incomplete_normal_bl == 0) {
+    GC_old_normal_bl = (word *)GC_scratch_alloc(sizeof(page_hash_table));
+    GC_incomplete_normal_bl = (word *)GC_scratch_alloc(
+                                                  sizeof(page_hash_table));
+    if (GC_old_normal_bl == 0 || GC_incomplete_normal_bl == 0) {
+      GC_err_printf("Insufficient memory for black list\n");
+      EXIT();
+    }
+    GC_clear_bl(GC_old_normal_bl);
+    GC_clear_bl(GC_incomplete_normal_bl);
+  }
+}
+
+GC_INNER void GC_bl_init(void)
+{
+    if (!GC_all_interior_pointers) {
+      GC_bl_init_no_interiors();
+    }
+    GC_old_stack_bl = (word *)GC_scratch_alloc(sizeof(page_hash_table));
+    GC_incomplete_stack_bl = (word *)GC_scratch_alloc(sizeof(page_hash_table));
+    if (GC_old_stack_bl == 0 || GC_incomplete_stack_bl == 0) {
+        GC_err_printf("Insufficient memory for black list\n");
+        EXIT();
+    }
+    GC_clear_bl(GC_old_stack_bl);
+    GC_clear_bl(GC_incomplete_stack_bl);
+}
+
+STATIC void GC_clear_bl(word *doomed)
+{
+    BZERO(doomed, sizeof(page_hash_table));
+}
+
+STATIC void GC_copy_bl(word *old, word *new)
+{
+    BCOPY(old, new, sizeof(page_hash_table));
+}
+
+static word total_stack_black_listed(void);
+
+/* Signal the completion of a collection.  Turn the incomplete black    */
+/* lists into new black lists, etc.                                     */
+GC_INNER void GC_promote_black_lists(void)
+{
+    word * very_old_normal_bl = GC_old_normal_bl;
+    word * very_old_stack_bl = GC_old_stack_bl;
+
+    GC_old_normal_bl = GC_incomplete_normal_bl;
+    GC_old_stack_bl = GC_incomplete_stack_bl;
+    if (!GC_all_interior_pointers) {
+      GC_clear_bl(very_old_normal_bl);
+    }
+    GC_clear_bl(very_old_stack_bl);
+    GC_incomplete_normal_bl = very_old_normal_bl;
+    GC_incomplete_stack_bl = very_old_stack_bl;
+    GC_total_stack_black_listed = total_stack_black_listed();
+    GC_VERBOSE_LOG_PRINTF(
+                "%lu bytes in heap blacklisted for interior pointers\n",
+                (unsigned long)GC_total_stack_black_listed);
+    if (GC_total_stack_black_listed != 0) {
+        GC_black_list_spacing =
+                HBLKSIZE*(GC_heapsize/GC_total_stack_black_listed);
+    }
+    if (GC_black_list_spacing < 3 * HBLKSIZE) {
+        GC_black_list_spacing = 3 * HBLKSIZE;
+    }
+    if (GC_black_list_spacing > MAXHINCR * HBLKSIZE) {
+        GC_black_list_spacing = MAXHINCR * HBLKSIZE;
+        /* Makes it easier to allocate really huge blocks, which otherwise */
+        /* may have problems with nonuniform blacklist distributions.      */
+        /* This way we should always succeed immediately after growing the */
+        /* heap.                                                           */
+    }
+}
+
+GC_INNER void GC_unpromote_black_lists(void)
+{
+    if (!GC_all_interior_pointers) {
+      GC_copy_bl(GC_old_normal_bl, GC_incomplete_normal_bl);
+    }
+    GC_copy_bl(GC_old_stack_bl, GC_incomplete_stack_bl);
+}
+
+/* P is not a valid pointer reference, but it falls inside      */
+/* the plausible heap bounds.                                   */
+/* Add it to the normal incomplete black list if appropriate.   */
+#ifdef PRINT_BLACK_LIST
+  GC_INNER void GC_add_to_black_list_normal(word p, ptr_t source)
+#else
+  GC_INNER void GC_add_to_black_list_normal(word p)
+#endif
+{
+  if (GC_modws_valid_offsets[p & (sizeof(word)-1)]) {
+    word index = PHT_HASH((word)p);
+
+    if (HDR(p) == 0 || get_pht_entry_from_index(GC_old_normal_bl, index)) {
+#     ifdef PRINT_BLACK_LIST
+        if (!get_pht_entry_from_index(GC_incomplete_normal_bl, index)) {
+          GC_print_blacklisted_ptr(p, source, "normal");
+        }
+#     endif
+      set_pht_entry_from_index(GC_incomplete_normal_bl, index);
+    } /* else this is probably just an interior pointer to an allocated */
+      /* object, and isn't worth black listing.                         */
+  }
+}
+
+/* And the same for false pointers from the stack. */
+#ifdef PRINT_BLACK_LIST
+  GC_INNER void GC_add_to_black_list_stack(word p, ptr_t source)
+#else
+  GC_INNER void GC_add_to_black_list_stack(word p)
+#endif
+{
+  word index = PHT_HASH((word)p);
+
+  if (HDR(p) == 0 || get_pht_entry_from_index(GC_old_stack_bl, index)) {
+#   ifdef PRINT_BLACK_LIST
+      if (!get_pht_entry_from_index(GC_incomplete_stack_bl, index)) {
+        GC_print_blacklisted_ptr(p, source, "stack");
+      }
+#   endif
+    set_pht_entry_from_index(GC_incomplete_stack_bl, index);
+  }
+}
+
+/*
+ * Is the block starting at h of size len bytes black listed?   If so,
+ * return the address of the next plausible r such that (r, len) might not
+ * be black listed.  (R may not actually be in the heap.  We guarantee only
+ * that every smaller value of r after h is also black listed.)
+ * If (h,len) is not black listed, return 0.
+ * Knows about the structure of the black list hash tables.
+ */
+struct hblk * GC_is_black_listed(struct hblk *h, word len)
+{
+    word index = PHT_HASH((word)h);
+    word i;
+    word nblocks;
+
+    if (!GC_all_interior_pointers
+        && (get_pht_entry_from_index(GC_old_normal_bl, index)
+            || get_pht_entry_from_index(GC_incomplete_normal_bl, index))) {
+      return (h+1);
+    }
+
+    nblocks = divHBLKSZ(len);
+    for (i = 0;;) {
+        if (GC_old_stack_bl[divWORDSZ(index)] == 0
+            && GC_incomplete_stack_bl[divWORDSZ(index)] == 0) {
+            /* An easy case */
+          i += WORDSZ - modWORDSZ(index);
+        } else {
+          if (get_pht_entry_from_index(GC_old_stack_bl, index)
+              || get_pht_entry_from_index(GC_incomplete_stack_bl, index)) {
+            return(h+i+1);
+          }
+          i++;
+        }
+        if (i >= nblocks) break;
+        index = PHT_HASH((word)(h+i));
+    }
+    return(0);
+}
+
+/* Return the number of blacklisted blocks in a given range.    */
+/* Used only for statistical purposes.                          */
+/* Looks only at the GC_incomplete_stack_bl.                    */
+STATIC word GC_number_stack_black_listed(struct hblk *start,
+                                         struct hblk *endp1)
+{
+    register struct hblk * h;
+    word result = 0;
+
+    for (h = start; (word)h < (word)endp1; h++) {
+        word index = PHT_HASH((word)h);
+
+        if (get_pht_entry_from_index(GC_old_stack_bl, index)) result++;
+    }
+    return(result);
+}
+
+/* Return the total number of (stack) black-listed bytes. */
+static word total_stack_black_listed(void)
+{
+    register unsigned i;
+    word total = 0;
+
+    for (i = 0; i < GC_n_heap_sects; i++) {
+        struct hblk * start = (struct hblk *) GC_heap_sects[i].hs_start;
+        struct hblk * endp1 = start + GC_heap_sects[i].hs_bytes/HBLKSIZE;
+
+        total += GC_number_stack_black_listed(start, endp1);
+    }
+    return(total * HBLKSIZE);
+}

+ 224 - 0
blitz.mod/bdwgc/checksums.c

@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 1992-1994 by Xerox Corporation.  All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+#include "private/gc_priv.h"
+
+#ifdef CHECKSUMS
+
+/* This is debugging code intended to verify the results of dirty bit   */
+/* computations. Works only in a single threaded environment.           */
+/* We assume that stubborn objects are changed only when they are       */
+/* enabled for writing.  (Certain kinds of writing are actually         */
+/* safe under other conditions.)                                        */
+#define NSUMS 10000
+
+#define OFFSET 0x10000
+
+typedef struct {
+        GC_bool new_valid;
+        word old_sum;
+        word new_sum;
+        struct hblk * block;    /* Block to which this refers + OFFSET  */
+                                /* to hide it from collector.           */
+} page_entry;
+
+page_entry GC_sums[NSUMS];
+
+STATIC word GC_faulted[NSUMS] = { 0 };
+                /* Record of pages on which we saw a write fault.       */
+
+STATIC size_t GC_n_faulted = 0;
+
+void GC_record_fault(struct hblk * h)
+{
+    word page = (word)h;
+
+    page += GC_page_size - 1;
+    page &= ~(GC_page_size - 1);
+    if (GC_n_faulted >= NSUMS) ABORT("write fault log overflowed");
+    GC_faulted[GC_n_faulted++] = page;
+}
+
+STATIC GC_bool GC_was_faulted(struct hblk *h)
+{
+    size_t i;
+    word page = (word)h;
+
+    page += GC_page_size - 1;
+    page &= ~(GC_page_size - 1);
+    for (i = 0; i < GC_n_faulted; ++i) {
+        if (GC_faulted[i] == page) return TRUE;
+    }
+    return FALSE;
+}
+
+STATIC word GC_checksum(struct hblk *h)
+{
+    word *p = (word *)h;
+    word *lim = (word *)(h+1);
+    word result = 0;
+
+    while ((word)p < (word)lim) {
+        result += *p++;
+    }
+    return(result | 0x80000000 /* doesn't look like pointer */);
+}
+
+#ifdef STUBBORN_ALLOC
+  /* Check whether a stubborn object from the given block appears on    */
+  /* the appropriate free list.                                         */
+  STATIC GC_bool GC_on_free_list(struct hblk *h)
+  {
+    hdr * hhdr = HDR(h);
+    size_t sz = BYTES_TO_WORDS(hhdr -> hb_sz);
+    ptr_t p;
+
+    if (sz > MAXOBJWORDS) return(FALSE);
+    for (p = GC_sobjfreelist[sz]; p != 0; p = obj_link(p)) {
+        if (HBLKPTR(p) == h) return(TRUE);
+    }
+    return(FALSE);
+  }
+#endif
+
+int GC_n_dirty_errors = 0;
+int GC_n_faulted_dirty_errors = 0;
+int GC_n_changed_errors = 0;
+int GC_n_clean = 0;
+int GC_n_dirty = 0;
+
+STATIC void GC_update_check_page(struct hblk *h, int index)
+{
+    page_entry *pe = GC_sums + index;
+    hdr * hhdr = HDR(h);
+    struct hblk *b;
+
+    if (pe -> block != 0 && pe -> block != h + OFFSET) ABORT("goofed");
+    pe -> old_sum = pe -> new_sum;
+    pe -> new_sum = GC_checksum(h);
+#   if !defined(MSWIN32) && !defined(MSWINCE)
+        if (pe -> new_sum != 0x80000000 && !GC_page_was_ever_dirty(h)) {
+            GC_err_printf("GC_page_was_ever_dirty(%p) is wrong\n", (void *)h);
+        }
+#   endif
+    if (GC_page_was_dirty(h)) {
+        GC_n_dirty++;
+    } else {
+        GC_n_clean++;
+    }
+    b = h;
+    while (IS_FORWARDING_ADDR_OR_NIL(hhdr) && hhdr != 0) {
+        b -= (word)hhdr;
+        hhdr = HDR(b);
+    }
+    if (pe -> new_valid
+        && hhdr != 0 && hhdr -> hb_descr != 0 /* may contain pointers */
+        && pe -> old_sum != pe -> new_sum) {
+        if (!GC_page_was_dirty(h) || !GC_page_was_ever_dirty(h)) {
+            GC_bool was_faulted = GC_was_faulted(h);
+            /* Set breakpoint here */GC_n_dirty_errors++;
+            if (was_faulted) GC_n_faulted_dirty_errors++;
+        }
+#       ifdef STUBBORN_ALLOC
+          if (!HBLK_IS_FREE(hhdr)
+            && hhdr -> hb_obj_kind == STUBBORN
+            && !GC_page_was_changed(h)
+            && !GC_on_free_list(h)) {
+            /* if GC_on_free_list(h) then reclaim may have touched it   */
+            /* without any allocations taking place.                    */
+            /* Set breakpoint here */GC_n_changed_errors++;
+          }
+#       endif
+    }
+    pe -> new_valid = TRUE;
+    pe -> block = h + OFFSET;
+}
+
+word GC_bytes_in_used_blocks = 0;
+
+STATIC void GC_add_block(struct hblk *h, word dummy GC_ATTR_UNUSED)
+{
+   hdr * hhdr = HDR(h);
+   size_t bytes = hhdr -> hb_sz;
+
+   bytes += HBLKSIZE-1;
+   bytes &= ~(HBLKSIZE-1);
+   GC_bytes_in_used_blocks += bytes;
+}
+
+STATIC void GC_check_blocks(void)
+{
+    word bytes_in_free_blocks = GC_large_free_bytes;
+
+    GC_bytes_in_used_blocks = 0;
+    GC_apply_to_all_blocks(GC_add_block, (word)0);
+    GC_COND_LOG_PRINTF("GC_bytes_in_used_blocks = %lu,"
+                       " bytes_in_free_blocks = %lu, heapsize = %lu\n",
+                       (unsigned long)GC_bytes_in_used_blocks,
+                       (unsigned long)bytes_in_free_blocks,
+                       (unsigned long)GC_heapsize);
+    if (GC_bytes_in_used_blocks + bytes_in_free_blocks != GC_heapsize) {
+        GC_err_printf("LOST SOME BLOCKS!!\n");
+    }
+}
+
+/* Should be called immediately after GC_read_dirty and GC_read_changed. */
+void GC_check_dirty(void)
+{
+    int index;
+    unsigned i;
+    struct hblk *h;
+    ptr_t start;
+
+    GC_check_blocks();
+
+    GC_n_dirty_errors = 0;
+    GC_n_faulted_dirty_errors = 0;
+    GC_n_changed_errors = 0;
+    GC_n_clean = 0;
+    GC_n_dirty = 0;
+
+    index = 0;
+    for (i = 0; i < GC_n_heap_sects; i++) {
+        start = GC_heap_sects[i].hs_start;
+        for (h = (struct hblk *)start;
+             (word)h < (word)(start + GC_heap_sects[i].hs_bytes); h++) {
+             GC_update_check_page(h, index);
+             index++;
+             if (index >= NSUMS) goto out;
+        }
+    }
+out:
+    GC_COND_LOG_PRINTF("Checked %lu clean and %lu dirty pages\n",
+                       (unsigned long)GC_n_clean, (unsigned long)GC_n_dirty);
+    if (GC_n_dirty_errors > 0) {
+        GC_err_printf("Found %d dirty bit errors (%d were faulted)\n",
+                      GC_n_dirty_errors, GC_n_faulted_dirty_errors);
+    }
+    if (GC_n_changed_errors > 0) {
+        GC_err_printf("Found %lu changed bit errors\n",
+                      (unsigned long)GC_n_changed_errors);
+        GC_err_printf(
+                "These may be benign (provoked by nonpointer changes)\n");
+#       ifdef THREADS
+          GC_err_printf(
+            "Also expect 1 per thread currently allocating a stubborn obj\n");
+#       endif
+    }
+    for (i = 0; i < GC_n_faulted; ++i) {
+        GC_faulted[i] = 0; /* Don't expose block pointers to GC */
+    }
+    GC_n_faulted = 0;
+}
+
+#endif /* CHECKSUMS */

+ 666 - 0
blitz.mod/bdwgc/darwin_stop_world.c

@@ -0,0 +1,666 @@
+/*
+ * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
+ * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
+ * Copyright (c) 2000-2010 by Hewlett-Packard Development Company.
+ * All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+#include "private/pthread_support.h"
+
+/* This probably needs more porting work to ppc64. */
+
+#if defined(GC_DARWIN_THREADS)
+
+/* From "Inside Mac OS X - Mach-O Runtime Architecture" published by Apple
+   Page 49:
+   "The space beneath the stack pointer, where a new stack frame would normally
+   be allocated, is called the red zone. This area as shown in Figure 3-2 may
+   be used for any purpose as long as a new stack frame does not need to be
+   added to the stack."
+
+   Page 50: "If a leaf procedure's red zone usage would exceed 224 bytes, then
+   it must set up a stack frame just like routines that call other routines."
+*/
+#ifdef POWERPC
+# if CPP_WORDSZ == 32
+#   define PPC_RED_ZONE_SIZE 224
+# elif CPP_WORDSZ == 64
+#   define PPC_RED_ZONE_SIZE 320
+# endif
+#endif
+
+#ifndef DARWIN_DONT_PARSE_STACK
+
+typedef struct StackFrame {
+  unsigned long savedSP;
+  unsigned long savedCR;
+  unsigned long savedLR;
+  unsigned long reserved[2];
+  unsigned long savedRTOC;
+} StackFrame;
+
+GC_INNER ptr_t GC_FindTopOfStack(unsigned long stack_start)
+{
+  StackFrame *frame;
+
+# ifdef POWERPC
+    if (stack_start == 0) {
+#     if CPP_WORDSZ == 32
+        __asm__ __volatile__ ("lwz %0,0(r1)" : "=r" (frame));
+#     else
+        __asm__ __volatile__ ("ld %0,0(r1)" : "=r" (frame));
+#     endif
+    } else
+# else
+    GC_ASSERT(stack_start != 0); /* not implemented */
+# endif /* !POWERPC */
+  /* else */ {
+    frame = (StackFrame *)stack_start;
+  }
+
+# ifdef DEBUG_THREADS_EXTRA
+    GC_log_printf("FindTopOfStack start at sp = %p\n", frame);
+# endif
+  while (frame->savedSP != 0) {
+    /* if there are no more stack frames, stop */
+
+    frame = (StackFrame*)frame->savedSP;
+
+    /* we do these next two checks after going to the next frame
+       because the LR for the first stack frame in the loop
+       is not set up on purpose, so we shouldn't check it. */
+    if ((frame->savedLR & ~0x3) == 0 || (frame->savedLR & ~0x3) == ~0x3U)
+      break; /* if the next LR is bogus, stop */
+  }
+# ifdef DEBUG_THREADS_EXTRA
+    GC_log_printf("FindTopOfStack finish at sp = %p\n", frame);
+# endif
+  return (ptr_t)frame;
+}
+
+#endif /* !DARWIN_DONT_PARSE_STACK */
+
+/* GC_query_task_threads controls whether to obtain the list of */
+/* the threads from the kernel or to use GC_threads table.      */
+#ifdef GC_NO_THREADS_DISCOVERY
+# define GC_query_task_threads FALSE
+#elif defined(GC_DISCOVER_TASK_THREADS)
+# define GC_query_task_threads TRUE
+#else
+  STATIC GC_bool GC_query_task_threads = FALSE;
+#endif /* !GC_NO_THREADS_DISCOVERY */
+
+/* Use implicit threads registration (all task threads excluding the GC */
+/* special ones are stopped and scanned).  Should be called before      */
+/* GC_INIT() (or, at least, before going multi-threaded).  Deprecated.  */
+GC_API void GC_CALL GC_use_threads_discovery(void)
+{
+# if defined(GC_NO_THREADS_DISCOVERY) || defined(DARWIN_DONT_PARSE_STACK)
+    ABORT("Darwin task-threads-based stop and push unsupported");
+# else
+    GC_ASSERT(!GC_need_to_lock);
+#   ifndef GC_DISCOVER_TASK_THREADS
+      GC_query_task_threads = TRUE;
+#   endif
+    GC_init_parallel(); /* just to be consistent with Win32 one */
+# endif
+}
+
+/* Evaluates the stack range for a given thread.  Returns the lower     */
+/* bound and sets *phi to the upper one.                                */
+STATIC ptr_t GC_stack_range_for(ptr_t *phi, thread_act_t thread, GC_thread p,
+                                GC_bool thread_blocked, mach_port_t my_thread)
+{
+  ptr_t lo;
+  if (thread == my_thread) {
+    GC_ASSERT(!thread_blocked);
+    lo = GC_approx_sp();
+#   ifndef DARWIN_DONT_PARSE_STACK
+      *phi = GC_FindTopOfStack(0);
+#   endif
+
+  } else if (thread_blocked) {
+    lo = p->stop_info.stack_ptr;
+#   ifndef DARWIN_DONT_PARSE_STACK
+      *phi = p->topOfStack;
+#   endif
+
+  } else {
+    /* MACHINE_THREAD_STATE_COUNT does not seem to be defined       */
+    /* everywhere.  Hence we use our own version.  Alternatively,   */
+    /* we could use THREAD_STATE_MAX (but seems to be not optimal). */
+    kern_return_t kern_result;
+    mach_msg_type_number_t thread_state_count = GC_MACH_THREAD_STATE_COUNT;
+    GC_THREAD_STATE_T state;
+
+    /* Get the thread state (registers, etc) */
+    kern_result = thread_get_state(thread, GC_MACH_THREAD_STATE,
+                                   (natural_t *)&state,
+                                   &thread_state_count);
+#   ifdef DEBUG_THREADS
+      GC_log_printf("thread_get_state returns value = %d\n", kern_result);
+#   endif
+    if (kern_result != KERN_SUCCESS)
+      ABORT("thread_get_state failed");
+
+#   if defined(I386)
+      lo = (void *)state.THREAD_FLD(esp);
+#     ifndef DARWIN_DONT_PARSE_STACK
+        *phi = GC_FindTopOfStack(state.THREAD_FLD(esp));
+#     endif
+      GC_push_one(state.THREAD_FLD(eax));
+      GC_push_one(state.THREAD_FLD(ebx));
+      GC_push_one(state.THREAD_FLD(ecx));
+      GC_push_one(state.THREAD_FLD(edx));
+      GC_push_one(state.THREAD_FLD(edi));
+      GC_push_one(state.THREAD_FLD(esi));
+      GC_push_one(state.THREAD_FLD(ebp));
+
+#   elif defined(X86_64)
+      lo = (void *)state.THREAD_FLD(rsp);
+#     ifndef DARWIN_DONT_PARSE_STACK
+        *phi = GC_FindTopOfStack(state.THREAD_FLD(rsp));
+#     endif
+      GC_push_one(state.THREAD_FLD(rax));
+      GC_push_one(state.THREAD_FLD(rbx));
+      GC_push_one(state.THREAD_FLD(rcx));
+      GC_push_one(state.THREAD_FLD(rdx));
+      GC_push_one(state.THREAD_FLD(rdi));
+      GC_push_one(state.THREAD_FLD(rsi));
+      GC_push_one(state.THREAD_FLD(rbp));
+      /* GC_push_one(state.THREAD_FLD(rsp)); */
+      GC_push_one(state.THREAD_FLD(r8));
+      GC_push_one(state.THREAD_FLD(r9));
+      GC_push_one(state.THREAD_FLD(r10));
+      GC_push_one(state.THREAD_FLD(r11));
+      GC_push_one(state.THREAD_FLD(r12));
+      GC_push_one(state.THREAD_FLD(r13));
+      GC_push_one(state.THREAD_FLD(r14));
+      GC_push_one(state.THREAD_FLD(r15));
+
+#   elif defined(POWERPC)
+      lo = (void *)(state.THREAD_FLD(r1) - PPC_RED_ZONE_SIZE);
+#     ifndef DARWIN_DONT_PARSE_STACK
+        *phi = GC_FindTopOfStack(state.THREAD_FLD(r1));
+#     endif
+      GC_push_one(state.THREAD_FLD(r0));
+      GC_push_one(state.THREAD_FLD(r2));
+      GC_push_one(state.THREAD_FLD(r3));
+      GC_push_one(state.THREAD_FLD(r4));
+      GC_push_one(state.THREAD_FLD(r5));
+      GC_push_one(state.THREAD_FLD(r6));
+      GC_push_one(state.THREAD_FLD(r7));
+      GC_push_one(state.THREAD_FLD(r8));
+      GC_push_one(state.THREAD_FLD(r9));
+      GC_push_one(state.THREAD_FLD(r10));
+      GC_push_one(state.THREAD_FLD(r11));
+      GC_push_one(state.THREAD_FLD(r12));
+      GC_push_one(state.THREAD_FLD(r13));
+      GC_push_one(state.THREAD_FLD(r14));
+      GC_push_one(state.THREAD_FLD(r15));
+      GC_push_one(state.THREAD_FLD(r16));
+      GC_push_one(state.THREAD_FLD(r17));
+      GC_push_one(state.THREAD_FLD(r18));
+      GC_push_one(state.THREAD_FLD(r19));
+      GC_push_one(state.THREAD_FLD(r20));
+      GC_push_one(state.THREAD_FLD(r21));
+      GC_push_one(state.THREAD_FLD(r22));
+      GC_push_one(state.THREAD_FLD(r23));
+      GC_push_one(state.THREAD_FLD(r24));
+      GC_push_one(state.THREAD_FLD(r25));
+      GC_push_one(state.THREAD_FLD(r26));
+      GC_push_one(state.THREAD_FLD(r27));
+      GC_push_one(state.THREAD_FLD(r28));
+      GC_push_one(state.THREAD_FLD(r29));
+      GC_push_one(state.THREAD_FLD(r30));
+      GC_push_one(state.THREAD_FLD(r31));
+
+#   elif defined(ARM32)
+      lo = (void *)state.__sp;
+#     ifndef DARWIN_DONT_PARSE_STACK
+        *phi = GC_FindTopOfStack(state.__sp);
+#     endif
+      GC_push_one(state.__r[0]);
+      GC_push_one(state.__r[1]);
+      GC_push_one(state.__r[2]);
+      GC_push_one(state.__r[3]);
+      GC_push_one(state.__r[4]);
+      GC_push_one(state.__r[5]);
+      GC_push_one(state.__r[6]);
+      GC_push_one(state.__r[7]);
+      GC_push_one(state.__r[8]);
+      GC_push_one(state.__r[9]);
+      GC_push_one(state.__r[10]);
+      GC_push_one(state.__r[11]);
+      GC_push_one(state.__r[12]);
+      /* GC_push_one(state.__sp); */
+      GC_push_one(state.__lr);
+      /* GC_push_one(state.__pc); */
+      GC_push_one(state.__cpsr);
+
+#   else
+#     error FIXME for non-x86 || ppc || arm architectures
+#   endif
+  } /* thread != my_thread */
+
+# ifdef DARWIN_DONT_PARSE_STACK
+    /* p is guaranteed to be non-NULL regardless of GC_query_task_threads. */
+    *phi = (p->flags & MAIN_THREAD) != 0 ? GC_stackbottom : p->stack_end;
+# endif
+# ifdef DEBUG_THREADS
+    GC_log_printf("Darwin: Stack for thread %p = [%p,%p)\n",
+                  (void *)thread, lo, *phi);
+# endif
+  return lo;
+}
+
+GC_INNER void GC_push_all_stacks(void)
+{
+  int i;
+  ptr_t lo, hi;
+  task_t my_task = current_task();
+  mach_port_t my_thread = mach_thread_self();
+  GC_bool found_me = FALSE;
+  int nthreads = 0;
+  word total_size = 0;
+  mach_msg_type_number_t listcount = (mach_msg_type_number_t)THREAD_TABLE_SZ;
+  if (!EXPECT(GC_thr_initialized, TRUE))
+    GC_thr_init();
+
+# ifndef DARWIN_DONT_PARSE_STACK
+    if (GC_query_task_threads) {
+      kern_return_t kern_result;
+      thread_act_array_t act_list = 0;
+
+      /* Obtain the list of the threads from the kernel.  */
+      kern_result = task_threads(my_task, &act_list, &listcount);
+      if (kern_result != KERN_SUCCESS)
+        ABORT("task_threads failed");
+
+      for (i = 0; i < (int)listcount; i++) {
+        thread_act_t thread = act_list[i];
+        lo = GC_stack_range_for(&hi, thread, NULL, FALSE, my_thread);
+        GC_ASSERT((word)lo <= (word)hi);
+        total_size += hi - lo;
+        GC_push_all_stack(lo, hi);
+        nthreads++;
+        if (thread == my_thread)
+          found_me = TRUE;
+        mach_port_deallocate(my_task, thread);
+      } /* for (i=0; ...) */
+
+      vm_deallocate(my_task, (vm_address_t)act_list,
+                    sizeof(thread_t) * listcount);
+    } else
+# endif /* !DARWIN_DONT_PARSE_STACK */
+  /* else */ {
+    for (i = 0; i < (int)listcount; i++) {
+      GC_thread p;
+      for (p = GC_threads[i]; p != NULL; p = p->next)
+        if ((p->flags & FINISHED) == 0) {
+          thread_act_t thread = (thread_act_t)p->stop_info.mach_thread;
+          lo = GC_stack_range_for(&hi, thread, p, (GC_bool)p->thread_blocked,
+                                  my_thread);
+          GC_ASSERT((word)lo <= (word)hi);
+          total_size += hi - lo;
+          GC_push_all_stack_sections(lo, hi, p->traced_stack_sect);
+          nthreads++;
+          if (thread == my_thread)
+            found_me = TRUE;
+        }
+    } /* for (i=0; ...) */
+  }
+
+  mach_port_deallocate(my_task, my_thread);
+  GC_VERBOSE_LOG_PRINTF("Pushed %d thread stacks\n", nthreads);
+  if (!found_me && !GC_in_thread_creation)
+    ABORT("Collecting from unknown thread");
+  GC_total_stacksize = total_size;
+}
+
+#ifndef GC_NO_THREADS_DISCOVERY
+
+# ifdef MPROTECT_VDB
+    STATIC mach_port_t GC_mach_handler_thread = 0;
+    STATIC GC_bool GC_use_mach_handler_thread = FALSE;
+
+    GC_INNER void GC_darwin_register_mach_handler_thread(mach_port_t thread)
+    {
+      GC_mach_handler_thread = thread;
+      GC_use_mach_handler_thread = TRUE;
+    }
+# endif /* MPROTECT_VDB */
+
+# ifndef GC_MAX_MACH_THREADS
+#   define GC_MAX_MACH_THREADS THREAD_TABLE_SZ
+# endif
+
+  struct GC_mach_thread {
+    thread_act_t thread;
+    GC_bool already_suspended;
+  };
+
+  struct GC_mach_thread GC_mach_threads[GC_MAX_MACH_THREADS];
+  STATIC int GC_mach_threads_count = 0;
+  /* FIXME: it is better to implement GC_mach_threads as a hash set.  */
+
+/* returns true if there's a thread in act_list that wasn't in old_list */
+STATIC GC_bool GC_suspend_thread_list(thread_act_array_t act_list, int count,
+                                      thread_act_array_t old_list,
+                                      int old_count, mach_port_t my_thread)
+{
+  int i;
+  int j = -1;
+  GC_bool changed = FALSE;
+
+  for (i = 0; i < count; i++) {
+    thread_act_t thread = act_list[i];
+    GC_bool found;
+    struct thread_basic_info info;
+    mach_msg_type_number_t outCount;
+    kern_return_t kern_result;
+
+    if (thread == my_thread
+#       ifdef MPROTECT_VDB
+          || (GC_mach_handler_thread == thread && GC_use_mach_handler_thread)
+#       endif
+        ) {
+      /* Don't add our and the handler threads. */
+      continue;
+    }
+#   ifdef PARALLEL_MARK
+      if (GC_is_mach_marker(thread))
+        continue; /* ignore the parallel marker threads */
+#   endif
+
+#   ifdef DEBUG_THREADS
+      GC_log_printf("Attempting to suspend thread %p\n", (void *)thread);
+#   endif
+    /* find the current thread in the old list */
+    found = FALSE;
+    {
+      int last_found = j; /* remember the previous found thread index */
+
+      /* Search for the thread starting from the last found one first.  */
+      while (++j < old_count)
+        if (old_list[j] == thread) {
+          found = TRUE;
+          break;
+        }
+      if (!found) {
+        /* If not found, search in the rest (beginning) of the list.    */
+        for (j = 0; j < last_found; j++)
+          if (old_list[j] == thread) {
+            found = TRUE;
+            break;
+          }
+
+        if (!found) {
+          /* add it to the GC_mach_threads list */
+          if (GC_mach_threads_count == GC_MAX_MACH_THREADS)
+            ABORT("Too many threads");
+          GC_mach_threads[GC_mach_threads_count].thread = thread;
+          /* default is not suspended */
+          GC_mach_threads[GC_mach_threads_count].already_suspended = FALSE;
+          changed = TRUE;
+        }
+      }
+    }
+
+    outCount = THREAD_INFO_MAX;
+    kern_result = thread_info(thread, THREAD_BASIC_INFO,
+                              (thread_info_t)&info, &outCount);
+    if (kern_result != KERN_SUCCESS) {
+      /* The thread may have quit since the thread_threads() call we  */
+      /* mark already suspended so it's not dealt with anymore later. */
+      if (!found)
+        GC_mach_threads[GC_mach_threads_count++].already_suspended = TRUE;
+      continue;
+    }
+#   ifdef DEBUG_THREADS
+      GC_log_printf("Thread state for %p = %d\n", (void *)thread, info.run_state);
+#   endif
+    if (info.suspend_count != 0) {
+      /* thread is already suspended. */
+      if (!found)
+        GC_mach_threads[GC_mach_threads_count++].already_suspended = TRUE;
+      continue;
+    }
+
+#   ifdef DEBUG_THREADS
+      GC_log_printf("Suspending %p\n", (void *)thread);
+#   endif
+    kern_result = thread_suspend(thread);
+    if (kern_result != KERN_SUCCESS) {
+      /* The thread may have quit since the thread_threads() call we  */
+      /* mark already suspended so it's not dealt with anymore later. */
+      if (!found)
+        GC_mach_threads[GC_mach_threads_count++].already_suspended = TRUE;
+      continue;
+    }
+    if (!found)
+      GC_mach_threads_count++;
+  }
+  return changed;
+}
+
+#endif /* !GC_NO_THREADS_DISCOVERY */
+
+/* Caller holds allocation lock.        */
+GC_INNER void GC_stop_world(void)
+{
+  unsigned i;
+  task_t my_task = current_task();
+  mach_port_t my_thread = mach_thread_self();
+  kern_return_t kern_result;
+
+# ifdef DEBUG_THREADS
+    GC_log_printf("Stopping the world from thread %p\n", (void *)my_thread);
+# endif
+# ifdef PARALLEL_MARK
+    if (GC_parallel) {
+      /* Make sure all free list construction has stopped before we     */
+      /* start.  No new construction can start, since free list         */
+      /* construction is required to acquire and release the GC lock    */
+      /* before it starts, and we have the lock.                        */
+      GC_acquire_mark_lock();
+      GC_ASSERT(GC_fl_builder_count == 0);
+      /* We should have previously waited for it to become zero. */
+    }
+# endif /* PARALLEL_MARK */
+
+  if (GC_query_task_threads) {
+#   ifndef GC_NO_THREADS_DISCOVERY
+      GC_bool changed;
+      thread_act_array_t act_list, prev_list;
+      mach_msg_type_number_t listcount, prevcount;
+
+      /* Clear out the mach threads list table.  We do not need to      */
+      /* really clear GC_mach_threads[] as it is used only in the range */
+      /* from 0 to GC_mach_threads_count-1, inclusive.                  */
+      GC_mach_threads_count = 0;
+
+      /* Loop stopping threads until you have gone over the whole list  */
+      /* twice without a new one appearing.  thread_create() won't      */
+      /* return (and thus the thread stop) until the new thread exists, */
+      /* so there is no window whereby you could stop a thread,         */
+      /* recognize it is stopped, but then have a new thread it created */
+      /* before stopping show up later.                                 */
+      changed = TRUE;
+      prev_list = NULL;
+      prevcount = 0;
+      do {
+        kern_result = task_threads(my_task, &act_list, &listcount);
+
+        if (kern_result == KERN_SUCCESS) {
+          changed = GC_suspend_thread_list(act_list, listcount, prev_list,
+                                           prevcount, my_thread);
+
+          if (prev_list != NULL) {
+            for (i = 0; i < prevcount; i++)
+              mach_port_deallocate(my_task, prev_list[i]);
+
+            vm_deallocate(my_task, (vm_address_t)prev_list,
+                          sizeof(thread_t) * prevcount);
+          }
+
+          /* Repeat while having changes. */
+          prev_list = act_list;
+          prevcount = listcount;
+        }
+      } while (changed);
+
+      GC_ASSERT(prev_list != 0);
+      for (i = 0; i < prevcount; i++)
+        mach_port_deallocate(my_task, prev_list[i]);
+      vm_deallocate(my_task, (vm_address_t)act_list,
+                    sizeof(thread_t) * listcount);
+#   endif /* !GC_NO_THREADS_DISCOVERY */
+
+  } else {
+    for (i = 0; i < THREAD_TABLE_SZ; i++) {
+      GC_thread p;
+
+      for (p = GC_threads[i]; p != NULL; p = p->next) {
+        if ((p->flags & FINISHED) == 0 && !p->thread_blocked &&
+             p->stop_info.mach_thread != my_thread) {
+
+          kern_result = thread_suspend(p->stop_info.mach_thread);
+          if (kern_result != KERN_SUCCESS)
+            ABORT("thread_suspend failed");
+        }
+      }
+    }
+  }
+
+# ifdef MPROTECT_VDB
+    if(GC_incremental) {
+      GC_mprotect_stop();
+    }
+# endif
+# ifdef PARALLEL_MARK
+    if (GC_parallel)
+      GC_release_mark_lock();
+# endif
+
+# ifdef DEBUG_THREADS
+    GC_log_printf("World stopped from %p\n", (void *)my_thread);
+# endif
+  mach_port_deallocate(my_task, my_thread);
+}
+
+GC_INLINE void GC_thread_resume(thread_act_t thread)
+{
+  kern_return_t kern_result;
+# if defined(DEBUG_THREADS) || defined(GC_ASSERTIONS)
+    struct thread_basic_info info;
+    mach_msg_type_number_t outCount = THREAD_INFO_MAX;
+    kern_result = thread_info(thread, THREAD_BASIC_INFO,
+                              (thread_info_t)&info, &outCount);
+    if (kern_result != KERN_SUCCESS)
+      ABORT("thread_info failed");
+# endif
+# ifdef DEBUG_THREADS
+    GC_log_printf("Resuming thread %p with state %d\n", (void *)thread,
+                  info.run_state);
+# endif
+  /* Resume the thread */
+  kern_result = thread_resume(thread);
+  if (kern_result != KERN_SUCCESS)
+    ABORT("thread_resume failed");
+}
+
+/* Caller holds allocation lock, and has held it continuously since     */
+/* the world stopped.                                                   */
+GC_INNER void GC_start_world(void)
+{
+  task_t my_task = current_task();
+  int i;
+# ifdef DEBUG_THREADS
+    GC_log_printf("World starting\n");
+# endif
+# ifdef MPROTECT_VDB
+    if(GC_incremental) {
+      GC_mprotect_resume();
+    }
+# endif
+
+  if (GC_query_task_threads) {
+#   ifndef GC_NO_THREADS_DISCOVERY
+      int j = GC_mach_threads_count;
+      kern_return_t kern_result;
+      thread_act_array_t act_list;
+      mach_msg_type_number_t listcount;
+
+      kern_result = task_threads(my_task, &act_list, &listcount);
+      if (kern_result != KERN_SUCCESS)
+        ABORT("task_threads failed");
+
+      for (i = 0; i < (int)listcount; i++) {
+        thread_act_t thread = act_list[i];
+        int last_found = j;        /* The thread index found during the   */
+                                   /* previous iteration (count value     */
+                                   /* means no thread found yet).         */
+
+        /* Search for the thread starting from the last found one first.  */
+        while (++j < GC_mach_threads_count) {
+          if (GC_mach_threads[j].thread == thread)
+            break;
+        }
+        if (j >= GC_mach_threads_count) {
+          /* If not found, search in the rest (beginning) of the list.    */
+          for (j = 0; j < last_found; j++) {
+            if (GC_mach_threads[j].thread == thread)
+              break;
+          }
+        }
+
+        if (j != last_found) {
+          /* The thread is found in GC_mach_threads.      */
+          if (GC_mach_threads[j].already_suspended) {
+#           ifdef DEBUG_THREADS
+              GC_log_printf("Not resuming already suspended thread %p\n",
+                            (void *)thread);
+#           endif
+          } else {
+            GC_thread_resume(thread);
+          }
+        }
+
+        mach_port_deallocate(my_task, thread);
+      }
+      vm_deallocate(my_task, (vm_address_t)act_list,
+                    sizeof(thread_t) * listcount);
+#   endif /* !GC_NO_THREADS_DISCOVERY */
+
+  } else {
+    mach_port_t my_thread = mach_thread_self();
+
+    for (i = 0; i < THREAD_TABLE_SZ; i++) {
+      GC_thread p;
+      for (p = GC_threads[i]; p != NULL; p = p->next) {
+        if ((p->flags & FINISHED) == 0 && !p->thread_blocked &&
+             p->stop_info.mach_thread != my_thread)
+          GC_thread_resume(p->stop_info.mach_thread);
+      }
+    }
+
+    mach_port_deallocate(my_task, my_thread);
+  }
+
+# ifdef DEBUG_THREADS
+    GC_log_printf("World started\n");
+# endif
+}
+
+#endif /* GC_DARWIN_THREADS */

+ 1218 - 0
blitz.mod/bdwgc/dbg_mlc.c

@@ -0,0 +1,1218 @@
+/*
+ * 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-2004 Hewlett-Packard Development Company, L.P.
+ * Copyright (C) 2007 Free Software Foundation, Inc
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+#include "private/dbg_mlc.h"
+
+#ifndef MSWINCE
+# include <errno.h>
+#endif
+#include <string.h>
+
+#ifndef SHORT_DBG_HDRS
+  /* Check whether object with base pointer p has debugging info. */
+  /* p is assumed to point to a legitimate object in our part     */
+  /* of the heap.                                                 */
+  /* This excludes the check as to whether the back pointer is    */
+  /* odd, which is added by the GC_HAS_DEBUG_INFO macro.          */
+  /* Note that if DBG_HDRS_ALL is set, uncollectible objects      */
+  /* on free lists may not have debug information set.  Thus it's */
+  /* not always safe to return TRUE (1), even if the client does  */
+  /* its part.  Return -1 if the object with debug info has been  */
+  /* marked as deallocated.                                       */
+  GC_INNER int GC_has_other_debug_info(ptr_t p)
+  {
+    ptr_t body = (ptr_t)((oh *)p + 1);
+    word sz = GC_size(p);
+
+    if (HBLKPTR(p) != HBLKPTR((ptr_t)body)
+        || sz < DEBUG_BYTES + EXTRA_BYTES) {
+      return 0;
+    }
+    if (((oh *)p) -> oh_sf != (START_FLAG ^ (word)body)
+        && ((word *)p)[BYTES_TO_WORDS(sz)-1] != (END_FLAG ^ (word)body)) {
+      return 0;
+    }
+    if (((oh *)p)->oh_sz == sz) {
+      /* Object may have had debug info, but has been deallocated     */
+      return -1;
+    }
+    return 1;
+  }
+#endif /* !SHORT_DBG_HDRS */
+
+#ifdef KEEP_BACK_PTRS
+
+# include <stdlib.h>
+
+# if defined(__GLIBC__) || defined(SOLARIS) \
+     || defined(HPUX) || defined(IRIX5) || defined(OSF1)
+#   define RANDOM() random()
+# else
+#   define RANDOM() (long)rand()
+# endif
+
+  /* Store back pointer to source in dest, if that appears to be possible. */
+  /* This is not completely safe, since we may mistakenly conclude that    */
+  /* dest has a debugging wrapper.  But the error probability is very      */
+  /* small, and this shouldn't be used in production code.                 */
+  /* We assume that dest is the real base pointer.  Source will usually    */
+  /* be a pointer to the interior of an object.                            */
+  GC_INNER void GC_store_back_pointer(ptr_t source, ptr_t dest)
+  {
+    if (GC_HAS_DEBUG_INFO(dest)) {
+      ((oh *)dest) -> oh_back_ptr = HIDE_BACK_PTR(source);
+    }
+  }
+
+  GC_INNER void GC_marked_for_finalization(ptr_t dest)
+  {
+    GC_store_back_pointer(MARKED_FOR_FINALIZATION, dest);
+  }
+
+  /* Store information about the object referencing dest in *base_p     */
+  /* and *offset_p.                                                     */
+  /*   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.                      */
+  GC_API GC_ref_kind GC_CALL GC_get_back_ptr_info(void *dest, void **base_p,
+                                                  size_t *offset_p)
+  {
+    oh * hdr = (oh *)GC_base(dest);
+    ptr_t bp;
+    ptr_t bp_base;
+
+#   ifdef LINT2
+      /* Explicitly instruct the code analysis tool that                */
+      /* GC_get_back_ptr_info is not expected to be called with an      */
+      /* incorrect "dest" value.                                        */
+      if (!hdr) ABORT("Invalid GC_get_back_ptr_info argument");
+#   endif
+    if (!GC_HAS_DEBUG_INFO((ptr_t) hdr)) return GC_NO_SPACE;
+    bp = GC_REVEAL_POINTER(hdr -> oh_back_ptr);
+    if (MARKED_FOR_FINALIZATION == bp) return GC_FINALIZER_REFD;
+    if (MARKED_FROM_REGISTER == bp) return GC_REFD_FROM_REG;
+    if (NOT_MARKED == bp) return GC_UNREFERENCED;
+#   if ALIGNMENT == 1
+      /* Heuristically try to fix off by 1 errors we introduced by      */
+      /* insisting on even addresses.                                   */
+      {
+        ptr_t alternate_ptr = bp + 1;
+        ptr_t target = *(ptr_t *)bp;
+        ptr_t alternate_target = *(ptr_t *)alternate_ptr;
+
+        if ((word)alternate_target >= (word)GC_least_plausible_heap_addr
+            && (word)alternate_target <= (word)GC_greatest_plausible_heap_addr
+            && ((word)target < (word)GC_least_plausible_heap_addr
+                || (word)target > (word)GC_greatest_plausible_heap_addr)) {
+            bp = alternate_ptr;
+        }
+      }
+#   endif
+    bp_base = GC_base(bp);
+    if (0 == bp_base) {
+      *base_p = bp;
+      *offset_p = 0;
+      return GC_REFD_FROM_ROOT;
+    } else {
+      if (GC_HAS_DEBUG_INFO(bp_base)) bp_base += sizeof(oh);
+      *base_p = bp_base;
+      *offset_p = bp - bp_base;
+      return GC_REFD_FROM_HEAP;
+    }
+  }
+
+  /* Generate a random heap address.            */
+  /* The resulting address is in the heap, but  */
+  /* not necessarily inside a valid object.     */
+  GC_API void * GC_CALL GC_generate_random_heap_address(void)
+  {
+    size_t i;
+    size_t size;
+    word heap_offset = RANDOM();
+
+    if (GC_heapsize > RAND_MAX) {
+        heap_offset *= RAND_MAX;
+        heap_offset += RANDOM();
+    }
+    heap_offset %= GC_heapsize;
+        /* This doesn't yield a uniform distribution, especially if     */
+        /* e.g. RAND_MAX = 1.5* GC_heapsize.  But for typical cases,    */
+        /* it's not too bad.                                            */
+    for (i = 0;; ++i) {
+        if (i >= GC_n_heap_sects)
+          ABORT("GC_generate_random_heap_address: size inconsistency");
+
+        size = GC_heap_sects[i].hs_bytes;
+        if (heap_offset < size) {
+            break;
+        } else {
+            heap_offset -= size;
+        }
+    }
+    return GC_heap_sects[i].hs_start + heap_offset;
+  }
+
+  /* Generate a random address inside a valid marked heap object. */
+  GC_API void * GC_CALL GC_generate_random_valid_address(void)
+  {
+    ptr_t result;
+    ptr_t base;
+    do {
+      result = GC_generate_random_heap_address();
+      base = GC_base(result);
+    } while (base == 0 || !GC_is_marked(base));
+    return result;
+  }
+
+  /* Print back trace for p */
+  GC_API void GC_CALL GC_print_backtrace(void *p)
+  {
+    void *current = p;
+    int i;
+    GC_ref_kind source;
+    size_t offset;
+    void *base;
+
+    GC_print_heap_obj(GC_base(current));
+
+    for (i = 0; ; ++i) {
+      source = GC_get_back_ptr_info(current, &base, &offset);
+      if (GC_UNREFERENCED == source) {
+        GC_err_printf("Reference could not be found\n");
+        goto out;
+      }
+      if (GC_NO_SPACE == source) {
+        GC_err_printf("No debug info in object: Can't find reference\n");
+        goto out;
+      }
+      GC_err_printf("Reachable via %d levels of pointers from ", i);
+      switch(source) {
+        case GC_REFD_FROM_ROOT:
+          GC_err_printf("root at %p\n\n", base);
+          goto out;
+        case GC_REFD_FROM_REG:
+          GC_err_printf("root in register\n\n");
+          goto out;
+        case GC_FINALIZER_REFD:
+          GC_err_printf("list of finalizable objects\n\n");
+          goto out;
+        case GC_REFD_FROM_HEAP:
+          GC_err_printf("offset %ld in object:\n", (long)offset);
+          /* Take GC_base(base) to get real base, i.e. header. */
+          GC_print_heap_obj(GC_base(base));
+          break;
+        default:
+          GC_err_printf("INTERNAL ERROR: UNEXPECTED SOURCE!!!!\n");
+          goto out;
+      }
+      current = base;
+    }
+    out:;
+  }
+
+  /* Force a garbage collection and generate/print a backtrace  */
+  /* from a random heap address.                                */
+  GC_INNER void GC_generate_random_backtrace_no_gc(void)
+  {
+    void * current;
+    current = GC_generate_random_valid_address();
+    GC_printf("\n****Chosen address %p in object\n", current);
+    GC_print_backtrace(current);
+  }
+
+  GC_API void GC_CALL GC_generate_random_backtrace(void)
+  {
+    if (GC_try_to_collect(GC_never_stop_func) == 0) {
+      GC_err_printf("Cannot generate a backtrace: "
+                    "garbage collection is disabled!\n");
+      return;
+    }
+    GC_generate_random_backtrace_no_gc();
+  }
+
+#endif /* KEEP_BACK_PTRS */
+
+# define CROSSES_HBLK(p, sz) \
+        (((word)((p) + sizeof(oh) + (sz) - 1) ^ (word)(p)) >= HBLKSIZE)
+
+/* Store debugging info into p.  Return displaced pointer.         */
+/* This version assumes we do hold the allocation lock.            */
+STATIC ptr_t GC_store_debug_info_inner(ptr_t p, word sz GC_ATTR_UNUSED,
+                                       const char *string, int linenum)
+{
+    word * result = (word *)((oh *)p + 1);
+
+    GC_ASSERT(GC_size(p) >= sizeof(oh) + sz);
+    GC_ASSERT(!(SMALL_OBJ(sz) && CROSSES_HBLK(p, sz)));
+#   ifdef KEEP_BACK_PTRS
+      ((oh *)p) -> oh_back_ptr = HIDE_BACK_PTR(NOT_MARKED);
+#   endif
+#   ifdef MAKE_BACK_GRAPH
+      ((oh *)p) -> oh_bg_ptr = HIDE_BACK_PTR((ptr_t)0);
+#   endif
+    ((oh *)p) -> oh_string = string;
+    ((oh *)p) -> oh_int = (word)linenum;
+#   ifndef SHORT_DBG_HDRS
+      ((oh *)p) -> oh_sz = sz;
+      ((oh *)p) -> oh_sf = START_FLAG ^ (word)result;
+      ((word *)p)[BYTES_TO_WORDS(GC_size(p))-1] =
+         result[SIMPLE_ROUNDED_UP_WORDS(sz)] = END_FLAG ^ (word)result;
+#   endif
+    return((ptr_t)result);
+}
+
+GC_INNER ptr_t GC_store_debug_info(ptr_t p, word sz, const char *string,
+                                   int linenum)
+{
+    ptr_t result;
+    DCL_LOCK_STATE;
+
+    LOCK();
+    result = GC_store_debug_info_inner(p, sz, string, linenum);
+    UNLOCK();
+    return result;
+}
+
+#ifndef SHORT_DBG_HDRS
+  /* Check the object with debugging info at ohdr.      */
+  /* Return NULL if it's OK.  Else return clobbered     */
+  /* address.                                           */
+  STATIC ptr_t GC_check_annotated_obj(oh *ohdr)
+  {
+    ptr_t body = (ptr_t)(ohdr + 1);
+    word gc_sz = GC_size((ptr_t)ohdr);
+    if (ohdr -> oh_sz + DEBUG_BYTES > gc_sz) {
+        return((ptr_t)(&(ohdr -> oh_sz)));
+    }
+    if (ohdr -> oh_sf != (START_FLAG ^ (word)body)) {
+        return((ptr_t)(&(ohdr -> oh_sf)));
+    }
+    if (((word *)ohdr)[BYTES_TO_WORDS(gc_sz)-1] != (END_FLAG ^ (word)body)) {
+        return((ptr_t)((word *)ohdr + BYTES_TO_WORDS(gc_sz)-1));
+    }
+    if (((word *)body)[SIMPLE_ROUNDED_UP_WORDS(ohdr -> oh_sz)]
+        != (END_FLAG ^ (word)body)) {
+        return((ptr_t)((word *)body + SIMPLE_ROUNDED_UP_WORDS(ohdr->oh_sz)));
+    }
+    return(0);
+  }
+#endif /* !SHORT_DBG_HDRS */
+
+STATIC GC_describe_type_fn GC_describe_type_fns[MAXOBJKINDS] = {0};
+
+GC_API void GC_CALL GC_register_describe_type_fn(int kind,
+                                                 GC_describe_type_fn fn)
+{
+  GC_describe_type_fns[kind] = fn;
+}
+
+#define GET_OH_LINENUM(ohdr) ((int)(ohdr)->oh_int)
+
+#ifndef SHORT_DBG_HDRS
+# define IF_NOT_SHORTDBG_HDRS(x) x
+# define COMMA_IFNOT_SHORTDBG_HDRS(x) /* comma */, x
+#else
+# define IF_NOT_SHORTDBG_HDRS(x) /* empty */
+# define COMMA_IFNOT_SHORTDBG_HDRS(x) /* empty */
+#endif
+
+/* Print a human-readable description of the object to stderr.          */
+/* p points to somewhere inside an object with the debugging info.      */
+STATIC void GC_print_obj(ptr_t p)
+{
+    oh * ohdr = (oh *)GC_base(p);
+    ptr_t q;
+    hdr * hhdr;
+    int kind;
+    char *kind_str;
+    char buffer[GC_TYPE_DESCR_LEN + 1];
+
+    GC_ASSERT(I_DONT_HOLD_LOCK());
+#   ifdef LINT2
+      if (!ohdr) ABORT("Invalid GC_print_obj argument");
+#   endif
+
+    q = (ptr_t)(ohdr + 1);
+    /* Print a type description for the object whose client-visible     */
+    /* address is q.                                                    */
+    hhdr = GC_find_header(q);
+    kind = hhdr -> hb_obj_kind;
+    if (0 != GC_describe_type_fns[kind] && GC_is_marked(ohdr)) {
+        /* This should preclude free list objects except with   */
+        /* thread-local allocation.                             */
+        buffer[GC_TYPE_DESCR_LEN] = 0;
+        (GC_describe_type_fns[kind])(q, buffer);
+        GC_ASSERT(buffer[GC_TYPE_DESCR_LEN] == 0);
+        kind_str = buffer;
+    } else {
+        switch(kind) {
+          case PTRFREE:
+            kind_str = "PTRFREE";
+            break;
+          case NORMAL:
+            kind_str = "NORMAL";
+            break;
+          case UNCOLLECTABLE:
+            kind_str = "UNCOLLECTABLE";
+            break;
+#         ifdef ATOMIC_UNCOLLECTABLE
+            case AUNCOLLECTABLE:
+              kind_str = "ATOMIC_UNCOLLECTABLE";
+              break;
+#         endif
+          case STUBBORN:
+            kind_str = "STUBBORN";
+            break;
+          default:
+            kind_str = NULL;
+                /* The alternative is to use snprintf(buffer) but it is */
+                /* not quite portable (see vsnprintf in misc.c).        */
+        }
+    }
+
+    if (NULL != kind_str) {
+        GC_err_printf("%p (%s:%d," IF_NOT_SHORTDBG_HDRS(" sz=%lu,") " %s)\n",
+                      (ptr_t)ohdr + sizeof(oh),
+                      ohdr->oh_string, GET_OH_LINENUM(ohdr) /*, */
+                      COMMA_IFNOT_SHORTDBG_HDRS((unsigned long)ohdr->oh_sz),
+                      kind_str);
+    } else {
+        GC_err_printf("%p (%s:%d," IF_NOT_SHORTDBG_HDRS(" sz=%lu,")
+                      " kind=%d descr=0x%lx)\n", (ptr_t)ohdr + sizeof(oh),
+                      ohdr->oh_string, GET_OH_LINENUM(ohdr) /*, */
+                      COMMA_IFNOT_SHORTDBG_HDRS((unsigned long)ohdr->oh_sz),
+                      kind, (unsigned long)hhdr->hb_descr);
+    }
+    PRINT_CALL_CHAIN(ohdr);
+}
+
+STATIC void GC_debug_print_heap_obj_proc(ptr_t p)
+{
+    GC_ASSERT(I_DONT_HOLD_LOCK());
+    if (GC_HAS_DEBUG_INFO(p)) {
+        GC_print_obj(p);
+    } else {
+        GC_default_print_heap_obj_proc(p);
+    }
+}
+
+#ifndef SHORT_DBG_HDRS
+  /* Use GC_err_printf and friends to print a description of the object */
+  /* whose client-visible address is p, and which was smashed at        */
+  /* clobbered_addr.                                                    */
+  STATIC void GC_print_smashed_obj(const char *msg, ptr_t p,
+                                   ptr_t clobbered_addr)
+  {
+    oh * ohdr = (oh *)GC_base(p);
+
+    GC_ASSERT(I_DONT_HOLD_LOCK());
+#   ifdef LINT2
+      if (!ohdr) ABORT("Invalid GC_print_smashed_obj argument");
+#   endif
+    if ((word)clobbered_addr <= (word)(&ohdr->oh_sz)
+        || ohdr -> oh_string == 0) {
+        GC_err_printf(
+                "%s %p in or near object at %p(<smashed>, appr. sz = %lu)\n",
+                msg, clobbered_addr, p,
+                (unsigned long)(GC_size((ptr_t)ohdr) - DEBUG_BYTES));
+    } else {
+        GC_err_printf("%s %p in or near object at %p (%s:%d, sz=%lu)\n",
+                msg, clobbered_addr, p,
+                (word)(ohdr -> oh_string) < HBLKSIZE ? "(smashed string)" :
+                ohdr -> oh_string[0] == '\0' ? "EMPTY(smashed?)" :
+                                                ohdr -> oh_string,
+                GET_OH_LINENUM(ohdr), (unsigned long)(ohdr -> oh_sz));
+        PRINT_CALL_CHAIN(ohdr);
+    }
+  }
+#endif
+
+#ifndef SHORT_DBG_HDRS
+  STATIC void GC_check_heap_proc (void);
+  STATIC void GC_print_all_smashed_proc (void);
+#else
+  STATIC void GC_do_nothing(void) {}
+#endif
+
+STATIC void GC_start_debugging_inner(void)
+{
+  GC_ASSERT(I_HOLD_LOCK());
+# ifndef SHORT_DBG_HDRS
+    GC_check_heap = GC_check_heap_proc;
+    GC_print_all_smashed = GC_print_all_smashed_proc;
+# else
+    GC_check_heap = GC_do_nothing;
+    GC_print_all_smashed = GC_do_nothing;
+# endif
+  GC_print_heap_obj = GC_debug_print_heap_obj_proc;
+  GC_debugging_started = TRUE;
+  GC_register_displacement_inner((word)sizeof(oh));
+}
+
+GC_INNER void GC_start_debugging(void)
+{
+  DCL_LOCK_STATE;
+
+  LOCK();
+  GC_start_debugging_inner();
+  UNLOCK();
+}
+
+size_t GC_debug_header_size = sizeof(oh);
+
+GC_API void GC_CALL GC_debug_register_displacement(size_t offset)
+{
+  DCL_LOCK_STATE;
+
+  LOCK();
+  GC_register_displacement_inner(offset);
+  GC_register_displacement_inner((word)sizeof(oh) + offset);
+  UNLOCK();
+}
+
+#ifdef GC_ADD_CALLER
+# if defined(HAVE_DLADDR) && defined(GC_RETURN_ADDR_PARENT)
+#   include <dlfcn.h>
+
+    STATIC void GC_caller_func_offset(word ad, const char **symp, int *offp)
+    {
+      Dl_info caller;
+
+      if (ad && dladdr((void *)ad, &caller) && caller.dli_sname != NULL) {
+        *symp = caller.dli_sname;
+        *offp = (int)((char *)ad - (char *)caller.dli_saddr);
+      }
+      if (NULL == *symp) {
+        *symp = "unknown";
+      }
+    }
+# else
+#   define GC_caller_func_offset(ad, symp, offp) (void)(*(symp) = "unknown")
+# endif
+#endif /* GC_ADD_CALLER */
+
+GC_API void * GC_CALL GC_debug_malloc(size_t lb, GC_EXTRA_PARAMS)
+{
+    void * result;
+
+    /* Note that according to malloc() specification, if size is 0 then */
+    /* malloc() returns either NULL, or a unique pointer value that can */
+    /* later be successfully passed to free(). We always do the latter. */
+    result = GC_malloc(lb + DEBUG_BYTES);
+#   ifdef GC_ADD_CALLER
+      if (s == NULL) {
+        GC_caller_func_offset(ra, &s, &i);
+      }
+#   endif
+    if (result == 0) {
+        GC_err_printf("GC_debug_malloc(%lu) returning NULL (%s:%d)\n",
+                      (unsigned long)lb, s, i);
+        return(0);
+    }
+    if (!GC_debugging_started) {
+        GC_start_debugging();
+    }
+    ADD_CALL_CHAIN(result, ra);
+    return (GC_store_debug_info(result, (word)lb, s, i));
+}
+
+GC_API void * GC_CALL GC_debug_malloc_ignore_off_page(size_t lb,
+                                                      GC_EXTRA_PARAMS)
+{
+    void * result = GC_malloc_ignore_off_page(lb + DEBUG_BYTES);
+
+    if (result == 0) {
+        GC_err_printf("GC_debug_malloc_ignore_off_page(%lu)"
+                      " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
+        return(0);
+    }
+    if (!GC_debugging_started) {
+        GC_start_debugging();
+    }
+    ADD_CALL_CHAIN(result, ra);
+    return (GC_store_debug_info(result, (word)lb, s, i));
+}
+
+GC_API void * GC_CALL GC_debug_malloc_atomic_ignore_off_page(size_t lb,
+                                                             GC_EXTRA_PARAMS)
+{
+    void * result = GC_malloc_atomic_ignore_off_page(lb + DEBUG_BYTES);
+
+    if (result == 0) {
+        GC_err_printf("GC_debug_malloc_atomic_ignore_off_page(%lu)"
+                      " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
+        return(0);
+    }
+    if (!GC_debugging_started) {
+        GC_start_debugging();
+    }
+    ADD_CALL_CHAIN(result, ra);
+    return (GC_store_debug_info(result, (word)lb, s, i));
+}
+
+#ifdef DBG_HDRS_ALL
+  /* An allocation function for internal use.  Normally internally      */
+  /* allocated objects do not have debug information.  But in this      */
+  /* case, we need to make sure that all objects have debug headers.    */
+  /* We assume debugging was started in collector initialization, and   */
+  /* we already hold the GC lock.                                       */
+  GC_INNER void * GC_debug_generic_malloc_inner(size_t lb, int k)
+  {
+    void * result = GC_generic_malloc_inner(lb + DEBUG_BYTES, k);
+
+    if (result == 0) {
+        GC_err_printf("GC internal allocation (%lu bytes) returning NULL\n",
+                       (unsigned long) lb);
+        return(0);
+    }
+    if (!GC_debugging_started) {
+        GC_start_debugging_inner();
+    }
+    ADD_CALL_CHAIN(result, GC_RETURN_ADDR);
+    return (GC_store_debug_info_inner(result, (word)lb, "INTERNAL", 0));
+  }
+
+  GC_INNER void * GC_debug_generic_malloc_inner_ignore_off_page(size_t lb,
+                                                                int k)
+  {
+    void * result = GC_generic_malloc_inner_ignore_off_page(
+                                                lb + DEBUG_BYTES, k);
+
+    if (result == 0) {
+        GC_err_printf("GC internal allocation (%lu bytes) returning NULL\n",
+                       (unsigned long) lb);
+        return(0);
+    }
+    if (!GC_debugging_started) {
+        GC_start_debugging_inner();
+    }
+    ADD_CALL_CHAIN(result, GC_RETURN_ADDR);
+    return (GC_store_debug_info_inner(result, (word)lb, "INTERNAL", 0));
+  }
+#endif /* DBG_HDRS_ALL */
+
+#ifdef STUBBORN_ALLOC
+  GC_API void * GC_CALL GC_debug_malloc_stubborn(size_t lb, GC_EXTRA_PARAMS)
+  {
+    void * result = GC_malloc_stubborn(lb + DEBUG_BYTES);
+
+    if (result == 0) {
+        GC_err_printf("GC_debug_malloc_stubborn(%lu)"
+                      " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
+        return(0);
+    }
+    if (!GC_debugging_started) {
+        GC_start_debugging();
+    }
+    ADD_CALL_CHAIN(result, ra);
+    return (GC_store_debug_info(result, (word)lb, s, i));
+  }
+
+  GC_API void GC_CALL GC_debug_change_stubborn(const void *p)
+  {
+    const void * q = GC_base_C(p);
+    hdr * hhdr;
+
+    if (q == 0) {
+        ABORT_ARG1("GC_debug_change_stubborn: bad arg", ": %p", p);
+    }
+    hhdr = HDR(q);
+    if (hhdr -> hb_obj_kind != STUBBORN) {
+        ABORT_ARG1("GC_debug_change_stubborn: arg not stubborn", ": %p", p);
+    }
+    GC_change_stubborn(q);
+  }
+
+  GC_API void GC_CALL GC_debug_end_stubborn_change(const void *p)
+  {
+    const void * q = GC_base_C(p);
+    hdr * hhdr;
+
+    if (q == 0) {
+        ABORT_ARG1("GC_debug_end_stubborn_change: bad arg", ": %p", p);
+    }
+    hhdr = HDR(q);
+    if (hhdr -> hb_obj_kind != STUBBORN) {
+        ABORT_ARG1("GC_debug_end_stubborn_change: arg not stubborn",
+                   ": %p", p);
+    }
+    GC_end_stubborn_change(q);
+  }
+
+#else /* !STUBBORN_ALLOC */
+
+  GC_API void * GC_CALL GC_debug_malloc_stubborn(size_t lb, GC_EXTRA_PARAMS)
+  {
+    return GC_debug_malloc(lb, OPT_RA s, i);
+  }
+
+  GC_API void GC_CALL GC_debug_change_stubborn(
+                                const void * p GC_ATTR_UNUSED) {}
+
+  GC_API void GC_CALL GC_debug_end_stubborn_change(
+                                const void * p GC_ATTR_UNUSED) {}
+#endif /* !STUBBORN_ALLOC */
+
+GC_API void * GC_CALL GC_debug_malloc_atomic(size_t lb, GC_EXTRA_PARAMS)
+{
+    void * result = GC_malloc_atomic(lb + DEBUG_BYTES);
+
+    if (result == 0) {
+        GC_err_printf("GC_debug_malloc_atomic(%lu) returning NULL (%s:%d)\n",
+                      (unsigned long)lb, s, i);
+        return(0);
+    }
+    if (!GC_debugging_started) {
+        GC_start_debugging();
+    }
+    ADD_CALL_CHAIN(result, ra);
+    return (GC_store_debug_info(result, (word)lb, s, i));
+}
+
+GC_API char * GC_CALL GC_debug_strdup(const char *str, GC_EXTRA_PARAMS)
+{
+  char *copy;
+  size_t lb;
+  if (str == NULL) {
+    if (GC_find_leak)
+      GC_err_printf("strdup(NULL) behavior is undefined\n");
+    return NULL;
+  }
+
+  lb = strlen(str) + 1;
+  copy = GC_debug_malloc_atomic(lb, OPT_RA s, i);
+  if (copy == NULL) {
+#   ifndef MSWINCE
+      errno = ENOMEM;
+#   endif
+    return NULL;
+  }
+  BCOPY(str, copy, lb);
+  return copy;
+}
+
+GC_API char * GC_CALL GC_debug_strndup(const char *str, size_t size,
+                                       GC_EXTRA_PARAMS)
+{
+  char *copy;
+  size_t len = strlen(str); /* str is expected to be non-NULL  */
+  if (len > size)
+    len = size;
+  copy = GC_debug_malloc_atomic(len + 1, OPT_RA s, i);
+  if (copy == NULL) {
+#   ifndef MSWINCE
+      errno = ENOMEM;
+#   endif
+    return NULL;
+  }
+  BCOPY(str, copy, len);
+  copy[len] = '\0';
+  return copy;
+}
+
+#ifdef GC_REQUIRE_WCSDUP
+# include <wchar.h> /* for wcslen() */
+
+  GC_API wchar_t * GC_CALL GC_debug_wcsdup(const wchar_t *str, GC_EXTRA_PARAMS)
+  {
+    size_t lb = (wcslen(str) + 1) * sizeof(wchar_t);
+    wchar_t *copy = GC_debug_malloc_atomic(lb, OPT_RA s, i);
+    if (copy == NULL) {
+#     ifndef MSWINCE
+        errno = ENOMEM;
+#     endif
+      return NULL;
+    }
+    BCOPY(str, copy, lb);
+    return copy;
+  }
+#endif /* GC_REQUIRE_WCSDUP */
+
+GC_API void * GC_CALL GC_debug_malloc_uncollectable(size_t lb,
+                                                    GC_EXTRA_PARAMS)
+{
+    void * result = GC_malloc_uncollectable(lb + UNCOLLECTABLE_DEBUG_BYTES);
+
+    if (result == 0) {
+        GC_err_printf("GC_debug_malloc_uncollectable(%lu)"
+                      " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
+        return(0);
+    }
+    if (!GC_debugging_started) {
+        GC_start_debugging();
+    }
+    ADD_CALL_CHAIN(result, ra);
+    return (GC_store_debug_info(result, (word)lb, s, i));
+}
+
+#ifdef ATOMIC_UNCOLLECTABLE
+  GC_API void * GC_CALL GC_debug_malloc_atomic_uncollectable(size_t lb,
+                                                             GC_EXTRA_PARAMS)
+  {
+    void * result =
+        GC_malloc_atomic_uncollectable(lb + UNCOLLECTABLE_DEBUG_BYTES);
+
+    if (result == 0) {
+        GC_err_printf("GC_debug_malloc_atomic_uncollectable(%lu)"
+                      " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
+        return(0);
+    }
+    if (!GC_debugging_started) {
+        GC_start_debugging();
+    }
+    ADD_CALL_CHAIN(result, ra);
+    return (GC_store_debug_info(result, (word)lb, s, i));
+  }
+#endif /* ATOMIC_UNCOLLECTABLE */
+
+#ifndef GC_FREED_MEM_MARKER
+# if CPP_WORDSZ == 32
+#   define GC_FREED_MEM_MARKER 0xdeadbeef
+# else
+#   define GC_FREED_MEM_MARKER GC_WORD_C(0xEFBEADDEdeadbeef)
+# endif
+#endif
+
+GC_API void GC_CALL GC_debug_free(void * p)
+{
+    ptr_t base;
+    if (0 == p) return;
+
+    base = GC_base(p);
+    if (base == 0) {
+      ABORT_ARG1("Invalid pointer passed to free()", ": %p", p);
+    }
+    if ((ptr_t)p - (ptr_t)base != sizeof(oh)) {
+      GC_err_printf(
+               "GC_debug_free called on pointer %p w/o debugging info\n", p);
+    } else {
+#     ifndef SHORT_DBG_HDRS
+        ptr_t clobbered = GC_check_annotated_obj((oh *)base);
+        word sz = GC_size(base);
+        if (clobbered != 0) {
+          GC_have_errors = TRUE;
+          if (((oh *)base) -> oh_sz == sz) {
+            GC_print_smashed_obj(
+                  "GC_debug_free: found previously deallocated (?) object at",
+                  p, clobbered);
+            return; /* ignore double free */
+          } else {
+            GC_print_smashed_obj("GC_debug_free: found smashed location at",
+                                 p, clobbered);
+          }
+        }
+        /* Invalidate size (mark the object as deallocated) */
+        ((oh *)base) -> oh_sz = sz;
+#     endif /* SHORT_DBG_HDRS */
+    }
+    if (GC_find_leak
+#       ifndef SHORT_DBG_HDRS
+          && ((ptr_t)p - (ptr_t)base != sizeof(oh) || !GC_findleak_delay_free)
+#       endif
+        ) {
+      GC_free(base);
+    } else {
+      hdr * hhdr = HDR(p);
+      if (hhdr -> hb_obj_kind == UNCOLLECTABLE
+#         ifdef ATOMIC_UNCOLLECTABLE
+            || hhdr -> hb_obj_kind == AUNCOLLECTABLE
+#         endif
+          ) {
+        GC_free(base);
+      } else {
+        size_t i;
+        size_t obj_sz = BYTES_TO_WORDS(hhdr -> hb_sz - sizeof(oh));
+
+        for (i = 0; i < obj_sz; ++i)
+          ((word *)p)[i] = GC_FREED_MEM_MARKER;
+        GC_ASSERT((word *)p + i == (word *)(base + hhdr -> hb_sz));
+      }
+    } /* !GC_find_leak */
+}
+
+#if defined(THREADS) && defined(DBG_HDRS_ALL)
+  /* Used internally; we assume it's called correctly.    */
+  GC_INNER void GC_debug_free_inner(void * p)
+  {
+    ptr_t base = GC_base(p);
+    GC_ASSERT((ptr_t)p - (ptr_t)base == sizeof(oh));
+#   ifdef LINT2
+      if (!base) ABORT("Invalid GC_debug_free_inner argument");
+#   endif
+#   ifndef SHORT_DBG_HDRS
+      /* Invalidate size */
+      ((oh *)base) -> oh_sz = GC_size(base);
+#   endif
+    GC_free_inner(base);
+  }
+#endif
+
+GC_API void * GC_CALL GC_debug_realloc(void * p, size_t lb, GC_EXTRA_PARAMS)
+{
+    void * base;
+    void * result;
+    hdr * hhdr;
+
+    if (p == 0) {
+      return GC_debug_malloc(lb, OPT_RA s, i);
+    }
+#   ifdef GC_ADD_CALLER
+      if (s == NULL) {
+        GC_caller_func_offset(ra, &s, &i);
+      }
+#   endif
+    base = GC_base(p);
+    if (base == 0) {
+        ABORT_ARG1("Invalid pointer passed to realloc()", ": %p", p);
+    }
+    if ((ptr_t)p - (ptr_t)base != sizeof(oh)) {
+        GC_err_printf(
+              "GC_debug_realloc called on pointer %p w/o debugging info\n", p);
+        return(GC_realloc(p, lb));
+    }
+    hhdr = HDR(base);
+    switch (hhdr -> hb_obj_kind) {
+#    ifdef STUBBORN_ALLOC
+      case STUBBORN:
+        result = GC_debug_malloc_stubborn(lb, OPT_RA s, i);
+        break;
+#    endif
+      case NORMAL:
+        result = GC_debug_malloc(lb, OPT_RA s, i);
+        break;
+      case PTRFREE:
+        result = GC_debug_malloc_atomic(lb, OPT_RA s, i);
+        break;
+      case UNCOLLECTABLE:
+        result = GC_debug_malloc_uncollectable(lb, OPT_RA s, i);
+        break;
+#    ifdef ATOMIC_UNCOLLECTABLE
+      case AUNCOLLECTABLE:
+        result = GC_debug_malloc_atomic_uncollectable(lb, OPT_RA s, i);
+        break;
+#    endif
+      default:
+        result = NULL; /* initialized to prevent warning. */
+        ABORT_RET("GC_debug_realloc: encountered bad kind");
+    }
+
+    if (result != NULL) {
+      size_t old_sz;
+#     ifdef SHORT_DBG_HDRS
+        old_sz = GC_size(base) - sizeof(oh);
+#     else
+        old_sz = ((oh *)base) -> oh_sz;
+#     endif
+      BCOPY(p, result, old_sz < lb ? old_sz : lb);
+      GC_debug_free(p);
+    }
+    return(result);
+}
+
+#ifndef SHORT_DBG_HDRS
+
+/* List of smashed (clobbered) locations.  We defer printing these,     */
+/* since we can't always print them nicely with the allocation lock     */
+/* held.  We put them here instead of in GC_arrays, since it may be     */
+/* useful to be able to look at them with the debugger.                 */
+#ifndef MAX_SMASHED
+# define MAX_SMASHED 20
+#endif
+STATIC ptr_t GC_smashed[MAX_SMASHED] = {0};
+STATIC unsigned GC_n_smashed = 0;
+
+STATIC void GC_add_smashed(ptr_t smashed)
+{
+    GC_ASSERT(GC_is_marked(GC_base(smashed)));
+    /* FIXME: Prevent adding an object while printing smashed list.     */
+    GC_smashed[GC_n_smashed] = smashed;
+    if (GC_n_smashed < MAX_SMASHED - 1) ++GC_n_smashed;
+      /* In case of overflow, we keep the first MAX_SMASHED-1   */
+      /* entries plus the last one.                             */
+    GC_have_errors = TRUE;
+}
+
+/* Print all objects on the list.  Clear the list.      */
+STATIC void GC_print_all_smashed_proc(void)
+{
+    unsigned i;
+
+    GC_ASSERT(I_DONT_HOLD_LOCK());
+    if (GC_n_smashed == 0) return;
+    GC_err_printf("GC_check_heap_block: found %u smashed heap objects:\n",
+                  GC_n_smashed);
+    for (i = 0; i < GC_n_smashed; ++i) {
+        ptr_t base = (ptr_t)GC_base(GC_smashed[i]);
+
+#       ifdef LINT2
+          if (!base) ABORT("Invalid GC_smashed element");
+#       endif
+        GC_print_smashed_obj("", base + sizeof(oh), GC_smashed[i]);
+        GC_smashed[i] = 0;
+    }
+    GC_n_smashed = 0;
+}
+
+/* Check all marked objects in the given block for validity     */
+/* Avoid GC_apply_to_each_object for performance reasons.       */
+STATIC void GC_check_heap_block(struct hblk *hbp, word dummy GC_ATTR_UNUSED)
+{
+    struct hblkhdr * hhdr = HDR(hbp);
+    size_t sz = hhdr -> hb_sz;
+    size_t bit_no;
+    char *p, *plim;
+
+    p = hbp->hb_body;
+    if (sz > MAXOBJBYTES) {
+      plim = p;
+    } else {
+      plim = hbp->hb_body + HBLKSIZE - sz;
+    }
+    /* go through all words in block */
+    for (bit_no = 0; (word)p <= (word)plim;
+         bit_no += MARK_BIT_OFFSET(sz), p += sz) {
+      if (mark_bit_from_hdr(hhdr, bit_no) && GC_HAS_DEBUG_INFO((ptr_t)p)) {
+        ptr_t clobbered = GC_check_annotated_obj((oh *)p);
+        if (clobbered != 0)
+          GC_add_smashed(clobbered);
+      }
+    }
+}
+
+/* This assumes that all accessible objects are marked, and that        */
+/* I hold the allocation lock.  Normally called by collector.           */
+STATIC void GC_check_heap_proc(void)
+{
+  GC_STATIC_ASSERT((sizeof(oh) & (GRANULE_BYTES - 1)) == 0);
+  /* FIXME: Should we check for twice that alignment?   */
+  GC_apply_to_all_blocks(GC_check_heap_block, 0);
+}
+
+GC_INNER GC_bool GC_check_leaked(ptr_t base)
+{
+  size_t i;
+  size_t obj_sz;
+  word *p;
+
+  if (
+#     if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH)
+        (*(word *)base & 1) != 0 &&
+#     endif
+      GC_has_other_debug_info(base) >= 0)
+    return TRUE; /* object has leaked */
+
+  /* Validate freed object's content. */
+  p = (word *)(base + sizeof(oh));
+  obj_sz = BYTES_TO_WORDS(HDR(base)->hb_sz - sizeof(oh));
+  for (i = 0; i < obj_sz; ++i)
+    if (p[i] != GC_FREED_MEM_MARKER) {
+        GC_set_mark_bit(base); /* do not reclaim it in this cycle */
+        GC_add_smashed((ptr_t)(&p[i])); /* alter-after-free detected */
+        break; /* don't report any other smashed locations in the object */
+    }
+
+  return FALSE; /* GC_debug_free() has been called */
+}
+
+#endif /* !SHORT_DBG_HDRS */
+
+#ifndef GC_NO_FINALIZATION
+
+struct closure {
+    GC_finalization_proc cl_fn;
+    void * cl_data;
+};
+
+STATIC void * GC_make_closure(GC_finalization_proc fn, void * data)
+{
+    struct closure * result =
+#   ifdef DBG_HDRS_ALL
+      (struct closure *) GC_debug_malloc(sizeof (struct closure),
+                                         GC_EXTRAS);
+#   else
+      (struct closure *) GC_malloc(sizeof (struct closure));
+#   endif
+    if (result != 0) {
+      result -> cl_fn = fn;
+      result -> cl_data = data;
+    }
+    return((void *)result);
+}
+
+/* An auxiliary fns to make finalization work correctly with displaced  */
+/* pointers introduced by the debugging allocators.                     */
+STATIC void GC_CALLBACK GC_debug_invoke_finalizer(void * obj, void * data)
+{
+    struct closure * cl = (struct closure *) data;
+    (*(cl -> cl_fn))((void *)((char *)obj + sizeof(oh)), cl -> cl_data);
+}
+
+/* Special finalizer_proc value to detect GC_register_finalizer() failure. */
+#define OFN_UNSET (GC_finalization_proc)(signed_word)-1
+
+/* Set ofn and ocd to reflect the values we got back.   */
+static void store_old(void *obj, GC_finalization_proc my_old_fn,
+                      struct closure *my_old_cd, GC_finalization_proc *ofn,
+                      void **ocd)
+{
+    if (0 != my_old_fn) {
+      if (my_old_fn == OFN_UNSET) {
+        /* register_finalizer() failed; (*ofn) and (*ocd) are unchanged. */
+        return;
+      }
+      if (my_old_fn != GC_debug_invoke_finalizer) {
+        GC_err_printf("Debuggable object at %p had a non-debug finalizer\n",
+                      obj);
+        /* This should probably be fatal. */
+      } else {
+        if (ofn) *ofn = my_old_cd -> cl_fn;
+        if (ocd) *ocd = my_old_cd -> cl_data;
+      }
+    } else {
+      if (ofn) *ofn = 0;
+      if (ocd) *ocd = 0;
+    }
+}
+
+GC_API void GC_CALL GC_debug_register_finalizer(void * obj,
+                                        GC_finalization_proc fn,
+                                        void * cd, GC_finalization_proc *ofn,
+                                        void * *ocd)
+{
+    GC_finalization_proc my_old_fn = OFN_UNSET;
+    void * my_old_cd;
+    ptr_t base = GC_base(obj);
+    if (0 == base) {
+        /* We won't collect it, hence finalizer wouldn't be run. */
+        if (ocd) *ocd = 0;
+        if (ofn) *ofn = 0;
+        return;
+    }
+    if ((ptr_t)obj - base != sizeof(oh)) {
+        GC_err_printf("GC_debug_register_finalizer called with"
+                      " non-base-pointer %p\n", obj);
+    }
+    if (0 == fn) {
+      GC_register_finalizer(base, 0, 0, &my_old_fn, &my_old_cd);
+    } else {
+      cd = GC_make_closure(fn, cd);
+      if (cd == 0) return; /* out of memory */
+      GC_register_finalizer(base, GC_debug_invoke_finalizer,
+                            cd, &my_old_fn, &my_old_cd);
+    }
+    store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd);
+}
+
+GC_API void GC_CALL GC_debug_register_finalizer_no_order
+                                    (void * obj, GC_finalization_proc fn,
+                                     void * cd, GC_finalization_proc *ofn,
+                                     void * *ocd)
+{
+    GC_finalization_proc my_old_fn = OFN_UNSET;
+    void * my_old_cd;
+    ptr_t base = GC_base(obj);
+    if (0 == base) {
+        /* We won't collect it, hence finalizer wouldn't be run. */
+        if (ocd) *ocd = 0;
+        if (ofn) *ofn = 0;
+        return;
+    }
+    if ((ptr_t)obj - base != sizeof(oh)) {
+        GC_err_printf("GC_debug_register_finalizer_no_order called with"
+                      " non-base-pointer %p\n", obj);
+    }
+    if (0 == fn) {
+      GC_register_finalizer_no_order(base, 0, 0, &my_old_fn, &my_old_cd);
+    } else {
+      cd = GC_make_closure(fn, cd);
+      if (cd == 0) return; /* out of memory */
+      GC_register_finalizer_no_order(base, GC_debug_invoke_finalizer,
+                                     cd, &my_old_fn, &my_old_cd);
+    }
+    store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd);
+}
+
+GC_API void GC_CALL GC_debug_register_finalizer_unreachable
+                                    (void * obj, GC_finalization_proc fn,
+                                     void * cd, GC_finalization_proc *ofn,
+                                     void * *ocd)
+{
+    GC_finalization_proc my_old_fn = OFN_UNSET;
+    void * my_old_cd;
+    ptr_t base = GC_base(obj);
+    if (0 == base) {
+        /* We won't collect it, hence finalizer wouldn't be run. */
+        if (ocd) *ocd = 0;
+        if (ofn) *ofn = 0;
+        return;
+    }
+    if ((ptr_t)obj - base != sizeof(oh)) {
+        GC_err_printf("GC_debug_register_finalizer_unreachable called with"
+                      " non-base-pointer %p\n", obj);
+    }
+    if (0 == fn) {
+      GC_register_finalizer_unreachable(base, 0, 0, &my_old_fn, &my_old_cd);
+    } else {
+      cd = GC_make_closure(fn, cd);
+      if (cd == 0) return; /* out of memory */
+      GC_register_finalizer_unreachable(base, GC_debug_invoke_finalizer,
+                                        cd, &my_old_fn, &my_old_cd);
+    }
+    store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd);
+}
+
+GC_API void GC_CALL GC_debug_register_finalizer_ignore_self
+                                    (void * obj, GC_finalization_proc fn,
+                                     void * cd, GC_finalization_proc *ofn,
+                                     void * *ocd)
+{
+    GC_finalization_proc my_old_fn = OFN_UNSET;
+    void * my_old_cd;
+    ptr_t base = GC_base(obj);
+    if (0 == base) {
+        /* We won't collect it, hence finalizer wouldn't be run. */
+        if (ocd) *ocd = 0;
+        if (ofn) *ofn = 0;
+        return;
+    }
+    if ((ptr_t)obj - base != sizeof(oh)) {
+        GC_err_printf("GC_debug_register_finalizer_ignore_self called with"
+                      " non-base-pointer %p\n", obj);
+    }
+    if (0 == fn) {
+      GC_register_finalizer_ignore_self(base, 0, 0, &my_old_fn, &my_old_cd);
+    } else {
+      cd = GC_make_closure(fn, cd);
+      if (cd == 0) return; /* out of memory */
+      GC_register_finalizer_ignore_self(base, GC_debug_invoke_finalizer,
+                                        cd, &my_old_fn, &my_old_cd);
+    }
+    store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd);
+}
+
+#endif /* !GC_NO_FINALIZATION */
+
+GC_API void * GC_CALL GC_debug_malloc_replacement(size_t lb)
+{
+    return GC_debug_malloc(lb, GC_DBG_EXTRAS);
+}
+
+GC_API void * GC_CALL GC_debug_realloc_replacement(void *p, size_t lb)
+{
+    return GC_debug_realloc(p, lb, GC_DBG_EXTRAS);
+}

+ 1507 - 0
blitz.mod/bdwgc/dyn_load.c

@@ -0,0 +1,1507 @@
+/*
+ * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1997 by Silicon Graphics.  All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+#include "private/gc_priv.h"
+
+/*
+ * This is incredibly OS specific code for tracking down data sections in
+ * dynamic libraries.  There appears to be no way of doing this quickly
+ * without groveling through undocumented data structures.  We would argue
+ * that this is a bug in the design of the dlopen interface.  THIS CODE
+ * MAY BREAK IN FUTURE OS RELEASES.  If this matters to you, don't hesitate
+ * to let your vendor know ...
+ *
+ * None of this is safe with dlclose and incremental collection.
+ * But then not much of anything is safe in the presence of dlclose.
+ */
+
+#if !defined(MACOS) && !defined(_WIN32_WCE) && !defined(__CC_ARM)
+# include <sys/types.h>
+#endif
+
+/* BTL: avoid circular redefinition of dlopen if GC_SOLARIS_THREADS defined */
+#undef GC_MUST_RESTORE_REDEFINED_DLOPEN
+#if defined(GC_PTHREADS) && !defined(GC_NO_DLOPEN) \
+    && !defined(GC_NO_THREAD_REDIRECTS) && !defined(GC_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    */
+  /* real system dlopen() in their implementation. We first remove      */
+  /* gc.h's dlopen definition and restore it later, after GC_dlopen().  */
+# undef dlopen
+# define GC_MUST_RESTORE_REDEFINED_DLOPEN
+#endif /* !GC_NO_DLOPEN */
+
+/* A user-supplied routine (custom filter) that might be called to      */
+/* determine whether a DSO really needs to be scanned by the GC.        */
+/* 0 means no filter installed.  May be unused on some platforms.       */
+/* FIXME: Add filter support for more platforms.                        */
+STATIC GC_has_static_roots_func GC_has_static_roots = 0;
+
+#if (defined(DYNAMIC_LOADING) || defined(MSWIN32) || defined(MSWINCE) \
+    || defined(CYGWIN32)) && !defined(PCR)
+
+#if !defined(SOLARISDL) && !defined(IRIX5) && \
+    !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) && \
+    !(defined(ALPHA) && defined(OSF1)) && \
+    !defined(HPUX) && !(defined(LINUX) && defined(__ELF__)) && \
+    !defined(AIX) && !defined(SCO_ELF) && !defined(DGUX) && \
+    !(defined(FREEBSD) && defined(__ELF__)) && \
+    !(defined(OPENBSD) && (defined(__ELF__) || defined(M68K))) && \
+    !(defined(NETBSD) && defined(__ELF__)) && !defined(HURD) && \
+    !defined(DARWIN) && !defined(CYGWIN32)
+ --> We only know how to find data segments of dynamic libraries for the
+ --> above.  Additional SVR4 variants might not be too
+ --> hard to add.
+#endif
+
+#include <stdio.h>
+#ifdef SOLARISDL
+#   include <sys/elf.h>
+#   include <dlfcn.h>
+#   include <link.h>
+#endif
+
+#if defined(NETBSD)
+#   include <sys/param.h>
+#   include <dlfcn.h>
+#   include <machine/elf_machdep.h>
+#   define ELFSIZE ARCH_ELFSIZE
+#endif
+
+#if defined(OPENBSD)
+# include <sys/param.h>
+# if OpenBSD >= 200519
+#   define HAVE_DL_ITERATE_PHDR
+# endif
+#endif /* OPENBSD */
+
+#if defined(SCO_ELF) || defined(DGUX) || defined(HURD) \
+    || (defined(__ELF__) && (defined(LINUX) || defined(FREEBSD) \
+                             || defined(NETBSD) || defined(OPENBSD)))
+# include <stddef.h>
+# if !defined(OPENBSD) && !defined(PLATFORM_ANDROID)
+    /* OpenBSD does not have elf.h file; link.h below is sufficient.    */
+    /* Exclude Android because linker.h below includes its own version. */
+#   include <elf.h>
+# endif
+# ifdef PLATFORM_ANDROID
+    /* If you don't need the "dynamic loading" feature, you may build   */
+    /* the collector with -D IGNORE_DYNAMIC_LOADING.                    */
+#   ifdef BIONIC_ELFDATA_REDEF_BUG
+      /* Workaround a problem in Bionic (as of Android 4.2) which has   */
+      /* mismatching ELF_DATA definitions in sys/exec_elf.h and         */
+      /* asm/elf.h included from linker.h file (similar to EM_ALPHA).   */
+#     include <asm/elf.h>
+#     include <linux/elf-em.h>
+#     undef ELF_DATA
+#     undef EM_ALPHA
+#   endif
+#   include <link.h>
+#   if !defined(GC_DONT_DEFINE_LINK_MAP)
+      /* link_map and r_debug should be defined explicitly,             */
+      /* as only bionic/linker/linker.h defines them but the header     */
+      /* itself is a C++ one starting from Android 4.3.                 */
+      struct link_map {
+        uintptr_t l_addr;
+        char* l_name;
+        uintptr_t l_ld;
+        struct link_map* l_next;
+        struct link_map* l_prev;
+      };
+      struct r_debug {
+        int32_t r_version;
+        struct link_map* r_map;
+        void (*r_brk)(void);
+        int32_t r_state;
+        uintptr_t r_ldbase;
+      };
+#   endif
+# else
+#   include <link.h>
+# endif
+#endif
+
+/* Newer versions of GNU/Linux define this macro.  We
+ * define it similarly for any ELF systems that don't.  */
+#  ifndef ElfW
+#    if defined(FREEBSD)
+#      if __ELF_WORD_SIZE == 32
+#        define ElfW(type) Elf32_##type
+#      else
+#        define ElfW(type) Elf64_##type
+#      endif
+#    elif defined(NETBSD) || defined(OPENBSD)
+#      if ELFSIZE == 32
+#        define ElfW(type) Elf32_##type
+#      else
+#        define ElfW(type) Elf64_##type
+#      endif
+#    else
+#      if !defined(ELF_CLASS) || ELF_CLASS == ELFCLASS32
+#        define ElfW(type) Elf32_##type
+#      else
+#        define ElfW(type) Elf64_##type
+#      endif
+#    endif
+#  endif
+
+#if defined(SOLARISDL) && !defined(USE_PROC_FOR_LIBRARIES)
+
+#ifdef LINT
+    Elf32_Dyn _DYNAMIC;
+#endif
+
+STATIC struct link_map *
+GC_FirstDLOpenedLinkMap(void)
+{
+    extern ElfW(Dyn) _DYNAMIC;
+    ElfW(Dyn) *dp;
+    static struct link_map * cachedResult = 0;
+    static ElfW(Dyn) *dynStructureAddr = 0;
+                /* BTL: added to avoid Solaris 5.3 ld.so _DYNAMIC bug   */
+
+#   ifdef SUNOS53_SHARED_LIB
+        /* BTL: Avoid the Solaris 5.3 bug that _DYNAMIC isn't being set */
+        /* up properly in dynamically linked .so's. This means we have  */
+        /* to use its value in the set of original object files loaded  */
+        /* at program startup.                                          */
+        if( dynStructureAddr == 0 ) {
+          void* startupSyms = dlopen(0, RTLD_LAZY);
+          dynStructureAddr = (ElfW(Dyn)*)dlsym(startupSyms, "_DYNAMIC");
+        }
+#   else
+        dynStructureAddr = &_DYNAMIC;
+#   endif
+
+    if (dynStructureAddr == 0) {
+        /* _DYNAMIC symbol not resolved. */
+        return(0);
+    }
+    if( cachedResult == 0 ) {
+        int tag;
+        for( dp = ((ElfW(Dyn) *)(&_DYNAMIC)); (tag = dp->d_tag) != 0; dp++ ) {
+            if( tag == DT_DEBUG ) {
+                struct link_map *lm
+                        = ((struct r_debug *)(dp->d_un.d_ptr))->r_map;
+                if( lm != 0 ) cachedResult = lm->l_next; /* might be NULL */
+                break;
+            }
+        }
+    }
+    return cachedResult;
+}
+
+#endif /* SOLARISDL ... */
+
+/* BTL: added to fix circular dlopen definition if GC_SOLARIS_THREADS defined */
+# ifdef GC_MUST_RESTORE_REDEFINED_DLOPEN
+#   define dlopen GC_dlopen
+# endif
+
+# if defined(SOLARISDL)
+
+/* Add dynamic library data sections to the root set.           */
+# if !defined(PCR) && !defined(GC_SOLARIS_THREADS) && defined(THREADS)
+        --> fix mutual exclusion with dlopen
+# endif
+
+# ifndef USE_PROC_FOR_LIBRARIES
+GC_INNER void GC_register_dynamic_libraries(void)
+{
+  struct link_map *lm;
+
+  for (lm = GC_FirstDLOpenedLinkMap(); lm != 0; lm = lm->l_next) {
+        ElfW(Ehdr) * e;
+        ElfW(Phdr) * p;
+        unsigned long offset;
+        char * start;
+        int i;
+
+        e = (ElfW(Ehdr) *) lm->l_addr;
+        p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff));
+        offset = ((unsigned long)(lm->l_addr));
+        for( i = 0; i < (int)e->e_phnum; i++, p++ ) {
+          switch( p->p_type ) {
+            case PT_LOAD:
+              {
+                if( !(p->p_flags & PF_W) ) break;
+                start = ((char *)(p->p_vaddr)) + offset;
+                GC_add_roots_inner(
+                  start,
+                  start + p->p_memsz,
+                  TRUE
+                );
+              }
+              break;
+            default:
+              break;
+          }
+        }
+    }
+}
+
+# endif /* !USE_PROC ... */
+# endif /* SOLARISDL */
+
+#if defined(SCO_ELF) || defined(DGUX) || defined(HURD) \
+    || (defined(__ELF__) && (defined(LINUX) || defined(FREEBSD) \
+                             || defined(NETBSD) || defined(OPENBSD)))
+
+#ifdef USE_PROC_FOR_LIBRARIES
+
+#include <string.h>
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#define MAPS_BUF_SIZE (32*1024)
+
+/* Sort an array of HeapSects by start address.                         */
+/* Unfortunately at least some versions of                              */
+/* Linux qsort end up calling malloc by way of sysconf, and hence can't */
+/* be used in the collector.  Hence we roll our own.  Should be         */
+/* reasonably fast if the array is already mostly sorted, as we expect  */
+/* it to be.                                                            */
+static void sort_heap_sects(struct HeapSect *base, size_t number_of_elements)
+{
+    signed_word n = (signed_word)number_of_elements;
+    signed_word nsorted = 1;
+    signed_word i;
+
+    while (nsorted < n) {
+      while (nsorted < n &&
+             (word)base[nsorted-1].hs_start < (word)base[nsorted].hs_start)
+          ++nsorted;
+      if (nsorted == n) break;
+      GC_ASSERT((word)base[nsorted-1].hs_start > (word)base[nsorted].hs_start);
+      i = nsorted - 1;
+      while (i >= 0 && (word)base[i].hs_start > (word)base[i+1].hs_start) {
+        struct HeapSect tmp = base[i];
+        base[i] = base[i+1];
+        base[i+1] = tmp;
+        --i;
+      }
+      GC_ASSERT((word)base[nsorted-1].hs_start < (word)base[nsorted].hs_start);
+      ++nsorted;
+    }
+}
+
+STATIC word GC_register_map_entries(char *maps)
+{
+    char *prot;
+    char *buf_ptr = maps;
+    ptr_t start, end;
+    unsigned int maj_dev;
+    ptr_t least_ha, greatest_ha;
+    unsigned i;
+    ptr_t datastart;
+
+#   ifdef DATASTART_IS_FUNC
+      static ptr_t datastart_cached = (ptr_t)(word)-1;
+
+      /* Evaluate DATASTART only once.  */
+      if (datastart_cached == (ptr_t)(word)-1) {
+        datastart_cached = (ptr_t)(DATASTART);
+      }
+      datastart = datastart_cached;
+#   else
+      datastart = (ptr_t)(DATASTART);
+#   endif
+
+    GC_ASSERT(I_HOLD_LOCK());
+    sort_heap_sects(GC_our_memory, GC_n_memory);
+    least_ha = GC_our_memory[0].hs_start;
+    greatest_ha = GC_our_memory[GC_n_memory-1].hs_start
+                  + GC_our_memory[GC_n_memory-1].hs_bytes;
+
+    for (;;) {
+        buf_ptr = GC_parse_map_entry(buf_ptr, &start, &end, &prot,
+                                     &maj_dev, 0);
+        if (buf_ptr == NULL) return 1;
+        if (prot[1] == 'w') {
+            /* This is a writable mapping.  Add it to           */
+            /* the root set unless it is already otherwise      */
+            /* accounted for.                                   */
+            if ((word)start <= (word)GC_stackbottom
+                && (word)end >= (word)GC_stackbottom) {
+                /* Stack mapping; discard       */
+                continue;
+            }
+#           ifdef THREADS
+              /* This may fail, since a thread may already be           */
+              /* unregistered, but its thread stack may still be there. */
+              /* That can fail because the stack may disappear while    */
+              /* we're marking.  Thus the marker is, and has to be      */
+              /* prepared to recover from segmentation faults.          */
+
+              if (GC_segment_is_thread_stack(start, end)) continue;
+
+              /* FIXME: NPTL squirrels                                  */
+              /* away pointers in pieces of the stack segment that we   */
+              /* don't scan.  We work around this                       */
+              /* by treating anything allocated by libpthread as        */
+              /* uncollectible, as we do in some other cases.           */
+              /* A specifically identified problem is that              */
+              /* thread stacks contain pointers to dynamic thread       */
+              /* vectors, which may be reused due to thread caching.    */
+              /* They may not be marked if the thread is still live.    */
+              /* This specific instance should be addressed by          */
+              /* INCLUDE_LINUX_THREAD_DESCR, but that doesn't quite     */
+              /* seem to suffice.                                       */
+              /* We currently trace entire thread stacks, if they are   */
+              /* are currently cached but unused.  This is              */
+              /* very suboptimal for performance reasons.               */
+#           endif
+            /* We no longer exclude the main data segment.              */
+            if ((word)end <= (word)least_ha
+                || (word)start >= (word)greatest_ha) {
+              /* The easy case; just trace entire segment */
+              GC_add_roots_inner((char *)start, (char *)end, TRUE);
+              continue;
+            }
+            /* Add sections that don't belong to us. */
+              i = 0;
+              while ((word)(GC_our_memory[i].hs_start
+                                + GC_our_memory[i].hs_bytes) < (word)start)
+                  ++i;
+              GC_ASSERT(i < GC_n_memory);
+              if ((word)GC_our_memory[i].hs_start <= (word)start) {
+                  start = GC_our_memory[i].hs_start
+                          + GC_our_memory[i].hs_bytes;
+                  ++i;
+              }
+              while (i < GC_n_memory
+                     && (word)GC_our_memory[i].hs_start < (word)end
+                     && (word)start < (word)end) {
+                  if ((word)start < (word)GC_our_memory[i].hs_start)
+                    GC_add_roots_inner((char *)start,
+                                       GC_our_memory[i].hs_start, TRUE);
+                  start = GC_our_memory[i].hs_start
+                          + GC_our_memory[i].hs_bytes;
+                  ++i;
+              }
+              if ((word)start < (word)end)
+                  GC_add_roots_inner((char *)start, (char *)end, TRUE);
+        }
+    }
+    return 1;
+}
+
+GC_INNER void GC_register_dynamic_libraries(void)
+{
+    if (!GC_register_map_entries(GC_get_maps()))
+        ABORT("Failed to read /proc for library registration");
+}
+
+/* We now take care of the main data segment ourselves: */
+GC_INNER GC_bool GC_register_main_static_data(void)
+{
+    return FALSE;
+}
+
+# define HAVE_REGISTER_MAIN_STATIC_DATA
+
+#else /* !USE_PROC_FOR_LIBRARIES */
+
+/* The following is the preferred way to walk dynamic libraries */
+/* for glibc 2.2.4+.  Unfortunately, it doesn't work for older  */
+/* versions.  Thanks to Jakub Jelinek for most of the code.     */
+
+#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \
+    || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG)) \
+    || defined(PLATFORM_ANDROID) /* Are others OK here, too? */
+/* We have the header files for a glibc that includes dl_iterate_phdr.  */
+/* It may still not be available in the library on the target system.   */
+/* Thus we also treat it as a weak symbol.                              */
+# define HAVE_DL_ITERATE_PHDR
+# ifdef PLATFORM_ANDROID
+    /* Android headers might have no such definition for some targets.  */
+    int dl_iterate_phdr(int (*cb)(struct dl_phdr_info *, size_t, void *),
+                        void *data);
+# endif
+# pragma weak dl_iterate_phdr
+#endif
+
+#if (defined(FREEBSD) && __FreeBSD__ >= 7)
+  /* On the FreeBSD system, any target system at major version 7 shall   */
+  /* have dl_iterate_phdr; therefore, we need not make it weak as above. */
+# define HAVE_DL_ITERATE_PHDR
+# define DL_ITERATE_PHDR_STRONG
+#endif
+
+#if defined(HAVE_DL_ITERATE_PHDR)
+
+# ifdef PT_GNU_RELRO
+/* Instead of registering PT_LOAD sections directly, we keep them       */
+/* in a temporary list, and filter them by excluding PT_GNU_RELRO       */
+/* segments.  Processing PT_GNU_RELRO sections with                     */
+/* GC_exclude_static_roots instead would be superficially cleaner.  But */
+/* it runs into trouble if a client registers an overlapping segment,   */
+/* which unfortunately seems quite possible.                            */
+
+#   define MAX_LOAD_SEGS MAX_ROOT_SETS
+
+    static struct load_segment {
+      ptr_t start;
+      ptr_t end;
+      /* Room for a second segment if we remove a RELRO segment */
+      /* from the middle.                                       */
+      ptr_t start2;
+      ptr_t end2;
+    } load_segs[MAX_LOAD_SEGS];
+
+    static int n_load_segs;
+# endif /* PT_GNU_RELRO */
+
+STATIC int GC_register_dynlib_callback(struct dl_phdr_info * info,
+                                       size_t size, void * ptr)
+{
+  const ElfW(Phdr) * p;
+  ptr_t start, end;
+  int i;
+
+  /* Make sure struct dl_phdr_info is at least as big as we need.  */
+  if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
+      + sizeof (info->dlpi_phnum))
+    return -1;
+
+  p = info->dlpi_phdr;
+  for( i = 0; i < (int)info->dlpi_phnum; i++, p++ ) {
+    switch( p->p_type ) {
+#     ifdef PT_GNU_RELRO
+        case PT_GNU_RELRO:
+        /* This entry is known to be constant and will eventually be remapped
+           read-only.  However, the address range covered by this entry is
+           typically a subset of a previously encountered "LOAD" segment, so
+           we need to exclude it.  */
+        {
+            int j;
+
+            start = ((ptr_t)(p->p_vaddr)) + info->dlpi_addr;
+            end = start + p->p_memsz;
+            for (j = n_load_segs; --j >= 0; ) {
+              if ((word)start >= (word)load_segs[j].start
+                  && (word)start < (word)load_segs[j].end) {
+                if (load_segs[j].start2 != 0) {
+                  WARN("More than one GNU_RELRO segment per load seg\n",0);
+                } else {
+                  GC_ASSERT((word)end <= (word)load_segs[j].end);
+                  /* Remove from the existing load segment */
+                  load_segs[j].end2 = load_segs[j].end;
+                  load_segs[j].end = start;
+                  load_segs[j].start2 = end;
+                }
+                break;
+              }
+              if (j == 0) WARN("Failed to find PT_GNU_RELRO segment"
+                               " inside PT_LOAD region", 0);
+            }
+        }
+
+        break;
+#     endif
+
+      case PT_LOAD:
+        {
+          GC_has_static_roots_func callback = GC_has_static_roots;
+          if( !(p->p_flags & PF_W) ) break;
+          start = ((char *)(p->p_vaddr)) + info->dlpi_addr;
+          end = start + p->p_memsz;
+
+          if (callback != 0 && !callback(info->dlpi_name, start, p->p_memsz))
+            break;
+#         ifdef PT_GNU_RELRO
+            if (n_load_segs >= MAX_LOAD_SEGS) ABORT("Too many PT_LOAD segs");
+#           if CPP_WORDSZ == 64
+              /* FIXME: GC_push_all eventually does the correct         */
+              /* rounding to the next multiple of ALIGNMENT, so, most   */
+              /* probably, we should remove the corresponding assertion */
+              /* check in GC_add_roots_inner along with this code line. */
+              /* start pointer value may require aligning */
+              start = (ptr_t)((word)start & ~(sizeof(word) - 1));
+#           endif
+            load_segs[n_load_segs].start = start;
+            load_segs[n_load_segs].end = end;
+            load_segs[n_load_segs].start2 = 0;
+            load_segs[n_load_segs].end2 = 0;
+            ++n_load_segs;
+#         else
+            GC_add_roots_inner(start, end, TRUE);
+#         endif /* PT_GNU_RELRO */
+        }
+      break;
+      default:
+        break;
+    }
+  }
+
+  *(int *)ptr = 1;     /* Signal that we were called */
+  return 0;
+}
+
+/* Do we need to separately register the main static data segment? */
+GC_INNER GC_bool GC_register_main_static_data(void)
+{
+# ifdef DL_ITERATE_PHDR_STRONG
+    /* If dl_iterate_phdr is not a weak symbol then don't test against  */
+    /* zero (otherwise a compiler might issue a warning).               */
+    return FALSE;
+# else
+    return (dl_iterate_phdr == 0); /* implicit conversion to function ptr */
+# endif
+}
+
+/* Return TRUE if we succeed, FALSE if dl_iterate_phdr wasn't there. */
+STATIC GC_bool GC_register_dynamic_libraries_dl_iterate_phdr(void)
+{
+  int did_something;
+  if (GC_register_main_static_data())
+    return FALSE;
+
+# ifdef PT_GNU_RELRO
+    {
+      static GC_bool excluded_segs = FALSE;
+      n_load_segs = 0;
+      if (!EXPECT(excluded_segs, TRUE)) {
+        GC_exclude_static_roots_inner((ptr_t)load_segs,
+                                      (ptr_t)load_segs + sizeof(load_segs));
+        excluded_segs = TRUE;
+      }
+    }
+# endif
+
+  did_something = 0;
+  dl_iterate_phdr(GC_register_dynlib_callback, &did_something);
+  if (did_something) {
+#   ifdef PT_GNU_RELRO
+      int i;
+
+      for (i = 0; i < n_load_segs; ++i) {
+        if ((word)load_segs[i].end > (word)load_segs[i].start) {
+          GC_add_roots_inner(load_segs[i].start, load_segs[i].end, TRUE);
+        }
+        if ((word)load_segs[i].end2 > (word)load_segs[i].start2) {
+          GC_add_roots_inner(load_segs[i].start2, load_segs[i].end2, TRUE);
+        }
+      }
+#   endif
+  } else {
+      char *datastart;
+      char *dataend;
+#     ifdef DATASTART_IS_FUNC
+        static ptr_t datastart_cached = (ptr_t)(word)-1;
+
+        /* Evaluate DATASTART only once.  */
+        if (datastart_cached == (ptr_t)(word)-1) {
+          datastart_cached = (ptr_t)(DATASTART);
+        }
+        datastart = (char *)datastart_cached;
+#     else
+        datastart = DATASTART;
+#     endif
+#     ifdef DATAEND_IS_FUNC
+        {
+          static ptr_t dataend_cached = 0;
+          /* Evaluate DATAEND only once. */
+          if (dataend_cached == 0) {
+            dataend_cached = (ptr_t)(DATAEND);
+          }
+          dataend = (char *)dataend_cached;
+        }
+#     else
+        dataend = DATAEND;
+#     endif
+
+      /* dl_iterate_phdr may forget the static data segment in  */
+      /* statically linked executables.                         */
+      GC_add_roots_inner(datastart, dataend, TRUE);
+#     if defined(DATASTART2)
+        GC_add_roots_inner(DATASTART2, (char *)(DATAEND2), TRUE);
+#     endif
+  }
+  return TRUE;
+}
+
+# define HAVE_REGISTER_MAIN_STATIC_DATA
+
+#else /* !HAVE_DL_ITERATE_PHDR */
+
+/* Dynamic loading code for Linux running ELF. Somewhat tested on
+ * Linux/x86, untested but hopefully should work on Linux/Alpha.
+ * This code was derived from the Solaris/ELF support. Thanks to
+ * whatever kind soul wrote that.  - Patrick Bridges */
+
+/* This doesn't necessarily work in all cases, e.g. with preloaded
+ * dynamic libraries.                                           */
+
+# if defined(NETBSD) || defined(OPENBSD)
+#   include <sys/exec_elf.h>
+   /* for compatibility with 1.4.x */
+#   ifndef DT_DEBUG
+#     define DT_DEBUG   21
+#   endif
+#   ifndef PT_LOAD
+#     define PT_LOAD    1
+#   endif
+#   ifndef PF_W
+#     define PF_W       2
+#   endif
+# elif !defined(PLATFORM_ANDROID)
+#  include <elf.h>
+# endif
+
+# ifndef PLATFORM_ANDROID
+#   include <link.h>
+# endif
+
+#endif /* !HAVE_DL_ITERATE_PHDR */
+
+#ifdef __GNUC__
+# pragma weak _DYNAMIC
+#endif
+extern ElfW(Dyn) _DYNAMIC[];
+
+STATIC struct link_map *
+GC_FirstDLOpenedLinkMap(void)
+{
+    ElfW(Dyn) *dp;
+    static struct link_map *cachedResult = 0;
+
+    if (0 == (ptr_t)_DYNAMIC) {
+        /* _DYNAMIC symbol not resolved. */
+        return(0);
+    }
+    if( cachedResult == 0 ) {
+#     if defined(NETBSD) && defined(RTLD_DI_LINKMAP)
+        struct link_map *lm = NULL;
+        if (!dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &lm))
+            cachedResult = lm;
+#     else
+        int tag;
+        for( dp = _DYNAMIC; (tag = dp->d_tag) != 0; dp++ ) {
+            if( tag == DT_DEBUG ) {
+                struct link_map *lm
+                        = ((struct r_debug *)(dp->d_un.d_ptr))->r_map;
+                if( lm != 0 ) cachedResult = lm->l_next; /* might be NULL */
+                break;
+            }
+        }
+#     endif /* !NETBSD || !RTLD_DI_LINKMAP */
+    }
+    return cachedResult;
+}
+
+GC_INNER void GC_register_dynamic_libraries(void)
+{
+  struct link_map *lm;
+
+# ifdef HAVE_DL_ITERATE_PHDR
+    if (GC_register_dynamic_libraries_dl_iterate_phdr()) {
+        return;
+    }
+# endif
+  for (lm = GC_FirstDLOpenedLinkMap(); lm != 0; lm = lm->l_next)
+    {
+        ElfW(Ehdr) * e;
+        ElfW(Phdr) * p;
+        unsigned long offset;
+        char * start;
+        int i;
+
+        e = (ElfW(Ehdr) *) lm->l_addr;
+#       ifdef PLATFORM_ANDROID
+          if (e == NULL)
+            continue;
+#       endif
+        p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff));
+        offset = ((unsigned long)(lm->l_addr));
+        for( i = 0; i < (int)e->e_phnum; i++, p++ ) {
+          switch( p->p_type ) {
+            case PT_LOAD:
+              {
+                if( !(p->p_flags & PF_W) ) break;
+                start = ((char *)(p->p_vaddr)) + offset;
+                GC_add_roots_inner(start, start + p->p_memsz, TRUE);
+              }
+              break;
+            default:
+              break;
+          }
+        }
+    }
+}
+
+#endif /* !USE_PROC_FOR_LIBRARIES */
+
+#endif /* LINUX */
+
+#if defined(IRIX5) || (defined(USE_PROC_FOR_LIBRARIES) && !defined(LINUX))
+
+#include <sys/procfs.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <elf.h>
+#include <errno.h>
+#include <signal.h>  /* Only for the following test. */
+#ifndef _sigargs
+# define IRIX6
+#endif
+
+/* We use /proc to track down all parts of the address space that are   */
+/* mapped by the process, and throw out regions we know we shouldn't    */
+/* worry about.  This may also work under other SVR4 variants.          */
+GC_INNER void GC_register_dynamic_libraries(void)
+{
+    static int fd = -1;
+    char buf[30];
+    static prmap_t * addr_map = 0;
+    static int current_sz = 0;  /* Number of records currently in addr_map */
+    static int needed_sz;       /* Required size of addr_map            */
+    int i;
+    long flags;
+    ptr_t start;
+    ptr_t limit;
+    ptr_t heap_start = HEAP_START;
+    ptr_t heap_end = heap_start;
+
+#   ifdef SOLARISDL
+#     define MA_PHYS 0
+#   endif /* SOLARISDL */
+
+    if (fd < 0) {
+      (void)snprintf(buf, sizeof(buf), "/proc/%ld", (long)getpid());
+      buf[sizeof(buf) - 1] = '\0';
+        /* The above generates a lint complaint, since pid_t varies.    */
+        /* It's unclear how to improve this.                            */
+      fd = open(buf, O_RDONLY);
+      if (fd < 0) {
+        ABORT("/proc open failed");
+      }
+    }
+    if (ioctl(fd, PIOCNMAP, &needed_sz) < 0) {
+        ABORT_ARG2("/proc PIOCNMAP ioctl failed",
+                   ": fd = %d, errno = %d", fd, errno);
+    }
+    if (needed_sz >= current_sz) {
+        current_sz = needed_sz * 2 + 1;
+                        /* Expansion, plus room for 0 record */
+        addr_map = (prmap_t *)GC_scratch_alloc(
+                                (word)current_sz * sizeof(prmap_t));
+        if (addr_map == NULL)
+          ABORT("Insufficient memory for address map");
+    }
+    if (ioctl(fd, PIOCMAP, addr_map) < 0) {
+        ABORT_ARG3("/proc PIOCMAP ioctl failed",
+                   ": errcode= %d, needed_sz= %d, addr_map= %p",
+                   errno, needed_sz, addr_map);
+    };
+    if (GC_n_heap_sects > 0) {
+        heap_end = GC_heap_sects[GC_n_heap_sects-1].hs_start
+                        + GC_heap_sects[GC_n_heap_sects-1].hs_bytes;
+        if ((word)heap_end < (word)GC_scratch_last_end_ptr)
+          heap_end = GC_scratch_last_end_ptr;
+    }
+    for (i = 0; i < needed_sz; i++) {
+        flags = addr_map[i].pr_mflags;
+        if ((flags & (MA_BREAK | MA_STACK | MA_PHYS
+                      | MA_FETCHOP | MA_NOTCACHED)) != 0) goto irrelevant;
+        if ((flags & (MA_READ | MA_WRITE)) != (MA_READ | MA_WRITE))
+            goto irrelevant;
+          /* The latter test is empirically useless in very old Irix    */
+          /* versions.  Other than the                                  */
+          /* main data and stack segments, everything appears to be     */
+          /* mapped readable, writable, executable, and shared(!!).     */
+          /* This makes no sense to me. - HB                            */
+        start = (ptr_t)(addr_map[i].pr_vaddr);
+        if (GC_roots_present(start)) goto irrelevant;
+        if ((word)start < (word)heap_end && (word)start >= (word)heap_start)
+                goto irrelevant;
+
+        limit = start + addr_map[i].pr_size;
+        /* The following seemed to be necessary for very old versions   */
+        /* of Irix, but it has been reported to discard relevant        */
+        /* segments under Irix 6.5.                                     */
+#       ifndef IRIX6
+          if (addr_map[i].pr_off == 0 && strncmp(start, ELFMAG, 4) == 0) {
+            /* Discard text segments, i.e. 0-offset mappings against    */
+            /* executable files which appear to have ELF headers.       */
+            caddr_t arg;
+            int obj;
+#           define MAP_IRR_SZ 10
+            static ptr_t map_irr[MAP_IRR_SZ];
+                                        /* Known irrelevant map entries */
+            static int n_irr = 0;
+            struct stat buf;
+            register int j;
+
+            for (j = 0; j < n_irr; j++) {
+                if (map_irr[j] == start) goto irrelevant;
+            }
+            arg = (caddr_t)start;
+            obj = ioctl(fd, PIOCOPENM, &arg);
+            if (obj >= 0) {
+                fstat(obj, &buf);
+                close(obj);
+                if ((buf.st_mode & 0111) != 0) {
+                    if (n_irr < MAP_IRR_SZ) {
+                        map_irr[n_irr++] = start;
+                    }
+                    goto irrelevant;
+                }
+            }
+          }
+#       endif /* !IRIX6 */
+        GC_add_roots_inner(start, limit, TRUE);
+      irrelevant: ;
+    }
+    /* Don't keep cached descriptor, for now.  Some kernels don't like us */
+    /* to keep a /proc file descriptor around during kill -9.             */
+        if (close(fd) < 0) ABORT("Couldn't close /proc file");
+        fd = -1;
+}
+
+# endif /* USE_PROC || IRIX5 */
+
+# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32)
+
+# ifndef WIN32_LEAN_AND_MEAN
+#   define WIN32_LEAN_AND_MEAN 1
+# endif
+# define NOSERVICE
+# include <windows.h>
+# include <stdlib.h>
+
+  /* We traverse the entire address space and register all segments     */
+  /* that could possibly have been written to.                          */
+  STATIC void GC_cond_add_roots(char *base, char * limit)
+  {
+#   ifdef GC_WIN32_THREADS
+      char * curr_base = base;
+      char * next_stack_lo;
+      char * next_stack_hi;
+
+      if (base == limit) return;
+      for(;;) {
+          GC_get_next_stack(curr_base, limit, &next_stack_lo, &next_stack_hi);
+          if ((word)next_stack_lo >= (word)limit) break;
+          if ((word)next_stack_lo > (word)curr_base)
+            GC_add_roots_inner(curr_base, next_stack_lo, TRUE);
+          curr_base = next_stack_hi;
+      }
+      if ((word)curr_base < (word)limit)
+        GC_add_roots_inner(curr_base, limit, TRUE);
+#   else
+      char * stack_top
+         = (char *)((word)GC_approx_sp() &
+                        ~(GC_sysinfo.dwAllocationGranularity - 1));
+
+      if (base == limit) return;
+      if ((word)limit > (word)stack_top
+          && (word)base < (word)GC_stackbottom) {
+          /* Part of the stack; ignore it. */
+          return;
+      }
+      GC_add_roots_inner(base, limit, TRUE);
+#   endif
+  }
+
+#ifdef DYNAMIC_LOADING
+  /* GC_register_main_static_data is not needed unless DYNAMIC_LOADING. */
+  GC_INNER GC_bool GC_register_main_static_data(void)
+  {
+#   if defined(MSWINCE) || defined(CYGWIN32)
+      /* Do we need to separately register the main static data segment? */
+      return FALSE;
+#   else
+      return GC_no_win32_dlls;
+#   endif
+  }
+# define HAVE_REGISTER_MAIN_STATIC_DATA
+#endif /* DYNAMIC_LOADING */
+
+# ifdef DEBUG_VIRTUALQUERY
+  void GC_dump_meminfo(MEMORY_BASIC_INFORMATION *buf)
+  {
+    GC_printf("BaseAddress = 0x%lx, AllocationBase = 0x%lx,"
+              " RegionSize = 0x%lx(%lu)\n", buf -> BaseAddress,
+              buf -> AllocationBase, buf -> RegionSize, buf -> RegionSize);
+    GC_printf("\tAllocationProtect = 0x%lx, State = 0x%lx, Protect = 0x%lx, "
+              "Type = 0x%lx\n", buf -> AllocationProtect, buf -> State,
+              buf -> Protect, buf -> Type);
+  }
+# endif /* DEBUG_VIRTUALQUERY */
+
+# if defined(MSWINCE) || defined(CYGWIN32)
+    /* FIXME: Should we really need to scan MEM_PRIVATE sections?       */
+    /* For now, we don't add MEM_PRIVATE sections to the data roots for */
+    /* WinCE because otherwise SEGV fault sometimes happens to occur in */
+    /* GC_mark_from() (and, even if we use WRAP_MARK_SOME, WinCE prints */
+    /* a "Data Abort" message to the debugging console).                */
+    /* To workaround that, use -DGC_REGISTER_MEM_PRIVATE.               */
+#   define GC_wnt TRUE
+# endif
+
+  GC_INNER void GC_register_dynamic_libraries(void)
+  {
+    MEMORY_BASIC_INFORMATION buf;
+    size_t result;
+    DWORD protect;
+    LPVOID p;
+    char * base;
+    char * limit, * new_limit;
+
+#   ifdef MSWIN32
+      if (GC_no_win32_dlls) return;
+#   endif
+    base = limit = p = GC_sysinfo.lpMinimumApplicationAddress;
+    while ((word)p < (word)GC_sysinfo.lpMaximumApplicationAddress) {
+        result = VirtualQuery(p, &buf, sizeof(buf));
+#       ifdef MSWINCE
+          if (result == 0) {
+            /* Page is free; advance to the next possible allocation base */
+            new_limit = (char *)
+                (((DWORD) p + GC_sysinfo.dwAllocationGranularity)
+                 & ~(GC_sysinfo.dwAllocationGranularity-1));
+          } else
+#       endif
+        /* else */ {
+            if (result != sizeof(buf)) {
+                ABORT("Weird VirtualQuery result");
+            }
+            new_limit = (char *)p + buf.RegionSize;
+            protect = buf.Protect;
+            if (buf.State == MEM_COMMIT
+                && (protect == PAGE_EXECUTE_READWRITE
+                    || protect == PAGE_READWRITE)
+                && (buf.Type == MEM_IMAGE
+#                   ifdef GC_REGISTER_MEM_PRIVATE
+                      || (protect == PAGE_READWRITE && buf.Type == MEM_PRIVATE)
+#                   else
+                      /* There is some evidence that we cannot always   */
+                      /* ignore MEM_PRIVATE sections under Windows ME   */
+                      /* and predecessors.  Hence we now also check for */
+                      /* that case.                                     */
+                      || (!GC_wnt && buf.Type == MEM_PRIVATE)
+#                   endif
+                   )
+                && !GC_is_heap_base(buf.AllocationBase)) {
+#               ifdef DEBUG_VIRTUALQUERY
+                  GC_dump_meminfo(&buf);
+#               endif
+                if ((char *)p != limit) {
+                    GC_cond_add_roots(base, limit);
+                    base = p;
+                }
+                limit = new_limit;
+            }
+        }
+        if ((word)p > (word)new_limit /* overflow */) break;
+        p = (LPVOID)new_limit;
+    }
+    GC_cond_add_roots(base, limit);
+  }
+
+#endif /* MSWIN32 || MSWINCE || CYGWIN32 */
+
+#if defined(ALPHA) && defined(OSF1)
+
+#include <loader.h>
+
+extern char *sys_errlist[];
+extern int sys_nerr;
+extern int errno;
+
+GC_INNER void GC_register_dynamic_libraries(void)
+{
+  int status;
+  ldr_process_t mypid;
+
+  /* module */
+    ldr_module_t moduleid = LDR_NULL_MODULE;
+    ldr_module_info_t moduleinfo;
+    size_t moduleinfosize = sizeof(moduleinfo);
+    size_t modulereturnsize;
+
+  /* region */
+    ldr_region_t region;
+    ldr_region_info_t regioninfo;
+    size_t regioninfosize = sizeof(regioninfo);
+    size_t regionreturnsize;
+
+  /* Obtain id of this process */
+    mypid = ldr_my_process();
+
+  /* For each module */
+    while (TRUE) {
+
+      /* Get the next (first) module */
+        status = ldr_next_module(mypid, &moduleid);
+
+      /* Any more modules? */
+        if (moduleid == LDR_NULL_MODULE)
+            break;    /* No more modules */
+
+      /* Check status AFTER checking moduleid because       */
+      /* of a bug in the non-shared ldr_next_module stub.   */
+        if (status != 0) {
+          ABORT_ARG3("ldr_next_module failed",
+                     ": status= %d, errcode= %d (%s)", status, errno,
+                     errno < sys_nerr ? sys_errlist[errno] : "");
+        }
+
+      /* Get the module information */
+        status = ldr_inq_module(mypid, moduleid, &moduleinfo,
+                                moduleinfosize, &modulereturnsize);
+        if (status != 0 )
+            ABORT("ldr_inq_module failed");
+
+      /* is module for the main program (i.e. nonshared portion)? */
+          if (moduleinfo.lmi_flags & LDR_MAIN)
+              continue;    /* skip the main module */
+
+#     ifdef DL_VERBOSE
+        GC_log_printf("---Module---\n");
+        GC_log_printf("Module ID\t = %16ld\n", moduleinfo.lmi_modid);
+        GC_log_printf("Count of regions = %16d\n", moduleinfo.lmi_nregion);
+        GC_log_printf("flags for module = %16lx\n", moduleinfo.lmi_flags);
+        GC_log_printf("module pathname\t = \"%s\"\n", moduleinfo.lmi_name);
+#     endif
+
+      /* For each region in this module */
+        for (region = 0; region < moduleinfo.lmi_nregion; region++) {
+          /* Get the region information */
+            status = ldr_inq_region(mypid, moduleid, region, &regioninfo,
+                                    regioninfosize, &regionreturnsize);
+            if (status != 0 )
+                ABORT("ldr_inq_region failed");
+
+          /* only process writable (data) regions */
+            if (! (regioninfo.lri_prot & LDR_W))
+                continue;
+
+#         ifdef DL_VERBOSE
+            GC_log_printf("--- Region ---\n");
+            GC_log_printf("Region number\t = %16ld\n",
+                          regioninfo.lri_region_no);
+            GC_log_printf("Protection flags = %016x\n", regioninfo.lri_prot);
+            GC_log_printf("Virtual address\t = %16p\n", regioninfo.lri_vaddr);
+            GC_log_printf("Mapped address\t = %16p\n",
+                          regioninfo.lri_mapaddr);
+            GC_log_printf("Region size\t = %16ld\n", regioninfo.lri_size);
+            GC_log_printf("Region name\t = \"%s\"\n", regioninfo.lri_name);
+#         endif
+
+          /* register region as a garbage collection root */
+          GC_add_roots_inner((char *)regioninfo.lri_mapaddr,
+                        (char *)regioninfo.lri_mapaddr + regioninfo.lri_size,
+                        TRUE);
+
+        }
+    }
+}
+#endif
+
+#if defined(HPUX)
+
+#include <errno.h>
+#include <dl.h>
+
+extern char *sys_errlist[];
+extern int sys_nerr;
+
+GC_INNER void GC_register_dynamic_libraries(void)
+{
+  int status;
+  int index = 1; /* Ordinal position in shared library search list */
+  struct shl_descriptor *shl_desc; /* Shared library info, see dl.h */
+
+  /* For each dynamic library loaded */
+    while (TRUE) {
+
+      /* Get info about next shared library */
+        status = shl_get(index, &shl_desc);
+
+      /* Check if this is the end of the list or if some error occurred */
+        if (status != 0) {
+#        ifdef GC_HPUX_THREADS
+           /* I've seen errno values of 0.  The man page is not clear   */
+           /* as to whether errno should get set on a -1 return.        */
+           break;
+#        else
+          if (errno == EINVAL) {
+            break; /* Moved past end of shared library list --> finished */
+          } else {
+            ABORT_ARG3("shl_get failed",
+                       ": status= %d, errcode= %d (%s)", status, errno,
+                       errno < sys_nerr ? sys_errlist[errno] : "");
+          }
+#        endif
+        }
+
+#     ifdef DL_VERBOSE
+        GC_log_printf("---Shared library---\n");
+        GC_log_printf("\tfilename\t= \"%s\"\n", shl_desc->filename);
+        GC_log_printf("\tindex\t\t= %d\n", index);
+        GC_log_printf("\thandle\t\t= %08x\n",
+                      (unsigned long) shl_desc->handle);
+        GC_log_printf("\ttext seg.start\t= %08x\n", shl_desc->tstart);
+        GC_log_printf("\ttext seg.end\t= %08x\n", shl_desc->tend);
+        GC_log_printf("\tdata seg.start\t= %08x\n", shl_desc->dstart);
+        GC_log_printf("\tdata seg.end\t= %08x\n", shl_desc->dend);
+        GC_log_printf("\tref.count\t= %lu\n", shl_desc->ref_count);
+#     endif
+
+      /* register shared library's data segment as a garbage collection root */
+        GC_add_roots_inner((char *) shl_desc->dstart,
+                           (char *) shl_desc->dend, TRUE);
+
+        index++;
+    }
+}
+#endif /* HPUX */
+
+#ifdef AIX
+# pragma alloca
+# include <sys/ldr.h>
+# include <sys/errno.h>
+  GC_INNER void GC_register_dynamic_libraries(void)
+  {
+        int len;
+        char *ldibuf;
+        int ldibuflen;
+        struct ld_info *ldi;
+
+        ldibuf = alloca(ldibuflen = 8192);
+
+        while ( (len = loadquery(L_GETINFO,ldibuf,ldibuflen)) < 0) {
+                if (errno != ENOMEM) {
+                        ABORT("loadquery failed");
+                }
+                ldibuf = alloca(ldibuflen *= 2);
+        }
+
+        ldi = (struct ld_info *)ldibuf;
+        while (ldi) {
+                len = ldi->ldinfo_next;
+                GC_add_roots_inner(
+                                ldi->ldinfo_dataorg,
+                                (ptr_t)(unsigned long)ldi->ldinfo_dataorg
+                                + ldi->ldinfo_datasize,
+                                TRUE);
+                ldi = len ? (struct ld_info *)((char *)ldi + len) : 0;
+        }
+  }
+#endif /* AIX */
+
+#ifdef DARWIN
+
+/* __private_extern__ hack required for pre-3.4 gcc versions.   */
+#ifndef __private_extern__
+# define __private_extern__ extern
+# include <mach-o/dyld.h>
+# undef __private_extern__
+#else
+# include <mach-o/dyld.h>
+#endif
+#include <mach-o/getsect.h>
+
+/*#define DARWIN_DEBUG*/
+
+/* Writable sections generally available on Darwin.     */
+STATIC const struct {
+    const char *seg;
+    const char *sect;
+} GC_dyld_sections[] = {
+    { SEG_DATA, SECT_DATA },
+    /* Used by FSF GCC, but not by OS X system tools, so far.   */
+    { SEG_DATA, "__static_data" },
+    { SEG_DATA, SECT_BSS },
+    { SEG_DATA, SECT_COMMON },
+    /* FSF GCC - zero-sized object sections for targets         */
+    /*supporting section anchors.                               */
+    { SEG_DATA, "__zobj_data" },
+    { SEG_DATA, "__zobj_bss" }
+};
+
+/* Additional writable sections:                                */
+/* GCC on Darwin constructs aligned sections "on demand", where */
+/* the alignment size is embedded in the section name.          */
+/* Furthermore, there are distinctions between sections         */
+/* containing private vs. public symbols.  It also constructs   */
+/* sections specifically for zero-sized objects, when the       */
+/* target supports section anchors.                             */
+STATIC const char * const GC_dyld_add_sect_fmts[] = {
+  "__bss%u",
+  "__pu_bss%u",
+  "__zo_bss%u",
+  "__zo_pu_bss%u"
+};
+
+/* Currently, mach-o will allow up to the max of 2^15 alignment */
+/* in an object file.                                           */
+#ifndef L2_MAX_OFILE_ALIGNMENT
+# define L2_MAX_OFILE_ALIGNMENT 15
+#endif
+
+STATIC const char *GC_dyld_name_for_hdr(const struct GC_MACH_HEADER *hdr)
+{
+    unsigned long i, c;
+    c = _dyld_image_count();
+    for (i = 0; i < c; i++)
+      if ((const struct GC_MACH_HEADER *)_dyld_get_image_header(i) == hdr)
+        return _dyld_get_image_name(i);
+    return NULL;
+}
+
+/* This should never be called by a thread holding the lock.    */
+STATIC void GC_dyld_image_add(const struct GC_MACH_HEADER *hdr,
+                              intptr_t slide)
+{
+  unsigned long start, end;
+  unsigned i, j;
+  const struct GC_MACH_SECTION *sec;
+  const char *name;
+  GC_has_static_roots_func callback = GC_has_static_roots;
+  char secnam[16];
+  const char *fmt;
+  DCL_LOCK_STATE;
+
+  if (GC_no_dls) return;
+# ifdef DARWIN_DEBUG
+    name = GC_dyld_name_for_hdr(hdr);
+# else
+    name = callback != 0 ? GC_dyld_name_for_hdr(hdr) : NULL;
+# endif
+  for (i = 0; i < sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]); i++) {
+    sec = GC_GETSECTBYNAME(hdr, GC_dyld_sections[i].seg,
+                           GC_dyld_sections[i].sect);
+    if (sec == NULL || sec->size < sizeof(word))
+      continue;
+    start = slide + sec->addr;
+    end = start + sec->size;
+    LOCK();
+    /* The user callback is called holding the lock.    */
+    if (callback == 0 || callback(name, (void*)start, (size_t)sec->size)) {
+#     ifdef DARWIN_DEBUG
+        GC_log_printf(
+              "Adding section __DATA,%s at %p-%p (%lu bytes) from image %s\n",
+               GC_dyld_sections[i].sect, (void*)start, (void*)end,
+               (unsigned long)sec->size, name);
+#     endif
+      GC_add_roots_inner((ptr_t)start, (ptr_t)end, FALSE);
+    }
+    UNLOCK();
+  }
+
+  /* Sections constructed on demand.    */
+  for (j = 0; j < sizeof(GC_dyld_add_sect_fmts) / sizeof(char *); j++) {
+    fmt = GC_dyld_add_sect_fmts[j];
+    /* Add our manufactured aligned BSS sections.       */
+    for (i = 0; i <= L2_MAX_OFILE_ALIGNMENT; i++) {
+      (void)snprintf(secnam, sizeof(secnam), fmt, (unsigned)i);
+      secnam[sizeof(secnam) - 1] = '\0';
+      sec = GC_GETSECTBYNAME(hdr, SEG_DATA, secnam);
+      if (sec == NULL || sec->size == 0)
+        continue;
+      start = slide + sec->addr;
+      end = start + sec->size;
+#     ifdef DARWIN_DEBUG
+        GC_log_printf("Adding on-demand section __DATA,%s at"
+                      " %p-%p (%lu bytes) from image %s\n",
+                      secnam, (void*)start, (void*)end,
+                      (unsigned long)sec->size, name);
+#     endif
+      GC_add_roots((char*)start, (char*)end);
+    }
+  }
+
+# ifdef DARWIN_DEBUG
+    GC_print_static_roots();
+# endif
+}
+
+/* This should never be called by a thread holding the lock.    */
+STATIC void GC_dyld_image_remove(const struct GC_MACH_HEADER *hdr,
+                                 intptr_t slide)
+{
+  unsigned long start, end;
+  unsigned i, j;
+  const struct GC_MACH_SECTION *sec;
+  char secnam[16];
+  const char *fmt;
+
+  for (i = 0; i < sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]); i++) {
+    sec = GC_GETSECTBYNAME(hdr, GC_dyld_sections[i].seg,
+                           GC_dyld_sections[i].sect);
+    if (sec == NULL || sec->size == 0)
+      continue;
+    start = slide + sec->addr;
+    end = start + sec->size;
+#   ifdef DARWIN_DEBUG
+      GC_log_printf(
+            "Removing section __DATA,%s at %p-%p (%lu bytes) from image %s\n",
+            GC_dyld_sections[i].sect, (void*)start, (void*)end,
+            (unsigned long)sec->size, GC_dyld_name_for_hdr(hdr));
+#   endif
+    GC_remove_roots((char*)start, (char*)end);
+  }
+
+  /* Remove our on-demand sections.     */
+  for (j = 0; j < sizeof(GC_dyld_add_sect_fmts) / sizeof(char *); j++) {
+    fmt = GC_dyld_add_sect_fmts[j];
+    for (i = 0; i <= L2_MAX_OFILE_ALIGNMENT; i++) {
+      (void)snprintf(secnam, sizeof(secnam), fmt, (unsigned)i);
+      secnam[sizeof(secnam) - 1] = '\0';
+      sec = GC_GETSECTBYNAME(hdr, SEG_DATA, secnam);
+      if (sec == NULL || sec->size == 0)
+        continue;
+      start = slide + sec->addr;
+      end = start + sec->size;
+#     ifdef DARWIN_DEBUG
+        GC_log_printf("Removing on-demand section __DATA,%s at"
+                      " %p-%p (%lu bytes) from image %s\n", secnam,
+                      (void*)start, (void*)end, (unsigned long)sec->size,
+                      GC_dyld_name_for_hdr(hdr));
+#     endif
+      GC_remove_roots((char*)start, (char*)end);
+    }
+  }
+
+# ifdef DARWIN_DEBUG
+    GC_print_static_roots();
+# endif
+}
+
+GC_INNER void GC_register_dynamic_libraries(void)
+{
+    /* Currently does nothing. The callbacks are setup by GC_init_dyld()
+    The dyld library takes it from there. */
+}
+
+/* The _dyld_* functions have an internal lock so no _dyld functions
+   can be called while the world is stopped without the risk of a deadlock.
+   Because of this we MUST setup callbacks BEFORE we ever stop the world.
+   This should be called BEFORE any thread in created and WITHOUT the
+   allocation lock held. */
+
+GC_INNER void GC_init_dyld(void)
+{
+  static GC_bool initialized = FALSE;
+
+  if (initialized) return;
+
+# ifdef DARWIN_DEBUG
+    GC_log_printf("Registering dyld callbacks...\n");
+# endif
+
+  /* Apple's Documentation:
+     When you call _dyld_register_func_for_add_image, the dynamic linker
+     runtime calls the specified callback (func) once for each of the images
+     that is currently loaded into the program. When a new image is added to
+     the program, your callback is called again with the mach_header for the
+     new image, and the virtual memory slide amount of the new image.
+
+     This WILL properly register already linked libraries and libraries
+     linked in the future.
+  */
+
+  _dyld_register_func_for_add_image(GC_dyld_image_add);
+  _dyld_register_func_for_remove_image(GC_dyld_image_remove);
+      /* Ignore 2 compiler warnings here: passing argument 1 of       */
+      /* '_dyld_register_func_for_add/remove_image' from incompatible */
+      /* pointer type.                                                */
+
+  /* Set this early to avoid reentrancy issues. */
+  initialized = TRUE;
+
+# ifdef NO_DYLD_BIND_FULLY_IMAGE
+    /* FIXME: What should we do in this case?   */
+# else
+    if (GC_no_dls) return; /* skip main data segment registration */
+
+    /* When the environment variable is set, the dynamic linker binds   */
+    /* all undefined symbols the application needs at launch time.      */
+    /* This includes function symbols that are normally bound lazily at */
+    /* the time of their first invocation.                              */
+    if (GETENV("DYLD_BIND_AT_LAUNCH") == 0) {
+      /* The environment variable is unset, so we should bind manually. */
+#     ifdef DARWIN_DEBUG
+        GC_log_printf("Forcing full bind of GC code...\n");
+#     endif
+      /* FIXME: '_dyld_bind_fully_image_containing_address' is deprecated. */
+      if (!_dyld_bind_fully_image_containing_address(
+                                                  (unsigned long *)GC_malloc))
+        ABORT("_dyld_bind_fully_image_containing_address failed");
+    }
+# endif
+}
+
+#define HAVE_REGISTER_MAIN_STATIC_DATA
+GC_INNER GC_bool GC_register_main_static_data(void)
+{
+  /* Already done through dyld callbacks */
+  return FALSE;
+}
+
+#endif /* DARWIN */
+
+#elif defined(PCR)
+
+# include "il/PCR_IL.h"
+# include "th/PCR_ThCtl.h"
+# include "mm/PCR_MM.h"
+
+  GC_INNER void GC_register_dynamic_libraries(void)
+  {
+    /* Add new static data areas of dynamically loaded modules. */
+    PCR_IL_LoadedFile * p = PCR_IL_GetLastLoadedFile();
+    PCR_IL_LoadedSegment * q;
+
+    /* Skip uncommitted files */
+    while (p != NIL && !(p -> lf_commitPoint)) {
+        /* The loading of this file has not yet been committed    */
+        /* Hence its description could be inconsistent.           */
+        /* Furthermore, it hasn't yet been run.  Hence its data   */
+        /* segments can't possibly reference heap allocated       */
+        /* objects.                                               */
+        p = p -> lf_prev;
+    }
+    for (; p != NIL; p = p -> lf_prev) {
+      for (q = p -> lf_ls; q != NIL; q = q -> ls_next) {
+        if ((q -> ls_flags & PCR_IL_SegFlags_Traced_MASK)
+            == PCR_IL_SegFlags_Traced_on) {
+          GC_add_roots_inner((char *)(q -> ls_addr),
+                             (char *)(q -> ls_addr) + q -> ls_bytes, TRUE);
+        }
+      }
+    }
+  }
+#endif /* PCR && !DYNAMIC_LOADING && !MSWIN32 */
+
+#if !defined(HAVE_REGISTER_MAIN_STATIC_DATA) && defined(DYNAMIC_LOADING)
+  /* Do we need to separately register the main static data segment? */
+  GC_INNER GC_bool GC_register_main_static_data(void)
+  {
+    return TRUE;
+  }
+#endif /* HAVE_REGISTER_MAIN_STATIC_DATA */
+
+/* Register a routine to filter dynamic library registration.  */
+GC_API void GC_CALL GC_register_has_static_roots_callback(
+                                        GC_has_static_roots_func callback)
+{
+    GC_has_static_roots = callback;
+}

+ 1114 - 0
blitz.mod/bdwgc/finalize.c

@@ -0,0 +1,1114 @@
+/*
+ * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
+ * Copyright (c) 1991-1996 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1996-1999 by Silicon Graphics.  All rights reserved.
+ * Copyright (C) 2007 Free Software Foundation, Inc
+
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+#include "private/gc_pmark.h"
+
+#ifndef GC_NO_FINALIZATION
+
+/* Type of mark procedure used for marking from finalizable object.     */
+/* This procedure normally does not mark the object, only its           */
+/* descendants.                                                         */
+typedef void (* finalization_mark_proc)(ptr_t /* finalizable_obj_ptr */);
+
+#define HASH3(addr,size,log_size) \
+        ((((word)(addr) >> 3) ^ ((word)(addr) >> (3 + (log_size)))) \
+         & ((size) - 1))
+#define HASH2(addr,log_size) HASH3(addr, 1 << (log_size), log_size)
+
+struct hash_chain_entry {
+    word hidden_key;
+    struct hash_chain_entry * next;
+};
+
+struct disappearing_link {
+    struct hash_chain_entry prolog;
+#   define dl_hidden_link prolog.hidden_key
+                                /* Field to be cleared.         */
+#   define dl_next(x) (struct disappearing_link *)((x) -> prolog.next)
+#   define dl_set_next(x, y) \
+                (void)((x)->prolog.next = (struct hash_chain_entry *)(y))
+    word dl_hidden_obj;         /* Pointer to object base       */
+};
+
+struct dl_hashtbl_s {
+    struct disappearing_link **head;
+    signed_word log_size;
+    word entries;
+};
+
+STATIC struct dl_hashtbl_s GC_dl_hashtbl = {
+    /* head */ NULL, /* log_size */ -1, /* entries */ 0 };
+#ifndef GC_LONG_REFS_NOT_NEEDED
+  STATIC struct dl_hashtbl_s GC_ll_hashtbl = { NULL, -1, 0 };
+#endif
+
+STATIC struct finalizable_object {
+    struct hash_chain_entry prolog;
+#   define fo_hidden_base prolog.hidden_key
+                                /* Pointer to object base.      */
+                                /* No longer hidden once object */
+                                /* is on finalize_now queue.    */
+#   define fo_next(x) (struct finalizable_object *)((x) -> prolog.next)
+#   define fo_set_next(x,y) ((x)->prolog.next = (struct hash_chain_entry *)(y))
+    GC_finalization_proc fo_fn; /* Finalizer.                   */
+    ptr_t fo_client_data;
+    word fo_object_size;        /* In bytes.                    */
+    finalization_mark_proc fo_mark_proc;        /* Mark-through procedure */
+} **GC_fo_head = 0;
+
+STATIC struct finalizable_object * GC_finalize_now = 0;
+        /* List of objects that should be finalized now.        */
+
+static signed_word log_fo_table_size = -1;
+
+GC_INNER void GC_push_finalizer_structures(void)
+{
+    GC_ASSERT((word)&GC_dl_hashtbl.head % sizeof(word) == 0);
+    GC_ASSERT((word)&GC_fo_head % sizeof(word) == 0);
+    GC_ASSERT((word)&GC_finalize_now % sizeof(word) == 0);
+
+# ifndef GC_LONG_REFS_NOT_NEEDED
+    GC_ASSERT((word)&GC_ll_hashtbl.head % sizeof(word) == 0);
+    GC_push_all((ptr_t)(&GC_ll_hashtbl.head),
+                (ptr_t)(&GC_ll_hashtbl.head) + sizeof(word));
+# endif
+
+    GC_push_all((ptr_t)(&GC_dl_hashtbl.head),
+                (ptr_t)(&GC_dl_hashtbl.head) + sizeof(word));
+    GC_push_all((ptr_t)(&GC_fo_head), (ptr_t)(&GC_fo_head) + sizeof(word));
+    GC_push_all((ptr_t)(&GC_finalize_now),
+                (ptr_t)(&GC_finalize_now) + sizeof(word));
+}
+
+/* Double the size of a hash table. *size_ptr is the log of its current */
+/* size.  May be a no-op.                                               */
+/* *table is a pointer to an array of hash headers.  If we succeed, we  */
+/* update both *table and *log_size_ptr.  Lock is held.                 */
+STATIC void GC_grow_table(struct hash_chain_entry ***table,
+                          signed_word *log_size_ptr)
+{
+    register word i;
+    register struct hash_chain_entry *p;
+    signed_word log_old_size = *log_size_ptr;
+    signed_word log_new_size = log_old_size + 1;
+    word old_size = ((log_old_size == -1)? 0: (1 << log_old_size));
+    word new_size = (word)1 << log_new_size;
+    /* FIXME: Power of 2 size often gets rounded up to one more page. */
+    struct hash_chain_entry **new_table = (struct hash_chain_entry **)
+        GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE(
+                (size_t)new_size * sizeof(struct hash_chain_entry *), NORMAL);
+
+    if (new_table == 0) {
+        if (*table == 0) {
+            ABORT("Insufficient space for initial table allocation");
+        } else {
+            return;
+        }
+    }
+    for (i = 0; i < old_size; i++) {
+      p = (*table)[i];
+      while (p != 0) {
+        ptr_t real_key = GC_REVEAL_POINTER(p -> hidden_key);
+        struct hash_chain_entry *next = p -> next;
+        size_t new_hash = HASH3(real_key, new_size, log_new_size);
+
+        p -> next = new_table[new_hash];
+        new_table[new_hash] = p;
+        p = next;
+      }
+    }
+    *log_size_ptr = log_new_size;
+    *table = new_table;
+}
+
+GC_API int GC_CALL GC_register_disappearing_link(void * * link)
+{
+    ptr_t base;
+
+    base = (ptr_t)GC_base(link);
+    if (base == 0)
+        ABORT("Bad arg to GC_register_disappearing_link");
+    return(GC_general_register_disappearing_link(link, base));
+}
+
+STATIC int GC_register_disappearing_link_inner(
+                        struct dl_hashtbl_s *dl_hashtbl, void **link,
+                        const void *obj)
+{
+    struct disappearing_link *curr_dl;
+    size_t index;
+    struct disappearing_link * new_dl;
+    DCL_LOCK_STATE;
+
+    LOCK();
+    GC_ASSERT(obj != NULL && GC_base_C(obj) == obj);
+    if (dl_hashtbl -> log_size == -1
+        || dl_hashtbl -> entries > ((word)1 << dl_hashtbl -> log_size)) {
+        GC_grow_table((struct hash_chain_entry ***)&dl_hashtbl -> head,
+                      &dl_hashtbl -> log_size);
+        GC_COND_LOG_PRINTF("Grew dl table to %u entries\n",
+                           1 << (unsigned)dl_hashtbl -> log_size);
+    }
+    index = HASH2(link, dl_hashtbl -> log_size);
+    for (curr_dl = dl_hashtbl -> head[index]; curr_dl != 0;
+         curr_dl = dl_next(curr_dl)) {
+        if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) {
+            curr_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj);
+            UNLOCK();
+            return GC_DUPLICATE;
+        }
+    }
+    new_dl = (struct disappearing_link *)
+        GC_INTERNAL_MALLOC(sizeof(struct disappearing_link),NORMAL);
+    if (0 == new_dl) {
+      GC_oom_func oom_fn = GC_oom_fn;
+      UNLOCK();
+      new_dl = (struct disappearing_link *)
+                (*oom_fn)(sizeof(struct disappearing_link));
+      if (0 == new_dl) {
+        return GC_NO_MEMORY;
+      }
+      /* It's not likely we'll make it here, but ... */
+      LOCK();
+      /* Recalculate index since the table may grow.    */
+      index = HASH2(link, dl_hashtbl -> log_size);
+      /* Check again that our disappearing link not in the table. */
+      for (curr_dl = dl_hashtbl -> head[index]; curr_dl != 0;
+           curr_dl = dl_next(curr_dl)) {
+        if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) {
+          curr_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj);
+          UNLOCK();
+#         ifndef DBG_HDRS_ALL
+            /* Free unused new_dl returned by GC_oom_fn() */
+            GC_free((void *)new_dl);
+#         endif
+          return GC_DUPLICATE;
+        }
+      }
+    }
+    new_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj);
+    new_dl -> dl_hidden_link = GC_HIDE_POINTER(link);
+    dl_set_next(new_dl, dl_hashtbl -> head[index]);
+    dl_hashtbl -> head[index] = new_dl;
+    dl_hashtbl -> entries++;
+    UNLOCK();
+    return GC_SUCCESS;
+}
+
+GC_API int GC_CALL GC_general_register_disappearing_link(void * * link,
+                                                         const void * obj)
+{
+    if (((word)link & (ALIGNMENT-1)) != 0 || NULL == link)
+        ABORT("Bad arg to GC_general_register_disappearing_link");
+    return GC_register_disappearing_link_inner(&GC_dl_hashtbl, link, obj);
+}
+
+#ifdef DBG_HDRS_ALL
+# define FREE_DL_ENTRY(curr_dl) dl_set_next(curr_dl, NULL)
+#else
+# define FREE_DL_ENTRY(curr_dl) GC_free(curr_dl)
+#endif
+
+/* Unregisters given link and returns the link entry to free.   */
+/* Assume the lock is held.                                     */
+GC_INLINE struct disappearing_link *GC_unregister_disappearing_link_inner(
+                                struct dl_hashtbl_s *dl_hashtbl, void **link)
+{
+    struct disappearing_link *curr_dl;
+    struct disappearing_link *prev_dl = NULL;
+    size_t index = HASH2(link, dl_hashtbl->log_size);
+
+    for (curr_dl = dl_hashtbl -> head[index]; curr_dl;
+         curr_dl = dl_next(curr_dl)) {
+        if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) {
+            /* Remove found entry from the table. */
+            if (NULL == prev_dl) {
+                dl_hashtbl -> head[index] = dl_next(curr_dl);
+            } else {
+                dl_set_next(prev_dl, dl_next(curr_dl));
+            }
+            dl_hashtbl -> entries--;
+            break;
+        }
+        prev_dl = curr_dl;
+    }
+    return curr_dl;
+}
+
+GC_API int GC_CALL GC_unregister_disappearing_link(void * * link)
+{
+    struct disappearing_link *curr_dl;
+    DCL_LOCK_STATE;
+
+    if (((word)link & (ALIGNMENT-1)) != 0) return(0); /* Nothing to do. */
+
+    LOCK();
+    curr_dl = GC_unregister_disappearing_link_inner(&GC_dl_hashtbl, link);
+    UNLOCK();
+    if (NULL == curr_dl) return 0;
+    FREE_DL_ENTRY(curr_dl);
+    return 1;
+}
+
+#ifndef GC_LONG_REFS_NOT_NEEDED
+  GC_API int GC_CALL GC_register_long_link(void * * link, const void * obj)
+  {
+    if (((word)link & (ALIGNMENT-1)) != 0 || NULL == link)
+        ABORT("Bad arg to GC_register_long_link");
+    return GC_register_disappearing_link_inner(&GC_ll_hashtbl, link, obj);
+  }
+
+  GC_API int GC_CALL GC_unregister_long_link(void * * link)
+  {
+    struct disappearing_link *curr_dl;
+    DCL_LOCK_STATE;
+
+    if (((word)link & (ALIGNMENT-1)) != 0) return(0); /* Nothing to do. */
+
+    LOCK();
+    curr_dl = GC_unregister_disappearing_link_inner(&GC_ll_hashtbl, link);
+    UNLOCK();
+    if (NULL == curr_dl) return 0;
+    FREE_DL_ENTRY(curr_dl);
+    return 1;
+  }
+#endif /* !GC_LONG_REFS_NOT_NEEDED */
+
+#ifndef GC_MOVE_DISAPPEARING_LINK_NOT_NEEDED
+  /* Moves a link.  Assume the lock is held.    */
+  STATIC int GC_move_disappearing_link_inner(
+                                struct dl_hashtbl_s *dl_hashtbl,
+                                void **link, void **new_link)
+  {
+    struct disappearing_link *curr_dl, *prev_dl, *new_dl;
+    size_t curr_index, new_index;
+    word curr_hidden_link;
+    word new_hidden_link;
+
+    /* Find current link.       */
+    curr_index = HASH2(link, dl_hashtbl -> log_size);
+    curr_hidden_link = GC_HIDE_POINTER(link);
+    prev_dl = NULL;
+    for (curr_dl = dl_hashtbl -> head[curr_index]; curr_dl;
+         curr_dl = dl_next(curr_dl)) {
+      if (curr_dl -> dl_hidden_link == curr_hidden_link)
+        break;
+      prev_dl = curr_dl;
+    }
+
+    if (NULL == curr_dl) {
+      return GC_NOT_FOUND;
+    }
+
+    if (link == new_link) {
+      return GC_SUCCESS; /* Nothing to do.      */
+    }
+
+    /* link found; now check new_link not present.      */
+    new_index = HASH2(new_link, dl_hashtbl -> log_size);
+    new_hidden_link = GC_HIDE_POINTER(new_link);
+    for (new_dl = dl_hashtbl -> head[new_index]; new_dl;
+         new_dl = dl_next(new_dl)) {
+      if (new_dl -> dl_hidden_link == new_hidden_link) {
+        /* Target already registered; bail.     */
+        return GC_DUPLICATE;
+      }
+    }
+
+    /* Remove from old, add to new, update link.        */
+    if (NULL == prev_dl) {
+      dl_hashtbl -> head[curr_index] = dl_next(curr_dl);
+    } else {
+      dl_set_next(prev_dl, dl_next(curr_dl));
+    }
+    curr_dl -> dl_hidden_link = new_hidden_link;
+    dl_set_next(curr_dl, dl_hashtbl -> head[new_index]);
+    dl_hashtbl -> head[new_index] = curr_dl;
+    return GC_SUCCESS;
+  }
+
+  GC_API int GC_CALL GC_move_disappearing_link(void **link, void **new_link)
+  {
+    int result;
+    DCL_LOCK_STATE;
+
+    if (((word)new_link & (ALIGNMENT-1)) != 0 || new_link == NULL)
+      ABORT("Bad new_link arg to GC_move_disappearing_link");
+    if (((word)link & (ALIGNMENT-1)) != 0)
+      return GC_NOT_FOUND; /* Nothing to do. */
+
+    LOCK();
+    result = GC_move_disappearing_link_inner(&GC_dl_hashtbl, link, new_link);
+    UNLOCK();
+    return result;
+  }
+
+# ifndef GC_LONG_REFS_NOT_NEEDED
+    GC_API int GC_CALL GC_move_long_link(void **link, void **new_link)
+    {
+      int result;
+      DCL_LOCK_STATE;
+
+      if (((word)new_link & (ALIGNMENT-1)) != 0 || new_link == NULL)
+        ABORT("Bad new_link arg to GC_move_disappearing_link");
+      if (((word)link & (ALIGNMENT-1)) != 0)
+        return GC_NOT_FOUND; /* Nothing to do. */
+
+      LOCK();
+      result = GC_move_disappearing_link_inner(&GC_ll_hashtbl, link, new_link);
+      UNLOCK();
+      return result;
+    }
+# endif /* !GC_LONG_REFS_NOT_NEEDED */
+#endif /* !GC_MOVE_DISAPPEARING_LINK_NOT_NEEDED */
+
+/* Possible finalization_marker procedures.  Note that mark stack       */
+/* overflow is handled by the caller, and is not a disaster.            */
+STATIC void GC_normal_finalize_mark_proc(ptr_t p)
+{
+    hdr * hhdr = HDR(p);
+
+    PUSH_OBJ(p, hhdr, GC_mark_stack_top,
+             &(GC_mark_stack[GC_mark_stack_size]));
+}
+
+/* This only pays very partial attention to the mark descriptor.        */
+/* It does the right thing for normal and atomic objects, and treats    */
+/* most others as normal.                                               */
+STATIC void GC_ignore_self_finalize_mark_proc(ptr_t p)
+{
+    hdr * hhdr = HDR(p);
+    word descr = hhdr -> hb_descr;
+    ptr_t q;
+    word r;
+    ptr_t scan_limit;
+    ptr_t target_limit = p + hhdr -> hb_sz - 1;
+
+    if ((descr & GC_DS_TAGS) == GC_DS_LENGTH) {
+       scan_limit = p + descr - sizeof(word);
+    } else {
+       scan_limit = target_limit + 1 - sizeof(word);
+    }
+    for (q = p; (word)q <= (word)scan_limit; q += ALIGNMENT) {
+        r = *(word *)q;
+        if (r < (word)p || r > (word)target_limit) {
+            GC_PUSH_ONE_HEAP(r, q, GC_mark_stack_top);
+        }
+    }
+}
+
+STATIC void GC_null_finalize_mark_proc(ptr_t p GC_ATTR_UNUSED) {}
+
+/* Possible finalization_marker procedures.  Note that mark stack       */
+/* overflow is handled by the caller, and is not a disaster.            */
+
+/* GC_unreachable_finalize_mark_proc is an alias for normal marking,    */
+/* but it is explicitly tested for, and triggers different              */
+/* behavior.  Objects registered in this way are not finalized          */
+/* if they are reachable by other finalizable objects, even if those    */
+/* other objects specify no ordering.                                   */
+STATIC void GC_unreachable_finalize_mark_proc(ptr_t p)
+{
+    GC_normal_finalize_mark_proc(p);
+}
+
+/* Register a finalization function.  See gc.h for details.     */
+/* The last parameter is a procedure that determines            */
+/* marking for finalization ordering.  Any objects marked       */
+/* by that procedure will be guaranteed to not have been        */
+/* finalized when this finalizer is invoked.                    */
+STATIC void GC_register_finalizer_inner(void * obj,
+                                        GC_finalization_proc fn, void *cd,
+                                        GC_finalization_proc *ofn, void **ocd,
+                                        finalization_mark_proc mp)
+{
+    ptr_t base;
+    struct finalizable_object * curr_fo, * prev_fo;
+    size_t index;
+    struct finalizable_object *new_fo = 0;
+    hdr *hhdr = NULL; /* initialized to prevent warning. */
+    GC_oom_func oom_fn;
+    DCL_LOCK_STATE;
+
+    LOCK();
+    if (log_fo_table_size == -1
+        || GC_fo_entries > ((word)1 << log_fo_table_size)) {
+        GC_grow_table((struct hash_chain_entry ***)&GC_fo_head,
+                      &log_fo_table_size);
+        GC_COND_LOG_PRINTF("Grew fo table to %u entries\n",
+                           1 << (unsigned)log_fo_table_size);
+    }
+    /* in the THREADS case we hold allocation lock.             */
+    base = (ptr_t)obj;
+    for (;;) {
+      index = HASH2(base, log_fo_table_size);
+      prev_fo = 0;
+      curr_fo = GC_fo_head[index];
+      while (curr_fo != 0) {
+        GC_ASSERT(GC_size(curr_fo) >= sizeof(struct finalizable_object));
+        if (curr_fo -> fo_hidden_base == GC_HIDE_POINTER(base)) {
+          /* Interruption by a signal in the middle of this     */
+          /* should be safe.  The client may see only *ocd      */
+          /* updated, but we'll declare that to be his problem. */
+          if (ocd) *ocd = (void *) (curr_fo -> fo_client_data);
+          if (ofn) *ofn = curr_fo -> fo_fn;
+          /* Delete the structure for base. */
+          if (prev_fo == 0) {
+            GC_fo_head[index] = fo_next(curr_fo);
+          } else {
+            fo_set_next(prev_fo, fo_next(curr_fo));
+          }
+          if (fn == 0) {
+            GC_fo_entries--;
+            /* May not happen if we get a signal.  But a high   */
+            /* estimate will only make the table larger than    */
+            /* necessary.                                       */
+#           if !defined(THREADS) && !defined(DBG_HDRS_ALL)
+              GC_free((void *)curr_fo);
+#           endif
+          } else {
+            curr_fo -> fo_fn = fn;
+            curr_fo -> fo_client_data = (ptr_t)cd;
+            curr_fo -> fo_mark_proc = mp;
+            /* Reinsert it.  We deleted it first to maintain    */
+            /* consistency in the event of a signal.            */
+            if (prev_fo == 0) {
+              GC_fo_head[index] = curr_fo;
+            } else {
+              fo_set_next(prev_fo, curr_fo);
+            }
+          }
+          UNLOCK();
+#         ifndef DBG_HDRS_ALL
+            if (EXPECT(new_fo != 0, FALSE)) {
+              /* Free unused new_fo returned by GC_oom_fn() */
+              GC_free((void *)new_fo);
+            }
+#         endif
+          return;
+        }
+        prev_fo = curr_fo;
+        curr_fo = fo_next(curr_fo);
+      }
+      if (EXPECT(new_fo != 0, FALSE)) {
+        /* new_fo is returned by GC_oom_fn(), so fn != 0 and hhdr != 0. */
+        break;
+      }
+      if (fn == 0) {
+        if (ocd) *ocd = 0;
+        if (ofn) *ofn = 0;
+        UNLOCK();
+        return;
+      }
+      GET_HDR(base, hhdr);
+      if (EXPECT(0 == hhdr, FALSE)) {
+        /* We won't collect it, hence finalizer wouldn't be run. */
+        if (ocd) *ocd = 0;
+        if (ofn) *ofn = 0;
+        UNLOCK();
+        return;
+      }
+      new_fo = (struct finalizable_object *)
+        GC_INTERNAL_MALLOC(sizeof(struct finalizable_object),NORMAL);
+      if (EXPECT(new_fo != 0, TRUE))
+        break;
+      oom_fn = GC_oom_fn;
+      UNLOCK();
+      new_fo = (struct finalizable_object *)
+                (*oom_fn)(sizeof(struct finalizable_object));
+      if (0 == new_fo) {
+        /* No enough memory.  *ocd and *ofn remains unchanged.  */
+        return;
+      }
+      /* It's not likely we'll make it here, but ... */
+      LOCK();
+      /* Recalculate index since the table may grow and         */
+      /* check again that our finalizer is not in the table.    */
+    }
+    GC_ASSERT(GC_size(new_fo) >= sizeof(struct finalizable_object));
+    if (ocd) *ocd = 0;
+    if (ofn) *ofn = 0;
+    new_fo -> fo_hidden_base = GC_HIDE_POINTER(base);
+    new_fo -> fo_fn = fn;
+    new_fo -> fo_client_data = (ptr_t)cd;
+    new_fo -> fo_object_size = hhdr -> hb_sz;
+    new_fo -> fo_mark_proc = mp;
+    fo_set_next(new_fo, GC_fo_head[index]);
+    GC_fo_entries++;
+    GC_fo_head[index] = new_fo;
+    UNLOCK();
+}
+
+GC_API void GC_CALL GC_register_finalizer(void * obj,
+                                  GC_finalization_proc fn, void * cd,
+                                  GC_finalization_proc *ofn, void ** ocd)
+{
+    GC_register_finalizer_inner(obj, fn, cd, ofn,
+                                ocd, GC_normal_finalize_mark_proc);
+}
+
+GC_API void GC_CALL GC_register_finalizer_ignore_self(void * obj,
+                               GC_finalization_proc fn, void * cd,
+                               GC_finalization_proc *ofn, void ** ocd)
+{
+    GC_register_finalizer_inner(obj, fn, cd, ofn,
+                                ocd, GC_ignore_self_finalize_mark_proc);
+}
+
+GC_API void GC_CALL GC_register_finalizer_no_order(void * obj,
+                               GC_finalization_proc fn, void * cd,
+                               GC_finalization_proc *ofn, void ** ocd)
+{
+    GC_register_finalizer_inner(obj, fn, cd, ofn,
+                                ocd, GC_null_finalize_mark_proc);
+}
+
+static GC_bool need_unreachable_finalization = FALSE;
+        /* Avoid the work if this isn't used.   */
+
+GC_API void GC_CALL GC_register_finalizer_unreachable(void * obj,
+                               GC_finalization_proc fn, void * cd,
+                               GC_finalization_proc *ofn, void ** ocd)
+{
+    need_unreachable_finalization = TRUE;
+    GC_ASSERT(GC_java_finalization);
+    GC_register_finalizer_inner(obj, fn, cd, ofn,
+                                ocd, GC_unreachable_finalize_mark_proc);
+}
+
+#ifndef NO_DEBUGGING
+  STATIC void GC_dump_finalization_links(
+                                const struct dl_hashtbl_s *dl_hashtbl)
+  {
+    struct disappearing_link *curr_dl;
+    ptr_t real_ptr, real_link;
+    size_t dl_size = dl_hashtbl->log_size == -1 ? 0 :
+                                1 << dl_hashtbl->log_size;
+    size_t i;
+
+    for (i = 0; i < dl_size; i++) {
+      for (curr_dl = dl_hashtbl -> head[i]; curr_dl != 0;
+           curr_dl = dl_next(curr_dl)) {
+        real_ptr = GC_REVEAL_POINTER(curr_dl -> dl_hidden_obj);
+        real_link = GC_REVEAL_POINTER(curr_dl -> dl_hidden_link);
+        GC_printf("Object: %p, link: %p\n", real_ptr, real_link);
+      }
+    }
+  }
+
+  void GC_dump_finalization(void)
+  {
+    struct finalizable_object * curr_fo;
+    size_t fo_size = log_fo_table_size == -1 ? 0 : 1 << log_fo_table_size;
+    ptr_t real_ptr;
+    size_t i;
+
+    GC_printf("Disappearing (short) links:\n");
+    GC_dump_finalization_links(&GC_dl_hashtbl);
+#   ifndef GC_LONG_REFS_NOT_NEEDED
+      GC_printf("Disappearing long links:\n");
+      GC_dump_finalization_links(&GC_ll_hashtbl);
+#   endif
+    GC_printf("Finalizers:\n");
+    for (i = 0; i < fo_size; i++) {
+      for (curr_fo = GC_fo_head[i]; curr_fo != 0;
+           curr_fo = fo_next(curr_fo)) {
+        real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
+        GC_printf("Finalizable object: %p\n", real_ptr);
+      }
+    }
+  }
+#endif /* !NO_DEBUGGING */
+
+#ifndef SMALL_CONFIG
+  STATIC word GC_old_dl_entries = 0; /* for stats printing */
+# ifndef GC_LONG_REFS_NOT_NEEDED
+    STATIC word GC_old_ll_entries = 0;
+# endif
+#endif /* !SMALL_CONFIG */
+
+#ifndef THREADS
+  /* Global variables to minimize the level of recursion when a client  */
+  /* finalizer allocates memory.                                        */
+  STATIC int GC_finalizer_nested = 0;
+                        /* Only the lowest byte is used, the rest is    */
+                        /* padding for proper global data alignment     */
+                        /* required for some compilers (like Watcom).   */
+  STATIC unsigned GC_finalizer_skipped = 0;
+
+  /* Checks and updates the level of finalizers recursion.              */
+  /* Returns NULL if GC_invoke_finalizers() should not be called by the */
+  /* collector (to minimize the risk of a deep finalizers recursion),   */
+  /* otherwise returns a pointer to GC_finalizer_nested.                */
+  STATIC unsigned char *GC_check_finalizer_nested(void)
+  {
+    unsigned nesting_level = *(unsigned char *)&GC_finalizer_nested;
+    if (nesting_level) {
+      /* We are inside another GC_invoke_finalizers().          */
+      /* Skip some implicitly-called GC_invoke_finalizers()     */
+      /* depending on the nesting (recursion) level.            */
+      if (++GC_finalizer_skipped < (1U << nesting_level)) return NULL;
+      GC_finalizer_skipped = 0;
+    }
+    *(char *)&GC_finalizer_nested = (char)(nesting_level + 1);
+    return (unsigned char *)&GC_finalizer_nested;
+  }
+#endif /* THREADS */
+
+#define ITERATE_DL_HASHTBL_BEGIN(dl_hashtbl, curr_dl, prev_dl) \
+  { \
+    size_t i; \
+    size_t dl_size = dl_hashtbl->log_size == -1 ? 0 : \
+                                1 << dl_hashtbl->log_size; \
+    for (i = 0; i < dl_size; i++) { \
+      curr_dl = dl_hashtbl -> head[i]; \
+      prev_dl = NULL; \
+      while (curr_dl) {
+
+#define ITERATE_DL_HASHTBL_END(curr_dl, prev_dl) \
+        prev_dl = curr_dl; \
+        curr_dl = dl_next(curr_dl); \
+      } \
+    } \
+  }
+
+#define DELETE_DL_HASHTBL_ENTRY(dl_hashtbl, curr_dl, prev_dl, next_dl) \
+  { \
+    next_dl = dl_next(curr_dl); \
+    if (NULL == prev_dl) { \
+        dl_hashtbl -> head[i] = next_dl; \
+    } else { \
+        dl_set_next(prev_dl, next_dl); \
+    } \
+    GC_clear_mark_bit(curr_dl); \
+    dl_hashtbl -> entries--; \
+    curr_dl = next_dl; \
+    continue; \
+  }
+
+GC_INLINE void GC_make_disappearing_links_disappear(
+                                struct dl_hashtbl_s* dl_hashtbl)
+{
+    struct disappearing_link *curr, *prev, *next;
+    ptr_t real_ptr, real_link;
+
+    ITERATE_DL_HASHTBL_BEGIN(dl_hashtbl, curr, prev)
+        real_ptr = GC_REVEAL_POINTER(curr -> dl_hidden_obj);
+        real_link = GC_REVEAL_POINTER(curr -> dl_hidden_link);
+        if (!GC_is_marked(real_ptr)) {
+            *(word *)real_link = 0;
+            GC_clear_mark_bit(curr);
+            DELETE_DL_HASHTBL_ENTRY(dl_hashtbl, curr, prev, next);
+        }
+    ITERATE_DL_HASHTBL_END(curr, prev)
+}
+
+GC_INLINE void GC_remove_dangling_disappearing_links(
+                                struct dl_hashtbl_s* dl_hashtbl)
+{
+    struct disappearing_link *curr, *prev, *next;
+    ptr_t real_link;
+
+    ITERATE_DL_HASHTBL_BEGIN(dl_hashtbl, curr, prev)
+        real_link = GC_base(GC_REVEAL_POINTER(curr -> dl_hidden_link));
+        if (NULL != real_link && !GC_is_marked(real_link)) {
+            GC_clear_mark_bit(curr);
+            DELETE_DL_HASHTBL_ENTRY(dl_hashtbl, curr, prev, next);
+        }
+    ITERATE_DL_HASHTBL_END(curr, prev)
+}
+
+/* Called with held lock (but the world is running).                    */
+/* Cause disappearing links to disappear and unreachable objects to be  */
+/* enqueued for finalization.                                           */
+GC_INNER void GC_finalize(void)
+{
+    struct finalizable_object * curr_fo, * prev_fo, * next_fo;
+    ptr_t real_ptr;
+    size_t i;
+    size_t fo_size = log_fo_table_size == -1 ? 0 : 1 << log_fo_table_size;
+
+#   ifndef SMALL_CONFIG
+      /* Save current GC_[dl/ll]_entries value for stats printing */
+      GC_old_dl_entries = GC_dl_hashtbl.entries;
+#     ifndef GC_LONG_REFS_NOT_NEEDED
+        GC_old_ll_entries = GC_ll_hashtbl.entries;
+#     endif
+#   endif
+
+    GC_make_disappearing_links_disappear(&GC_dl_hashtbl);
+
+  /* Mark all objects reachable via chains of 1 or more pointers        */
+  /* from finalizable objects.                                          */
+    GC_ASSERT(GC_mark_state == MS_NONE);
+    for (i = 0; i < fo_size; i++) {
+      for (curr_fo = GC_fo_head[i]; curr_fo != 0;
+           curr_fo = fo_next(curr_fo)) {
+        GC_ASSERT(GC_size(curr_fo) >= sizeof(struct finalizable_object));
+        real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
+        if (!GC_is_marked(real_ptr)) {
+            GC_MARKED_FOR_FINALIZATION(real_ptr);
+            GC_MARK_FO(real_ptr, curr_fo -> fo_mark_proc);
+            if (GC_is_marked(real_ptr)) {
+                WARN("Finalization cycle involving %p\n", real_ptr);
+            }
+        }
+      }
+    }
+  /* Enqueue for finalization all objects that are still                */
+  /* unreachable.                                                       */
+    GC_bytes_finalized = 0;
+    for (i = 0; i < fo_size; i++) {
+      curr_fo = GC_fo_head[i];
+      prev_fo = 0;
+      while (curr_fo != 0) {
+        real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
+        if (!GC_is_marked(real_ptr)) {
+            if (!GC_java_finalization) {
+              GC_set_mark_bit(real_ptr);
+            }
+            /* Delete from hash table */
+              next_fo = fo_next(curr_fo);
+              if (prev_fo == 0) {
+                GC_fo_head[i] = next_fo;
+              } else {
+                fo_set_next(prev_fo, next_fo);
+              }
+              GC_fo_entries--;
+            /* Add to list of objects awaiting finalization.    */
+              fo_set_next(curr_fo, GC_finalize_now);
+              GC_finalize_now = curr_fo;
+              /* unhide object pointer so any future collections will   */
+              /* see it.                                                */
+              curr_fo -> fo_hidden_base =
+                        (word)GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
+              GC_bytes_finalized +=
+                        curr_fo -> fo_object_size
+                        + sizeof(struct finalizable_object);
+            GC_ASSERT(GC_is_marked(GC_base(curr_fo)));
+            curr_fo = next_fo;
+        } else {
+            prev_fo = curr_fo;
+            curr_fo = fo_next(curr_fo);
+        }
+      }
+    }
+
+  if (GC_java_finalization) {
+    /* make sure we mark everything reachable from objects finalized
+       using the no_order mark_proc */
+      for (curr_fo = GC_finalize_now;
+         curr_fo != NULL; curr_fo = fo_next(curr_fo)) {
+        real_ptr = (ptr_t)curr_fo -> fo_hidden_base;
+        if (!GC_is_marked(real_ptr)) {
+            if (curr_fo -> fo_mark_proc == GC_null_finalize_mark_proc) {
+                GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc);
+            }
+            if (curr_fo -> fo_mark_proc != GC_unreachable_finalize_mark_proc) {
+                GC_set_mark_bit(real_ptr);
+            }
+        }
+      }
+
+    /* now revive finalize-when-unreachable objects reachable from
+       other finalizable objects */
+      if (need_unreachable_finalization) {
+        curr_fo = GC_finalize_now;
+        prev_fo = 0;
+        while (curr_fo != 0) {
+          next_fo = fo_next(curr_fo);
+          if (curr_fo -> fo_mark_proc == GC_unreachable_finalize_mark_proc) {
+            real_ptr = (ptr_t)curr_fo -> fo_hidden_base;
+            if (!GC_is_marked(real_ptr)) {
+              GC_set_mark_bit(real_ptr);
+            } else {
+              if (prev_fo == 0)
+                GC_finalize_now = next_fo;
+              else
+                fo_set_next(prev_fo, next_fo);
+
+              curr_fo -> fo_hidden_base =
+                                GC_HIDE_POINTER(curr_fo -> fo_hidden_base);
+              GC_bytes_finalized -=
+                  curr_fo->fo_object_size + sizeof(struct finalizable_object);
+
+              i = HASH2(real_ptr, log_fo_table_size);
+              fo_set_next (curr_fo, GC_fo_head[i]);
+              GC_fo_entries++;
+              GC_fo_head[i] = curr_fo;
+              curr_fo = prev_fo;
+            }
+          }
+          prev_fo = curr_fo;
+          curr_fo = next_fo;
+        }
+      }
+  }
+
+  GC_remove_dangling_disappearing_links(&GC_dl_hashtbl);
+# ifndef GC_LONG_REFS_NOT_NEEDED
+    GC_make_disappearing_links_disappear(&GC_ll_hashtbl);
+    GC_remove_dangling_disappearing_links(&GC_ll_hashtbl);
+# endif
+
+  if (GC_fail_count) {
+    /* Don't prevent running finalizers if there has been an allocation */
+    /* failure recently.                                                */
+#   ifdef THREADS
+      GC_reset_finalizer_nested();
+#   else
+      GC_finalizer_nested = 0;
+#   endif
+  }
+}
+
+#ifndef JAVA_FINALIZATION_NOT_NEEDED
+
+  /* Enqueue all remaining finalizers to be run - Assumes lock is held. */
+  STATIC void GC_enqueue_all_finalizers(void)
+  {
+    struct finalizable_object * curr_fo, * prev_fo, * next_fo;
+    ptr_t real_ptr;
+    register int i;
+    int fo_size;
+
+    fo_size = log_fo_table_size == -1 ? 0 : 1 << log_fo_table_size;
+    GC_bytes_finalized = 0;
+    for (i = 0; i < fo_size; i++) {
+        curr_fo = GC_fo_head[i];
+        prev_fo = 0;
+      while (curr_fo != 0) {
+          real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
+          GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc);
+          GC_set_mark_bit(real_ptr);
+
+          /* Delete from hash table */
+          next_fo = fo_next(curr_fo);
+          if (prev_fo == 0) {
+              GC_fo_head[i] = next_fo;
+          } else {
+              fo_set_next(prev_fo, next_fo);
+          }
+          GC_fo_entries--;
+
+          /* Add to list of objects awaiting finalization.      */
+          fo_set_next(curr_fo, GC_finalize_now);
+          GC_finalize_now = curr_fo;
+
+          /* unhide object pointer so any future collections will       */
+          /* see it.                                                    */
+          curr_fo -> fo_hidden_base =
+                        (word)GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
+          GC_bytes_finalized +=
+                curr_fo -> fo_object_size + sizeof(struct finalizable_object);
+          curr_fo = next_fo;
+        }
+    }
+  }
+
+  /* Invoke all remaining finalizers that haven't yet been run.
+   * This is needed for strict compliance with the Java standard,
+   * which can make the runtime guarantee that all finalizers are run.
+   * Unfortunately, the Java standard implies we have to keep running
+   * finalizers until there are no more left, a potential infinite loop.
+   * YUCK.
+   * Note that this is even more dangerous than the usual Java
+   * finalizers, in that objects reachable from static variables
+   * may have been finalized when these finalizers are run.
+   * Finalizers run at this point must be prepared to deal with a
+   * mostly broken world.
+   * This routine is externally callable, so is called without
+   * the allocation lock.
+   */
+  GC_API void GC_CALL GC_finalize_all(void)
+  {
+    DCL_LOCK_STATE;
+
+    LOCK();
+    while (GC_fo_entries > 0) {
+      GC_enqueue_all_finalizers();
+      UNLOCK();
+      GC_invoke_finalizers();
+      /* Running the finalizers in this thread is arguably not a good   */
+      /* idea when we should be notifying another thread to run them.   */
+      /* But otherwise we don't have a great way to wait for them to    */
+      /* run.                                                           */
+      LOCK();
+    }
+    UNLOCK();
+  }
+
+#endif /* !JAVA_FINALIZATION_NOT_NEEDED */
+
+/* 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.)                          */
+GC_API int GC_CALL GC_should_invoke_finalizers(void)
+{
+    return GC_finalize_now != 0;
+}
+
+/* Invoke finalizers for all objects that are ready to be finalized.    */
+/* Should be called without allocation lock.                            */
+GC_API int GC_CALL GC_invoke_finalizers(void)
+{
+    struct finalizable_object * curr_fo;
+    int count = 0;
+    word bytes_freed_before = 0; /* initialized to prevent warning. */
+    DCL_LOCK_STATE;
+
+    while (GC_finalize_now != 0) {
+#       ifdef THREADS
+            LOCK();
+#       endif
+        if (count == 0) {
+            bytes_freed_before = GC_bytes_freed;
+            /* Don't do this outside, since we need the lock. */
+        }
+        curr_fo = GC_finalize_now;
+#       ifdef THREADS
+            if (curr_fo != 0) GC_finalize_now = fo_next(curr_fo);
+            UNLOCK();
+            if (curr_fo == 0) break;
+#       else
+            GC_finalize_now = fo_next(curr_fo);
+#       endif
+        fo_set_next(curr_fo, 0);
+        (*(curr_fo -> fo_fn))((ptr_t)(curr_fo -> fo_hidden_base),
+                              curr_fo -> fo_client_data);
+        curr_fo -> fo_client_data = 0;
+        ++count;
+#       ifdef UNDEFINED
+            /* This is probably a bad idea.  It throws off accounting if */
+            /* nearly all objects are finalizable.  O.w. it shouldn't    */
+            /* matter.                                                   */
+            GC_free((void *)curr_fo);
+#       endif
+    }
+    /* bytes_freed_before is initialized whenever count != 0 */
+    if (count != 0 && bytes_freed_before != GC_bytes_freed) {
+        LOCK();
+        GC_finalizer_bytes_freed += (GC_bytes_freed - bytes_freed_before);
+        UNLOCK();
+    }
+    return count;
+}
+
+static GC_word last_finalizer_notification = 0;
+
+GC_INNER void GC_notify_or_invoke_finalizers(void)
+{
+    GC_finalizer_notifier_proc notifier_fn = 0;
+#   if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH)
+      static word last_back_trace_gc_no = 1;    /* Skip first one. */
+#   endif
+    DCL_LOCK_STATE;
+
+#   if defined(THREADS) && !defined(KEEP_BACK_PTRS) \
+       && !defined(MAKE_BACK_GRAPH)
+      /* Quick check (while unlocked) for an empty finalization queue.  */
+      if (GC_finalize_now == 0) return;
+#   endif
+    LOCK();
+
+    /* This is a convenient place to generate backtraces if appropriate, */
+    /* since that code is not callable with the allocation lock.         */
+#   if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH)
+      if (GC_gc_no > last_back_trace_gc_no) {
+#       ifdef KEEP_BACK_PTRS
+          long i;
+          /* Stops when GC_gc_no wraps; that's OK.      */
+          last_back_trace_gc_no = (word)(-1);  /* disable others. */
+          for (i = 0; i < GC_backtraces; ++i) {
+              /* FIXME: This tolerates concurrent heap mutation,        */
+              /* which may cause occasional mysterious results.         */
+              /* We need to release the GC lock, since GC_print_callers */
+              /* acquires it.  It probably shouldn't.                   */
+              UNLOCK();
+              GC_generate_random_backtrace_no_gc();
+              LOCK();
+          }
+          last_back_trace_gc_no = GC_gc_no;
+#       endif
+#       ifdef MAKE_BACK_GRAPH
+          if (GC_print_back_height) {
+            UNLOCK();
+            GC_print_back_graph_stats();
+            LOCK();
+          }
+#       endif
+      }
+#   endif
+    if (GC_finalize_now == 0) {
+      UNLOCK();
+      return;
+    }
+
+    if (!GC_finalize_on_demand) {
+      unsigned char *pnested = GC_check_finalizer_nested();
+      UNLOCK();
+      /* Skip GC_invoke_finalizers() if nested */
+      if (pnested != NULL) {
+        (void) GC_invoke_finalizers();
+        *pnested = 0; /* Reset since no more finalizers. */
+#       ifndef THREADS
+          GC_ASSERT(GC_finalize_now == 0);
+#       endif   /* Otherwise GC can run concurrently and add more */
+      }
+      return;
+    }
+
+    /* These variables require synchronization to avoid data races.     */
+    if (last_finalizer_notification != GC_gc_no) {
+        last_finalizer_notification = GC_gc_no;
+        notifier_fn = GC_finalizer_notifier;
+    }
+    UNLOCK();
+    if (notifier_fn != 0)
+        (*notifier_fn)(); /* Invoke the notifier */
+}
+
+#ifndef SMALL_CONFIG
+# ifndef GC_LONG_REFS_NOT_NEEDED
+#   define IF_LONG_REFS_PRESENT_ELSE(x,y) (x)
+# else
+#   define IF_LONG_REFS_PRESENT_ELSE(x,y) (y)
+# endif
+
+  GC_INNER void GC_print_finalization_stats(void)
+  {
+    struct finalizable_object *fo;
+    unsigned long ready = 0;
+
+    GC_log_printf("%lu finalization entries;"
+                  " %lu/%lu short/long disappearing links alive\n",
+                  (unsigned long)GC_fo_entries,
+                  (unsigned long)GC_dl_hashtbl.entries,
+                  (unsigned long)IF_LONG_REFS_PRESENT_ELSE(
+                                                GC_ll_hashtbl.entries, 0));
+
+    for (fo = GC_finalize_now; 0 != fo; fo = fo_next(fo))
+      ++ready;
+    GC_log_printf("%lu finalization-ready objects;"
+                  " %ld/%ld short/long links cleared\n",
+                  ready,
+                  (long)GC_old_dl_entries - (long)GC_dl_hashtbl.entries,
+                  (long)IF_LONG_REFS_PRESENT_ELSE(
+                              GC_old_ll_entries - GC_ll_hashtbl.entries, 0));
+  }
+#endif /* !SMALL_CONFIG */
+
+#endif /* !GC_NO_FINALIZATION */

+ 172 - 0
blitz.mod/bdwgc/fnlz_mlc.c

@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2011 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.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ *
+ */
+
+#include "private/gc_priv.h"
+
+#ifdef ENABLE_DISCLAIM
+
+#include "gc_disclaim.h"
+
+#ifdef THREAD_LOCAL_ALLOC
+# include "private/thread_local_alloc.h"
+#else
+  STATIC ptr_t * GC_finalized_objfreelist = NULL;
+#endif /* !THREAD_LOCAL_ALLOC */
+
+STATIC int GC_finalized_kind = 0;
+
+STATIC int GC_CALLBACK GC_finalized_disclaim(void *obj)
+{
+    void **fc_addr;
+    const struct GC_finalizer_closure *fc;
+
+    fc_addr = &((void **)obj)[GC_size(obj) / sizeof(void *) - 1];
+    fc = *fc_addr;
+    if (fc != NULL) {
+       /* [1] The disclaim function may be passed fragments from the    */
+       /* free-list, on which it should not run finalization.           */
+       /* To recognize this case, we use the fact that the first word   */
+       /* on such fragments are always even (a link to the next         */
+       /* fragment, or NULL).  If it is desirable to have a finalizer   */
+       /* which does not use the first word for storing finalization    */
+       /* info, GC_reclaim_with_finalization must be extended to clear  */
+       /* fragments so that the assumption holds for the selected word. */
+        (*fc->proc)(obj, fc->cd);
+        *fc_addr = NULL;
+    }
+    return 0;
+}
+
+static GC_bool done_init = FALSE;
+
+GC_API void GC_CALL GC_init_finalized_malloc(void)
+{
+    DCL_LOCK_STATE;
+
+    GC_init();  /* In case it's not already done.       */
+    LOCK();
+    if (done_init) {
+        UNLOCK();
+        return;
+    }
+    done_init = TRUE;
+
+    GC_finalized_objfreelist = (ptr_t *)GC_new_free_list_inner();
+    GC_finalized_kind = GC_new_kind_inner((void **)GC_finalized_objfreelist,
+                                          GC_DS_LENGTH, TRUE, TRUE);
+    GC_register_disclaim_proc(GC_finalized_kind, GC_finalized_disclaim, TRUE);
+    UNLOCK();
+}
+
+GC_API void GC_CALL GC_register_disclaim_proc(int kind, GC_disclaim_proc proc,
+                                              int mark_unconditionally)
+{
+    GC_ASSERT((unsigned)kind < MAXOBJKINDS);
+    GC_obj_kinds[kind].ok_disclaim_proc = proc;
+    GC_obj_kinds[kind].ok_mark_unconditionally = (GC_bool)mark_unconditionally;
+}
+
+#ifdef THREAD_LOCAL_ALLOC
+  STATIC void * GC_core_finalized_malloc(size_t lb,
+                                const struct GC_finalizer_closure *fclos)
+#else
+  GC_API void * GC_CALL GC_finalized_malloc(size_t lb,
+                                const struct GC_finalizer_closure *fclos)
+#endif
+{
+    ptr_t op;
+    ptr_t *opp;
+    word lg;
+    DCL_LOCK_STATE;
+
+    lb += sizeof(void *);
+    GC_ASSERT(done_init);
+    if (SMALL_OBJ(lb)) {
+        GC_DBG_COLLECT_AT_MALLOC(lb);
+        lg = GC_size_map[lb];
+        opp = &GC_finalized_objfreelist[lg];
+        LOCK();
+        op = *opp;
+        if (EXPECT(0 == op, FALSE)) {
+            UNLOCK();
+            op = GC_generic_malloc((word)lb, GC_finalized_kind);
+            if (NULL == op)
+                return NULL;
+            /* GC_generic_malloc has extended the size map for us.      */
+            lg = GC_size_map[lb];
+        } else {
+            *opp = obj_link(op);
+            obj_link(op) = 0;
+            GC_bytes_allocd += GRANULES_TO_BYTES(lg);
+            UNLOCK();
+        }
+        GC_ASSERT(lg > 0);
+        ((const void **)op)[GRANULES_TO_WORDS(lg) - 1] = fclos;
+    } else {
+        size_t op_sz;
+
+        op = GC_generic_malloc((word)lb, GC_finalized_kind);
+        if (NULL == op)
+            return NULL;
+        op_sz = GC_size(op);
+        GC_ASSERT(op_sz >= lb);
+        ((const void **)op)[op_sz / sizeof(void *) - 1] = fclos;
+    }
+    return GC_clear_stack(op);
+}
+
+#ifdef THREAD_LOCAL_ALLOC
+  GC_API void * GC_CALL GC_finalized_malloc(size_t client_lb,
+                                const struct GC_finalizer_closure *fclos)
+  {
+    size_t lb = client_lb + sizeof(void *);
+    size_t lg = ROUNDED_UP_GRANULES(lb);
+    GC_tlfs tsd;
+    void *result;
+    void **tiny_fl, **my_fl, *my_entry;
+    void *next;
+
+    if (EXPECT(lg >= GC_TINY_FREELISTS, FALSE))
+        return GC_core_finalized_malloc(client_lb, fclos);
+
+    tsd = GC_getspecific(GC_thread_key);
+    tiny_fl = tsd->finalized_freelists;
+    my_fl = tiny_fl + lg;
+    my_entry = *my_fl;
+    while (EXPECT((word)my_entry
+                  <= DIRECT_GRANULES + GC_TINY_FREELISTS + 1, FALSE)) {
+        if ((word)my_entry - 1 < DIRECT_GRANULES) {
+            *my_fl = (ptr_t)my_entry + lg + 1;
+            return GC_core_finalized_malloc(client_lb, fclos);
+        } else {
+            GC_generic_malloc_many(GC_RAW_BYTES_FROM_INDEX(lg),
+                                   GC_finalized_kind, my_fl);
+            my_entry = *my_fl;
+            if (my_entry == 0) {
+                return (*GC_get_oom_fn())(lb);
+            }
+        }
+    }
+
+    next = obj_link(my_entry);
+    result = (void *)my_entry;
+    *my_fl = next;
+    obj_link(result) = 0;
+    ((const void **)result)[GRANULES_TO_WORDS(lg) - 1] = fclos;
+    PREFETCH_FOR_WRITE(next);
+    return result;
+  }
+#endif /* THREAD_LOCAL_ALLOC */
+
+#endif /* ENABLE_DISCLAIM */

+ 88 - 0
blitz.mod/bdwgc/gc_cpp.cc

@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to copy this code for any purpose,
+ * provided the above notices are retained on all copies.
+ */
+
+/*************************************************************************
+This implementation module for gc_c++.h provides an implementation of
+the global operators "new" and "delete" that calls the Boehm
+allocator.  All objects allocated by this implementation will be
+uncollectible but part of the root set of the collector.
+
+You should ensure (using implementation-dependent techniques) that the
+linker finds this module before the library that defines the default
+built-in "new" and "delete".
+**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifndef GC_BUILD
+# define GC_BUILD
+#endif
+
+#include "gc_cpp.h"
+
+#if !defined(GC_NEW_DELETE_NEED_THROW) && defined(__GNUC__) \
+    && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
+# define GC_NEW_DELETE_NEED_THROW
+#endif
+
+#ifdef GC_NEW_DELETE_NEED_THROW
+# include <new> /* for std::bad_alloc */
+# define GC_DECL_NEW_THROW throw(std::bad_alloc)
+# define GC_DECL_DELETE_THROW throw()
+#else
+# define GC_DECL_NEW_THROW /* empty */
+# define GC_DECL_DELETE_THROW /* empty */
+#endif /* !GC_NEW_DELETE_NEED_THROW */
+
+void* operator new( size_t size ) GC_DECL_NEW_THROW {
+  return GC_MALLOC_UNCOLLECTABLE(size);
+}
+
+#if !defined(__CYGWIN__)
+  void operator delete( void* obj ) GC_DECL_DELETE_THROW {
+    GC_FREE(obj);
+  }
+#endif /* !__CYGWIN__ */
+
+#ifdef GC_OPERATOR_NEW_ARRAY
+  void* operator new[]( size_t size ) GC_DECL_NEW_THROW {
+    return GC_MALLOC_UNCOLLECTABLE(size);
+  }
+
+  void operator delete[]( void* obj ) GC_DECL_DELETE_THROW {
+    GC_FREE(obj);
+  }
+#endif /* GC_OPERATOR_NEW_ARRAY */
+
+#ifdef _MSC_VER
+
+  // This new operator is used by VC++ in case of Debug builds!
+  void* operator new( size_t size, int /* nBlockUse */,
+                     const char * szFileName, int nLine ) GC_DECL_NEW_THROW
+  {
+#   ifndef GC_DEBUG
+      return GC_malloc_uncollectable(size);
+#   else
+      return GC_debug_malloc_uncollectable(size, szFileName, nLine);
+#   endif
+  }
+
+# if _MSC_VER > 1020
+    // This new operator is used by VC++ 7.0 and later in Debug builds.
+    void* operator new[]( size_t size, int nBlockUse,
+                         const char* szFileName, int nLine ) GC_DECL_NEW_THROW
+    {
+      return operator new(size, nBlockUse, szFileName, nLine);
+    }
+# endif
+
+#endif /* _MSC_VER */

+ 2 - 0
blitz.mod/bdwgc/gc_cpp.cpp

@@ -0,0 +1,2 @@
+// Visual C++ seems to prefer a .cpp extension to .cc
+#include "gc_cpp.cc"

+ 100 - 0
blitz.mod/bdwgc/gc_dlopen.c

@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1997 by Silicon Graphics.  All rights reserved.
+ * Copyright (c) 2000 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.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+#include "private/gc_priv.h"
+
+/* This used to be in dyn_load.c.  It was extracted into a separate     */
+/* file to avoid having to link against libdl.{a,so} if the client      */
+/* doesn't call dlopen.  Of course this fails if the collector is in    */
+/* a dynamic library. -HB                                               */
+#if defined(GC_PTHREADS) && !defined(GC_NO_DLOPEN)
+
+#undef GC_MUST_RESTORE_REDEFINED_DLOPEN
+#if defined(dlopen) && !defined(GC_USE_LD_WRAP)
+  /* To support various threads pkgs, 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    */
+  /* real system dlopen() in their implementation. We first remove      */
+  /* gc.h's dlopen definition and restore it later, after GC_dlopen().  */
+# undef dlopen
+# define GC_MUST_RESTORE_REDEFINED_DLOPEN
+#endif
+
+/* Make sure we're not in the middle of a collection, and make sure we  */
+/* don't start any.  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 (or, even, heap overflow) in the presence of many  */
+/* dlopen calls in either a multi-threaded environment, or if the       */
+/* library initialization code allocates substantial amounts of GC'ed   */
+/* memory.                                                              */
+#ifndef USE_PROC_FOR_LIBRARIES
+  static void disable_gc_for_dlopen(void)
+  {
+    DCL_LOCK_STATE;
+    LOCK();
+    while (GC_incremental && GC_collection_in_progress()) {
+      GC_collect_a_little_inner(1000);
+    }
+    ++GC_dont_gc;
+    UNLOCK();
+  }
+#endif
+
+/* Redefine dlopen to guarantee mutual exclusion with           */
+/* GC_register_dynamic_libraries.  Should probably happen for   */
+/* other operating systems, too.                                */
+
+/* This is similar to WRAP/REAL_FUNC() in pthread_support.c.    */
+#ifdef GC_USE_LD_WRAP
+# define WRAP_DLFUNC(f) __wrap_##f
+# define REAL_DLFUNC(f) __real_##f
+  void * REAL_DLFUNC(dlopen)(const char *, int);
+#else
+# define WRAP_DLFUNC(f) GC_##f
+# define REAL_DLFUNC(f) f
+#endif
+
+GC_API void * WRAP_DLFUNC(dlopen)(const char *path, int mode)
+{
+  void * result;
+
+# ifndef USE_PROC_FOR_LIBRARIES
+    /* Disable collections.  This solution risks heap growth (or,       */
+    /* even, heap overflow) but there seems no better solutions.        */
+    disable_gc_for_dlopen();
+# endif
+  result = REAL_DLFUNC(dlopen)(path, mode);
+# ifndef USE_PROC_FOR_LIBRARIES
+    GC_enable(); /* undoes disable_gc_for_dlopen */
+# endif
+  return(result);
+}
+
+#ifdef GC_USE_LD_WRAP
+  /* Define GC_ function as an alias for the plain one, which will be   */
+  /* intercepted.  This allows files which include gc.h, and hence      */
+  /* generate references to the GC_ symbol, to see the right symbol.    */
+  GC_API void *GC_dlopen(const char *path, int mode)
+  {
+    return dlopen(path, mode);
+  }
+#endif /* GC_USE_LD_WRAP */
+
+#ifdef GC_MUST_RESTORE_REDEFINED_DLOPEN
+# define dlopen GC_dlopen
+#endif
+
+#endif  /* GC_PTHREADS && !GC_NO_DLOPEN */

+ 278 - 0
blitz.mod/bdwgc/gcj_mlc.c

@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ *
+ */
+
+#include "private/gc_pmark.h"  /* includes gc_priv.h */
+
+#ifdef GC_GCJ_SUPPORT
+
+/*
+ * This is an allocator interface tuned for gcj (the GNU static
+ * java compiler).
+ *
+ * Each allocated object has a pointer in its first word to a vtable,
+ * which for our purposes is simply a structure describing the type of
+ * the object.
+ * This descriptor structure contains a GC marking descriptor at offset
+ * MARK_DESCR_OFFSET.
+ *
+ * It is hoped that this interface may also be useful for other systems,
+ * possibly with some tuning of the constants.  But the immediate goal
+ * is to get better gcj performance.
+ *
+ * We assume:
+ *  1) Counting on explicit initialization of this interface is OK;
+ *  2) FASTLOCK is not a significant win.
+ */
+
+#include "gc_gcj.h"
+#include "private/dbg_mlc.h"
+
+#ifdef GC_ASSERTIONS
+  GC_INNER /* variable is also used in thread_local_alloc.c */
+#else
+  STATIC
+#endif
+GC_bool GC_gcj_malloc_initialized = FALSE;
+
+int GC_gcj_kind = 0;    /* Object kind for objects with descriptors     */
+                        /* in "vtable".                                 */
+int GC_gcj_debug_kind = 0;
+                        /* The kind of objects that is always marked    */
+                        /* with a mark proc call.                       */
+
+GC_INNER ptr_t * GC_gcjobjfreelist = NULL;
+
+STATIC ptr_t * GC_gcjdebugobjfreelist = NULL;
+
+STATIC struct GC_ms_entry * GC_gcj_fake_mark_proc(word * addr GC_ATTR_UNUSED,
+                        struct GC_ms_entry *mark_stack_ptr,
+                        struct GC_ms_entry * mark_stack_limit GC_ATTR_UNUSED,
+                        word env GC_ATTR_UNUSED)
+{
+    ABORT_RET("No client gcj mark proc is specified");
+    return mark_stack_ptr;
+}
+
+/* Caller does not hold allocation lock. */
+GC_API void GC_CALL GC_init_gcj_malloc(int mp_index,
+                                       void * /* really GC_mark_proc */mp)
+{
+    GC_bool ignore_gcj_info;
+    DCL_LOCK_STATE;
+
+    if (mp == 0)        /* In case GC_DS_PROC is unused.        */
+      mp = (void *)(word)GC_gcj_fake_mark_proc;
+
+    GC_init();  /* In case it's not already done.       */
+    LOCK();
+    if (GC_gcj_malloc_initialized) {
+      UNLOCK();
+      return;
+    }
+    GC_gcj_malloc_initialized = TRUE;
+#   ifdef GC_IGNORE_GCJ_INFO
+      /* This is useful for debugging on platforms with missing getenv(). */
+      ignore_gcj_info = 1;
+#   else
+      ignore_gcj_info = (0 != GETENV("GC_IGNORE_GCJ_INFO"));
+#   endif
+    if (ignore_gcj_info) {
+      GC_COND_LOG_PRINTF("Gcj-style type information is disabled!\n");
+    }
+    GC_ASSERT(GC_mark_procs[mp_index] == (GC_mark_proc)0); /* unused */
+    GC_mark_procs[mp_index] = (GC_mark_proc)(word)mp;
+    if ((unsigned)mp_index >= GC_n_mark_procs)
+        ABORT("GC_init_gcj_malloc: bad index");
+    /* Set up object kind gcj-style indirect descriptor. */
+      GC_gcjobjfreelist = (ptr_t *)GC_new_free_list_inner();
+      if (ignore_gcj_info) {
+        /* Use a simple length-based descriptor, thus forcing a fully   */
+        /* conservative scan.                                           */
+        GC_gcj_kind = GC_new_kind_inner((void **)GC_gcjobjfreelist,
+                                        (0 | GC_DS_LENGTH),
+                                        TRUE, TRUE);
+      } else {
+        GC_gcj_kind = GC_new_kind_inner(
+                        (void **)GC_gcjobjfreelist,
+                        (((word)(-(signed_word)MARK_DESCR_OFFSET
+                                 - GC_INDIR_PER_OBJ_BIAS))
+                         | GC_DS_PER_OBJECT),
+                        FALSE, TRUE);
+      }
+    /* Set up object kind for objects that require mark proc call.      */
+      if (ignore_gcj_info) {
+        GC_gcj_debug_kind = GC_gcj_kind;
+        GC_gcjdebugobjfreelist = GC_gcjobjfreelist;
+      } else {
+        GC_gcjdebugobjfreelist = (ptr_t *)GC_new_free_list_inner();
+        GC_gcj_debug_kind = GC_new_kind_inner(
+                                (void **)GC_gcjdebugobjfreelist,
+                                GC_MAKE_PROC(mp_index,
+                                             1 /* allocated with debug info */),
+                                FALSE, TRUE);
+      }
+    UNLOCK();
+}
+
+#define GENERAL_MALLOC_INNER(lb,k) \
+    GC_clear_stack(GC_generic_malloc_inner(lb, k))
+
+#define GENERAL_MALLOC_INNER_IOP(lb,k) \
+    GC_clear_stack(GC_generic_malloc_inner_ignore_off_page(lb, k))
+
+/* We need a mechanism to release the lock and invoke finalizers.       */
+/* We don't really have an opportunity to do this on a rarely executed  */
+/* path on which the lock is not held.  Thus we check at a              */
+/* rarely executed point at which it is safe to release the lock.       */
+/* We do this even where we could just call GC_INVOKE_FINALIZERS,       */
+/* since it's probably cheaper and certainly more uniform.              */
+/* FIXME - Consider doing the same elsewhere?                           */
+static void maybe_finalize(void)
+{
+   static word last_finalized_no = 0;
+   DCL_LOCK_STATE;
+
+   if (GC_gc_no == last_finalized_no ||
+       !EXPECT(GC_is_initialized, TRUE)) return;
+   UNLOCK();
+   GC_INVOKE_FINALIZERS();
+   LOCK();
+   last_finalized_no = GC_gc_no;
+}
+
+/* Allocate an object, clear it, and store the pointer to the   */
+/* type structure (vtable in gcj).                              */
+/* This adds a byte at the end of the object if GC_malloc would.*/
+#ifdef THREAD_LOCAL_ALLOC
+  GC_INNER void * GC_core_gcj_malloc(size_t lb,
+                                     void * ptr_to_struct_containing_descr)
+#else
+  GC_API void * GC_CALL GC_gcj_malloc(size_t lb,
+                                      void * ptr_to_struct_containing_descr)
+#endif
+{
+    ptr_t op;
+    ptr_t * opp;
+    word lg;
+    DCL_LOCK_STATE;
+
+    GC_DBG_COLLECT_AT_MALLOC(lb);
+    if(SMALL_OBJ(lb)) {
+        lg = GC_size_map[lb];
+        opp = &(GC_gcjobjfreelist[lg]);
+        LOCK();
+        op = *opp;
+        if(EXPECT(op == 0, FALSE)) {
+            maybe_finalize();
+            op = (ptr_t)GENERAL_MALLOC_INNER((word)lb, GC_gcj_kind);
+            if (0 == op) {
+                GC_oom_func oom_fn = GC_oom_fn;
+                UNLOCK();
+                return((*oom_fn)(lb));
+            }
+        } else {
+            *opp = obj_link(op);
+            GC_bytes_allocd += GRANULES_TO_BYTES(lg);
+        }
+        *(void **)op = ptr_to_struct_containing_descr;
+        GC_ASSERT(((void **)op)[1] == 0);
+        UNLOCK();
+    } else {
+        LOCK();
+        maybe_finalize();
+        op = (ptr_t)GENERAL_MALLOC_INNER((word)lb, GC_gcj_kind);
+        if (0 == op) {
+            GC_oom_func oom_fn = GC_oom_fn;
+            UNLOCK();
+            return((*oom_fn)(lb));
+        }
+        *(void **)op = ptr_to_struct_containing_descr;
+        UNLOCK();
+    }
+    return((void *) op);
+}
+
+/* Similar to GC_gcj_malloc, but add debug info.  This is allocated     */
+/* with GC_gcj_debug_kind.                                              */
+GC_API void * GC_CALL GC_debug_gcj_malloc(size_t lb,
+                void * ptr_to_struct_containing_descr, GC_EXTRA_PARAMS)
+{
+    void * result;
+    DCL_LOCK_STATE;
+
+    /* We're careful to avoid extra calls, which could          */
+    /* confuse the backtrace.                                   */
+    LOCK();
+    maybe_finalize();
+    result = GC_generic_malloc_inner(lb + DEBUG_BYTES, GC_gcj_debug_kind);
+    if (result == 0) {
+        GC_oom_func oom_fn = GC_oom_fn;
+        UNLOCK();
+        GC_err_printf("GC_debug_gcj_malloc(%lu, %p) returning NULL (%s:%d)\n",
+                (unsigned long)lb, ptr_to_struct_containing_descr, s, i);
+        return((*oom_fn)(lb));
+    }
+    *((void **)((ptr_t)result + sizeof(oh))) = ptr_to_struct_containing_descr;
+    UNLOCK();
+    if (!GC_debugging_started) {
+        GC_start_debugging();
+    }
+    ADD_CALL_CHAIN(result, ra);
+    return (GC_store_debug_info(result, (word)lb, s, i));
+}
+
+/* There is no THREAD_LOCAL_ALLOC for GC_gcj_malloc_ignore_off_page().  */
+GC_API void * GC_CALL GC_gcj_malloc_ignore_off_page(size_t lb,
+                                     void * ptr_to_struct_containing_descr)
+{
+    ptr_t op;
+    ptr_t * opp;
+    word lg;
+    DCL_LOCK_STATE;
+
+    GC_DBG_COLLECT_AT_MALLOC(lb);
+    if(SMALL_OBJ(lb)) {
+        lg = GC_size_map[lb];
+        opp = &(GC_gcjobjfreelist[lg]);
+        LOCK();
+        op = *opp;
+        if (EXPECT(0 == op, FALSE)) {
+            maybe_finalize();
+            op = (ptr_t)GENERAL_MALLOC_INNER_IOP(lb, GC_gcj_kind);
+            if (0 == op) {
+                GC_oom_func oom_fn = GC_oom_fn;
+                UNLOCK();
+                return((*oom_fn)(lb));
+            }
+        } else {
+            *opp = obj_link(op);
+            GC_bytes_allocd += GRANULES_TO_BYTES(lg);
+        }
+    } else {
+        LOCK();
+        maybe_finalize();
+        op = (ptr_t)GENERAL_MALLOC_INNER_IOP(lb, GC_gcj_kind);
+        if (0 == op) {
+            GC_oom_func oom_fn = GC_oom_fn;
+            UNLOCK();
+            return((*oom_fn)(lb));
+        }
+    }
+    *(void **)op = ptr_to_struct_containing_descr;
+    UNLOCK();
+    return((void *) op);
+}
+
+#endif  /* GC_GCJ_SUPPORT */

+ 406 - 0
blitz.mod/bdwgc/headers.c

@@ -0,0 +1,406 @@
+/*
+ * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
+ * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+#include "private/gc_priv.h"
+
+/*
+ * This implements:
+ * 1. allocation of heap block headers
+ * 2. A map from addresses to heap block addresses to heap block headers
+ *
+ * Access speed is crucial.  We implement an index structure based on a 2
+ * level tree.
+ */
+
+STATIC bottom_index * GC_all_bottom_indices = 0;
+                                /* Pointer to first (lowest addr) */
+                                /* bottom_index.                  */
+
+STATIC bottom_index * GC_all_bottom_indices_end = 0;
+                                /* Pointer to last (highest addr) */
+                                /* bottom_index.                  */
+
+/* Non-macro version of header location routine */
+GC_INNER hdr * GC_find_header(ptr_t h)
+{
+#   ifdef HASH_TL
+        hdr * result;
+        GET_HDR(h, result);
+        return(result);
+#   else
+        return(HDR_INNER(h));
+#   endif
+}
+
+/* Handle a header cache miss.  Returns a pointer to the        */
+/* header corresponding to p, if p can possibly be a valid      */
+/* object pointer, and 0 otherwise.                             */
+/* GUARANTEED to return 0 for a pointer past the first page     */
+/* of an object unless both GC_all_interior_pointers is set     */
+/* and p is in fact a valid object pointer.                     */
+/* Never returns a pointer to a free hblk.                      */
+GC_INNER hdr *
+#ifdef PRINT_BLACK_LIST
+  GC_header_cache_miss(ptr_t p, hdr_cache_entry *hce, ptr_t source)
+#else
+  GC_header_cache_miss(ptr_t p, hdr_cache_entry *hce)
+#endif
+{
+  hdr *hhdr;
+  HC_MISS();
+  GET_HDR(p, hhdr);
+  if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) {
+    if (GC_all_interior_pointers) {
+      if (hhdr != 0) {
+        ptr_t current = p;
+
+        current = (ptr_t)HBLKPTR(current);
+        do {
+            current = current - HBLKSIZE*(word)hhdr;
+            hhdr = HDR(current);
+        } while(IS_FORWARDING_ADDR_OR_NIL(hhdr));
+        /* current points to near the start of the large object */
+        if (hhdr -> hb_flags & IGNORE_OFF_PAGE)
+            return 0;
+        if (HBLK_IS_FREE(hhdr)
+            || p - current >= (ptrdiff_t)(hhdr->hb_sz)) {
+            GC_ADD_TO_BLACK_LIST_NORMAL(p, source);
+            /* Pointer past the end of the block */
+            return 0;
+        }
+      } else {
+        GC_ADD_TO_BLACK_LIST_NORMAL(p, source);
+        /* And return zero: */
+      }
+      GC_ASSERT(hhdr == 0 || !HBLK_IS_FREE(hhdr));
+      return hhdr;
+      /* Pointers past the first page are probably too rare     */
+      /* to add them to the cache.  We don't.                   */
+      /* And correctness relies on the fact that we don't.      */
+    } else {
+      if (hhdr == 0) {
+        GC_ADD_TO_BLACK_LIST_NORMAL(p, source);
+      }
+      return 0;
+    }
+  } else {
+    if (HBLK_IS_FREE(hhdr)) {
+      GC_ADD_TO_BLACK_LIST_NORMAL(p, source);
+      return 0;
+    } else {
+      hce -> block_addr = (word)(p) >> LOG_HBLKSIZE;
+      hce -> hce_hdr = hhdr;
+      return hhdr;
+    }
+  }
+}
+
+/* Routines to dynamically allocate collector data structures that will */
+/* never be freed.                                                       */
+
+static ptr_t scratch_free_ptr = 0;
+
+/* GC_scratch_last_end_ptr is end point of last obtained scratch area.  */
+/* GC_scratch_end_ptr is end point of current scratch area.             */
+
+GC_INNER ptr_t GC_scratch_alloc(size_t bytes)
+{
+    register ptr_t result = scratch_free_ptr;
+
+    bytes += GRANULE_BYTES-1;
+    bytes &= ~(GRANULE_BYTES-1);
+    scratch_free_ptr += bytes;
+    if ((word)scratch_free_ptr <= (word)GC_scratch_end_ptr) {
+        return(result);
+    }
+    {
+        word bytes_to_get = MINHINCR * HBLKSIZE;
+
+        if (bytes_to_get <= bytes) {
+          /* Undo the damage, and get memory directly */
+            bytes_to_get = bytes;
+#           ifdef USE_MMAP
+                bytes_to_get += GC_page_size - 1;
+                bytes_to_get &= ~(GC_page_size - 1);
+#           endif
+            result = (ptr_t)GET_MEM(bytes_to_get);
+            GC_add_to_our_memory(result, bytes_to_get);
+            scratch_free_ptr -= bytes;
+            GC_scratch_last_end_ptr = result + bytes;
+            return(result);
+        }
+        result = (ptr_t)GET_MEM(bytes_to_get);
+        GC_add_to_our_memory(result, bytes_to_get);
+        if (result == 0) {
+            WARN("Out of memory - trying to allocate less\n", 0);
+            scratch_free_ptr -= bytes;
+            bytes_to_get = bytes;
+#           ifdef USE_MMAP
+                bytes_to_get += GC_page_size - 1;
+                bytes_to_get &= ~(GC_page_size - 1);
+#           endif
+            result = (ptr_t)GET_MEM(bytes_to_get);
+            GC_add_to_our_memory(result, bytes_to_get);
+            return result;
+        }
+        scratch_free_ptr = result;
+        GC_scratch_end_ptr = scratch_free_ptr + bytes_to_get;
+        GC_scratch_last_end_ptr = GC_scratch_end_ptr;
+        return(GC_scratch_alloc(bytes));
+    }
+}
+
+static hdr * hdr_free_list = 0;
+
+/* Return an uninitialized header */
+static hdr * alloc_hdr(void)
+{
+    register hdr * result;
+
+    if (hdr_free_list == 0) {
+        result = (hdr *) GC_scratch_alloc((word)(sizeof(hdr)));
+    } else {
+        result = hdr_free_list;
+        hdr_free_list = (hdr *) (result -> hb_next);
+    }
+    return(result);
+}
+
+GC_INLINE void free_hdr(hdr * hhdr)
+{
+    hhdr -> hb_next = (struct hblk *) hdr_free_list;
+    hdr_free_list = hhdr;
+}
+
+#ifdef COUNT_HDR_CACHE_HITS
+  /* Used for debugging/profiling (the symbols are externally visible). */
+  word GC_hdr_cache_hits = 0;
+  word GC_hdr_cache_misses = 0;
+#endif
+
+GC_INNER void GC_init_headers(void)
+{
+    register unsigned i;
+
+    GC_all_nils = (bottom_index *)GC_scratch_alloc((word)sizeof(bottom_index));
+    if (GC_all_nils == NULL) {
+      GC_err_printf("Insufficient memory for GC_all_nils\n");
+      EXIT();
+    }
+    BZERO(GC_all_nils, sizeof(bottom_index));
+    for (i = 0; i < TOP_SZ; i++) {
+        GC_top_index[i] = GC_all_nils;
+    }
+}
+
+/* Make sure that there is a bottom level index block for address addr  */
+/* Return FALSE on failure.                                             */
+static GC_bool get_index(word addr)
+{
+    word hi = (word)(addr) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE);
+    bottom_index * r;
+    bottom_index * p;
+    bottom_index ** prev;
+    bottom_index *pi;
+
+#   ifdef HASH_TL
+      word i = TL_HASH(hi);
+      bottom_index * old;
+
+      old = p = GC_top_index[i];
+      while(p != GC_all_nils) {
+          if (p -> key == hi) return(TRUE);
+          p = p -> hash_link;
+      }
+      r = (bottom_index*)GC_scratch_alloc((word)(sizeof (bottom_index)));
+      if (r == 0) return(FALSE);
+      BZERO(r, sizeof (bottom_index));
+      r -> hash_link = old;
+      GC_top_index[i] = r;
+#   else
+      if (GC_top_index[hi] != GC_all_nils) return(TRUE);
+      r = (bottom_index*)GC_scratch_alloc((word)(sizeof (bottom_index)));
+      if (r == 0) return(FALSE);
+      GC_top_index[hi] = r;
+      BZERO(r, sizeof (bottom_index));
+#   endif
+    r -> key = hi;
+    /* Add it to the list of bottom indices */
+      prev = &GC_all_bottom_indices;    /* pointer to p */
+      pi = 0;                           /* bottom_index preceding p */
+      while ((p = *prev) != 0 && p -> key < hi) {
+        pi = p;
+        prev = &(p -> asc_link);
+      }
+      r -> desc_link = pi;
+      if (0 == p) {
+        GC_all_bottom_indices_end = r;
+      } else {
+        p -> desc_link = r;
+      }
+      r -> asc_link = p;
+      *prev = r;
+    return(TRUE);
+}
+
+/* Install a header for block h.        */
+/* The header is uninitialized.         */
+/* Returns the header or 0 on failure.  */
+GC_INNER struct hblkhdr * GC_install_header(struct hblk *h)
+{
+    hdr * result;
+
+    if (!get_index((word) h)) return(0);
+    result = alloc_hdr();
+    if (result) {
+      SET_HDR(h, result);
+#     ifdef USE_MUNMAP
+        result -> hb_last_reclaimed = (unsigned short)GC_gc_no;
+#     endif
+    }
+    return(result);
+}
+
+/* Set up forwarding counts for block h of size sz */
+GC_INNER GC_bool GC_install_counts(struct hblk *h, size_t sz/* bytes */)
+{
+    struct hblk * hbp;
+    word i;
+
+    for (hbp = h; (word)hbp < (word)h + sz; hbp += BOTTOM_SZ) {
+        if (!get_index((word) hbp)) return(FALSE);
+    }
+    if (!get_index((word)h + sz - 1)) return(FALSE);
+    for (hbp = h + 1; (word)hbp < (word)h + sz; hbp += 1) {
+        i = HBLK_PTR_DIFF(hbp, h);
+        SET_HDR(hbp, (hdr *)(i > MAX_JUMP? MAX_JUMP : i));
+    }
+    return(TRUE);
+}
+
+/* Remove the header for block h */
+GC_INNER void GC_remove_header(struct hblk *h)
+{
+    hdr **ha;
+    GET_HDR_ADDR(h, ha);
+    free_hdr(*ha);
+    *ha = 0;
+}
+
+/* Remove forwarding counts for h */
+GC_INNER void GC_remove_counts(struct hblk *h, size_t sz/* bytes */)
+{
+    register struct hblk * hbp;
+    for (hbp = h+1; (word)hbp < (word)h + sz; hbp += 1) {
+        SET_HDR(hbp, 0);
+    }
+}
+
+/* Apply fn to all allocated blocks */
+/*VARARGS1*/
+void GC_apply_to_all_blocks(void (*fn)(struct hblk *h, word client_data),
+                            word client_data)
+{
+    signed_word j;
+    bottom_index * index_p;
+
+    for (index_p = GC_all_bottom_indices; index_p != 0;
+         index_p = index_p -> asc_link) {
+        for (j = BOTTOM_SZ-1; j >= 0;) {
+            if (!IS_FORWARDING_ADDR_OR_NIL(index_p->index[j])) {
+                if (!HBLK_IS_FREE(index_p->index[j])) {
+                    (*fn)(((struct hblk *)
+                              (((index_p->key << LOG_BOTTOM_SZ) + (word)j)
+                               << LOG_HBLKSIZE)),
+                          client_data);
+                }
+                j--;
+             } else if (index_p->index[j] == 0) {
+                j--;
+             } else {
+                j -= (signed_word)(index_p->index[j]);
+             }
+         }
+     }
+}
+
+/* Get the next valid block whose address is at least h */
+/* Return 0 if there is none.                           */
+GC_INNER struct hblk * GC_next_used_block(struct hblk *h)
+{
+    register bottom_index * bi;
+    register word j = ((word)h >> LOG_HBLKSIZE) & (BOTTOM_SZ-1);
+
+    GET_BI(h, bi);
+    if (bi == GC_all_nils) {
+        register word hi = (word)h >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE);
+        bi = GC_all_bottom_indices;
+        while (bi != 0 && bi -> key < hi) bi = bi -> asc_link;
+        j = 0;
+    }
+    while(bi != 0) {
+        while (j < BOTTOM_SZ) {
+            hdr * hhdr = bi -> index[j];
+            if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) {
+                j++;
+            } else {
+                if (!HBLK_IS_FREE(hhdr)) {
+                    return((struct hblk *)
+                              (((bi -> key << LOG_BOTTOM_SZ) + j)
+                               << LOG_HBLKSIZE));
+                } else {
+                    j += divHBLKSZ(hhdr -> hb_sz);
+                }
+            }
+        }
+        j = 0;
+        bi = bi -> asc_link;
+    }
+    return(0);
+}
+
+/* Get the last (highest address) block whose address is        */
+/* at most h.  Return 0 if there is none.                       */
+/* Unlike the above, this may return a free block.              */
+GC_INNER struct hblk * GC_prev_block(struct hblk *h)
+{
+    register bottom_index * bi;
+    register signed_word j = ((word)h >> LOG_HBLKSIZE) & (BOTTOM_SZ-1);
+
+    GET_BI(h, bi);
+    if (bi == GC_all_nils) {
+        register word hi = (word)h >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE);
+        bi = GC_all_bottom_indices_end;
+        while (bi != 0 && bi -> key > hi) bi = bi -> desc_link;
+        j = BOTTOM_SZ - 1;
+    }
+    while(bi != 0) {
+        while (j >= 0) {
+            hdr * hhdr = bi -> index[j];
+            if (0 == hhdr) {
+                --j;
+            } else if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) {
+                j -= (signed_word)hhdr;
+            } else {
+                return((struct hblk *)
+                          (((bi -> key << LOG_BOTTOM_SZ) + j)
+                               << LOG_HBLKSIZE));
+            }
+        }
+        j = BOTTOM_SZ - 1;
+        bi = bi -> desc_link;
+    }
+    return(0);
+}

+ 354 - 0
blitz.mod/bdwgc/include/cord.h

@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 1993-1994 by Xerox Corporation.  All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+/*
+ * Cords are immutable character strings.  A number of operations
+ * on long cords are much more efficient than their strings.h counterpart.
+ * In particular, concatenation takes constant time independent of the length
+ * of the arguments.  (Cords are represented as trees, with internal
+ * nodes representing concatenation and leaves consisting of either C
+ * strings or a functional description of the string.)
+ *
+ * The following are reasonable applications of cords.  They would perform
+ * unacceptably if C strings were used:
+ * - A compiler that produces assembly language output by repeatedly
+ *   concatenating instructions onto a cord representing the output file.
+ * - A text editor that converts the input file to a cord, and then
+ *   performs editing operations by producing a new cord representing
+ *   the file after each character change (and keeping the old ones in an
+ *   edit history)
+ *
+ * For optimal performance, cords should be built by
+ * concatenating short sections.
+ * This interface is designed for maximum compatibility with C strings.
+ * ASCII NUL characters may be embedded in cords using CORD_from_fn.
+ * This is handled correctly, but CORD_to_char_star will produce a string
+ * with embedded NULs when given such a cord.
+ *
+ * This interface is fairly big, largely for performance reasons.
+ * The most basic constants and functions:
+ *
+ * CORD - the type of a cord;
+ * CORD_EMPTY - empty cord;
+ * CORD_len(cord) - length of a cord;
+ * CORD_cat(cord1,cord2) - concatenation of two cords;
+ * CORD_substr(cord, start, len) - substring (or subcord);
+ * CORD_pos i;  CORD_FOR(i, cord) {  ... CORD_pos_fetch(i) ... } -
+ *    examine each character in a cord.  CORD_pos_fetch(i) is the char.
+ * CORD_fetch(int i) - Retrieve i'th character (slowly).
+ * CORD_cmp(cord1, cord2) - compare two cords.
+ * CORD_from_file(FILE * f) - turn a read-only file into a cord.
+ * CORD_to_char_star(cord) - convert to C string.
+ *   (Non-NULL C constant strings are cords.)
+ * CORD_printf (etc.) - cord version of printf. Use %r for cords.
+ */
+#ifndef CORD_H
+#define CORD_H
+
+#include <stddef.h>
+#include <stdio.h>
+
+#ifdef GC_DLL
+  /* Same as for GC_API in gc_config_macros.h.  */
+# ifdef CORD_BUILD
+#   if defined(__MINGW32__) || defined(__CEGCC__)
+#     define CORD_API __declspec(dllexport)
+#   elif defined(_MSC_VER) || defined(__DMC__) || defined(__BORLANDC__) \
+         || defined(__CYGWIN__) || defined(__WATCOMC__)
+#     define CORD_API extern __declspec(dllexport)
+#   elif defined(__GNUC__) && (__GNUC__ >= 4 \
+                               || defined(GC_VISIBILITY_HIDDEN_SET))
+    /* Only matters if used in conjunction with -fvisibility=hidden option. */
+#     define CORD_API extern __attribute__((__visibility__("default")))
+#   endif
+# else
+#   if defined(__MINGW32__) || defined(__CEGCC__) || defined(_MSC_VER) \
+       || defined(__DMC__) || defined(__BORLANDC__) || defined(__CYGWIN__)
+#     define CORD_API __declspec(dllimport)
+#   elif defined(__WATCOMC__)
+#     define CORD_API extern __declspec(dllimport)
+#   endif
+# endif /* !CORD_BUILD */
+#endif /* GC_DLL */
+
+#ifndef CORD_API
+# define CORD_API extern
+#endif
+
+/* Cords have type const char *.  This is cheating quite a bit, and not */
+/* 100% portable.  But it means that nonempty character string          */
+/* constants may be used as cords directly, provided the string is      */
+/* never modified in place.  The empty cord is represented by, and      */
+/* can be written as, 0.                                                */
+
+typedef const char * CORD;
+
+/* An empty cord is always represented as nil   */
+#define CORD_EMPTY 0
+
+/* Is a nonempty cord represented as a C string? */
+#define CORD_IS_STRING(s) (*(s) != '\0')
+
+/* Concatenate two cords.  If the arguments are C strings, they may     */
+/* not be subsequently altered.                                         */
+CORD_API CORD CORD_cat(CORD x, CORD y);
+
+/* Concatenate a cord and a C string with known length.  Except for the */
+/* empty string case, this is a special case of CORD_cat.  Since the    */
+/* length is known, it can be faster.                                   */
+/* The string y is shared with the resulting CORD.  Hence it should     */
+/* not be altered by the caller.                                        */
+CORD_API CORD CORD_cat_char_star(CORD x, const char * y, size_t leny);
+
+/* Compute the length of a cord */
+CORD_API size_t CORD_len(CORD x);
+
+/* Cords may be represented by functions defining the ith character */
+typedef char (* CORD_fn)(size_t i, void * client_data);
+
+/* Turn a functional description into a cord.   */
+CORD_API CORD CORD_from_fn(CORD_fn fn, void * client_data, size_t len);
+
+/* Return the substring (subcord really) of x with length at most n,    */
+/* starting at position i.  (The initial character has position 0.)     */
+CORD_API CORD CORD_substr(CORD x, size_t i, size_t n);
+
+/* Return the argument, but rebalanced to allow more efficient          */
+/* character retrieval, substring operations, and comparisons.          */
+/* This is useful only for cords that were built using repeated         */
+/* concatenation.  Guarantees log time access to the result, unless     */
+/* x was obtained through a large number of repeated substring ops      */
+/* or the embedded functional descriptions take longer to evaluate.     */
+/* May reallocate significant parts of the cord.  The argument is not   */
+/* modified; only the result is balanced.                               */
+CORD_API CORD CORD_balance(CORD x);
+
+/* The following traverse a cord by applying a function to each         */
+/* character.  This is occasionally appropriate, especially where       */
+/* speed is crucial.  But, since C doesn't have nested functions,       */
+/* clients of this sort of traversal are clumsy to write.  Consider     */
+/* the functions that operate on cord positions instead.                */
+
+/* Function to iteratively apply to individual characters in cord.      */
+typedef int (* CORD_iter_fn)(char c, void * client_data);
+
+/* Function to apply to substrings of a cord.  Each substring is a      */
+/* a C character string, not a general cord.                            */
+typedef int (* CORD_batched_iter_fn)(const char * s, void * client_data);
+#define CORD_NO_FN ((CORD_batched_iter_fn)0)
+
+/* Apply f1 to each character in the cord, in ascending order,          */
+/* starting at position i. If                                           */
+/* f2 is not CORD_NO_FN, then multiple calls to f1 may be replaced by   */
+/* a single call to f2.  The parameter f2 is provided only to allow     */
+/* some optimization by the client.  This terminates when the right     */
+/* end of this string is reached, or when f1 or f2 return != 0.  In the */
+/* latter case CORD_iter returns != 0.  Otherwise it returns 0.         */
+/* The specified value of i must be < CORD_len(x).                      */
+CORD_API int CORD_iter5(CORD x, size_t i, CORD_iter_fn f1,
+                        CORD_batched_iter_fn f2, void * client_data);
+
+/* A simpler version that starts at 0, and without f2:  */
+CORD_API int CORD_iter(CORD x, CORD_iter_fn f1, void * client_data);
+#define CORD_iter(x, f1, cd) CORD_iter5(x, 0, f1, CORD_NO_FN, cd)
+
+/* Similar to CORD_iter5, but end-to-beginning. No provisions for       */
+/* CORD_batched_iter_fn.                                                */
+CORD_API int CORD_riter4(CORD x, size_t i, CORD_iter_fn f1, void * client_data);
+
+/* A simpler version that starts at the end:    */
+CORD_API int CORD_riter(CORD x, CORD_iter_fn f1, void * client_data);
+
+/* Functions that operate on cord positions.  The easy way to traverse  */
+/* cords.  A cord position is logically a pair consisting of a cord     */
+/* and an index into that cord.  But it is much faster to retrieve a    */
+/* character based on a position than on an index.  Unfortunately,      */
+/* positions are big (order of a few 100 bytes), so allocate them with  */
+/* caution.                                                             */
+/* Things in cord_pos.h should be treated as opaque, except as          */
+/* described below.  Also note that                                     */
+/* CORD_pos_fetch, CORD_next and CORD_prev have both macro and function */
+/* definitions.  The former may evaluate their argument more than once. */
+#include "cord_pos.h"
+
+/*
+        Visible definitions from above:
+
+        typedef <OPAQUE but fairly big> CORD_pos[1];
+
+        * Extract the cord from a position:
+        CORD CORD_pos_to_cord(CORD_pos p);
+
+        * Extract the current index from a position:
+        size_t CORD_pos_to_index(CORD_pos p);
+
+        * Fetch the character located at the given position:
+        char CORD_pos_fetch(CORD_pos p);
+
+        * Initialize the position to refer to the given cord and index.
+        * Note that this is the most expensive function on positions:
+        void CORD_set_pos(CORD_pos p, CORD x, size_t i);
+
+        * Advance the position to the next character.
+        * P must be initialized and valid.
+        * Invalidates p if past end:
+        void CORD_next(CORD_pos p);
+
+        * Move the position to the preceding character.
+        * P must be initialized and valid.
+        * Invalidates p if past beginning:
+        void CORD_prev(CORD_pos p);
+
+        * Is the position valid, i.e. inside the cord?
+        int CORD_pos_valid(CORD_pos p);
+*/
+#define CORD_FOR(pos, cord) \
+    for (CORD_set_pos(pos, cord, 0); CORD_pos_valid(pos); CORD_next(pos))
+
+
+/* An out of memory handler to call.  May be supplied by client.        */
+/* Must not return.                                                     */
+extern void (* CORD_oom_fn)(void);
+
+/* Dump the representation of x to stdout in an implementation defined  */
+/* manner.  Intended for debugging only.                                */
+CORD_API void CORD_dump(CORD x);
+
+/* The following could easily be implemented by the client.  They are   */
+/* provided in cordxtra.c for convenience.                              */
+
+/* Concatenate a character to the end of a cord.        */
+CORD_API CORD CORD_cat_char(CORD x, char c);
+
+/* Concatenate n cords. */
+CORD_API CORD CORD_catn(int n, /* CORD */ ...);
+
+/* Return the character in CORD_substr(x, i, 1)         */
+CORD_API char CORD_fetch(CORD x, size_t i);
+
+/* Return < 0, 0, or > 0, depending on whether x < y, x = y, x > y      */
+CORD_API int CORD_cmp(CORD x, CORD y);
+
+/* A generalization that takes both starting positions for the          */
+/* comparison, and a limit on the number of characters to be compared.  */
+CORD_API int CORD_ncmp(CORD x, size_t x_start, CORD y, size_t y_start,
+                       size_t len);
+
+/* Find the first occurrence of s in x at position start or later.      */
+/* Return the position of the first character of s in x, or             */
+/* CORD_NOT_FOUND if there is none.                                     */
+CORD_API size_t CORD_str(CORD x, size_t start, CORD s);
+
+/* Return a cord consisting of i copies of (possibly NUL) c.  Dangerous */
+/* in conjunction with CORD_to_char_star.                               */
+/* The resulting representation takes constant space, independent of i. */
+CORD_API CORD CORD_chars(char c, size_t i);
+#define CORD_nul(i) CORD_chars('\0', (i))
+
+/* Turn a file into cord.  The file must be seekable.  Its contents     */
+/* must remain constant.  The file may be accessed as an immediate      */
+/* result of this call and/or as a result of subsequent accesses to     */
+/* the cord.  Short files are likely to be immediately read, but        */
+/* long files are likely to be read on demand, possibly relying on      */
+/* stdio for buffering.                                                 */
+/* We must have exclusive access to the descriptor f, i.e. we may       */
+/* read it at any time, and expect the file pointer to be               */
+/* where we left it.  Normally this should be invoked as                */
+/* CORD_from_file(fopen(...))                                           */
+/* CORD_from_file arranges to close the file descriptor when it is no   */
+/* longer needed (e.g. when the result becomes inaccessible).           */
+/* The file f must be such that ftell reflects the actual character     */
+/* position in the file, i.e. the number of characters that can be      */
+/* or were read with fread.  On UNIX systems this is always true.  On   */
+/* MS Windows systems, f must be opened in binary mode.                 */
+CORD_API CORD CORD_from_file(FILE * f);
+
+/* Equivalent to the above, except that the entire file will be read    */
+/* and the file pointer will be closed immediately.                     */
+/* The binary mode restriction from above does not apply.               */
+CORD_API CORD CORD_from_file_eager(FILE * f);
+
+/* Equivalent to the above, except that the file will be read on demand.*/
+/* The binary mode restriction applies.                                 */
+CORD_API CORD CORD_from_file_lazy(FILE * f);
+
+/* Turn a cord into a C string. The result shares no structure with     */
+/* x, and is thus modifiable.                                           */
+CORD_API char * CORD_to_char_star(CORD x);
+
+/* Turn a C string into a CORD.  The C string is copied, and so may     */
+/* subsequently be modified.                                            */
+CORD_API CORD CORD_from_char_star(const char *s);
+
+/* Identical to the above, but the result may share structure with      */
+/* the argument and is thus not modifiable.                             */
+CORD_API const char * CORD_to_const_char_star(CORD x);
+
+/* Write a cord to a file, starting at the current position.  No        */
+/* trailing NULs are newlines are added.                                */
+/* Returns EOF if a write error occurs, 1 otherwise.                    */
+CORD_API int CORD_put(CORD x, FILE * f);
+
+/* "Not found" result for the following two functions.                  */
+#define CORD_NOT_FOUND ((size_t)(-1))
+
+/* A vague analog of strchr.  Returns the position (an integer, not     */
+/* a pointer) of the first occurrence of (char) c inside x at position  */
+/* i or later. The value i must be < CORD_len(x).                       */
+CORD_API size_t CORD_chr(CORD x, size_t i, int c);
+
+/* A vague analog of strrchr.  Returns index of the last occurrence     */
+/* of (char) c inside x at position i or earlier. The value i           */
+/* must be < CORD_len(x).                                               */
+CORD_API size_t CORD_rchr(CORD x, size_t i, int c);
+
+
+/* The following are also not primitive, but are implemented in         */
+/* cordprnt.c.  They provide functionality similar to the ANSI C        */
+/* functions with corresponding names, but with the following           */
+/* additions and changes:                                               */
+/* 1. A %r conversion specification specifies a CORD argument.  Field   */
+/*    width, precision, etc. have the same semantics as for %s.         */
+/*    (Note that %c, %C, and %S were already taken.)                    */
+/* 2. The format string is represented as a CORD.                       */
+/* 3. CORD_sprintf and CORD_vsprintf assign the result through the 1st  */
+/*    argument. Unlike their ANSI C versions, there is no need to guess */
+/*    the correct buffer size.                                          */
+/* 4. Most of the conversions are implement through the native          */
+/*    vsprintf.  Hence they are usually no faster, and                  */
+/*    idiosyncrasies of the native printf are preserved.  However,      */
+/*    CORD arguments to CORD_sprintf and CORD_vsprintf are NOT copied;  */
+/*    the result shares the original structure.  This may make them     */
+/*    very efficient in some unusual applications.                      */
+/*    The format string is copied.                                      */
+/* All functions return the number of characters generated or -1 on     */
+/* error.  This complies with the ANSI standard, but is inconsistent    */
+/* with some older implementations of sprintf.                          */
+
+/* The implementation of these is probably less portable than the rest  */
+/* of this package.                                                     */
+
+#ifndef CORD_NO_IO
+
+#include <stdarg.h>
+
+CORD_API int CORD_sprintf(CORD * out, CORD format, ...);
+CORD_API int CORD_vsprintf(CORD * out, CORD format, va_list args);
+CORD_API int CORD_fprintf(FILE * f, CORD format, ...);
+CORD_API int CORD_vfprintf(FILE * f, CORD format, va_list args);
+CORD_API int CORD_printf(CORD format, ...);
+CORD_API int CORD_vprintf(CORD format, va_list args);
+
+#endif /* CORD_NO_IO */
+
+#endif /* CORD_H */

+ 120 - 0
blitz.mod/bdwgc/include/cord_pos.h

@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 1993-1994 by Xerox Corporation.  All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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 should never be included directly; included only from cord.h.   */
+#if !defined(CORD_POSITION_H) && defined(CORD_H)
+#define CORD_POSITION_H
+
+/* The representation of CORD_position.  This is private to the */
+/* implementation, but the size is known to clients.  Also      */
+/* the implementation of some exported macros relies on it.     */
+/* Don't use anything defined here and not in cord.h.           */
+
+# define MAX_DEPTH 48
+        /* The maximum depth of a balanced cord + 1.            */
+        /* We don't let cords get deeper than MAX_DEPTH.        */
+
+struct CORD_pe {
+    CORD pe_cord;
+    size_t pe_start_pos;
+};
+
+/* A structure describing an entry on the path from the root    */
+/* to current position.                                         */
+typedef struct CORD_Pos {
+    size_t cur_pos;
+    int path_len;
+#       define CORD_POS_INVALID (0x55555555)
+                /* path_len == INVALID <==> position invalid */
+    const char *cur_leaf;       /* Current leaf, if it is a string.     */
+                                /* If the current leaf is a function,   */
+                                /* then this may point to function_buf  */
+                                /* containing the next few characters.  */
+                                /* Always points to a valid string      */
+                                /* containing the current character     */
+                                /* unless cur_end is 0.                 */
+    size_t cur_start;   /* Start position of cur_leaf   */
+    size_t cur_end;     /* Ending position of cur_leaf  */
+                        /* 0 if cur_leaf is invalid.    */
+    struct CORD_pe path[MAX_DEPTH + 1];
+        /* path[path_len] is the leaf corresponding to cur_pos  */
+        /* path[0].pe_cord is the cord we point to.             */
+#   define FUNCTION_BUF_SZ 8
+    char function_buf[FUNCTION_BUF_SZ]; /* Space for next few chars     */
+                                        /* from function node.          */
+} CORD_pos[1];
+
+/* Extract the cord from a position:    */
+CORD_API CORD CORD_pos_to_cord(CORD_pos p);
+
+/* Extract the current index from a position:   */
+CORD_API size_t CORD_pos_to_index(CORD_pos p);
+
+/* Fetch the character located at the given position:   */
+CORD_API char CORD_pos_fetch(CORD_pos p);
+
+/* Initialize the position to refer to the give cord and index. */
+/* Note that this is the most expensive function on positions:  */
+CORD_API void CORD_set_pos(CORD_pos p, CORD x, size_t i);
+
+/* Advance the position to the next character.  */
+/* P must be initialized and valid.             */
+/* Invalidates p if past end:                   */
+CORD_API void CORD_next(CORD_pos p);
+
+/* Move the position to the preceding character.        */
+/* P must be initialized and valid.                     */
+/* Invalidates p if past beginning:                     */
+CORD_API void CORD_prev(CORD_pos p);
+
+/* Is the position valid, i.e. inside the cord?         */
+CORD_API int CORD_pos_valid(CORD_pos p);
+
+CORD_API char CORD__pos_fetch(CORD_pos);
+CORD_API void CORD__next(CORD_pos);
+CORD_API void CORD__prev(CORD_pos);
+
+#define CORD_pos_fetch(p)       \
+    (((p)[0].cur_end != 0)? \
+        (p)[0].cur_leaf[(p)[0].cur_pos - (p)[0].cur_start] \
+        : CORD__pos_fetch(p))
+
+#define CORD_next(p)    \
+    (((p)[0].cur_pos + 1 < (p)[0].cur_end)? \
+        (p)[0].cur_pos++ \
+        : (CORD__next(p), 0))
+
+#define CORD_prev(p)    \
+    (((p)[0].cur_end != 0 && (p)[0].cur_pos > (p)[0].cur_start)? \
+        (p)[0].cur_pos-- \
+        : (CORD__prev(p), 0))
+
+#define CORD_pos_to_index(p) ((p)[0].cur_pos)
+
+#define CORD_pos_to_cord(p) ((p)[0].path[0].pe_cord)
+
+#define CORD_pos_valid(p) ((p)[0].path_len != CORD_POS_INVALID)
+
+/* Some grubby stuff for performance-critical friends:  */
+#define CORD_pos_chars_left(p) ((long)((p)[0].cur_end) - (long)((p)[0].cur_pos))
+        /* Number of characters in cache.  <= 0 ==> none        */
+
+#define CORD_pos_advance(p,n) ((p)[0].cur_pos += (n) - 1, CORD_next(p))
+        /* Advance position by n characters     */
+        /* 0 < n < CORD_pos_chars_left(p)       */
+
+#define CORD_pos_cur_char_addr(p) \
+        (p)[0].cur_leaf + ((p)[0].cur_pos - (p)[0].cur_start)
+        /* address of current character in cache.       */
+
+#endif

+ 68 - 0
blitz.mod/bdwgc/include/ec.h

@@ -0,0 +1,68 @@
+# ifndef EC_H
+# define EC_H
+
+# ifndef CORD_H
+#  include "cord.h"
+# endif
+
+/* Extensible cords are strings that may be destructively appended to.  */
+/* They allow fast construction of cords from characters that are       */
+/* being read from a stream.                                            */
+/*
+ * A client might look like:
+ *
+ *      {
+ *          CORD_ec x;
+ *          CORD result;
+ *          char c;
+ *          FILE *f;
+ *
+ *          ...
+ *          CORD_ec_init(x);
+ *          while(...) {
+ *              c = getc(f);
+ *              ...
+ *              CORD_ec_append(x, c);
+ *          }
+ *          result = CORD_balance(CORD_ec_to_cord(x));
+ *
+ * If a C string is desired as the final result, the call to CORD_balance
+ * may be replaced by a call to CORD_to_char_star.
+ */
+
+# ifndef CORD_BUFSZ
+#   define CORD_BUFSZ 128
+# endif
+
+typedef struct CORD_ec_struct {
+    CORD ec_cord;
+    char * ec_bufptr;
+    char ec_buf[CORD_BUFSZ+1];
+} CORD_ec[1];
+
+/* This structure represents the concatenation of ec_cord with  */
+/* ec_buf[0 ... (ec_bufptr-ec_buf-1)]                           */
+
+/* Flush the buffer part of the extended chord into ec_cord.    */
+/* Note that this is almost the only real function, and it is   */
+/* implemented in 6 lines in cordxtra.c                         */
+void CORD_ec_flush_buf(CORD_ec x);
+
+/* Convert an extensible cord to a cord. */
+# define CORD_ec_to_cord(x) (CORD_ec_flush_buf(x), (x)[0].ec_cord)
+
+/* Initialize an extensible cord. */
+#define CORD_ec_init(x) \
+                ((x)[0].ec_cord = 0, (void)((x)[0].ec_bufptr = (x)[0].ec_buf))
+
+/* Append a character to an extensible cord.    */
+#define CORD_ec_append(x, c) \
+                (((x)[0].ec_bufptr == (x)[0].ec_buf + CORD_BUFSZ ? \
+                        (CORD_ec_flush_buf(x), 0) : 0), \
+                 (void)(*(x)[0].ec_bufptr++ = (c)))
+
+/* Append a cord to an extensible cord.  Structure remains shared with  */
+/* original.                                                            */
+void CORD_ec_append_cord(CORD_ec x, CORD s);
+
+# endif /* EC_H */

+ 2 - 0
blitz.mod/bdwgc/include/extra/gc.h

@@ -0,0 +1,2 @@
+/* This file is installed for backward compatibility. */
+#include <gc/gc.h>

+ 2 - 0
blitz.mod/bdwgc/include/extra/gc_cpp.h

@@ -0,0 +1,2 @@
+/* This file is installed for backward compatibility. */
+#include <gc/gc_cpp.h>

+ 1763 - 0
blitz.mod/bdwgc/include/gc.h

@@ -0,0 +1,1763 @@
+/*
+ * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
+ * Copyright (c) 1991-1995 by Xerox Corporation.  All rights reserved.
+ * Copyright 1996-1999 by Silicon Graphics.  All rights reserved.
+ * Copyright 1999 by Hewlett-Packard Company.  All rights reserved.
+ * Copyright (C) 2007 Free Software Foundation, Inc
+ * Copyright (c) 2000-2011 by Hewlett-Packard Development Company.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+/*
+ * Note that this defines a large number of tuning hooks, which can
+ * safely be ignored in nearly all cases.  For normal use it suffices
+ * to call only GC_MALLOC and perhaps GC_REALLOC.
+ * For better performance, also look at GC_MALLOC_ATOMIC, and
+ * GC_enable_incremental.  If you need an action to be performed
+ * immediately before an object is collected, look at GC_register_finalizer.
+ * If you are using Solaris threads, look at the end of this file.
+ * Everything else is best ignored unless you encounter performance
+ * problems.
+ */
+
+#ifndef GC_H
+#define GC_H
+
+#include "gc_version.h"
+        /* Define version numbers here to allow test on build machine   */
+        /* for cross-builds.  Note that this defines the header         */
+        /* version number, which may or may not match that of the       */
+        /* dynamic library.  GC_get_version() can be used to obtain     */
+        /* the latter.                                                  */
+
+#include "gc_config_macros.h"
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+typedef void * GC_PTR;  /* preserved only for backward compatibility    */
+
+/* Define word and signed_word to be unsigned and signed types of the   */
+/* size as char * or void *.  There seems to be no way to do this       */
+/* even semi-portably.  The following is probably no better/worse       */
+/* than almost anything else.                                           */
+/* The ANSI standard suggests that size_t and ptrdiff_t might be        */
+/* better choices.  But those had incorrect definitions on some older   */
+/* systems.  Notably "typedef int size_t" is WRONG.                     */
+#ifdef _WIN64
+# ifdef __int64
+    typedef unsigned __int64 GC_word;
+    typedef __int64 GC_signed_word;
+# else
+    typedef unsigned long long GC_word;
+    typedef long long GC_signed_word;
+# endif
+#else
+  typedef unsigned long GC_word;
+  typedef long GC_signed_word;
+#endif
+
+/* Get the GC library version. The returned value is a constant in the  */
+/* form: ((version_major<<16) | (version_minor<<8) | version_micro).    */
+GC_API unsigned GC_CALL GC_get_version(void);
+
+/* Public read-only variables */
+/* The supplied getter functions are preferred for new code.            */
+
+GC_API GC_ATTR_DEPRECATED GC_word GC_gc_no;
+                        /* Counter incremented per collection.          */
+                        /* Includes empty GCs at startup.               */
+GC_API GC_word GC_CALL GC_get_gc_no(void);
+                        /* GC_get_gc_no() is unsynchronized, so         */
+                        /* it requires GC_call_with_alloc_lock() to     */
+                        /* avoid data races on multiprocessors.         */
+
+#ifdef GC_THREADS
+  GC_API GC_ATTR_DEPRECATED int GC_parallel;
+                        /* GC is parallelized for performance on        */
+                        /* multiprocessors.  Currently set only         */
+                        /* implicitly if collector is built with        */
+                        /* PARALLEL_MARK defined and if either:         */
+                        /*  Env variable GC_NPROC is set to > 1, or     */
+                        /*  GC_NPROC is not set and this is an MP.      */
+                        /* If GC_parallel is on (non-zero), incremental */
+                        /* collection is only partially functional,     */
+                        /* and may not be desirable.  The getter does   */
+                        /* not use or need synchronization (i.e.        */
+                        /* acquiring the GC lock).  Starting from       */
+                        /* GC v7.3, GC_parallel value is equal to the   */
+                        /* number of marker threads minus one (i.e.     */
+                        /* number of existing parallel marker threads   */
+                        /* excluding the initiating one).               */
+  GC_API int GC_CALL GC_get_parallel(void);
+#endif
+
+
+/* Public R/W variables */
+/* The supplied setter and getter functions are preferred for new code. */
+
+typedef void * (GC_CALLBACK * GC_oom_func)(size_t /* bytes_requested */);
+GC_API GC_ATTR_DEPRECATED GC_oom_func GC_oom_fn;
+                        /* When there is insufficient memory to satisfy */
+                        /* an allocation request, we return             */
+                        /* (*GC_oom_fn)(size).  By default this just    */
+                        /* returns NULL.                                */
+                        /* If it returns, it must return 0 or a valid   */
+                        /* pointer to a previously allocated heap       */
+                        /* object.  GC_oom_fn must not be 0.            */
+                        /* Both the supplied setter and the getter      */
+                        /* acquire the GC lock (to avoid data races).   */
+GC_API void GC_CALL GC_set_oom_fn(GC_oom_func) GC_ATTR_NONNULL(1);
+GC_API GC_oom_func GC_CALL GC_get_oom_fn(void);
+
+typedef void (GC_CALLBACK * GC_on_heap_resize_proc)(GC_word /* new_size */);
+GC_API GC_ATTR_DEPRECATED GC_on_heap_resize_proc GC_on_heap_resize;
+                        /* Invoked when the heap grows or shrinks.      */
+                        /* Called with the world stopped (and the       */
+                        /* allocation lock held).  May be 0.            */
+GC_API void GC_CALL GC_set_on_heap_resize(GC_on_heap_resize_proc);
+GC_API GC_on_heap_resize_proc GC_CALL GC_get_on_heap_resize(void);
+                        /* Both the supplied setter and the getter      */
+                        /* acquire the GC lock (to avoid data races).   */
+
+GC_API GC_ATTR_DEPRECATED int GC_find_leak;
+                        /* Do not actually garbage collect, but simply  */
+                        /* report inaccessible memory that was not      */
+                        /* deallocated with GC_free.  Initial value     */
+                        /* is determined by FIND_LEAK macro.            */
+                        /* The value should not typically be modified   */
+                        /* after GC initialization (and, thus, it does  */
+                        /* not use or need synchronization).            */
+GC_API void GC_CALL GC_set_find_leak(int);
+GC_API int GC_CALL GC_get_find_leak(void);
+
+GC_API GC_ATTR_DEPRECATED int GC_all_interior_pointers;
+                        /* Arrange for pointers to object interiors to  */
+                        /* be recognized as valid.  Typically should    */
+                        /* not be changed after GC initialization (in   */
+                        /* case of calling it after the GC is           */
+                        /* initialized, the setter acquires the GC lock */
+                        /* (to avoid data races).  The initial value    */
+                        /* depends on whether the GC is built with      */
+                        /* ALL_INTERIOR_POINTERS macro defined or not.  */
+                        /* Unless DONT_ADD_BYTE_AT_END is defined, this */
+                        /* also affects whether sizes are increased by  */
+                        /* at least a byte to allow "off the end"       */
+                        /* pointer recognition.  Must be only 0 or 1.   */
+GC_API void GC_CALL GC_set_all_interior_pointers(int);
+GC_API int GC_CALL GC_get_all_interior_pointers(void);
+
+GC_API GC_ATTR_DEPRECATED int GC_finalize_on_demand;
+                        /* If nonzero, finalizers will only be run in   */
+                        /* response to an explicit GC_invoke_finalizers */
+                        /* call.  The default is determined by whether  */
+                        /* the FINALIZE_ON_DEMAND macro is defined      */
+                        /* when the collector is built.                 */
+                        /* The setter and getter are unsynchronized.    */
+GC_API void GC_CALL GC_set_finalize_on_demand(int);
+GC_API int GC_CALL GC_get_finalize_on_demand(void);
+
+GC_API GC_ATTR_DEPRECATED int GC_java_finalization;
+                        /* Mark objects reachable from finalizable      */
+                        /* objects in a separate post-pass.  This makes */
+                        /* it a bit safer to use non-topologically-     */
+                        /* ordered finalization.  Default value is      */
+                        /* determined by JAVA_FINALIZATION macro.       */
+                        /* Enables register_finalizer_unreachable to    */
+                        /* work correctly.                              */
+                        /* The setter and getter are unsynchronized.    */
+GC_API void GC_CALL GC_set_java_finalization(int);
+GC_API int GC_CALL GC_get_java_finalization(void);
+
+typedef void (GC_CALLBACK * GC_finalizer_notifier_proc)(void);
+GC_API GC_ATTR_DEPRECATED GC_finalizer_notifier_proc GC_finalizer_notifier;
+                        /* Invoked by the collector when there are      */
+                        /* objects to be finalized.  Invoked at most    */
+                        /* once per GC cycle.  Never invoked unless     */
+                        /* GC_finalize_on_demand is set.                */
+                        /* Typically this will notify a finalization    */
+                        /* thread, which will call GC_invoke_finalizers */
+                        /* in response.  May be 0 (means no notifier).  */
+                        /* Both the supplied setter and the getter      */
+                        /* acquire the GC lock (to avoid data races).   */
+GC_API void GC_CALL GC_set_finalizer_notifier(GC_finalizer_notifier_proc);
+GC_API GC_finalizer_notifier_proc GC_CALL GC_get_finalizer_notifier(void);
+
+GC_API
+# ifndef GC_DONT_GC
+    GC_ATTR_DEPRECATED
+# endif
+  int GC_dont_gc;       /* != 0 ==> Don't collect.  In versions 6.2a1+, */
+                        /* this overrides explicit GC_gcollect() calls. */
+                        /* Used as a counter, so that nested enabling   */
+                        /* and disabling work correctly.  Should        */
+                        /* normally be updated with GC_enable() and     */
+                        /* GC_disable() calls.  Direct assignment to    */
+                        /* GC_dont_gc is deprecated.  To check whether  */
+                        /* GC is disabled, GC_is_disabled() is          */
+                        /* preferred for new code.                      */
+
+GC_API GC_ATTR_DEPRECATED int GC_dont_expand;
+                        /* Do not expand the heap unless explicitly     */
+                        /* requested or forced to.  The setter and      */
+                        /* getter are unsynchronized.                   */
+GC_API void GC_CALL GC_set_dont_expand(int);
+GC_API int GC_CALL GC_get_dont_expand(void);
+
+GC_API GC_ATTR_DEPRECATED int GC_use_entire_heap;
+                /* Causes the non-incremental 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 large 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 GC_ATTR_DEPRECATED int GC_full_freq;
+                            /* Number of partial collections between    */
+                            /* full collections.  Matters only if       */
+                            /* GC_incremental is set.                   */
+                            /* Full collections are also triggered if   */
+                            /* the collector detects a substantial      */
+                            /* increase in the number of in-use heap    */
+                            /* blocks.  Values in the tens are now      */
+                            /* perfectly reasonable, unlike for         */
+                            /* earlier GC versions.                     */
+                        /* The setter and getter are unsynchronized, so */
+                        /* GC_call_with_alloc_lock() is required to     */
+                        /* avoid data races (if the value is modified   */
+                        /* after the GC is put to multi-threaded mode). */
+GC_API void GC_CALL GC_set_full_freq(int);
+GC_API int GC_CALL GC_get_full_freq(void);
+
+GC_API GC_ATTR_DEPRECATED GC_word GC_non_gc_bytes;
+                        /* Bytes not considered candidates for          */
+                        /* collection.  Used only to control scheduling */
+                        /* of collections.  Updated by                  */
+                        /* GC_malloc_uncollectable and GC_free.         */
+                        /* Wizards only.                                */
+                        /* The setter and getter are unsynchronized, so */
+                        /* GC_call_with_alloc_lock() is required to     */
+                        /* avoid data races (if the value is modified   */
+                        /* after the GC is put to multi-threaded mode). */
+GC_API void GC_CALL GC_set_non_gc_bytes(GC_word);
+GC_API GC_word GC_CALL GC_get_non_gc_bytes(void);
+
+GC_API GC_ATTR_DEPRECATED int GC_no_dls;
+                        /* Don't register dynamic library data segments. */
+                        /* Wizards only.  Should be used only if the     */
+                        /* application explicitly registers all roots.   */
+                        /* (In some environments like Microsoft Windows  */
+                        /* and Apple's Darwin, this may also prevent     */
+                        /* registration of the main data segment as part */
+                        /* of the root set.)                             */
+                        /* The setter and getter are unsynchronized.     */
+GC_API void GC_CALL GC_set_no_dls(int);
+GC_API int GC_CALL GC_get_no_dls(void);
+
+GC_API GC_ATTR_DEPRECATED GC_word GC_free_space_divisor;
+                        /* We try to make sure that we allocate at      */
+                        /* least N/GC_free_space_divisor bytes between  */
+                        /* collections, where N is twice the number     */
+                        /* of traced bytes, plus the number of untraced */
+                        /* bytes (bytes in "atomic" objects), plus      */
+                        /* a rough estimate of the root set size.       */
+                        /* N approximates GC tracing work per GC.       */
+                        /* Initially, GC_free_space_divisor = 3.        */
+                        /* Increasing its value will use less space     */
+                        /* but more collection time.  Decreasing it     */
+                        /* will appreciably decrease collection time    */
+                        /* at the expense of space.                     */
+                        /* The setter and getter are unsynchronized, so */
+                        /* GC_call_with_alloc_lock() is required to     */
+                        /* avoid data races (if the value is modified   */
+                        /* after the GC is put to multi-threaded mode). */
+GC_API void GC_CALL GC_set_free_space_divisor(GC_word);
+GC_API GC_word GC_CALL GC_get_free_space_divisor(void);
+
+GC_API GC_ATTR_DEPRECATED GC_word GC_max_retries;
+                        /* The maximum number of GCs attempted before   */
+                        /* reporting out of memory after heap           */
+                        /* expansion fails.  Initially 0.               */
+                        /* The setter and getter are unsynchronized, so */
+                        /* GC_call_with_alloc_lock() is required to     */
+                        /* avoid data races (if the value is modified   */
+                        /* after the GC is put to multi-threaded mode). */
+GC_API void GC_CALL GC_set_max_retries(GC_word);
+GC_API GC_word GC_CALL GC_get_max_retries(void);
+
+
+GC_API GC_ATTR_DEPRECATED char *GC_stackbottom;
+                                /* Cool end of user stack.              */
+                                /* May be set in the client prior to    */
+                                /* calling any GC_ routines.  This      */
+                                /* avoids some overhead, and            */
+                                /* potentially some signals that can    */
+                                /* confuse debuggers.  Otherwise the    */
+                                /* collector attempts to set it         */
+                                /* automatically.                       */
+                                /* For multi-threaded code, this is the */
+                                /* cold end of the stack for the        */
+                                /* primordial thread.  Portable clients */
+                                /* should use GC_get_stack_base(),      */
+                                /* GC_call_with_gc_active() and         */
+                                /* GC_register_my_thread() instead.     */
+
+GC_API GC_ATTR_DEPRECATED int GC_dont_precollect;
+                                /* Do not collect as part of GC         */
+                                /* initialization.  Should be set only  */
+                                /* if the client wants a chance to      */
+                                /* manually initialize the root set     */
+                                /* before the first collection.         */
+                                /* Interferes with blacklisting.        */
+                                /* Wizards only.  The setter and getter */
+                                /* are unsynchronized (and no external  */
+                                /* locking is needed since the value is */
+                                /* accessed at GC initialization only). */
+GC_API void GC_CALL GC_set_dont_precollect(int);
+GC_API int GC_CALL GC_get_dont_precollect(void);
+
+GC_API GC_ATTR_DEPRECATED unsigned long GC_time_limit;
+                               /* If incremental collection is enabled, */
+                               /* We try to terminate collections       */
+                               /* after this many milliseconds.  Not a  */
+                               /* hard time bound.  Setting this to     */
+                               /* GC_TIME_UNLIMITED will essentially    */
+                               /* disable incremental collection while  */
+                               /* leaving generational collection       */
+                               /* enabled.                              */
+#define GC_TIME_UNLIMITED 999999
+                               /* Setting GC_time_limit to this value   */
+                               /* will disable the "pause time exceeded"*/
+                               /* tests.                                */
+                        /* The setter and getter are unsynchronized, so */
+                        /* GC_call_with_alloc_lock() is required to     */
+                        /* avoid data races (if the value is modified   */
+                        /* after the GC is put to multi-threaded mode). */
+GC_API void GC_CALL GC_set_time_limit(unsigned long);
+GC_API unsigned long GC_CALL GC_get_time_limit(void);
+
+/* Public procedures */
+
+/* Set whether the GC will allocate executable memory pages or not.     */
+/* A non-zero argument instructs the collector to allocate memory with  */
+/* the executable flag on.  Must be called before the collector is      */
+/* initialized.  May have no effect on some platforms.  The default     */
+/* value is controlled by NO_EXECUTE_PERMISSION macro (if present then  */
+/* the flag is off).  Portable clients should have                      */
+/* GC_set_pages_executable(1) call (before GC_INIT) provided they are   */
+/* going to execute code on any of the GC-allocated memory objects.     */
+GC_API void GC_CALL GC_set_pages_executable(int);
+
+/* Returns non-zero value if the GC is set to the allocate-executable   */
+/* mode.  The mode could be changed by GC_set_pages_executable (before  */
+/* GC_INIT) unless the former has no effect on the platform.  Does not  */
+/* use or need synchronization (i.e. acquiring the allocator lock).     */
+GC_API int GC_CALL GC_get_pages_executable(void);
+
+/* Overrides the default handle-fork mode.  Non-zero value means GC     */
+/* should install proper pthread_atfork handlers.  Has effect only if   */
+/* called before GC_INIT.  Clients should invoke GC_set_handle_fork     */
+/* with non-zero argument if going to use fork with GC functions called */
+/* in the forked child.  (Note that such client and atfork handlers     */
+/* activities are not fully POSIX-compliant.)  GC_set_handle_fork       */
+/* instructs GC_init to setup GC fork handlers using pthread_atfork,    */
+/* the latter might fail (or, even, absent on some targets) causing     */
+/* abort at GC initialization.  Starting from 7.3alpha3, problems with  */
+/* missing (or failed) pthread_atfork() could be avoided by invocation  */
+/* of GC_set_handle_fork(-1) at application start-up and surrounding    */
+/* each fork() with the relevant GC_atfork_prepare/parent/child calls.  */
+GC_API void GC_CALL GC_set_handle_fork(int);
+
+/* Routines to handle POSIX fork() manually (no-op if handled           */
+/* automatically).  GC_atfork_prepare should be called immediately      */
+/* before fork(); GC_atfork_parent should be invoked just after fork in */
+/* the branch that corresponds to parent process (i.e., fork result is  */
+/* non-zero); GC_atfork_child is to be called immediately in the child  */
+/* branch (i.e., fork result is 0). Note that GC_atfork_child() call    */
+/* should, of course, precede GC_start_mark_threads call (if any).      */
+GC_API void GC_CALL GC_atfork_prepare(void);
+GC_API void GC_CALL GC_atfork_parent(void);
+GC_API void GC_CALL GC_atfork_child(void);
+
+/* Initialize the collector.  Portable clients should call GC_INIT()    */
+/* from the main program instead.                                       */
+GC_API void GC_CALL GC_init(void);
+
+/* General purpose allocation routines, with roughly malloc calling     */
+/* conv.  The atomic versions promise that no relevant pointers are     */
+/* contained in the object.  The non-atomic versions guarantee that the */
+/* new object is cleared.  GC_malloc_stubborn promises that no changes  */
+/* to the object will occur after GC_end_stubborn_change has been       */
+/* called on the result of GC_malloc_stubborn.  GC_malloc_uncollectable */
+/* allocates an object that is scanned for pointers to collectible      */
+/* objects, but is not itself collectible.  The object is scanned even  */
+/* if it does not appear to be reachable.  GC_malloc_uncollectable and  */
+/* GC_free called on the resulting object implicitly update             */
+/* GC_non_gc_bytes appropriately.                                       */
+/* Note that the GC_malloc_stubborn support doesn't really exist        */
+/* anymore.  MANUAL_VDB provides comparable functionality.              */
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+        GC_malloc(size_t /* size_in_bytes */);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+        GC_malloc_atomic(size_t /* size_in_bytes */);
+GC_API GC_ATTR_MALLOC char * GC_CALL GC_strdup(const char *);
+GC_API GC_ATTR_MALLOC char * GC_CALL
+        GC_strndup(const char *, size_t) GC_ATTR_NONNULL(1);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+        GC_malloc_uncollectable(size_t /* size_in_bytes */);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+        GC_malloc_stubborn(size_t /* size_in_bytes */);
+
+/* GC_memalign() is not well tested.                                    */
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(2) void * GC_CALL
+        GC_memalign(size_t /* align */, size_t /* lb */);
+GC_API int GC_CALL GC_posix_memalign(void ** /* memptr */, size_t /* align */,
+                        size_t /* lb */) GC_ATTR_NONNULL(1);
+
+/* Explicitly deallocate an object.  Dangerous if used incorrectly.     */
+/* Requires a pointer to the base of an object.                         */
+/* If the argument is stubborn, it should not be changeable when freed. */
+/* An object should not be enabled for finalization when it is          */
+/* explicitly deallocated.                                              */
+/* GC_free(0) is a no-op, as required by ANSI C for free.               */
+GC_API void GC_CALL GC_free(void *);
+
+/* Stubborn objects may be changed only if the collector is explicitly  */
+/* informed.  The collector is implicitly informed of coming change     */
+/* when such an object is first allocated.  The following routines      */
+/* inform the collector that an object will no longer be changed, or    */
+/* that it will once again be changed.  Only non-NULL pointer stores    */
+/* into the object are considered to be changes.  The argument to       */
+/* GC_end_stubborn_change must be exactly the value returned by         */
+/* GC_malloc_stubborn or passed to GC_change_stubborn.  (In the second  */
+/* case, it may be an interior pointer within 512 bytes of the          */
+/* beginning of the objects.)  There is a performance penalty for       */
+/* allowing more than one stubborn object to be changed at once, but it */
+/* is acceptable to do so.  The same applies to dropping stubborn       */
+/* objects that are still changeable.                                   */
+GC_API void GC_CALL GC_change_stubborn(const void *) GC_ATTR_NONNULL(1);
+GC_API void GC_CALL GC_end_stubborn_change(const void *) GC_ATTR_NONNULL(1);
+
+/* Return a pointer to the base (lowest address) of an object given     */
+/* a pointer to a location within the object.                           */
+/* I.e., map an interior pointer to the corresponding base pointer.     */
+/* Note that with debugging allocation, this returns a pointer to the   */
+/* actual base of the object, i.e. the debug information, not to        */
+/* the base of the user object.                                         */
+/* Return 0 if displaced_pointer doesn't point to within a valid        */
+/* object.                                                              */
+/* Note that a deallocated object in the garbage collected heap         */
+/* may be considered valid, even if it has been deallocated with        */
+/* GC_free.                                                             */
+GC_API void * GC_CALL GC_base(void * /* displaced_pointer */);
+
+/* Return non-zero (TRUE) if and only if the argument points to         */
+/* somewhere in GC heap.  Primary use is as a fast alternative to       */
+/* GC_base to check whether the pointed object is allocated by GC       */
+/* or not.  It is assumed that the collector is already initialized.    */
+GC_API int GC_CALL GC_is_heap_ptr(const void *);
+
+/* Given a pointer to the base of an object, return its size in bytes.  */
+/* The returned size may be slightly larger than what was originally    */
+/* requested.                                                           */
+GC_API size_t GC_CALL GC_size(const void * /* obj_addr */) GC_ATTR_NONNULL(1);
+
+/* For compatibility with C library.  This is occasionally faster than  */
+/* a malloc followed by a bcopy.  But if you rely on that, either here  */
+/* or with the standard C library, your code is broken.  In my          */
+/* opinion, it shouldn't have been invented, but now we're stuck. -HB   */
+/* The resulting object has the same kind as the original.              */
+/* If the argument is stubborn, the result will have changes enabled.   */
+/* It is an error to have changes enabled for the original object.      */
+/* Follows ANSI conventions for NULL old_object.                        */
+GC_API void * GC_CALL GC_realloc(void * /* old_object */,
+                                 size_t /* new_size_in_bytes */)
+                        /* 'realloc' attr */ GC_ATTR_ALLOC_SIZE(2);
+
+/* Explicitly increase the heap size.   */
+/* Returns 0 on failure, 1 on success.  */
+GC_API int GC_CALL GC_expand_hp(size_t /* number_of_bytes */);
+
+/* Limit the heap size to n bytes.  Useful when you're debugging,       */
+/* especially on systems that don't handle running out of memory well.  */
+/* n == 0 ==> unbounded.  This is the default.  This setter function is */
+/* unsynchronized (so it might require GC_call_with_alloc_lock to avoid */
+/* data races).                                                         */
+GC_API void GC_CALL GC_set_max_heap_size(GC_word /* n */);
+
+/* Inform the collector that a certain section of statically allocated  */
+/* memory contains no pointers to garbage collected memory.  Thus it    */
+/* need not be scanned.  This is sometimes important if the application */
+/* maps large read/write files into the address space, which could be   */
+/* mistaken for dynamic library data segments on some systems.          */
+/* Both section start and end are not needed to be pointer-aligned.     */
+GC_API void GC_CALL GC_exclude_static_roots(void * /* low_address */,
+                                            void * /* high_address_plus_1 */);
+
+/* Clear the set of root segments.  Wizards only.                       */
+GC_API void GC_CALL GC_clear_roots(void);
+
+/* Add a root segment.  Wizards only.                                   */
+/* Both segment start and end are not needed to be pointer-aligned.     */
+/* low_address must not be greater than high_address_plus_1.            */
+GC_API void GC_CALL GC_add_roots(void * /* low_address */,
+                                 void * /* high_address_plus_1 */);
+
+/* Remove a root segment.  Wizards only.                                */
+/* May be unimplemented on some platforms.                              */
+GC_API void GC_CALL GC_remove_roots(void * /* low_address */,
+                                    void * /* high_address_plus_1 */);
+
+/* Add a displacement to the set of those considered valid by the       */
+/* collector.  GC_register_displacement(n) means that if p was returned */
+/* by GC_malloc, then (char *)p + n will be considered to be a valid    */
+/* pointer to p.  N must be small and less than the size of p.          */
+/* (All pointers to the interior of objects from the stack are          */
+/* considered valid in any case.  This applies to heap objects and      */
+/* static data.)                                                        */
+/* Preferably, this should be called before any other GC procedures.    */
+/* Calling it later adds to the probability of excess memory            */
+/* retention.                                                           */
+/* This is a no-op if the collector has recognition of                  */
+/* arbitrary interior pointers enabled, which is now the default.       */
+GC_API void GC_CALL GC_register_displacement(size_t /* n */);
+
+/* The following version should be used if any debugging allocation is  */
+/* being done.                                                          */
+GC_API void GC_CALL GC_debug_register_displacement(size_t /* n */);
+
+/* Explicitly trigger a full, world-stop collection.    */
+GC_API void GC_CALL GC_gcollect(void);
+
+/* Same as above but ignores the default stop_func setting and tries to */
+/* unmap as much memory as possible (regardless of the corresponding    */
+/* switch setting).  The recommended usage: on receiving a system       */
+/* low-memory event; before retrying a system call failed because of    */
+/* the system is running out of resources.                              */
+GC_API void GC_CALL GC_gcollect_and_unmap(void);
+
+/* Trigger a full world-stopped collection.  Abort the collection if    */
+/* and when stop_func returns a nonzero value.  Stop_func will be       */
+/* called frequently, and should be reasonably fast.  (stop_func is     */
+/* called with the allocation lock held and the world might be stopped; */
+/* it's not allowed for stop_func to manipulate pointers to the garbage */
+/* collected heap or call most of GC functions.)  This works even       */
+/* if virtual dirty bits, and hence incremental collection is not       */
+/* available for this architecture.  Collections can be aborted faster  */
+/* than normal pause times for incremental collection.  However,        */
+/* aborted collections do no useful work; the next collection needs     */
+/* to start from the beginning.  stop_func must not be 0.               */
+/* GC_try_to_collect() returns 0 if the collection was aborted (or the  */
+/* collections are disabled), 1 if it succeeded.                        */
+typedef int (GC_CALLBACK * GC_stop_func)(void);
+GC_API int GC_CALL GC_try_to_collect(GC_stop_func /* stop_func */)
+                                                        GC_ATTR_NONNULL(1);
+
+/* Set and get the default stop_func.  The default stop_func is used by */
+/* GC_gcollect() and by implicitly trigged collections (except for the  */
+/* case when handling out of memory).  Must not be 0.                   */
+/* Both the setter and getter acquire the GC lock to avoid data races.  */
+GC_API void GC_CALL GC_set_stop_func(GC_stop_func /* stop_func */)
+                                                        GC_ATTR_NONNULL(1);
+GC_API GC_stop_func GC_CALL GC_get_stop_func(void);
+
+/* Return the number of bytes in the heap.  Excludes collector private  */
+/* data structures.  Excludes the unmapped memory (returned to the OS). */
+/* Includes empty blocks and fragmentation loss.  Includes some pages   */
+/* that were allocated but never written.                               */
+/* This is an unsynchronized getter, so it should be called typically   */
+/* with the GC lock held to avoid data races on multiprocessors (the    */
+/* alternative is to use GC_get_heap_usage_safe or GC_get_prof_stats    */
+/* API calls instead).                                                  */
+/* This getter remains lock-free (unsynchronized) for compatibility     */
+/* reason since some existing clients call it from a GC callback        */
+/* holding the allocator lock.  (This API function and the following    */
+/* four ones bellow were made thread-safe in GC v7.2alpha1 and          */
+/* reverted back in v7.2alpha7 for the reason described.)               */
+GC_API size_t GC_CALL GC_get_heap_size(void);
+
+/* Return a lower bound on the number of free bytes in the heap         */
+/* (excluding the unmapped memory space).  This is an unsynchronized    */
+/* getter (see GC_get_heap_size comment regarding thread-safety).       */
+GC_API size_t GC_CALL GC_get_free_bytes(void);
+
+/* Return the size (in bytes) of the unmapped memory (which is returned */
+/* to the OS but could be remapped back by the collector later unless   */
+/* the OS runs out of system/virtual memory). This is an unsynchronized */
+/* getter (see GC_get_heap_size comment regarding thread-safety).       */
+GC_API size_t GC_CALL GC_get_unmapped_bytes(void);
+
+/* Return the number of bytes allocated since the last collection.      */
+/* This is an unsynchronized getter (see GC_get_heap_size comment       */
+/* regarding thread-safety).                                            */
+GC_API size_t GC_CALL GC_get_bytes_since_gc(void);
+
+/* Return the total number of bytes allocated in this process.          */
+/* Never decreases, except due to wrapping.  This is an unsynchronized  */
+/* getter (see GC_get_heap_size comment regarding thread-safety).       */
+GC_API size_t GC_CALL GC_get_total_bytes(void);
+
+/* Return the heap usage information.  This is a thread-safe (atomic)   */
+/* alternative for the five above getters.   (This function acquires    */
+/* the allocator lock thus preventing data racing and returning the     */
+/* consistent result.)  Passing NULL pointer is allowed for any         */
+/* argument.  Returned (filled in) values are of word type.             */
+/* (This API function was introduced in GC v7.2alpha7 at the same time  */
+/* when GC_get_heap_size and the friends were made lock-free again.)    */
+GC_API void GC_CALL GC_get_heap_usage_safe(GC_word * /* pheap_size */,
+                                           GC_word * /* pfree_bytes */,
+                                           GC_word * /* punmapped_bytes */,
+                                           GC_word * /* pbytes_since_gc */,
+                                           GC_word * /* ptotal_bytes */);
+
+/* Structure used to query GC statistics (profiling information).       */
+/* More fields could be added in the future.  To preserve compatibility */
+/* new fields should be added only to the end, and no deprecated fields */
+/* should be removed from.                                              */
+struct GC_prof_stats_s {
+  GC_word heapsize_full;
+            /* Heap size in bytes (including the area unmapped to OS).  */
+            /* Same as GC_get_heap_size() + GC_get_unmapped_bytes().    */
+  GC_word free_bytes_full;
+            /* Total bytes contained in free and unmapped blocks.       */
+            /* Same as GC_get_free_bytes() + GC_get_unmapped_bytes().   */
+  GC_word unmapped_bytes;
+            /* Amount of memory unmapped to OS.  Same as the value      */
+            /* returned by GC_get_unmapped_bytes().                     */
+  GC_word bytes_allocd_since_gc;
+            /* Number of bytes allocated since the recent collection.   */
+            /* Same as returned by GC_get_bytes_since_gc().             */
+  GC_word allocd_bytes_before_gc;
+            /* Number of bytes allocated before the recent garbage      */
+            /* collection.  The value may wrap.  Same as the result of  */
+            /* GC_get_total_bytes() - GC_get_bytes_since_gc().          */
+  GC_word non_gc_bytes;
+            /* Number of bytes not considered candidates for garbage    */
+            /* collection.  Same as returned by GC_get_non_gc_bytes().  */
+  GC_word gc_no;
+            /* Garbage collection cycle number.  The value may wrap     */
+            /* (and could be -1).  Same as returned by GC_get_gc_no().  */
+  GC_word markers_m1;
+            /* Number of marker threads (excluding the initiating one). */
+            /* Same as returned by GC_get_parallel (or 0 if the         */
+            /* collector is single-threaded).                           */
+  GC_word bytes_reclaimed_since_gc;
+            /* Approximate number of reclaimed bytes after recent GC.   */
+  GC_word reclaimed_bytes_before_gc;
+            /* Approximate number of bytes reclaimed before the recent  */
+            /* garbage collection.  The value may wrap.                 */
+};
+
+/* Atomically get GC statistics (various global counters).  Clients     */
+/* should pass the size of the buffer (of GC_prof_stats_s type) to fill */
+/* in the values - this is for interoperability between different GC    */
+/* versions, an old client could have fewer fields, and vice versa,     */
+/* client could use newer gc.h (with more entries declared in the       */
+/* structure) than that of the linked libgc binary; in the latter case, */
+/* unsupported (unknown) fields are filled in with -1.  Return the size */
+/* (in bytes) of the filled in part of the structure (excluding all     */
+/* unknown fields, if any).                                             */
+GC_API size_t GC_CALL GC_get_prof_stats(struct GC_prof_stats_s *,
+                                        size_t /* stats_sz */);
+#ifdef GC_THREADS
+  /* Same as above but unsynchronized (i.e., not holding the allocation */
+  /* lock).  Clients should call it using GC_call_with_alloc_lock to    */
+  /* avoid data races on multiprocessors.                               */
+  GC_API size_t GC_CALL GC_get_prof_stats_unsafe(struct GC_prof_stats_s *,
+                                                 size_t /* stats_sz */);
+#endif
+
+/* Disable garbage collection.  Even GC_gcollect calls will be          */
+/* ineffective.                                                         */
+GC_API void GC_CALL GC_disable(void);
+
+/* Return non-zero (TRUE) if and only if garbage collection is disabled */
+/* (i.e., GC_dont_gc value is non-zero).  Does not acquire the lock.    */
+GC_API int GC_CALL GC_is_disabled(void);
+
+/* Try to re-enable garbage collection.  GC_disable() and GC_enable()   */
+/* calls nest.  Garbage collection is enabled if the number of calls to */
+/* both functions is equal.                                             */
+GC_API void GC_CALL GC_enable(void);
+
+/* Enable incremental/generational collection.  Not advisable unless    */
+/* dirty bits are available or most heap objects are pointer-free       */
+/* (atomic) or immutable.  Don't use in leak finding mode.  Ignored if  */
+/* GC_dont_gc is non-zero.  Only the generational piece of this is      */
+/* functional if GC_parallel is non-zero or if GC_time_limit is         */
+/* GC_TIME_UNLIMITED.  Causes thread-local variant of GC_gcj_malloc()   */
+/* to revert to locked allocation.  Must be called before any such      */
+/* GC_gcj_malloc() calls.  For best performance, should be called as    */
+/* early as possible.  On some platforms, calling it later may have     */
+/* adverse effects.                                                     */
+/* Safe to call before GC_INIT().  Includes a  GC_init() call.          */
+GC_API void GC_CALL GC_enable_incremental(void);
+
+/* Does incremental mode write-protect pages?  Returns zero or  */
+/* more of the following, or'ed together:                       */
+#define GC_PROTECTS_POINTER_HEAP  1 /* May protect non-atomic objs.     */
+#define GC_PROTECTS_PTRFREE_HEAP  2
+#define GC_PROTECTS_STATIC_DATA   4 /* Currently never.                 */
+#define GC_PROTECTS_STACK         8 /* Probably impractical.            */
+
+#define GC_PROTECTS_NONE 0
+/* The collector is assumed to be initialized before this call.         */
+GC_API int GC_CALL GC_incremental_protection_needs(void);
+
+/* Perform some garbage collection work, if appropriate.        */
+/* Return 0 if there is no more work to be done.                */
+/* Typically performs an amount of work corresponding roughly   */
+/* to marking from one page.  May do more work if further       */
+/* progress requires it, e.g. if incremental collection is      */
+/* disabled.  It is reasonable to call this in a wait loop      */
+/* until it returns 0.                                          */
+GC_API int GC_CALL GC_collect_a_little(void);
+
+/* Allocate an object of size lb bytes.  The client guarantees that     */
+/* as long as the object is live, it will be referenced by a pointer    */
+/* that points to somewhere within the first 256 bytes of the object.   */
+/* (This should normally be declared volatile to prevent the compiler   */
+/* from invalidating this assertion.)  This routine is only useful      */
+/* if a large array is being allocated.  It reduces the chance of       */
+/* accidentally retaining such an array as a result of scanning an      */
+/* integer that happens to be an address inside the array.  (Actually,  */
+/* it reduces the chance of the allocator not finding space for such    */
+/* an array, since it will try hard to avoid introducing such a false   */
+/* reference.)  On a SunOS 4.X or MS Windows system this is recommended */
+/* for arrays likely to be larger than 100K or so.  For other systems,  */
+/* or if the collector is not configured to recognize all interior      */
+/* pointers, the threshold is normally much higher.                     */
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+        GC_malloc_ignore_off_page(size_t /* lb */);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+        GC_malloc_atomic_ignore_off_page(size_t /* lb */);
+
+#ifdef GC_ADD_CALLER
+# define GC_EXTRAS GC_RETURN_ADDR, __FILE__, __LINE__
+# define GC_EXTRA_PARAMS GC_word ra, const char * s, int i
+#else
+# define GC_EXTRAS __FILE__, __LINE__
+# define GC_EXTRA_PARAMS const char * s, int i
+#endif
+
+/* The following is only defined if the library has been suitably       */
+/* compiled:                                                            */
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+        GC_malloc_atomic_uncollectable(size_t /* size_in_bytes */);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+        GC_debug_malloc_atomic_uncollectable(size_t, GC_EXTRA_PARAMS);
+
+/* Debugging (annotated) allocation.  GC_gcollect will check            */
+/* objects allocated in this way for overwrites, etc.                   */
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+        GC_debug_malloc(size_t /* size_in_bytes */, GC_EXTRA_PARAMS);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+        GC_debug_malloc_atomic(size_t /* size_in_bytes */, GC_EXTRA_PARAMS);
+GC_API GC_ATTR_MALLOC char * GC_CALL
+        GC_debug_strdup(const char *, GC_EXTRA_PARAMS);
+GC_API GC_ATTR_MALLOC char * GC_CALL
+        GC_debug_strndup(const char *, size_t, GC_EXTRA_PARAMS)
+                                                        GC_ATTR_NONNULL(1);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+        GC_debug_malloc_uncollectable(size_t /* size_in_bytes */,
+                                      GC_EXTRA_PARAMS);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+        GC_debug_malloc_stubborn(size_t /* size_in_bytes */, GC_EXTRA_PARAMS);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+        GC_debug_malloc_ignore_off_page(size_t /* size_in_bytes */,
+                                        GC_EXTRA_PARAMS);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+        GC_debug_malloc_atomic_ignore_off_page(size_t /* size_in_bytes */,
+                                        GC_EXTRA_PARAMS);
+GC_API void GC_CALL GC_debug_free(void *);
+GC_API void * GC_CALL GC_debug_realloc(void * /* old_object */,
+                        size_t /* new_size_in_bytes */, GC_EXTRA_PARAMS)
+                        /* 'realloc' attr */ GC_ATTR_ALLOC_SIZE(2);
+GC_API void GC_CALL GC_debug_change_stubborn(const void *) GC_ATTR_NONNULL(1);
+GC_API void GC_CALL GC_debug_end_stubborn_change(const void *)
+                                                        GC_ATTR_NONNULL(1);
+
+/* Routines that allocate objects with debug information (like the      */
+/* above), but just fill in dummy file and line number information.     */
+/* Thus they can serve as drop-in malloc/realloc replacements.  This    */
+/* can be useful for two reasons:                                       */
+/* 1) It allows the collector to be built with DBG_HDRS_ALL defined     */
+/*    even if some allocation calls come from 3rd party libraries       */
+/*    that can't be recompiled.                                         */
+/* 2) On some platforms, the file and line information is redundant,    */
+/*    since it can be reconstructed from a stack trace.  On such        */
+/*    platforms it may be more convenient not to recompile, e.g. for    */
+/*    leak detection.  This can be accomplished by instructing the      */
+/*    linker to replace malloc/realloc with these.                      */
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+        GC_debug_malloc_replacement(size_t /* size_in_bytes */);
+GC_API /* 'realloc' attr */ GC_ATTR_ALLOC_SIZE(2) void * GC_CALL
+        GC_debug_realloc_replacement(void * /* object_addr */,
+                                     size_t /* size_in_bytes */);
+
+#ifdef GC_DEBUG_REPLACEMENT
+# define GC_MALLOC(sz) GC_debug_malloc_replacement(sz)
+# define GC_REALLOC(old, sz) GC_debug_realloc_replacement(old, sz)
+#elif defined(GC_DEBUG)
+# define GC_MALLOC(sz) GC_debug_malloc(sz, GC_EXTRAS)
+# define GC_REALLOC(old, sz) GC_debug_realloc(old, sz, GC_EXTRAS)
+#else
+# define GC_MALLOC(sz) GC_malloc(sz)
+# define GC_REALLOC(old, sz) GC_realloc(old, sz)
+#endif /* !GC_DEBUG_REPLACEMENT && !GC_DEBUG */
+
+#ifdef GC_DEBUG
+# define GC_MALLOC_ATOMIC(sz) GC_debug_malloc_atomic(sz, GC_EXTRAS)
+# define GC_STRDUP(s) GC_debug_strdup(s, GC_EXTRAS)
+# define GC_STRNDUP(s, sz) GC_debug_strndup(s, sz, GC_EXTRAS)
+# define GC_MALLOC_ATOMIC_UNCOLLECTABLE(sz) \
+                        GC_debug_malloc_atomic_uncollectable(sz, GC_EXTRAS)
+# define GC_MALLOC_UNCOLLECTABLE(sz) \
+                        GC_debug_malloc_uncollectable(sz, GC_EXTRAS)
+# define GC_MALLOC_IGNORE_OFF_PAGE(sz) \
+                        GC_debug_malloc_ignore_off_page(sz, GC_EXTRAS)
+# define GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(sz) \
+                        GC_debug_malloc_atomic_ignore_off_page(sz, GC_EXTRAS)
+# define GC_FREE(p) GC_debug_free(p)
+# define GC_REGISTER_FINALIZER(p, f, d, of, od) \
+      GC_debug_register_finalizer(p, f, d, of, od)
+# define GC_REGISTER_FINALIZER_IGNORE_SELF(p, f, d, of, od) \
+      GC_debug_register_finalizer_ignore_self(p, f, d, of, od)
+# define GC_REGISTER_FINALIZER_NO_ORDER(p, f, d, of, od) \
+      GC_debug_register_finalizer_no_order(p, f, d, of, od)
+# define GC_REGISTER_FINALIZER_UNREACHABLE(p, f, d, of, od) \
+      GC_debug_register_finalizer_unreachable(p, f, d, of, od)
+# define GC_MALLOC_STUBBORN(sz) GC_debug_malloc_stubborn(sz, GC_EXTRAS)
+# define GC_CHANGE_STUBBORN(p) GC_debug_change_stubborn(p)
+# define GC_END_STUBBORN_CHANGE(p) GC_debug_end_stubborn_change(p)
+# define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link, obj) \
+      GC_general_register_disappearing_link(link, \
+                                        GC_base((/* no const */ void *)(obj)))
+# define GC_REGISTER_LONG_LINK(link, obj) \
+      GC_register_long_link(link, GC_base((/* no const */ void *)(obj)))
+# define GC_REGISTER_DISPLACEMENT(n) GC_debug_register_displacement(n)
+#else
+# define GC_MALLOC_ATOMIC(sz) GC_malloc_atomic(sz)
+# define GC_STRDUP(s) GC_strdup(s)
+# define GC_STRNDUP(s, sz) GC_strndup(s, sz)
+# define GC_MALLOC_ATOMIC_UNCOLLECTABLE(sz) GC_malloc_atomic_uncollectable(sz)
+# define GC_MALLOC_UNCOLLECTABLE(sz) GC_malloc_uncollectable(sz)
+# define GC_MALLOC_IGNORE_OFF_PAGE(sz) \
+                        GC_malloc_ignore_off_page(sz)
+# define GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(sz) \
+                        GC_malloc_atomic_ignore_off_page(sz)
+# define GC_FREE(p) GC_free(p)
+# define GC_REGISTER_FINALIZER(p, f, d, of, od) \
+      GC_register_finalizer(p, f, d, of, od)
+# define GC_REGISTER_FINALIZER_IGNORE_SELF(p, f, d, of, od) \
+      GC_register_finalizer_ignore_self(p, f, d, of, od)
+# define GC_REGISTER_FINALIZER_NO_ORDER(p, f, d, of, od) \
+      GC_register_finalizer_no_order(p, f, d, of, od)
+# define GC_REGISTER_FINALIZER_UNREACHABLE(p, f, d, of, od) \
+      GC_register_finalizer_unreachable(p, f, d, of, od)
+# define GC_MALLOC_STUBBORN(sz) GC_malloc_stubborn(sz)
+# define GC_CHANGE_STUBBORN(p) GC_change_stubborn(p)
+# define GC_END_STUBBORN_CHANGE(p) GC_end_stubborn_change(p)
+# define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link, obj) \
+      GC_general_register_disappearing_link(link, obj)
+# define GC_REGISTER_LONG_LINK(link, obj) \
+      GC_register_long_link(link, obj)
+# define GC_REGISTER_DISPLACEMENT(n) GC_register_displacement(n)
+#endif /* !GC_DEBUG */
+
+/* The following are included because they are often convenient, and    */
+/* reduce the chance for a misspecified size argument.  But calls may   */
+/* expand to something syntactically incorrect if t is a complicated    */
+/* type expression.  Note that, unlike C++ new operator, these ones     */
+/* may return NULL (if out of memory).                                  */
+#define GC_NEW(t)               ((t*)GC_MALLOC(sizeof(t)))
+#define GC_NEW_ATOMIC(t)        ((t*)GC_MALLOC_ATOMIC(sizeof(t)))
+#define GC_NEW_STUBBORN(t)      ((t*)GC_MALLOC_STUBBORN(sizeof(t)))
+#define GC_NEW_UNCOLLECTABLE(t) ((t*)GC_MALLOC_UNCOLLECTABLE(sizeof(t)))
+
+#ifdef GC_REQUIRE_WCSDUP
+  /* This might be unavailable on some targets (or not needed). */
+  /* wchar_t should be defined in stddef.h */
+  GC_API GC_ATTR_MALLOC wchar_t * GC_CALL
+        GC_wcsdup(const wchar_t *) GC_ATTR_NONNULL(1);
+  GC_API GC_ATTR_MALLOC wchar_t * GC_CALL
+        GC_debug_wcsdup(const wchar_t *, GC_EXTRA_PARAMS) GC_ATTR_NONNULL(1);
+# ifdef GC_DEBUG
+#   define GC_WCSDUP(s) GC_debug_wcsdup(s, GC_EXTRAS)
+# else
+#   define GC_WCSDUP(s) GC_wcsdup(s)
+# endif
+#endif /* GC_REQUIRE_WCSDUP */
+
+/* Finalization.  Some of these primitives are grossly unsafe.          */
+/* The idea is to make them both cheap, and sufficient to build         */
+/* a safer layer, closer to Modula-3, Java, or PCedar finalization.     */
+/* The interface represents my conclusions from a long discussion       */
+/* with Alan Demers, Dan Greene, Carl Hauser, Barry Hayes,              */
+/* Christian Jacobi, and Russ Atkinson.  It's not perfect, and          */
+/* probably nobody else agrees with it.     Hans-J. Boehm  3/13/92      */
+typedef void (GC_CALLBACK * GC_finalization_proc)(void * /* obj */,
+                                                  void * /* client_data */);
+
+GC_API void GC_CALL GC_register_finalizer(void * /* obj */,
+                        GC_finalization_proc /* fn */, void * /* cd */,
+                        GC_finalization_proc * /* ofn */, void ** /* ocd */)
+                                                GC_ATTR_NONNULL(1);
+GC_API void GC_CALL GC_debug_register_finalizer(void * /* obj */,
+                        GC_finalization_proc /* fn */, void * /* cd */,
+                        GC_finalization_proc * /* ofn */, void ** /* ocd */)
+                                                GC_ATTR_NONNULL(1);
+        /* When obj is no longer accessible, invoke             */
+        /* (*fn)(obj, cd).  If a and b are inaccessible, and    */
+        /* a points to b (after disappearing links have been    */
+        /* made to disappear), then only a will be              */
+        /* finalized.  (If this does not create any new         */
+        /* pointers to b, then b will be finalized after the    */
+        /* next collection.)  Any finalizable object that       */
+        /* is reachable from itself by following one or more    */
+        /* pointers will not be finalized (or collected).       */
+        /* Thus cycles involving finalizable objects should     */
+        /* be avoided, or broken by disappearing links.         */
+        /* All but the last finalizer registered for an object  */
+        /* is ignored.                                          */
+        /* Finalization may be removed by passing 0 as fn.      */
+        /* Finalizers are implicitly unregistered when they are */
+        /* enqueued for finalization (i.e. become ready to be   */
+        /* finalized).                                          */
+        /* The old finalizer and client data are stored in      */
+        /* *ofn and *ocd.  (ofn and/or ocd may be NULL.         */
+        /* The allocation lock is held while *ofn and *ocd are  */
+        /* updated.  In case of error (no memory to register    */
+        /* new finalizer), *ofn and *ocd remain unchanged.)     */
+        /* Fn is never invoked on an accessible object,         */
+        /* provided hidden pointers are converted to real       */
+        /* pointers only if the allocation lock is held, and    */
+        /* such conversions are not performed by finalization   */
+        /* routines.                                            */
+        /* If GC_register_finalizer is aborted as a result of   */
+        /* a signal, the object may be left with no             */
+        /* finalization, even if neither the old nor new        */
+        /* finalizer were NULL.                                 */
+        /* Obj should be the starting address of an object      */
+        /* allocated by GC_malloc or friends. Obj may also be   */
+        /* NULL or point to something outside GC heap (in this  */
+        /* case, fn is ignored, *ofn and *ocd are set to NULL). */
+        /* Note that any garbage collectible object referenced  */
+        /* by cd will be considered accessible until the        */
+        /* finalizer is invoked.                                */
+
+/* Another versions of the above follow.  It ignores            */
+/* self-cycles, i.e. pointers from a finalizable object to      */
+/* itself.  There is a stylistic argument that this is wrong,   */
+/* but it's unavoidable for C++, since the compiler may         */
+/* silently introduce these.  It's also benign in that specific */
+/* case.  And it helps if finalizable objects are split to      */
+/* avoid cycles.                                                */
+/* Note that cd will still be viewed as accessible, even if it  */
+/* refers to the object itself.                                 */
+GC_API void GC_CALL GC_register_finalizer_ignore_self(void * /* obj */,
+                        GC_finalization_proc /* fn */, void * /* cd */,
+                        GC_finalization_proc * /* ofn */, void ** /* ocd */)
+                                                GC_ATTR_NONNULL(1);
+GC_API void GC_CALL GC_debug_register_finalizer_ignore_self(void * /* obj */,
+                        GC_finalization_proc /* fn */, void * /* cd */,
+                        GC_finalization_proc * /* ofn */, void ** /* ocd */)
+                                                GC_ATTR_NONNULL(1);
+
+/* Another version of the above.  It ignores all cycles.        */
+/* It should probably only be used by Java implementations.     */
+/* Note that cd will still be viewed as accessible, even if it  */
+/* refers to the object itself.                                 */
+GC_API void GC_CALL GC_register_finalizer_no_order(void * /* obj */,
+                        GC_finalization_proc /* fn */, void * /* cd */,
+                        GC_finalization_proc * /* ofn */, void ** /* ocd */)
+                                                GC_ATTR_NONNULL(1);
+GC_API void GC_CALL GC_debug_register_finalizer_no_order(void * /* obj */,
+                        GC_finalization_proc /* fn */, void * /* cd */,
+                        GC_finalization_proc * /* ofn */, void ** /* ocd */)
+                                                GC_ATTR_NONNULL(1);
+
+/* This is a special finalizer that is useful when an object's  */
+/* finalizer must be run when the object is known to be no      */
+/* longer reachable, not even from other finalizable objects.   */
+/* It behaves like "normal" finalization, except that the       */
+/* finalizer is not run while the object is reachable from      */
+/* other objects specifying unordered finalization.             */
+/* Effectively it allows an object referenced, possibly         */
+/* indirectly, from an unordered finalizable object to override */
+/* the unordered finalization request.                          */
+/* This can be used in combination with finalizer_no_order so   */
+/* as to release resources that must not be released while an   */
+/* object can still be brought back to life by other            */
+/* finalizers.                                                  */
+/* Only works if GC_java_finalization is set.  Probably only    */
+/* of interest when implementing a language that requires       */
+/* unordered finalization (e.g. Java, C#).                      */
+GC_API void GC_CALL GC_register_finalizer_unreachable(void * /* obj */,
+                        GC_finalization_proc /* fn */, void * /* cd */,
+                        GC_finalization_proc * /* ofn */, void ** /* ocd */)
+                                                GC_ATTR_NONNULL(1);
+GC_API void GC_CALL GC_debug_register_finalizer_unreachable(void * /* obj */,
+                        GC_finalization_proc /* fn */, void * /* cd */,
+                        GC_finalization_proc * /* ofn */, void ** /* ocd */)
+                                                GC_ATTR_NONNULL(1);
+
+#define GC_NO_MEMORY 2  /* Failure due to lack of memory.       */
+
+/* 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      */
+/* use involves calling GC_register_disappearing_link(&p),      */
+/* where p is a pointer that is not followed by finalization    */
+/* code, and should not be considered in determining            */
+/* finalization order.                                          */
+GC_API int GC_CALL GC_register_disappearing_link(void ** /* link */)
+                                                GC_ATTR_NONNULL(1);
+        /* Link should point to a field of a heap allocated     */
+        /* object obj.  *link will be cleared when obj is       */
+        /* found to be inaccessible.  This happens BEFORE any   */
+        /* finalization code is invoked, and BEFORE any         */
+        /* decisions about finalization order are made.         */
+        /* This is useful in telling the finalizer that         */
+        /* some pointers are not essential for proper           */
+        /* finalization.  This may avoid finalization cycles.   */
+        /* Note that obj may be resurrected by another          */
+        /* finalizer, and thus the clearing of *link may        */
+        /* be visible to non-finalization code.                 */
+        /* There's an argument that an arbitrary action should  */
+        /* be allowed here, instead of just clearing a pointer. */
+        /* But this causes problems if that action alters, or   */
+        /* examines connectivity.  Returns GC_DUPLICATE if link */
+        /* was already registered, GC_SUCCESS if registration   */
+        /* succeeded, GC_NO_MEMORY if it failed for lack of     */
+        /* memory, and GC_oom_fn did not handle the problem.    */
+        /* Only exists for backward compatibility.  See below:  */
+
+GC_API int GC_CALL GC_general_register_disappearing_link(void ** /* link */,
+                                                    const void * /* obj */)
+                        GC_ATTR_NONNULL(1) GC_ATTR_NONNULL(2);
+        /* A slight generalization of the above. *link is       */
+        /* cleared when obj first becomes inaccessible.  This   */
+        /* can be used to implement weak pointers easily and    */
+        /* safely. Typically link will point to a location      */
+        /* holding a disguised pointer to obj.  (A pointer      */
+        /* inside an "atomic" object is effectively disguised.) */
+        /* In this way, weak pointers are broken before any     */
+        /* object reachable from them gets finalized.           */
+        /* Each link may be registered only with one obj value, */
+        /* i.e. all objects but the last one (link registered   */
+        /* with) are ignored.  This was added after a long      */
+        /* email discussion with John Ellis.                    */
+        /* link must be non-NULL (and be properly aligned).     */
+        /* obj must be a pointer to the first word of an object */
+        /* allocated by GC_malloc or friends.  It is unsafe to  */
+        /* explicitly deallocate the object containing link.    */
+        /* Explicit deallocation of obj may or may not cause    */
+        /* link to eventually be cleared.                       */
+        /* This function can be used to implement certain types */
+        /* of weak pointers.  Note, however, this generally     */
+        /* requires that the allocation lock is held (see       */
+        /* GC_call_with_alloc_lock() below) when the disguised  */
+        /* pointer is accessed.  Otherwise a strong pointer     */
+        /* could be recreated between the time the collector    */
+        /* decides to reclaim the object and the link is        */
+        /* cleared.  Returns GC_SUCCESS if registration         */
+        /* succeeded (a new link is registered), GC_DUPLICATE   */
+        /* if link was already registered (with some object),   */
+        /* GC_NO_MEMORY if registration failed for lack of      */
+        /* memory (and GC_oom_fn did not handle the problem).   */
+
+GC_API int GC_CALL GC_move_disappearing_link(void ** /* link */,
+                                             void ** /* new_link */)
+                        GC_ATTR_NONNULL(2);
+        /* Moves a link previously registered via               */
+        /* GC_general_register_disappearing_link (or            */
+        /* GC_register_disappearing_link).  Does not change the */
+        /* target object of the weak reference.  Does not       */
+        /* change (*new_link) content.  May be called with      */
+        /* new_link equal to link (to check whether link has    */
+        /* been registered).  Returns GC_SUCCESS on success,    */
+        /* GC_DUPLICATE if there is already another             */
+        /* disappearing link at the new location (never         */
+        /* returned if new_link is equal to link), GC_NOT_FOUND */
+        /* if no link is registered at the original location.   */
+
+GC_API int GC_CALL GC_unregister_disappearing_link(void ** /* link */);
+        /* Undoes a registration by either of the above two     */
+        /* routines.  Returns 0 if link was not actually        */
+        /* registered (otherwise returns 1).                    */
+
+GC_API int GC_CALL GC_register_long_link(void ** /* link */,
+                                    const void * /* obj */)
+                        GC_ATTR_NONNULL(1) GC_ATTR_NONNULL(2);
+        /* Similar to GC_general_register_disappearing_link but */
+        /* *link only gets cleared when obj becomes truly       */
+        /* inaccessible.  An object becomes truly inaccessible  */
+        /* when it can no longer be resurrected from its        */
+        /* finalizer (e.g. by assigning itself to a pointer     */
+        /* traceable from root).  This can be used to implement */
+        /* long weak pointers easily and safely.                */
+
+GC_API int GC_CALL GC_move_long_link(void ** /* link */,
+                                     void ** /* new_link */)
+                        GC_ATTR_NONNULL(2);
+        /* Similar to GC_move_disappearing_link but for a link  */
+        /* previously registered via GC_register_long_link.     */
+
+GC_API int GC_CALL GC_unregister_long_link(void ** /* link */);
+        /* Similar to GC_unregister_disappearing_link but for a */
+        /* registration by either of the above two routines.    */
+
+/* Returns !=0 if GC_invoke_finalizers has something to do.     */
+GC_API int GC_CALL GC_should_invoke_finalizers(void);
+
+GC_API int GC_CALL GC_invoke_finalizers(void);
+        /* Run finalizers for all objects that are ready to     */
+        /* be finalized.  Return the number of finalizers       */
+        /* that were run.  Normally this is also called         */
+        /* implicitly during some allocations.  If              */
+        /* GC_finalize_on_demand is nonzero, it must be called  */
+        /* explicitly.                                          */
+
+/* Explicitly tell the collector that an object is reachable    */
+/* at a particular program point.  This prevents the argument   */
+/* pointer from being optimized away, even it is otherwise no   */
+/* longer needed.  It should have no visible effect in the      */
+/* absence of finalizers or disappearing links.  But it may be  */
+/* needed to prevent finalizers from running while the          */
+/* associated external resource is still in use.                */
+/* The function is sometimes called keep_alive in other         */
+/* settings.                                                    */
+#if defined(__GNUC__) && !defined(__INTEL_COMPILER)
+# define GC_reachable_here(ptr) \
+                __asm__ __volatile__(" " : : "X"(ptr) : "memory")
+#else
+  GC_API void GC_CALL GC_noop1(GC_word);
+# define GC_reachable_here(ptr) GC_noop1((GC_word)(ptr))
+#endif
+
+/* GC_set_warn_proc can be used to redirect or filter warning messages. */
+/* p may not be a NULL pointer.  msg is printf format string (arg must  */
+/* match the format).  Both the setter and the getter acquire the GC    */
+/* lock (to avoid data races).                                          */
+typedef void (GC_CALLBACK * GC_warn_proc)(char * /* msg */,
+                                          GC_word /* arg */);
+GC_API void GC_CALL GC_set_warn_proc(GC_warn_proc /* p */) GC_ATTR_NONNULL(1);
+/* GC_get_warn_proc returns the current warn_proc.                      */
+GC_API GC_warn_proc GC_CALL GC_get_warn_proc(void);
+
+/* GC_ignore_warn_proc may be used as an argument for GC_set_warn_proc  */
+/* to suppress all warnings (unless statistics printing is turned on).  */
+GC_API void GC_CALLBACK GC_ignore_warn_proc(char *, GC_word);
+
+/* abort_func is invoked on GC fatal aborts (just before OS-dependent   */
+/* abort or exit(1) is called).  Must be non-NULL.  The default one     */
+/* outputs msg to stderr provided msg is non-NULL.  msg is NULL if      */
+/* invoked before exit(1) otherwise msg is non-NULL (i.e., if invoked   */
+/* before abort).  Both the setter and getter acquire the GC lock.      */
+/* Both the setter and getter are defined only if the library has been  */
+/* compiled without SMALL_CONFIG.                                       */
+typedef void (GC_CALLBACK * GC_abort_func)(const char * /* msg */);
+GC_API void GC_CALL GC_set_abort_func(GC_abort_func) GC_ATTR_NONNULL(1);
+GC_API GC_abort_func GC_CALL GC_get_abort_func(void);
+
+/* The following is intended to be used by a higher level       */
+/* (e.g. Java-like) finalization facility.  It is expected      */
+/* that finalization code will arrange for hidden pointers to   */
+/* disappear.  Otherwise objects can be accessed after they     */
+/* have been collected.                                         */
+/* Note that putting pointers in atomic objects or in           */
+/* non-pointer slots of "typed" objects is equivalent to        */
+/* disguising them in this way, and may have other advantages.  */
+typedef GC_word GC_hidden_pointer;
+#define GC_HIDE_POINTER(p) (~(GC_hidden_pointer)(p))
+/* Converting a hidden pointer to a real pointer requires verifying     */
+/* that the object still exists.  This involves acquiring the           */
+/* allocator lock to avoid a race with the collector.                   */
+#define GC_REVEAL_POINTER(p) ((void *)GC_HIDE_POINTER(p))
+
+#if defined(I_HIDE_POINTERS) || defined(GC_I_HIDE_POINTERS)
+  /* This exists only for compatibility (the GC-prefixed symbols are    */
+  /* preferred for new code).                                           */
+# define HIDE_POINTER(p) GC_HIDE_POINTER(p)
+# define REVEAL_POINTER(p) GC_REVEAL_POINTER(p)
+#endif
+
+typedef void * (GC_CALLBACK * GC_fn_type)(void * /* client_data */);
+GC_API void * GC_CALL GC_call_with_alloc_lock(GC_fn_type /* fn */,
+                                void * /* client_data */) GC_ATTR_NONNULL(1);
+
+/* These routines are intended to explicitly notify the collector       */
+/* of new threads.  Often this is unnecessary because thread creation   */
+/* is implicitly intercepted by the collector, using header-file        */
+/* defines, or linker-based interception.  In the long run the intent   */
+/* is to always make redundant registration safe.  In the short run,    */
+/* this is being implemented a platform at a time.                      */
+/* The interface is complicated by the fact that we probably will not   */
+/* ever be able to automatically determine the stack base for thread    */
+/* stacks on all platforms.                                             */
+
+/* Structure representing the base of a thread stack.  On most          */
+/* platforms this contains just a single address.                       */
+struct GC_stack_base {
+  void * mem_base; /* Base of memory stack. */
+# if defined(__ia64) || defined(__ia64__) || defined(_M_IA64)
+    void * reg_base; /* Base of separate register stack. */
+# endif
+};
+
+typedef void * (GC_CALLBACK * GC_stack_base_func)(
+                struct GC_stack_base * /* sb */, void * /* arg */);
+
+/* Call a function with a stack base structure corresponding to         */
+/* somewhere in the GC_call_with_stack_base frame.  This often can      */
+/* be used to provide a sufficiently accurate stack base.  And we       */
+/* implement it everywhere.                                             */
+GC_API void * GC_CALL GC_call_with_stack_base(GC_stack_base_func /* fn */,
+                                        void * /* arg */) GC_ATTR_NONNULL(1);
+
+#define GC_SUCCESS 0
+#define GC_DUPLICATE 1          /* Was already registered.              */
+#define GC_NO_THREADS 2         /* No thread support in GC.             */
+        /* GC_NO_THREADS is not returned by any GC function anymore.    */
+#define GC_UNIMPLEMENTED 3 /* Not yet implemented on this platform.     */
+#define GC_NOT_FOUND 4          /* Requested link not found (returned   */
+                                /* by GC_move_disappearing_link).       */
+
+#if defined(GC_DARWIN_THREADS) || defined(GC_WIN32_THREADS)
+  /* Use implicit thread registration and processing (via Win32 DllMain */
+  /* or Darwin task_threads).  Deprecated.  Must be called before       */
+  /* GC_INIT() and other GC routines.  Should be avoided if             */
+  /* GC_pthread_create, GC_beginthreadex (or GC_CreateThread) could be  */
+  /* called instead.  Disables parallelized GC on Win32.                */
+  GC_API void GC_CALL GC_use_threads_discovery(void);
+#endif
+
+#ifdef GC_THREADS
+  /* Suggest the GC to use the specific signal to suspend threads.      */
+  /* Has no effect after GC_init and on non-POSIX systems.              */
+  GC_API void GC_CALL GC_set_suspend_signal(int);
+
+  /* Suggest the GC to use the specific signal to resume threads.       */
+  /* Has no effect after GC_init and on non-POSIX systems.              */
+  GC_API void GC_CALL GC_set_thr_restart_signal(int);
+
+  /* Return the signal number (constant after initialization) used by   */
+  /* the GC to suspend threads on POSIX systems.  Return -1 otherwise.  */
+  GC_API int GC_CALL GC_get_suspend_signal(void);
+
+  /* Return the signal number (constant after initialization) used by   */
+  /* the garbage collector to restart (resume) threads on POSIX         */
+  /* systems.  Return -1 otherwise.                                     */
+  GC_API int GC_CALL GC_get_thr_restart_signal(void);
+
+  /* Restart marker threads after POSIX fork in child.  Meaningless in  */
+  /* other situations.  Should not be called if fork followed by exec.  */
+  GC_API void GC_CALL GC_start_mark_threads(void);
+
+  /* Explicitly enable GC_register_my_thread() invocation.              */
+  /* Done implicitly if a GC thread-creation function is called (or     */
+  /* implicit thread registration is activated).  Otherwise, it must    */
+  /* be called from the main (or any previously registered) thread      */
+  /* between the collector initialization and the first explicit        */
+  /* registering of a thread (it should be called as late as possible). */
+  GC_API void GC_CALL GC_allow_register_threads(void);
+
+  /* Register the current thread, with the indicated stack base, as     */
+  /* a new thread whose stack(s) should be traced by the GC.  If it     */
+  /* is not implicitly called by the GC, this must be called before a   */
+  /* thread can allocate garbage collected memory, or assign pointers   */
+  /* to the garbage collected heap.  Once registered, a thread will be  */
+  /* stopped during garbage collections.                                */
+  /* This call must be previously enabled (see above).                  */
+  /* This should never be called from the main thread, where it is      */
+  /* always done implicitly.  This is normally done implicitly if GC_   */
+  /* functions are called to create the thread, e.g. by including gc.h  */
+  /* (which redefines some system functions) before calling the system  */
+  /* thread creation function.  Nonetheless, thread cleanup routines    */
+  /* (eg., pthread key destructor) typically require manual thread      */
+  /* registering (and unregistering) if pointers to GC-allocated        */
+  /* objects are manipulated inside.                                    */
+  /* It is also always done implicitly on some platforms if             */
+  /* GC_use_threads_discovery() is called at start-up.  Except for the  */
+  /* latter case, the explicit call is normally required for threads    */
+  /* created by third-party libraries.                                  */
+  /* A manually registered thread requires manual unregistering.        */
+  GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *)
+                                                        GC_ATTR_NONNULL(1);
+
+  /* Return non-zero (TRUE) if and only if the calling thread is        */
+  /* registered with the garbage collector.                             */
+  GC_API int GC_CALL GC_thread_is_registered(void);
+
+  /* Unregister the current thread.  Only an explicitly registered      */
+  /* thread (i.e. for which GC_register_my_thread() returns GC_SUCCESS) */
+  /* is allowed (and required) to call this function.  (As a special    */
+  /* exception, it is also allowed to once unregister the main thread.) */
+  /* The thread may no longer allocate garbage collected memory or      */
+  /* manipulate pointers to the garbage collected heap after making     */
+  /* this call.  Specifically, if it wants to return or otherwise       */
+  /* communicate a pointer to the garbage-collected heap to another     */
+  /* thread, it must do this before calling GC_unregister_my_thread,    */
+  /* most probably by saving it in a global data structure.  Must not   */
+  /* be called inside a GC callback function (except for                */
+  /* GC_call_with_stack_base() one).                                    */
+  GC_API int GC_CALL GC_unregister_my_thread(void);
+#endif /* GC_THREADS */
+
+/* Wrapper for functions that are likely to block (or, at least, do not */
+/* allocate garbage collected memory and/or manipulate pointers to the  */
+/* garbage collected heap) for an appreciable length of time.  While fn */
+/* is running, the collector is said to be in the "inactive" state for  */
+/* the current thread (this means that the thread is not suspended and  */
+/* the thread's stack frames "belonging" to the functions in the        */
+/* "inactive" state are not scanned during garbage collections).  It is */
+/* allowed for fn to call GC_call_with_gc_active() (even recursively),  */
+/* thus temporarily toggling the collector's state back to "active".    */
+GC_API void * GC_CALL GC_do_blocking(GC_fn_type /* fn */,
+                                void * /* client_data */) GC_ATTR_NONNULL(1);
+
+/* Call a function switching to the "active" state of the collector for */
+/* the current thread (i.e. the user function is allowed to call any    */
+/* GC function and/or manipulate pointers to the garbage collected      */
+/* heap).  GC_call_with_gc_active() has the functionality opposite to   */
+/* GC_do_blocking() one.  It is assumed that the collector is already   */
+/* initialized and the current thread is registered.  fn may toggle     */
+/* the collector thread's state temporarily to "inactive" one by using  */
+/* GC_do_blocking.  GC_call_with_gc_active() often can be used to       */
+/* provide a sufficiently accurate stack base.                          */
+GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type /* fn */,
+                                void * /* client_data */) GC_ATTR_NONNULL(1);
+
+/* Attempt to fill in the GC_stack_base structure with the stack base   */
+/* for this thread.  This appears to be required to implement anything  */
+/* like the JNI AttachCurrentThread in an environment in which new      */
+/* threads are not automatically registered with the collector.         */
+/* It is also unfortunately hard to implement well on many platforms.   */
+/* Returns GC_SUCCESS or GC_UNIMPLEMENTED.  This function acquires the  */
+/* GC lock on some platforms.                                           */
+GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *)
+                                                        GC_ATTR_NONNULL(1);
+
+/* The following routines are primarily intended for use with a         */
+/* preprocessor which inserts calls to check C pointer arithmetic.      */
+/* They indicate failure by invoking the corresponding _print_proc.     */
+
+/* Check that p and q point to the same object.                 */
+/* Fail conspicuously if they don't.                            */
+/* Returns the first argument.                                  */
+/* Succeeds if neither p nor q points to the heap.              */
+/* May succeed if both p and q point to between heap objects.   */
+GC_API void * GC_CALL GC_same_obj(void * /* p */, void * /* q */);
+
+/* Checked pointer pre- and post- increment operations.  Note that      */
+/* the second argument is in units of bytes, not multiples of the       */
+/* object size.  This should either be invoked from a macro, or the     */
+/* call should be automatically generated.                              */
+GC_API void * GC_CALL GC_pre_incr(void **, ptrdiff_t /* how_much */)
+                                                        GC_ATTR_NONNULL(1);
+GC_API void * GC_CALL GC_post_incr(void **, ptrdiff_t /* how_much */)
+                                                        GC_ATTR_NONNULL(1);
+
+/* Check that p is visible                                              */
+/* to the collector as a possibly pointer containing location.          */
+/* If it isn't fail conspicuously.                                      */
+/* Returns the argument in all cases.  May erroneously succeed          */
+/* in hard cases.  (This is intended for debugging use with             */
+/* untyped allocations.  The idea is that it should be possible, though */
+/* slow, to add such a call to all indirect pointer stores.)            */
+/* Currently useless for multi-threaded worlds.                         */
+GC_API void * GC_CALL GC_is_visible(void * /* p */);
+
+/* Check that if p is a pointer to a heap page, then it points to       */
+/* a valid displacement within a heap object.                           */
+/* Fail conspicuously if this property does not hold.                   */
+/* Uninteresting with GC_all_interior_pointers.                         */
+/* Always returns its argument.                                         */
+GC_API void * GC_CALL GC_is_valid_displacement(void * /* p */);
+
+/* Explicitly dump the GC state.  This is most often called from the    */
+/* debugger, or by setting the GC_DUMP_REGULARLY environment variable,  */
+/* but it may be useful to call it from client code during debugging.   */
+/* Defined only if the library has been compiled without NO_DEBUGGING.  */
+GC_API void GC_CALL GC_dump(void);
+
+/* Safer, but slow, pointer addition.  Probably useful mainly with      */
+/* a preprocessor.  Useful only for heap pointers.                      */
+/* Only the macros without trailing digits are meant to be used         */
+/* by clients.  These are designed to model the available C pointer     */
+/* arithmetic expressions.                                              */
+/* Even then, these are probably more useful as                         */
+/* documentation than as part of the API.                               */
+/* Note that GC_PTR_ADD evaluates the first argument more than once.    */
+#if defined(GC_DEBUG) && defined(__GNUC__)
+# define GC_PTR_ADD3(x, n, type_of_result) \
+        ((type_of_result)GC_same_obj((x)+(n), (x)))
+# define GC_PRE_INCR3(x, n, type_of_result) \
+        ((type_of_result)GC_pre_incr((void **)(&(x)), (n)*sizeof(*x)))
+# define GC_POST_INCR3(x, n, type_of_result) \
+        ((type_of_result)GC_post_incr((void **)(&(x)), (n)*sizeof(*x)))
+# define GC_PTR_ADD(x, n) GC_PTR_ADD3(x, n, typeof(x))
+# define GC_PRE_INCR(x, n) GC_PRE_INCR3(x, n, typeof(x))
+# define GC_POST_INCR(x) GC_POST_INCR3(x, 1, typeof(x))
+# define GC_POST_DECR(x) GC_POST_INCR3(x, -1, typeof(x))
+#else /* !GC_DEBUG || !__GNUC__ */
+  /* We can't do this right without typeof, which ANSI decided was not    */
+  /* sufficiently useful.  Without it we resort to the non-debug version. */
+  /* FIXME: This should eventually support C++0x decltype.                */
+# define GC_PTR_ADD(x, n) ((x)+(n))
+# define GC_PRE_INCR(x, n) ((x) += (n))
+# define GC_POST_INCR(x) ((x)++)
+# define GC_POST_DECR(x) ((x)--)
+#endif /* !GC_DEBUG || !__GNUC__ */
+
+/* Safer assignment of a pointer to a non-stack location.       */
+#ifdef GC_DEBUG
+# define GC_PTR_STORE(p, q) \
+        (*(void **)GC_is_visible(p) = GC_is_valid_displacement(q))
+#else
+# define GC_PTR_STORE(p, q) (*(p) = (q))
+#endif
+
+/* Functions called to report pointer checking errors */
+GC_API void (GC_CALLBACK * GC_same_obj_print_proc)(void * /* p */,
+                                                   void * /* q */);
+GC_API void (GC_CALLBACK * GC_is_valid_displacement_print_proc)(void *);
+GC_API void (GC_CALLBACK * GC_is_visible_print_proc)(void *);
+
+#ifdef GC_PTHREADS
+  /* For pthread support, we generally need to intercept a number of    */
+  /* thread library calls.  We do that here by macro defining them.     */
+# include "gc_pthread_redirects.h"
+#endif
+
+/* This returns a list of objects, linked through their first word.     */
+/* Its use can greatly reduce lock contention problems, since the       */
+/* allocation lock can be acquired and released many fewer times.       */
+GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_many(size_t /* lb */);
+#define GC_NEXT(p) (*(void * *)(p))     /* Retrieve the next element    */
+                                        /* in returned list.            */
+
+/* A filter function to control the scanning of dynamic libraries.      */
+/* If implemented, called by GC before registering a dynamic library    */
+/* (discovered by GC) section as a static data root (called only as     */
+/* a last reason not to register).  The filename of the library, the    */
+/* address and the length of the memory region (section) are passed.    */
+/* This routine should return nonzero if that region should be scanned. */
+/* Always called with the allocation lock held.  Depending on the       */
+/* platform, might be called with the "world" stopped.                  */
+typedef int (GC_CALLBACK * GC_has_static_roots_func)(
+                                        const char * /* dlpi_name */,
+                                        void * /* section_start */,
+                                        size_t /* section_size */);
+
+/* Register a new callback (a user-supplied filter) to control the      */
+/* scanning of dynamic libraries.  Replaces any previously registered   */
+/* callback.  May be 0 (means no filtering).  May be unused on some     */
+/* platforms (if the filtering is unimplemented or inappropriate).      */
+GC_API void GC_CALL GC_register_has_static_roots_callback(
+                                        GC_has_static_roots_func);
+
+#if defined(GC_WIN32_THREADS) \
+    && (!defined(GC_PTHREADS) || defined(GC_BUILD) || defined(WINAPI))
+                /* Note: for Cygwin and win32-pthread, this is skipped  */
+                /* unless windows.h is included before gc.h.            */
+
+# if !defined(GC_NO_THREAD_DECLS) || defined(GC_BUILD)
+
+#   ifdef __cplusplus
+      } /* Including windows.h in an extern "C" context no longer works. */
+#   endif
+
+#   if !defined(_WIN32_WCE) && !defined(__CEGCC__)
+#     include <process.h> /* For _beginthreadex, _endthreadex */
+#   endif
+
+#   include <windows.h>
+
+#   ifdef __cplusplus
+      extern "C" {
+#   endif
+
+#   ifdef GC_UNDERSCORE_STDCALL
+      /* Explicitly prefix exported/imported WINAPI (__stdcall) symbols */
+      /* with '_' (underscore).  Might be useful if MinGW/x86 is used.  */
+#     define GC_CreateThread _GC_CreateThread
+#     define GC_ExitThread _GC_ExitThread
+#   endif
+
+#   ifdef GC_INSIDE_DLL
+      /* Export GC DllMain to be invoked from client DllMain.   */
+#     ifdef GC_UNDERSCORE_STDCALL
+#       define GC_DllMain _GC_DllMain
+#     endif
+      GC_API BOOL WINAPI GC_DllMain(HINSTANCE /* inst */, ULONG /* reason */,
+                                    LPVOID /* reserved */);
+#   endif /* GC_INSIDE_DLL */
+
+#   if !defined(_UINTPTR_T) && !defined(_UINTPTR_T_DEFINED) \
+       && !defined(UINTPTR_MAX)
+      typedef GC_word GC_uintptr_t;
+#   else
+      typedef uintptr_t GC_uintptr_t;
+#   endif
+#   define GC_WIN32_SIZE_T GC_uintptr_t
+
+    /* All threads must be created using GC_CreateThread or             */
+    /* GC_beginthreadex, or must explicitly call GC_register_my_thread  */
+    /* (and call GC_unregister_my_thread before thread termination), so */
+    /* that they will be recorded in the thread table.  For backward    */
+    /* compatibility, it is possible to build the GC with GC_DLL        */
+    /* defined, and to call GC_use_threads_discovery.  This implicitly  */
+    /* registers all created threads, but appears to be less robust.    */
+    /* Currently the collector expects all threads to fall through and  */
+    /* terminate normally, or call GC_endthreadex() or GC_ExitThread,   */
+    /* so that the thread is properly unregistered.                     */
+    GC_API HANDLE WINAPI GC_CreateThread(
+                LPSECURITY_ATTRIBUTES /* lpThreadAttributes */,
+                GC_WIN32_SIZE_T /* dwStackSize */,
+                LPTHREAD_START_ROUTINE /* lpStartAddress */,
+                LPVOID /* lpParameter */, DWORD /* dwCreationFlags */,
+                LPDWORD /* lpThreadId */);
+
+#   ifndef DECLSPEC_NORETURN
+      /* Typically defined in winnt.h. */
+#     define DECLSPEC_NORETURN /* empty */
+#   endif
+
+    GC_API DECLSPEC_NORETURN void WINAPI GC_ExitThread(
+                                                DWORD /* dwExitCode */);
+
+#   if !defined(_WIN32_WCE) && !defined(__CEGCC__)
+      GC_API GC_uintptr_t GC_CALL GC_beginthreadex(
+                        void * /* security */, unsigned /* stack_size */,
+                        unsigned (__stdcall *)(void *),
+                        void * /* arglist */, unsigned /* initflag */,
+                        unsigned * /* thrdaddr */);
+
+      /* Note: _endthreadex() is not currently marked as no-return in   */
+      /* VC++ and MinGW headers, so we don't mark it neither.           */
+      GC_API void GC_CALL GC_endthreadex(unsigned /* retval */);
+#   endif /* !_WIN32_WCE */
+
+# endif /* !GC_NO_THREAD_DECLS */
+
+# ifdef GC_WINMAIN_REDIRECT
+    /* win32_threads.c implements the real WinMain(), which will start  */
+    /* a new thread to call GC_WinMain() after initializing the garbage */
+    /* collector.                                                       */
+#   define WinMain GC_WinMain
+# endif
+
+  /* For compatibility only. */
+# define GC_use_DllMain GC_use_threads_discovery
+
+# ifndef GC_NO_THREAD_REDIRECTS
+#   define CreateThread GC_CreateThread
+#   define ExitThread GC_ExitThread
+#   undef _beginthreadex
+#   define _beginthreadex GC_beginthreadex
+#   undef _endthreadex
+#   define _endthreadex GC_endthreadex
+/* #define _beginthread { > "Please use _beginthreadex instead of _beginthread" < } */
+# endif /* !GC_NO_THREAD_REDIRECTS */
+
+#endif /* GC_WIN32_THREADS */
+
+/* Public setter and getter for switching "unmap as much as possible"   */
+/* mode on(1) and off(0).  Has no effect unless unmapping is turned on. */
+/* Has no effect on implicitly-initiated garbage collections.  Initial  */
+/* value is controlled by GC_FORCE_UNMAP_ON_GCOLLECT.  The setter and   */
+/* getter are unsynchronized.                                           */
+GC_API void GC_CALL GC_set_force_unmap_on_gcollect(int);
+GC_API int GC_CALL GC_get_force_unmap_on_gcollect(void);
+
+/* Fully portable code should call GC_INIT() from the main program      */
+/* before making any other GC_ calls.  On most platforms this is a      */
+/* no-op and the collector self-initializes.  But a number of           */
+/* platforms make that too hard.                                        */
+/* A GC_INIT call is required if the collector is built with            */
+/* THREAD_LOCAL_ALLOC defined and the initial allocation call is not    */
+/* to GC_malloc() or GC_malloc_atomic().                                */
+
+#ifdef __CYGWIN32__
+  /* Similarly gnu-win32 DLLs need explicit initialization from the     */
+  /* main program, as does AIX.                                         */
+  extern int _data_start__[], _data_end__[], _bss_start__[], _bss_end__[];
+# define GC_DATASTART ((GC_word)_data_start__ < (GC_word)_bss_start__ ? \
+                       (void *)_data_start__ : (void *)_bss_start__)
+# define GC_DATAEND ((GC_word)_data_end__ > (GC_word)_bss_end__ ? \
+                     (void *)_data_end__ : (void *)_bss_end__)
+# define GC_INIT_CONF_ROOTS GC_add_roots(GC_DATASTART, GC_DATAEND); \
+                                 GC_gcollect() /* For blacklisting. */
+        /* Required at least if GC is in a DLL.  And doesn't hurt. */
+#elif defined(_AIX)
+  extern int _data[], _end[];
+# define GC_DATASTART ((void *)((ulong)_data))
+# define GC_DATAEND ((void *)((ulong)_end))
+# define GC_INIT_CONF_ROOTS GC_add_roots(GC_DATASTART, GC_DATAEND)
+#elif (defined(PLATFORM_ANDROID) || defined(__ANDROID__)) \
+      && !defined(GC_NOT_DLL)
+  /* Required if GC is built as shared lib with -D IGNORE_DYNAMIC_LOADING. */
+# pragma weak __data_start
+  extern int __data_start[], _end[];
+# define GC_INIT_CONF_ROOTS (void)((GC_word)(__data_start) != 0 ? \
+                                (GC_add_roots(__data_start, _end), 0) : 0)
+#else
+# define GC_INIT_CONF_ROOTS /* empty */
+#endif
+
+#ifdef GC_DONT_EXPAND
+  /* Set GC_dont_expand to TRUE at start-up */
+# define GC_INIT_CONF_DONT_EXPAND GC_set_dont_expand(1)
+#else
+# define GC_INIT_CONF_DONT_EXPAND /* empty */
+#endif
+
+#ifdef GC_FORCE_UNMAP_ON_GCOLLECT
+  /* Turn on "unmap as much as possible on explicit GC" mode at start-up */
+# define GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT \
+                GC_set_force_unmap_on_gcollect(1)
+#else
+# define GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT /* empty */
+#endif
+
+#ifdef GC_DONT_GC
+  /* This is for debugging only (useful if environment variables are    */
+  /* unsupported); cannot call GC_disable as goes before GC_init.       */
+# define GC_INIT_CONF_MAX_RETRIES (void)(GC_dont_gc = 1)
+#elif defined(GC_MAX_RETRIES)
+  /* Set GC_max_retries to the desired value at start-up */
+# define GC_INIT_CONF_MAX_RETRIES GC_set_max_retries(GC_MAX_RETRIES)
+#else
+# define GC_INIT_CONF_MAX_RETRIES /* empty */
+#endif
+
+#ifdef GC_FREE_SPACE_DIVISOR
+  /* Set GC_free_space_divisor to the desired value at start-up */
+# define GC_INIT_CONF_FREE_SPACE_DIVISOR \
+                GC_set_free_space_divisor(GC_FREE_SPACE_DIVISOR)
+#else
+# define GC_INIT_CONF_FREE_SPACE_DIVISOR /* empty */
+#endif
+
+#ifdef GC_FULL_FREQ
+  /* Set GC_full_freq to the desired value at start-up */
+# define GC_INIT_CONF_FULL_FREQ GC_set_full_freq(GC_FULL_FREQ)
+#else
+# define GC_INIT_CONF_FULL_FREQ /* empty */
+#endif
+
+#ifdef GC_TIME_LIMIT
+  /* Set GC_time_limit to the desired value at start-up */
+# define GC_INIT_CONF_TIME_LIMIT GC_set_time_limit(GC_TIME_LIMIT)
+#else
+# define GC_INIT_CONF_TIME_LIMIT /* empty */
+#endif
+
+#if defined(GC_SIG_SUSPEND) && defined(GC_THREADS)
+# define GC_INIT_CONF_SUSPEND_SIGNAL GC_set_suspend_signal(GC_SIG_SUSPEND)
+#else
+# define GC_INIT_CONF_SUSPEND_SIGNAL /* empty */
+#endif
+
+#if defined(GC_SIG_THR_RESTART) && defined(GC_THREADS)
+# define GC_INIT_CONF_THR_RESTART_SIGNAL \
+                GC_set_thr_restart_signal(GC_SIG_THR_RESTART)
+#else
+# define GC_INIT_CONF_THR_RESTART_SIGNAL /* empty */
+#endif
+
+#ifdef GC_MAXIMUM_HEAP_SIZE
+  /* Limit the heap size to the desired value (useful for debugging).   */
+  /* The limit could be overridden either at the program start-up by    */
+  /* the similar environment variable or anytime later by the           */
+  /* corresponding API function call.                                   */
+# define GC_INIT_CONF_MAXIMUM_HEAP_SIZE \
+                GC_set_max_heap_size(GC_MAXIMUM_HEAP_SIZE)
+#else
+# define GC_INIT_CONF_MAXIMUM_HEAP_SIZE /* empty */
+#endif
+
+#ifdef GC_IGNORE_WARN
+  /* Turn off all warnings at start-up (after GC initialization) */
+# define GC_INIT_CONF_IGNORE_WARN GC_set_warn_proc(GC_ignore_warn_proc)
+#else
+# define GC_INIT_CONF_IGNORE_WARN /* empty */
+#endif
+
+#ifdef GC_INITIAL_HEAP_SIZE
+  /* Set heap size to the desired value at start-up */
+# define GC_INIT_CONF_INITIAL_HEAP_SIZE \
+                { size_t heap_size = GC_get_heap_size(); \
+                  if (heap_size < (GC_INITIAL_HEAP_SIZE)) \
+                    (void)GC_expand_hp((GC_INITIAL_HEAP_SIZE) - heap_size); }
+#else
+# define GC_INIT_CONF_INITIAL_HEAP_SIZE /* empty */
+#endif
+
+/* Portable clients should call this at the program start-up.  More     */
+/* over, some platforms require this call to be done strictly from the  */
+/* primordial thread.                                                   */
+#define GC_INIT() { GC_INIT_CONF_DONT_EXPAND; /* pre-init */ \
+                    GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT; \
+                    GC_INIT_CONF_MAX_RETRIES; \
+                    GC_INIT_CONF_FREE_SPACE_DIVISOR; \
+                    GC_INIT_CONF_FULL_FREQ; \
+                    GC_INIT_CONF_TIME_LIMIT; \
+                    GC_INIT_CONF_SUSPEND_SIGNAL; \
+                    GC_INIT_CONF_THR_RESTART_SIGNAL; \
+                    GC_INIT_CONF_MAXIMUM_HEAP_SIZE; \
+                    GC_init(); /* real GC initialization */ \
+                    GC_INIT_CONF_ROOTS; /* post-init */ \
+                    GC_INIT_CONF_IGNORE_WARN; \
+                    GC_INIT_CONF_INITIAL_HEAP_SIZE; }
+
+/* win32S may not free all resources on process exit.   */
+/* This explicitly deallocates the heap.                */
+GC_API void GC_CALL GC_win32_free_heap(void);
+
+#if defined(__SYMBIAN32__)
+  void GC_init_global_static_roots(void);
+#endif
+
+#if defined(_AMIGA) && !defined(GC_AMIGA_MAKINGLIB)
+  /* Allocation really goes through GC_amiga_allocwrapper_do.   */
+  void *GC_amiga_realloc(void *, size_t);
+# define GC_realloc(a,b) GC_amiga_realloc(a,b)
+  void GC_amiga_set_toany(void (*)(void));
+  extern int GC_amiga_free_space_divisor_inc;
+  extern void *(*GC_amiga_allocwrapper_do)(size_t, void *(GC_CALL *)(size_t));
+# define GC_malloc(a) \
+        (*GC_amiga_allocwrapper_do)(a,GC_malloc)
+# define GC_malloc_atomic(a) \
+        (*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic)
+# define GC_malloc_uncollectable(a) \
+        (*GC_amiga_allocwrapper_do)(a,GC_malloc_uncollectable)
+# define GC_malloc_stubborn(a) \
+        (*GC_amiga_allocwrapper_do)(a,GC_malloc_stubborn)
+# define GC_malloc_atomic_uncollectable(a) \
+        (*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic_uncollectable)
+# define GC_malloc_ignore_off_page(a) \
+        (*GC_amiga_allocwrapper_do)(a,GC_malloc_ignore_off_page)
+# define GC_malloc_atomic_ignore_off_page(a) \
+        (*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic_ignore_off_page)
+#endif /* _AMIGA && !GC_AMIGA_MAKINGLIB */
+
+#ifdef __cplusplus
+  }  /* end of extern "C" */
+#endif
+
+#endif /* GC_H */

+ 325 - 0
blitz.mod/bdwgc/include/gc_allocator.h

@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 1996-1997
+ * Silicon Graphics Computer Systems, Inc.
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation.  Silicon Graphics makes no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * Copyright (c) 2002
+ * Hewlett-Packard Company
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation.  Hewlett-Packard Company makes no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ */
+
+/*
+ * This implements standard-conforming allocators that interact with
+ * the garbage collector.  Gc_allocator<T> allocates garbage-collectible
+ * objects of type T.  Traceable_allocator<T> allocates objects that
+ * are not themselves garbage collected, but are scanned by the
+ * collector for pointers to collectible objects.  Traceable_alloc
+ * should be used for explicitly managed STL containers that may
+ * point to collectible objects.
+ *
+ * This code was derived from an earlier version of the GNU C++ standard
+ * library, which itself was derived from the SGI STL implementation.
+ *
+ * Ignore-off-page allocator: George T. Talbot
+ */
+
+#ifndef GC_ALLOCATOR_H
+
+#define GC_ALLOCATOR_H
+
+#include "gc.h"
+#include <new> // for placement new
+
+#if defined(__GNUC__)
+#  define GC_ATTR_UNUSED __attribute__((__unused__))
+#else
+#  define GC_ATTR_UNUSED
+#endif
+
+/* First some helpers to allow us to dispatch on whether or not a type
+ * is known to be pointer-free.
+ * These are private, except that the client may invoke the
+ * GC_DECLARE_PTRFREE macro.
+ */
+
+struct GC_true_type {};
+struct GC_false_type {};
+
+template <class GC_tp>
+struct GC_type_traits {
+  GC_false_type GC_is_ptr_free;
+};
+
+# define GC_DECLARE_PTRFREE(T) \
+template<> struct GC_type_traits<T> { GC_true_type GC_is_ptr_free; }
+
+GC_DECLARE_PTRFREE(char);
+GC_DECLARE_PTRFREE(signed char);
+GC_DECLARE_PTRFREE(unsigned char);
+GC_DECLARE_PTRFREE(signed short);
+GC_DECLARE_PTRFREE(unsigned short);
+GC_DECLARE_PTRFREE(signed int);
+GC_DECLARE_PTRFREE(unsigned int);
+GC_DECLARE_PTRFREE(signed long);
+GC_DECLARE_PTRFREE(unsigned long);
+GC_DECLARE_PTRFREE(float);
+GC_DECLARE_PTRFREE(double);
+GC_DECLARE_PTRFREE(long double);
+/* The client may want to add others.   */
+
+// In the following GC_Tp is GC_true_type if we are allocating a
+// pointer-free object.
+template <class GC_Tp>
+inline void * GC_selective_alloc(size_t n, GC_Tp, bool ignore_off_page) {
+    return ignore_off_page?GC_MALLOC_IGNORE_OFF_PAGE(n):GC_MALLOC(n);
+}
+
+template <>
+inline void * GC_selective_alloc<GC_true_type>(size_t n, GC_true_type,
+                                               bool ignore_off_page) {
+    return ignore_off_page? GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(n)
+                          : GC_MALLOC_ATOMIC(n);
+}
+
+/* Now the public gc_allocator<T> class:
+ */
+template <class GC_Tp>
+class gc_allocator {
+public:
+  typedef size_t     size_type;
+  typedef ptrdiff_t  difference_type;
+  typedef GC_Tp*       pointer;
+  typedef const GC_Tp* const_pointer;
+  typedef GC_Tp&       reference;
+  typedef const GC_Tp& const_reference;
+  typedef GC_Tp        value_type;
+
+  template <class GC_Tp1> struct rebind {
+    typedef gc_allocator<GC_Tp1> other;
+  };
+
+  gc_allocator()  {}
+    gc_allocator(const gc_allocator&) throw() {}
+# if !(GC_NO_MEMBER_TEMPLATES || 0 < _MSC_VER && _MSC_VER <= 1200)
+  // MSVC++ 6.0 do not support member templates
+  template <class GC_Tp1> gc_allocator(const gc_allocator<GC_Tp1>&) throw() {}
+# endif
+  ~gc_allocator() throw() {}
+
+  pointer address(reference GC_x) const { return &GC_x; }
+  const_pointer address(const_reference GC_x) const { return &GC_x; }
+
+  // GC_n is permitted to be 0.  The C++ standard says nothing about what
+  // the return value is when GC_n == 0.
+  GC_Tp* allocate(size_type GC_n, const void* = 0) {
+    GC_type_traits<GC_Tp> traits;
+    return static_cast<GC_Tp *>
+            (GC_selective_alloc(GC_n * sizeof(GC_Tp),
+                                traits.GC_is_ptr_free, false));
+  }
+
+  // __p is not permitted to be a null pointer.
+  void deallocate(pointer __p, size_type GC_ATTR_UNUSED GC_n)
+    { GC_FREE(__p); }
+
+  size_type max_size() const throw()
+    { return size_t(-1) / sizeof(GC_Tp); }
+
+  void construct(pointer __p, const GC_Tp& __val) { new(__p) GC_Tp(__val); }
+  void destroy(pointer __p) { __p->~GC_Tp(); }
+};
+
+template<>
+class gc_allocator<void> {
+  typedef size_t      size_type;
+  typedef ptrdiff_t   difference_type;
+  typedef void*       pointer;
+  typedef const void* const_pointer;
+  typedef void        value_type;
+
+  template <class GC_Tp1> struct rebind {
+    typedef gc_allocator<GC_Tp1> other;
+  };
+};
+
+
+template <class GC_T1, class GC_T2>
+inline bool operator==(const gc_allocator<GC_T1>&, const gc_allocator<GC_T2>&)
+{
+  return true;
+}
+
+template <class GC_T1, class GC_T2>
+inline bool operator!=(const gc_allocator<GC_T1>&, const gc_allocator<GC_T2>&)
+{
+  return false;
+}
+
+
+/* Now the public gc_allocator_ignore_off_page<T> class:
+ */
+template <class GC_Tp>
+class gc_allocator_ignore_off_page {
+public:
+  typedef size_t     size_type;
+  typedef ptrdiff_t  difference_type;
+  typedef GC_Tp*       pointer;
+  typedef const GC_Tp* const_pointer;
+  typedef GC_Tp&       reference;
+  typedef const GC_Tp& const_reference;
+  typedef GC_Tp        value_type;
+
+  template <class GC_Tp1> struct rebind {
+    typedef gc_allocator_ignore_off_page<GC_Tp1> other;
+  };
+
+  gc_allocator_ignore_off_page()  {}
+    gc_allocator_ignore_off_page(const gc_allocator_ignore_off_page&) throw() {}
+# if !(GC_NO_MEMBER_TEMPLATES || 0 < _MSC_VER && _MSC_VER <= 1200)
+  // MSVC++ 6.0 do not support member templates
+  template <class GC_Tp1>
+    gc_allocator_ignore_off_page(const gc_allocator_ignore_off_page<GC_Tp1>&)
+        throw() {}
+# endif
+  ~gc_allocator_ignore_off_page() throw() {}
+
+  pointer address(reference GC_x) const { return &GC_x; }
+  const_pointer address(const_reference GC_x) const { return &GC_x; }
+
+  // GC_n is permitted to be 0.  The C++ standard says nothing about what
+  // the return value is when GC_n == 0.
+  GC_Tp* allocate(size_type GC_n, const void* = 0) {
+    GC_type_traits<GC_Tp> traits;
+    return static_cast<GC_Tp *>
+            (GC_selective_alloc(GC_n * sizeof(GC_Tp),
+                                traits.GC_is_ptr_free, true));
+  }
+
+  // __p is not permitted to be a null pointer.
+  void deallocate(pointer __p, size_type GC_ATTR_UNUSED GC_n)
+    { GC_FREE(__p); }
+
+  size_type max_size() const throw()
+    { return size_t(-1) / sizeof(GC_Tp); }
+
+  void construct(pointer __p, const GC_Tp& __val) { new(__p) GC_Tp(__val); }
+  void destroy(pointer __p) { __p->~GC_Tp(); }
+};
+
+template<>
+class gc_allocator_ignore_off_page<void> {
+  typedef size_t      size_type;
+  typedef ptrdiff_t   difference_type;
+  typedef void*       pointer;
+  typedef const void* const_pointer;
+  typedef void        value_type;
+
+  template <class GC_Tp1> struct rebind {
+    typedef gc_allocator_ignore_off_page<GC_Tp1> other;
+  };
+};
+
+template <class GC_T1, class GC_T2>
+inline bool operator==(const gc_allocator_ignore_off_page<GC_T1>&, const gc_allocator_ignore_off_page<GC_T2>&)
+{
+  return true;
+}
+
+template <class GC_T1, class GC_T2>
+inline bool operator!=(const gc_allocator_ignore_off_page<GC_T1>&, const gc_allocator_ignore_off_page<GC_T2>&)
+{
+  return false;
+}
+
+/*
+ * And the public traceable_allocator class.
+ */
+
+// Note that we currently don't specialize the pointer-free case, since a
+// pointer-free traceable container doesn't make that much sense,
+// though it could become an issue due to abstraction boundaries.
+template <class GC_Tp>
+class traceable_allocator {
+public:
+  typedef size_t     size_type;
+  typedef ptrdiff_t  difference_type;
+  typedef GC_Tp*       pointer;
+  typedef const GC_Tp* const_pointer;
+  typedef GC_Tp&       reference;
+  typedef const GC_Tp& const_reference;
+  typedef GC_Tp        value_type;
+
+  template <class GC_Tp1> struct rebind {
+    typedef traceable_allocator<GC_Tp1> other;
+  };
+
+  traceable_allocator() throw() {}
+    traceable_allocator(const traceable_allocator&) throw() {}
+# if !(GC_NO_MEMBER_TEMPLATES || 0 < _MSC_VER && _MSC_VER <= 1200)
+  // MSVC++ 6.0 do not support member templates
+  template <class GC_Tp1> traceable_allocator
+          (const traceable_allocator<GC_Tp1>&) throw() {}
+# endif
+  ~traceable_allocator() throw() {}
+
+  pointer address(reference GC_x) const { return &GC_x; }
+  const_pointer address(const_reference GC_x) const { return &GC_x; }
+
+  // GC_n is permitted to be 0.  The C++ standard says nothing about what
+  // the return value is when GC_n == 0.
+  GC_Tp* allocate(size_type GC_n, const void* = 0) {
+    return static_cast<GC_Tp*>(GC_MALLOC_UNCOLLECTABLE(GC_n * sizeof(GC_Tp)));
+  }
+
+  // __p is not permitted to be a null pointer.
+  void deallocate(pointer __p, size_type GC_ATTR_UNUSED GC_n)
+    { GC_FREE(__p); }
+
+  size_type max_size() const throw()
+    { return size_t(-1) / sizeof(GC_Tp); }
+
+  void construct(pointer __p, const GC_Tp& __val) { new(__p) GC_Tp(__val); }
+  void destroy(pointer __p) { __p->~GC_Tp(); }
+};
+
+template<>
+class traceable_allocator<void> {
+  typedef size_t      size_type;
+  typedef ptrdiff_t   difference_type;
+  typedef void*       pointer;
+  typedef const void* const_pointer;
+  typedef void        value_type;
+
+  template <class GC_Tp1> struct rebind {
+    typedef traceable_allocator<GC_Tp1> other;
+  };
+};
+
+
+template <class GC_T1, class GC_T2>
+inline bool operator==(const traceable_allocator<GC_T1>&, const traceable_allocator<GC_T2>&)
+{
+  return true;
+}
+
+template <class GC_T1, class GC_T2>
+inline bool operator!=(const traceable_allocator<GC_T1>&, const traceable_allocator<GC_T2>&)
+{
+  return false;
+}
+
+#endif /* GC_ALLOCATOR_H */

+ 98 - 0
blitz.mod/bdwgc/include/gc_backptr.h

@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
+ * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
+ * Copyright (c) 2000-2009 by Hewlett-Packard Development Company.
+ * All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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 a simple API to implement pointer back tracing, i.e.
+ * to answer questions such as "who is pointing to this" or
+ * "why is this object being retained by the collector"
+ *
+ * This API assumes that we have an ANSI C compiler.
+ *
+ * Most of these calls yield useful information on only after
+ * a garbage collection.  Usually the client will first force
+ * a full collection and then gather information, preferably
+ * before much intervening allocation.
+ *
+ * The implementation of the interface is only about 99.9999%
+ * correct.  It is intended to be good enough for profiling,
+ * but is not intended to be used with production code.
+ *
+ * Results are likely to be much more useful if all allocation is
+ * accomplished through the debugging allocators.
+ *
+ * The implementation idea is due to A. Demers.
+ */
+
+#ifndef GC_BACKPTR_H
+#define GC_BACKPTR_H
+
+#ifndef GC_H
+# include "gc.h"
+#endif
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/* Store information about the object referencing dest in *base_p     */
+/* and *offset_p.                                                     */
+/* If multiple objects or roots point to dest, the one reported       */
+/* will be the last on used by the garbage collector to trace the     */
+/* object.                                                            */
+/*   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.                      */
+typedef enum {
+    GC_UNREFERENCED,    /* No reference info available.         */
+    GC_NO_SPACE,        /* Dest not allocated with debug alloc. */
+    GC_REFD_FROM_ROOT,  /* Referenced directly by root *base_p. */
+    GC_REFD_FROM_REG,   /* Referenced from a register, i.e.     */
+                        /* a root without an address.           */
+    GC_REFD_FROM_HEAP,  /* Referenced from another heap obj.    */
+    GC_FINALIZER_REFD   /* Finalizable and hence accessible.    */
+} GC_ref_kind;
+
+GC_API GC_ref_kind GC_CALL GC_get_back_ptr_info(void * /* dest */,
+                                void ** /* base_p */, size_t * /* offset_p */)
+                                GC_ATTR_NONNULL(1);
+
+/* Generate a random heap address.            */
+/* The resulting address is in the heap, but  */
+/* not necessarily inside a valid object.     */
+GC_API void * GC_CALL GC_generate_random_heap_address(void);
+
+/* Generate a random address inside a valid marked heap object. */
+GC_API void * GC_CALL GC_generate_random_valid_address(void);
+
+/* Force a garbage collection and generate a backtrace from a   */
+/* random heap address.                                         */
+/* This uses the GC logging mechanism (GC_printf) to produce    */
+/* output.  It can often be called from a debugger.  The        */
+/* source in dbg_mlc.c also serves as a sample client.          */
+GC_API void GC_CALL GC_generate_random_backtrace(void);
+
+/* Print a backtrace from a specific address.  Used by the      */
+/* above.  The client should call GC_gcollect() immediately     */
+/* before invocation.                                           */
+GC_API void GC_CALL GC_print_backtrace(void *) GC_ATTR_NONNULL(1);
+
+#ifdef __cplusplus
+  } /* end of extern "C" */
+#endif
+
+#endif /* GC_BACKPTR_H */

+ 386 - 0
blitz.mod/bdwgc/include/gc_config_macros.h

@@ -0,0 +1,386 @@
+/*
+ * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
+ * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
+ * Copyright (c) 2000-2009 by Hewlett-Packard Development Company.
+ * All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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 should never be included directly; it is included only from gc.h. */
+/* We separate it only to make gc.h more suitable as documentation.       */
+#if defined(GC_H)
+
+/* Some tests for old macros.  These violate our namespace rules and    */
+/* will disappear shortly.  Use the GC_ names.                          */
+#if defined(SOLARIS_THREADS) || defined(_SOLARIS_THREADS) \
+    || defined(_SOLARIS_PTHREADS) || defined(GC_SOLARIS_PTHREADS)
+  /* We no longer support old style Solaris threads.            */
+  /* GC_SOLARIS_THREADS now means pthreads.                     */
+# ifndef GC_SOLARIS_THREADS
+#   define GC_SOLARIS_THREADS
+# endif
+#endif
+#if defined(IRIX_THREADS)
+# define GC_IRIX_THREADS
+#endif
+#if defined(DGUX_THREADS) && !defined(GC_DGUX386_THREADS)
+# define GC_DGUX386_THREADS
+#endif
+#if defined(AIX_THREADS)
+# define GC_AIX_THREADS
+#endif
+#if defined(HPUX_THREADS)
+# define GC_HPUX_THREADS
+#endif
+#if defined(OSF1_THREADS)
+# define GC_OSF1_THREADS
+#endif
+#if defined(LINUX_THREADS)
+# define GC_LINUX_THREADS
+#endif
+#if defined(WIN32_THREADS)
+# define GC_WIN32_THREADS
+#endif
+#if defined(RTEMS_THREADS)
+# define GC_RTEMS_PTHREADS
+#endif
+#if defined(USE_LD_WRAP)
+# define GC_USE_LD_WRAP
+#endif
+
+#if defined(GC_WIN32_PTHREADS) && !defined(GC_WIN32_THREADS)
+  /* Using pthreads-w32 library. */
+# define GC_WIN32_THREADS
+#endif
+
+#if defined(GC_AIX_THREADS) || defined(GC_DARWIN_THREADS) \
+    || defined(GC_DGUX386_THREADS) || defined(GC_FREEBSD_THREADS) \
+    || defined(GC_GNU_THREADS) || defined(GC_HPUX_THREADS) \
+    || defined(GC_IRIX_THREADS) || defined(GC_LINUX_THREADS) \
+    || defined(GC_NETBSD_THREADS) || defined(GC_OPENBSD_THREADS) \
+    || defined(GC_OSF1_THREADS) || defined(GC_SOLARIS_THREADS) \
+    || defined(GC_WIN32_THREADS) || defined(GC_RTEMS_PTHREADS)
+# ifndef GC_THREADS
+#   define GC_THREADS
+# endif
+#elif defined(GC_THREADS)
+# if defined(__linux__)
+#   define GC_LINUX_THREADS
+# endif
+# if !defined(__linux__) && (defined(_PA_RISC1_1) || defined(_PA_RISC2_0) \
+                             || defined(hppa) || defined(__HPPA)) \
+     || (defined(__ia64) && defined(_HPUX_SOURCE))
+#   define GC_HPUX_THREADS
+# endif
+# if !defined(__linux__) && (defined(__alpha) || defined(__alpha__))
+#   define GC_OSF1_THREADS
+# endif
+# if defined(__mips) && !defined(__linux__)
+#   define GC_IRIX_THREADS
+# endif
+# if defined(__sparc) && !defined(__linux__) \
+     || defined(sun) && (defined(i386) || defined(__i386__) \
+                         || defined(__amd64__))
+#   define GC_SOLARIS_THREADS
+# elif defined(__APPLE__) && defined(__MACH__)
+#   define GC_DARWIN_THREADS
+# elif defined(__OpenBSD__)
+#   define GC_OPENBSD_THREADS
+# elif !defined(GC_LINUX_THREADS) && !defined(GC_HPUX_THREADS) \
+       && !defined(GC_OSF1_THREADS) && !defined(GC_IRIX_THREADS)
+    /* FIXME: Should we really need for FreeBSD and NetBSD to check     */
+    /* that no other GC_xxx_THREADS macro is set?                       */
+#   if defined(__FreeBSD__) || defined(__DragonFly__)
+#     define GC_FREEBSD_THREADS
+#   elif defined(__NetBSD__)
+#     define GC_NETBSD_THREADS
+#   endif
+# endif
+# if defined(DGUX) && (defined(i386) || defined(__i386__))
+#   define GC_DGUX386_THREADS
+# endif
+# if defined(_AIX)
+#   define GC_AIX_THREADS
+# endif
+# if (defined(_WIN32) || defined(_MSC_VER) || defined(__BORLANDC__) \
+      || defined(__CYGWIN32__) || defined(__CYGWIN__) || defined(__CEGCC__) \
+      || defined(_WIN32_WCE) || defined(__MINGW32__)) \
+     && !defined(GC_WIN32_THREADS)
+    /* Either posix or native Win32 threads. */
+#   define GC_WIN32_THREADS
+# endif
+# if defined(__rtems__) && (defined(i386) || defined(__i386__))
+#   define GC_RTEMS_PTHREADS
+# endif
+#endif /* GC_THREADS */
+
+#undef GC_PTHREADS
+#if (!defined(GC_WIN32_THREADS) || defined(GC_WIN32_PTHREADS) \
+     || defined(__CYGWIN32__) || defined(__CYGWIN__)) && defined(GC_THREADS)
+  /* Posix threads. */
+# define GC_PTHREADS
+#endif
+
+#if !defined(_PTHREADS) && defined(GC_NETBSD_THREADS)
+# define _PTHREADS
+#endif
+
+#if defined(GC_DGUX386_THREADS) && !defined(_POSIX4A_DRAFT10_SOURCE)
+# define _POSIX4A_DRAFT10_SOURCE 1
+#endif
+
+#if !defined(_REENTRANT) && defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS)
+  /* Better late than never.  This fails if system headers that depend  */
+  /* on this were previously included.                                  */
+# define _REENTRANT
+#endif
+
+#define __GC
+#if !defined(_WIN32_WCE) || defined(__GNUC__)
+# include <stddef.h>
+# if defined(__MINGW32__) && !defined(_WIN32_WCE)
+#   include <stdint.h>
+    /* We mention uintptr_t.                                            */
+    /* Perhaps this should be included in pure msft environments        */
+    /* as well?                                                         */
+# endif
+#else /* _WIN32_WCE */
+  /* Yet more kludges for WinCE.        */
+# include <stdlib.h> /* size_t is defined here */
+# ifndef _PTRDIFF_T_DEFINED
+    /* ptrdiff_t is not defined */
+#   define _PTRDIFF_T_DEFINED
+    typedef long ptrdiff_t;
+# endif
+#endif /* _WIN32_WCE */
+
+#if !defined(GC_NOT_DLL) && !defined(GC_DLL) \
+    && ((defined(_DLL) && !defined(__GNUC__)) \
+        || (defined(DLL_EXPORT) && defined(GC_BUILD)))
+# define GC_DLL
+#endif
+
+#if defined(GC_DLL) && !defined(GC_API)
+
+# if defined(__MINGW32__) || defined(__CEGCC__)
+#   ifdef GC_BUILD
+#     define GC_API __declspec(dllexport)
+#   else
+#     define GC_API __declspec(dllimport)
+#   endif
+
+# elif defined(_MSC_VER) || defined(__DMC__) || defined(__BORLANDC__) \
+        || defined(__CYGWIN__)
+#   ifdef GC_BUILD
+#     define GC_API extern __declspec(dllexport)
+#   else
+#     define GC_API __declspec(dllimport)
+#   endif
+
+# elif defined(__WATCOMC__)
+#   ifdef GC_BUILD
+#     define GC_API extern __declspec(dllexport)
+#   else
+#     define GC_API extern __declspec(dllimport)
+#   endif
+
+# elif defined(__SYMBIAN32__)
+#   ifdef GC_BUILD
+#     define GC_API extern EXPORT_C
+#   else
+#     define GC_API extern IMPORT_C
+#   endif
+
+# elif defined(__GNUC__)
+    /* Only matters if used in conjunction with -fvisibility=hidden option. */
+#   if defined(GC_BUILD) && (__GNUC__ >= 4 \
+                             || defined(GC_VISIBILITY_HIDDEN_SET))
+#     define GC_API extern __attribute__((__visibility__("default")))
+#   endif
+# endif
+#endif /* GC_DLL */
+
+#ifndef GC_API
+# define GC_API extern
+#endif
+
+#ifndef GC_CALL
+# define GC_CALL
+#endif
+
+#ifndef GC_CALLBACK
+# define GC_CALLBACK GC_CALL
+#endif
+
+#ifndef GC_ATTR_MALLOC
+  /* 'malloc' attribute should be used for all malloc-like functions    */
+  /* (to tell the compiler that a function may be treated as if any     */
+  /* non-NULL pointer it returns cannot alias any other pointer valid   */
+  /* when the function returns).  If the client code violates this rule */
+  /* by using custom GC_oom_func then define GC_OOM_FUNC_RETURNS_ALIAS. */
+# ifdef GC_OOM_FUNC_RETURNS_ALIAS
+#   define GC_ATTR_MALLOC /* empty */
+# elif defined(__GNUC__) && (__GNUC__ > 3 \
+                             || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
+#   define GC_ATTR_MALLOC __attribute__((__malloc__))
+# elif defined(_MSC_VER) && _MSC_VER >= 14
+#   define GC_ATTR_MALLOC __declspec(noalias) __declspec(restrict)
+# else
+#   define GC_ATTR_MALLOC
+# endif
+#endif
+
+#ifndef GC_ATTR_ALLOC_SIZE
+  /* 'alloc_size' attribute improves __builtin_object_size correctness. */
+  /* Only single-argument form of 'alloc_size' attribute is used.       */
+# if defined(__GNUC__) && (__GNUC__ > 4 \
+        || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3 && !defined(__ICC)) \
+        || __clang_major__ > 3 \
+        || (__clang_major__ == 3 && __clang_minor__ >= 2))
+#   define GC_ATTR_ALLOC_SIZE(argnum) __attribute__((__alloc_size__(argnum)))
+# else
+#   define GC_ATTR_ALLOC_SIZE(argnum)
+# endif
+#endif
+
+#ifndef GC_ATTR_NONNULL
+# if defined(__GNUC__) && __GNUC__ >= 4
+#   define GC_ATTR_NONNULL(argnum) __attribute__((__nonnull__(argnum)))
+# else
+#   define GC_ATTR_NONNULL(argnum) /* empty */
+# endif
+#endif
+
+#ifndef GC_ATTR_DEPRECATED
+# ifdef GC_BUILD
+#   undef GC_ATTR_DEPRECATED
+#   define GC_ATTR_DEPRECATED /* empty */
+# elif defined(__GNUC__) && __GNUC__ >= 4
+#   define GC_ATTR_DEPRECATED __attribute__((__deprecated__))
+# elif defined(_MSC_VER) && _MSC_VER >= 12
+#   define GC_ATTR_DEPRECATED __declspec(deprecated)
+# else
+#   define GC_ATTR_DEPRECATED /* empty */
+# endif
+#endif
+
+#if defined(__sgi) && !defined(__GNUC__) && _COMPILER_VERSION >= 720
+# define GC_ADD_CALLER
+# define GC_RETURN_ADDR (GC_word)__return_address
+#endif
+
+#if defined(__linux__) || defined(__GLIBC__)
+# if !defined(__native_client__)
+#   include <features.h>
+# endif
+# if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1 || __GLIBC__ > 2) \
+        && !defined(__ia64__) && !defined(__UCLIBC__) \
+        && !defined(GC_HAVE_BUILTIN_BACKTRACE)
+#   define GC_HAVE_BUILTIN_BACKTRACE
+# endif
+# if defined(__i386__) || defined(__amd64__) || defined(__x86_64__)
+#   define GC_CAN_SAVE_CALL_STACKS
+# endif
+#endif /* GLIBC */
+
+#if defined(_MSC_VER) && _MSC_VER >= 1200 /* version 12.0+ (MSVC 6.0+) */ \
+        && !defined(_AMD64_) && !defined(_M_X64) && !defined(_WIN32_WCE) \
+        && !defined(GC_HAVE_NO_BUILTIN_BACKTRACE) \
+        && !defined(GC_HAVE_BUILTIN_BACKTRACE)
+# define GC_HAVE_BUILTIN_BACKTRACE
+#endif
+
+#if defined(GC_HAVE_BUILTIN_BACKTRACE) && !defined(GC_CAN_SAVE_CALL_STACKS)
+# define GC_CAN_SAVE_CALL_STACKS
+#endif
+
+#if defined(__sparc__)
+# define GC_CAN_SAVE_CALL_STACKS
+#endif
+
+/* If we're on an a platform on which we can't save call stacks, but    */
+/* gcc is normally used, we go ahead and define GC_ADD_CALLER.          */
+/* We make this decision independent of whether gcc is actually being   */
+/* used, in order to keep the interface consistent, and allow mixing    */
+/* of compilers.                                                        */
+/* This may also be desirable if it is possible but expensive to        */
+/* retrieve the call chain.                                             */
+#if (defined(__linux__) || defined(__NetBSD__) || defined(__OpenBSD__) \
+     || defined(__FreeBSD__) || defined(__DragonFly__) \
+     || defined(PLATFORM_ANDROID) || defined(__ANDROID__)) \
+    && !defined(GC_CAN_SAVE_CALL_STACKS)
+# define GC_ADD_CALLER
+# if __GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
+    /* gcc knows how to retrieve return address, but we don't know      */
+    /* how to generate call stacks.                                     */
+#   define GC_RETURN_ADDR (GC_word)__builtin_return_address(0)
+#   if (__GNUC__ >= 4) && (defined(__i386__) || defined(__amd64__) \
+        || defined(__x86_64__) /* and probably others... */)
+#     define GC_RETURN_ADDR_PARENT \
+        (GC_word)__builtin_extract_return_addr(__builtin_return_address(1))
+#   endif
+# else
+    /* Just pass 0 for gcc compatibility.       */
+#   define GC_RETURN_ADDR 0
+# endif
+#endif /* !GC_CAN_SAVE_CALL_STACKS */
+
+#ifdef GC_PTHREADS
+
+# if (defined(GC_DARWIN_THREADS) || defined(GC_WIN32_PTHREADS) \
+      || defined(__native_client__) || defined(GC_RTEMS_PTHREADS)) \
+      && !defined(GC_NO_DLOPEN)
+    /* Either there is no dlopen() or we do not need to intercept it.   */
+#   define GC_NO_DLOPEN
+# endif
+
+# if (defined(GC_DARWIN_THREADS) || defined(GC_WIN32_PTHREADS) \
+      || defined(GC_OPENBSD_THREADS) || defined(__native_client__)) \
+     && !defined(GC_NO_PTHREAD_SIGMASK)
+    /* Either there is no pthread_sigmask() or no need to intercept it. */
+#   define GC_NO_PTHREAD_SIGMASK
+# endif
+
+# if defined(__native_client__)
+    /* At present, NaCl pthread_create() prototype does not have        */
+    /* "const" for its "attr" argument; also, NaCl pthread_exit() one   */
+    /* does not have "noreturn" attribute.                              */
+#   ifndef GC_PTHREAD_CREATE_CONST
+#     define GC_PTHREAD_CREATE_CONST /* empty */
+#   endif
+#   ifndef GC_PTHREAD_EXIT_ATTRIBUTE
+#     define GC_PTHREAD_EXIT_ATTRIBUTE /* empty */
+#   endif
+# endif
+
+# if !defined(GC_PTHREAD_EXIT_ATTRIBUTE) \
+     && !defined(PLATFORM_ANDROID) && !defined(__ANDROID__) \
+     && (defined(GC_LINUX_THREADS) || defined(GC_SOLARIS_THREADS))
+    /* Intercept pthread_exit on Linux and Solaris.     */
+#   if defined(__GNUC__) /* since GCC v2.7 */
+#     define GC_PTHREAD_EXIT_ATTRIBUTE __attribute__((__noreturn__))
+#   elif defined(__NORETURN) /* used in Solaris */
+#     define GC_PTHREAD_EXIT_ATTRIBUTE __NORETURN
+#   else
+#     define GC_PTHREAD_EXIT_ATTRIBUTE /* empty */
+#   endif
+# endif
+
+# if (!defined(GC_PTHREAD_EXIT_ATTRIBUTE) || defined(__native_client__)) \
+     && !defined(GC_NO_PTHREAD_CANCEL)
+    /* Either there is no pthread_cancel() or no need to intercept it.  */
+#   define GC_NO_PTHREAD_CANCEL
+# endif
+
+#endif /* GC_PTHREADS */
+
+#endif

+ 436 - 0
blitz.mod/bdwgc/include/gc_cpp.h

@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program for any
+ * purpose, provided the above notices are retained on all copies.
+ * 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.
+ */
+
+#ifndef GC_CPP_H
+#define GC_CPP_H
+
+/****************************************************************************
+C++ Interface to the Boehm Collector
+
+    John R. Ellis and Jesse Hull
+
+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://ftp.parc.xerox.com/pub/ellis/gc).
+
+All heap-allocated objects are either "collectible" or
+"uncollectible".  Programs must explicitly delete uncollectible
+objects, whereas the garbage collector will automatically delete
+collectible objects when it discovers them to be inaccessible.
+Collectible objects may freely point at uncollectible objects and vice
+versa.
+
+Objects allocated with the built-in "::operator new" are uncollectible.
+
+Objects derived from class "gc" are collectible.  For example:
+
+    class A: public gc {...};
+    A* a = new A;       // a is collectible.
+
+Collectible instances of non-class types can be allocated using the GC
+(or UseGC) placement:
+
+    typedef int A[ 10 ];
+    A* a = new (GC) A;
+
+Uncollectible instances of classes derived from "gc" can be allocated
+using the NoGC placement:
+
+    class A: public gc {...};
+    A* a = new (NoGC) A;   // a is uncollectible.
+
+The new(PointerFreeGC) syntax allows the allocation of collectible
+objects that are not scanned by the collector.  This useful if you
+are allocating compressed data, bitmaps, or network packets.  (In
+the latter case, it may remove danger of unfriendly network packets
+intentionally containing values that cause spurious memory retention.)
+
+Both uncollectible and collectible objects can be explicitly deleted
+with "delete", which invokes an object's destructors and frees its
+storage immediately.
+
+A collectible object may have a clean-up function, which will be
+invoked when the collector discovers the object to be inaccessible.
+An object derived from "gc_cleanup" or containing a member derived
+from "gc_cleanup" has a default clean-up function that invokes the
+object's destructors.  Explicit clean-up functions may be specified as
+an additional placement argument:
+
+    A* a = ::new (GC, MyCleanup) A;
+
+An object is considered "accessible" by the collector if it can be
+reached by a path of pointers from static variables, automatic
+variables of active functions, or from some object with clean-up
+enabled; pointers from an object to itself are ignored.
+
+Thus, if objects A and B both have clean-up functions, and A points at
+B, B is considered accessible.  After A's clean-up is invoked and its
+storage released, B will then become inaccessible and will have its
+clean-up invoked.  If A points at B and B points to A, forming a
+cycle, then that's considered a storage leak, and neither will be
+collectible.  See the interface gc.h for low-level facilities for
+handling such cycles of objects with clean-up.
+
+The collector cannot guarantee that it will find all inaccessible
+objects.  In practice, it finds almost all of them.
+
+
+Cautions:
+
+1. Be sure the collector has been augmented with "make c++" or
+"--enable-cplusplus".
+
+2.  If your compiler supports the new "operator new[]" syntax, then
+add -DGC_OPERATOR_NEW_ARRAY to the Makefile.
+
+If your compiler doesn't support "operator new[]", beware that an
+array of type T, where T is derived from "gc", may or may not be
+allocated as a collectible object (it depends on the compiler).  Use
+the explicit GC placement to make the array collectible.  For example:
+
+    class A: public gc {...};
+    A* a1 = new A[ 10 ];        // collectible or uncollectible?
+    A* a2 = new (GC) A[ 10 ];   // collectible.
+
+3. The destructors of collectible arrays of objects derived from
+"gc_cleanup" will not be invoked properly.  For example:
+
+    class A: public gc_cleanup {...};
+    A* a = new (GC) A[ 10 ];    // destructors not invoked correctly
+
+Typically, only the destructor for the first element of the array will
+be invoked when the array is garbage-collected.  To get all the
+destructors of any array executed, you must supply an explicit
+clean-up function:
+
+    A* a = new (GC, MyCleanUp) A[ 10 ];
+
+(Implementing clean-up of arrays correctly, portably, and in a way
+that preserves the correct exception semantics requires a language
+extension, e.g. the "gc" keyword.)
+
+4. Compiler bugs (now hopefully history):
+
+* Solaris 2's CC (SC3.0) doesn't implement t->~T() correctly, so the
+destructors of classes derived from gc_cleanup won't be invoked.
+You'll have to explicitly register a clean-up function with
+new-placement syntax.
+
+* Evidently cfront 3.0 does not allow destructors to be explicitly
+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"
+
+#ifdef GC_NAMESPACE
+# define GC_NS_QUALIFY(T) boehmgc::T
+#else
+# define GC_NS_QUALIFY(T) T
+#endif
+
+#ifndef THINK_CPLUS
+#  define GC_cdecl GC_CALLBACK
+#else
+#  define GC_cdecl _cdecl
+#endif
+
+#if ! defined( GC_NO_OPERATOR_NEW_ARRAY ) \
+    && !defined(_ENABLE_ARRAYNEW) /* Digimars */ \
+    && (defined(__BORLANDC__) && (__BORLANDC__ < 0x450) \
+        || (defined(__GNUC__) && \
+            (__GNUC__ < 2 || __GNUC__ == 2 && __GNUC_MINOR__ < 6)) \
+        || (defined(_MSC_VER) && _MSC_VER <= 1020) \
+        || (defined(__WATCOMC__) && __WATCOMC__ < 1050))
+#   define GC_NO_OPERATOR_NEW_ARRAY
+#endif
+
+#if !defined(GC_NO_OPERATOR_NEW_ARRAY) && !defined(GC_OPERATOR_NEW_ARRAY)
+#   define GC_OPERATOR_NEW_ARRAY
+#endif
+
+#if (!defined(__BORLANDC__) || __BORLANDC__ > 0x0620) \
+    && ! defined ( __sgi ) && ! defined( __WATCOMC__ ) \
+    && (!defined(_MSC_VER) || _MSC_VER > 1020)
+#  define GC_PLACEMENT_DELETE
+#endif
+
+#ifdef GC_NAMESPACE
+namespace boehmgc
+{
+#endif
+
+enum GCPlacement {
+  UseGC,
+# ifndef GC_NAME_CONFLICT
+    GC=UseGC,
+# endif
+  NoGC,
+  PointerFreeGC
+};
+
+class gc {
+  public:
+    inline void* operator new( size_t size );
+    inline void* operator new( size_t size, GCPlacement gcp );
+    inline void* operator new( size_t size, void *p );
+        /* Must be redefined here, since the other overloadings */
+        /* hide the global definition.                          */
+    inline void operator delete( void* obj );
+#   ifdef GC_PLACEMENT_DELETE
+      inline void operator delete( void*, GCPlacement );
+        /* called if construction fails.        */
+      inline void operator delete( void*, void* );
+#   endif
+
+#ifdef GC_OPERATOR_NEW_ARRAY
+    inline void* operator new[]( size_t size );
+    inline void* operator new[]( size_t size, GCPlacement gcp );
+    inline void* operator new[]( size_t size, void *p );
+    inline void operator delete[]( void* obj );
+#   ifdef GC_PLACEMENT_DELETE
+      inline void operator delete[]( void*, GCPlacement );
+      inline void operator delete[]( void*, void* );
+#   endif
+#endif /* GC_OPERATOR_NEW_ARRAY */
+};
+    /*
+    Instances of classes derived from "gc" will be allocated in the
+    collected heap by default, unless an explicit NoGC placement is
+    specified. */
+
+class gc_cleanup: virtual public gc {
+  public:
+    inline gc_cleanup();
+    inline virtual ~gc_cleanup();
+private:
+    inline static void GC_cdecl cleanup( void* obj, void* clientData );
+};
+    /*
+    Instances of classes derived from "gc_cleanup" will be allocated
+    in the collected heap by default.  When the collector discovers an
+    inaccessible object derived from "gc_cleanup" or containing a
+    member derived from "gc_cleanup", its destructors will be
+    invoked. */
+
+extern "C" {
+    typedef void (GC_CALLBACK * GCCleanUpFunc)( void* obj, void* clientData );
+}
+
+#ifdef GC_NAMESPACE
+}
+#endif
+
+#ifdef _MSC_VER
+  // Disable warning that "no matching operator delete found; memory will
+  // not be freed if initialization throws an exception"
+# pragma warning(disable:4291)
+#endif
+
+inline void* operator new( size_t size, GC_NS_QUALIFY(GCPlacement) gcp,
+                          GC_NS_QUALIFY(GCCleanUpFunc) cleanup = 0,
+                          void* clientData = 0 );
+    /*
+    Allocates a collectible or uncollectible object, according to the
+    value of "gcp".
+
+    For collectible objects, if "cleanup" is non-null, then when the
+    allocated object "obj" becomes inaccessible, the collector will
+    invoke the function "cleanup( obj, clientData )" but will not
+    invoke the object's destructors.  It is an error to explicitly
+    delete an object allocated with a non-null "cleanup".
+
+    It is an error to specify a non-null "cleanup" with NoGC or for
+    classes derived from "gc_cleanup" or containing members derived
+    from "gc_cleanup". */
+
+#ifdef GC_PLACEMENT_DELETE
+  inline void operator delete( void*, GC_NS_QUALIFY(GCPlacement),
+                              GC_NS_QUALIFY(GCCleanUpFunc), void * );
+#endif
+
+#ifdef _MSC_VER
+ /** This ensures that the system default operator new[] doesn't get
+  *  undefined, which is what seems to happen on VC++ 6 for some reason
+  *  if we define a multi-argument operator new[].
+  *  There seems to be no way to redirect new in this environment without
+  *  including this everywhere.
+  */
+# if _MSC_VER > 1020
+    void *operator new[]( size_t size );
+    void operator delete[]( void* obj );
+# endif
+
+  void* operator new( size_t size );
+  void operator delete( void* obj );
+
+  // This new operator is used by VC++ in case of Debug builds !
+  void* operator new( size_t size, int /* nBlockUse */,
+                     const char * szFileName, int nLine );
+#endif /* _MSC_VER */
+
+#ifdef GC_OPERATOR_NEW_ARRAY
+  inline void* operator new[]( size_t size, GC_NS_QUALIFY(GCPlacement) gcp,
+                              GC_NS_QUALIFY(GCCleanUpFunc) cleanup = 0,
+                              void* clientData = 0 );
+                /* The operator new for arrays, identical to the above. */
+#endif /* GC_OPERATOR_NEW_ARRAY */
+
+/****************************************************************************
+
+Inline implementation
+
+****************************************************************************/
+
+#ifdef GC_NAMESPACE
+namespace boehmgc
+{
+#endif
+
+inline void* gc::operator new( size_t size ) {
+    return GC_MALLOC( size );
+}
+
+inline void* gc::operator new( size_t size, GCPlacement gcp ) {
+    if (gcp == UseGC)
+        return GC_MALLOC( size );
+    else if (gcp == PointerFreeGC)
+        return GC_MALLOC_ATOMIC( size );
+    else
+        return GC_MALLOC_UNCOLLECTABLE( size );
+}
+
+inline void* gc::operator new( size_t /* size */, void *p ) {
+    return p;
+}
+
+inline void gc::operator delete( void* obj ) {
+    GC_FREE( obj );
+}
+
+#ifdef GC_PLACEMENT_DELETE
+  inline void gc::operator delete( void*, void* ) {}
+
+  inline void gc::operator delete( void* p, GCPlacement /* gcp */ ) {
+    GC_FREE(p);
+  }
+#endif
+
+#ifdef GC_OPERATOR_NEW_ARRAY
+  inline void* gc::operator new[]( size_t size ) {
+    return gc::operator new( size );
+  }
+
+  inline void* gc::operator new[]( size_t size, GCPlacement gcp ) {
+    return gc::operator new( size, gcp );
+  }
+
+  inline void* gc::operator new[]( size_t /* size */, void *p ) {
+    return p;
+  }
+
+  inline void gc::operator delete[]( void* obj ) {
+    gc::operator delete( obj );
+  }
+
+# ifdef GC_PLACEMENT_DELETE
+    inline void gc::operator delete[]( void*, void* ) {}
+
+    inline void gc::operator delete[]( void* p, GCPlacement /* gcp */ ) {
+      gc::operator delete(p);
+    }
+# endif
+#endif /* GC_OPERATOR_NEW_ARRAY */
+
+inline gc_cleanup::~gc_cleanup() {
+    GC_register_finalizer_ignore_self( GC_base(this), 0, 0, 0, 0 );
+}
+
+inline void GC_CALLBACK gc_cleanup::cleanup( void* obj, void* displ ) {
+    ((gc_cleanup*) ((char*) obj + (ptrdiff_t) displ))->~gc_cleanup();
+}
+
+inline gc_cleanup::gc_cleanup() {
+    GC_finalization_proc oldProc;
+    void* oldData;
+    void* base = GC_base( (void *) this );
+    if (0 != base)  {
+      // Don't call the debug version, since this is a real base address.
+      GC_register_finalizer_ignore_self( base, (GC_finalization_proc)cleanup,
+                                        (void*)((char*)this - (char*)base),
+                                        &oldProc, &oldData );
+      if (0 != oldProc) {
+        GC_register_finalizer_ignore_self( base, oldProc, oldData, 0, 0 );
+      }
+    }
+}
+
+#ifdef GC_NAMESPACE
+}
+#endif
+
+inline void* operator new( size_t size, GC_NS_QUALIFY(GCPlacement) gcp,
+                          GC_NS_QUALIFY(GCCleanUpFunc) cleanup,
+                          void* clientData )
+{
+    void* obj;
+
+    if (gcp == GC_NS_QUALIFY(UseGC)) {
+        obj = GC_MALLOC( size );
+        if (cleanup != 0)
+            GC_REGISTER_FINALIZER_IGNORE_SELF( obj, cleanup, clientData,
+                                              0, 0 );
+    } else if (gcp == GC_NS_QUALIFY(PointerFreeGC)) {
+        obj = GC_MALLOC_ATOMIC( size );
+    } else {
+        obj = GC_MALLOC_UNCOLLECTABLE( size );
+    };
+    return obj;
+}
+
+#ifdef GC_PLACEMENT_DELETE
+  inline void operator delete( void *p, GC_NS_QUALIFY(GCPlacement) /* gcp */,
+                              GC_NS_QUALIFY(GCCleanUpFunc) /* cleanup */,
+                              void* /* clientData */ )
+  {
+    GC_FREE(p);
+  }
+#endif /* GC_PLACEMENT_DELETE */
+
+#ifdef GC_OPERATOR_NEW_ARRAY
+  inline void* operator new[]( size_t size, GC_NS_QUALIFY(GCPlacement) gcp,
+                              GC_NS_QUALIFY(GCCleanUpFunc) cleanup,
+                              void* clientData )
+  {
+    return ::operator new( size, gcp, cleanup, clientData );
+  }
+#endif /* GC_OPERATOR_NEW_ARRAY */
+
+#if defined(__CYGWIN__)
+# include <new> // for delete throw()
+  inline void operator delete(void *p)
+  {
+    GC_FREE(p);
+  }
+#endif
+
+#endif /* GC_CPP_H */

+ 55 - 0
blitz.mod/bdwgc/include/gc_disclaim.h

@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2007-2011 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.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ *
+ */
+
+#ifndef GC_DISCLAIM_H
+#define GC_DISCLAIM_H
+
+#include "gc.h"
+
+/* This API is defined only if the library has been suitably compiled   */
+/* (i.e. with ENABLE_DISCLAIM defined).                                 */
+
+/* Prepare the object kind used by GC_finalized_malloc.  Call it from   */
+/* your initialization code or, at least, at some point before using    */
+/* finalized allocations.  The function is thread-safe.                 */
+GC_API void GC_CALL GC_init_finalized_malloc(void);
+
+/* Type of a disclaim call-back.                                        */
+typedef int (GC_CALLBACK * GC_disclaim_proc)(void * /*obj*/);
+
+/* Register "proc" to be called on each object of "kind" ready to be    */
+/* reclaimed.  If "proc" returns non-zero, the collector will not       */
+/* reclaim the object on this GC cycle.  Objects reachable from "proc"  */
+/* will be protected from collection if "mark_from_all" is non-zero,    */
+/* but at the expense that long chains of objects will take many cycles */
+/* to reclaim.                                                          */
+GC_API void GC_CALL GC_register_disclaim_proc(int /*kind*/,
+                                              GC_disclaim_proc /*proc*/,
+                                              int /*mark_from_all*/);
+
+/* The finalizer closure used by GC_finalized_malloc.                   */
+struct GC_finalizer_closure {
+    GC_finalization_proc proc;
+    void *cd;
+};
+
+/* Allocate "size" bytes which is finalized by "fc".  This uses a       */
+/* dedicated object kind with a disclaim procedure, and is more         */
+/* efficient than GC_register_finalizer and friends.                    */
+/* GC_init_finalized_malloc must be called before using this.           */
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+        GC_finalized_malloc(size_t /*size*/,
+                            const struct GC_finalizer_closure * /*fc*/);
+
+#endif

+ 110 - 0
blitz.mod/bdwgc/include/gc_gcj.h

@@ -0,0 +1,110 @@
+/*
+ * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
+ * Copyright (c) 1991-1995 by Xerox Corporation.  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.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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 file assumes the collector has been compiled with GC_GCJ_SUPPORT. */
+
+/*
+ * We allocate objects whose first word contains a pointer to a struct
+ * describing the object type.  This struct contains a garbage collector mark
+ * descriptor at offset MARK_DESCR_OFFSET.  Alternatively, the objects
+ * may be marked by the mark procedure passed to GC_init_gcj_malloc.
+ */
+
+#ifndef GC_GCJ_H
+#define GC_GCJ_H
+
+        /* Gcj keeps GC descriptor as second word of vtable.    This    */
+        /* probably needs to be adjusted for other clients.             */
+        /* We currently assume that this offset is such that:           */
+        /*      - all objects of this kind are large enough to have     */
+        /*        a value at that offset, and                           */
+        /*      - it is not zero.                                       */
+        /* These assumptions allow objects on the free list to be       */
+        /* marked normally.                                             */
+
+#ifndef GC_H
+# include "gc.h"
+#endif
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/* The following allocators signal an out of memory condition with      */
+/* return GC_oom_fn(bytes);                                             */
+
+/* The following function must be called before the gcj allocators      */
+/* can be invoked.                                                      */
+/* mp_index and mp are the index and mark_proc (see gc_mark.h)          */
+/* respectively for the allocated objects.  Mark_proc will be           */
+/* used to build the descriptor for objects allocated through the       */
+/* debugging interface.  The mark_proc will be invoked on all such      */
+/* objects with an "environment" value of 1.  The client may choose     */
+/* to use the same mark_proc for some of its generated mark descriptors.*/
+/* In that case, it should use a different "environment" value to       */
+/* detect the presence or absence of the debug header.                  */
+/* Mp is really of type mark_proc, as defined in gc_mark.h.  We don't   */
+/* want to include that here for namespace pollution reasons.           */
+/* Passing in mp_index here instead of having GC_init_gcj_malloc()      */
+/* internally call GC_new_proc() is quite ugly, but in typical usage    */
+/* scenarios a compiler also has to know about mp_index, so             */
+/* generating it dynamically is not acceptable.  Mp_index will          */
+/* typically be an integer < RESERVED_MARK_PROCS, so that it doesn't    */
+/* collide with GC_new_proc allocated indices.  If the application      */
+/* needs no other reserved indices, zero                                */
+/* (GC_GCJ_RESERVED_MARK_PROC_INDEX in gc_mark.h) is an obvious choice. */
+GC_API void GC_CALL GC_init_gcj_malloc(int /* mp_index */,
+                                void * /* really mark_proc */ /* mp */);
+
+/* Allocate an object, clear it, and store the pointer to the   */
+/* type structure (vtable in gcj).                              */
+/* This adds a byte at the end of the object if GC_malloc would.*/
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+        GC_gcj_malloc(size_t /* lb */,
+                      void * /* ptr_to_struct_containing_descr */);
+
+/* The debug versions allocate such that the specified mark_proc        */
+/* is always invoked.                                                   */
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+        GC_debug_gcj_malloc(size_t /* lb */,
+                            void * /* ptr_to_struct_containing_descr */,
+                            GC_EXTRA_PARAMS);
+
+/* Similar to GC_gcj_malloc, but assumes that a pointer to near the     */
+/* beginning of the resulting object is always maintained.              */
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+        GC_gcj_malloc_ignore_off_page(size_t /* lb */,
+                                void * /* ptr_to_struct_containing_descr */);
+
+/* The kind numbers of normal and debug gcj objects.            */
+/* Useful only for debug support, we hope.                      */
+GC_API int GC_gcj_kind;
+
+GC_API int GC_gcj_debug_kind;
+
+#ifdef GC_DEBUG
+# define GC_GCJ_MALLOC(s,d) GC_debug_gcj_malloc(s,d,GC_EXTRAS)
+# define GC_GCJ_MALLOC_IGNORE_OFF_PAGE(s,d) GC_debug_gcj_malloc(s,d,GC_EXTRAS)
+#else
+# define GC_GCJ_MALLOC(s,d) GC_gcj_malloc(s,d)
+# define GC_GCJ_MALLOC_IGNORE_OFF_PAGE(s,d) GC_gcj_malloc_ignore_off_page(s,d)
+#endif
+
+#ifdef __cplusplus
+  } /* end of extern "C" */
+#endif
+
+#endif /* GC_GCJ_H */

+ 146 - 0
blitz.mod/bdwgc/include/gc_inline.h

@@ -0,0 +1,146 @@
+/*
+ * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
+ * Copyright (c) 1991-1995 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 2005 Hewlett-Packard Development Company, L.P.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+#ifndef GC_INLINE_H
+#define GC_INLINE_H
+
+/* WARNING:                                                             */
+/* Note that for these routines, it is the clients responsibility to    */
+/* add the extra byte at the end to deal with one-past-the-end pointers.*/
+/* In the standard collector configuration, the collector assumes that  */
+/* such a byte has been added, and hence does not trace the last word   */
+/* in the resulting object.                                             */
+/* This is not an issue if the collector is compiled with               */
+/* DONT_ADD_BYTE_AT_END, or if GC_all_interior_pointers is not set.     */
+/* This interface is most useful for compilers that generate C.         */
+/* It is also used internally for thread-local allocation.              */
+/* Manual use is hereby discouraged.                                    */
+
+#include "gc.h"
+#include "gc_tiny_fl.h"
+
+#if __GNUC__ >= 3
+# define GC_EXPECT(expr, outcome) __builtin_expect(expr,outcome)
+  /* Equivalent to (expr), but predict that usually (expr)==outcome. */
+#else
+# define GC_EXPECT(expr, outcome) (expr)
+#endif /* __GNUC__ */
+
+#ifndef GC_ASSERT
+# define GC_ASSERT(expr) /* empty */
+#endif
+
+/* Store a pointer to a list of newly allocated objects of kind k and   */
+/* size lb in *result.  The caller must make sure that *result is       */
+/* traced even if objects are ptrfree.                                  */
+GC_API void GC_CALL GC_generic_malloc_many(size_t /* lb */, int /* k */,
+                                           void ** /* result */);
+
+/* The ultimately general inline allocation macro.  Allocate an object  */
+/* of size granules, putting the resulting pointer in result.  Tiny_fl  */
+/* is a "tiny" free list array, which will be used first, if the size   */
+/* is appropriate.  If granules is too large, we allocate with          */
+/* default_expr instead.  If we need to refill the free list, we use    */
+/* GC_generic_malloc_many with the indicated kind.                      */
+/* Tiny_fl should be an array of GC_TINY_FREELISTS void * pointers.     */
+/* If num_direct is nonzero, and the individual free list pointers      */
+/* are initialized to (void *)1, then we allocate numdirect granules    */
+/* directly using gmalloc before putting multiple objects into the      */
+/* tiny_fl entry.  If num_direct is zero, then the free lists may also  */
+/* be initialized to (void *)0.                                         */
+/* Note that we use the zeroth free list to hold objects 1 granule in   */
+/* size that are used to satisfy size 0 allocation requests.            */
+/* We rely on much of this hopefully getting optimized away in the      */
+/* num_direct = 0 case.                                                 */
+/* Particularly if granules is constant, this should generate a small   */
+/* amount of code.                                                      */
+# define GC_FAST_MALLOC_GRANS(result,granules,tiny_fl,num_direct,\
+                              kind,default_expr,init) \
+  do { \
+    if (GC_EXPECT((granules) >= GC_TINY_FREELISTS,0)) { \
+        result = (default_expr); \
+    } else { \
+        void **my_fl = (tiny_fl) + (granules); \
+        void *my_entry=*my_fl; \
+        void *next; \
+    \
+        while (GC_EXPECT((GC_word)my_entry \
+                        <= (num_direct) + GC_TINY_FREELISTS + 1, 0)) { \
+            /* Entry contains counter or NULL */ \
+            if ((GC_word)my_entry - 1 < (num_direct)) { \
+                /* Small counter value, not NULL */ \
+                *my_fl = (char *)my_entry + (granules) + 1; \
+                result = (default_expr); \
+                goto out; \
+            } else { \
+                /* Large counter or NULL */ \
+                GC_generic_malloc_many(((granules) == 0? GC_GRANULE_BYTES : \
+                                        GC_RAW_BYTES_FROM_INDEX(granules)), \
+                                       kind, my_fl); \
+                my_entry = *my_fl; \
+                if (my_entry == 0) { \
+                    result = (*GC_get_oom_fn())((granules)*GC_GRANULE_BYTES); \
+                    goto out; \
+                } \
+            } \
+        } \
+        next = *(void **)(my_entry); \
+        result = (void *)my_entry; \
+        *my_fl = next; \
+        init; \
+        PREFETCH_FOR_WRITE(next); \
+        GC_ASSERT(GC_size(result) >= (granules)*GC_GRANULE_BYTES); \
+        GC_ASSERT((kind) == PTRFREE || ((GC_word *)result)[1] == 0); \
+      out: ; \
+    } \
+  } while (0)
+
+# define GC_WORDS_TO_WHOLE_GRANULES(n) \
+        GC_WORDS_TO_GRANULES((n) + GC_GRANULE_WORDS - 1)
+
+/* Allocate n words (NOT BYTES).  X is made to point to the result.     */
+/* This should really only be used if GC_all_interior_pointers is       */
+/* not set, or DONT_ADD_BYTE_AT_END is set.  See above.                 */
+/* The semantics changed in version 7.0; we no longer lock, and         */
+/* the caller is responsible for supplying a cleared tiny_fl            */
+/* free list array.  For single-threaded applications, this may be      */
+/* a global array.                                                      */
+# define GC_MALLOC_WORDS(result,n,tiny_fl) \
+  do { \
+    size_t grans = GC_WORDS_TO_WHOLE_GRANULES(n); \
+    GC_FAST_MALLOC_GRANS(result, grans, tiny_fl, 0, \
+                         NORMAL, GC_malloc(grans*GC_GRANULE_BYTES), \
+                         *(void **)(result) = 0); \
+  } while (0)
+
+# define GC_MALLOC_ATOMIC_WORDS(result,n,tiny_fl) \
+  do { \
+    size_t grans = GC_WORDS_TO_WHOLE_GRANULES(n); \
+    GC_FAST_MALLOC_GRANS(result, grans, tiny_fl, 0, \
+                         PTRFREE, GC_malloc_atomic(grans*GC_GRANULE_BYTES), \
+                         (void)0 /* no initialization */); \
+  } while (0)
+
+/* And once more for two word initialized objects: */
+# define GC_CONS(result, first, second, tiny_fl) \
+  do { \
+    size_t grans = GC_WORDS_TO_WHOLE_GRANULES(2); \
+    GC_FAST_MALLOC_GRANS(result, grans, tiny_fl, 0, \
+                         NORMAL, GC_malloc(grans*GC_GRANULE_BYTES), \
+                         *(void **)(result) = (void *)(first)); \
+    ((void **)(result))[1] = (void *)(second); \
+  } while (0)
+
+#endif /* !GC_INLINE_H */

+ 268 - 0
blitz.mod/bdwgc/include/gc_mark.h

@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 2001 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.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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 contains interfaces to the GC marker that are likely to be useful to
+ * clients that provide detailed heap layout information to the collector.
+ * This interface should not be used by normal C or C++ clients.
+ * It will be useful to runtimes for other languages.
+ *
+ * This is an experts-only interface!  There are many ways to break the
+ * collector in subtle ways by using this functionality.
+ */
+#ifndef GC_MARK_H
+#define GC_MARK_H
+
+#ifndef GC_H
+# include "gc.h"
+#endif
+
+#ifdef __cplusplus
+  extern "C" {
+#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.        */
+/* Global variables describing mark stack are not necessarily valid.    */
+/* (This usually saves a few cycles by keeping things in registers.)    */
+/* Assumed to scan about GC_PROC_BYTES on average.  If it needs to do   */
+/* much more work than that, it should do it in smaller pieces by       */
+/* pushing itself back on the mark stack.                               */
+/* Note that it should always do some work (defined as marking some     */
+/* objects) before pushing more than one entry on the mark stack.       */
+/* This is required to ensure termination in the event of mark stack    */
+/* overflows.                                                           */
+/* This procedure is always called with at least one empty entry on the */
+/* mark stack.                                                          */
+/* Currently we require that mark procedures look for pointers in a     */
+/* subset of the places the conservative marker would.  It must be safe */
+/* to invoke the normal mark procedure instead.                         */
+/* WARNING: Such a mark procedure may be invoked on an unused object    */
+/* residing on a free list.  Such objects are cleared, except for a     */
+/* free list link field in the first word.  Thus mark procedures may    */
+/* not count on the presence of a type descriptor, and must handle this */
+/* case correctly somehow.                                              */
+#define GC_PROC_BYTES 100
+
+#ifdef GC_BUILD
+  struct GC_ms_entry;
+#else
+  struct GC_ms_entry { void *opaque; };
+#endif
+typedef struct GC_ms_entry * (*GC_mark_proc)(GC_word * /* addr */,
+                                struct GC_ms_entry * /* mark_stack_ptr */,
+                                struct GC_ms_entry * /* mark_stack_limit */,
+                                GC_word /* env */);
+
+#define GC_LOG_MAX_MARK_PROCS 6
+#define GC_MAX_MARK_PROCS (1 << GC_LOG_MAX_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 GC_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.                                          */
+#define GC_DS_TAG_BITS 2
+#define GC_DS_TAGS   ((1 << GC_DS_TAG_BITS) - 1)
+#define GC_DS_LENGTH 0  /* The entire word is a length in bytes that    */
+                        /* must be a multiple of 4.                     */
+#define GC_DS_BITMAP 1  /* 30 (62) bits are a bitmap describing pointer */
+                        /* fields.  The msb is 1 if the first word      */
+                        /* is a pointer.                                */
+                        /* (This unconventional ordering sometimes      */
+                        /* makes the marker slightly faster.)           */
+                        /* Zeroes indicate definite nonpointers.  Ones  */
+                        /* indicate possible pointers.                  */
+                        /* Only usable if pointers are word aligned.    */
+#define GC_DS_PROC   2
+                        /* The objects referenced by this object can be */
+                        /* pushed on the mark stack by invoking         */
+                        /* PROC(descr).  ENV(descr) is passed as the    */
+                        /* last argument.                               */
+#define GC_MAKE_PROC(proc_index, env) \
+            (((((env) << GC_LOG_MAX_MARK_PROCS) \
+               | (proc_index)) << GC_DS_TAG_BITS) | GC_DS_PROC)
+#define GC_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) - GC_INDIR_PER_OBJ_BIAS   */
+                        /* The latter alternative can be used if each   */
+                        /* object contains a type descriptor in the     */
+                        /* first word.                                  */
+                        /* Note that in the multi-threaded environments */
+                        /* per-object descriptors must be located in    */
+                        /* either the first two or last two words of    */
+                        /* the object, since only those are guaranteed  */
+                        /* to be cleared while the allocation lock is   */
+                        /* held.                                        */
+#define GC_INDIR_PER_OBJ_BIAS 0x10
+
+GC_API void * GC_least_plausible_heap_addr;
+GC_API void * GC_greatest_plausible_heap_addr;
+                        /* Bounds on the heap.  Guaranteed valid        */
+                        /* Likely to include future heap expansion.     */
+                        /* Hence usually includes not-yet-mapped        */
+                        /* memory.                                      */
+
+/* Handle nested references in a custom mark procedure.                 */
+/* Check if obj is a valid object. If so, ensure that it is marked.     */
+/* If it was not previously marked, push its contents onto the mark     */
+/* stack for future scanning.  The object will then be scanned using    */
+/* its mark descriptor.                                                 */
+/* Returns the new mark stack pointer.                                  */
+/* Handles mark stack overflows correctly.                              */
+/* Since this marks first, it makes progress even if there are mark     */
+/* stack overflows.                                                     */
+/* Src is the address of the pointer to obj, which is used only         */
+/* for back pointer-based heap debugging.                               */
+/* It is strongly recommended that most objects be handled without mark */
+/* procedures, e.g. with bitmap descriptors, and that mark procedures   */
+/* be reserved for exceptional cases.  That will ensure that            */
+/* performance of this call is not extremely performance critical.      */
+/* (Otherwise we would need to inline GC_mark_and_push completely,      */
+/* which would tie the client code to a fixed collector version.)       */
+/* Note that mark procedures should explicitly call FIXUP_POINTER()     */
+/* if required.                                                         */
+GC_API struct GC_ms_entry * GC_CALL GC_mark_and_push(void * /* obj */,
+                                struct GC_ms_entry * /* mark_stack_ptr */,
+                                struct GC_ms_entry * /* mark_stack_limit */,
+                                void ** /* src */);
+
+#define GC_MARK_AND_PUSH(obj, msp, lim, src) \
+          ((GC_word)(obj) >= (GC_word)GC_least_plausible_heap_addr && \
+           (GC_word)(obj) <= (GC_word)GC_greatest_plausible_heap_addr ? \
+           GC_mark_and_push(obj, msp, lim, src) : (msp))
+
+GC_API size_t GC_debug_header_size;
+       /* The size of the header added to objects allocated through    */
+       /* the GC_debug routines.                                       */
+       /* Defined as a variable so that client mark procedures don't   */
+       /* need to be recompiled for collector version changes.         */
+#define GC_USR_PTR_FROM_BASE(p) ((void *)((char *)(p) + GC_debug_header_size))
+
+/* And some routines to support creation of new "kinds", e.g. with      */
+/* custom mark procedures, by language runtimes.                        */
+/* The _inner versions assume the caller holds the allocation lock.     */
+
+/* Return a new free list array.        */
+GC_API void ** GC_CALL GC_new_free_list(void);
+GC_API void ** GC_CALL GC_new_free_list_inner(void);
+
+/* Return a new kind, as specified. */
+GC_API unsigned GC_CALL GC_new_kind(void ** /* free_list */,
+                            GC_word /* mark_descriptor_template */,
+                            int /* add_size_to_descriptor */,
+                            int /* clear_new_objects */) GC_ATTR_NONNULL(1);
+                /* The last two parameters must be zero or one. */
+GC_API unsigned GC_CALL GC_new_kind_inner(void ** /* free_list */,
+                            GC_word /* mark_descriptor_template */,
+                            int /* add_size_to_descriptor */,
+                            int /* clear_new_objects */) GC_ATTR_NONNULL(1);
+
+/* Return a new mark procedure identifier, suitable for use as  */
+/* the first argument in GC_MAKE_PROC.                          */
+GC_API unsigned GC_CALL GC_new_proc(GC_mark_proc);
+GC_API unsigned GC_CALL GC_new_proc_inner(GC_mark_proc);
+
+/* Allocate an object of a given kind.  By default, there are only      */
+/* a few kinds: composite (pointer-free), atomic, uncollectible, etc.   */
+/* We claim it is possible for clever client code that understands the  */
+/* GC internals to add more, e.g. to communicate object layout          */
+/* information to the collector.  Note that in the multi-threaded       */
+/* contexts, this is usually unsafe for kinds that have the descriptor  */
+/* in the object itself, since there is otherwise a window in which     */
+/* the descriptor is not correct.  Even in the single-threaded case,    */
+/* we need to be sure that cleared objects on a free list don't         */
+/* cause a GC crash if they are accidentally traced.                    */
+GC_API GC_ATTR_MALLOC void * GC_CALL GC_generic_malloc(size_t /* lb */,
+                                                       int /* k */);
+
+GC_API GC_ATTR_MALLOC void * GC_CALL GC_generic_malloc_ignore_off_page(
+                                        size_t /* lb */, int /* k */);
+                                /* As above, but pointers to past the   */
+                                /* first page of the resulting object   */
+                                /* are ignored.                         */
+
+typedef void (GC_CALLBACK * GC_describe_type_fn)(void * /* p */,
+                                                 char * /* out_buf */);
+                                /* A procedure which                    */
+                                /* produces a human-readable            */
+                                /* description of the "type" of object  */
+                                /* p into the buffer out_buf of length  */
+                                /* GC_TYPE_DESCR_LEN.  This is used by  */
+                                /* the debug support when printing      */
+                                /* objects.                             */
+                                /* These functions should be as robust  */
+                                /* as possible, though we do avoid      */
+                                /* invoking them on objects on the      */
+                                /* global free list.                    */
+#define GC_TYPE_DESCR_LEN 40
+
+GC_API void GC_CALL GC_register_describe_type_fn(int /* kind */,
+                                                 GC_describe_type_fn);
+                                /* Register a describe_type function    */
+                                /* to be used when printing objects     */
+                                /* of a particular kind.                */
+
+/* Clear some of the inaccessible part of the stack.  Returns its       */
+/* argument, so it can be used in a tail call position, hence clearing  */
+/* another frame.  Argument may be NULL.                                */
+GC_API void * GC_CALL GC_clear_stack(void *);
+
+/* Set and get the client notifier on collections.  The client function */
+/* is called at the start of every full GC (called with the allocation  */
+/* lock held).  May be 0.  This is a really tricky interface to use     */
+/* correctly.  Unless you really understand the collector internals,    */
+/* the callback should not, directly or indirectly, make any GC_ or     */
+/* potentially blocking calls.  In particular, it is not safe to        */
+/* allocate memory using the garbage collector from within the callback */
+/* function.  Both the setter and getter acquire the GC lock.           */
+typedef void (GC_CALLBACK * GC_start_callback_proc)(void);
+GC_API void GC_CALL GC_set_start_callback(GC_start_callback_proc);
+GC_API GC_start_callback_proc GC_CALL GC_get_start_callback(void);
+
+/* Slow/general mark bit manipulation.  The caller must hold the        */
+/* allocation lock.  GC_is_marked returns 1 (TRUE) or 0.                */
+GC_API int GC_CALL GC_is_marked(const void *) GC_ATTR_NONNULL(1);
+GC_API void GC_CALL GC_clear_mark_bit(const void *) GC_ATTR_NONNULL(1);
+GC_API void GC_CALL GC_set_mark_bit(const void *) GC_ATTR_NONNULL(1);
+
+/* Push everything in the given range onto the mark stack.              */
+/* (GC_push_conditional pushes either all or only dirty pages depending */
+/* on the third argument.)                                              */
+GC_API void GC_CALL GC_push_all(char * /* bottom */, char * /* top */);
+GC_API void GC_CALL GC_push_conditional(char * /* bottom */, char * /* top */,
+                                        int /* bool all */);
+
+/* Set and get the client push-other-roots procedure.  A client         */
+/* supplied procedure should also call the original procedure.          */
+/* Note that both the setter and getter require some external           */
+/* synchronization to avoid data race.                                  */
+typedef void (GC_CALLBACK * GC_push_other_roots_proc)(void);
+GC_API void GC_CALL GC_set_push_other_roots(GC_push_other_roots_proc);
+GC_API GC_push_other_roots_proc GC_CALL GC_get_push_other_roots(void);
+
+#ifdef __cplusplus
+  } /* end of extern "C" */
+#endif
+
+#endif /* GC_MARK_H */

+ 95 - 0
blitz.mod/bdwgc/include/gc_pthread_redirects.h

@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
+ * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
+ * Copyright (c) 2000-2010 by Hewlett-Packard Development Company.
+ * All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+/* Our pthread support normally needs to intercept a number of thread   */
+/* calls.  We arrange to do that here, if appropriate.                  */
+
+/* Included from gc.h only.  Included only if GC_PTHREADS.              */
+#if defined(GC_H) && defined(GC_PTHREADS)
+
+/* We need to intercept calls to many of the threads primitives, so     */
+/* that we can locate thread stacks and stop the world.                 */
+/* Note also that the collector cannot always see thread specific data. */
+/* Thread specific data should generally consist of pointers to         */
+/* uncollectible objects (allocated with GC_malloc_uncollectable,       */
+/* not the system malloc), which are deallocated using the destructor   */
+/* facility in thr_keycreate.  Alternatively, keep a redundant pointer  */
+/* to thread specific data on the thread stack.                         */
+
+#include <pthread.h>
+
+#ifndef GC_NO_DLOPEN
+# include <dlfcn.h>
+  GC_API void *GC_dlopen(const char * /* path */, int /* mode */);
+#endif /* !GC_NO_DLOPEN */
+
+#ifndef GC_NO_PTHREAD_SIGMASK
+# include <signal.h>
+  GC_API int GC_pthread_sigmask(int /* how */, const sigset_t *,
+                                sigset_t * /* oset */);
+#endif /* !GC_NO_PTHREAD_SIGMASK */
+
+#ifndef GC_PTHREAD_CREATE_CONST
+  /* This is used for pthread_create() only.    */
+# define GC_PTHREAD_CREATE_CONST const
+#endif
+
+GC_API int GC_pthread_create(pthread_t *,
+                             GC_PTHREAD_CREATE_CONST pthread_attr_t *,
+                             void *(*)(void *), void * /* arg */);
+GC_API int GC_pthread_join(pthread_t, void ** /* retval */);
+GC_API int GC_pthread_detach(pthread_t);
+
+#ifndef GC_NO_PTHREAD_CANCEL
+  GC_API int GC_pthread_cancel(pthread_t);
+#endif
+
+#if defined(GC_PTHREAD_EXIT_ATTRIBUTE) && !defined(GC_PTHREAD_EXIT_DECLARED)
+# define GC_PTHREAD_EXIT_DECLARED
+  GC_API void GC_pthread_exit(void *) GC_PTHREAD_EXIT_ATTRIBUTE;
+#endif
+
+#if !defined(GC_NO_THREAD_REDIRECTS) && !defined(GC_USE_LD_WRAP)
+  /* Unless the compiler supports #pragma extern_prefix, the Tru64      */
+  /* UNIX <pthread.h> redefines some POSIX thread functions to use      */
+  /* mangled names.  Anyway, it's safe to undef them before redefining. */
+# undef pthread_create
+# undef pthread_join
+# undef pthread_detach
+# define pthread_create GC_pthread_create
+# define pthread_join GC_pthread_join
+# define pthread_detach GC_pthread_detach
+
+# ifndef GC_NO_PTHREAD_SIGMASK
+#   undef pthread_sigmask
+#   define pthread_sigmask GC_pthread_sigmask
+# endif
+# ifndef GC_NO_DLOPEN
+#   undef dlopen
+#   define dlopen GC_dlopen
+# endif
+# ifndef GC_NO_PTHREAD_CANCEL
+#   undef pthread_cancel
+#   define pthread_cancel GC_pthread_cancel
+# endif
+# ifdef GC_PTHREAD_EXIT_ATTRIBUTE
+#   undef pthread_exit
+#   define pthread_exit GC_pthread_exit
+# endif
+#endif /* !GC_NO_THREAD_REDIRECTS */
+
+#endif /* GC_PTHREADS */

+ 90 - 0
blitz.mod/bdwgc/include/gc_tiny_fl.h

@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 1999-2005 Hewlett-Packard Development Company, L.P.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+#ifndef GC_TINY_FL_H
+#define GC_TINY_FL_H
+/*
+ * Constants and data structures for "tiny" free lists.
+ * These are used for thread-local allocation or in-lined allocators.
+ * Each global free list also essentially starts with one of these.
+ * However, global free lists are known to the GC.  "Tiny" free lists
+ * are basically private to the client.  Their contents are viewed as
+ * "in use" and marked accordingly by the core of the GC.
+ *
+ * Note that inlined code might know about the layout of these and the constants
+ * involved.  Thus any change here may invalidate clients, and such changes should
+ * be avoided.  Hence we keep this as simple as possible.
+ */
+
+/*
+ * We always set GC_GRANULE_BYTES to twice the length of a pointer.
+ * This means that all allocation requests are rounded up to the next
+ * multiple of 16 on 64-bit architectures or 8 on 32-bit architectures.
+ * This appears to be a reasonable compromise between fragmentation overhead
+ * and space usage for mark bits (usually mark bytes).
+ * On many 64-bit architectures some memory references require 16-byte
+ * alignment, making this necessary anyway.
+ * For a few 32-bit architecture (e.g. x86), we may also need 16-byte alignment
+ * for certain memory references.  But currently that does not seem to be the
+ * default for all conventional malloc implementations, so we ignore that
+ * problem.
+ * It would always be safe, and often useful, to be able to allocate very
+ * small objects with smaller alignment.  But that would cost us mark bit
+ * space, so we no longer do so.
+ */
+#ifndef GC_GRANULE_BYTES
+  /* GC_GRANULE_BYTES should not be overridden in any instances of the GC */
+  /* library that may be shared between applications, since it affects    */
+  /* the binary interface to the library.                                 */
+# if defined(__LP64__) || defined (_LP64) || defined(_WIN64) \
+        || defined(__s390x__) \
+        || (defined(__x86_64__) && !defined(__ILP32__)) \
+        || defined(__alpha__) || defined(__powerpc64__) \
+        || defined(__arch64__)
+#  define GC_GRANULE_BYTES 16
+#  define GC_GRANULE_WORDS 2
+# else
+#  define GC_GRANULE_BYTES 8
+#  define GC_GRANULE_WORDS 2
+# endif
+#endif /* !GC_GRANULE_BYTES */
+
+#if GC_GRANULE_WORDS == 2
+#  define GC_WORDS_TO_GRANULES(n) ((n)>>1)
+#else
+#  define GC_WORDS_TO_GRANULES(n) ((n)*sizeof(void *)/GC_GRANULE_BYTES)
+#endif
+
+/* A "tiny" free list header contains TINY_FREELISTS pointers to        */
+/* singly linked lists of objects of different sizes, the ith one       */
+/* containing objects i granules in size.  Note that there is a list    */
+/* of size zero objects.                                                */
+#ifndef GC_TINY_FREELISTS
+# if GC_GRANULE_BYTES == 16
+#   define GC_TINY_FREELISTS 25
+# else
+#   define GC_TINY_FREELISTS 33 /* Up to and including 256 bytes */
+# endif
+#endif /* !GC_TINY_FREELISTS */
+
+/* The ith free list corresponds to size i*GC_GRANULE_BYTES     */
+/* Internally to the collector, the index can be computed with  */
+/* ROUNDED_UP_GRANULES.  Externally, we don't know whether      */
+/* DONT_ADD_BYTE_AT_END is set, but the client should know.     */
+
+/* Convert a free list index to the actual size of objects      */
+/* on that list, including extra space we added.  Not an        */
+/* inverse of the above.                                        */
+#define GC_RAW_BYTES_FROM_INDEX(i) ((i) * GC_GRANULE_BYTES)
+
+#endif /* GC_TINY_FL_H */

+ 117 - 0
blitz.mod/bdwgc/include/gc_typed.h

@@ -0,0 +1,117 @@
+/*
+ * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
+ * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
+ * Copyright 1996 Silicon Graphics.  All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+/*
+ * Some simple primitives for allocation with explicit type information.
+ * Facilities for dynamic type inference may be added later.
+ * Should be used only for extremely performance critical applications,
+ * or if conservative collector leakage is otherwise a problem (unlikely).
+ * Note that this is implemented completely separately from the rest
+ * of the collector, and is not linked in unless referenced.
+ * This does not currently support GC_DEBUG in any interesting way.
+ */
+
+#ifndef GC_TYPED_H
+#define GC_TYPED_H
+
+#ifndef GC_H
+# include "gc.h"
+#endif
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+typedef GC_word * GC_bitmap;
+        /* The least significant bit of the first word is one if        */
+        /* the first word in the object may be a pointer.               */
+
+#define GC_WORDSZ (8 * sizeof(GC_word))
+#define GC_get_bit(bm, index) \
+            (((bm)[(index) / GC_WORDSZ] >> ((index) % GC_WORDSZ)) & 1)
+#define GC_set_bit(bm, index) \
+            ((bm)[(index) / GC_WORDSZ] |= (GC_word)1 << ((index) % GC_WORDSZ))
+#define GC_WORD_OFFSET(t, f) (offsetof(t,f) / sizeof(GC_word))
+#define GC_WORD_LEN(t) (sizeof(t) / sizeof(GC_word))
+#define GC_BITMAP_SIZE(t) ((GC_WORD_LEN(t) + GC_WORDSZ - 1) / GC_WORDSZ)
+
+typedef GC_word GC_descr;
+
+GC_API GC_descr GC_CALL GC_make_descriptor(const GC_word * /* GC_bitmap bm */,
+                                           size_t /* len */);
+                /* Return a type descriptor for the object whose layout */
+                /* is described by the argument.                        */
+                /* The least significant bit of the first word is one   */
+                /* if the first word in the object may be a pointer.    */
+                /* The second argument specifies the number of          */
+                /* meaningful bits in the bitmap.  The actual object    */
+                /* may be larger (but not smaller).  Any additional     */
+                /* words in the object are assumed not to contain       */
+                /* pointers.                                            */
+                /* Returns a conservative approximation in the          */
+                /* (unlikely) case of insufficient memory to build      */
+                /* the descriptor.  Calls to GC_make_descriptor         */
+                /* may consume some amount of a finite resource.  This  */
+                /* is intended to be called once per type, not once     */
+                /* per allocation.                                      */
+
+/* It is possible to generate a descriptor for a C type T with  */
+/* word aligned pointer fields f1, f2, ... as follows:                  */
+/*                                                                      */
+/* GC_descr T_descr;                                                    */
+/* GC_word T_bitmap[GC_BITMAP_SIZE(T)] = {0};                           */
+/* GC_set_bit(T_bitmap, GC_WORD_OFFSET(T,f1));                          */
+/* GC_set_bit(T_bitmap, GC_WORD_OFFSET(T,f2));                          */
+/* ...                                                                  */
+/* T_descr = GC_make_descriptor(T_bitmap, GC_WORD_LEN(T));              */
+
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+        GC_malloc_explicitly_typed(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_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+        GC_malloc_explicitly_typed_ignore_off_page(size_t /* size_in_bytes */,
+                                                   GC_descr /* d */);
+
+GC_API GC_ATTR_MALLOC void * GC_CALL
+        GC_calloc_explicitly_typed(size_t /* nelements */,
+                                   size_t /* element_size_in_bytes */,
+                                   GC_descr /* d */);
+        /* Allocate an array of nelements elements, each of the */
+        /* given size, and with the given descriptor.           */
+        /* The element size must be a multiple of the byte      */
+        /* 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_EXPLICITLY_TYPED(bytes, d) GC_MALLOC(bytes)
+# define GC_CALLOC_EXPLICITLY_TYPED(n, bytes, d) GC_MALLOC((n) * (bytes))
+#else
+# define GC_MALLOC_EXPLICITLY_TYPED(bytes, d) \
+                        GC_malloc_explicitly_typed(bytes, d)
+# define GC_CALLOC_EXPLICITLY_TYPED(n, bytes, d) \
+                        GC_calloc_explicitly_typed(n, bytes, d)
+#endif
+
+#ifdef __cplusplus
+  } /* matches extern "C" */
+#endif
+
+#endif /* GC_TYPED_H */

+ 47 - 0
blitz.mod/bdwgc/include/gc_version.h

@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
+ * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
+ * Copyright (c) 2000-2009 by Hewlett-Packard Development Company.
+ * All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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 should never be included directly; it is included only from gc.h. */
+#if defined(GC_H)
+
+/* The policy regarding version numbers: development code has odd       */
+/* "minor" number (and "micro" part is 0); when development is finished */
+/* and a release is prepared, "minor" number is incremented (keeping    */
+/* "micro" number still zero), whenever a defect is fixed a new release */
+/* is prepared incrementing "micro" part to odd value (the most stable  */
+/* release has the biggest "micro" number).                             */
+
+/* The version here should match that in configure/configure.ac */
+/* Eventually this one may become unnecessary.  For now we need */
+/* it to keep the old-style build process working.              */
+#define GC_TMP_VERSION_MAJOR 7
+#define GC_TMP_VERSION_MINOR 4
+#define GC_TMP_VERSION_MICRO 0 /* 7.4.0 */
+
+#ifdef GC_VERSION_MAJOR
+# if GC_TMP_VERSION_MAJOR != GC_VERSION_MAJOR \
+     || GC_TMP_VERSION_MINOR != GC_VERSION_MINOR \
+     || GC_TMP_VERSION_MICRO != GC_VERSION_MICRO
+#   error Inconsistent version info.  Check README.md, include/gc_version.h and configure.ac.
+# endif
+#else
+# define GC_VERSION_MAJOR GC_TMP_VERSION_MAJOR
+# define GC_VERSION_MINOR GC_TMP_VERSION_MINOR
+# define GC_VERSION_MICRO GC_TMP_VERSION_MICRO
+#endif /* !GC_VERSION_MAJOR */
+
+#endif

+ 54 - 0
blitz.mod/bdwgc/include/include.am

@@ -0,0 +1,54 @@
+#
+# THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+# OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+#
+# Permission is hereby granted to use or copy this program
+# for any purpose,  provided the above notices are retained on all copies.
+# 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.
+
+## Process this file with automake to produce part of Makefile.in.
+
+# installed headers
+#
+pkginclude_HEADERS += \
+        include/gc.h \
+        include/gc_allocator.h \
+        include/gc_backptr.h \
+        include/gc_config_macros.h \
+        include/gc_disclaim.h \
+        include/gc_gcj.h \
+        include/gc_inline.h \
+        include/gc_mark.h \
+        include/gc_pthread_redirects.h \
+        include/gc_tiny_fl.h \
+        include/gc_typed.h \
+        include/gc_version.h \
+        include/javaxfc.h \
+        include/leak_detector.h \
+        include/weakpointer.h
+
+# headers which are not installed
+#
+dist_noinst_HEADERS += \
+        include/cord.h \
+        include/cord_pos.h \
+        include/ec.h \
+        include/new_gc_alloc.h \
+        include/private/darwin_semaphore.h \
+        include/private/darwin_stop_world.h \
+        include/private/dbg_mlc.h \
+        include/private/gc_hdrs.h \
+        include/private/gc_locks.h \
+        include/private/gc_pmark.h \
+        include/private/gc_priv.h \
+        include/private/gcconfig.h \
+        include/private/pthread_stop_world.h \
+        include/private/pthread_support.h \
+        include/private/specific.h \
+        include/private/thread_local_alloc.h
+
+# unprefixed header
+include_HEADERS += \
+        include/extra/gc.h

+ 45 - 0
blitz.mod/bdwgc/include/javaxfc.h

@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
+ * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
+ * Copyright (c) 2000-2009 by Hewlett-Packard Development Company.
+ * All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+#ifndef GC_H
+# include "gc.h"
+#endif
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*
+ * Invoke all remaining finalizers that haven't yet been run.  (Since the
+ * notifier is not called, this should be called from a separate thread.)
+ * This function is needed for strict compliance with the Java standard,
+ * which can make the runtime guarantee that all finalizers are run.
+ * This is problematic for several reasons:
+ * 1) It means that finalizers, and all methods called by them,
+ *    must be prepared to deal with objects that have been finalized in
+ *    spite of the fact that they are still referenced by statically
+ *    allocated pointer variables.
+ * 1) It may mean that we get stuck in an infinite loop running
+ *    finalizers which create new finalizable objects, though that's
+ *    probably unlikely.
+ * Thus this is not recommended for general use.
+ */
+GC_API void GC_CALL GC_finalize_all(void);
+
+#ifdef __cplusplus
+  } /* end of extern "C" */
+#endif

+ 68 - 0
blitz.mod/bdwgc/include/leak_detector.h

@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2000-2011 by Hewlett-Packard Development Company.
+ * All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+#ifndef GC_LEAK_DETECTOR_H
+#define GC_LEAK_DETECTOR_H
+
+/* Include leak_detector.h (eg., via GCC --include directive)   */
+/* to turn BoehmGC into a Leak Detector.                        */
+
+#ifndef GC_DEBUG
+# define GC_DEBUG
+#endif
+#include "gc.h"
+
+#ifndef GC_DONT_INCLUDE_STDLIB
+  /* We ensure stdlib.h and string.h are included before        */
+  /* redirecting malloc() and the accompanying functions.       */
+# include <stdlib.h>
+# include <string.h>
+#endif
+
+#undef malloc
+#define malloc(n) GC_MALLOC(n)
+#undef calloc
+#define calloc(m,n) GC_MALLOC((m)*(n))
+#undef free
+#define free(p) GC_FREE(p)
+#undef realloc
+#define realloc(p,n) GC_REALLOC(p,n)
+
+#undef strdup
+#define strdup(s) GC_STRDUP(s)
+#undef strndup
+#define strndup(s,n) GC_STRNDUP(s,n)
+
+#ifdef GC_REQUIRE_WCSDUP
+  /* The collector should be built with GC_REQUIRE_WCSDUP       */
+  /* defined as well to redirect wcsdup().                      */
+# include <wchar.h>
+# undef wcsdup
+# define wcsdup(s) GC_WCSDUP(s)
+#endif
+
+#undef memalign
+#define memalign(a,n) GC_memalign(a,n)
+#undef posix_memalign
+#define posix_memalign(p,a,n) GC_posix_memalign(p,a,n)
+
+#ifndef CHECK_LEAKS
+# define CHECK_LEAKS() GC_gcollect()
+  /* Note 1: CHECK_LEAKS does not have GC prefix (preserved for */
+  /* backward compatibility).                                   */
+  /* Note 2: GC_gcollect() is also called automatically in the  */
+  /* leak-finding mode at program exit.                         */
+#endif
+
+#endif /* GC_LEAK_DETECTOR_H */

+ 480 - 0
blitz.mod/bdwgc/include/new_gc_alloc.h

@@ -0,0 +1,480 @@
+/*
+ * Copyright (c) 1996-1998 by Silicon Graphics.  All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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 a revision of gc_alloc.h for SGI STL versions > 3.0
+// Unlike earlier versions, it supplements the standard "alloc.h"
+// instead of replacing it.
+//
+// This is sloppy about variable names used in header files.
+// It also doesn't yet understand the new header file names or
+// namespaces.
+//
+// This assumes the collector has been compiled with -DATOMIC_UNCOLLECTABLE.
+// The user should also consider -DREDIRECT_MALLOC=GC_uncollectable_malloc,
+// to ensure that object allocated through malloc are traced.
+//
+// Some of this could be faster in the explicit deallocation case.
+// In particular, we spend too much time clearing objects on the
+// free lists.  That could be avoided.
+//
+// This uses template classes with static members, and hence does not work
+// with g++ 2.7.2 and earlier.
+//
+// Unlike its predecessor, this one simply defines
+//      gc_alloc
+//      single_client_gc_alloc
+//      traceable_alloc
+//      single_client_traceable_alloc
+//
+// It does not redefine alloc.  Nor does it change the default allocator,
+// though the user may wish to do so.  (The argument against changing
+// the default allocator is that it may introduce subtle link compatibility
+// problems.  The argument for changing it is that the usual default
+// allocator is usually a very bad choice for a garbage collected environment.)
+//
+
+#ifndef GC_ALLOC_H
+
+#include "gc.h"
+
+#if (__GNUC__ < 3)
+# include <stack>  // A more portable way to get stl_alloc.h .
+#else
+# include <bits/stl_alloc.h>
+# ifndef __STL_BEGIN_NAMESPACE
+# define __STL_BEGIN_NAMESPACE namespace std {
+# define __STL_END_NAMESPACE };
+# endif
+#ifndef __STL_USE_STD_ALLOCATORS
+#define __STL_USE_STD_ALLOCATORS
+#endif
+#endif
+
+/* A hack to deal with gcc 3.1.  If you are using gcc3.1 and later,     */
+/* you should probably really use gc_allocator.h instead.               */
+#if defined (__GNUC__) && \
+    (__GNUC__ > 3 || (__GNUC__ == 3 && (__GNUC_MINOR__ >= 1)))
+# define simple_alloc __simple_alloc
+#endif
+
+#define GC_ALLOC_H
+
+#include <stddef.h>
+#include <string.h>
+
+// The following need to match collector data structures.
+// We can't include gc_priv.h, since that pulls in way too much stuff.
+// This should eventually be factored out into another include file.
+
+extern "C" {
+    GC_API void ** const GC_objfreelist_ptr;
+    GC_API void ** const GC_aobjfreelist_ptr;
+    GC_API void ** const GC_uobjfreelist_ptr;
+    GC_API void ** const GC_auobjfreelist_ptr;
+
+    GC_API void GC_CALL GC_incr_bytes_allocd(size_t bytes);
+    GC_API void GC_CALL GC_incr_bytes_freed(size_t bytes);
+
+    GC_API char * GC_CALL GC_generic_malloc_words_small(size_t word, int kind);
+                /* FIXME: Doesn't exist anymore.        */
+}
+
+// Object kinds; must match PTRFREE, NORMAL, UNCOLLECTABLE, and
+// AUNCOLLECTABLE in gc_priv.h.
+
+enum { GC_PTRFREE = 0, GC_NORMAL = 1, GC_UNCOLLECTABLE = 2,
+       GC_AUNCOLLECTABLE = 3 };
+
+enum { GC_max_fast_bytes = 255 };
+
+enum { GC_bytes_per_word = sizeof(char *) };
+
+enum { GC_byte_alignment = 8 };
+
+enum { GC_word_alignment = GC_byte_alignment/GC_bytes_per_word };
+
+inline void * &GC_obj_link(void * p)
+{   return *reinterpret_cast<void **>(p);  }
+
+// Compute a number of words >= n+1 bytes.
+// The +1 allows for pointers one past the end.
+inline size_t GC_round_up(size_t n)
+{
+    return ((n + GC_byte_alignment)/GC_byte_alignment)*GC_word_alignment;
+}
+
+// The same but don't allow for extra byte.
+inline size_t GC_round_up_uncollectable(size_t n)
+{
+    return ((n + GC_byte_alignment - 1)/GC_byte_alignment)*GC_word_alignment;
+}
+
+template <int dummy>
+class GC_aux_template {
+public:
+  // File local count of allocated words.  Occasionally this is
+  // added into the global count.  A separate count is necessary since the
+  // real one must be updated with a procedure call.
+  static size_t GC_bytes_recently_allocd;
+
+  // Same for uncollectible memory.  Not yet reflected in either
+  // GC_bytes_recently_allocd or GC_non_gc_bytes.
+  static size_t GC_uncollectable_bytes_recently_allocd;
+
+  // Similar counter for explicitly deallocated memory.
+  static size_t GC_bytes_recently_freed;
+
+  // Again for uncollectible memory.
+  static size_t GC_uncollectable_bytes_recently_freed;
+
+  static void * GC_out_of_line_malloc(size_t nwords, int kind);
+};
+
+template <int dummy>
+size_t GC_aux_template<dummy>::GC_bytes_recently_allocd = 0;
+
+template <int dummy>
+size_t GC_aux_template<dummy>::GC_uncollectable_bytes_recently_allocd = 0;
+
+template <int dummy>
+size_t GC_aux_template<dummy>::GC_bytes_recently_freed = 0;
+
+template <int dummy>
+size_t GC_aux_template<dummy>::GC_uncollectable_bytes_recently_freed = 0;
+
+template <int dummy>
+void * GC_aux_template<dummy>::GC_out_of_line_malloc(size_t nwords, int kind)
+{
+    GC_bytes_recently_allocd += GC_uncollectable_bytes_recently_allocd;
+    GC_non_gc_bytes +=
+                GC_uncollectable_bytes_recently_allocd;
+    GC_uncollectable_bytes_recently_allocd = 0;
+
+    GC_bytes_recently_freed += GC_uncollectable_bytes_recently_freed;
+    GC_non_gc_bytes -= GC_uncollectable_bytes_recently_freed;
+    GC_uncollectable_bytes_recently_freed = 0;
+
+    GC_incr_bytes_allocd(GC_bytes_recently_allocd);
+    GC_bytes_recently_allocd = 0;
+
+    GC_incr_bytes_freed(GC_bytes_recently_freed);
+    GC_bytes_recently_freed = 0;
+
+    return GC_generic_malloc_words_small(nwords, kind);
+}
+
+typedef GC_aux_template<0> GC_aux;
+
+// A fast, single-threaded, garbage-collected allocator
+// We assume the first word will be immediately overwritten.
+// In this version, deallocation is not a no-op, and explicit
+// deallocation is likely to help performance.
+template <int dummy>
+class single_client_gc_alloc_template {
+    public:
+        static void * allocate(size_t n)
+        {
+            size_t nwords = GC_round_up(n);
+            void ** flh;
+            void * op;
+
+            if (n > GC_max_fast_bytes) return GC_malloc(n);
+            flh = GC_objfreelist_ptr + nwords;
+            if (0 == (op = *flh)) {
+                return GC_aux::GC_out_of_line_malloc(nwords, GC_NORMAL);
+            }
+            *flh = GC_obj_link(op);
+            GC_aux::GC_bytes_recently_allocd += nwords * GC_bytes_per_word;
+            return op;
+        }
+        static void * ptr_free_allocate(size_t n)
+        {
+            size_t nwords = GC_round_up(n);
+            void ** flh;
+            void * op;
+
+            if (n > GC_max_fast_bytes) return GC_malloc_atomic(n);
+            flh = GC_aobjfreelist_ptr + nwords;
+            if (0 == (op = *flh)) {
+                return GC_aux::GC_out_of_line_malloc(nwords, GC_PTRFREE);
+            }
+            *flh = GC_obj_link(op);
+            GC_aux::GC_bytes_recently_allocd += nwords * GC_bytes_per_word;
+            return op;
+        }
+        static void deallocate(void *p, size_t n)
+        {
+            size_t nwords = GC_round_up(n);
+            void ** flh;
+
+            if (n > GC_max_fast_bytes)  {
+                GC_free(p);
+            } else {
+                flh = GC_objfreelist_ptr + nwords;
+                GC_obj_link(p) = *flh;
+                memset(reinterpret_cast<char *>(p) + GC_bytes_per_word, 0,
+                       GC_bytes_per_word * (nwords - 1));
+                *flh = p;
+                GC_aux::GC_bytes_recently_freed += nwords * GC_bytes_per_word;
+            }
+        }
+        static void ptr_free_deallocate(void *p, size_t n)
+        {
+            size_t nwords = GC_round_up(n);
+            void ** flh;
+
+            if (n > GC_max_fast_bytes) {
+                GC_free(p);
+            } else {
+                flh = GC_aobjfreelist_ptr + nwords;
+                GC_obj_link(p) = *flh;
+                *flh = p;
+                GC_aux::GC_bytes_recently_freed += nwords * GC_bytes_per_word;
+            }
+        }
+};
+
+typedef single_client_gc_alloc_template<0> single_client_gc_alloc;
+
+// Once more, for uncollectible objects.
+template <int dummy>
+class single_client_traceable_alloc_template {
+    public:
+        static void * allocate(size_t n)
+        {
+            size_t nwords = GC_round_up_uncollectable(n);
+            void ** flh;
+            void * op;
+
+            if (n > GC_max_fast_bytes) return GC_malloc_uncollectable(n);
+            flh = GC_uobjfreelist_ptr + nwords;
+            if (0 == (op = *flh)) {
+                return GC_aux::GC_out_of_line_malloc(nwords, GC_UNCOLLECTABLE);
+            }
+            *flh = GC_obj_link(op);
+            GC_aux::GC_uncollectable_bytes_recently_allocd +=
+                                        nwords * GC_bytes_per_word;
+            return op;
+        }
+        static void * ptr_free_allocate(size_t n)
+        {
+            size_t nwords = GC_round_up_uncollectable(n);
+            void ** flh;
+            void * op;
+
+            if (n > GC_max_fast_bytes) return GC_malloc_atomic_uncollectable(n);
+            flh = GC_auobjfreelist_ptr + nwords;
+            if (0 == (op = *flh)) {
+                return GC_aux::GC_out_of_line_malloc(nwords, GC_AUNCOLLECTABLE);
+            }
+            *flh = GC_obj_link(op);
+            GC_aux::GC_uncollectable_bytes_recently_allocd +=
+                                        nwords * GC_bytes_per_word;
+            return op;
+        }
+        static void deallocate(void *p, size_t n)
+        {
+            size_t nwords = GC_round_up_uncollectable(n);
+            void ** flh;
+
+            if (n > GC_max_fast_bytes)  {
+                GC_free(p);
+            } else {
+                flh = GC_uobjfreelist_ptr + nwords;
+                GC_obj_link(p) = *flh;
+                *flh = p;
+                GC_aux::GC_uncollectable_bytes_recently_freed +=
+                                nwords * GC_bytes_per_word;
+            }
+        }
+        static void ptr_free_deallocate(void *p, size_t n)
+        {
+            size_t nwords = GC_round_up_uncollectable(n);
+            void ** flh;
+
+            if (n > GC_max_fast_bytes) {
+                GC_free(p);
+            } else {
+                flh = GC_auobjfreelist_ptr + nwords;
+                GC_obj_link(p) = *flh;
+                *flh = p;
+                GC_aux::GC_uncollectable_bytes_recently_freed +=
+                                nwords * GC_bytes_per_word;
+            }
+        }
+};
+
+typedef single_client_traceable_alloc_template<0> single_client_traceable_alloc;
+
+template < int dummy >
+class gc_alloc_template {
+    public:
+        static void * allocate(size_t n) { return GC_malloc(n); }
+        static void * ptr_free_allocate(size_t n)
+                { return GC_malloc_atomic(n); }
+        static void deallocate(void *, size_t) { }
+        static void ptr_free_deallocate(void *, size_t) { }
+};
+
+typedef gc_alloc_template < 0 > gc_alloc;
+
+template < int dummy >
+class traceable_alloc_template {
+    public:
+        static void * allocate(size_t n) { return GC_malloc_uncollectable(n); }
+        static void * ptr_free_allocate(size_t n)
+                { return GC_malloc_atomic_uncollectable(n); }
+        static void deallocate(void *p, size_t) { GC_free(p); }
+        static void ptr_free_deallocate(void *p, size_t) { GC_free(p); }
+};
+
+typedef traceable_alloc_template < 0 > traceable_alloc;
+
+// We want to specialize simple_alloc so that it does the right thing
+// for all pointer-free types.  At the moment there is no portable way to
+// even approximate that.  The following approximation should work for
+// SGI compilers, and recent versions of g++.
+
+// GC_SPECIALIZE() is used internally.
+#define GC_SPECIALIZE(T,alloc) \
+  class simple_alloc<T, alloc> { \
+  public: \
+    static T *allocate(size_t n) \
+        { return 0 == n? 0 : \
+            reinterpret_cast<T*>(alloc::ptr_free_allocate(n * sizeof(T))); } \
+    static T *allocate(void) \
+        { return reinterpret_cast<T*>(alloc::ptr_free_allocate(sizeof(T))); } \
+    static void deallocate(T *p, size_t n) \
+        { if (0 != n) alloc::ptr_free_deallocate(p, n * sizeof(T)); } \
+    static void deallocate(T *p) \
+        { alloc::ptr_free_deallocate(p, sizeof(T)); } \
+  };
+
+__STL_BEGIN_NAMESPACE
+
+GC_SPECIALIZE(char, gc_alloc)
+GC_SPECIALIZE(int, gc_alloc)
+GC_SPECIALIZE(unsigned, gc_alloc)
+GC_SPECIALIZE(float, gc_alloc)
+GC_SPECIALIZE(double, gc_alloc)
+
+GC_SPECIALIZE(char, traceable_alloc)
+GC_SPECIALIZE(int, traceable_alloc)
+GC_SPECIALIZE(unsigned, traceable_alloc)
+GC_SPECIALIZE(float, traceable_alloc)
+GC_SPECIALIZE(double, traceable_alloc)
+
+GC_SPECIALIZE(char, single_client_gc_alloc)
+GC_SPECIALIZE(int, single_client_gc_alloc)
+GC_SPECIALIZE(unsigned, single_client_gc_alloc)
+GC_SPECIALIZE(float, single_client_gc_alloc)
+GC_SPECIALIZE(double, single_client_gc_alloc)
+
+GC_SPECIALIZE(char, single_client_traceable_alloc)
+GC_SPECIALIZE(int, single_client_traceable_alloc)
+GC_SPECIALIZE(unsigned, single_client_traceable_alloc)
+GC_SPECIALIZE(float, single_client_traceable_alloc)
+GC_SPECIALIZE(double, single_client_traceable_alloc)
+
+__STL_END_NAMESPACE
+
+#ifdef __STL_USE_STD_ALLOCATORS
+
+__STL_BEGIN_NAMESPACE
+
+template <class _Tp>
+struct _Alloc_traits<_Tp, gc_alloc >
+{
+  static const bool _S_instanceless = true;
+  typedef simple_alloc<_Tp, gc_alloc > _Alloc_type;
+  typedef __allocator<_Tp, gc_alloc > allocator_type;
+};
+
+inline bool operator==(const gc_alloc&,
+                       const gc_alloc&)
+{
+  return true;
+}
+
+inline bool operator!=(const gc_alloc&,
+                       const gc_alloc&)
+{
+  return false;
+}
+
+template <class _Tp>
+struct _Alloc_traits<_Tp, single_client_gc_alloc >
+{
+  static const bool _S_instanceless = true;
+  typedef simple_alloc<_Tp, single_client_gc_alloc > _Alloc_type;
+  typedef __allocator<_Tp, single_client_gc_alloc > allocator_type;
+};
+
+inline bool operator==(const single_client_gc_alloc&,
+                       const single_client_gc_alloc&)
+{
+  return true;
+}
+
+inline bool operator!=(const single_client_gc_alloc&,
+                       const single_client_gc_alloc&)
+{
+  return false;
+}
+
+template <class _Tp>
+struct _Alloc_traits<_Tp, traceable_alloc >
+{
+  static const bool _S_instanceless = true;
+  typedef simple_alloc<_Tp, traceable_alloc > _Alloc_type;
+  typedef __allocator<_Tp, traceable_alloc > allocator_type;
+};
+
+inline bool operator==(const traceable_alloc&,
+                       const traceable_alloc&)
+{
+  return true;
+}
+
+inline bool operator!=(const traceable_alloc&,
+                       const traceable_alloc&)
+{
+  return false;
+}
+
+template <class _Tp>
+struct _Alloc_traits<_Tp, single_client_traceable_alloc >
+{
+  static const bool _S_instanceless = true;
+  typedef simple_alloc<_Tp, single_client_traceable_alloc > _Alloc_type;
+  typedef __allocator<_Tp, single_client_traceable_alloc > allocator_type;
+};
+
+inline bool operator==(const single_client_traceable_alloc&,
+                       const single_client_traceable_alloc&)
+{
+  return true;
+}
+
+inline bool operator!=(const single_client_traceable_alloc&,
+                       const single_client_traceable_alloc&)
+{
+  return false;
+}
+
+__STL_END_NAMESPACE
+
+#endif /* __STL_USE_STD_ALLOCATORS */
+
+#endif /* GC_ALLOC_H */

+ 83 - 0
blitz.mod/bdwgc/include/private/darwin_semaphore.h

@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
+ * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
+ * Copyright (c) 2000-2009 by Hewlett-Packard Development Company.
+ * All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+#ifndef GC_DARWIN_SEMAPHORE_H
+#define GC_DARWIN_SEMAPHORE_H
+
+#if !defined(GC_DARWIN_THREADS)
+# error darwin_semaphore.h included with GC_DARWIN_THREADS not defined
+#endif
+
+/* This is a very simple semaphore implementation for Darwin.  It is    */
+/* implemented in terms of pthread calls so it is not async signal      */
+/* safe.  But this is not a problem because signals are not used to     */
+/* suspend threads on Darwin.                                           */
+
+typedef struct {
+    pthread_mutex_t mutex;
+    pthread_cond_t cond;
+    int value;
+} sem_t;
+
+GC_INLINE int sem_init(sem_t *sem, int pshared, int value) {
+    int ret;
+    if(pshared)
+        ABORT("sem_init with pshared set");
+    sem->value = value;
+
+    ret = pthread_mutex_init(&sem->mutex,NULL);
+    if(ret < 0) return -1;
+    ret = pthread_cond_init(&sem->cond,NULL);
+    if(ret < 0) return -1;
+    return 0;
+}
+
+GC_INLINE int sem_post(sem_t *sem) {
+    if(pthread_mutex_lock(&sem->mutex) < 0)
+        return -1;
+    sem->value++;
+    if(pthread_cond_signal(&sem->cond) < 0) {
+        pthread_mutex_unlock(&sem->mutex);
+        return -1;
+    }
+    if(pthread_mutex_unlock(&sem->mutex) < 0)
+        return -1;
+    return 0;
+}
+
+GC_INLINE int sem_wait(sem_t *sem) {
+    if(pthread_mutex_lock(&sem->mutex) < 0)
+        return -1;
+    while(sem->value == 0) {
+        pthread_cond_wait(&sem->cond,&sem->mutex);
+    }
+    sem->value--;
+    if(pthread_mutex_unlock(&sem->mutex) < 0)
+        return -1;
+    return 0;
+}
+
+GC_INLINE int sem_destroy(sem_t *sem) {
+    int ret;
+    ret = pthread_cond_destroy(&sem->cond);
+    if(ret < 0) return -1;
+    ret = pthread_mutex_destroy(&sem->mutex);
+    if(ret < 0) return -1;
+    return 0;
+}
+
+#endif

+ 46 - 0
blitz.mod/bdwgc/include/private/darwin_stop_world.h

@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
+ * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
+ * Copyright (c) 2000-2009 by Hewlett-Packard Development Company.
+ * All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+#ifndef GC_DARWIN_STOP_WORLD_H
+#define GC_DARWIN_STOP_WORLD_H
+
+#if !defined(GC_DARWIN_THREADS)
+# error darwin_stop_world.h included without GC_DARWIN_THREADS defined
+#endif
+
+#include <mach/mach.h>
+#include <mach/thread_act.h>
+
+struct thread_stop_info {
+  mach_port_t mach_thread;
+  ptr_t stack_ptr; /* Valid only when thread is in a "blocked" state.   */
+};
+
+#ifndef DARWIN_DONT_PARSE_STACK
+  GC_INNER ptr_t GC_FindTopOfStack(unsigned long);
+#endif
+
+#ifdef MPROTECT_VDB
+  GC_INNER void GC_mprotect_stop(void);
+  GC_INNER void GC_mprotect_resume(void);
+#endif
+
+#if defined(PARALLEL_MARK) && !defined(GC_NO_THREADS_DISCOVERY)
+  GC_INNER GC_bool GC_is_mach_marker(thread_act_t);
+#endif
+
+#endif

+ 166 - 0
blitz.mod/bdwgc/include/private/dbg_mlc.h

@@ -0,0 +1,166 @@
+/*
+ * 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.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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 mostly an internal header file.  Typical clients should
+ * not use it.  Clients that define their own object kinds with
+ * debugging allocators will probably want to include this, however.
+ * No attempt is made to keep the namespace clean.  This should not be
+ * included from header files that are frequently included by clients.
+ */
+
+#ifndef _DBG_MLC_H
+#define _DBG_MLC_H
+
+#include "gc_priv.h"
+#ifdef KEEP_BACK_PTRS
+# include "gc_backptr.h"
+#endif
+
+#if CPP_WORDSZ == 32
+# define START_FLAG (word)0xfedcedcb
+# define END_FLAG (word)0xbcdecdef
+#else
+# define START_FLAG GC_WORD_C(0xFEDCEDCBfedcedcb)
+# define END_FLAG GC_WORD_C(0xBCDECDEFbcdecdef)
+#endif
+        /* Stored both one past the end of user object, and one before  */
+        /* the end of the object as seen by the allocator.              */
+
+#if defined(KEEP_BACK_PTRS) || defined(PRINT_BLACK_LIST) \
+    || defined(MAKE_BACK_GRAPH)
+  /* Pointer "source"s that aren't real locations.      */
+  /* Used in oh_back_ptr fields and as "source"         */
+  /* argument to some marking functions.                */
+# define NOT_MARKED (ptr_t)0
+# define MARKED_FOR_FINALIZATION ((ptr_t)(word)2)
+                /* Object was marked because it is finalizable. */
+# define MARKED_FROM_REGISTER ((ptr_t)(word)4)
+                /* Object was marked from a register.  Hence the        */
+                /* source of the reference doesn't have an address.     */
+#endif /* KEEP_BACK_PTRS || PRINT_BLACK_LIST */
+
+/* Object header */
+typedef struct {
+# if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH)
+    /* We potentially keep two different kinds of back          */
+    /* pointers.  KEEP_BACK_PTRS stores a single back           */
+    /* pointer in each reachable object to allow reporting      */
+    /* of why an object was retained.  MAKE_BACK_GRAPH          */
+    /* builds a graph containing the inverse of all             */
+    /* "points-to" edges including those involving              */
+    /* objects that have just become unreachable. This          */
+    /* allows detection of growing chains of unreachable        */
+    /* objects.  It may be possible to eventually combine       */
+    /* both, but for now we keep them separate.  Both           */
+    /* kinds of back pointers are hidden using the              */
+    /* following macros.  In both cases, the plain version      */
+    /* is constrained to have an least significant bit of 1,    */
+    /* to allow it to be distinguished from a free list         */
+    /* link.  This means the plain version must have an         */
+    /* lsb of 0.                                                */
+    /* Note that blocks dropped by black-listing will           */
+    /* also have the lsb clear once debugging has               */
+    /* started.                                                 */
+    /* We're careful never to overwrite a value with lsb 0.     */
+#   if ALIGNMENT == 1
+      /* Fudge back pointer to be even. */
+#     define HIDE_BACK_PTR(p) GC_HIDE_POINTER(~1 & (GC_word)(p))
+#   else
+#     define HIDE_BACK_PTR(p) GC_HIDE_POINTER(p)
+#   endif
+#   ifdef KEEP_BACK_PTRS
+      GC_hidden_pointer oh_back_ptr;
+#   endif
+#   ifdef MAKE_BACK_GRAPH
+      GC_hidden_pointer oh_bg_ptr;
+#   endif
+#   if defined(KEEP_BACK_PTRS) != defined(MAKE_BACK_GRAPH)
+      /* Keep double-pointer-sized alignment.   */
+      word oh_dummy;
+#   endif
+# endif
+  const char * oh_string;       /* object descriptor string     */
+  word oh_int;                  /* object descriptor integers   */
+# ifdef NEED_CALLINFO
+    struct callinfo oh_ci[NFRAMES];
+# endif
+# ifndef SHORT_DBG_HDRS
+    word oh_sz;                 /* Original malloc arg.         */
+    word oh_sf;                 /* start flag */
+# endif /* SHORT_DBG_HDRS */
+} oh;
+/* The size of the above structure is assumed not to de-align things,   */
+/* and to be a multiple of the word length.                             */
+
+#ifdef SHORT_DBG_HDRS
+# define DEBUG_BYTES (sizeof (oh))
+# define UNCOLLECTABLE_DEBUG_BYTES DEBUG_BYTES
+#else
+  /* Add space for END_FLAG, but use any extra space that was already   */
+  /* added to catch off-the-end pointers.                               */
+  /* For uncollectible objects, the extra byte is not added.            */
+# define UNCOLLECTABLE_DEBUG_BYTES (sizeof (oh) + sizeof (word))
+# define DEBUG_BYTES (UNCOLLECTABLE_DEBUG_BYTES - EXTRA_BYTES)
+#endif
+
+/* Round bytes to words without adding extra byte at end.       */
+#define SIMPLE_ROUNDED_UP_WORDS(n) BYTES_TO_WORDS((n) + WORDS_TO_BYTES(1) - 1)
+
+/* ADD_CALL_CHAIN stores a (partial) call chain into an object  */
+/* header.  It may be called with or without the allocation     */
+/* lock.                                                        */
+/* PRINT_CALL_CHAIN prints the call chain stored in an object   */
+/* to stderr.  It requires that we do not hold the lock.        */
+#if defined(SAVE_CALL_CHAIN)
+  struct callinfo;
+  GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]);
+  GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]);
+# 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)
+#elif defined(GC_ADD_CALLER)
+  struct callinfo;
+  GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]);
+# 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
+
+#ifdef GC_ADD_CALLER
+# define OPT_RA ra,
+#else
+# define OPT_RA
+#endif
+
+/* Check whether object with base pointer p has debugging info  */
+/* p is assumed to point to a legitimate object in our part     */
+/* of the heap.                                                 */
+#ifdef SHORT_DBG_HDRS
+# define GC_has_other_debug_info(p) 1
+#else
+  GC_INNER int GC_has_other_debug_info(ptr_t p);
+#endif
+
+#if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH)
+# define GC_HAS_DEBUG_INFO(p) \
+        ((*((word *)p) & 1) && GC_has_other_debug_info(p) > 0)
+#else
+# define GC_HAS_DEBUG_INFO(p) (GC_has_other_debug_info(p) > 0)
+#endif
+
+#endif /* _DBG_MLC_H */

+ 212 - 0
blitz.mod/bdwgc/include/private/gc_hdrs.h

@@ -0,0 +1,212 @@
+/*
+ * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
+ * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+#ifndef GC_HEADERS_H
+#define GC_HEADERS_H
+
+typedef struct hblkhdr hdr;
+
+#if CPP_WORDSZ != 32 && CPP_WORDSZ < 36
+        --> Get a real machine.
+#endif
+
+/*
+ * 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 take advantage of a header lookup
+ * cache.  This is a locally declared direct mapped cache, used inside
+ * the marker.  The HC_GET_HDR macro uses and maintains this
+ * cache.  Assuming we get reasonable hit rates, this shaves a few
+ * memory references from each pointer validation.
+ */
+
+#if CPP_WORDSZ > 32
+# define HASH_TL
+#endif
+
+/* Define appropriate out-degrees for each of the two tree levels       */
+#if defined(LARGE_CONFIG) || !defined(SMALL_CONFIG)
+# define LOG_BOTTOM_SZ 10
+#else
+# define LOG_BOTTOM_SZ 11
+        /* Keep top index size reasonable with smaller blocks.  */
+#endif
+#define BOTTOM_SZ (1 << LOG_BOTTOM_SZ)
+
+#ifndef HASH_TL
+# define LOG_TOP_SZ (WORDSZ - LOG_BOTTOM_SZ - LOG_HBLKSIZE)
+#else
+# define LOG_TOP_SZ 11
+#endif
+#define TOP_SZ (1 << LOG_TOP_SZ)
+
+/* #define COUNT_HDR_CACHE_HITS  */
+
+#ifdef COUNT_HDR_CACHE_HITS
+  extern word GC_hdr_cache_hits; /* used for debugging/profiling */
+  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)
+
+#ifdef PRINT_BLACK_LIST
+  GC_INNER hdr * GC_header_cache_miss(ptr_t p, hdr_cache_entry *hce,
+                                      ptr_t source);
+# define HEADER_CACHE_MISS(p, hce, source) \
+          GC_header_cache_miss(p, hce, source)
+#else
+  GC_INNER hdr * GC_header_cache_miss(ptr_t p, hdr_cache_entry *hce);
+# define HEADER_CACHE_MISS(p, hce, source) GC_header_cache_miss(p, hce)
+#endif
+
+/* Set hhdr to the header for p.  Analogous to GET_HDR below,           */
+/* except that in the case of large objects, it                         */
+/* gets the header for the object beginning, if GC_all_interior_ptrs    */
+/* is set.                                                              */
+/* Returns zero if p points to somewhere other than the first page      */
+/* of an object, and it is not a valid pointer to the object.           */
+#define HC_GET_HDR(p, hhdr, source, exit_label) \
+        do { \
+          hdr_cache_entry * hce = HCE(p); \
+          if (EXPECT(HCE_VALID_FOR(hce, p), TRUE)) { \
+            HC_HIT(); \
+            hhdr = hce -> hce_hdr; \
+          } else { \
+            hhdr = HEADER_CACHE_MISS(p, hce, source); \
+            if (0 == hhdr) goto exit_label; \
+          } \
+        } while (0)
+
+typedef struct bi {
+    hdr * index[BOTTOM_SZ];
+        /*
+         * The bottom level index contains one of three kinds of values:
+         * 0 means we're not responsible for this block,
+         *   or this is a block other than the first one in a free block.
+         * 1 < (long)X <= MAX_JUMP means the block starts at least
+         *        X * HBLKSIZE bytes before the current address.
+         * A valid pointer points to a hdr structure. (The above can't be
+         * valid pointers due to the GET_MEM return convention.)
+         */
+    struct bi * asc_link;       /* All indices are linked in    */
+                                /* ascending order...           */
+    struct bi * desc_link;      /* ... and in descending order. */
+    word key;                   /* high order address bits.     */
+# ifdef HASH_TL
+    struct bi * hash_link;      /* Hash chain link.             */
+# endif
+} bottom_index;
+
+/* bottom_index GC_all_nils; - really part of GC_arrays */
+
+/* extern bottom_index * GC_top_index []; - really part of GC_arrays */
+                                /* Each entry points to a bottom_index. */
+                                /* On a 32 bit machine, it points to    */
+                                /* the index for a set of high order    */
+                                /* bits equal to the index.  For longer */
+                                /* addresses, we hash the high order    */
+                                /* bits to compute the index in         */
+                                /* GC_top_index, and each entry points  */
+                                /* to a hash chain.                     */
+                                /* The last entry in each chain is      */
+                                /* GC_all_nils.                         */
+
+
+#define MAX_JUMP (HBLKSIZE - 1)
+
+#define HDR_FROM_BI(bi, p) \
+                ((bi)->index[((word)(p) >> LOG_HBLKSIZE) & (BOTTOM_SZ - 1)])
+#ifndef HASH_TL
+# define BI(p) (GC_top_index \
+              [(word)(p) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE)])
+# define HDR_INNER(p) HDR_FROM_BI(BI(p),p)
+# ifdef SMALL_CONFIG
+#     define HDR(p) GC_find_header((ptr_t)(p))
+# else
+#     define HDR(p) HDR_INNER(p)
+# endif
+# define GET_BI(p, bottom_indx) (void)((bottom_indx) = BI(p))
+# define GET_HDR(p, hhdr) (void)((hhdr) = HDR(p))
+# define SET_HDR(p, hhdr) (void)(HDR_INNER(p) = (hhdr))
+# define GET_HDR_ADDR(p, ha) (void)((ha) = &HDR_INNER(p))
+#else /* hash */
+  /* Hash function for tree top level */
+# define TL_HASH(hi) ((hi) & (TOP_SZ - 1))
+  /* Set bottom_indx to point to the bottom index for address p */
+# define GET_BI(p, bottom_indx) \
+        do { \
+          register word hi = \
+              (word)(p) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE); \
+          register bottom_index * _bi = GC_top_index[TL_HASH(hi)]; \
+          while (_bi -> key != hi && _bi != GC_all_nils) \
+              _bi = _bi -> hash_link; \
+          (bottom_indx) = _bi; \
+        } while (0)
+# define GET_HDR_ADDR(p, ha) \
+        do { \
+          register bottom_index * bi; \
+          GET_BI(p, bi); \
+          (ha) = &HDR_FROM_BI(bi, p); \
+        } while (0)
+# define GET_HDR(p, hhdr) \
+        do { \
+          register hdr ** _ha; \
+          GET_HDR_ADDR(p, _ha); \
+          (hhdr) = *_ha; \
+        } while (0)
+# define SET_HDR(p, hhdr) \
+        do { \
+          register hdr ** _ha; \
+          GET_HDR_ADDR(p, _ha); \
+          *_ha = (hhdr); \
+        } while (0)
+# define HDR(p) GC_find_header((ptr_t)(p))
+#endif
+
+/* Is the result a forwarding address to someplace closer to the        */
+/* beginning of the block or NULL?                                      */
+#define IS_FORWARDING_ADDR_OR_NIL(hhdr) ((size_t) (hhdr) <= MAX_JUMP)
+
+/* Get an HBLKSIZE aligned address closer to the beginning of the block */
+/* h.  Assumes hhdr == HDR(h) and IS_FORWARDING_ADDR(hhdr).             */
+#define FORWARDED_ADDR(h, hhdr) ((struct hblk *)(h) - (size_t)(hhdr))
+
+#endif /* GC_HEADERS_H */

+ 220 - 0
blitz.mod/bdwgc/include/private/gc_locks.h

@@ -0,0 +1,220 @@
+/*
+ * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
+ * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1996-1999 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.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+#ifndef GC_LOCKS_H
+#define GC_LOCKS_H
+
+/*
+ * Mutual exclusion between allocator/collector routines.
+ * Needed if there is more than one allocator thread.
+ * DCL_LOCK_STATE declares any local variables needed by LOCK and UNLOCK.
+ *
+ * Note that I_HOLD_LOCK and I_DONT_HOLD_LOCK are used only positively
+ * in assertions, and may return TRUE in the "don't know" case.
+ */
+# ifdef THREADS
+
+#  if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS)
+#    include "atomic_ops.h"
+#  endif
+
+#  ifdef PCR
+#    include <base/PCR_Base.h>
+#    include <th/PCR_Th.h>
+     GC_EXTERN PCR_Th_ML GC_allocate_ml;
+#    define DCL_LOCK_STATE \
+         PCR_ERes GC_fastLockRes; PCR_sigset_t GC_old_sig_mask
+#    define UNCOND_LOCK() PCR_Th_ML_Acquire(&GC_allocate_ml)
+#    define UNCOND_UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml)
+#  endif
+
+#  if (!defined(AO_HAVE_test_and_set_acquire) || defined(GC_RTEMS_PTHREADS) \
+       || defined(SN_TARGET_PS3) || defined(GC_WIN32_THREADS) \
+       || defined(LINT2)) && defined(GC_PTHREADS)
+#    define USE_PTHREAD_LOCKS
+#  endif
+
+#  if defined(GC_WIN32_THREADS) && !defined(USE_PTHREAD_LOCKS)
+#    ifndef WIN32_LEAN_AND_MEAN
+#      define WIN32_LEAN_AND_MEAN 1
+#    endif
+#    define NOSERVICE
+#    include <windows.h>
+#    define NO_THREAD (DWORD)(-1)
+     GC_EXTERN CRITICAL_SECTION GC_allocate_ml;
+#    ifdef GC_ASSERTIONS
+       GC_EXTERN DWORD GC_lock_holder;
+#      define SET_LOCK_HOLDER() GC_lock_holder = GetCurrentThreadId()
+#      define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD
+#      define I_HOLD_LOCK() (!GC_need_to_lock \
+                           || GC_lock_holder == GetCurrentThreadId())
+#      define I_DONT_HOLD_LOCK() (!GC_need_to_lock \
+                           || GC_lock_holder != GetCurrentThreadId())
+#      define UNCOND_LOCK() \
+                { GC_ASSERT(I_DONT_HOLD_LOCK()); \
+                  EnterCriticalSection(&GC_allocate_ml); \
+                  SET_LOCK_HOLDER(); }
+#      define UNCOND_UNLOCK() \
+                { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \
+                  LeaveCriticalSection(&GC_allocate_ml); }
+#    else
+#      define UNCOND_LOCK() EnterCriticalSection(&GC_allocate_ml)
+#      define UNCOND_UNLOCK() LeaveCriticalSection(&GC_allocate_ml)
+#    endif /* !GC_ASSERTIONS */
+#  elif defined(GC_PTHREADS)
+#    include <pthread.h>
+
+     /* Posix allows pthread_t to be a struct, though it rarely is.     */
+     /* Unfortunately, we need to use a pthread_t to index a data       */
+     /* structure.  It also helps if comparisons don't involve a        */
+     /* function call.  Hence we introduce platform-dependent macros    */
+     /* to compare pthread_t ids and to map them to integers.           */
+     /* the mapping to integers does not need to result in different    */
+     /* integers for each thread, though that should be true as much    */
+     /* as possible.                                                    */
+     /* Refine to exclude platforms on which pthread_t is struct */
+#    if !defined(GC_WIN32_PTHREADS)
+#      define NUMERIC_THREAD_ID(id) ((unsigned long)(id))
+#      define THREAD_EQUAL(id1, id2) ((id1) == (id2))
+#      define NUMERIC_THREAD_ID_UNIQUE
+#    else
+#      define NUMERIC_THREAD_ID(id) ((unsigned long)(id.p))
+       /* Using documented internal details of win32-pthread library.   */
+       /* Faster than pthread_equal(). Should not change with           */
+       /* future versions of win32-pthread library.                     */
+#      define THREAD_EQUAL(id1, id2) ((id1.p == id2.p) && (id1.x == id2.x))
+#      undef NUMERIC_THREAD_ID_UNIQUE
+       /* Generic definitions based on pthread_equal() always work but  */
+       /* will result in poor performance (as NUMERIC_THREAD_ID is      */
+       /* defined to just a constant) and weak assertion checking.      */
+#    endif
+#    define NO_THREAD ((unsigned long)(-1l))
+                /* != NUMERIC_THREAD_ID(pthread_self()) for any thread */
+
+#    if !defined(THREAD_LOCAL_ALLOC) && !defined(USE_PTHREAD_LOCKS)
+      /* In the THREAD_LOCAL_ALLOC case, the allocation lock tends to   */
+      /* be held for long periods, if it is held at all.  Thus spinning */
+      /* and sleeping for fixed periods are likely to result in         */
+      /* significant wasted time.  We thus rely mostly on queued locks. */
+#     define USE_SPIN_LOCK
+      GC_EXTERN volatile AO_TS_t GC_allocate_lock;
+      GC_INNER void GC_lock(void);
+        /* Allocation lock holder.  Only set if acquired by client through */
+        /* GC_call_with_alloc_lock.                                        */
+#     ifdef GC_ASSERTIONS
+#        define UNCOND_LOCK() \
+              { GC_ASSERT(I_DONT_HOLD_LOCK()); \
+                if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_SET) \
+                  GC_lock(); \
+                SET_LOCK_HOLDER(); }
+#        define UNCOND_UNLOCK() \
+              { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \
+                AO_CLEAR(&GC_allocate_lock); }
+#     else
+#        define UNCOND_LOCK() \
+              { GC_ASSERT(I_DONT_HOLD_LOCK()); \
+                if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_SET) \
+                  GC_lock(); }
+#        define UNCOND_UNLOCK() AO_CLEAR(&GC_allocate_lock)
+#     endif /* !GC_ASSERTIONS */
+#    else /* THREAD_LOCAL_ALLOC || USE_PTHREAD_LOCKS */
+#      ifndef USE_PTHREAD_LOCKS
+#        define USE_PTHREAD_LOCKS
+#      endif
+#    endif /* THREAD_LOCAL_ALLOC || USE_PTHREAD_LOCKS */
+#    ifdef USE_PTHREAD_LOCKS
+#      include <pthread.h>
+       GC_EXTERN pthread_mutex_t GC_allocate_ml;
+#      ifdef GC_ASSERTIONS
+#        define UNCOND_LOCK() { GC_ASSERT(I_DONT_HOLD_LOCK()); \
+                                GC_lock(); SET_LOCK_HOLDER(); }
+#        define UNCOND_UNLOCK() \
+                { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \
+                  pthread_mutex_unlock(&GC_allocate_ml); }
+#      else /* !GC_ASSERTIONS */
+#        if defined(NO_PTHREAD_TRYLOCK)
+#          ifdef USE_SPIN_LOCK
+#            define UNCOND_LOCK() GC_lock()
+#          else
+#            define UNCOND_LOCK() pthread_mutex_lock(&GC_allocate_ml)
+#          endif
+#        else
+#          define UNCOND_LOCK() \
+              { if (0 != pthread_mutex_trylock(&GC_allocate_ml)) \
+                  GC_lock(); }
+#        endif
+#        define UNCOND_UNLOCK() pthread_mutex_unlock(&GC_allocate_ml)
+#      endif /* !GC_ASSERTIONS */
+#    endif /* USE_PTHREAD_LOCKS */
+#    ifdef GC_ASSERTIONS
+       GC_EXTERN unsigned long GC_lock_holder;
+#      define SET_LOCK_HOLDER() \
+                GC_lock_holder = NUMERIC_THREAD_ID(pthread_self())
+#      define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD
+#      define I_HOLD_LOCK() \
+                (!GC_need_to_lock || \
+                 GC_lock_holder == NUMERIC_THREAD_ID(pthread_self()))
+#      ifndef NUMERIC_THREAD_ID_UNIQUE
+#        define I_DONT_HOLD_LOCK() 1  /* Conservatively say yes */
+#      else
+#        define I_DONT_HOLD_LOCK() \
+                (!GC_need_to_lock \
+                 || GC_lock_holder != NUMERIC_THREAD_ID(pthread_self()))
+#      endif
+#    endif /* GC_ASSERTIONS */
+     GC_EXTERN volatile GC_bool GC_collecting;
+#    define ENTER_GC() GC_collecting = 1;
+#    define EXIT_GC() GC_collecting = 0;
+     GC_INNER void GC_lock(void);
+#  endif /* GC_PTHREADS with linux_threads.c implementation */
+   GC_EXTERN GC_bool GC_need_to_lock;
+
+# else /* !THREADS */
+#   define LOCK() (void)0
+#   define UNLOCK() (void)0
+#   ifdef GC_ASSERTIONS
+#     define I_HOLD_LOCK() TRUE
+#     define I_DONT_HOLD_LOCK() TRUE
+                /* Used only in positive assertions or to test whether  */
+                /* we still need to acquire the lock.  TRUE works in    */
+                /* either case.                                         */
+#   endif
+# endif /* !THREADS */
+
+#if defined(UNCOND_LOCK) && !defined(LOCK)
+# ifdef LINT2
+    /* Instruct code analysis tools not to care about GC_need_to_lock   */
+    /* influence to LOCK/UNLOCK semantic.                               */
+#   define LOCK() UNCOND_LOCK()
+#   define UNLOCK() UNCOND_UNLOCK()
+# else
+                /* At least two thread running; need to lock.   */
+#   define LOCK() do { if (GC_need_to_lock) UNCOND_LOCK(); } while (0)
+#   define UNLOCK() do { if (GC_need_to_lock) UNCOND_UNLOCK(); } while (0)
+# endif
+#endif
+
+# ifndef ENTER_GC
+#   define ENTER_GC()
+#   define EXIT_GC()
+# endif
+
+# ifndef DCL_LOCK_STATE
+#   define DCL_LOCK_STATE
+# endif
+
+#endif /* GC_LOCKS_H */

+ 473 - 0
blitz.mod/bdwgc/include/private/gc_pmark.h

@@ -0,0 +1,473 @@
+/*
+ * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 2001 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.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ *
+ */
+
+/* Private declarations of GC marker data structures and macros */
+
+/*
+ * Declarations of mark stack.  Needed by marker and client supplied mark
+ * routines.  Transitively include gc_priv.h.
+ */
+#ifndef GC_PMARK_H
+#define GC_PMARK_H
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifndef GC_BUILD
+# define GC_BUILD
+#endif
+
+#if defined(KEEP_BACK_PTRS) || defined(PRINT_BLACK_LIST)
+# include "dbg_mlc.h"
+#endif
+
+#ifndef GC_MARK_H
+# include "../gc_mark.h"
+#endif
+
+#ifndef GC_PRIVATE_H
+# include "gc_priv.h"
+#endif
+
+/* The real declarations of the following is in gc_priv.h, so that      */
+/* we can avoid scanning the following table.                           */
+/*
+mark_proc GC_mark_procs[MAX_MARK_PROCS];
+*/
+
+#ifndef MARK_DESCR_OFFSET
+# define MARK_DESCR_OFFSET sizeof(word)
+#endif
+
+/*
+ * Mark descriptor stuff that should remain private for now, mostly
+ * because it's hard to export WORDSZ without including gcconfig.h.
+ */
+#define BITMAP_BITS (WORDSZ - GC_DS_TAG_BITS)
+#define PROC(descr) \
+      (GC_mark_procs[((descr) >> GC_DS_TAG_BITS) & (GC_MAX_MARK_PROCS-1)])
+#define ENV(descr) \
+      ((descr) >> (GC_DS_TAG_BITS + GC_LOG_MAX_MARK_PROCS))
+#define MAX_ENV \
+      (((word)1 << (WORDSZ - GC_DS_TAG_BITS - GC_LOG_MAX_MARK_PROCS)) - 1)
+
+GC_EXTERN unsigned GC_n_mark_procs;
+
+/* Number of mark stack entries to discard on overflow. */
+#define GC_MARK_STACK_DISCARDS (INITIAL_MARK_STACK_SIZE/8)
+
+GC_EXTERN size_t GC_mark_stack_size;
+
+#ifdef PARALLEL_MARK
+    /*
+     * Allow multiple threads to participate in the marking process.
+     * This works roughly as follows:
+     *  The main mark stack never shrinks, but it can grow.
+     *
+     *  The initiating threads holds the GC lock, and sets GC_help_wanted.
+     *
+     *  Other threads:
+     *     1) update helper_count (while holding mark_lock.)
+     *     2) allocate a local mark stack
+     *     repeatedly:
+     *          3) Steal a global mark stack entry by atomically replacing
+     *             its descriptor with 0.
+     *          4) Copy it to the local stack.
+     *          5) Mark on the local stack until it is empty, or
+     *             it may be profitable to copy it back.
+     *          6) If necessary, copy local stack to global one,
+     *             holding mark lock.
+     *    7) Stop when the global mark stack is empty.
+     *    8) decrement helper_count (holding mark_lock).
+     *
+     * This is an experiment to see if we can do something along the lines
+     * of the University of Tokyo SGC in a less intrusive, though probably
+     * also less performant, way.
+     */
+
+    /* GC_mark_stack_top is protected by mark lock.     */
+
+    /*
+     * GC_notify_all_marker() is used when GC_help_wanted is first set,
+     * when the last helper becomes inactive,
+     * when something is added to the global mark stack, and just after
+     * GC_mark_no is incremented.
+     * This could be split into multiple CVs (and probably should be to
+     * scale to really large numbers of processors.)
+     */
+#endif /* PARALLEL_MARK */
+
+GC_INNER mse * GC_signal_mark_stack_overflow(mse *msp);
+
+/* Push the object obj with corresponding heap block header hhdr onto   */
+/* the mark stack.                                                      */
+#define PUSH_OBJ(obj, hhdr, mark_stack_top, mark_stack_limit) \
+  do { \
+    register word _descr = (hhdr) -> hb_descr; \
+    GC_ASSERT(!HBLK_IS_FREE(hhdr)); \
+    if (_descr != 0) { \
+        mark_stack_top++; \
+        if ((word)mark_stack_top >= (word)(mark_stack_limit)) { \
+          mark_stack_top = GC_signal_mark_stack_overflow(mark_stack_top); \
+        } \
+        mark_stack_top -> mse_start = (obj); \
+        mark_stack_top -> mse_descr.w = _descr; \
+    } \
+  } while (0)
+
+/* Push the contents of current onto the mark stack if it is a valid    */
+/* ptr to a currently unmarked object.  Mark it.                        */
+/* If we assumed a standard-conforming compiler, we could probably      */
+/* generate the exit_label transparently.                               */
+#define PUSH_CONTENTS(current, mark_stack_top, mark_stack_limit, \
+                      source, exit_label) \
+  do { \
+    hdr * my_hhdr; \
+    HC_GET_HDR(current, my_hhdr, source, exit_label); \
+    PUSH_CONTENTS_HDR(current, mark_stack_top, mark_stack_limit, \
+                  source, exit_label, my_hhdr, TRUE); \
+  exit_label: ; \
+  } while (0)
+
+/* Set mark bit, exit if it was already set.    */
+#ifdef USE_MARK_BYTES
+  /* There is a race here, and we may set                               */
+  /* the bit twice in the concurrent case.  This can result in the      */
+  /* object being pushed twice.  But that's only a performance issue.   */
+# define SET_MARK_BIT_EXIT_IF_SET(hhdr,bit_no,exit_label) \
+    do { \
+        char * mark_byte_addr = (char *)hhdr -> hb_marks + (bit_no); \
+        if (*mark_byte_addr) goto exit_label; \
+        *mark_byte_addr = 1; \
+    } while (0)
+#else
+# ifdef PARALLEL_MARK
+    /* This is used only if we explicitly set USE_MARK_BITS.            */
+    /* The following may fail to exit even if the bit was already set.  */
+    /* For our uses, that's benign:                                     */
+#   define OR_WORD_EXIT_IF_SET(addr, bits, exit_label) \
+        do { \
+          if (!(*(addr) & (bits))) { \
+            AO_or((volatile AO_t *)(addr), (AO_t)(bits)); \
+          } else { \
+            goto exit_label; \
+          } \
+        } while (0)
+# else
+#   define OR_WORD_EXIT_IF_SET(addr, bits, exit_label) \
+        do { \
+           word old = *(addr); \
+           word my_bits = (bits); \
+           if (old & my_bits) goto exit_label; \
+           *(addr) = (old | my_bits); \
+        } while (0)
+# endif /* !PARALLEL_MARK */
+# define SET_MARK_BIT_EXIT_IF_SET(hhdr,bit_no,exit_label) \
+    do { \
+        word * mark_word_addr = hhdr -> hb_marks + divWORDSZ(bit_no); \
+        OR_WORD_EXIT_IF_SET(mark_word_addr, (word)1 << modWORDSZ(bit_no), \
+                            exit_label); \
+    } while (0)
+#endif /* !USE_MARK_BYTES */
+
+#ifdef PARALLEL_MARK
+# define INCR_MARKS(hhdr) \
+                AO_store(&hhdr->hb_n_marks, AO_load(&hhdr->hb_n_marks) + 1)
+#else
+# define INCR_MARKS(hhdr) (void)(++hhdr->hb_n_marks)
+#endif
+
+#ifdef ENABLE_TRACE
+# define TRACE(source, cmd) \
+        if (GC_trace_addr != 0 && (ptr_t)(source) == GC_trace_addr) cmd
+# define TRACE_TARGET(target, cmd) \
+        if (GC_trace_addr != 0 && (target) == *(ptr_t *)GC_trace_addr) cmd
+#else
+# define TRACE(source, cmd)
+# define TRACE_TARGET(source, cmd)
+#endif
+
+#if defined(I386) && defined(__GNUC__)
+# define LONG_MULT(hprod, lprod, x, y) \
+    do { \
+        __asm__ __volatile__("mull %2" : "=a"(lprod), "=d"(hprod) \
+                             : "g"(y), "0"(x)); \
+    } while (0)
+#else
+# define LONG_MULT(hprod, lprod, x, y) \
+    do { \
+        unsigned long long prod = (unsigned long long)(x) \
+                                  * (unsigned long long)(y); \
+        GC_STATIC_ASSERT(sizeof(x) + sizeof(y) <= sizeof(prod)); \
+        hprod = prod >> 32; \
+        lprod = (unsigned32)prod; \
+    } while (0)
+#endif /* !I386 */
+
+/* If the mark bit corresponding to current is not set, set it, and     */
+/* push the contents of the object on the mark stack.  Current points   */
+/* to the beginning of the object.  We rely on the fact that the        */
+/* preceding header calculation will succeed for a pointer past the     */
+/* first page of an object, only if it is in fact a valid pointer       */
+/* to the object.  Thus we can omit the otherwise necessary tests       */
+/* here.  Note in particular that the "displ" value is the displacement */
+/* from the beginning of the heap block, which may itself be in the     */
+/* interior of a large object.                                          */
+#ifdef MARK_BIT_PER_GRANULE
+# define PUSH_CONTENTS_HDR(current, mark_stack_top, mark_stack_limit, \
+                           source, exit_label, hhdr, do_offset_check) \
+  do { \
+    size_t displ = HBLKDISPL(current); /* Displacement in block; in bytes. */\
+    /* displ is always within range.  If current doesn't point to       */ \
+    /* first block, then we are in the all_interior_pointers case, and  */ \
+    /* it is safe to use any displacement value.                        */ \
+    size_t gran_displ = BYTES_TO_GRANULES(displ); \
+    size_t gran_offset = hhdr -> hb_map[gran_displ]; \
+    size_t byte_offset = displ & (GRANULE_BYTES - 1); \
+    ptr_t base = current; \
+    /* The following always fails for large block references. */ \
+    if (EXPECT((gran_offset | byte_offset) != 0, FALSE))  { \
+        if (hhdr -> hb_large_block) { \
+          /* gran_offset is bogus.      */ \
+          size_t obj_displ; \
+          base = (ptr_t)(hhdr -> hb_block); \
+          obj_displ = (ptr_t)(current) - base; \
+          if (obj_displ != displ) { \
+            GC_ASSERT(obj_displ < hhdr -> hb_sz); \
+            /* Must be in all_interior_pointer case, not first block */ \
+            /* already did validity check on cache miss.             */ \
+          } else { \
+            if (do_offset_check && !GC_valid_offsets[obj_displ]) { \
+              GC_ADD_TO_BLACK_LIST_NORMAL(current, source); \
+              goto exit_label; \
+            } \
+          } \
+          gran_displ = 0; \
+          GC_ASSERT(hhdr -> hb_sz > HBLKSIZE || \
+                    hhdr -> hb_block == HBLKPTR(current)); \
+          GC_ASSERT((word)hhdr->hb_block <= (word)(current)); \
+        } else { \
+          size_t obj_displ = GRANULES_TO_BYTES(gran_offset) \
+                             + byte_offset; \
+          if (do_offset_check && !GC_valid_offsets[obj_displ]) { \
+            GC_ADD_TO_BLACK_LIST_NORMAL(current, source); \
+            goto exit_label; \
+          } \
+          gran_displ -= gran_offset; \
+          base -= obj_displ; \
+        } \
+    } \
+    GC_ASSERT(hhdr == GC_find_header(base)); \
+    GC_ASSERT(gran_displ % BYTES_TO_GRANULES(hhdr -> hb_sz) == 0); \
+    TRACE(source, GC_log_printf("GC #%u: passed validity tests\n", \
+                                (unsigned)GC_gc_no)); \
+    SET_MARK_BIT_EXIT_IF_SET(hhdr, gran_displ, exit_label); \
+    TRACE(source, GC_log_printf("GC #%u: previously unmarked\n", \
+                                (unsigned)GC_gc_no)); \
+    TRACE_TARGET(base, \
+        GC_log_printf("GC #%u: marking %p from %p instead\n", \
+                      (unsigned)GC_gc_no, base, source)); \
+    INCR_MARKS(hhdr); \
+    GC_STORE_BACK_PTR((ptr_t)source, base); \
+    PUSH_OBJ(base, hhdr, mark_stack_top, mark_stack_limit); \
+  } while (0)
+#endif /* MARK_BIT_PER_GRANULE */
+
+#ifdef MARK_BIT_PER_OBJ
+# define PUSH_CONTENTS_HDR(current, mark_stack_top, mark_stack_limit, \
+                           source, exit_label, hhdr, do_offset_check) \
+  do { \
+    size_t displ = HBLKDISPL(current); /* Displacement in block; in bytes. */\
+    unsigned32 low_prod, high_prod; \
+    unsigned32 inv_sz = hhdr -> hb_inv_sz; \
+    ptr_t base = current; \
+    LONG_MULT(high_prod, low_prod, displ, inv_sz); \
+    /* product is > and within sz_in_bytes of displ * sz_in_bytes * 2**32 */ \
+    if (EXPECT(low_prod >> 16 != 0, FALSE))  { \
+      /* FIXME: fails if offset is a multiple of HBLKSIZE which becomes 0 */ \
+        if (inv_sz == LARGE_INV_SZ) { \
+          size_t obj_displ; \
+          base = (ptr_t)(hhdr -> hb_block); \
+          obj_displ = (ptr_t)(current) - base; \
+          if (obj_displ != displ) { \
+            GC_ASSERT(obj_displ < hhdr -> hb_sz); \
+            /* Must be in all_interior_pointer case, not first block */ \
+            /* already did validity check on cache miss.             */ \
+          } else { \
+            if (do_offset_check && !GC_valid_offsets[obj_displ]) { \
+              GC_ADD_TO_BLACK_LIST_NORMAL(current, source); \
+              goto exit_label; \
+            } \
+          } \
+          GC_ASSERT(hhdr -> hb_sz > HBLKSIZE || \
+                    hhdr -> hb_block == HBLKPTR(current)); \
+          GC_ASSERT((word)hhdr->hb_block < (word)(current)); \
+        } else { \
+          /* Accurate enough if HBLKSIZE <= 2**15.      */ \
+          GC_STATIC_ASSERT(HBLKSIZE <= (1 << 15)); \
+          size_t obj_displ = (((low_prod >> 16) + 1) * (hhdr->hb_sz)) >> 16; \
+          if (do_offset_check && !GC_valid_offsets[obj_displ]) { \
+            GC_ADD_TO_BLACK_LIST_NORMAL(current, source); \
+            goto exit_label; \
+          } \
+          base -= obj_displ; \
+        } \
+    } \
+    /* May get here for pointer to start of block not at        */ \
+    /* beginning of object.  If so, it's valid, and we're fine. */ \
+    GC_ASSERT(high_prod <= HBLK_OBJS(hhdr -> hb_sz)); \
+    TRACE(source, GC_log_printf("GC #%u: passed validity tests\n", \
+                                (unsigned)GC_gc_no)); \
+    SET_MARK_BIT_EXIT_IF_SET(hhdr, high_prod, exit_label); \
+    TRACE(source, GC_log_printf("GC #%u: previously unmarked\n", \
+                                (unsigned)GC_gc_no)); \
+    TRACE_TARGET(base, \
+        GC_log_printf("GC #%u: marking %p from %p instead\n", \
+                      (unsigned)GC_gc_no, base, source)); \
+    INCR_MARKS(hhdr); \
+    GC_STORE_BACK_PTR((ptr_t)source, base); \
+    PUSH_OBJ(base, hhdr, mark_stack_top, mark_stack_limit); \
+  } while (0)
+#endif /* MARK_BIT_PER_OBJ */
+
+#if defined(PRINT_BLACK_LIST) || defined(KEEP_BACK_PTRS)
+# define PUSH_ONE_CHECKED_STACK(p, source) \
+        GC_mark_and_push_stack((ptr_t)(p), (ptr_t)(source))
+#else
+# define PUSH_ONE_CHECKED_STACK(p, source) \
+        GC_mark_and_push_stack((ptr_t)(p))
+#endif
+
+/*
+ * Push a single value onto mark stack. Mark from the object pointed to by p.
+ * Invoke FIXUP_POINTER(p) before any further processing.
+ * P is considered valid even if it is an interior pointer.
+ * Previously marked objects are not pushed.  Hence we make progress even
+ * if the mark stack overflows.
+ */
+
+#if NEED_FIXUP_POINTER
+    /* Try both the raw version and the fixed up one.   */
+# define GC_PUSH_ONE_STACK(p, source) \
+    do { \
+      if ((word)(p) >= (word)GC_least_plausible_heap_addr \
+          && (word)(p) < (word)GC_greatest_plausible_heap_addr) { \
+         PUSH_ONE_CHECKED_STACK(p, source); \
+      } \
+      FIXUP_POINTER(p); \
+      if ((word)(p) >= (word)GC_least_plausible_heap_addr \
+          && (word)(p) < (word)GC_greatest_plausible_heap_addr) { \
+         PUSH_ONE_CHECKED_STACK(p, source); \
+      } \
+    } while (0)
+#else /* !NEED_FIXUP_POINTER */
+# define GC_PUSH_ONE_STACK(p, source) \
+    do { \
+      if ((word)(p) >= (word)GC_least_plausible_heap_addr \
+          && (word)(p) < (word)GC_greatest_plausible_heap_addr) { \
+         PUSH_ONE_CHECKED_STACK(p, source); \
+      } \
+    } while (0)
+#endif
+
+/* As above, but interior pointer recognition as for normal heap pointers. */
+#define GC_PUSH_ONE_HEAP(p,source,mark_stack_top) \
+    do { \
+      FIXUP_POINTER(p); \
+      if ((word)(p) >= (word)GC_least_plausible_heap_addr \
+          && (word)(p) < (word)GC_greatest_plausible_heap_addr) \
+        mark_stack_top = GC_mark_and_push((void *)(p), mark_stack_top, \
+                                GC_mark_stack_limit, (void * *)(source)); \
+    } while (0)
+
+/* Mark starting at mark stack entry top (incl.) down to        */
+/* mark stack entry bottom (incl.).  Stop after performing      */
+/* about one page worth of work.  Return the new mark stack     */
+/* top entry.                                                   */
+GC_INNER mse * GC_mark_from(mse * top, mse * bottom, mse *limit);
+
+#define MARK_FROM_MARK_STACK() \
+        GC_mark_stack_top = GC_mark_from(GC_mark_stack_top, \
+                                         GC_mark_stack, \
+                                         GC_mark_stack + GC_mark_stack_size);
+
+#define GC_mark_stack_empty() ((word)GC_mark_stack_top < (word)GC_mark_stack)
+
+/*
+ * Mark from one finalizable object using the specified
+ * mark proc. May not mark the object pointed to by
+ * real_ptr. That is the job of the caller, if appropriate.
+ * Note that this is called with the mutator running, but
+ * with us holding the allocation lock.  This is safe only if the
+ * mutator needs the allocation lock to reveal hidden pointers.
+ * FIXME: Why do we need the GC_mark_state test below?
+ */
+#define GC_MARK_FO(real_ptr, mark_proc) \
+  do { \
+    (*(mark_proc))(real_ptr); \
+    while (!GC_mark_stack_empty()) MARK_FROM_MARK_STACK(); \
+    if (GC_mark_state != MS_NONE) { \
+        GC_set_mark_bit(real_ptr); \
+        while (!GC_mark_some((ptr_t)0)) { /* empty */ } \
+    } \
+  } while (0)
+
+GC_EXTERN GC_bool GC_mark_stack_too_small;
+                                /* We need a larger mark stack.  May be */
+                                /* set by client supplied mark routines.*/
+
+typedef int mark_state_t;       /* Current state of marking, as follows:*/
+                                /* Used to remember where we are during */
+                                /* concurrent marking.                  */
+
+                                /* We say something is dirty if it was  */
+                                /* written since the last time we       */
+                                /* retrieved dirty bits.  We say it's   */
+                                /* grungy if it was marked dirty in the */
+                                /* last set of bits we retrieved.       */
+
+                                /* Invariant I: all roots and marked    */
+                                /* objects p are either dirty, or point */
+                                /* to objects q that are either marked  */
+                                /* or a pointer to q appears in a range */
+                                /* on the mark stack.                   */
+
+#define MS_NONE 0               /* No marking in progress. I holds.     */
+                                /* Mark stack is empty.                 */
+
+#define MS_PUSH_RESCUERS 1      /* Rescuing objects are currently       */
+                                /* being pushed.  I holds, except       */
+                                /* that grungy roots may point to       */
+                                /* unmarked objects, as may marked      */
+                                /* grungy objects above scan_ptr.       */
+
+#define MS_PUSH_UNCOLLECTABLE 2 /* I holds, except that marked          */
+                                /* uncollectible objects above scan_ptr */
+                                /* may point to unmarked objects.       */
+                                /* Roots may point to unmarked objects  */
+
+#define MS_ROOTS_PUSHED 3       /* I holds, mark stack may be nonempty  */
+
+#define MS_PARTIALLY_INVALID 4  /* I may not hold, e.g. because of M.S. */
+                                /* overflow.  However marked heap       */
+                                /* objects below scan_ptr point to      */
+                                /* marked or stacked objects.           */
+
+#define MS_INVALID 5            /* I may not hold.                      */
+
+GC_EXTERN mark_state_t GC_mark_state;
+
+#endif  /* GC_PMARK_H */

+ 2511 - 0
blitz.mod/bdwgc/include/private/gc_priv.h

@@ -0,0 +1,2511 @@
+/*
+ * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
+ * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1996-1999 by Silicon Graphics.  All rights reserved.
+ * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P.
+ *
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+#ifndef GC_PRIVATE_H
+#define GC_PRIVATE_H
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifndef GC_BUILD
+# define GC_BUILD
+#endif
+
+#if (defined(__linux__) || defined(__GLIBC__) || defined(__GNU__)) \
+    && !defined(_GNU_SOURCE)
+  /* Can't test LINUX, since this must be defined before other includes. */
+# define _GNU_SOURCE 1
+#endif
+
+#if (defined(DGUX) && defined(GC_THREADS) || defined(DGUX386_THREADS) \
+     || defined(GC_DGUX386_THREADS)) && !defined(_USING_POSIX4A_DRAFT10)
+# define _USING_POSIX4A_DRAFT10 1
+#endif
+
+# if defined(NO_DEBUGGING) && !defined(GC_ASSERTIONS) && !defined(NDEBUG)
+    /* To turn off assertion checking (in atomic_ops.h). */
+#   define NDEBUG 1
+# endif
+
+#ifndef GC_H
+# include "../gc.h"
+#endif
+
+#include <stdlib.h>
+#if !defined(sony_news)
+# include <stddef.h>
+#endif
+
+#ifdef DGUX
+# include <sys/types.h>
+# include <sys/time.h>
+# include <sys/resource.h>
+#endif /* DGUX */
+
+#ifdef BSD_TIME
+# include <sys/types.h>
+# include <sys/time.h>
+# include <sys/resource.h>
+#endif /* BSD_TIME */
+
+#ifdef PARALLEL_MARK
+# define AO_REQUIRE_CAS
+# if !defined(__GNUC__) && !defined(AO_ASSUME_WINDOWS98)
+#   define AO_ASSUME_WINDOWS98
+# endif
+#endif
+
+#ifndef GC_TINY_FL_H
+# include "../gc_tiny_fl.h"
+#endif
+
+#ifndef GC_MARK_H
+# include "../gc_mark.h"
+#endif
+
+typedef GC_word word;
+typedef GC_signed_word signed_word;
+typedef unsigned int unsigned32;
+
+typedef int GC_bool;
+#define TRUE 1
+#define FALSE 0
+
+typedef char * ptr_t;   /* A generic pointer to which we can add        */
+                        /* byte displacements and which can be used     */
+                        /* for address comparisons.                     */
+
+#ifndef GCCONFIG_H
+# include "gcconfig.h"
+#endif
+
+#ifndef GC_INNER
+  /* This tagging macro must be used at the start of every variable     */
+  /* definition which is declared with GC_EXTERN.  Should be also used  */
+  /* for the GC-scope function definitions and prototypes.  Must not be */
+  /* used in gcconfig.h.  Shouldn't be used for the debugging-only      */
+  /* functions.  Currently, not used for the functions declared in or   */
+  /* called from the "dated" source files (pcr_interface.c and files    */
+  /* located in the "extra" folder).                                    */
+# if defined(GC_DLL) && defined(__GNUC__) && !defined(MSWIN32) \
+        && !defined(MSWINCE) && !defined(CYGWIN32)
+#   if __GNUC__ >= 4
+      /* See the corresponding GC_API definition. */
+#     define GC_INNER __attribute__((__visibility__("hidden")))
+#   else
+      /* The attribute is unsupported. */
+#     define GC_INNER /* empty */
+#   endif
+# else
+#   define GC_INNER /* empty */
+# endif
+
+# define GC_EXTERN extern GC_INNER
+  /* Used only for the GC-scope variables (prefixed with "GC_")         */
+  /* declared in the header files.  Must not be used for thread-local   */
+  /* variables.  Must not be used in gcconfig.h.  Shouldn't be used for */
+  /* the debugging-only or profiling-only variables.  Currently, not    */
+  /* used for the variables accessed from the "dated" source files      */
+  /* (pcr_interface.c, specific.c/h, and in the "extra" folder).        */
+  /* The corresponding variable definition must start with GC_INNER.    */
+#endif /* !GC_INNER */
+
+#ifndef HEADERS_H
+# include "gc_hdrs.h"
+#endif
+
+#ifndef GC_ATTR_UNUSED
+# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#   define GC_ATTR_UNUSED __attribute__((__unused__))
+# else
+#   define GC_ATTR_UNUSED /* empty */
+# endif
+#endif /* !GC_ATTR_UNUSED */
+
+#if __GNUC__ >= 3 && !defined(LINT2)
+# define EXPECT(expr, outcome) __builtin_expect(expr,outcome)
+  /* Equivalent to (expr), but predict that usually (expr)==outcome. */
+#else
+# define EXPECT(expr, outcome) (expr)
+#endif /* __GNUC__ */
+
+#ifdef HAVE_CONFIG_H
+  /* The "inline" keyword is determined by Autoconf AC_C_INLINE.    */
+# define GC_INLINE static inline
+#elif defined(_MSC_VER) || defined(__INTEL_COMPILER) || defined(__DMC__) \
+        || ((__GNUC__ >= 3) && defined(__STRICT_ANSI__)) \
+        || defined(__WATCOMC__)
+# define GC_INLINE static __inline
+#elif (__GNUC__ >= 3) || defined(__sun)
+# define GC_INLINE static inline
+#else
+# define GC_INLINE static
+#endif
+
+#ifndef GC_API_OSCALL
+  /* This is used to identify GC routines called by name from OS.       */
+# if defined(__GNUC__)
+#   if __GNUC__ >= 4
+      /* Same as GC_API if GC_DLL.      */
+#     define GC_API_OSCALL extern __attribute__((__visibility__("default")))
+#   else
+      /* The attribute is unsupported.  */
+#     define GC_API_OSCALL extern
+#   endif
+# else
+#   define GC_API_OSCALL GC_API
+# endif
+#endif
+
+#ifndef GC_API_PRIV
+# define GC_API_PRIV GC_API
+#endif
+
+#ifndef GC_LOCKS_H
+# include "gc_locks.h"
+#endif
+
+#define ONES ((word)(signed_word)(-1))
+
+# ifdef STACK_GROWS_DOWN
+#   define COOLER_THAN >
+#   define HOTTER_THAN <
+#   define MAKE_COOLER(x,y) if ((word)((x) + (y)) > (word)(x)) {(x) += (y);} \
+                            else (x) = (ptr_t)ONES
+#   define MAKE_HOTTER(x,y) (x) -= (y)
+# else
+#   define COOLER_THAN <
+#   define HOTTER_THAN >
+#   define MAKE_COOLER(x,y) if ((word)((x) - (y)) < (word)(x)) {(x) -= (y);} \
+                            else (x) = 0
+#   define MAKE_HOTTER(x,y) (x) += (y)
+# endif
+
+#if defined(AMIGA) && defined(__SASC)
+#   define GC_FAR __far
+#else
+#   define GC_FAR
+#endif
+
+
+/*********************************/
+/*                               */
+/* Definitions for conservative  */
+/* collector                     */
+/*                               */
+/*********************************/
+
+/*********************************/
+/*                               */
+/* Easily changeable parameters  */
+/*                               */
+/*********************************/
+
+/* #define STUBBORN_ALLOC */
+                    /* Enable stubborn allocation, and thus a limited   */
+                    /* form of incremental collection w/o dirty bits.   */
+
+/* #define ALL_INTERIOR_POINTERS */
+                    /* Forces all pointers into the interior of an      */
+                    /* object to be considered valid.  Also causes the  */
+                    /* sizes of all objects to be inflated by at least  */
+                    /* one byte.  This should suffice to guarantee      */
+                    /* that in the presence of a compiler that does     */
+                    /* not perform garbage-collector-unsafe             */
+                    /* optimizations, all portable, strictly ANSI       */
+                    /* conforming C programs should be safely usable    */
+                    /* with malloc replaced by GC_malloc and free       */
+                    /* calls removed.  There are several disadvantages: */
+                    /* 1. There are probably no interesting, portable,  */
+                    /*    strictly ANSI conforming C programs.          */
+                    /* 2. This option makes it hard for the collector   */
+                    /*    to allocate space that is not "pointed to"    */
+                    /*    by integers, etc.  Under SunOS 4.X with a     */
+                    /*    statically linked libc, we empirically        */
+                    /*    observed that it would be difficult to        */
+                    /*    allocate individual objects larger than 100K. */
+                    /*    Even if only smaller objects are allocated,   */
+                    /*    more swap space is likely to be needed.       */
+                    /*    Fortunately, much of this will never be       */
+                    /*    touched.                                      */
+                    /* If you can easily avoid using this option, do.   */
+                    /* If not, try to keep individual objects small.    */
+                    /* This is now really controlled at startup,        */
+                    /* through GC_all_interior_pointers.                */
+
+
+#ifndef GC_NO_FINALIZATION
+#  define GC_INVOKE_FINALIZERS() GC_notify_or_invoke_finalizers()
+   GC_INNER void GC_notify_or_invoke_finalizers(void);
+                        /* If GC_finalize_on_demand is not set, invoke  */
+                        /* eligible finalizers. Otherwise:              */
+                        /* Call *GC_finalizer_notifier if there are     */
+                        /* finalizers to be run, and we haven't called  */
+                        /* this procedure yet this GC cycle.            */
+
+   GC_INNER void GC_push_finalizer_structures(void);
+   GC_INNER void GC_finalize(void);
+                        /* Perform all indicated finalization actions   */
+                        /* on unmarked objects.                         */
+                        /* Unreachable finalizable objects are enqueued */
+                        /* for processing by GC_invoke_finalizers.      */
+                        /* Invoked with lock.                           */
+
+#  ifndef SMALL_CONFIG
+     GC_INNER void GC_print_finalization_stats(void);
+#  endif
+#else
+#  define GC_INVOKE_FINALIZERS() (void)0
+#endif /* GC_NO_FINALIZATION */
+
+#if !defined(DONT_ADD_BYTE_AT_END)
+# ifdef LINT2
+    /* Explicitly instruct the code analysis tool that                  */
+    /* GC_all_interior_pointers is assumed to have only 0 or 1 value.   */
+#   define EXTRA_BYTES (GC_all_interior_pointers? 1 : 0)
+# else
+#   define EXTRA_BYTES GC_all_interior_pointers
+# endif
+# define MAX_EXTRA_BYTES 1
+#else
+# define EXTRA_BYTES 0
+# define MAX_EXTRA_BYTES 0
+#endif
+
+
+# ifndef LARGE_CONFIG
+#   define MINHINCR 16   /* Minimum heap increment, in blocks of HBLKSIZE  */
+                         /* Must be multiple of largest page size.         */
+#   define MAXHINCR 2048 /* Maximum heap increment, in blocks              */
+# else
+#   define MINHINCR 64
+#   define MAXHINCR 4096
+# endif
+
+# define BL_LIMIT GC_black_list_spacing
+                           /* If we need a block of N bytes, and we have */
+                           /* a block of N + BL_LIMIT bytes available,   */
+                           /* and N > BL_LIMIT,                          */
+                           /* but all possible positions in it are       */
+                           /* blacklisted, we just use it anyway (and    */
+                           /* print a warning, if warnings are enabled). */
+                           /* This risks subsequently leaking the block  */
+                           /* due to a false reference.  But not using   */
+                           /* the block risks unreasonable immediate     */
+                           /* heap growth.                               */
+
+/*********************************/
+/*                               */
+/* Stack saving for debugging    */
+/*                               */
+/*********************************/
+
+#ifdef NEED_CALLINFO
+    struct callinfo {
+        word ci_pc;     /* Caller, not callee, pc       */
+#       if NARGS > 0
+            word ci_arg[NARGS]; /* bit-wise complement to avoid retention */
+#       endif
+#       if (NFRAMES * (NARGS + 1)) % 2 == 1
+            /* Likely alignment problem. */
+            word ci_dummy;
+#       endif
+    };
+#endif
+
+#ifdef SAVE_CALL_CHAIN
+  /* Fill in the pc and argument information for up to NFRAMES of my    */
+  /* callers.  Ignore my frame and my callers frame.                    */
+  GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]);
+  GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]);
+#endif
+
+
+/*********************************/
+/*                               */
+/* OS interface routines         */
+/*                               */
+/*********************************/
+
+#ifdef BSD_TIME
+# undef CLOCK_TYPE
+# undef GET_TIME
+# undef MS_TIME_DIFF
+# define CLOCK_TYPE struct timeval
+# define GET_TIME(x) \
+                do { \
+                  struct rusage rusage; \
+                  getrusage(RUSAGE_SELF, &rusage); \
+                  x = rusage.ru_utime; \
+                } while (0)
+# define MS_TIME_DIFF(a,b) ((unsigned long)(a.tv_sec - b.tv_sec) * 1000 \
+                            + (unsigned long)(a.tv_usec - b.tv_usec) / 1000)
+#elif defined(MSWIN32) || defined(MSWINCE)
+# ifndef WIN32_LEAN_AND_MEAN
+#   define WIN32_LEAN_AND_MEAN 1
+# endif
+# define NOSERVICE
+# include <windows.h>
+# include <winbase.h>
+# define CLOCK_TYPE DWORD
+# define GET_TIME(x) (void)(x = GetTickCount())
+# define MS_TIME_DIFF(a,b) ((long)((a)-(b)))
+#else /* !MSWIN32, !MSWINCE, !BSD_TIME */
+# include <time.h>
+# if defined(FREEBSD) && !defined(CLOCKS_PER_SEC)
+#   include <machine/limits.h>
+#   define CLOCKS_PER_SEC CLK_TCK
+# endif
+# if !defined(CLOCKS_PER_SEC)
+#   define CLOCKS_PER_SEC 1000000
+    /* This is technically a bug in the implementation.                 */
+    /* ANSI requires that CLOCKS_PER_SEC be defined.  But at least      */
+    /* under SunOS 4.1.1, it isn't.  Also note that the combination of  */
+    /* ANSI C and POSIX is incredibly gross here.  The type clock_t     */
+    /* is used by both clock() and times().  But on some machines       */
+    /* these use different notions of a clock tick, CLOCKS_PER_SEC      */
+    /* seems to apply only to clock.  Hence we use it here.  On many    */
+    /* machines, including SunOS, clock actually uses units of          */
+    /* microseconds (which are not really clock ticks).                 */
+# endif
+# define CLOCK_TYPE clock_t
+# define GET_TIME(x) (void)(x = clock())
+# define MS_TIME_DIFF(a,b) (CLOCKS_PER_SEC % 1000 == 0 ? \
+        (unsigned long)((a) - (b)) / (unsigned long)(CLOCKS_PER_SEC / 1000) \
+        : ((unsigned long)((a) - (b)) * 1000) / (unsigned long)CLOCKS_PER_SEC)
+  /* Avoid using double type since some targets (like ARM) might        */
+  /* require -lm option for double-to-long conversion.                  */
+#endif /* !BSD_TIME && !MSWIN32 */
+
+/* We use bzero and bcopy internally.  They may not be available.       */
+# if defined(SPARC) && defined(SUNOS4)
+#   define BCOPY_EXISTS
+# endif
+# if defined(M68K) && defined(AMIGA)
+#   define BCOPY_EXISTS
+# endif
+# if defined(M68K) && defined(NEXT)
+#   define BCOPY_EXISTS
+# endif
+# if defined(VAX)
+#   define BCOPY_EXISTS
+# endif
+# if defined(AMIGA)
+#   include <string.h>
+#   define BCOPY_EXISTS
+# endif
+# if defined(DARWIN)
+#   include <string.h>
+#   define BCOPY_EXISTS
+# endif
+# if defined(MACOS) && defined(POWERPC)
+#   include <MacMemory.h>
+#   define bcopy(x,y,n) BlockMoveData(x, y, n)
+#   define bzero(x,n) BlockZero(x, n)
+#   define BCOPY_EXISTS
+# endif
+
+# ifndef BCOPY_EXISTS
+#   include <string.h>
+#   define BCOPY(x,y,n) memcpy(y, x, (size_t)(n))
+#   define BZERO(x,n)  memset(x, 0, (size_t)(n))
+# else
+#   define BCOPY(x,y,n) bcopy((void *)(x),(void *)(y),(size_t)(n))
+#   define BZERO(x,n) bzero((void *)(x),(size_t)(n))
+# endif
+
+/*
+ * Stop and restart mutator threads.
+ */
+# ifdef PCR
+#     include "th/PCR_ThCtl.h"
+#     define STOP_WORLD() \
+        PCR_ThCtl_SetExclusiveMode(PCR_ThCtl_ExclusiveMode_stopNormal, \
+                                   PCR_allSigsBlocked, \
+                                   PCR_waitForever)
+#     define START_WORLD() \
+        PCR_ThCtl_SetExclusiveMode(PCR_ThCtl_ExclusiveMode_null, \
+                                   PCR_allSigsBlocked, \
+                                   PCR_waitForever)
+# else
+#   if defined(GC_WIN32_THREADS) || defined(GC_PTHREADS)
+      GC_INNER void GC_stop_world(void);
+      GC_INNER void GC_start_world(void);
+#     define STOP_WORLD() GC_stop_world()
+#     define START_WORLD() GC_start_world()
+#   else
+        /* Just do a sanity check: we are not inside GC_do_blocking().  */
+#     define STOP_WORLD() GC_ASSERT(GC_blocked_sp == NULL)
+#     define START_WORLD()
+#   endif
+# endif
+
+/* Abandon ship */
+# ifdef PCR
+#   define ABORT(s) PCR_Base_Panic(s)
+# else
+#   if defined(MSWINCE) && !defined(DebugBreak) \
+       && (!defined(UNDER_CE) || (defined(__MINGW32CE__) && !defined(ARM32)))
+      /* This simplifies linking for WinCE (and, probably, doesn't      */
+      /* hurt debugging much); use -DDebugBreak=DebugBreak to override  */
+      /* this behavior if really needed.  This is also a workaround for */
+      /* x86mingw32ce toolchain (if it is still declaring DebugBreak()  */
+      /* instead of defining it as a macro).                            */
+#     define DebugBreak() _exit(-1) /* there is no abort() in WinCE */
+#   endif
+#   ifdef SMALL_CONFIG
+#     define GC_on_abort(msg) (void)0 /* be silent on abort */
+#   else
+      GC_API_PRIV GC_abort_func GC_on_abort;
+#   endif /* !SMALL_CONFIG */
+#   if defined(MSWIN32) && (defined(NO_DEBUGGING) || defined(LINT2))
+      /* A more user-friendly abort after showing fatal message.        */
+#     define ABORT(msg) (GC_on_abort(msg), _exit(-1))
+                /* Exit on error without running "at-exit" callbacks.   */
+#   elif defined(MSWINCE) && defined(NO_DEBUGGING)
+#     define ABORT(msg) (GC_on_abort(msg), ExitProcess(-1))
+#   elif defined(MSWIN32) || defined(MSWINCE)
+#     define ABORT(msg) { GC_on_abort(msg); DebugBreak(); }
+                /* Note that: on a WinCE box, this could be silently    */
+                /* ignored (i.e., the program is not aborted);          */
+                /* DebugBreak is a statement in some toolchains.        */
+#   else
+#     define ABORT(msg) (GC_on_abort(msg), abort())
+#   endif /* !MSWIN32 */
+# endif /* !PCR */
+
+/* For abort message with 1-3 arguments.  C_msg and C_fmt should be     */
+/* literals.  C_msg should not contain format specifiers.  Arguments    */
+/* should match their format specifiers.                                */
+#define ABORT_ARG1(C_msg, C_fmt, arg1) \
+                do { \
+                  GC_COND_LOG_PRINTF(C_msg /* + */ C_fmt, arg1); \
+                  ABORT(C_msg); \
+                } while (0)
+#define ABORT_ARG2(C_msg, C_fmt, arg1, arg2) \
+                do { \
+                  GC_COND_LOG_PRINTF(C_msg /* + */ C_fmt, arg1, arg2); \
+                  ABORT(C_msg); \
+                } while (0)
+#define ABORT_ARG3(C_msg, C_fmt, arg1, arg2, arg3) \
+                do { \
+                  GC_COND_LOG_PRINTF(C_msg /* + */ C_fmt, arg1, arg2, arg3); \
+                  ABORT(C_msg); \
+                } while (0)
+
+/* Same as ABORT but does not have 'no-return' attribute.       */
+/* ABORT on a dummy condition (which is always true).           */
+#define ABORT_RET(msg) \
+              if ((signed_word)GC_current_warn_proc == -1) {} else ABORT(msg)
+
+/* Exit abnormally, but without making a mess (e.g. out of memory) */
+# ifdef PCR
+#   define EXIT() PCR_Base_Exit(1,PCR_waitForever)
+# else
+#   define EXIT() (GC_on_abort(NULL), exit(1 /* EXIT_FAILURE */))
+# endif
+
+/* Print warning message, e.g. almost out of memory.    */
+/* The argument (if any) format specifier should be:    */
+/* "%s", "%p" or "%"WARN_PRIdPTR.                       */
+#define WARN(msg, arg) (*GC_current_warn_proc)("GC Warning: " msg, \
+                                               (GC_word)(arg))
+GC_EXTERN GC_warn_proc GC_current_warn_proc;
+
+/* Print format type macro for decimal signed_word value passed WARN(). */
+/* This could be redefined for Win64 or LLP64, but typically should     */
+/* not be done as the WARN format string is, possibly, processed on the */
+/* client side, so non-standard print type modifiers (like MS "I64d")   */
+/* should be avoided here if possible.                                  */
+#ifndef WARN_PRIdPTR
+  /* Assume sizeof(void *) == sizeof(long) (or a little-endian machine) */
+# define WARN_PRIdPTR "ld"
+#endif
+
+/* Get environment entry */
+#ifdef GC_READ_ENV_FILE
+  GC_INNER char * GC_envfile_getenv(const char *name);
+# define GETENV(name) GC_envfile_getenv(name)
+#elif defined(NO_GETENV)
+# define GETENV(name) NULL
+#elif defined(EMPTY_GETENV_RESULTS)
+  /* Workaround for a reputed Wine bug.   */
+  GC_INLINE char * fixed_getenv(const char *name)
+  {
+    char *value = getenv(name);
+    return value != NULL && *value != '\0' ? value : NULL;
+  }
+# define GETENV(name) fixed_getenv(name)
+#else
+# define GETENV(name) getenv(name)
+#endif
+
+#if defined(DARWIN)
+# ifndef MAC_OS_X_VERSION_MAX_ALLOWED
+#   include <AvailabilityMacros.h>
+                /* Include this header just to import the above macro.  */
+# endif
+# if defined(POWERPC)
+#   if CPP_WORDSZ == 32
+#     define GC_THREAD_STATE_T          ppc_thread_state_t
+#     define GC_MACH_THREAD_STATE       PPC_THREAD_STATE
+#     define GC_MACH_THREAD_STATE_COUNT PPC_THREAD_STATE_COUNT
+#   else
+#     define GC_THREAD_STATE_T          ppc_thread_state64_t
+#     define GC_MACH_THREAD_STATE       PPC_THREAD_STATE64
+#     define GC_MACH_THREAD_STATE_COUNT PPC_THREAD_STATE64_COUNT
+#   endif
+# elif defined(I386) || defined(X86_64)
+#   if CPP_WORDSZ == 32
+#     if defined(i386_THREAD_STATE_COUNT) && !defined(x86_THREAD_STATE32_COUNT)
+        /* Use old naming convention for 32-bit x86.    */
+#       define GC_THREAD_STATE_T                i386_thread_state_t
+#       define GC_MACH_THREAD_STATE             i386_THREAD_STATE
+#       define GC_MACH_THREAD_STATE_COUNT       i386_THREAD_STATE_COUNT
+#     else
+#       define GC_THREAD_STATE_T                x86_thread_state32_t
+#       define GC_MACH_THREAD_STATE             x86_THREAD_STATE32
+#       define GC_MACH_THREAD_STATE_COUNT       x86_THREAD_STATE32_COUNT
+#     endif
+#   else
+#     define GC_THREAD_STATE_T          x86_thread_state64_t
+#     define GC_MACH_THREAD_STATE       x86_THREAD_STATE64
+#     define GC_MACH_THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT
+#   endif
+# else
+#   if defined(ARM32)
+#     define GC_THREAD_STATE_T                  arm_thread_state_t
+#     ifdef ARM_MACHINE_THREAD_STATE_COUNT
+#       define GC_MACH_THREAD_STATE             ARM_MACHINE_THREAD_STATE
+#       define GC_MACH_THREAD_STATE_COUNT       ARM_MACHINE_THREAD_STATE_COUNT
+#     endif
+#   else
+#     error define GC_THREAD_STATE_T
+#   endif
+# endif
+# ifndef GC_MACH_THREAD_STATE
+#   define GC_MACH_THREAD_STATE         MACHINE_THREAD_STATE
+#   define GC_MACH_THREAD_STATE_COUNT   MACHINE_THREAD_STATE_COUNT
+# endif
+
+# if CPP_WORDSZ == 32
+#   define GC_MACH_HEADER   mach_header
+#   define GC_MACH_SECTION  section
+#   define GC_GETSECTBYNAME getsectbynamefromheader
+# else
+#   define GC_MACH_HEADER   mach_header_64
+#   define GC_MACH_SECTION  section_64
+#   define GC_GETSECTBYNAME getsectbynamefromheader_64
+# endif
+
+  /* Try to work out the right way to access thread state structure     */
+  /* members.  The structure has changed its definition in different    */
+  /* Darwin versions.  This now defaults to the (older) names           */
+  /* without __, thus hopefully, not breaking any existing              */
+  /* Makefile.direct builds.                                            */
+# if __DARWIN_UNIX03
+#   define THREAD_FLD(x) __ ## x
+# else
+#   define THREAD_FLD(x) x
+# endif
+#endif /* DARWIN */
+
+/*********************************/
+/*                               */
+/* Word-size-dependent defines   */
+/*                               */
+/*********************************/
+
+#if CPP_WORDSZ == 32
+# define WORDS_TO_BYTES(x) ((x)<<2)
+# define BYTES_TO_WORDS(x) ((x)>>2)
+# define LOGWL             ((word)5) /* log[2] of CPP_WORDSZ    */
+# define modWORDSZ(n) ((n) & 0x1f) /* n mod size of word        */
+# if ALIGNMENT != 4
+#   define UNALIGNED_PTRS
+# endif
+#endif
+
+#if CPP_WORDSZ == 64
+#  define WORDS_TO_BYTES(x)   ((x)<<3)
+#  define BYTES_TO_WORDS(x)   ((x)>>3)
+#  define LOGWL               ((word)6)    /* log[2] of CPP_WORDSZ */
+#  define modWORDSZ(n) ((n) & 0x3f)        /* n mod size of word            */
+#  if ALIGNMENT != 8
+#       define UNALIGNED_PTRS
+#  endif
+#endif
+
+/* The first TINY_FREELISTS free lists correspond to the first  */
+/* TINY_FREELISTS multiples of GRANULE_BYTES, i.e. we keep      */
+/* separate free lists for each multiple of GRANULE_BYTES       */
+/* up to (TINY_FREELISTS-1) * GRANULE_BYTES.  After that they   */
+/* may be spread out further.                                   */
+#include "../gc_tiny_fl.h"
+#define GRANULE_BYTES GC_GRANULE_BYTES
+#define TINY_FREELISTS GC_TINY_FREELISTS
+
+#define WORDSZ ((word)CPP_WORDSZ)
+#define SIGNB  ((word)1 << (WORDSZ-1))
+#define BYTES_PER_WORD      ((word)(sizeof (word)))
+#define divWORDSZ(n) ((n) >> LOGWL)     /* divide n by size of word */
+
+#if GRANULE_BYTES == 8
+# define BYTES_TO_GRANULES(n) ((n)>>3)
+# define GRANULES_TO_BYTES(n) ((n)<<3)
+# if CPP_WORDSZ == 64
+#   define GRANULES_TO_WORDS(n) (n)
+# elif CPP_WORDSZ == 32
+#   define GRANULES_TO_WORDS(n) ((n)<<1)
+# else
+#   define GRANULES_TO_WORDS(n) BYTES_TO_WORDS(GRANULES_TO_BYTES(n))
+# endif
+#elif GRANULE_BYTES == 16
+# define BYTES_TO_GRANULES(n) ((n)>>4)
+# define GRANULES_TO_BYTES(n) ((n)<<4)
+# if CPP_WORDSZ == 64
+#   define GRANULES_TO_WORDS(n) ((n)<<1)
+# elif CPP_WORDSZ == 32
+#   define GRANULES_TO_WORDS(n) ((n)<<2)
+# else
+#   define GRANULES_TO_WORDS(n) BYTES_TO_WORDS(GRANULES_TO_BYTES(n))
+# endif
+#else
+# error Bad GRANULE_BYTES value
+#endif
+
+/*********************/
+/*                   */
+/*  Size Parameters  */
+/*                   */
+/*********************/
+
+/* Heap block size, bytes. Should be power of 2.                */
+/* Incremental GC with MPROTECT_VDB currently requires the      */
+/* page size to be a multiple of HBLKSIZE.  Since most modern   */
+/* architectures support variable page sizes down to 4K, and    */
+/* X86 is generally 4K, we now default to 4K, except for        */
+/*   Alpha: Seems to be used with 8K pages.                     */
+/*   SMALL_CONFIG: Want less block-level fragmentation.         */
+#ifndef HBLKSIZE
+# if defined(LARGE_CONFIG) || !defined(SMALL_CONFIG)
+#   ifdef ALPHA
+#     define CPP_LOG_HBLKSIZE 13
+#   else
+#     define CPP_LOG_HBLKSIZE 12
+#   endif
+# else
+#   define CPP_LOG_HBLKSIZE 10
+# endif
+#else
+# if HBLKSIZE == 512
+#   define CPP_LOG_HBLKSIZE 9
+# elif HBLKSIZE == 1024
+#   define CPP_LOG_HBLKSIZE 10
+# elif HBLKSIZE == 2048
+#   define CPP_LOG_HBLKSIZE 11
+# elif HBLKSIZE == 4096
+#   define CPP_LOG_HBLKSIZE 12
+# elif HBLKSIZE == 8192
+#   define CPP_LOG_HBLKSIZE 13
+# elif HBLKSIZE == 16384
+#   define CPP_LOG_HBLKSIZE 14
+# else
+    --> fix HBLKSIZE
+# endif
+# undef HBLKSIZE
+#endif
+
+# define CPP_HBLKSIZE (1 << CPP_LOG_HBLKSIZE)
+# define LOG_HBLKSIZE   ((size_t)CPP_LOG_HBLKSIZE)
+# define HBLKSIZE ((size_t)CPP_HBLKSIZE)
+
+
+/*  max size objects supported by freelist (larger objects are  */
+/*  allocated directly with allchblk(), by rounding to the next */
+/*  multiple of HBLKSIZE.                                       */
+
+#define CPP_MAXOBJBYTES (CPP_HBLKSIZE/2)
+#define MAXOBJBYTES ((size_t)CPP_MAXOBJBYTES)
+#define CPP_MAXOBJWORDS BYTES_TO_WORDS(CPP_MAXOBJBYTES)
+#define MAXOBJWORDS ((size_t)CPP_MAXOBJWORDS)
+#define CPP_MAXOBJGRANULES BYTES_TO_GRANULES(CPP_MAXOBJBYTES)
+#define MAXOBJGRANULES ((size_t)CPP_MAXOBJGRANULES)
+
+# define divHBLKSZ(n) ((n) >> LOG_HBLKSIZE)
+
+# define HBLK_PTR_DIFF(p,q) divHBLKSZ((ptr_t)p - (ptr_t)q)
+        /* Equivalent to subtracting 2 hblk pointers.   */
+        /* We do it this way because a compiler should  */
+        /* find it hard to use an integer division      */
+        /* instead of a shift.  The bundled SunOS 4.1   */
+        /* o.w. sometimes pessimizes the subtraction to */
+        /* involve a call to .div.                      */
+
+# define modHBLKSZ(n) ((n) & (HBLKSIZE-1))
+
+# define HBLKPTR(objptr) ((struct hblk *)(((word) (objptr)) & ~(HBLKSIZE-1)))
+
+# define HBLKDISPL(objptr) (((size_t) (objptr)) & (HBLKSIZE-1))
+
+/* Round up byte allocation requests to integral number of words, etc. */
+# define ROUNDED_UP_GRANULES(n) \
+        BYTES_TO_GRANULES((n) + (GRANULE_BYTES - 1 + EXTRA_BYTES))
+# if MAX_EXTRA_BYTES == 0
+#  define SMALL_OBJ(bytes) EXPECT((bytes) <= (MAXOBJBYTES), TRUE)
+# else
+#  define SMALL_OBJ(bytes) \
+            (EXPECT((bytes) <= (MAXOBJBYTES - MAX_EXTRA_BYTES), TRUE) \
+             || (bytes) <= MAXOBJBYTES - EXTRA_BYTES)
+        /* This really just tests bytes <= MAXOBJBYTES - EXTRA_BYTES.   */
+        /* But we try to avoid looking up EXTRA_BYTES.                  */
+# endif
+# define ADD_SLOP(bytes) ((bytes) + EXTRA_BYTES)
+# ifndef MIN_WORDS
+#  define MIN_WORDS 2   /* FIXME: obsolete */
+# endif
+
+/*
+ * Hash table representation of sets of pages.
+ * Implements a map from aligned HBLKSIZE chunks of the address space to one
+ * bit each.
+ * This assumes it is OK to spuriously set bits, e.g. because multiple
+ * addresses are represented by a single location.
+ * Used by black-listing code, and perhaps by dirty bit maintenance code.
+ */
+
+# ifdef LARGE_CONFIG
+#   if CPP_WORDSZ == 32
+#     define LOG_PHT_ENTRIES 20 /* Collisions likely at 1M blocks,      */
+                                /* which is >= 4GB.  Each table takes   */
+                                /* 128KB, some of which may never be    */
+                                /* touched.                             */
+#   else
+#     define LOG_PHT_ENTRIES 21 /* Collisions likely at 2M blocks,      */
+                                /* which is >= 8GB.  Each table takes   */
+                                /* 256KB, some of which may never be    */
+                                /* touched.                             */
+#   endif
+# elif !defined(SMALL_CONFIG)
+#   define LOG_PHT_ENTRIES  18   /* Collisions are likely if heap grows */
+                                 /* to more than 256K hblks >= 1GB.     */
+                                 /* Each hash table occupies 32K bytes. */
+                                 /* Even for somewhat smaller heaps,    */
+                                 /* say half that, collisions may be an */
+                                 /* issue because we blacklist          */
+                                 /* addresses outside the heap.         */
+# else
+#   define LOG_PHT_ENTRIES  15   /* Collisions are likely if heap grows */
+                                 /* to more than 32K hblks = 128MB.     */
+                                 /* Each hash table occupies 4K bytes.  */
+# endif
+# define PHT_ENTRIES ((word)1 << LOG_PHT_ENTRIES)
+# define PHT_SIZE (PHT_ENTRIES >> LOGWL)
+typedef word page_hash_table[PHT_SIZE];
+
+# define PHT_HASH(addr) ((((word)(addr)) >> LOG_HBLKSIZE) & (PHT_ENTRIES - 1))
+
+# define get_pht_entry_from_index(bl, index) \
+                (((bl)[divWORDSZ(index)] >> modWORDSZ(index)) & 1)
+# define set_pht_entry_from_index(bl, index) \
+                (bl)[divWORDSZ(index)] |= (word)1 << modWORDSZ(index)
+# define clear_pht_entry_from_index(bl, index) \
+                (bl)[divWORDSZ(index)] &= ~((word)1 << modWORDSZ(index))
+/* And a dumb but thread-safe version of set_pht_entry_from_index.      */
+/* This sets (many) extra bits.                                         */
+# define set_pht_entry_from_index_safe(bl, index) \
+                (bl)[divWORDSZ(index)] = ONES
+
+
+/********************************************/
+/*                                          */
+/*    H e a p   B l o c k s                 */
+/*                                          */
+/********************************************/
+
+/*  heap block header */
+#define HBLKMASK   (HBLKSIZE-1)
+
+#define MARK_BITS_PER_HBLK (HBLKSIZE/GRANULE_BYTES)
+           /* upper bound                                    */
+           /* We allocate 1 bit per allocation granule.      */
+           /* If MARK_BIT_PER_GRANULE is defined, we use     */
+           /* every nth bit, where n is the number of        */
+           /* allocation granules per object.  If            */
+           /* MARK_BIT_PER_OBJ is defined, we only use the   */
+           /* initial group of mark bits, and it is safe     */
+           /* to allocate smaller header for large objects.  */
+
+#ifdef PARALLEL_MARK
+# include "atomic_ops.h"
+# define counter_t volatile AO_t
+#else
+  typedef size_t counter_t;
+# if defined(THREADS) && (defined(MPROTECT_VDB) \
+                || (defined(GC_ASSERTIONS) && defined(THREAD_LOCAL_ALLOC)))
+#   include "atomic_ops.h"
+# endif
+#endif /* !PARALLEL_MARK */
+
+union word_ptr_ao_u {
+  word w;
+  signed_word sw;
+  void *vp;
+# ifdef AO_HAVE_load
+    volatile AO_t ao;
+# endif
+};
+
+/* We maintain layout maps for heap blocks containing objects of a given */
+/* size.  Each entry in this map describes a byte offset and has the     */
+/* following type.                                                       */
+struct hblkhdr {
+    struct hblk * hb_next;      /* Link field for hblk free list         */
+                                /* and for lists of chunks waiting to be */
+                                /* reclaimed.                            */
+    struct hblk * hb_prev;      /* Backwards link for free list.        */
+    struct hblk * hb_block;     /* The corresponding block.             */
+    unsigned char hb_obj_kind;
+                         /* Kind of objects in the block.  Each kind    */
+                         /* identifies a mark procedure and a set of    */
+                         /* list headers.  Sometimes called regions.    */
+    unsigned char hb_flags;
+#       define IGNORE_OFF_PAGE  1       /* Ignore pointers that do not  */
+                                        /* point to the first page of   */
+                                        /* this object.                 */
+#       define WAS_UNMAPPED 2   /* This is a free block, which has      */
+                                /* been unmapped from the address       */
+                                /* space.                               */
+                                /* GC_remap must be invoked on it       */
+                                /* before it can be reallocated.        */
+                                /* Only set with USE_MUNMAP.            */
+#       define FREE_BLK 4       /* Block is free, i.e. not in use.      */
+#       ifdef ENABLE_DISCLAIM
+#         define HAS_DISCLAIM 8
+                                /* This kind has a callback on reclaim. */
+#         define MARK_UNCONDITIONALLY 0x10
+                                /* Mark from all objects, marked or     */
+                                /* not.  Used to mark objects needed by */
+                                /* reclaim notifier.                    */
+#       endif
+    unsigned short hb_last_reclaimed;
+                                /* Value of GC_gc_no when block was     */
+                                /* last allocated or swept. May wrap.   */
+                                /* For a free block, this is maintained */
+                                /* only for USE_MUNMAP, and indicates   */
+                                /* when the header was allocated, or    */
+                                /* when the size of the block last      */
+                                /* changed.                             */
+    size_t hb_sz;  /* If in use, size in bytes, of objects in the block. */
+                   /* if free, the size in bytes of the whole block      */
+                   /* We assume that this is convertible to signed_word  */
+                   /* without generating a negative result.  We avoid    */
+                   /* generating free blocks larger than that.           */
+    word hb_descr;              /* object descriptor for marking.  See  */
+                                /* mark.h.                              */
+#   ifdef MARK_BIT_PER_OBJ
+      unsigned32 hb_inv_sz;     /* A good upper bound for 2**32/hb_sz.  */
+                                /* For large objects, we use            */
+                                /* LARGE_INV_SZ.                        */
+#     define LARGE_INV_SZ (1 << 16)
+#   else
+      unsigned char hb_large_block;
+      short * hb_map;           /* Essentially a table of remainders    */
+                                /* mod BYTES_TO_GRANULES(hb_sz), except */
+                                /* for large blocks.  See GC_obj_map.   */
+#   endif
+    counter_t hb_n_marks;       /* Number of set mark bits, excluding   */
+                                /* the one always set at the end.       */
+                                /* Currently it is concurrently         */
+                                /* updated and hence only approximate.  */
+                                /* But a zero value does guarantee that */
+                                /* the block contains no marked         */
+                                /* objects.                             */
+                                /* Ensuring this property means that we */
+                                /* never decrement it to zero during a  */
+                                /* collection, and hence the count may  */
+                                /* be one too high.  Due to concurrent  */
+                                /* updates, an arbitrary number of      */
+                                /* increments, but not all of them (!)  */
+                                /* may be lost, hence it may in theory  */
+                                /* be much too low.                     */
+                                /* The count may also be too high if    */
+                                /* multiple mark threads mark the       */
+                                /* same object due to a race.           */
+                                /* Without parallel marking, the count  */
+                                /* is accurate.                         */
+#   ifdef USE_MARK_BYTES
+#     define MARK_BITS_SZ (MARK_BITS_PER_HBLK + 1)
+        /* Unlike the other case, this is in units of bytes.            */
+        /* Since we force double-word alignment, we need at most one    */
+        /* mark bit per 2 words.  But we do allocate and set one        */
+        /* extra mark bit to avoid an explicit check for the            */
+        /* partial object at the end of each block.                     */
+      union {
+        char _hb_marks[MARK_BITS_SZ];
+                            /* The i'th byte is 1 if the object         */
+                            /* starting at granule i or object i is     */
+                            /* marked, 0 o.w.                           */
+                            /* The mark bit for the "one past the       */
+                            /* end" object is always set to avoid a     */
+                            /* special case test in the marker.         */
+        word dummy;     /* Force word alignment of mark bytes. */
+      } _mark_byte_union;
+#     define hb_marks _mark_byte_union._hb_marks
+#   else
+#     define MARK_BITS_SZ (MARK_BITS_PER_HBLK/CPP_WORDSZ + 1)
+      word hb_marks[MARK_BITS_SZ];
+#   endif /* !USE_MARK_BYTES */
+};
+
+# define ANY_INDEX 23   /* "Random" mark bit index for assertions */
+
+/*  heap block body */
+
+# define HBLK_WORDS (HBLKSIZE/sizeof(word))
+# define HBLK_GRANULES (HBLKSIZE/GRANULE_BYTES)
+
+/* The number of objects in a block dedicated to a certain size.        */
+/* may erroneously yield zero (instead of one) for large objects.       */
+# define HBLK_OBJS(sz_in_bytes) (HBLKSIZE/(sz_in_bytes))
+
+struct hblk {
+    char hb_body[HBLKSIZE];
+};
+
+# define HBLK_IS_FREE(hdr) (((hdr) -> hb_flags & FREE_BLK) != 0)
+
+# define OBJ_SZ_TO_BLOCKS(sz) divHBLKSZ((sz) + HBLKSIZE-1)
+    /* Size of block (in units of HBLKSIZE) needed to hold objects of   */
+    /* given sz (in bytes).                                             */
+
+/* Object free list link */
+# define obj_link(p) (*(void  **)(p))
+
+# define LOG_MAX_MARK_PROCS 6
+# define MAX_MARK_PROCS (1 << LOG_MAX_MARK_PROCS)
+
+/* Root sets.  Logically private to mark_rts.c.  But we don't want the  */
+/* tables scanned, so we put them here.                                 */
+/* MAX_ROOT_SETS is the maximum number of ranges that can be    */
+/* registered as static roots.                                  */
+# ifdef LARGE_CONFIG
+#   define MAX_ROOT_SETS 8192
+# elif !defined(SMALL_CONFIG)
+#   define MAX_ROOT_SETS 2048
+# else
+#   define MAX_ROOT_SETS 512
+# endif
+
+# define MAX_EXCLUSIONS (MAX_ROOT_SETS/4)
+/* Maximum number of segments that can be excluded from root sets.      */
+
+/*
+ * Data structure for excluded static roots.
+ */
+struct exclusion {
+    ptr_t e_start;
+    ptr_t e_end;
+};
+
+/* Data structure for list of root sets.                                */
+/* We keep a hash table, so that we can filter out duplicate additions. */
+/* Under Win32, we need to do a better job of filtering overlaps, so    */
+/* we resort to sequential search, and pay the price.                   */
+struct roots {
+        ptr_t r_start;/* multiple of word size */
+        ptr_t r_end;  /* multiple of word size and greater than r_start */
+#       if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32)
+          struct roots * r_next;
+#       endif
+        GC_bool r_tmp;
+                /* Delete before registering new dynamic libraries */
+};
+
+#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32)
+    /* Size of hash table index to roots.       */
+#   define LOG_RT_SIZE 6
+#   define RT_SIZE (1 << LOG_RT_SIZE) /* Power of 2, may be != MAX_ROOT_SETS */
+#endif
+
+#ifndef MAX_HEAP_SECTS
+# ifdef LARGE_CONFIG
+#   if CPP_WORDSZ > 32
+#     define MAX_HEAP_SECTS 8192        /* overflows at roughly 128 GB  */
+#   else
+#     define MAX_HEAP_SECTS 768         /* Separately added heap sections. */
+#   endif
+# elif defined(SMALL_CONFIG) && !defined(USE_PROC_FOR_LIBRARIES)
+#   if defined(PARALLEL_MARK) && (defined(MSWIN32) || defined(CYGWIN32))
+#     define MAX_HEAP_SECTS 384
+#   else
+#     define MAX_HEAP_SECTS 128         /* Roughly 256MB (128*2048*1K)  */
+#   endif
+# elif CPP_WORDSZ > 32
+#   define MAX_HEAP_SECTS 1024          /* Roughly 8GB                  */
+# else
+#   define MAX_HEAP_SECTS 512           /* Roughly 4GB                  */
+# endif
+#endif /* !MAX_HEAP_SECTS */
+
+typedef struct GC_ms_entry {
+    ptr_t mse_start;    /* First word of object, word aligned.  */
+    union word_ptr_ao_u mse_descr;
+                        /* Descriptor; low order two bits are tags,     */
+                        /* as described in gc_mark.h.                   */
+} mse;
+
+/* Lists of all heap blocks and free lists      */
+/* as well as other random data structures      */
+/* that should not be scanned by the            */
+/* collector.                                   */
+/* These are grouped together in a struct       */
+/* so that they can be easily skipped by the    */
+/* GC_mark routine.                             */
+/* The ordering is weird to make GC_malloc      */
+/* faster by keeping the important fields       */
+/* sufficiently close together that a           */
+/* single load of a base register will do.      */
+/* Scalars that could easily appear to          */
+/* be pointers are also put here.               */
+/* The main fields should precede any           */
+/* conditionally included fields, so that       */
+/* gc_inl.h will work even if a different set   */
+/* of macros is defined when the client is      */
+/* compiled.                                    */
+
+struct _GC_arrays {
+  word _heapsize;               /* Heap size in bytes.                  */
+  word _requested_heapsize;     /* Heap size due to explicit expansion. */
+  ptr_t _last_heap_addr;
+  ptr_t _prev_heap_addr;
+  word _large_free_bytes;
+        /* Total bytes contained in blocks on large object free */
+        /* list.                                                */
+  word _large_allocd_bytes;
+        /* Total number of bytes in allocated large objects blocks.     */
+        /* For the purposes of this counter and the next one only, a    */
+        /* large object is one that occupies a block of at least        */
+        /* 2*HBLKSIZE.                                                  */
+  word _max_large_allocd_bytes;
+        /* Maximum number of bytes that were ever allocated in          */
+        /* large object blocks.  This is used to help decide when it    */
+        /* is safe to split up a large block.                           */
+  word _bytes_allocd_before_gc;
+                /* Number of bytes allocated before this        */
+                /* collection cycle.                            */
+# ifndef SEPARATE_GLOBALS
+#   define GC_bytes_allocd GC_arrays._bytes_allocd
+    word _bytes_allocd;
+        /* Number of bytes allocated during this collection cycle.      */
+# endif
+  word _bytes_dropped;
+        /* Number of black-listed bytes dropped during GC cycle */
+        /* as a result of repeated scanning during allocation   */
+        /* attempts.  These are treated largely as allocated,   */
+        /* even though they are not useful to the client.       */
+  word _bytes_finalized;
+        /* Approximate number of bytes in objects (and headers) */
+        /* that became ready for finalization in the last       */
+        /* collection.                                          */
+  word _bytes_freed;
+        /* Number of explicitly deallocated bytes of memory     */
+        /* since last collection.                               */
+  word _finalizer_bytes_freed;
+        /* Bytes of memory explicitly deallocated while         */
+        /* finalizers were running.  Used to approximate mem.   */
+        /* explicitly deallocated by finalizers.                */
+  ptr_t _scratch_end_ptr;
+  ptr_t _scratch_last_end_ptr;
+        /* Used by headers.c, and can easily appear to point to */
+        /* heap.                                                */
+  mse *_mark_stack;
+        /* Limits of stack for GC_mark routine.  All ranges     */
+        /* between GC_mark_stack (incl.) and GC_mark_stack_top  */
+        /* (incl.) still need to be marked from.                */
+  mse *_mark_stack_limit;
+# ifdef PARALLEL_MARK
+    mse *volatile _mark_stack_top;
+        /* Updated only with mark lock held, but read asynchronously.   */
+        /* TODO: Use union to avoid casts to AO_t */
+# else
+    mse *_mark_stack_top;
+# endif
+  GC_mark_proc _mark_procs[MAX_MARK_PROCS];
+        /* Table of user-defined mark procedures.  There is     */
+        /* a small number of these, which can be referenced     */
+        /* by DS_PROC mark descriptors.  See gc_mark.h.         */
+# ifndef SEPARATE_GLOBALS
+#   define GC_objfreelist GC_arrays._objfreelist
+    void *_objfreelist[MAXOBJGRANULES+1];
+                          /* free list for objects */
+#   define GC_aobjfreelist GC_arrays._aobjfreelist
+    void *_aobjfreelist[MAXOBJGRANULES+1];
+                          /* free list for atomic objs  */
+# endif
+  void *_uobjfreelist[MAXOBJGRANULES+1];
+                          /* Uncollectible but traced objs      */
+                          /* objects on this and auobjfreelist  */
+                          /* are always marked, except during   */
+                          /* garbage collections.               */
+# ifdef ATOMIC_UNCOLLECTABLE
+#   define GC_auobjfreelist GC_arrays._auobjfreelist
+    void *_auobjfreelist[MAXOBJGRANULES+1];
+                        /* Atomic uncollectible but traced objs */
+# endif
+  word _composite_in_use; /* Number of bytes in the accessible  */
+                          /* composite objects.                 */
+  word _atomic_in_use;    /* Number of bytes in the accessible  */
+                          /* atomic objects.                    */
+# ifdef USE_MUNMAP
+#   define GC_unmapped_bytes GC_arrays._unmapped_bytes
+    word _unmapped_bytes;
+# else
+#   define GC_unmapped_bytes 0
+# endif
+  size_t _size_map[MAXOBJBYTES+1];
+        /* Number of granules to allocate when asked for a certain      */
+        /* number of bytes.                                             */
+
+# ifdef STUBBORN_ALLOC
+#   define GC_sobjfreelist GC_arrays._sobjfreelist
+    ptr_t _sobjfreelist[MAXOBJGRANULES+1];
+# endif
+                          /* free list for immutable objects    */
+# ifdef MARK_BIT_PER_GRANULE
+#   define GC_obj_map GC_arrays._obj_map
+    short * _obj_map[MAXOBJGRANULES+1];
+                       /* If not NULL, then a pointer to a map of valid */
+                       /* object addresses.                             */
+                       /* _obj_map[sz_in_granules][i] is                */
+                       /* i % sz_in_granules.                           */
+                       /* This is now used purely to replace a          */
+                       /* division in the marker by a table lookup.     */
+                       /* _obj_map[0] is used for large objects and     */
+                       /* contains all nonzero entries.  This gets us   */
+                       /* out of the marker fast path without an extra  */
+                       /* test.                                         */
+#   define MAP_LEN BYTES_TO_GRANULES(HBLKSIZE)
+# endif
+# define VALID_OFFSET_SZ HBLKSIZE
+  char _valid_offsets[VALID_OFFSET_SZ];
+                                /* GC_valid_offsets[i] == TRUE ==> i    */
+                                /* is registered as a displacement.     */
+  char _modws_valid_offsets[sizeof(word)];
+                                /* GC_valid_offsets[i] ==>                */
+                                /* GC_modws_valid_offsets[i%sizeof(word)] */
+# ifdef STUBBORN_ALLOC
+#   define GC_changed_pages GC_arrays._changed_pages
+    page_hash_table _changed_pages;
+        /* Stubborn object pages that were changes since last call to   */
+        /* GC_read_changed.                                             */
+#   define GC_prev_changed_pages GC_arrays._prev_changed_pages
+    page_hash_table _prev_changed_pages;
+        /* Stubborn object pages that were changes before last call to  */
+        /* GC_read_changed.                                             */
+# endif
+# if defined(PROC_VDB) || defined(MPROTECT_VDB) \
+     || defined(GWW_VDB) || defined(MANUAL_VDB)
+#   define GC_grungy_pages GC_arrays._grungy_pages
+    page_hash_table _grungy_pages; /* Pages that were dirty at last     */
+                                   /* GC_read_dirty.                    */
+# endif
+# if defined(MPROTECT_VDB) || defined(MANUAL_VDB)
+#   define GC_dirty_pages GC_arrays._dirty_pages
+    volatile page_hash_table _dirty_pages;
+                        /* Pages dirtied since last GC_read_dirty. */
+# endif
+# if defined(PROC_VDB) || defined(GWW_VDB)
+#   define GC_written_pages GC_arrays._written_pages
+    page_hash_table _written_pages;     /* Pages ever dirtied   */
+# endif
+# define GC_heap_sects GC_arrays._heap_sects
+  struct HeapSect {
+    ptr_t hs_start;
+    size_t hs_bytes;
+  } _heap_sects[MAX_HEAP_SECTS];        /* Heap segments potentially    */
+                                        /* client objects.              */
+# if defined(USE_PROC_FOR_LIBRARIES)
+#   define GC_our_memory GC_arrays._our_memory
+    struct HeapSect _our_memory[MAX_HEAP_SECTS];
+                                        /* All GET_MEM allocated        */
+                                        /* memory.  Includes block      */
+                                        /* headers and the like.        */
+# endif
+# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32)
+#   define GC_heap_bases GC_arrays._heap_bases
+    ptr_t _heap_bases[MAX_HEAP_SECTS];
+                /* Start address of memory regions obtained from kernel. */
+# endif
+# ifdef MSWINCE
+#   define GC_heap_lengths GC_arrays._heap_lengths
+    word _heap_lengths[MAX_HEAP_SECTS];
+                /* Committed lengths of memory regions obtained from kernel. */
+# endif
+  struct roots _static_roots[MAX_ROOT_SETS];
+# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32)
+#   define GC_root_index GC_arrays._root_index
+    struct roots * _root_index[RT_SIZE];
+# endif
+  struct exclusion _excl_table[MAX_EXCLUSIONS];
+  /* Block header index; see gc_headers.h */
+  bottom_index * _all_nils;
+  bottom_index * _top_index [TOP_SZ];
+# ifdef ENABLE_TRACE
+#   define GC_trace_addr GC_arrays._trace_addr
+    ptr_t _trace_addr;
+# endif
+# ifdef SAVE_CALL_CHAIN
+#   define GC_last_stack GC_arrays._last_stack
+    struct callinfo _last_stack[NFRAMES];
+                /* Stack at last garbage collection.  Useful for        */
+                /* debugging mysterious object disappearances.  In the  */
+                /* multi-threaded case, we currently only save the      */
+                /* calling stack.                                       */
+# endif
+};
+
+GC_API_PRIV GC_FAR struct _GC_arrays GC_arrays;
+
+#define GC_all_nils GC_arrays._all_nils
+#define GC_atomic_in_use GC_arrays._atomic_in_use
+#define GC_bytes_allocd_before_gc GC_arrays._bytes_allocd_before_gc
+#define GC_bytes_dropped GC_arrays._bytes_dropped
+#define GC_bytes_finalized GC_arrays._bytes_finalized
+#define GC_bytes_freed GC_arrays._bytes_freed
+#define GC_composite_in_use GC_arrays._composite_in_use
+#define GC_excl_table GC_arrays._excl_table
+#define GC_finalizer_bytes_freed GC_arrays._finalizer_bytes_freed
+#define GC_heapsize GC_arrays._heapsize
+#define GC_large_allocd_bytes GC_arrays._large_allocd_bytes
+#define GC_large_free_bytes GC_arrays._large_free_bytes
+#define GC_last_heap_addr GC_arrays._last_heap_addr
+#define GC_mark_stack GC_arrays._mark_stack
+#define GC_mark_stack_limit GC_arrays._mark_stack_limit
+#define GC_mark_stack_top GC_arrays._mark_stack_top
+#define GC_mark_procs GC_arrays._mark_procs
+#define GC_max_large_allocd_bytes GC_arrays._max_large_allocd_bytes
+#define GC_modws_valid_offsets GC_arrays._modws_valid_offsets
+#define GC_prev_heap_addr GC_arrays._prev_heap_addr
+#define GC_requested_heapsize GC_arrays._requested_heapsize
+#define GC_scratch_end_ptr GC_arrays._scratch_end_ptr
+#define GC_scratch_last_end_ptr GC_arrays._scratch_last_end_ptr
+#define GC_size_map GC_arrays._size_map
+#define GC_static_roots GC_arrays._static_roots
+#define GC_top_index GC_arrays._top_index
+#define GC_uobjfreelist GC_arrays._uobjfreelist
+#define GC_valid_offsets GC_arrays._valid_offsets
+
+#define beginGC_arrays ((ptr_t)(&GC_arrays))
+#define endGC_arrays (((ptr_t)(&GC_arrays)) + (sizeof GC_arrays))
+#define USED_HEAP_SIZE (GC_heapsize - GC_large_free_bytes)
+
+/* Object kinds: */
+#define MAXOBJKINDS 16
+
+GC_EXTERN struct obj_kind {
+   void **ok_freelist;  /* Array of free listheaders for this kind of object */
+                        /* Point either to GC_arrays or to storage allocated */
+                        /* with GC_scratch_alloc.                            */
+   struct hblk **ok_reclaim_list;
+                        /* List headers for lists of blocks waiting to be */
+                        /* swept.                                         */
+                        /* Indexed by object size in granules.            */
+   word ok_descriptor;  /* Descriptor template for objects in this      */
+                        /* block.                                       */
+   GC_bool ok_relocate_descr;
+                        /* Add object size in bytes to descriptor       */
+                        /* template to obtain descriptor.  Otherwise    */
+                        /* template is used as is.                      */
+   GC_bool ok_init;   /* Clear objects before putting them on the free list. */
+#  ifdef ENABLE_DISCLAIM
+     GC_bool ok_mark_unconditionally;
+                        /* Mark from all, including unmarked, objects   */
+                        /* in block.  Used to protect objects reachable */
+                        /* from reclaim notifiers.                      */
+     int (GC_CALLBACK *ok_disclaim_proc)(void * /*obj*/);
+                        /* The disclaim procedure is called before obj  */
+                        /* is reclaimed, but must also tolerate being   */
+                        /* called with object from freelist.  Non-zero  */
+                        /* exit prevents object from being reclaimed.   */
+#    define OK_DISCLAIM_INITZ /* comma */, FALSE, 0
+#  else
+#    define OK_DISCLAIM_INITZ /* empty */
+#  endif /* !ENABLE_DISCLAIM */
+} GC_obj_kinds[MAXOBJKINDS];
+
+#define beginGC_obj_kinds ((ptr_t)(&GC_obj_kinds))
+#define endGC_obj_kinds (beginGC_obj_kinds + (sizeof GC_obj_kinds))
+
+/* Variables that used to be in GC_arrays, but need to be accessed by   */
+/* inline allocation code.  If they were in GC_arrays, the inlined      */
+/* allocation code would include GC_arrays offsets (as it did), which   */
+/* introduce maintenance problems.                                      */
+
+#ifdef SEPARATE_GLOBALS
+  extern word GC_bytes_allocd;
+        /* Number of bytes allocated during this collection cycle.      */
+  extern ptr_t GC_objfreelist[MAXOBJGRANULES+1];
+                          /* free list for NORMAL objects */
+# define beginGC_objfreelist ((ptr_t)(&GC_objfreelist))
+# define endGC_objfreelist (beginGC_objfreelist + sizeof(GC_objfreelist))
+
+  extern ptr_t GC_aobjfreelist[MAXOBJGRANULES+1];
+                          /* free list for atomic (PTRFREE) objs        */
+# define beginGC_aobjfreelist ((ptr_t)(&GC_aobjfreelist))
+# define endGC_aobjfreelist (beginGC_aobjfreelist + sizeof(GC_aobjfreelist))
+#endif /* SEPARATE_GLOBALS */
+
+/* Predefined kinds: */
+#define PTRFREE 0
+#define NORMAL  1
+#define UNCOLLECTABLE 2
+#ifdef ATOMIC_UNCOLLECTABLE
+# define AUNCOLLECTABLE 3
+# define STUBBORN 4
+# define IS_UNCOLLECTABLE(k) (((k) & ~1) == UNCOLLECTABLE)
+#else
+# define STUBBORN 3
+# define IS_UNCOLLECTABLE(k) ((k) == UNCOLLECTABLE)
+#endif
+
+GC_EXTERN unsigned GC_n_kinds;
+
+GC_EXTERN word GC_n_heap_sects; /* Number of separately added heap      */
+                                /* sections.                            */
+
+#ifdef USE_PROC_FOR_LIBRARIES
+  GC_EXTERN word GC_n_memory;   /* Number of GET_MEM allocated memory   */
+                                /* sections.                            */
+#endif
+
+GC_EXTERN word GC_page_size;
+
+#if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32)
+  struct _SYSTEM_INFO;
+  GC_EXTERN struct _SYSTEM_INFO GC_sysinfo;
+  GC_INNER GC_bool GC_is_heap_base(ptr_t p);
+#endif
+
+
+GC_EXTERN word GC_black_list_spacing;
+                        /* Average number of bytes between blacklisted  */
+                        /* blocks. Approximate.                         */
+                        /* Counts only blocks that are                  */
+                        /* "stack-blacklisted", i.e. that are           */
+                        /* problematic in the interior of an object.    */
+
+#ifdef GC_GCJ_SUPPORT
+  extern struct hblk * GC_hblkfreelist[];
+                                        /* Remains visible to GNU GCJ. */
+#endif
+
+#ifdef GC_DISABLE_INCREMENTAL
+# define GC_incremental FALSE
+                        /* Hopefully allow optimizer to remove some code. */
+# define TRUE_INCREMENTAL FALSE
+#else
+  GC_EXTERN GC_bool GC_incremental;
+                        /* Using incremental/generational collection. */
+# define TRUE_INCREMENTAL \
+        (GC_incremental && GC_time_limit != GC_TIME_UNLIMITED)
+        /* True incremental, not just generational, mode */
+#endif /* !GC_DISABLE_INCREMENTAL */
+
+GC_EXTERN word GC_root_size; /* Total size of registered root sections. */
+
+GC_EXTERN GC_bool GC_debugging_started;
+                                /* GC_debug_malloc has been called.     */
+
+/* This is used by GC_do_blocking[_inner]().            */
+struct blocking_data {
+    GC_fn_type fn;
+    void * client_data; /* and result */
+};
+
+/* This is used by GC_call_with_gc_active(), GC_push_all_stack_sections(). */
+struct GC_traced_stack_sect_s {
+  ptr_t saved_stack_ptr;
+# ifdef IA64
+    ptr_t saved_backing_store_ptr;
+    ptr_t backing_store_end;
+# endif
+  struct GC_traced_stack_sect_s *prev;
+};
+
+#ifdef THREADS
+  /* Process all "traced stack sections" - scan entire stack except for */
+  /* frames belonging to the user functions invoked by GC_do_blocking.  */
+  GC_INNER void GC_push_all_stack_sections(ptr_t lo, ptr_t hi,
+                        struct GC_traced_stack_sect_s *traced_stack_sect);
+  GC_EXTERN word GC_total_stacksize; /* updated on every push_all_stacks */
+#else
+  GC_EXTERN ptr_t GC_blocked_sp;
+  GC_EXTERN struct GC_traced_stack_sect_s *GC_traced_stack_sect;
+                        /* Points to the "frame" data held in stack by  */
+                        /* the innermost GC_call_with_gc_active().      */
+                        /* NULL if no such "frame" active.              */
+#endif /* !THREADS */
+
+#ifdef IA64
+  /* Similar to GC_push_all_stack_sections() but for IA-64 registers store. */
+  GC_INNER void GC_push_all_register_sections(ptr_t bs_lo, ptr_t bs_hi,
+                  int eager, struct GC_traced_stack_sect_s *traced_stack_sect);
+#endif
+
+/*  Marks are in a reserved area in                          */
+/*  each heap block.  Each word has one mark bit associated  */
+/*  with it. Only those corresponding to the beginning of an */
+/*  object are used.                                         */
+
+/* Mark bit operations */
+
+/*
+ * Retrieve, set, clear the nth mark bit in a given heap block.
+ *
+ * (Recall that bit n corresponds to nth object or allocation granule
+ * relative to the beginning of the block, including unused words)
+ */
+
+#ifdef USE_MARK_BYTES
+# define mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[n])
+# define set_mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[n] = 1)
+# define clear_mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[n] = 0)
+#else
+/* Set mark bit correctly, even if mark bits may be concurrently        */
+/* accessed.                                                            */
+# ifdef PARALLEL_MARK
+    /* This is used only if we explicitly set USE_MARK_BITS.    */
+#   define OR_WORD(addr, bits) AO_or((volatile AO_t *)(addr), (AO_t)(bits))
+# else
+#   define OR_WORD(addr, bits) (void)(*(addr) |= (bits))
+# endif
+# define mark_bit_from_hdr(hhdr,n) \
+              (((hhdr)->hb_marks[divWORDSZ(n)] >> modWORDSZ(n)) & (word)1)
+# define set_mark_bit_from_hdr(hhdr,n) \
+              OR_WORD((hhdr)->hb_marks+divWORDSZ(n), (word)1 << modWORDSZ(n))
+# define clear_mark_bit_from_hdr(hhdr,n) \
+              ((hhdr)->hb_marks[divWORDSZ(n)] &= ~((word)1 << modWORDSZ(n)))
+#endif /* !USE_MARK_BYTES */
+
+#ifdef MARK_BIT_PER_OBJ
+#  define MARK_BIT_NO(offset, sz) (((unsigned)(offset))/(sz))
+        /* Get the mark bit index corresponding to the given byte       */
+        /* offset and size (in bytes).                                  */
+#  define MARK_BIT_OFFSET(sz) 1
+        /* Spacing between useful mark bits.                            */
+#  define IF_PER_OBJ(x) x
+#  define FINAL_MARK_BIT(sz) ((sz) > MAXOBJBYTES? 1 : HBLK_OBJS(sz))
+        /* Position of final, always set, mark bit.                     */
+#else /* MARK_BIT_PER_GRANULE */
+#  define MARK_BIT_NO(offset, sz) BYTES_TO_GRANULES((unsigned)(offset))
+#  define MARK_BIT_OFFSET(sz) BYTES_TO_GRANULES(sz)
+#  define IF_PER_OBJ(x)
+#  define FINAL_MARK_BIT(sz) \
+                ((sz) > MAXOBJBYTES ? MARK_BITS_PER_HBLK \
+                                : BYTES_TO_GRANULES((sz) * HBLK_OBJS(sz)))
+#endif
+
+/* Important internal collector routines */
+
+GC_INNER ptr_t GC_approx_sp(void);
+
+GC_INNER GC_bool GC_should_collect(void);
+
+void GC_apply_to_all_blocks(void (*fn)(struct hblk *h, word client_data),
+                            word client_data);
+                        /* Invoke fn(hbp, client_data) for each         */
+                        /* allocated heap block.                        */
+GC_INNER struct hblk * GC_next_used_block(struct hblk * h);
+                        /* Return first in-use block >= h       */
+GC_INNER struct hblk * GC_prev_block(struct hblk * h);
+                        /* Return last block <= h.  Returned block      */
+                        /* is managed by GC, but may or may not be in   */
+                        /* use.                                         */
+GC_INNER void GC_mark_init(void);
+GC_INNER void GC_clear_marks(void);
+                        /* Clear mark bits for all heap objects.        */
+GC_INNER void GC_invalidate_mark_state(void);
+                                /* Tell the marker that marked          */
+                                /* objects may point to unmarked        */
+                                /* ones, and roots may point to         */
+                                /* unmarked objects.  Reset mark stack. */
+GC_INNER GC_bool GC_mark_some(ptr_t cold_gc_frame);
+                        /* Perform about one pages worth of marking     */
+                        /* work of whatever kind is needed.  Returns    */
+                        /* quickly if no collection is in progress.     */
+                        /* Return TRUE if mark phase finished.          */
+GC_INNER void GC_initiate_gc(void);
+                                /* initiate collection.                 */
+                                /* If the mark state is invalid, this   */
+                                /* becomes full collection.  Otherwise  */
+                                /* it's partial.                        */
+
+GC_INNER GC_bool GC_collection_in_progress(void);
+                        /* Collection is in progress, or was abandoned. */
+
+#ifndef GC_DISABLE_INCREMENTAL
+# define GC_PUSH_CONDITIONAL(b, t, all) \
+                GC_push_conditional((ptr_t)(b), (ptr_t)(t), all)
+                        /* Do either of GC_push_all or GC_push_selected */
+                        /* depending on the third arg.                  */
+#else
+# define GC_PUSH_CONDITIONAL(b, t, all) GC_push_all((ptr_t)(b), (ptr_t)(t))
+#endif
+
+GC_INNER void GC_push_all_stack(ptr_t b, ptr_t t);
+                                    /* As GC_push_all but consider      */
+                                    /* interior pointers as valid.      */
+GC_INNER void GC_push_all_eager(ptr_t b, ptr_t t);
+                                    /* Same as GC_push_all_stack, but   */
+                                    /* ensures that stack is scanned    */
+                                    /* immediately, not just scheduled  */
+                                    /* for scanning.                    */
+
+  /* In the threads case, we push part of the current thread stack      */
+  /* with GC_push_all_eager when we push the registers.  This gets the  */
+  /* callee-save registers that may disappear.  The remainder of the    */
+  /* stacks are scheduled for scanning in *GC_push_other_roots, which   */
+  /* is thread-package-specific.                                        */
+
+GC_INNER void GC_push_roots(GC_bool all, ptr_t cold_gc_frame);
+                                        /* Push all or dirty roots.     */
+
+GC_API_PRIV GC_push_other_roots_proc GC_push_other_roots;
+                        /* Push system or application specific roots    */
+                        /* onto the mark stack.  In some environments   */
+                        /* (e.g. threads environments) this is          */
+                        /* predefined to be non-zero.  A client         */
+                        /* supplied replacement should also call the    */
+                        /* original function.  Remains externally       */
+                        /* visible as used by some well-known 3rd-party */
+                        /* software (e.g., ECL) currently.              */
+
+#ifdef THREADS
+  void GC_push_thread_structures(void);
+#endif
+GC_EXTERN void (*GC_push_typed_structures)(void);
+                        /* A pointer such that we can avoid linking in  */
+                        /* the typed allocation support if unused.      */
+
+GC_INNER void GC_with_callee_saves_pushed(void (*fn)(ptr_t, void *),
+                                          ptr_t arg);
+
+#if defined(SPARC) || defined(IA64)
+  /* Cause all stacked registers to be saved in memory.  Return a       */
+  /* pointer to the top of the corresponding memory stack.              */
+  ptr_t GC_save_regs_in_stack(void);
+#endif
+                        /* Push register contents onto mark stack.      */
+
+#if defined(MSWIN32) || defined(MSWINCE)
+  void __cdecl GC_push_one(word p);
+#else
+  void GC_push_one(word p);
+                              /* If p points to an object, mark it    */
+                              /* and push contents on the mark stack  */
+                              /* Pointer recognition test always      */
+                              /* accepts interior pointers, i.e. this */
+                              /* is appropriate for pointers found on */
+                              /* stack.                               */
+#endif
+
+#if defined(PRINT_BLACK_LIST) || defined(KEEP_BACK_PTRS)
+  GC_INNER void GC_mark_and_push_stack(ptr_t p, ptr_t source);
+                                /* Ditto, omits plausibility test       */
+#else
+  GC_INNER void GC_mark_and_push_stack(ptr_t p);
+#endif
+
+GC_INNER void GC_clear_hdr_marks(hdr * hhdr);
+                                    /* Clear the mark bits in a header */
+GC_INNER void GC_set_hdr_marks(hdr * hhdr);
+                                    /* Set the mark bits in a header */
+GC_INNER void GC_set_fl_marks(ptr_t p);
+                                    /* Set all mark bits associated with */
+                                    /* a free list.                      */
+#if defined(GC_ASSERTIONS) && defined(THREADS) && defined(THREAD_LOCAL_ALLOC)
+  void GC_check_fl_marks(void **);
+                                    /* Check that all mark bits         */
+                                    /* associated with a free list are  */
+                                    /* set.  Abort if not.              */
+#endif
+void GC_add_roots_inner(ptr_t b, ptr_t e, GC_bool tmp);
+GC_INNER void GC_exclude_static_roots_inner(void *start, void *finish);
+#if defined(DYNAMIC_LOADING) || defined(MSWIN32) || defined(MSWINCE) \
+    || defined(CYGWIN32) || defined(PCR)
+  GC_INNER void GC_register_dynamic_libraries(void);
+                /* Add dynamic library data sections to the root set. */
+#endif
+GC_INNER void GC_cond_register_dynamic_libraries(void);
+                /* Remove and reregister dynamic libraries if we're     */
+                /* configured to do that at each GC.                    */
+
+/* Machine dependent startup routines */
+ptr_t GC_get_main_stack_base(void);     /* Cold end of stack.           */
+#ifdef IA64
+  GC_INNER ptr_t GC_get_register_stack_base(void);
+                                        /* Cold end of register stack.  */
+#endif
+void GC_register_data_segments(void);
+
+#ifdef THREADS
+  GC_INNER void GC_thr_init(void);
+  GC_INNER void GC_init_parallel(void);
+#else
+  GC_INNER GC_bool GC_is_static_root(ptr_t p);
+                /* Is the address p in one of the registered static     */
+                /* root sections?                                       */
+#endif
+
+/* Black listing: */
+#ifdef PRINT_BLACK_LIST
+  GC_INNER void GC_add_to_black_list_normal(word p, ptr_t source);
+                        /* Register bits as a possible future false     */
+                        /* reference from the heap or static data       */
+# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \
+                if (GC_all_interior_pointers) { \
+                  GC_add_to_black_list_stack((word)(bits), (source)); \
+                } else \
+                  GC_add_to_black_list_normal((word)(bits), (source))
+  GC_INNER void GC_add_to_black_list_stack(word p, ptr_t source);
+# define GC_ADD_TO_BLACK_LIST_STACK(bits, source) \
+            GC_add_to_black_list_stack((word)(bits), (source))
+#else
+  GC_INNER void GC_add_to_black_list_normal(word p);
+# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \
+                if (GC_all_interior_pointers) { \
+                  GC_add_to_black_list_stack((word)(bits)); \
+                } else \
+                  GC_add_to_black_list_normal((word)(bits))
+  GC_INNER void GC_add_to_black_list_stack(word p);
+# define GC_ADD_TO_BLACK_LIST_STACK(bits, source) \
+            GC_add_to_black_list_stack((word)(bits))
+#endif /* PRINT_BLACK_LIST */
+
+struct hblk * GC_is_black_listed(struct hblk * h, word len);
+                        /* If there are likely to be false references   */
+                        /* to a block starting at h of the indicated    */
+                        /* length, then return the next plausible       */
+                        /* starting location for h that might avoid     */
+                        /* these false references.  Remains externally  */
+                        /* visible as used by GNU GCJ currently.        */
+
+GC_INNER void GC_promote_black_lists(void);
+                        /* Declare an end to a black listing phase.     */
+GC_INNER void GC_unpromote_black_lists(void);
+                        /* Approximately undo the effect of the above.  */
+                        /* This actually loses some information, but    */
+                        /* only in a reasonably safe way.               */
+
+GC_INNER ptr_t GC_scratch_alloc(size_t bytes);
+                                /* GC internal memory allocation for    */
+                                /* small objects.  Deallocation is not  */
+                                /* possible.  May return NULL.          */
+
+/* Heap block layout maps: */
+GC_INNER GC_bool GC_add_map_entry(size_t sz);
+                                /* Add a heap block map for objects of  */
+                                /* size sz to obj_map.                  */
+                                /* Return FALSE on failure.             */
+GC_INNER void GC_register_displacement_inner(size_t offset);
+                                /* Version of GC_register_displacement  */
+                                /* that assumes lock is already held.   */
+
+/*  hblk allocation: */
+GC_INNER void GC_new_hblk(size_t size_in_granules, int kind);
+                                /* Allocate a new heap block, and build */
+                                /* a free list in it.                   */
+
+GC_INNER ptr_t GC_build_fl(struct hblk *h, size_t words, GC_bool clear,
+                           ptr_t list);
+                                /* Build a free list for objects of     */
+                                /* size sz in block h.  Append list to  */
+                                /* end of the free lists.  Possibly     */
+                                /* clear objects on the list.  Normally */
+                                /* called by GC_new_hblk, but also      */
+                                /* called explicitly without GC lock.   */
+
+GC_INNER struct hblk * GC_allochblk(size_t size_in_bytes, int kind,
+                                    unsigned flags);
+                                /* Allocate a heap block, inform        */
+                                /* the marker that block is valid       */
+                                /* for objects of indicated size.       */
+
+GC_INNER ptr_t GC_alloc_large(size_t lb, int k, unsigned flags);
+                        /* Allocate a large block of size lb bytes.     */
+                        /* The block is not cleared.                    */
+                        /* Flags is 0 or IGNORE_OFF_PAGE.               */
+                        /* Calls GC_allchblk to do the actual           */
+                        /* allocation, but also triggers GC and/or      */
+                        /* heap expansion as appropriate.               */
+                        /* Does not update GC_bytes_allocd, but does    */
+                        /* other accounting.                            */
+
+GC_INNER void GC_freehblk(struct hblk * p);
+                                /* Deallocate a heap block and mark it  */
+                                /* as invalid.                          */
+
+/*  Misc GC: */
+GC_INNER GC_bool GC_expand_hp_inner(word n);
+GC_INNER void GC_start_reclaim(GC_bool abort_if_found);
+                                /* Restore unmarked objects to free     */
+                                /* lists, or (if abort_if_found is      */
+                                /* TRUE) report them.                   */
+                                /* Sweeping of small object pages is    */
+                                /* largely deferred.                    */
+GC_INNER void GC_continue_reclaim(size_t sz, int kind);
+                                /* Sweep pages of the given size and    */
+                                /* kind, as long as possible, and       */
+                                /* as long as the corr. free list is    */
+                                /* empty.  Sz is in granules.           */
+
+GC_INNER GC_bool GC_reclaim_all(GC_stop_func stop_func, GC_bool ignore_old);
+                                /* Reclaim all blocks.  Abort (in a     */
+                                /* consistent state) if f returns TRUE. */
+GC_INNER ptr_t GC_reclaim_generic(struct hblk * hbp, hdr *hhdr, size_t sz,
+                                  GC_bool init, ptr_t list,
+                                  signed_word *count);
+                                /* Rebuild free list in hbp with        */
+                                /* header hhdr, with objects of size sz */
+                                /* bytes.  Add list to the end of the   */
+                                /* free list.  Add the number of        */
+                                /* reclaimed bytes to *count.           */
+GC_INNER GC_bool GC_block_empty(hdr * hhdr);
+                                /* Block completely unmarked?   */
+GC_INNER int GC_CALLBACK GC_never_stop_func(void);
+                                /* Always returns 0 (FALSE).            */
+GC_INNER GC_bool GC_try_to_collect_inner(GC_stop_func f);
+
+                                /* Collect; caller must have acquired   */
+                                /* lock.  Collection is aborted if f    */
+                                /* returns TRUE.  Returns TRUE if it    */
+                                /* completes successfully.              */
+#define GC_gcollect_inner() \
+                (void)GC_try_to_collect_inner(GC_never_stop_func)
+
+GC_EXTERN GC_bool GC_is_initialized; /* GC_init() has been run. */
+
+#if defined(MSWIN32) || defined(MSWINCE)
+  void GC_deinit(void);
+                                /* Free any resources allocated by      */
+                                /* GC_init                              */
+#endif
+
+GC_INNER void GC_collect_a_little_inner(int n);
+                                /* Do n units worth of garbage          */
+                                /* collection work, if appropriate.     */
+                                /* A unit is an amount appropriate for  */
+                                /* HBLKSIZE bytes of allocation.        */
+
+GC_INNER void * GC_generic_malloc_inner(size_t lb, int k);
+                                /* Allocate an object of the given      */
+                                /* kind but assuming lock already held. */
+GC_INNER void * GC_generic_malloc_inner_ignore_off_page(size_t lb, int k);
+                                /* Allocate an object, where            */
+                                /* the client guarantees that there     */
+                                /* will always be a pointer to the      */
+                                /* beginning of the object while the    */
+                                /* object is live.                      */
+
+GC_INNER ptr_t GC_allocobj(size_t sz, int kind);
+                                /* Make the indicated                   */
+                                /* free list nonempty, and return its   */
+                                /* head.  Sz is in granules.            */
+
+#ifdef GC_ADD_CALLER
+  /* GC_DBG_EXTRAS is used by GC debug API functions (unlike GC_EXTRAS  */
+  /* used by GC debug API macros) thus GC_RETURN_ADDR_PARENT (pointing  */
+  /* to client caller) should be used if possible.                      */
+# ifdef GC_RETURN_ADDR_PARENT
+#  define GC_DBG_EXTRAS GC_RETURN_ADDR_PARENT, NULL, 0
+# else
+#  define GC_DBG_EXTRAS GC_RETURN_ADDR, NULL, 0
+# endif
+#else
+# define GC_DBG_EXTRAS "unknown", 0
+#endif
+
+/* We make the GC_clear_stack() call a tail one, hoping to get more of  */
+/* the stack.                                                           */
+#define GENERAL_MALLOC(lb,k) \
+    GC_clear_stack(GC_generic_malloc(lb, k))
+#define GENERAL_MALLOC_IOP(lb,k) \
+    GC_clear_stack(GC_generic_malloc_ignore_off_page(lb, k))
+
+#ifdef GC_COLLECT_AT_MALLOC
+  extern size_t GC_dbg_collect_at_malloc_min_lb;
+                            /* variable visible outside for debugging   */
+# define GC_DBG_COLLECT_AT_MALLOC(lb) \
+                (void)((lb) >= GC_dbg_collect_at_malloc_min_lb ? \
+                            (GC_gcollect(), 0) : 0)
+#else
+# define GC_DBG_COLLECT_AT_MALLOC(lb) (void)0
+#endif /* !GC_COLLECT_AT_MALLOC */
+
+/* Allocation routines that bypass the thread local cache.      */
+#ifdef THREAD_LOCAL_ALLOC
+  GC_INNER void * GC_core_malloc(size_t);
+  GC_INNER void * GC_core_malloc_atomic(size_t);
+# ifdef GC_GCJ_SUPPORT
+    GC_INNER void * GC_core_gcj_malloc(size_t, void *);
+# endif
+#endif /* THREAD_LOCAL_ALLOC */
+
+GC_INNER void GC_init_headers(void);
+GC_INNER struct hblkhdr * GC_install_header(struct hblk *h);
+                                /* Install a header for block h.        */
+                                /* Return 0 on failure, or the header   */
+                                /* otherwise.                           */
+GC_INNER GC_bool GC_install_counts(struct hblk * h, size_t sz);
+                                /* Set up forwarding counts for block   */
+                                /* h of size sz.                        */
+                                /* Return FALSE on failure.             */
+GC_INNER void GC_remove_header(struct hblk * h);
+                                /* Remove the header for block h.       */
+GC_INNER void GC_remove_counts(struct hblk * h, size_t sz);
+                                /* Remove forwarding counts for h.      */
+GC_INNER hdr * GC_find_header(ptr_t h);
+
+GC_INNER void GC_add_to_heap(struct hblk *p, size_t bytes);
+                        /* Add a HBLKSIZE aligned chunk to the heap.    */
+
+#ifdef USE_PROC_FOR_LIBRARIES
+  GC_INNER void GC_add_to_our_memory(ptr_t p, size_t bytes);
+                        /* Add a chunk to GC_our_memory.        */
+                        /* If p == 0, do nothing.               */
+#else
+# define GC_add_to_our_memory(p, bytes)
+#endif
+
+GC_INNER void GC_print_all_errors(void);
+                        /* Print smashed and leaked objects, if any.    */
+                        /* Clear the lists of such objects.             */
+
+GC_EXTERN void (*GC_check_heap)(void);
+                        /* Check that all objects in the heap with      */
+                        /* debugging info are intact.                   */
+                        /* Add any that are not to GC_smashed list.     */
+GC_EXTERN void (*GC_print_all_smashed)(void);
+                        /* Print GC_smashed if it's not empty.          */
+                        /* Clear GC_smashed list.                       */
+GC_EXTERN void (*GC_print_heap_obj)(ptr_t p);
+                        /* If possible print (using GC_err_printf)      */
+                        /* a more detailed description (terminated with */
+                        /* "\n") of the object referred to by p.        */
+
+#if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG)
+  void GC_print_address_map(void);
+                        /* Print an address map of the process.         */
+#endif
+
+#ifndef SHORT_DBG_HDRS
+  GC_EXTERN GC_bool GC_findleak_delay_free;
+                        /* Do not immediately deallocate object on      */
+                        /* free() in the leak-finding mode, just mark   */
+                        /* it as freed (and deallocate it after GC).    */
+  GC_INNER GC_bool GC_check_leaked(ptr_t base); /* from dbg_mlc.c */
+#endif
+
+GC_EXTERN GC_bool GC_have_errors; /* We saw a smashed or leaked object. */
+                                  /* Call error printing routine        */
+                                  /* occasionally.  It is OK to read it */
+                                  /* without acquiring the lock.        */
+
+#define VERBOSE 2
+#ifndef SMALL_CONFIG
+  /* GC_print_stats should be visible to extra/MacOS.c. */
+  extern int GC_print_stats;    /* Nonzero generates basic GC log.      */
+                                /* VERBOSE generates add'l messages.    */
+#else /* SMALL_CONFIG */
+# define GC_print_stats 0
+  /* Will this remove the message character strings from the executable? */
+  /* With a particular level of optimizations, it should...              */
+#endif
+
+#ifdef KEEP_BACK_PTRS
+  GC_EXTERN long GC_backtraces;
+  GC_INNER void GC_generate_random_backtrace_no_gc(void);
+#endif
+
+GC_EXTERN GC_bool GC_print_back_height;
+
+#ifdef MAKE_BACK_GRAPH
+  void GC_print_back_graph_stats(void);
+#endif
+
+#ifdef THREADS
+  GC_INNER void GC_free_inner(void * p);
+#endif
+
+/* Macros used for collector internal allocation.       */
+/* These assume the collector lock is held.             */
+#ifdef DBG_HDRS_ALL
+  GC_INNER void * GC_debug_generic_malloc_inner(size_t lb, int k);
+  GC_INNER void * GC_debug_generic_malloc_inner_ignore_off_page(size_t lb,
+                                                                int k);
+# define GC_INTERNAL_MALLOC GC_debug_generic_malloc_inner
+# define GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE \
+               GC_debug_generic_malloc_inner_ignore_off_page
+# ifdef THREADS
+    GC_INNER void GC_debug_free_inner(void * p);
+#   define GC_INTERNAL_FREE GC_debug_free_inner
+# else
+#   define GC_INTERNAL_FREE GC_debug_free
+# endif
+#else
+# define GC_INTERNAL_MALLOC GC_generic_malloc_inner
+# define GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE \
+               GC_generic_malloc_inner_ignore_off_page
+# ifdef THREADS
+#   define GC_INTERNAL_FREE GC_free_inner
+# else
+#   define GC_INTERNAL_FREE GC_free
+# endif
+#endif /* !DBG_HDRS_ALL */
+
+#ifdef USE_MUNMAP
+  /* Memory unmapping: */
+  GC_INNER void GC_unmap_old(void);
+  GC_INNER void GC_merge_unmapped(void);
+  GC_INNER void GC_unmap(ptr_t start, size_t bytes);
+  GC_INNER void GC_remap(ptr_t start, size_t bytes);
+  GC_INNER void GC_unmap_gap(ptr_t start1, size_t bytes1, ptr_t start2,
+                             size_t bytes2);
+#endif
+
+#ifdef CAN_HANDLE_FORK
+  GC_EXTERN int GC_handle_fork;
+                /* Fork-handling mode:                                  */
+                /* 0 means no fork handling requested (but client could */
+                /* anyway call fork() provided it is surrounded with    */
+                /* GC_atfork_prepare/parent/child calls);               */
+                /* -1 means GC tries to use pthread_at_fork if it is    */
+                /* available (if it succeeds then GC_handle_fork value  */
+                /* is changed to 1), client should nonetheless surround */
+                /* fork() with GC_atfork_prepare/parent/child (for the  */
+                /* case of pthread_at_fork failure or absence);         */
+                /* 1 (or other values) means client fully relies on     */
+                /* pthread_at_fork (so if it is missing or failed then  */
+                /* abort occurs in GC_init), GC_atfork_prepare and the  */
+                /* accompanying routines are no-op in such a case.      */
+#endif
+
+#ifndef GC_DISABLE_INCREMENTAL
+  GC_EXTERN GC_bool GC_dirty_maintained;
+                                /* Dirty bits are being maintained,     */
+                                /* either for incremental collection,   */
+                                /* or to limit the root set.            */
+
+  /* Virtual dirty bit implementation:            */
+  /* Each implementation exports the following:   */
+  GC_INNER void GC_read_dirty(void);
+                        /* Retrieve dirty bits. */
+  GC_INNER GC_bool GC_page_was_dirty(struct hblk *h);
+                        /* Read retrieved dirty bits.   */
+  GC_INNER void GC_remove_protection(struct hblk *h, word nblocks,
+                                   GC_bool pointerfree);
+                        /* h is about to be written or allocated.  Ensure   */
+                        /* that it's not write protected by the virtual     */
+                        /* dirty bit implementation.                        */
+
+  GC_INNER void GC_dirty_init(void);
+#endif /* !GC_DISABLE_INCREMENTAL */
+
+/* Same as GC_base but excepts and returns a pointer to const object.   */
+#define GC_base_C(p) ((const void *)GC_base((/* no const */ void *)(p)))
+
+/* Stubborn objects: */
+void GC_read_changed(void); /* Analogous to GC_read_dirty */
+GC_bool GC_page_was_changed(struct hblk * h);
+                                /* Analogous to GC_page_was_dirty */
+void GC_clean_changing_list(void);
+                                /* Collect obsolete changing list entries */
+void GC_stubborn_init(void);
+
+/* Debugging print routines: */
+void GC_print_block_list(void);
+void GC_print_hblkfreelist(void);
+void GC_print_heap_sects(void);
+void GC_print_static_roots(void);
+/* void GC_dump(void); - declared in gc.h */
+
+extern word GC_fo_entries; /* should be visible in extra/MacOS.c */
+
+#ifdef KEEP_BACK_PTRS
+   GC_INNER void GC_store_back_pointer(ptr_t source, ptr_t dest);
+   GC_INNER void GC_marked_for_finalization(ptr_t dest);
+#  define GC_STORE_BACK_PTR(source, dest) GC_store_back_pointer(source, dest)
+#  define GC_MARKED_FOR_FINALIZATION(dest) GC_marked_for_finalization(dest)
+#else
+#  define GC_STORE_BACK_PTR(source, dest)
+#  define GC_MARKED_FOR_FINALIZATION(dest)
+#endif
+
+/* Make arguments appear live to compiler */
+void GC_noop6(word, word, word, word, word, word);
+
+GC_API void GC_CALL GC_noop1(word);
+
+#ifndef GC_ATTR_FORMAT_PRINTF
+# if defined(__GNUC__) && __GNUC__ >= 3
+#   define GC_ATTR_FORMAT_PRINTF(spec_argnum, first_checked) \
+        __attribute__((__format__(__printf__, spec_argnum, first_checked)))
+# else
+#   define GC_ATTR_FORMAT_PRINTF(spec_argnum, first_checked)
+# endif
+#endif
+
+/* Logging and diagnostic output:       */
+/* GC_printf is used typically on client explicit print requests.       */
+/* For all GC_X_printf routines, it is recommended to put "\n" at       */
+/* 'format' string end (for output atomicity).                          */
+GC_API_PRIV void GC_printf(const char * format, ...)
+                        GC_ATTR_FORMAT_PRINTF(1, 2);
+                        /* A version of printf that doesn't allocate,   */
+                        /* 1K total output length.                      */
+                        /* (We use sprintf.  Hopefully that doesn't     */
+                        /* allocate for long arguments.)                */
+GC_API_PRIV void GC_err_printf(const char * format, ...)
+                        GC_ATTR_FORMAT_PRINTF(1, 2);
+
+/* Basic logging routine.  Typically, GC_log_printf is called directly  */
+/* only inside various DEBUG_x blocks.                                  */
+#if defined(__cplusplus) && defined(SYMBIAN)
+  extern "C" {
+#endif
+GC_API_PRIV void GC_log_printf(const char * format, ...)
+                        GC_ATTR_FORMAT_PRINTF(1, 2);
+#if defined(__cplusplus) && defined(SYMBIAN)
+  }
+#endif
+
+#ifndef GC_ANDROID_LOG
+# define GC_PRINT_STATS_FLAG (GC_print_stats != 0)
+# define GC_INFOLOG_PRINTF GC_COND_LOG_PRINTF
+  /* GC_verbose_log_printf is called only if GC_print_stats is VERBOSE. */
+# define GC_verbose_log_printf GC_log_printf
+#else
+  extern GC_bool GC_quiet;
+# define GC_PRINT_STATS_FLAG (!GC_quiet)
+  /* INFO/DBG loggers are enabled even if GC_print_stats is off. */
+# ifndef GC_INFOLOG_PRINTF
+#   define GC_INFOLOG_PRINTF if (GC_quiet) {} else GC_info_log_printf
+# endif
+  GC_INNER void GC_info_log_printf(const char *format, ...)
+                        GC_ATTR_FORMAT_PRINTF(1, 2);
+  GC_INNER void GC_verbose_log_printf(const char *format, ...)
+                        GC_ATTR_FORMAT_PRINTF(1, 2);
+#endif /* GC_ANDROID_LOG */
+
+/* Convenient macros for GC_[verbose_]log_printf invocation.    */
+#define GC_COND_LOG_PRINTF \
+                if (EXPECT(!GC_print_stats, TRUE)) {} else GC_log_printf
+#define GC_VERBOSE_LOG_PRINTF \
+    if (EXPECT(GC_print_stats != VERBOSE, TRUE)) {} else GC_verbose_log_printf
+#ifndef GC_DBGLOG_PRINTF
+# define GC_DBGLOG_PRINTF if (!GC_PRINT_STATS_FLAG) {} else GC_log_printf
+#endif
+
+void GC_err_puts(const char *s);
+                        /* Write s to stderr, don't buffer, don't add   */
+                        /* newlines, don't ...                          */
+
+/* Handy macro for logging size values (of word type) in KiB (rounding  */
+/* to nearest value).                                                   */
+#define TO_KiB_UL(v) ((unsigned long)(((v) + ((1 << 9) - 1)) >> 10))
+
+GC_EXTERN unsigned GC_fail_count;
+                        /* How many consecutive GC/expansion failures?  */
+                        /* Reset by GC_allochblk(); defined in alloc.c. */
+
+GC_EXTERN long GC_large_alloc_warn_interval; /* defined in misc.c */
+
+GC_EXTERN signed_word GC_bytes_found;
+                /* Number of reclaimed bytes after garbage collection;  */
+                /* protected by GC lock; defined in reclaim.c.          */
+
+#ifndef GC_GET_HEAP_USAGE_NOT_NEEDED
+  GC_EXTERN word GC_reclaimed_bytes_before_gc;
+                /* Number of bytes reclaimed before this        */
+                /* collection cycle; used for statistics only.  */
+#endif
+
+#ifdef USE_MUNMAP
+  GC_EXTERN int GC_unmap_threshold; /* defined in allchblk.c */
+  GC_EXTERN GC_bool GC_force_unmap_on_gcollect; /* defined in misc.c */
+#endif
+
+#ifdef MSWIN32
+  GC_EXTERN GC_bool GC_no_win32_dlls; /* defined in os_dep.c */
+  GC_EXTERN GC_bool GC_wnt;     /* Is Windows NT derivative;    */
+                                /* defined and set in os_dep.c. */
+#endif
+
+#ifdef THREADS
+# if defined(MSWIN32) || defined(MSWINCE)
+    GC_EXTERN CRITICAL_SECTION GC_write_cs; /* defined in misc.c */
+#   ifdef GC_ASSERTIONS
+      GC_EXTERN GC_bool GC_write_disabled;
+                                /* defined in win32_threads.c;  */
+                                /* protected by GC_write_cs.    */
+
+#   endif
+# endif
+# ifdef MPROTECT_VDB
+    GC_EXTERN volatile AO_TS_t GC_fault_handler_lock;
+                                        /* defined in os_dep.c */
+# endif
+# ifdef MSWINCE
+    GC_EXTERN GC_bool GC_dont_query_stack_min;
+                                /* Defined and set in os_dep.c. */
+# endif
+#elif defined(IA64)
+  GC_EXTERN ptr_t GC_save_regs_ret_val; /* defined in mach_dep.c. */
+                        /* Previously set to backing store pointer.     */
+#endif /* !THREADS */
+
+#ifdef THREAD_LOCAL_ALLOC
+  GC_EXTERN GC_bool GC_world_stopped; /* defined in alloc.c */
+  GC_INNER void GC_mark_thread_local_free_lists(void);
+#endif
+
+#ifdef GC_GCJ_SUPPORT
+# ifdef GC_ASSERTIONS
+    GC_EXTERN GC_bool GC_gcj_malloc_initialized; /* defined in gcj_mlc.c */
+# endif
+  GC_EXTERN ptr_t * GC_gcjobjfreelist;
+#endif
+
+#if defined(GWW_VDB) && defined(MPROTECT_VDB)
+  GC_INNER GC_bool GC_gww_dirty_init(void);
+  /* Defined in os_dep.c.  Returns TRUE if GetWriteWatch is available.  */
+  /* May be called repeatedly.                                          */
+#endif
+
+#if defined(CHECKSUMS) || defined(PROC_VDB)
+  GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk * h);
+                        /* Could the page contain valid heap pointers?  */
+#endif
+
+GC_INNER void GC_default_print_heap_obj_proc(ptr_t p);
+
+GC_INNER void GC_extend_size_map(size_t); /* in misc.c */
+
+GC_INNER void GC_setpagesize(void);
+
+GC_INNER void GC_initialize_offsets(void);      /* defined in obj_map.c */
+
+GC_INNER void GC_bl_init(void);
+GC_INNER void GC_bl_init_no_interiors(void);    /* defined in blacklst.c */
+
+GC_INNER void GC_start_debugging(void); /* defined in dbg_mlc.c */
+
+/* Store debugging info into p.  Return displaced pointer.      */
+/* Assumes we don't hold allocation lock.                       */
+GC_INNER ptr_t GC_store_debug_info(ptr_t p, word sz, const char *str,
+                                   int linenum);
+
+#ifdef REDIRECT_MALLOC
+# ifdef GC_LINUX_THREADS
+    GC_INNER GC_bool GC_text_mapping(char *nm, ptr_t *startp, ptr_t *endp);
+                                                /* from os_dep.c */
+# endif
+#elif defined(USE_WINALLOC)
+  GC_INNER void GC_add_current_malloc_heap(void);
+#endif /* !REDIRECT_MALLOC */
+
+#ifdef MAKE_BACK_GRAPH
+  GC_INNER void GC_build_back_graph(void);
+  GC_INNER void GC_traverse_back_graph(void);
+#endif
+
+#ifdef MSWIN32
+  GC_INNER void GC_init_win32(void);
+#endif
+
+#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32)
+  GC_INNER void * GC_roots_present(ptr_t);
+        /* The type is a lie, since the real type doesn't make sense here, */
+        /* and we only test for NULL.                                      */
+#endif
+
+#ifdef GC_WIN32_THREADS
+  GC_INNER void GC_get_next_stack(char *start, char * limit, char **lo,
+                                  char **hi);
+# ifdef MPROTECT_VDB
+    GC_INNER void GC_set_write_fault_handler(void);
+# endif
+#endif /* GC_WIN32_THREADS */
+
+#ifdef THREADS
+  GC_INNER void GC_reset_finalizer_nested(void);
+  GC_INNER unsigned char *GC_check_finalizer_nested(void);
+  GC_INNER void GC_do_blocking_inner(ptr_t data, void * context);
+  GC_INNER void GC_push_all_stacks(void);
+# ifdef USE_PROC_FOR_LIBRARIES
+    GC_INNER GC_bool GC_segment_is_thread_stack(ptr_t lo, ptr_t hi);
+# endif
+# ifdef IA64
+    GC_INNER ptr_t GC_greatest_stack_base_below(ptr_t bound);
+# endif
+#endif /* THREADS */
+
+#ifdef DYNAMIC_LOADING
+  GC_INNER GC_bool GC_register_main_static_data(void);
+# ifdef DARWIN
+    GC_INNER void GC_init_dyld(void);
+# endif
+#endif /* DYNAMIC_LOADING */
+
+#ifdef SEARCH_FOR_DATA_START
+  GC_INNER void GC_init_linux_data_start(void);
+#endif
+
+#if defined(NETBSD) && defined(__ELF__)
+  GC_INNER void GC_init_netbsd_elf(void);
+#endif
+
+#ifdef UNIX_LIKE
+  GC_INNER void GC_set_and_save_fault_handler(void (*handler)(int));
+#endif
+
+#ifdef NEED_PROC_MAPS
+# if defined(DYNAMIC_LOADING) && defined(USE_PROC_FOR_LIBRARIES)
+    GC_INNER char *GC_parse_map_entry(char *buf_ptr, ptr_t *start, ptr_t *end,
+                                      char **prot, unsigned int *maj_dev,
+                                      char **mapping_name);
+# endif
+  GC_INNER char *GC_get_maps(void); /* from os_dep.c */
+#endif /* NEED_PROC_MAPS */
+
+#ifdef GC_ASSERTIONS
+# define GC_ASSERT(expr) \
+              do { \
+                if (!(expr)) { \
+                  GC_err_printf("Assertion failure: %s:%d\n", \
+                                __FILE__, __LINE__); \
+                  ABORT("assertion failure"); \
+                } \
+              } while (0)
+  GC_INNER word GC_compute_large_free_bytes(void);
+  GC_INNER word GC_compute_root_size(void);
+#else
+# define GC_ASSERT(expr)
+#endif
+
+/* Check a compile time assertion at compile time.  The error   */
+/* message for failure is a bit baroque, but ...                */
+#if defined(mips) && !defined(__GNUC__)
+/* DOB: MIPSPro C gets an internal error taking the sizeof an array type.
+   This code works correctly (ugliness is to avoid "unused var" warnings) */
+# define GC_STATIC_ASSERT(expr) \
+    do { if (0) { char j[(expr)? 1 : -1]; j[0]='\0'; j[0]=j[0]; } } while(0)
+#else
+# define GC_STATIC_ASSERT(expr) (void)sizeof(char[(expr)? 1 : -1])
+#endif
+
+#define COND_DUMP_CHECKS \
+          do { \
+            GC_ASSERT(GC_compute_large_free_bytes() == GC_large_free_bytes); \
+            GC_ASSERT(GC_compute_root_size() == GC_root_size); \
+          } while (0)
+
+#ifndef NO_DEBUGGING
+  GC_EXTERN GC_bool GC_dump_regularly;
+                                /* Generate regular debugging dumps.    */
+# define COND_DUMP if (EXPECT(GC_dump_regularly, FALSE)) GC_dump(); \
+                        else COND_DUMP_CHECKS
+#else
+# define COND_DUMP COND_DUMP_CHECKS
+#endif
+
+#if defined(PARALLEL_MARK)
+  /* We need additional synchronization facilities from the thread      */
+  /* support.  We believe these are less performance critical           */
+  /* than the main garbage collector lock; standard pthreads-based      */
+  /* implementations should be sufficient.                              */
+
+# define GC_markers_m1 GC_parallel
+                        /* Number of mark threads we would like to have */
+                        /* excluding the initiating thread.             */
+
+  /* The mark lock and condition variable.  If the GC lock is also      */
+  /* acquired, the GC lock must be acquired first.  The mark lock is    */
+  /* used to both protect some variables used by the parallel           */
+  /* marker, and to protect GC_fl_builder_count, below.                 */
+  /* GC_notify_all_marker() is called when                              */
+  /* the state of the parallel marker changes                           */
+  /* in some significant way (see gc_mark.h for details).  The          */
+  /* latter set of events includes incrementing GC_mark_no.             */
+  /* GC_notify_all_builder() is called when GC_fl_builder_count         */
+  /* reaches 0.                                                         */
+
+  GC_INNER void GC_acquire_mark_lock(void);
+  GC_INNER void GC_release_mark_lock(void);
+  GC_INNER void GC_notify_all_builder(void);
+  GC_INNER void GC_wait_for_reclaim(void);
+
+  GC_EXTERN word GC_fl_builder_count;   /* Protected by mark lock.      */
+
+  GC_INNER void GC_notify_all_marker(void);
+  GC_INNER void GC_wait_marker(void);
+  GC_EXTERN word GC_mark_no;            /* Protected by mark lock.      */
+
+  GC_INNER void GC_help_marker(word my_mark_no);
+              /* Try to help out parallel marker for mark cycle         */
+              /* my_mark_no.  Returns if the mark cycle finishes or     */
+              /* was already done, or there was nothing to do for       */
+              /* some other reason.                                     */
+#endif /* PARALLEL_MARK */
+
+#if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS) && !defined(NACL) \
+    && !defined(SIG_SUSPEND)
+  /* We define the thread suspension signal here, so that we can refer  */
+  /* to it in the dirty bit implementation, if necessary.  Ideally we   */
+  /* would allocate a (real-time?) signal using the standard mechanism. */
+  /* unfortunately, there is no standard mechanism.  (There is one      */
+  /* in Linux glibc, but it's not exported.)  Thus we continue to use   */
+  /* the same hard-coded signals we've always used.                     */
+# if defined(GC_LINUX_THREADS) || defined(GC_DGUX386_THREADS)
+#   if defined(SPARC) && !defined(SIGPWR)
+      /* SPARC/Linux doesn't properly define SIGPWR in <signal.h>.      */
+      /* It is aliased to SIGLOST in asm/signal.h, though.              */
+#     define SIG_SUSPEND SIGLOST
+#   else
+      /* Linuxthreads itself uses SIGUSR1 and SIGUSR2.                  */
+#     define SIG_SUSPEND SIGPWR
+#   endif
+# elif defined(GC_OPENBSD_THREADS)
+#   ifndef GC_OPENBSD_UTHREADS
+#     define SIG_SUSPEND SIGXFSZ
+#   endif
+# elif !defined(GC_DARWIN_THREADS)
+#   if defined(_SIGRTMIN)
+#     define SIG_SUSPEND _SIGRTMIN + 6
+#   else
+#     define SIG_SUSPEND SIGRTMIN + 6
+#   endif
+# endif
+#endif /* GC_PTHREADS && !SIG_SUSPEND */
+
+#if defined(GC_PTHREADS) && !defined(GC_SEM_INIT_PSHARED)
+# define GC_SEM_INIT_PSHARED 0
+#endif
+
+#include <setjmp.h>
+
+/* Some macros for setjmp that works across signal handlers     */
+/* were possible, and a couple of routines to facilitate        */
+/* catching accesses to bad addresses when that's               */
+/* possible/needed.                                             */
+#if (defined(UNIX_LIKE) || (defined(NEED_FIND_LIMIT) && defined(CYGWIN32))) \
+    && !defined(GC_NO_SIGSETJMP)
+# if defined(SUNOS5SIGS) && !defined(FREEBSD) && !defined(LINUX)
+#  include <sys/siginfo.h>
+# endif
+  /* Define SETJMP and friends to be the version that restores  */
+  /* the signal mask.                                           */
+# define SETJMP(env) sigsetjmp(env, 1)
+# define LONGJMP(env, val) siglongjmp(env, val)
+# define JMP_BUF sigjmp_buf
+#else
+# ifdef ECOS
+#   define SETJMP(env) hal_setjmp(env)
+# else
+#   define SETJMP(env) setjmp(env)
+# endif
+# define LONGJMP(env, val) longjmp(env, val)
+# define JMP_BUF jmp_buf
+#endif /* !UNIX_LIKE || GC_NO_SIGSETJMP */
+
+/* Do we need the GC_find_limit machinery to find the end of a  */
+/* data segment.                                                */
+#if defined(HEURISTIC2) || defined(SEARCH_FOR_DATA_START)
+# define NEED_FIND_LIMIT
+#endif
+
+#if !defined(STACKBOTTOM) && defined(HEURISTIC2)
+# define NEED_FIND_LIMIT
+#endif
+
+#if (defined(SVR4) || defined(AUX) || defined(DGUX) \
+    || (defined(LINUX) && defined(SPARC))) && !defined(PCR)
+# define NEED_FIND_LIMIT
+#endif
+
+#if defined(FREEBSD) && (defined(I386) || defined(X86_64) \
+                        || defined(powerpc) || defined(__powerpc__))
+# include <machine/trap.h>
+# if !defined(PCR)
+#   define NEED_FIND_LIMIT
+# endif
+#endif /* FREEBSD */
+
+#if (defined(NETBSD) || defined(OPENBSD)) && defined(__ELF__) \
+    && !defined(NEED_FIND_LIMIT)
+  /* Used by GC_init_netbsd_elf() in os_dep.c. */
+# define NEED_FIND_LIMIT
+#endif
+
+#if defined(IA64) && !defined(NEED_FIND_LIMIT)
+# define NEED_FIND_LIMIT
+     /* May be needed for register backing store base. */
+#endif
+
+#if defined(NEED_FIND_LIMIT) \
+     || (defined(USE_PROC_FOR_LIBRARIES) && defined(THREADS))
+  JMP_BUF GC_jmp_buf;
+
+  /* Set up a handler for address faults which will longjmp to  */
+  /* GC_jmp_buf;                                                */
+  GC_INNER void GC_setup_temporary_fault_handler(void);
+  /* Undo the effect of GC_setup_temporary_fault_handler.       */
+  GC_INNER void GC_reset_fault_handler(void);
+#endif /* NEED_FIND_LIMIT || USE_PROC_FOR_LIBRARIES */
+
+/* Some convenience macros for cancellation support. */
+#if defined(CANCEL_SAFE)
+# if defined(GC_ASSERTIONS) && (defined(USE_COMPILER_TLS) \
+     || (defined(LINUX) && !defined(ARM32) \
+              && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) \
+     || defined(HPUX) /* and probably others ... */))
+    extern __thread unsigned char GC_cancel_disable_count;
+#   define NEED_CANCEL_DISABLE_COUNT
+#   define INCR_CANCEL_DISABLE() ++GC_cancel_disable_count
+#   define DECR_CANCEL_DISABLE() --GC_cancel_disable_count
+#   define ASSERT_CANCEL_DISABLED() GC_ASSERT(GC_cancel_disable_count > 0)
+# else
+#   define INCR_CANCEL_DISABLE()
+#   define DECR_CANCEL_DISABLE()
+#   define ASSERT_CANCEL_DISABLED() (void)0
+# endif /* GC_ASSERTIONS & ... */
+# define DISABLE_CANCEL(state) \
+        do { pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state); \
+          INCR_CANCEL_DISABLE(); } while (0)
+# define RESTORE_CANCEL(state) \
+        do { ASSERT_CANCEL_DISABLED(); \
+          pthread_setcancelstate(state, NULL); \
+          DECR_CANCEL_DISABLE(); } while (0)
+#else /* !CANCEL_SAFE */
+# define DISABLE_CANCEL(state) (void)0
+# define RESTORE_CANCEL(state) (void)0
+# define ASSERT_CANCEL_DISABLED() (void)0
+#endif /* !CANCEL_SAFE */
+
+#endif /* GC_PRIVATE_H */

+ 2940 - 0
blitz.mod/bdwgc/include/private/gcconfig.h

@@ -0,0 +1,2940 @@
+/*
+ * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
+ * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
+ * Copyright (c) 2000-2004 Hewlett-Packard Development Company, L.P.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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 header is private to the gc.  It is almost always included from
+ * gc_priv.h.  However it is possible to include it by itself if just the
+ * configuration macros are needed.  In that
+ * case, a few declarations relying on types declared in gc_priv.h will be
+ * omitted.
+ */
+
+#ifndef GCCONFIG_H
+#define GCCONFIG_H
+
+# ifndef GC_PRIVATE_H
+    /* Fake ptr_t declaration, just to avoid compilation errors.        */
+    /* This avoids many instances if "ifndef GC_PRIVATE_H" below.       */
+    typedef struct GC_undefined_struct * ptr_t;
+#   include <stddef.h>  /* For size_t etc. */
+# endif
+
+/* Machine dependent parameters.  Some tuning parameters can be found   */
+/* near the top of gc_private.h.                                        */
+
+/* Machine specific parts contributed by various people.  See README file. */
+
+#if defined(__ANDROID__) && !defined(PLATFORM_ANDROID)
+  /* __ANDROID__ macro is defined by Android NDK gcc.   */
+# define PLATFORM_ANDROID 1
+#endif
+
+#if defined(__SYMBIAN32__) && !defined(SYMBIAN)
+# define SYMBIAN
+# ifdef __WINS__
+#   pragma data_seg(".data2")
+# endif
+#endif
+
+/* First a unified test for Linux: */
+# if (defined(linux) || defined(__linux__) || defined(PLATFORM_ANDROID)) \
+     && !defined(LINUX) && !defined(__native_client__)
+#   define LINUX
+# endif
+
+/* And one for QNX: */
+# if defined(__QNX__)
+#    define I386
+#    define OS_TYPE "QNX"
+#    define SA_RESTART 0
+#    define HEURISTIC1
+     extern char etext[];
+#    define DATASTART ((ptr_t)(etext))
+     extern int _end[];
+#    define DATAEND (_end)
+#    define mach_type_known
+# endif
+
+/* And one for NetBSD: */
+# if defined(__NetBSD__)
+#    define NETBSD
+# endif
+
+/* And one for OpenBSD: */
+# if defined(__OpenBSD__)
+#    define OPENBSD
+# endif
+
+/* And one for FreeBSD: */
+# if (defined(__FreeBSD__) || defined(__DragonFly__) \
+      || defined(__FreeBSD_kernel__)) && !defined(FREEBSD)
+#    define FREEBSD
+# endif
+
+/* And one for Darwin: */
+# if defined(macosx) || (defined(__APPLE__) && defined(__MACH__))
+#   define DARWIN
+# endif
+
+/* Determine the machine type: */
+# if defined(__native_client__)
+#    define NACL
+#    define I386
+#    define mach_type_known
+# endif
+# if defined(__aarch64__)
+#    define AARCH64
+#    if !defined(LINUX)
+#      define NOSYS
+#      define mach_type_known
+#    endif
+# endif
+# if defined(__arm) || defined(__arm__) || defined(__thumb__)
+#    define ARM32
+#    if !defined(LINUX) && !defined(NETBSD) && !defined(FREEBSD) \
+        && !defined(OPENBSD) && !defined(DARWIN) \
+        && !defined(_WIN32) && !defined(__CEGCC__) && !defined(SYMBIAN)
+#      define NOSYS
+#      define mach_type_known
+#    endif
+# endif
+# if defined(sun) && defined(mc68000)
+#    error SUNOS4 no longer supported
+# endif
+# if defined(hp9000s300)
+#    error M68K based HP machines no longer supported.
+# endif
+# if defined(OPENBSD) && defined(m68k)
+     /* FIXME: Should we remove this case? */
+#    define M68K
+#    define mach_type_known
+# endif
+# if defined(OPENBSD) && defined(__sparc__)
+#    define SPARC
+#    define mach_type_known
+# endif
+# if defined(OPENBSD) && defined(__arm__)
+#    define ARM32
+#    define mach_type_known
+# endif
+# if defined(OPENBSD) && defined(__sh__)
+#    define SH
+#    define mach_type_known
+# endif
+# if defined(NETBSD) && (defined(m68k) || defined(__m68k__))
+#    define M68K
+#    define mach_type_known
+# endif
+# if defined(NETBSD) && defined(__powerpc__)
+#    define POWERPC
+#    define mach_type_known
+# endif
+# if defined(NETBSD) && (defined(__arm32__) || defined(__arm__))
+#    define ARM32
+#    define mach_type_known
+# endif
+# if defined(NETBSD) && defined(__sh__)
+#    define SH
+#    define mach_type_known
+# endif
+# if defined(vax) || defined(__vax__)
+#    define VAX
+#    ifdef ultrix
+#       define ULTRIX
+#    else
+#       define BSD
+#    endif
+#    define mach_type_known
+# endif
+# if defined(__NetBSD__) && defined(__vax__)
+#    define VAX
+#    define mach_type_known
+# endif
+# if defined(mips) || defined(__mips) || defined(_mips)
+#    define MIPS
+#    if defined(nec_ews) || defined(_nec_ews)
+#      define EWS4800
+#    endif
+#    if !defined(LINUX) && !defined(EWS4800) && !defined(NETBSD) \
+        && !defined(OPENBSD)
+#      if defined(ultrix) || defined(__ultrix)
+#        define ULTRIX
+#      else
+#        define IRIX5   /* or IRIX 6.X */
+#      endif
+#    endif /* !LINUX */
+#    if defined(__NetBSD__) && defined(__MIPSEL__)
+#      undef ULTRIX
+#    endif
+#    define mach_type_known
+# endif
+# if defined(DGUX) && (defined(i386) || defined(__i386__))
+#    define I386
+#    ifndef _USING_DGUX
+#    define _USING_DGUX
+#    endif
+#    define mach_type_known
+# endif
+# if defined(sequent) && (defined(i386) || defined(__i386__))
+#    define I386
+#    define SEQUENT
+#    define mach_type_known
+# endif
+# if defined(sun) && (defined(i386) || defined(__i386__))
+#    define I386
+#    define SOLARIS
+#    define mach_type_known
+# endif
+# if defined(sun) && defined(__amd64)
+#    define X86_64
+#    define SOLARIS
+#    define mach_type_known
+# endif
+# if (defined(__OS2__) || defined(__EMX__)) && defined(__32BIT__)
+#    define I386
+#    define OS2
+#    define mach_type_known
+# endif
+# if defined(ibm032)
+#   error IBM PC/RT no longer supported.
+# endif
+# if defined(sun) && (defined(sparc) || defined(__sparc))
+#   define SPARC
+    /* Test for SunOS 5.x */
+#     include <errno.h>
+#     define SOLARIS
+#   define mach_type_known
+# endif
+# if defined(sparc) && defined(unix) && !defined(sun) && !defined(linux) \
+     && !defined(__OpenBSD__) && !defined(__NetBSD__) \
+     && !defined(__FreeBSD__) && !defined(__DragonFly__)
+#   define SPARC
+#   define DRSNX
+#   define mach_type_known
+# endif
+# if defined(_IBMR2)
+#   define POWERPC
+#   define AIX
+#   define mach_type_known
+# endif
+# if defined(__NetBSD__) && defined(__sparc__)
+#   define SPARC
+#   define mach_type_known
+# endif
+# if defined(_M_XENIX) && defined(_M_SYSV) && defined(_M_I386)
+        /* The above test may need refinement   */
+#   define I386
+#   if defined(_SCO_ELF)
+#     define SCO_ELF
+#   else
+#     define SCO
+#   endif
+#   define mach_type_known
+# endif
+# if defined(_AUX_SOURCE)
+#   error A/UX no longer supported
+# endif
+# if defined(_PA_RISC1_0) || defined(_PA_RISC1_1) || defined(_PA_RISC2_0) \
+     || defined(hppa) || defined(__hppa__)
+#   define HP_PA
+#   if !defined(LINUX) && !defined(HPUX) && !defined(OPENBSD)
+#     define HPUX
+#   endif
+#   define mach_type_known
+# endif
+# if defined(__ia64) && (defined(_HPUX_SOURCE) || defined(__HP_aCC))
+#   define IA64
+#   ifndef HPUX
+#     define HPUX
+#   endif
+#   define mach_type_known
+# endif
+# if defined(__BEOS__) && defined(_X86_)
+#    define I386
+#    define BEOS
+#    define mach_type_known
+# endif
+# if defined(OPENBSD) && defined(__amd64__)
+#    define X86_64
+#    define mach_type_known
+# endif
+# if defined(LINUX) && (defined(i386) || defined(__i386__))
+#    define I386
+#    define mach_type_known
+# endif
+# if defined(LINUX) && defined(__x86_64__)
+#    define X86_64
+#    define mach_type_known
+# endif
+# if defined(LINUX) && (defined(__ia64__) || defined(__ia64))
+#    define IA64
+#    define mach_type_known
+# endif
+# if defined(LINUX) && defined(__aarch64__)
+#    define AARCH64
+#    define mach_type_known
+# endif
+# if defined(LINUX) && (defined(__arm) || defined(__arm__))
+#    define ARM32
+#    define mach_type_known
+# endif
+# if defined(LINUX) && defined(__cris__)
+#    ifndef CRIS
+#       define CRIS
+#    endif
+#    define mach_type_known
+# endif
+# if defined(LINUX) && (defined(powerpc) || defined(__powerpc__) \
+                        || defined(powerpc64) || defined(__powerpc64__))
+#    define POWERPC
+#    define mach_type_known
+# endif
+# if defined(LINUX) && defined(__mc68000__)
+#    define M68K
+#    define mach_type_known
+# endif
+# if defined(LINUX) && (defined(sparc) || defined(__sparc__))
+#    define SPARC
+#    define mach_type_known
+# endif
+# if defined(LINUX) && defined(__sh__)
+#    define SH
+#    define mach_type_known
+# endif
+# if defined(LINUX) && defined(__avr32__)
+#    define AVR32
+#    define mach_type_known
+# endif
+# if defined(LINUX) && defined(__m32r__)
+#    define M32R
+#    define mach_type_known
+# endif
+# if defined(__alpha) || defined(__alpha__)
+#   define ALPHA
+#   if !defined(LINUX) && !defined(NETBSD) && !defined(OPENBSD) \
+       && !defined(FREEBSD)
+#     define OSF1       /* a.k.a Digital Unix */
+#   endif
+#   define mach_type_known
+# endif
+# if defined(_AMIGA) && !defined(AMIGA)
+#   define AMIGA
+# endif
+# ifdef AMIGA
+#   define M68K
+#   define mach_type_known
+# endif
+# if defined(THINK_C) \
+     || (defined(__MWERKS__) && !defined(__powerc) && !defined(SYMBIAN))
+#   define M68K
+#   define MACOS
+#   define mach_type_known
+# endif
+# if defined(__MWERKS__) && defined(__powerc) && !defined(__MACH__) \
+     && !defined(SYMBIAN)
+#   define POWERPC
+#   define MACOS
+#   define mach_type_known
+# endif
+# if defined(__OpenBSD__) && defined(__powerpc__)
+#   define POWERPC
+#   define OPENBSD
+#   define mach_type_known
+# endif
+# if defined(DARWIN)
+#   if defined(__ppc__)  || defined(__ppc64__)
+#    define POWERPC
+#    define mach_type_known
+#   elif defined(__x86_64__) || defined(__x86_64)
+#    define X86_64
+#    define mach_type_known
+#   elif defined(__i386__)
+#    define I386
+#    define mach_type_known
+#   elif defined(__arm__)
+#    define ARM32
+#    define mach_type_known
+#    define DARWIN_DONT_PARSE_STACK
+#   endif
+# endif
+# if defined(__rtems__) && (defined(i386) || defined(__i386__))
+#   define I386
+#   define RTEMS
+#   define mach_type_known
+# endif
+# if defined(NeXT) && defined(mc68000)
+#   define M68K
+#   define NEXT
+#   define mach_type_known
+# endif
+# if defined(NeXT) && (defined(i386) || defined(__i386__))
+#   define I386
+#   define NEXT
+#   define mach_type_known
+# endif
+# if defined(__OpenBSD__) && (defined(i386) || defined(__i386__))
+#   define I386
+#   define OPENBSD
+#   define mach_type_known
+# endif
+# if defined(__NetBSD__) && (defined(i386) || defined(__i386__))
+#   define I386
+#   define mach_type_known
+# endif
+# if defined(__NetBSD__) && defined(__x86_64__)
+#    define X86_64
+#    define mach_type_known
+# endif
+# if defined(FREEBSD) && (defined(i386) || defined(__i386__))
+#   define I386
+#   define mach_type_known
+# endif
+# if defined(FREEBSD) && (defined(__amd64__) || defined(__x86_64__))
+#   define X86_64
+#   define mach_type_known
+# endif
+# if defined(FREEBSD) && defined(__ia64__)
+#   define IA64
+#   define mach_type_known
+# endif
+# if defined(FREEBSD) && defined(__sparc__)
+#   define SPARC
+#   define mach_type_known
+# endif
+# if defined(FREEBSD) && (defined(powerpc) || defined(__powerpc__))
+#   define POWERPC
+#   define mach_type_known
+# endif
+# if defined(FREEBSD) && defined(__arm__)
+#   define ARM32
+#   define mach_type_known
+# endif
+# if defined(bsdi) && (defined(i386) || defined(__i386__))
+#    define I386
+#    define BSDI
+#    define mach_type_known
+# endif
+# if !defined(mach_type_known) && defined(__386BSD__)
+#   define I386
+#   define THREE86BSD
+#   define mach_type_known
+# endif
+# if defined(_CX_UX) && defined(_M88K)
+#   define M88K
+#   define CX_UX
+#   define mach_type_known
+# endif
+# if defined(DGUX) && defined(m88k)
+#   define M88K
+    /* DGUX defined */
+#   define mach_type_known
+# endif
+# if defined(_WIN32_WCE) || defined(__CEGCC__) || defined(__MINGW32CE__)
+    /* SH3, SH4, MIPS already defined for corresponding architectures */
+#   if defined(SH3) || defined(SH4)
+#     define SH
+#   endif
+#   if defined(x86) || defined(__i386__)
+#     define I386
+#   endif
+#   if defined(_M_ARM) || defined(ARM) || defined(_ARM_)
+#     define ARM32
+#   endif
+#   define MSWINCE
+#   define mach_type_known
+# else
+#   if ((defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300)) \
+       || (defined(_WIN32) && !defined(__CYGWIN32__) && !defined(__CYGWIN__) \
+           && !defined(SYMBIAN))
+#     if defined(__LP64__) || defined(_WIN64)
+#       define X86_64
+#     else
+#       define I386
+#     endif
+#     define MSWIN32    /* or Win64 */
+#     define mach_type_known
+#   endif
+#   if defined(_MSC_VER) && defined(_M_IA64)
+#     define IA64
+#     define MSWIN32    /* Really win64, but we don't treat 64-bit      */
+                        /* variants as a different platform.            */
+#   endif
+# endif
+# if defined(__DJGPP__)
+#   define I386
+#   ifndef DJGPP
+#     define DJGPP  /* MSDOS running the DJGPP port of GCC */
+#   endif
+#   define mach_type_known
+# endif
+# if defined(__CYGWIN32__) || defined(__CYGWIN__)
+#   define I386
+#   define CYGWIN32
+#   define mach_type_known
+# endif
+# if defined(__MINGW32__) && !defined(mach_type_known)
+#   define I386
+#   define MSWIN32
+#   define mach_type_known
+# endif
+# if defined(__BORLANDC__)
+#   define I386
+#   define MSWIN32
+#   define mach_type_known
+# endif
+# if defined(_UTS) && !defined(mach_type_known)
+#   define S370
+#   define UTS4
+#   define mach_type_known
+# endif
+# if defined(__pj__)
+#   error PicoJava no longer supported
+    /* The implementation had problems, and I haven't heard of users    */
+    /* in ages.  If you want it resurrected, let me know.               */
+# endif
+# if defined(__embedded__) && defined(PPC)
+#   define POWERPC
+#   define NOSYS
+#   define mach_type_known
+# endif
+
+# if defined(__WATCOMC__) && defined(__386__)
+#   define I386
+#   if !defined(OS2) && !defined(MSWIN32) && !defined(DOS4GW)
+#     if defined(__OS2__)
+#       define OS2
+#     else
+#       if defined(__WINDOWS_386__) || defined(__NT__)
+#         define MSWIN32
+#       else
+#         define DOS4GW
+#       endif
+#     endif
+#   endif
+#   define mach_type_known
+# endif
+# if defined(__s390__) && defined(LINUX)
+#    define S390
+#    define mach_type_known
+# endif
+# if defined(__GNU__)
+#   if defined(__i386__)
+/* The Debian Hurd running on generic PC */
+#     define  HURD
+#     define  I386
+#     define  mach_type_known
+#    endif
+# endif
+# if defined(__TANDEM)
+    /* Nonstop S-series */
+    /* FIXME: Should recognize Integrity series? */
+#   define MIPS
+#   define NONSTOP
+#   define mach_type_known
+# endif
+# if defined(__hexagon__) && defined(LINUX)
+#    define HEXAGON
+#    define mach_type_known
+# endif
+
+# if defined(SYMBIAN)
+#   define mach_type_known
+# endif
+
+/* Feel free to add more clauses here */
+
+/* Or manually define the machine type here.  A machine type is         */
+/* characterized by the architecture.  Some                             */
+/* machine types are further subdivided by OS.                          */
+/* Macros such as LINUX, FREEBSD, etc. distinguish them.                */
+/* SYSV on an M68K actually means A/UX.                                 */
+/* The distinction in these cases is usually the stack starting address */
+# ifndef mach_type_known
+#   error "The collector has not been ported to this machine/OS combination."
+# endif
+                    /* Mapping is: M68K       ==> Motorola 680X0        */
+                    /*             (NEXT, and SYSV (A/UX),              */
+                    /*             MACOS and AMIGA variants)            */
+                    /*             I386       ==> Intel 386             */
+                    /*              (SEQUENT, OS2, SCO, LINUX, NETBSD,  */
+                    /*               FREEBSD, THREE86BSD, MSWIN32,      */
+                    /*               BSDI, SOLARIS, NEXT and others)    */
+                    /*             NS32K      ==> Encore Multimax       */
+                    /*             MIPS       ==> R2000 through R14K    */
+                    /*                  (many variants)                 */
+                    /*             VAX        ==> DEC VAX               */
+                    /*                  (BSD, ULTRIX variants)          */
+                    /*             HP_PA      ==> HP9000/700 & /800     */
+                    /*                            HP/UX, LINUX          */
+                    /*             SPARC      ==> SPARC v7/v8/v9        */
+                    /*                  (SOLARIS, LINUX, DRSNX variants)        */
+                    /*             ALPHA      ==> DEC Alpha             */
+                    /*                  (OSF1 and LINUX variants)       */
+                    /*             M88K       ==> Motorola 88XX0        */
+                    /*                  (CX_UX and DGUX)                */
+                    /*             S370       ==> 370-like machine      */
+                    /*                  running Amdahl UTS4             */
+                    /*             S390       ==> 390-like machine      */
+                    /*                  running LINUX                   */
+                    /*             AARCH64    ==> ARM AArch64           */
+                    /*             ARM32      ==> Intel StrongARM       */
+                    /*             IA64       ==> Intel IPF             */
+                    /*                            (e.g. Itanium)        */
+                    /*                  (LINUX and HPUX)                */
+                    /*             SH         ==> Hitachi SuperH        */
+                    /*                  (LINUX & MSWINCE)               */
+                    /*             X86_64     ==> AMD x86-64            */
+                    /*             POWERPC    ==> IBM/Apple PowerPC     */
+                    /*                  (MACOS(<=9),DARWIN(incl.MACOSX),*/
+                    /*                   LINUX, NETBSD, AIX, NOSYS      */
+                    /*                   variants)                      */
+                    /*                  Handles 32 and 64-bit variants. */
+                    /*             CRIS       ==> Axis Etrax            */
+                    /*             M32R       ==> Renesas M32R          */
+                    /*             HEXAGON    ==> Qualcomm Hexagon      */
+
+
+/*
+ * For each architecture and OS, the following need to be defined:
+ *
+ * CPP_WORDSZ is a simple integer constant representing the word size.
+ * in bits.  We assume byte addressability, where a byte has 8 bits.
+ * We also assume CPP_WORDSZ is either 32 or 64.
+ * (We care about the length of pointers, not hardware
+ * bus widths.  Thus a 64 bit processor with a C compiler that uses
+ * 32 bit pointers should use CPP_WORDSZ of 32, not 64. Default is 32.)
+ *
+ * MACH_TYPE is a string representation of the machine type.
+ * OS_TYPE is analogous for the OS.
+ *
+ * ALIGNMENT is the largest N, such that
+ * all pointer are guaranteed to be aligned on N byte boundaries.
+ * defining it to be 1 will always work, but perform poorly.
+ *
+ * DATASTART is the beginning of the data segment.
+ * On some platforms SEARCH_FOR_DATA_START is defined.
+ * SEARCH_FOR_DATASTART will cause GC_data_start to
+ * be set to an address determined by accessing data backwards from _end
+ * until an unmapped page is found.  DATASTART will be defined to be
+ * GC_data_start.
+ * On UNIX-like systems, the collector will scan the area between DATASTART
+ * and DATAEND for root pointers.
+ *
+ * DATAEND, if not "end", where "end" is defined as "extern int end[]".
+ * RTH suggests gaining access to linker script synth'd values with
+ * this idiom instead of "&end", where "end" is defined as "extern int end".
+ * Otherwise, "GCC will assume these are in .sdata/.sbss" and it will, e.g.,
+ * cause failures on alpha*-*-* with -msmall-data or -fpic or mips-*-*
+ * without any special options.
+ *
+ * STACKBOTTOM is the cool end of the stack, which is usually the
+ * highest address in the stack.
+ * Under PCR or OS/2, we have other ways of finding thread stacks.
+ * For each machine, the following should:
+ * 1) define STACK_GROWS_UP if the stack grows toward higher addresses, and
+ * 2) define exactly one of
+ *      STACKBOTTOM (should be defined to be an expression)
+ *      LINUX_STACKBOTTOM
+ *      HEURISTIC1
+ *      HEURISTIC2
+ * If STACKBOTTOM is defined, then it's value will be used directly as the
+ * stack base.  If LINUX_STACKBOTTOM is defined, then it will be determined
+ * with a method appropriate for most Linux systems.  Currently we look
+ * first for __libc_stack_end (currently only if USE_LIBC_PRIVATES is
+ * defined), and if that fails read it from /proc.  (If USE_LIBC_PRIVATES
+ * is not defined and NO_PROC_STAT is defined, we revert to HEURISTIC2.)
+ * If either of the last two macros are defined, then STACKBOTTOM is computed
+ * during collector startup using one of the following two heuristics:
+ * HEURISTIC1:  Take an address inside GC_init's frame, and round it up to
+ *              the next multiple of STACK_GRAN.
+ * HEURISTIC2:  Take an address inside GC_init's frame, increment it repeatedly
+ *              in small steps (decrement if STACK_GROWS_UP), and read the value
+ *              at each location.  Remember the value when the first
+ *              Segmentation violation or Bus error is signaled.  Round that
+ *              to the nearest plausible page boundary, and use that instead
+ *              of STACKBOTTOM.
+ *
+ * Gustavo Rodriguez-Rivera points out that on most (all?) Unix machines,
+ * the value of environ is a pointer that can serve as STACKBOTTOM.
+ * I expect that HEURISTIC2 can be replaced by this approach, which
+ * interferes far less with debugging.  However it has the disadvantage
+ * that it's confused by a putenv call before the collector is initialized.
+ * This could be dealt with by intercepting putenv ...
+ *
+ * If no expression for STACKBOTTOM can be found, and neither of the above
+ * heuristics are usable, the collector can still be used with all of the above
+ * undefined, provided one of the following is done:
+ * 1) GC_mark_roots can be changed to somehow mark from the correct stack(s)
+ *    without reference to STACKBOTTOM.  This is appropriate for use in
+ *    conjunction with thread packages, since there will be multiple stacks.
+ *    (Allocating thread stacks in the heap, and treating them as ordinary
+ *    heap data objects is also possible as a last resort.  However, this is
+ *    likely to introduce significant amounts of excess storage retention
+ *    unless the dead parts of the thread stacks are periodically cleared.)
+ * 2) Client code may set GC_stackbottom before calling any GC_ routines.
+ *    If the author of the client code controls the main program, this is
+ *    easily accomplished by introducing a new main program, setting
+ *    GC_stackbottom to the address of a local variable, and then calling
+ *    the original main program.  The new main program would read something
+ *    like (provided real_main() is not inlined by the compiler):
+ *
+ *              # include "gc_private.h"
+ *
+ *              main(argc, argv, envp)
+ *              int argc;
+ *              char **argv, **envp;
+ *              {
+ *                  volatile int dummy;
+ *
+ *                  GC_stackbottom = (ptr_t)(&dummy);
+ *                  return(real_main(argc, argv, envp));
+ *              }
+ *
+ *
+ * Each architecture may also define the style of virtual dirty bit
+ * implementation to be used:
+ *   MPROTECT_VDB: Write protect the heap and catch faults.
+ *   GWW_VDB: Use win32 GetWriteWatch primitive.
+ *   PROC_VDB: Use the SVR4 /proc primitives to read dirty bits.
+ *
+ * The first and second one may be combined, in which case a runtime
+ * selection will be made, based on GetWriteWatch availability.
+ *
+ * An architecture may define DYNAMIC_LOADING if dyn_load.c
+ * defined GC_register_dynamic_libraries() for the architecture.
+ *
+ * An architecture may define PREFETCH(x) to preload the cache with *x.
+ * This defaults to GCC built-in operation (or a no-op for other compilers).
+ *
+ * PREFETCH_FOR_WRITE(x) is used if *x is about to be written.
+ *
+ * An architecture may also define CLEAR_DOUBLE(x) to be a fast way to
+ * clear the two words at GC_malloc-aligned address x.  By default,
+ * word stores of 0 are used instead.
+ *
+ * HEAP_START may be defined as the initial address hint for mmap-based
+ * allocation.
+ */
+
+/* If we are using a recent version of gcc, we can use                    */
+/* __builtin_unwind_init() to push the relevant registers onto the stack. */
+# if defined(__GNUC__) && ((__GNUC__ >= 3) \
+                           || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8)) \
+     && !defined(__INTEL_COMPILER) && !defined(__PATHCC__) \
+     && !(defined(POWERPC) && defined(DARWIN)) /* for MacOS X 10.3.9 */ \
+     && !defined(RTEMS) \
+     && !defined(__clang__) /* since no-op in clang (3.0) */
+#   define HAVE_BUILTIN_UNWIND_INIT
+# endif
+
+# ifdef SYMBIAN
+#   define MACH_TYPE "SYMBIAN"
+#   define OS_TYPE "SYMBIAN"
+#   define CPP_WORDSZ 32
+#   define ALIGNMENT 4
+#   define DATASTART NULL
+#   define DATAEND NULL
+# endif
+
+# define STACK_GRAN 0x1000000
+# ifdef M68K
+#   define MACH_TYPE "M68K"
+#   define ALIGNMENT 2
+#   ifdef OPENBSD
+        /* FIXME: Should we remove this case? */
+#       define OS_TYPE "OPENBSD"
+#       define HEURISTIC2
+#       ifdef __ELF__
+          extern ptr_t GC_data_start;
+#         define DATASTART GC_data_start
+#         define DYNAMIC_LOADING
+#       else
+          extern char etext[];
+#         define DATASTART ((ptr_t)(etext))
+#       endif
+#   endif
+#   ifdef NETBSD
+#       define OS_TYPE "NETBSD"
+#       define HEURISTIC2
+#       ifdef __ELF__
+          extern ptr_t GC_data_start;
+#         define DATASTART GC_data_start
+#         define DYNAMIC_LOADING
+#       else
+          extern char etext[];
+#         define DATASTART ((ptr_t)(etext))
+#       endif
+#   endif
+#   ifdef LINUX
+#       define OS_TYPE "LINUX"
+#       define LINUX_STACKBOTTOM
+#       define MPROTECT_VDB
+#       ifdef __ELF__
+#            define DYNAMIC_LOADING
+#            include <features.h>
+#            if defined(__GLIBC__) && __GLIBC__ >= 2
+#              define SEARCH_FOR_DATA_START
+#            else /* !GLIBC2 */
+#              ifdef PLATFORM_ANDROID
+#                define __environ environ
+#              endif
+               extern char **__environ;
+#              define DATASTART ((ptr_t)(&__environ))
+                             /* hideous kludge: __environ is the first */
+                             /* word in crt0.o, and delimits the start */
+                             /* of the data segment, no matter which   */
+                             /* ld options were passed through.        */
+                             /* We could use _etext instead, but that  */
+                             /* would include .rodata, which may       */
+                             /* contain large read-only data tables    */
+                             /* that we'd rather not scan.             */
+#            endif /* !GLIBC2 */
+             extern int _end[];
+#            define DATAEND (ptr_t)(_end)
+#       else
+             extern int etext[];
+#            define DATASTART ((ptr_t)((((word) (etext)) + 0xfff) & ~0xfff))
+#       endif
+#   endif
+#   ifdef AMIGA
+#       define OS_TYPE "AMIGA"
+                /* STACKBOTTOM and DATASTART handled specially  */
+                /* in os_dep.c                                  */
+#       define DATAEND  /* not needed */
+#       define GETPAGESIZE() 4096
+#   endif
+#   ifdef MACOS
+#     ifndef __LOWMEM__
+#     include <LowMem.h>
+#     endif
+#     define OS_TYPE "MACOS"
+                /* see os_dep.c for details of global data segments. */
+#     define STACKBOTTOM ((ptr_t) LMGetCurStackBase())
+#     define DATAEND    /* not needed */
+#     define GETPAGESIZE() 4096
+#   endif
+#   ifdef NEXT
+#       define OS_TYPE "NEXT"
+#       define DATASTART ((ptr_t) get_etext())
+#       define DATASTART_IS_FUNC
+#       define STACKBOTTOM ((ptr_t) 0x4000000)
+#       define DATAEND  /* not needed */
+#   endif
+# endif
+
+# if defined(POWERPC)
+#   define MACH_TYPE "POWERPC"
+#   ifdef MACOS
+#     define ALIGNMENT 2  /* Still necessary?  Could it be 4?   */
+#     ifndef __LOWMEM__
+#     include <LowMem.h>
+#     endif
+#     define OS_TYPE "MACOS"
+                        /* see os_dep.c for details of global data segments. */
+#     define STACKBOTTOM ((ptr_t) LMGetCurStackBase())
+#     define DATAEND  /* not needed */
+#   endif
+#   ifdef LINUX
+#     if defined(__powerpc64__)
+#       define ALIGNMENT 8
+#       define CPP_WORDSZ 64
+#       ifndef HBLKSIZE
+#         define HBLKSIZE 4096
+#       endif
+#     else
+#       define ALIGNMENT 4
+#     endif
+#     define OS_TYPE "LINUX"
+      /* HEURISTIC1 has been reliably reported to fail for a 32-bit     */
+      /* executable on a 64 bit kernel.                                 */
+#     define LINUX_STACKBOTTOM
+#     define DYNAMIC_LOADING
+#     define SEARCH_FOR_DATA_START
+      extern int _end[];
+#     define DATAEND (ptr_t)(_end)
+#   endif
+#   ifdef DARWIN
+#     define OS_TYPE "DARWIN"
+#     define DYNAMIC_LOADING
+#     if defined(__ppc64__)
+#       define ALIGNMENT 8
+#       define CPP_WORDSZ 64
+#       define STACKBOTTOM ((ptr_t) 0x7fff5fc00000)
+#       define CACHE_LINE_SIZE 64
+#       ifndef HBLKSIZE
+#         define HBLKSIZE 4096
+#       endif
+#     else
+#       define ALIGNMENT 4
+#       define STACKBOTTOM ((ptr_t) 0xc0000000)
+#     endif
+      /* XXX: see get_end(3), get_etext() and get_end() should not be used. */
+      /* These aren't used when dyld support is enabled (it is by default). */
+#     define DATASTART ((ptr_t) get_etext())
+#     define DATAEND   ((ptr_t) get_end())
+#     ifndef USE_MMAP
+#       define USE_MMAP
+#     endif
+#     define USE_MMAP_ANON
+#     define MPROTECT_VDB
+#     include <unistd.h>
+#     define GETPAGESIZE() getpagesize()
+#     if defined(USE_PPC_PREFETCH) && defined(__GNUC__)
+        /* The performance impact of prefetches is untested */
+#       define PREFETCH(x) \
+          __asm__ __volatile__ ("dcbt 0,%0" : : "r" ((const void *) (x)))
+#       define PREFETCH_FOR_WRITE(x) \
+          __asm__ __volatile__ ("dcbtst 0,%0" : : "r" ((const void *) (x)))
+#     endif
+      /* There seems to be some issues with trylock hanging on darwin.  */
+      /* This should be looked into some more.                          */
+#     define NO_PTHREAD_TRYLOCK
+#   endif
+#   ifdef OPENBSD
+#     define OS_TYPE "OPENBSD"
+#     define ALIGNMENT 4
+#     ifndef GC_OPENBSD_THREADS
+#       include <sys/param.h>
+#       include <uvm/uvm_extern.h>
+#       define STACKBOTTOM ((ptr_t) USRSTACK)
+#     endif
+      extern int __data_start[];
+#     define DATASTART ((ptr_t)__data_start)
+      extern char _end[];
+#     define DATAEND ((ptr_t)(&_end))
+#     define DYNAMIC_LOADING
+#   endif
+#   ifdef FREEBSD
+#       if defined(__powerpc64__)
+#           define ALIGNMENT 8
+#           define CPP_WORDSZ 64
+#           ifndef HBLKSIZE
+#               define HBLKSIZE 4096
+#           endif
+#       else
+#           define ALIGNMENT 4
+#       endif
+#       define OS_TYPE "FREEBSD"
+#       ifndef GC_FREEBSD_THREADS
+#           define MPROTECT_VDB
+#       endif
+#       define SIG_SUSPEND SIGUSR1
+#       define SIG_THR_RESTART SIGUSR2
+#       define FREEBSD_STACKBOTTOM
+#       ifdef __ELF__
+#           define DYNAMIC_LOADING
+#       endif
+        extern char etext[];
+        ptr_t GC_FreeBSDGetDataStart(size_t, ptr_t);
+#       define DATASTART GC_FreeBSDGetDataStart(0x1000, (ptr_t)etext)
+#       define DATASTART_IS_FUNC
+#   endif
+#   ifdef NETBSD
+#     define ALIGNMENT 4
+#     define OS_TYPE "NETBSD"
+#     define HEURISTIC2
+      extern ptr_t GC_data_start;
+#     define DATASTART GC_data_start
+#     define DYNAMIC_LOADING
+#   endif
+#   ifdef SN_TARGET_PS3
+#     define NO_GETENV
+#     define CPP_WORDSZ 32
+#     define ALIGNMENT 4
+      extern int _end [];
+      extern int __bss_start;
+#     define DATAEND (ptr_t)(_end)
+#     define DATASTART (ptr_t)(__bss_start)
+#     define STACKBOTTOM ((ptr_t)ps3_get_stack_bottom())
+#     define NO_PTHREAD_TRYLOCK
+                /* Current GC LOCK() implementation for PS3 explicitly  */
+                /* use pthread_mutex_lock for some reason.              */
+#   endif
+#   ifdef AIX
+#     define OS_TYPE "AIX"
+#     undef ALIGNMENT /* in case it's defined   */
+#     undef IA64
+      /* DOB: some AIX installs stupidly define IA64 in */
+      /* /usr/include/sys/systemcfg.h                   */
+#     ifdef __64BIT__
+#       define ALIGNMENT 8
+#       define CPP_WORDSZ 64
+#       define STACKBOTTOM ((ptr_t)0x1000000000000000)
+#     else
+#       define ALIGNMENT 4
+#       define CPP_WORDSZ 32
+#       define STACKBOTTOM ((ptr_t)((ulong)&errno))
+#     endif
+#     ifndef USE_MMAP
+#       define USE_MMAP
+#     endif
+#     define USE_MMAP_ANON
+        /* From AIX linker man page:
+        _text Specifies the first location of the program.
+        _etext Specifies the first location after the program.
+        _data Specifies the first location of the data.
+        _edata Specifies the first location after the initialized data
+        _end or end Specifies the first location after all data.
+        */
+      extern int _data[], _end[];
+#     define DATASTART ((ptr_t)((ulong)_data))
+#     define DATAEND ((ptr_t)((ulong)_end))
+      extern int errno;
+#     define DYNAMIC_LOADING
+        /* For really old versions of AIX, this may have to be removed. */
+#   endif
+
+#   ifdef NOSYS
+#     define ALIGNMENT 4
+#     define OS_TYPE "NOSYS"
+      extern void __end[], __dso_handle[];
+#     define DATASTART (__dso_handle)  /* OK, that's ugly.  */
+#     define DATAEND (ptr_t)(__end)
+        /* Stack starts at 0xE0000000 for the simulator.  */
+#     undef STACK_GRAN
+#     define STACK_GRAN 0x10000000
+#     define HEURISTIC1
+#   endif
+# endif
+
+# ifdef VAX
+#   define MACH_TYPE "VAX"
+#   define ALIGNMENT 4  /* Pointers are longword aligned by 4.2 C compiler */
+    extern char etext[];
+#   define DATASTART ((ptr_t)(etext))
+#   ifdef BSD
+#       define OS_TYPE "BSD"
+#       define HEURISTIC1
+                        /* HEURISTIC2 may be OK, but it's hard to test. */
+#   endif
+#   ifdef ULTRIX
+#       define OS_TYPE "ULTRIX"
+#       define STACKBOTTOM ((ptr_t) 0x7fffc800)
+#   endif
+# endif
+
+# ifdef SPARC
+#   define MACH_TYPE "SPARC"
+#   if defined(__arch64__) || defined(__sparcv9)
+#     define ALIGNMENT 8
+#     define CPP_WORDSZ 64
+#     define ELF_CLASS ELFCLASS64
+#   else
+#     define ALIGNMENT 4        /* Required by hardware */
+#     define CPP_WORDSZ 32
+#   endif
+    /* Don't define USE_ASM_PUSH_REGS.  We do use an asm helper, but    */
+    /* not to push the registers on the mark stack.                     */
+#   ifdef SOLARIS
+#       define OS_TYPE "SOLARIS"
+        extern int _etext[];
+        extern int _end[];
+        ptr_t GC_SysVGetDataStart(size_t, ptr_t);
+#       define DATASTART GC_SysVGetDataStart(0x10000, (ptr_t)_etext)
+#       define DATASTART_IS_FUNC
+#       define DATAEND (ptr_t)(_end)
+#       if !defined(USE_MMAP) && defined(REDIRECT_MALLOC)
+#         define USE_MMAP
+            /* Otherwise we now use calloc.  Mmap may result in the     */
+            /* heap interleaved with thread stacks, which can result in */
+            /* excessive blacklisting.  Sbrk is unusable since it       */
+            /* doesn't interact correctly with the system malloc.       */
+#       endif
+#       ifdef USE_MMAP
+#         define HEAP_START (ptr_t)0x40000000
+#       else
+#         define HEAP_START DATAEND
+#       endif
+#       define PROC_VDB
+/*      HEURISTIC1 reportedly no longer works under 2.7.                */
+/*      HEURISTIC2 probably works, but this appears to be preferable.   */
+/*      Apparently USRSTACK is defined to be USERLIMIT, but in some     */
+/*      installations that's undefined.  We work around this with a     */
+/*      gross hack:                                                     */
+#       include <sys/vmparam.h>
+#       ifdef USERLIMIT
+          /* This should work everywhere, but doesn't.  */
+#         define STACKBOTTOM ((ptr_t) USRSTACK)
+#       else
+#         define HEURISTIC2
+#       endif
+#       include <unistd.h>
+#       define GETPAGESIZE()  sysconf(_SC_PAGESIZE)
+                /* getpagesize() appeared to be missing from at least one */
+                /* Solaris 5.4 installation.  Weird.                      */
+#       define DYNAMIC_LOADING
+#   endif
+#   ifdef DRSNX
+#       define OS_TYPE "DRSNX"
+        ptr_t GC_SysVGetDataStart(size_t, ptr_t);
+        extern int etext[];
+#       define DATASTART GC_SysVGetDataStart(0x10000, (ptr_t)etext)
+#       define DATASTART_IS_FUNC
+#       define MPROTECT_VDB
+#       define STACKBOTTOM ((ptr_t) 0xdfff0000)
+#       define DYNAMIC_LOADING
+#   endif
+#   ifdef LINUX
+#     define OS_TYPE "LINUX"
+#     ifdef __ELF__
+#       define DYNAMIC_LOADING
+#     else
+#       error --> Linux SPARC a.out not supported
+#     endif
+      extern int _end[];
+      extern int _etext[];
+#     define DATAEND (ptr_t)(_end)
+#     define SVR4
+      ptr_t GC_SysVGetDataStart(size_t, ptr_t);
+#     ifdef __arch64__
+#       define DATASTART GC_SysVGetDataStart(0x100000, (ptr_t)_etext)
+#     else
+#       define DATASTART GC_SysVGetDataStart(0x10000, (ptr_t)_etext)
+#     endif
+#     define DATASTART_IS_FUNC
+#     define LINUX_STACKBOTTOM
+#   endif
+#   ifdef OPENBSD
+#     define OS_TYPE "OPENBSD"
+#     ifndef GC_OPENBSD_THREADS
+#       include <sys/param.h>
+#       include <uvm/uvm_extern.h>
+#       define STACKBOTTOM ((ptr_t) USRSTACK)
+#     endif
+      extern int __data_start[];
+#     define DATASTART ((ptr_t)__data_start)
+      extern char _end[];
+#     define DATAEND ((ptr_t)(&_end))
+#     define DYNAMIC_LOADING
+#   endif
+#   ifdef NETBSD
+#     define OS_TYPE "NETBSD"
+#     define HEURISTIC2
+#     ifdef __ELF__
+        extern ptr_t GC_data_start;
+#       define DATASTART GC_data_start
+#       define DYNAMIC_LOADING
+#     else
+        extern char etext[];
+#       define DATASTART ((ptr_t)(etext))
+#     endif
+#   endif
+#   ifdef FREEBSD
+#       define OS_TYPE "FREEBSD"
+#       define SIG_SUSPEND SIGUSR1
+#       define SIG_THR_RESTART SIGUSR2
+#       define FREEBSD_STACKBOTTOM
+#       ifdef __ELF__
+#           define DYNAMIC_LOADING
+#       endif
+        extern char etext[];
+        extern char edata[];
+        extern char end[];
+#       define NEED_FIND_LIMIT
+#       define DATASTART ((ptr_t)(&etext))
+        ptr_t GC_find_limit(ptr_t, GC_bool);
+#       define DATAEND (GC_find_limit (DATASTART, TRUE))
+#       define DATAEND_IS_FUNC
+#       define DATASTART2 ((ptr_t)(&edata))
+#       define DATAEND2 ((ptr_t)(&end))
+#   endif
+# endif
+
+# ifdef I386
+#   define MACH_TYPE "I386"
+#   if defined(__LP64__) || defined(_WIN64)
+#     error This should be handled as X86_64
+#   else
+#     define CPP_WORDSZ 32
+#     define ALIGNMENT 4
+                        /* Appears to hold for all "32 bit" compilers   */
+                        /* except Borland.  The -a4 option fixes        */
+                        /* Borland.  For Watcom the option is -zp4.     */
+#   endif
+#   ifdef SEQUENT
+#       define OS_TYPE "SEQUENT"
+        extern int etext[];
+#       define DATASTART ((ptr_t)((((word) (etext)) + 0xfff) & ~0xfff))
+#       define STACKBOTTOM ((ptr_t) 0x3ffff000)
+#   endif
+#   ifdef BEOS
+#     define OS_TYPE "BEOS"
+#     include <OS.h>
+#     define GETPAGESIZE() B_PAGE_SIZE
+      extern int etext[];
+#     define DATASTART ((ptr_t)((((word) (etext)) + 0xfff) & ~0xfff))
+#   endif
+#   ifdef SOLARIS
+#       define OS_TYPE "SOLARIS"
+        extern int _etext[], _end[];
+        ptr_t GC_SysVGetDataStart(size_t, ptr_t);
+#       define DATASTART GC_SysVGetDataStart(0x1000, (ptr_t)_etext)
+#       define DATASTART_IS_FUNC
+#       define DATAEND (ptr_t)(_end)
+/*      # define STACKBOTTOM ((ptr_t)(_start)) worked through 2.7,      */
+/*      but reportedly breaks under 2.8.  It appears that the stack     */
+/*      base is a property of the executable, so this should not break  */
+/*      old executables.                                                */
+/*      HEURISTIC2 probably works, but this appears to be preferable.   */
+#       include <sys/vm.h>
+#       define STACKBOTTOM ((ptr_t) USRSTACK)
+/* At least in Solaris 2.5, PROC_VDB gives wrong values for dirty bits. */
+/* It appears to be fixed in 2.8 and 2.9.                               */
+#       ifdef SOLARIS25_PROC_VDB_BUG_FIXED
+#         define PROC_VDB
+#       endif
+#       ifndef GC_THREADS
+#         define MPROTECT_VDB
+#       endif
+#       define DYNAMIC_LOADING
+#       if !defined(USE_MMAP) && defined(REDIRECT_MALLOC)
+#         define USE_MMAP
+            /* Otherwise we now use calloc.  Mmap may result in the     */
+            /* heap interleaved with thread stacks, which can result in */
+            /* excessive blacklisting.  Sbrk is unusable since it       */
+            /* doesn't interact correctly with the system malloc.       */
+#       endif
+#       ifdef USE_MMAP
+#         define HEAP_START (ptr_t)0x40000000
+#       else
+#         define HEAP_START DATAEND
+#       endif
+#   endif
+#   ifdef SCO
+#       define OS_TYPE "SCO"
+        extern int etext[];
+#       define DATASTART ((ptr_t)((((word) (etext)) + 0x3fffff) \
+                                  & ~0x3fffff) \
+                                 +((word)etext & 0xfff))
+#       define STACKBOTTOM ((ptr_t) 0x7ffffffc)
+#   endif
+#   ifdef SCO_ELF
+#       define OS_TYPE "SCO_ELF"
+        extern int etext[];
+#       define DATASTART ((ptr_t)(etext))
+#       define STACKBOTTOM ((ptr_t) 0x08048000)
+#       define DYNAMIC_LOADING
+#       define ELF_CLASS ELFCLASS32
+#   endif
+#   ifdef DGUX
+#       define OS_TYPE "DGUX"
+        extern int _etext, _end;
+        ptr_t GC_SysVGetDataStart(size_t, ptr_t);
+#       define DATASTART GC_SysVGetDataStart(0x1000, (ptr_t)(&_etext))
+#       define DATASTART_IS_FUNC
+#       define DATAEND (ptr_t)(&_end)
+#       define STACK_GROWS_DOWN
+#       define HEURISTIC2
+#       include <unistd.h>
+#       define GETPAGESIZE()  sysconf(_SC_PAGESIZE)
+#       define DYNAMIC_LOADING
+#       ifndef USE_MMAP
+#         define USE_MMAP
+#       endif
+#       define MAP_FAILED (void *) ((word)-1)
+#       ifdef USE_MMAP
+#         define HEAP_START (ptr_t)0x40000000
+#       else
+#         define HEAP_START DATAEND
+#       endif
+#   endif /* DGUX */
+
+#   ifdef NACL
+#      define OS_TYPE "NACL"
+       extern int etext[];
+/* #define DATASTART ((ptr_t)((((word) (etext)) + 0xfff) & ~0xfff)) */
+#      define DATASTART ((ptr_t)0x10000000)
+       extern int _end[];
+#      define DATAEND (_end)
+#      undef STACK_GRAN
+#      define STACK_GRAN 0x10000
+#      define HEURISTIC1
+#      define NO_PTHREAD_GETATTR_NP
+#      define GETPAGESIZE() 65536
+#      ifndef MAX_NACL_GC_THREADS
+#        define MAX_NACL_GC_THREADS 1024
+#      endif
+#   endif /* NACL */
+
+#   ifdef LINUX
+#       define OS_TYPE "LINUX"
+#       define LINUX_STACKBOTTOM
+#       if 0
+#         define HEURISTIC1
+#         undef STACK_GRAN
+#         define STACK_GRAN 0x10000000
+          /* STACKBOTTOM is usually 0xc0000000, but this changes with   */
+          /* different kernel configurations.  In particular, systems   */
+          /* with 2GB physical memory will usually move the user        */
+          /* address space limit, and hence initial SP to 0x80000000.   */
+#       endif
+#       if !defined(GC_LINUX_THREADS) || !defined(REDIRECT_MALLOC)
+#           define MPROTECT_VDB
+#       else
+            /* We seem to get random errors in incremental mode,        */
+            /* possibly because Linux threads is itself a malloc client */
+            /* and can't deal with the signals.                         */
+#       endif
+#       define HEAP_START (ptr_t)0x1000
+                /* This encourages mmap to give us low addresses,       */
+                /* thus allowing the heap to grow to ~3GB               */
+#       ifdef __ELF__
+#            define DYNAMIC_LOADING
+#            ifdef UNDEFINED    /* includes ro data */
+               extern int _etext[];
+#              define DATASTART ((ptr_t)((((word) (_etext)) + 0xfff) & ~0xfff))
+#            endif
+#            include <features.h>
+#            if defined(__GLIBC__) && __GLIBC__ >= 2 \
+                || defined(PLATFORM_ANDROID)
+#                define SEARCH_FOR_DATA_START
+#            else
+                 extern char **__environ;
+#                define DATASTART ((ptr_t)(&__environ))
+                              /* hideous kludge: __environ is the first */
+                              /* word in crt0.o, and delimits the start */
+                              /* of the data segment, no matter which   */
+                              /* ld options were passed through.        */
+                              /* We could use _etext instead, but that  */
+                              /* would include .rodata, which may       */
+                              /* contain large read-only data tables    */
+                              /* that we'd rather not scan.             */
+#            endif
+             extern int _end[];
+#            define DATAEND (ptr_t)(_end)
+#            if defined(PLATFORM_ANDROID) && !defined(GC_NO_SIGSETJMP)
+               /* As of Android NDK r8b, _sigsetjmp is still missing    */
+               /* for x86 (setjmp is used instead to find data_start).  */
+#              define GC_NO_SIGSETJMP
+#            endif
+#       else
+             extern int etext[];
+#            define DATASTART ((ptr_t)((((word) (etext)) + 0xfff) & ~0xfff))
+#       endif
+#       ifdef USE_I686_PREFETCH
+#         define PREFETCH(x) \
+            __asm__ __volatile__ ("prefetchnta %0" : : "m"(*(char *)(x)))
+            /* Empirically prefetcht0 is much more effective at reducing     */
+            /* cache miss stalls for the targeted load instructions.  But it */
+            /* seems to interfere enough with other cache traffic that the   */
+            /* net result is worse than prefetchnta.                         */
+#         ifdef FORCE_WRITE_PREFETCH
+            /* Using prefetches for write seems to have a slight negative    */
+            /* impact on performance, at least for a PIII/500.               */
+#           define PREFETCH_FOR_WRITE(x) \
+              __asm__ __volatile__ ("prefetcht0 %0" : : "m"(*(char *)(x)))
+#         else
+#           define NO_PREFETCH_FOR_WRITE
+#         endif
+#       elif defined(USE_3DNOW_PREFETCH)
+#         define PREFETCH(x) \
+            __asm__ __volatile__ ("prefetch %0" : : "m"(*(char *)(x)))
+#         define PREFETCH_FOR_WRITE(x) \
+            __asm__ __volatile__ ("prefetchw %0" : : "m"(*(char *)(x)))
+#       endif
+#   endif
+#   ifdef CYGWIN32
+#       define OS_TYPE "CYGWIN32"
+#       define DATASTART ((ptr_t)GC_DATASTART)  /* From gc.h */
+#       define DATAEND   ((ptr_t)GC_DATAEND)
+#       undef STACK_GRAN
+#       define STACK_GRAN 0x10000
+#       ifdef USE_MMAP
+#         define NEED_FIND_LIMIT
+#         define USE_MMAP_ANON
+#       endif
+#   endif
+#   ifdef OS2
+#       define OS_TYPE "OS2"
+                /* STACKBOTTOM and DATASTART are handled specially in   */
+                /* os_dep.c. OS2 actually has the right                 */
+                /* system call!                                         */
+#       define DATAEND  /* not needed */
+#   endif
+#   ifdef MSWIN32
+#       define OS_TYPE "MSWIN32"
+                /* STACKBOTTOM and DATASTART are handled specially in   */
+                /* os_dep.c.                                            */
+#       define MPROTECT_VDB
+#       define GWW_VDB
+#       define DATAEND  /* not needed */
+#   endif
+#   ifdef MSWINCE
+#       define OS_TYPE "MSWINCE"
+#       define DATAEND  /* not needed */
+#   endif
+#   ifdef DJGPP
+#       define OS_TYPE "DJGPP"
+#       include "stubinfo.h"
+        extern int etext[];
+        extern int _stklen;
+        extern int __djgpp_stack_limit;
+#       define DATASTART ((ptr_t)((((word) (etext)) + 0x1ff) & ~0x1ff))
+/* #define STACKBOTTOM ((ptr_t)((word)_stubinfo+_stubinfo->size+_stklen)) */
+#       define STACKBOTTOM ((ptr_t)((word) __djgpp_stack_limit + _stklen))
+                /* This may not be right.  */
+#   endif
+#   ifdef OPENBSD
+#       define OS_TYPE "OPENBSD"
+#       ifndef GC_OPENBSD_THREADS
+#         include <sys/param.h>
+#         include <uvm/uvm_extern.h>
+#         define STACKBOTTOM ((ptr_t) USRSTACK)
+#       endif
+        extern int __data_start[];
+#       define DATASTART ((ptr_t)__data_start)
+        extern char _end[];
+#       define DATAEND ((ptr_t)(&_end))
+#       define DYNAMIC_LOADING
+#   endif
+#   ifdef FREEBSD
+#       define OS_TYPE "FREEBSD"
+#       ifndef GC_FREEBSD_THREADS
+#           define MPROTECT_VDB
+#       endif
+#       ifdef __GLIBC__
+#           define SIG_SUSPEND          (32+6)
+#           define SIG_THR_RESTART      (32+5)
+            extern int _end[];
+#           define DATAEND (ptr_t)(_end)
+#       else
+#           define SIG_SUSPEND SIGUSR1
+#           define SIG_THR_RESTART SIGUSR2
+                /* SIGTSTP and SIGCONT could be used alternatively.     */
+#       endif
+#       define FREEBSD_STACKBOTTOM
+#       ifdef __ELF__
+#           define DYNAMIC_LOADING
+#       endif
+        extern char etext[];
+        char * GC_FreeBSDGetDataStart(size_t, ptr_t);
+#       define DATASTART GC_FreeBSDGetDataStart(0x1000, (ptr_t)etext)
+#       define DATASTART_IS_FUNC
+#   endif
+#   ifdef NETBSD
+#       define OS_TYPE "NETBSD"
+#       ifdef __ELF__
+#           define DYNAMIC_LOADING
+#       endif
+#   endif
+#   ifdef THREE86BSD
+#       define OS_TYPE "THREE86BSD"
+#   endif
+#   ifdef BSDI
+#       define OS_TYPE "BSDI"
+#   endif
+#   if defined(NETBSD) || defined(THREE86BSD) || defined(BSDI)
+#       define HEURISTIC2
+        extern char etext[];
+#       define DATASTART ((ptr_t)(etext))
+#   endif
+#   ifdef NEXT
+#       define OS_TYPE "NEXT"
+#       define DATASTART ((ptr_t) get_etext())
+#       define DATASTART_IS_FUNC
+#       define STACKBOTTOM ((ptr_t)0xc0000000)
+#       define DATAEND  /* not needed */
+#   endif
+#   ifdef RTEMS
+#       define OS_TYPE "RTEMS"
+#       include <sys/unistd.h>
+        extern int etext[];
+        extern int end[];
+        void *rtems_get_stack_bottom(void);
+#       define InitStackBottom rtems_get_stack_bottom()
+#       define DATASTART ((ptr_t)etext)
+#       define DATAEND ((ptr_t)end)
+#       define STACKBOTTOM ((ptr_t)InitStackBottom)
+#       define SIG_SUSPEND SIGUSR1
+#       define SIG_THR_RESTART SIGUSR2
+#   endif
+#   ifdef DOS4GW
+#     define OS_TYPE "DOS4GW"
+      extern long __nullarea;
+      extern char _end;
+      extern char *_STACKTOP;
+      /* Depending on calling conventions Watcom C either precedes      */
+      /* or does not precedes with underscore names of C-variables.     */
+      /* Make sure startup code variables always have the same names.   */
+      #pragma aux __nullarea "*";
+      #pragma aux _end "*";
+#     define STACKBOTTOM ((ptr_t) _STACKTOP)
+                         /* confused? me too. */
+#     define DATASTART ((ptr_t) &__nullarea)
+#     define DATAEND ((ptr_t) &_end)
+#   endif
+#   ifdef HURD
+#     define OS_TYPE "HURD"
+#     define STACK_GROWS_DOWN
+#     define HEURISTIC2
+#     define SIG_SUSPEND SIGUSR1
+#     define SIG_THR_RESTART SIGUSR2
+#     define SEARCH_FOR_DATA_START
+      extern int _end[];
+#     define DATAEND ((ptr_t) (_end))
+/* #     define MPROTECT_VDB  Not quite working yet? */
+#     define DYNAMIC_LOADING
+#   endif
+#   ifdef DARWIN
+#     define OS_TYPE "DARWIN"
+#     define DARWIN_DONT_PARSE_STACK
+#     define DYNAMIC_LOADING
+      /* XXX: see get_end(3), get_etext() and get_end() should not be used. */
+      /* These aren't used when dyld support is enabled (it is by default). */
+#     define DATASTART ((ptr_t) get_etext())
+#     define DATAEND   ((ptr_t) get_end())
+#     define STACKBOTTOM ((ptr_t) 0xc0000000)
+#     ifndef USE_MMAP
+#       define USE_MMAP
+#     endif
+#     define USE_MMAP_ANON
+#     define MPROTECT_VDB
+#     include <unistd.h>
+#     define GETPAGESIZE() getpagesize()
+      /* There seems to be some issues with trylock hanging on darwin.  */
+      /* This should be looked into some more.                          */
+#     define NO_PTHREAD_TRYLOCK
+#   endif /* DARWIN */
+# endif
+
+# ifdef NS32K
+#   define MACH_TYPE "NS32K"
+#   define ALIGNMENT 4
+    extern char **environ;
+#   define DATASTART ((ptr_t)(&environ))
+                              /* hideous kludge: environ is the first   */
+                              /* word in crt0.o, and delimits the start */
+                              /* of the data segment, no matter which   */
+                              /* ld options were passed through.        */
+#   define STACKBOTTOM ((ptr_t) 0xfffff000) /* for Encore */
+# endif
+
+# ifdef MIPS
+#   define MACH_TYPE "MIPS"
+#   ifdef LINUX
+#     define OS_TYPE "LINUX"
+#     define DYNAMIC_LOADING
+      extern int _end[];
+#     define DATAEND (ptr_t)(_end)
+#     pragma weak __data_start
+      extern int __data_start[];
+#     define DATASTART ((ptr_t)(__data_start))
+#     ifdef _MIPS_SZPTR
+#       define CPP_WORDSZ _MIPS_SZPTR
+#       define ALIGNMENT (_MIPS_SZPTR/8)
+#     else
+#       define ALIGNMENT 4
+#     endif
+#     ifndef HBLKSIZE
+#       define HBLKSIZE 4096
+#     endif
+#     if __GLIBC__ == 2 && __GLIBC_MINOR__ >= 2 || __GLIBC__ > 2
+#       define LINUX_STACKBOTTOM
+#     else
+#       define STACKBOTTOM ((ptr_t)0x7fff8000)
+#     endif
+#   endif /* Linux */
+#   ifdef EWS4800
+#      define HEURISTIC2
+#      if defined(_MIPS_SZPTR) && (_MIPS_SZPTR == 64)
+         extern int _fdata[], _end[];
+#        define DATASTART ((ptr_t)_fdata)
+#        define DATAEND ((ptr_t)_end)
+#        define CPP_WORDSZ _MIPS_SZPTR
+#        define ALIGNMENT (_MIPS_SZPTR/8)
+#      else
+         extern int etext[], edata[], end[];
+         extern int _DYNAMIC_LINKING[], _gp[];
+#        define DATASTART ((ptr_t)((((word)etext + 0x3ffff) & ~0x3ffff) \
+               + ((word)etext & 0xffff)))
+#        define DATAEND (ptr_t)(edata)
+#        define DATASTART2 (_DYNAMIC_LINKING \
+               ? (ptr_t)(((word)_gp + 0x8000 + 0x3ffff) & ~0x3ffff) \
+               : (ptr_t)edata)
+#        define DATAEND2 (ptr_t)(end)
+#        define ALIGNMENT 4
+#      endif
+#      define OS_TYPE "EWS4800"
+#   endif
+#   ifdef ULTRIX
+#       define HEURISTIC2
+#       define DATASTART (ptr_t)0x10000000
+                              /* Could probably be slightly higher since */
+                              /* startup code allocates lots of stuff.   */
+#       define OS_TYPE "ULTRIX"
+#       define ALIGNMENT 4
+#   endif
+#   ifdef IRIX5
+#       define HEURISTIC2
+        extern int _fdata[];
+#       define DATASTART ((ptr_t)(_fdata))
+#       ifdef USE_MMAP
+#         define HEAP_START (ptr_t)0x30000000
+#       else
+#         define HEAP_START DATASTART
+#       endif
+                              /* Lowest plausible heap address.         */
+                              /* In the MMAP case, we map there.        */
+                              /* In either case it is used to identify  */
+                              /* heap sections so they're not           */
+                              /* considered as roots.                   */
+#       define OS_TYPE "IRIX5"
+/*#       define MPROTECT_VDB DOB: this should work, but there is evidence */
+/*              of recent breakage.                                        */
+#       ifdef _MIPS_SZPTR
+#         define CPP_WORDSZ _MIPS_SZPTR
+#         define ALIGNMENT (_MIPS_SZPTR/8)
+#       else
+#         define ALIGNMENT 4
+#       endif
+#       define DYNAMIC_LOADING
+#   endif
+#   ifdef MSWINCE
+#       define OS_TYPE "MSWINCE"
+#       define ALIGNMENT 4
+#       define DATAEND /* not needed */
+#   endif
+#   if defined(NETBSD)
+#     define OS_TYPE "NETBSD"
+#     define ALIGNMENT 4
+#     define HEURISTIC2
+#     ifdef __ELF__
+        extern ptr_t GC_data_start;
+#       define DATASTART GC_data_start
+#       define NEED_FIND_LIMIT
+#       define DYNAMIC_LOADING
+#     else
+#       define DATASTART ((ptr_t) 0x10000000)
+#       define STACKBOTTOM ((ptr_t) 0x7ffff000)
+#     endif /* _ELF_ */
+#  endif
+#  ifdef OPENBSD
+#    define OS_TYPE "OPENBSD"
+#    define ALIGNMENT 4
+#     ifndef GC_OPENBSD_THREADS
+#      include <sys/param.h>
+#      include <uvm/uvm_extern.h>
+#      define STACKBOTTOM ((ptr_t) USRSTACK)
+#    endif
+     extern int _fdata[];
+#    define DATASTART ((ptr_t)_fdata)
+     extern char _end[];
+#    define DATAEND ((ptr_t)(&_end))
+#    define DYNAMIC_LOADING
+#  endif
+#  if defined(NONSTOP)
+#    define CPP_WORDSZ 32
+#    define OS_TYPE "NONSTOP"
+#    define ALIGNMENT 4
+#    define DATASTART ((ptr_t) 0x08000000)
+     extern char **environ;
+#    define DATAEND ((ptr_t)(environ - 0x10))
+#    define STACKBOTTOM ((ptr_t) 0x4fffffff)
+#   endif
+# endif
+
+# ifdef HP_PA
+#   define MACH_TYPE "HP_PA"
+#   ifdef __LP64__
+#     define CPP_WORDSZ 64
+#     define ALIGNMENT 8
+#   else
+#     define CPP_WORDSZ 32
+#     define ALIGNMENT 4
+#   endif
+#   if !defined(GC_HPUX_THREADS) && !defined(GC_LINUX_THREADS) \
+       && !defined(OPENBSD) && !defined(LINUX) /* For now. */
+#     define MPROTECT_VDB
+#   endif
+#   define STACK_GROWS_UP
+#   ifdef HPUX
+#     define OS_TYPE "HPUX"
+      extern int __data_start[];
+#     define DATASTART ((ptr_t)(__data_start))
+#     ifdef USE_HPUX_FIXED_STACKBOTTOM
+        /* The following appears to work for 7xx systems running HP/UX  */
+        /* 9.xx.  Furthermore, it might result in much faster           */
+        /* collections than HEURISTIC2, which may involve scanning      */
+        /* segments that directly precede the stack.  It is not the     */
+        /* default, since it may not work on older machine/OS           */
+        /* combinations. (Thanks to Raymond X.T. Nijssen for uncovering */
+        /* this.)                                                       */
+#       define STACKBOTTOM ((ptr_t) 0x7b033000)  /* from /etc/conf/h/param.h */
+#     else
+        /* Gustavo Rodriguez-Rivera suggested changing HEURISTIC2       */
+        /* to this.  Note that the GC must be initialized before the    */
+        /* first putenv call.                                           */
+        extern char ** environ;
+#       define STACKBOTTOM ((ptr_t)environ)
+#     endif
+#     define DYNAMIC_LOADING
+#     include <unistd.h>
+#     define GETPAGESIZE() sysconf(_SC_PAGE_SIZE)
+#     ifndef __GNUC__
+#       define PREFETCH(x)  do { \
+                              register long addr = (long)(x); \
+                              (void) _asm ("LDW", 0, 0, addr, 0); \
+                            } while (0)
+#     endif
+#   endif /* HPUX */
+#   ifdef LINUX
+#     define OS_TYPE "LINUX"
+#     define LINUX_STACKBOTTOM
+#     define DYNAMIC_LOADING
+#     define SEARCH_FOR_DATA_START
+      extern int _end[];
+#     define DATAEND (ptr_t)(&_end)
+#   endif /* LINUX */
+#  ifdef OPENBSD
+#     define OS_TYPE "OPENBSD"
+#     ifndef GC_OPENBSD_THREADS
+#       include <sys/param.h>
+#       include <uvm/uvm_extern.h>
+#       define STACKBOTTOM ((ptr_t) USRSTACK)
+#     endif
+      extern int __data_start[];
+#     define DATASTART ((ptr_t)__data_start)
+      extern char _end[];
+#     define DATAEND ((ptr_t)(&_end))
+#     define DYNAMIC_LOADING
+#  endif
+# endif /* HP_PA */
+
+# ifdef ALPHA
+#   define MACH_TYPE "ALPHA"
+#   define ALIGNMENT 8
+#   define CPP_WORDSZ 64
+#   ifdef NETBSD
+#       define OS_TYPE "NETBSD"
+#       define HEURISTIC2
+        extern ptr_t GC_data_start;
+#       define DATASTART GC_data_start
+#       define ELFCLASS32 32
+#       define ELFCLASS64 64
+#       define ELF_CLASS ELFCLASS64
+#       define DYNAMIC_LOADING
+#   endif
+#   ifdef OPENBSD
+#       define OS_TYPE "OPENBSD"
+#       define ELF_CLASS ELFCLASS64
+#       ifndef GC_OPENBSD_THREADS
+#         include <sys/param.h>
+#         include <uvm/uvm_extern.h>
+#         define STACKBOTTOM ((ptr_t) USRSTACK)
+#       endif
+        extern int __data_start[];
+#       define DATASTART ((ptr_t)__data_start)
+        extern char _end[];
+#       define DATAEND ((ptr_t)(&_end))
+#       define DYNAMIC_LOADING
+#   endif
+#   ifdef FREEBSD
+#       define OS_TYPE "FREEBSD"
+/* MPROTECT_VDB is not yet supported at all on FreeBSD/alpha. */
+#       define SIG_SUSPEND SIGUSR1
+#       define SIG_THR_RESTART SIGUSR2
+                /* SIGTSTP and SIGCONT could be used alternatively.     */
+#       define FREEBSD_STACKBOTTOM
+#       ifdef __ELF__
+#           define DYNAMIC_LOADING
+#       endif
+/* Handle unmapped hole alpha*-*-freebsd[45]* puts between etext and edata. */
+        extern char etext[];
+        extern char edata[];
+        extern char end[];
+#       define NEED_FIND_LIMIT
+#       define DATASTART ((ptr_t)(&etext))
+        ptr_t GC_find_limit(ptr_t, GC_bool);
+#       define DATAEND (GC_find_limit (DATASTART, TRUE))
+#       define DATAEND_IS_FUNC
+#       define DATASTART2 ((ptr_t)(&edata))
+#       define DATAEND2 ((ptr_t)(&end))
+#   endif
+#   ifdef OSF1
+#       define OS_TYPE "OSF1"
+#       define DATASTART ((ptr_t) 0x140000000)
+        extern int _end[];
+#       define DATAEND ((ptr_t) &_end)
+        extern char ** environ;
+        /* round up from the value of environ to the nearest page boundary */
+        /* Probably breaks if putenv is called before collector            */
+        /* initialization.                                                 */
+#       define STACKBOTTOM ((ptr_t)(((word)(environ) | (getpagesize()-1))+1))
+/* #    define HEURISTIC2 */
+        /* Normally HEURISTIC2 is too conservative, since               */
+        /* the text segment immediately follows the stack.              */
+        /* Hence we give an upper pound.                                */
+        /* This is currently unused, since we disabled HEURISTIC2       */
+        extern int __start[];
+#       define HEURISTIC2_LIMIT ((ptr_t)((word)(__start) & ~(getpagesize()-1)))
+#       ifndef GC_OSF1_THREADS
+          /* Unresolved signal issues with threads.     */
+#         define MPROTECT_VDB
+#       endif
+#       define DYNAMIC_LOADING
+#   endif
+#   ifdef LINUX
+#       define OS_TYPE "LINUX"
+#       define LINUX_STACKBOTTOM
+#       ifdef __ELF__
+#         define SEARCH_FOR_DATA_START
+#         define DYNAMIC_LOADING
+#       else
+#           define DATASTART ((ptr_t) 0x140000000)
+#       endif
+        extern int _end[];
+#       define DATAEND (ptr_t)(_end)
+#       define MPROTECT_VDB
+                /* Has only been superficially tested.  May not */
+                /* work on all versions.                        */
+#   endif
+# endif
+
+# ifdef IA64
+#   define MACH_TYPE "IA64"
+#   ifdef HPUX
+#       ifdef _ILP32
+#         define CPP_WORDSZ 32
+            /* Requires 8 byte alignment for malloc */
+#         define ALIGNMENT 4
+#       else
+#         ifndef _LP64
+#           error --> unknown ABI
+#         endif
+#         define CPP_WORDSZ 64
+            /* Requires 16 byte alignment for malloc */
+#         define ALIGNMENT 8
+#       endif
+#       define OS_TYPE "HPUX"
+        extern int __data_start[];
+#       define DATASTART ((ptr_t)(__data_start))
+        /* Gustavo Rodriguez-Rivera suggested changing HEURISTIC2       */
+        /* to this.  Note that the GC must be initialized before the    */
+        /* first putenv call.                                           */
+        extern char ** environ;
+#       define STACKBOTTOM ((ptr_t)environ)
+#       define HPUX_STACKBOTTOM
+#       define DYNAMIC_LOADING
+#       include <unistd.h>
+#       define GETPAGESIZE() sysconf(_SC_PAGE_SIZE)
+        /* The following was empirically determined, and is probably    */
+        /* not very robust.                                             */
+        /* Note that the backing store base seems to be at a nice       */
+        /* address minus one page.                                      */
+#       define BACKING_STORE_DISPLACEMENT 0x1000000
+#       define BACKING_STORE_ALIGNMENT 0x1000
+        extern ptr_t GC_register_stackbottom;
+#       define BACKING_STORE_BASE GC_register_stackbottom
+        /* Known to be wrong for recent HP/UX versions!!!       */
+#   endif
+#   ifdef LINUX
+#       define CPP_WORDSZ 64
+#       define ALIGNMENT 8
+#       define OS_TYPE "LINUX"
+        /* The following works on NUE and older kernels:        */
+/* #       define STACKBOTTOM ((ptr_t) 0xa000000000000000l)     */
+        /* This does not work on NUE:                           */
+#       define LINUX_STACKBOTTOM
+        /* We also need the base address of the register stack  */
+        /* backing store.  This is computed in                  */
+        /* GC_linux_register_stack_base based on the following  */
+        /* constants:                                           */
+#       define BACKING_STORE_ALIGNMENT 0x100000
+#       define BACKING_STORE_DISPLACEMENT 0x80000000
+        extern ptr_t GC_register_stackbottom;
+#       define BACKING_STORE_BASE GC_register_stackbottom
+#       define SEARCH_FOR_DATA_START
+#       ifdef __GNUC__
+#         define DYNAMIC_LOADING
+#       else
+          /* In the Intel compiler environment, we seem to end up with  */
+          /* statically linked executables and an undefined reference   */
+          /* to _DYNAMIC                                                */
+#       endif
+#       define MPROTECT_VDB
+                /* Requires Linux 2.3.47 or later.      */
+        extern int _end[];
+#       define DATAEND (ptr_t)(_end)
+#       ifdef __GNUC__
+#         ifndef __INTEL_COMPILER
+#           define PREFETCH(x) \
+              __asm__ ("        lfetch  [%0]": : "r"(x))
+#           define PREFETCH_FOR_WRITE(x) \
+              __asm__ ("        lfetch.excl     [%0]": : "r"(x))
+#           define CLEAR_DOUBLE(x) \
+              __asm__ ("        stf.spill       [%0]=f0": : "r"((void *)(x)))
+#         else
+#           include <ia64intrin.h>
+#           define PREFETCH(x) __lfetch(__lfhint_none, (x))
+#           define PREFETCH_FOR_WRITE(x) __lfetch(__lfhint_nta, (x))
+#           define CLEAR_DOUBLE(x) __stf_spill((void *)(x), 0)
+#         endif /* __INTEL_COMPILER */
+#       endif
+#   endif
+#   ifdef MSWIN32
+      /* FIXME: This is a very partial guess.  There is no port, yet.   */
+#     define OS_TYPE "MSWIN32"
+                /* STACKBOTTOM and DATASTART are handled specially in   */
+                /* os_dep.c.                                            */
+#     define DATAEND  /* not needed */
+#     if defined(_WIN64)
+#       define CPP_WORDSZ 64
+#     else
+#       define CPP_WORDSZ 32   /* Is this possible?     */
+#     endif
+#     define ALIGNMENT 8
+#   endif
+# endif
+
+# ifdef M88K
+#   define MACH_TYPE "M88K"
+#   define ALIGNMENT 4
+    extern int etext[];
+#   ifdef CX_UX
+#       define OS_TYPE "CX_UX"
+#       define DATASTART ((((word)etext + 0x3fffff) & ~0x3fffff) + 0x10000)
+#   endif
+#   ifdef  DGUX
+#       define OS_TYPE "DGUX"
+        ptr_t GC_SysVGetDataStart(size_t, ptr_t);
+#       define DATASTART GC_SysVGetDataStart(0x10000, (ptr_t)etext)
+#       define DATASTART_IS_FUNC
+#   endif
+#   define STACKBOTTOM ((char*)0xf0000000) /* determined empirically */
+# endif
+
+# ifdef S370
+    /* If this still works, and if anyone cares, this should probably   */
+    /* be moved to the S390 category.                                   */
+#   define MACH_TYPE "S370"
+#   define ALIGNMENT 4  /* Required by hardware */
+#   ifdef UTS4
+#       define OS_TYPE "UTS4"
+        extern int etext[];
+        extern int _etext[];
+        extern int _end[];
+        ptr_t GC_SysVGetDataStart(size_t, ptr_t);
+#       define DATASTART GC_SysVGetDataStart(0x10000, (ptr_t)_etext)
+#       define DATASTART_IS_FUNC
+#       define DATAEND (ptr_t)(_end)
+#       define HEURISTIC2
+#   endif
+# endif
+
+# ifdef S390
+#   define MACH_TYPE "S390"
+#   ifndef __s390x__
+#   define ALIGNMENT 4
+#   define CPP_WORDSZ 32
+#   else
+#   define ALIGNMENT 8
+#   define CPP_WORDSZ 64
+#   ifndef HBLKSIZE
+#     define HBLKSIZE 4096
+#   endif
+#   endif
+#   ifdef LINUX
+#       define OS_TYPE "LINUX"
+#       define LINUX_STACKBOTTOM
+#       define DYNAMIC_LOADING
+        extern int __data_start[] __attribute__((__weak__));
+#       define DATASTART ((ptr_t)(__data_start))
+        extern int _end[] __attribute__((__weak__));
+#       define DATAEND (ptr_t)(_end)
+#       define CACHE_LINE_SIZE 256
+#       define GETPAGESIZE() 4096
+#   endif
+# endif
+
+# ifdef AARCH64
+#   define CPP_WORDSZ 64
+#   define MACH_TYPE "AARCH64"
+#   define ALIGNMENT 8
+#   ifndef HBLKSIZE
+#     define HBLKSIZE 4096
+#   endif
+#   ifdef LINUX
+#     define OS_TYPE "LINUX"
+#     define LINUX_STACKBOTTOM
+#     define DYNAMIC_LOADING
+      extern int __data_start[];
+#     define DATASTART ((ptr_t)__data_start)
+      extern char _end[];
+#     define DATAEND ((ptr_t)(&_end))
+#   endif
+#   ifdef NOSYS
+      /* __data_start is usually defined in the target linker script.   */
+      extern int __data_start[];
+#     define DATASTART ((ptr_t)__data_start)
+      extern void *__stack_base__;
+#     define STACKBOTTOM ((ptr_t)__stack_base__)
+#   endif
+# endif
+
+# ifdef ARM32
+#   define CPP_WORDSZ 32
+#   define MACH_TYPE "ARM32"
+#   define ALIGNMENT 4
+#   ifdef NETBSD
+#       define OS_TYPE "NETBSD"
+#       define HEURISTIC2
+#       ifdef __ELF__
+           extern ptr_t GC_data_start;
+#          define DATASTART GC_data_start
+#          define DYNAMIC_LOADING
+#       else
+           extern char etext[];
+#          define DATASTART ((ptr_t)(etext))
+#       endif
+#   endif
+#   ifdef LINUX
+#       define OS_TYPE "LINUX"
+#       define LINUX_STACKBOTTOM
+#       undef STACK_GRAN
+#       define STACK_GRAN 0x10000000
+#       ifdef __ELF__
+#            define DYNAMIC_LOADING
+#            include <features.h>
+#            if defined(__GLIBC__) && __GLIBC__ >= 2 \
+                || defined(PLATFORM_ANDROID)
+#                define SEARCH_FOR_DATA_START
+#            else
+                 extern char **__environ;
+#                define DATASTART ((ptr_t)(&__environ))
+                              /* hideous kludge: __environ is the first */
+                              /* word in crt0.o, and delimits the start */
+                              /* of the data segment, no matter which   */
+                              /* ld options were passed through.        */
+                              /* We could use _etext instead, but that  */
+                              /* would include .rodata, which may       */
+                              /* contain large read-only data tables    */
+                              /* that we'd rather not scan.             */
+#            endif
+             extern int _end[];
+#            define DATAEND (ptr_t)(_end)
+#       else
+             extern int etext[];
+#            define DATASTART ((ptr_t)((((word) (etext)) + 0xfff) & ~0xfff))
+#       endif
+#   endif
+#   ifdef MSWINCE
+#     define OS_TYPE "MSWINCE"
+#     define DATAEND /* not needed */
+#   endif
+#   ifdef FREEBSD
+      /* FreeBSD/arm */
+#     define ALIGNMENT 4
+#     define OS_TYPE "FREEBSD"
+#     ifdef __ELF__
+#       define DYNAMIC_LOADING
+#     endif
+#     define HEURISTIC2
+      extern char etext[];
+#     define SEARCH_FOR_DATA_START
+#   endif
+#   ifdef DARWIN
+      /* iPhone */
+#     define OS_TYPE "DARWIN"
+#     ifndef GC_DONT_REGISTER_MAIN_STATIC_DATA
+#       define DYNAMIC_LOADING
+#     endif
+#     define DATASTART ((ptr_t) get_etext())
+#     define DATAEND   ((ptr_t) get_end())
+#     define STACKBOTTOM ((ptr_t) 0x30000000)
+#     ifndef USE_MMAP
+#       define USE_MMAP
+#     endif
+#     define USE_MMAP_ANON
+#     define MPROTECT_VDB
+#     include <unistd.h>
+#     define GETPAGESIZE() getpagesize()
+      /* FIXME: There seems to be some issues with trylock hanging on   */
+      /* darwin. This should be looked into some more.                  */
+#     define NO_PTHREAD_TRYLOCK
+#     ifndef NO_DYLD_BIND_FULLY_IMAGE
+#       define NO_DYLD_BIND_FULLY_IMAGE
+#     endif
+#   endif
+#   ifdef OPENBSD
+#     define ALIGNMENT 4
+#     define OS_TYPE "OPENBSD"
+#     ifndef GC_OPENBSD_THREADS
+#       include <sys/param.h>
+#       include <uvm/uvm_extern.h>
+#       define STACKBOTTOM ((ptr_t) USRSTACK)
+#     endif
+      extern int __data_start[];
+#     define DATASTART ((ptr_t)__data_start)
+      extern char _end[];
+#     define DATAEND ((ptr_t)(&_end))
+#     define DYNAMIC_LOADING
+#   endif
+#   ifdef NOSYS
+      /* __data_start is usually defined in the target linker script.  */
+      extern int __data_start[];
+#     define DATASTART (ptr_t)(__data_start)
+      /* __stack_base__ is set in newlib/libc/sys/arm/crt0.S  */
+      extern void *__stack_base__;
+#     define STACKBOTTOM ((ptr_t) (__stack_base__))
+#   endif
+#endif
+
+# ifdef CRIS
+#   define MACH_TYPE "CRIS"
+#   define CPP_WORDSZ 32
+#   define ALIGNMENT 1
+#   define OS_TYPE "LINUX"
+#   define DYNAMIC_LOADING
+#   define LINUX_STACKBOTTOM
+#   define SEARCH_FOR_DATA_START
+      extern int _end[];
+#   define DATAEND (ptr_t)(_end)
+# endif
+
+# if defined(SH) && !defined(SH4)
+#   define MACH_TYPE "SH"
+#   define ALIGNMENT 4
+#   ifdef MSWINCE
+#     define OS_TYPE "MSWINCE"
+#     define DATAEND /* not needed */
+#   endif
+#   ifdef LINUX
+#     define OS_TYPE "LINUX"
+#     define LINUX_STACKBOTTOM
+#     define DYNAMIC_LOADING
+#     define SEARCH_FOR_DATA_START
+      extern int _end[];
+#     define DATAEND (ptr_t)(_end)
+#   endif
+#   ifdef NETBSD
+#      define OS_TYPE "NETBSD"
+#      define HEURISTIC2
+       extern ptr_t GC_data_start;
+#      define DATASTART GC_data_start
+#      define DYNAMIC_LOADING
+#   endif
+#   ifdef OPENBSD
+#      define OS_TYPE "OPENBSD"
+#      ifndef GC_OPENBSD_THREADS
+#        include <sys/param.h>
+#        include <uvm/uvm_extern.h>
+#        define STACKBOTTOM ((ptr_t) USRSTACK)
+#      endif
+       extern int __data_start[];
+#      define DATASTART ((ptr_t)__data_start)
+       extern char _end[];
+#      define DATAEND ((ptr_t)(&_end))
+#      define DYNAMIC_LOADING
+#   endif
+# endif
+
+# ifdef SH4
+#   define MACH_TYPE "SH4"
+#   define OS_TYPE "MSWINCE"
+#   define ALIGNMENT 4
+#   define DATAEND /* not needed */
+# endif
+
+# ifdef AVR32
+#   define MACH_TYPE "AVR32"
+#   define CPP_WORDSZ 32
+#   define ALIGNMENT 4
+#   define OS_TYPE "LINUX"
+#   define DYNAMIC_LOADING
+#   define LINUX_STACKBOTTOM
+#   define SEARCH_FOR_DATA_START
+    extern int _end[];
+#   define DATAEND (_end)
+# endif
+
+# ifdef M32R
+#   define CPP_WORDSZ 32
+#   define MACH_TYPE "M32R"
+#   define ALIGNMENT 4
+#   ifdef LINUX
+#     define OS_TYPE "LINUX"
+#     define LINUX_STACKBOTTOM
+#     undef STACK_GRAN
+#     define STACK_GRAN 0x10000000
+#     define DYNAMIC_LOADING
+#     define SEARCH_FOR_DATA_START
+      extern int _end[];
+#     define DATAEND (ptr_t)(_end)
+#   endif
+# endif
+
+# ifdef X86_64
+#   define MACH_TYPE "X86_64"
+#   ifdef __ILP32__
+#     define ALIGNMENT 4
+#     define CPP_WORDSZ 32
+#   else
+#     define ALIGNMENT 8
+#     define CPP_WORDSZ 64
+#   endif
+#   ifndef HBLKSIZE
+#     define HBLKSIZE 4096
+#   endif
+#   define CACHE_LINE_SIZE 64
+#   ifdef OPENBSD
+#       define OS_TYPE "OPENBSD"
+#       define ELF_CLASS ELFCLASS64
+#       ifndef GC_OPENBSD_THREADS
+#         include <sys/param.h>
+#         include <uvm/uvm_extern.h>
+#         define STACKBOTTOM ((ptr_t) USRSTACK)
+#       endif
+        extern int __data_start[];
+#       define DATASTART ((ptr_t)__data_start)
+        extern char _end[];
+#       define DATAEND ((ptr_t)(&_end))
+#       define DYNAMIC_LOADING
+#   endif
+#   ifdef LINUX
+#       define OS_TYPE "LINUX"
+#       define LINUX_STACKBOTTOM
+#       if !defined(GC_LINUX_THREADS) || !defined(REDIRECT_MALLOC)
+#           define MPROTECT_VDB
+#       else
+            /* We seem to get random errors in incremental mode,        */
+            /* possibly because Linux threads is itself a malloc client */
+            /* and can't deal with the signals.                         */
+#       endif
+#       ifdef __ELF__
+#            define DYNAMIC_LOADING
+#            ifdef UNDEFINED    /* includes ro data */
+               extern int _etext[];
+#              define DATASTART ((ptr_t)((((word) (_etext)) + 0xfff) & ~0xfff))
+#            endif
+#            include <features.h>
+#            define SEARCH_FOR_DATA_START
+             extern int _end[];
+#            define DATAEND (ptr_t)(_end)
+#       else
+             extern int etext[];
+#            define DATASTART ((ptr_t)((((word) (etext)) + 0xfff) & ~0xfff))
+#       endif
+#       if defined(__GLIBC__)
+          /* At present, there's a bug in GLibc getcontext() on         */
+          /* Linux/x64 (it clears FPU exception mask).  We define this  */
+          /* macro to workaround it.                                    */
+          /* FIXME: This seems to be fixed in GLibc v2.14.              */
+#         define GETCONTEXT_FPU_EXCMASK_BUG
+#       endif
+#   endif
+#   ifdef DARWIN
+#     define OS_TYPE "DARWIN"
+#     define DARWIN_DONT_PARSE_STACK
+#     define DYNAMIC_LOADING
+      /* XXX: see get_end(3), get_etext() and get_end() should not be used. */
+      /* These aren't used when dyld support is enabled (it is by default)  */
+#     define DATASTART ((ptr_t) get_etext())
+#     define DATAEND   ((ptr_t) get_end())
+#     define STACKBOTTOM ((ptr_t) 0x7fff5fc00000)
+#     ifndef USE_MMAP
+#       define USE_MMAP
+#     endif
+#     define USE_MMAP_ANON
+#     define MPROTECT_VDB
+#     include <unistd.h>
+#     define GETPAGESIZE() getpagesize()
+      /* There seems to be some issues with trylock hanging on darwin.  */
+      /* This should be looked into some more.                          */
+#     define NO_PTHREAD_TRYLOCK
+#   endif
+#   ifdef FREEBSD
+#       define OS_TYPE "FREEBSD"
+#       ifndef GC_FREEBSD_THREADS
+#           define MPROTECT_VDB
+#       endif
+#       ifdef __GLIBC__
+#           define SIG_SUSPEND          (32+6)
+#           define SIG_THR_RESTART      (32+5)
+            extern int _end[];
+#           define DATAEND (ptr_t)(_end)
+#       else
+#           define SIG_SUSPEND SIGUSR1
+#           define SIG_THR_RESTART SIGUSR2
+                /* SIGTSTP and SIGCONT could be used alternatively.     */
+#       endif
+#       define FREEBSD_STACKBOTTOM
+#       ifdef __ELF__
+#           define DYNAMIC_LOADING
+#       endif
+        extern char etext[];
+        ptr_t GC_FreeBSDGetDataStart(size_t, ptr_t);
+#       define DATASTART GC_FreeBSDGetDataStart(0x1000, (ptr_t)etext)
+#       define DATASTART_IS_FUNC
+#   endif
+#   ifdef NETBSD
+#       define OS_TYPE "NETBSD"
+#       define HEURISTIC2
+#       ifdef __ELF__
+            extern ptr_t GC_data_start;
+#           define DATASTART GC_data_start
+#           define DYNAMIC_LOADING
+#       else
+#           define SEARCH_FOR_DATA_START
+#       endif
+#   endif
+#   ifdef SOLARIS
+#       define OS_TYPE "SOLARIS"
+#       define ELF_CLASS ELFCLASS64
+        extern int _etext[], _end[];
+        ptr_t GC_SysVGetDataStart(size_t, ptr_t);
+#       define DATASTART GC_SysVGetDataStart(0x1000, (ptr_t)_etext)
+#       define DATASTART_IS_FUNC
+#       define DATAEND (ptr_t)(_end)
+/*      # define STACKBOTTOM ((ptr_t)(_start)) worked through 2.7,      */
+/*      but reportedly breaks under 2.8.  It appears that the stack     */
+/*      base is a property of the executable, so this should not break  */
+/*      old executables.                                                */
+/*      HEURISTIC2 probably works, but this appears to be preferable.   */
+/*      Apparently USRSTACK is defined to be USERLIMIT, but in some     */
+/*      installations that's undefined.  We work around this with a     */
+/*      gross hack:                                                     */
+#       include <sys/vmparam.h>
+#       ifdef USERLIMIT
+          /* This should work everywhere, but doesn't.  */
+#         define STACKBOTTOM ((ptr_t) USRSTACK)
+#       else
+#         define HEURISTIC2
+#       endif
+/* At least in Solaris 2.5, PROC_VDB gives wrong values for dirty bits. */
+/* It appears to be fixed in 2.8 and 2.9.                               */
+#       ifdef SOLARIS25_PROC_VDB_BUG_FIXED
+#         define PROC_VDB
+#       endif
+#       ifndef GC_THREADS
+#         define MPROTECT_VDB
+#       endif
+#       define DYNAMIC_LOADING
+#       if !defined(USE_MMAP) && defined(REDIRECT_MALLOC)
+#         define USE_MMAP
+            /* Otherwise we now use calloc.  Mmap may result in the     */
+            /* heap interleaved with thread stacks, which can result in */
+            /* excessive blacklisting.  Sbrk is unusable since it       */
+            /* doesn't interact correctly with the system malloc.       */
+#       endif
+#       ifdef USE_MMAP
+#         define HEAP_START (ptr_t)0x40000000
+#       else
+#         define HEAP_START DATAEND
+#       endif
+#   endif
+#   ifdef MSWIN32
+#       define OS_TYPE "MSWIN32"
+                /* STACKBOTTOM and DATASTART are handled specially in   */
+                /* os_dep.c.                                            */
+#       if !defined(__GNUC__) || defined(__INTEL_COMPILER)
+          /* GCC does not currently support SetUnhandledExceptionFilter */
+          /* (does not generate SEH unwinding information) on x64.      */
+#         define MPROTECT_VDB
+#       endif
+#       define GWW_VDB
+#       define DATAEND  /* not needed */
+#   endif
+# endif /* X86_64 */
+
+# ifdef HEXAGON
+#   define CPP_WORDSZ 32
+#   define MACH_TYPE "HEXAGON"
+#   define ALIGNMENT 4
+#   ifdef LINUX
+#       define OS_TYPE "LINUX"
+#       define LINUX_STACKBOTTOM
+#       define MPROTECT_VDB
+#       ifdef __ELF__
+#            define DYNAMIC_LOADING
+#            include <features.h>
+#            if defined(__GLIBC__) && __GLIBC__ >= 2
+#                define SEARCH_FOR_DATA_START
+#            else
+#                error --> unknown Hexagon libc configuration
+#            endif
+             extern int _end[];
+#            define DATAEND (ptr_t)(_end)
+#       else
+#            error --> bad Hexagon Linux configuration
+#       endif
+#   else
+#       error --> unknown Hexagon OS configuration
+#   endif
+# endif
+
+#if defined(__GLIBC__) && !defined(DONT_USE_LIBC_PRIVATES)
+  /* Use glibc's stack-end marker. */
+# define USE_LIBC_PRIVATES
+#endif
+
+#if defined(LINUX_STACKBOTTOM) && defined(NO_PROC_STAT) \
+    && !defined(USE_LIBC_PRIVATES)
+    /* This combination will fail, since we have no way to get  */
+    /* the stack base.  Use HEURISTIC2 instead.                 */
+#   undef LINUX_STACKBOTTOM
+#   define HEURISTIC2
+    /* This may still fail on some architectures like IA64.     */
+    /* We tried ...                                             */
+#endif
+
+#if defined(LINUX) && defined(USE_MMAP)
+    /* The kernel may do a somewhat better job merging mappings etc.    */
+    /* with anonymous mappings.                                         */
+#   define USE_MMAP_ANON
+#endif
+
+#if defined(GC_LINUX_THREADS) && defined(REDIRECT_MALLOC) \
+    && !defined(USE_PROC_FOR_LIBRARIES)
+    /* Nptl allocates thread stacks with mmap, which is fine.  But it   */
+    /* keeps a cache of thread stacks.  Thread stacks contain the       */
+    /* thread control blocks.  These in turn contain a pointer to       */
+    /* (sizeof (void *) from the beginning of) the dtv for thread-local */
+    /* storage, which is calloc allocated.  If we don't scan the cached */
+    /* thread stacks, we appear to lose the dtv.  This tends to         */
+    /* result in something that looks like a bogus dtv count, which     */
+    /* tends to result in a memset call on a block that is way too      */
+    /* large.  Sometimes we're lucky and the process just dies ...      */
+    /* There seems to be a similar issue with some other memory         */
+    /* allocated by the dynamic loader.                                 */
+    /* This should be avoidable by either:                              */
+    /* - Defining USE_PROC_FOR_LIBRARIES here.                          */
+    /*   That performs very poorly, precisely because we end up         */
+    /*   scanning cached stacks.                                        */
+    /* - Have calloc look at its callers.                               */
+    /*   In spite of the fact that it is gross and disgusting.          */
+    /* In fact neither seems to suffice, probably in part because       */
+    /* even with USE_PROC_FOR_LIBRARIES, we don't scan parts of stack   */
+    /* segments that appear to be out of bounds.  Thus we actually      */
+    /* do both, which seems to yield the best results.                  */
+
+#   define USE_PROC_FOR_LIBRARIES
+#endif
+
+#ifndef STACK_GROWS_UP
+# define STACK_GROWS_DOWN
+#endif
+
+#ifndef CPP_WORDSZ
+# define CPP_WORDSZ 32
+#endif
+
+#ifndef OS_TYPE
+# define OS_TYPE ""
+#endif
+
+#ifndef DATAEND
+  extern int end[];
+# define DATAEND (ptr_t)(end)
+#endif
+
+#if defined(PLATFORM_ANDROID) && !defined(THREADS) \
+    && !defined(USE_GET_STACKBASE_FOR_MAIN)
+  /* Always use pthread_attr_getstack on Android ("-lpthread" option is  */
+  /* not needed to be specified manually) since GC_linux_main_stack_base */
+  /* causes app crash if invoked inside Dalvik VM.                       */
+# define USE_GET_STACKBASE_FOR_MAIN
+#endif
+
+#if (defined(SVR4) || defined(PLATFORM_ANDROID)) && !defined(GETPAGESIZE)
+# include <unistd.h>
+# define GETPAGESIZE()  sysconf(_SC_PAGESIZE)
+#endif
+
+#ifndef GETPAGESIZE
+# if defined(SOLARIS) || defined(IRIX5) || defined(LINUX) \
+     || defined(NETBSD) || defined(FREEBSD) || defined(HPUX)
+#   include <unistd.h>
+# endif
+# define GETPAGESIZE() getpagesize()
+#endif
+
+#if defined(SOLARIS) || defined(DRSNX) || defined(UTS4)
+        /* OS has SVR4 generic features.        */
+        /* Probably others also qualify.        */
+# define SVR4
+#endif
+
+#if defined(SOLARIS) || defined(DRSNX)
+        /* OS has SOLARIS style semi-undocumented interface     */
+        /* to dynamic loader.                                   */
+# define SOLARISDL
+        /* OS has SOLARIS style signal handlers.        */
+# define SUNOS5SIGS
+#endif
+
+#if defined(HPUX)
+# define SUNOS5SIGS
+#endif
+
+#if defined(FREEBSD) && (defined(__DragonFly__) || __FreeBSD__ >= 4 \
+                         || (__FreeBSD_kernel__ >= 4))
+# define SUNOS5SIGS
+#endif
+
+#if !defined(GC_EXPLICIT_SIGNALS_UNBLOCK) && defined(SUNOS5SIGS) \
+    && !defined(GC_NO_PTHREAD_SIGMASK)
+# define GC_EXPLICIT_SIGNALS_UNBLOCK
+#endif
+
+#ifdef GC_NETBSD_THREADS
+# define SIGRTMIN 33
+# define SIGRTMAX 63
+#endif
+
+#ifdef GC_OPENBSD_THREADS
+# include <sys/param.h>
+  /* Prior to 5.2 release, OpenBSD had user threads and required        */
+  /* special handling.                                                  */
+# if OpenBSD < 201211
+#   define GC_OPENBSD_UTHREADS 1
+# endif
+#endif /* GC_OPENBSD_THREADS */
+
+#if defined(SVR4) || defined(LINUX) || defined(IRIX5) || defined(HPUX) \
+    || defined(OPENBSD) || defined(NETBSD) || defined(FREEBSD) \
+    || defined(DGUX) || defined(BSD) || defined(HURD) \
+    || defined(AIX) || defined(DARWIN) || defined(OSF1)
+# define UNIX_LIKE      /* Basic Unix-like system calls work.   */
+#endif
+
+#if CPP_WORDSZ != 32 && CPP_WORDSZ != 64
+# error --> bad word size
+#endif
+
+#ifndef ALIGNMENT
+# error --> undefined ALIGNMENT
+#endif
+
+#ifdef PCR
+# undef DYNAMIC_LOADING
+# undef STACKBOTTOM
+# undef HEURISTIC1
+# undef HEURISTIC2
+# undef PROC_VDB
+# undef MPROTECT_VDB
+# define PCR_VDB
+#endif
+
+#if !defined(STACKBOTTOM) && (defined(ECOS) || defined(NOSYS))
+# error --> undefined STACKBOTTOM
+#endif
+
+#ifdef IGNORE_DYNAMIC_LOADING
+# undef DYNAMIC_LOADING
+#endif
+
+#if defined(SMALL_CONFIG) && !defined(GC_DISABLE_INCREMENTAL)
+  /* Presumably not worth the space it takes.   */
+# define GC_DISABLE_INCREMENTAL
+#endif
+
+#if (defined(MSWIN32) || defined(MSWINCE)) && !defined(USE_WINALLOC)
+  /* USE_WINALLOC is only an option for Cygwin. */
+# define USE_WINALLOC
+#endif
+
+#ifdef USE_WINALLOC
+# undef USE_MMAP
+#endif
+
+#if defined(GC_DISABLE_INCREMENTAL) || defined(MANUAL_VDB)
+# undef GWW_VDB
+# undef MPROTECT_VDB
+# undef PCR_VDB
+# undef PROC_VDB
+#endif
+
+#ifdef GC_DISABLE_INCREMENTAL
+# undef CHECKSUMS
+#endif
+
+#ifdef USE_GLOBAL_ALLOC
+  /* Cannot pass MEM_WRITE_WATCH to GlobalAlloc().      */
+# undef GWW_VDB
+#endif
+
+#ifdef USE_MUNMAP
+  /* FIXME: Remove this undef if possible.      */
+# undef MPROTECT_VDB  /* Can't deal with address space holes.   */
+#endif
+
+/* PARALLEL_MARK does not cause undef MPROTECT_VDB any longer.  */
+
+#if defined(MPROTECT_VDB) && defined(GC_PREFER_MPROTECT_VDB)
+  /* Choose MPROTECT_VDB manually (if multiple strategies available).   */
+# undef PCR_VDB
+# undef PROC_VDB
+  /* #undef GWW_VDB - handled in os_dep.c       */
+#endif
+
+#ifdef PROC_VDB
+  /* Multi-VDB mode is not implemented. */
+# undef MPROTECT_VDB
+#endif
+
+#if !defined(PCR_VDB) && !defined(PROC_VDB) && !defined(MPROTECT_VDB) \
+    && !defined(GWW_VDB) && !defined(MANUAL_VDB) \
+    && !defined(GC_DISABLE_INCREMENTAL)
+# define DEFAULT_VDB
+#endif
+
+#if ((defined(UNIX_LIKE) && (defined(DARWIN) || defined(HURD) \
+                             || defined(OPENBSD) || defined(ARM32) \
+                             || defined(MIPS) || defined(AVR32))) \
+     || (defined(LINUX) && (defined(SPARC) || defined(M68K))) \
+     || ((defined(RTEMS) || defined(PLATFORM_ANDROID)) && defined(I386))) \
+    && !defined(NO_GETCONTEXT)
+# define NO_GETCONTEXT
+#endif
+
+#ifndef PREFETCH
+# if defined(__GNUC__) && __GNUC__ >= 3 && !defined(NO_PREFETCH)
+#   define PREFETCH(x) __builtin_prefetch((x), 0, 0)
+# else
+#   define PREFETCH(x) (void)0
+# endif
+#endif
+
+#ifndef PREFETCH_FOR_WRITE
+# if defined(__GNUC__) && __GNUC__ >= 3 && !defined(NO_PREFETCH_FOR_WRITE)
+#   define PREFETCH_FOR_WRITE(x) __builtin_prefetch((x), 1)
+# else
+#   define PREFETCH_FOR_WRITE(x) (void)0
+# endif
+#endif
+
+#ifndef CACHE_LINE_SIZE
+# define CACHE_LINE_SIZE 32     /* Wild guess   */
+#endif
+
+#ifndef STATIC
+# ifndef NO_DEBUGGING
+#   define STATIC /* ignore to aid profiling and possibly debugging     */
+# else
+#   define STATIC static
+# endif
+#endif
+
+#if defined(LINUX) && (defined(USE_PROC_FOR_LIBRARIES) || defined(IA64) \
+                       || !defined(SMALL_CONFIG))
+# define NEED_PROC_MAPS
+#endif
+
+#if defined(LINUX) || defined(HURD) || defined(__GLIBC__)
+# define REGISTER_LIBRARIES_EARLY
+  /* We sometimes use dl_iterate_phdr, which may acquire an internal    */
+  /* lock.  This isn't safe after the world has stopped.  So we must    */
+  /* call GC_register_dynamic_libraries before stopping the world.      */
+  /* For performance reasons, this may be beneficial on other           */
+  /* platforms as well, though it should be avoided in win32.           */
+#endif /* LINUX */
+
+#if defined(SEARCH_FOR_DATA_START)
+  extern ptr_t GC_data_start;
+# define DATASTART GC_data_start
+#endif
+
+#ifndef CLEAR_DOUBLE
+# define CLEAR_DOUBLE(x) (((word*)(x))[0] = 0, ((word*)(x))[1] = 0)
+#endif
+
+#if defined(GC_LINUX_THREADS) && defined(REDIRECT_MALLOC) \
+    && !defined(INCLUDE_LINUX_THREAD_DESCR)
+  /* Will not work, since libc and the dynamic loader use thread        */
+  /* locals, sometimes as the only reference.                           */
+# define INCLUDE_LINUX_THREAD_DESCR
+#endif
+
+#if defined(GC_IRIX_THREADS) && !defined(IRIX5)
+# error --> inconsistent configuration
+#endif
+#if defined(GC_LINUX_THREADS) && !defined(LINUX) && !defined(NACL)
+# error --> inconsistent configuration
+#endif
+#if defined(GC_NETBSD_THREADS) && !defined(NETBSD)
+# error --> inconsistent configuration
+#endif
+#if defined(GC_FREEBSD_THREADS) && !defined(FREEBSD)
+# error --> inconsistent configuration
+#endif
+#if defined(GC_SOLARIS_THREADS) && !defined(SOLARIS)
+# error --> inconsistent configuration
+#endif
+#if defined(GC_HPUX_THREADS) && !defined(HPUX)
+# error --> inconsistent configuration
+#endif
+#if defined(GC_AIX_THREADS) && !defined(_AIX)
+# error --> inconsistent configuration
+#endif
+#if defined(GC_GNU_THREADS) && !defined(HURD)
+# error --> inconsistent configuration
+#endif
+#if defined(GC_WIN32_THREADS) && !defined(MSWIN32) && !defined(CYGWIN32) \
+    && !defined(MSWINCE)
+# error --> inconsistent configuration
+#endif
+
+#if defined(PCR) || defined(GC_WIN32_THREADS) || defined(GC_PTHREADS) \
+    || defined(SN_TARGET_PS3)
+# define THREADS
+#endif
+
+#if defined(PARALLEL_MARK) && !defined(THREADS)
+# error "invalid config - PARALLEL_MARK requires GC_THREADS"
+#endif
+
+#if defined(UNIX_LIKE) && defined(THREADS) && !defined(NO_CANCEL_SAFE) \
+    && !defined(PLATFORM_ANDROID)
+  /* Make the code cancellation-safe.  This basically means that we     */
+  /* ensure that cancellation requests are ignored while we are in      */
+  /* the collector.  This applies only to Posix deferred cancellation;  */
+  /* we don't handle Posix asynchronous cancellation.                   */
+  /* Note that this only works if pthread_setcancelstate is             */
+  /* async-signal-safe, at least in the absence of asynchronous         */
+  /* cancellation.  This appears to be true for the glibc version,      */
+  /* though it is not documented.  Without that assumption, there       */
+  /* seems to be no way to safely wait in a signal handler, which       */
+  /* we need to do for thread suspension.                               */
+  /* Also note that little other code appears to be cancellation-safe.  */
+  /* Hence it may make sense to turn this off for performance.          */
+# define CANCEL_SAFE
+#endif
+
+#ifdef CANCEL_SAFE
+# define IF_CANCEL(x) x
+#else
+# define IF_CANCEL(x) /* empty */
+#endif
+
+#if !defined(CAN_HANDLE_FORK) && !defined(NO_HANDLE_FORK) \
+    && !defined(HAVE_NO_FORK) \
+    && ((defined(GC_PTHREADS) && !defined(NACL) \
+         && !defined(GC_WIN32_PTHREADS) && !defined(USE_WINALLOC)) \
+        || (defined(DARWIN) && defined(MPROTECT_VDB)) || defined(HANDLE_FORK))
+  /* Attempts (where supported and requested) to make GC_malloc work in */
+  /* a child process fork'ed from a multi-threaded parent.              */
+# define CAN_HANDLE_FORK
+#endif
+
+#if defined(CAN_HANDLE_FORK) && !defined(CAN_CALL_ATFORK) \
+    && !defined(HURD) && !defined(PLATFORM_ANDROID)
+  /* Have working pthread_atfork().     */
+# define CAN_CALL_ATFORK
+#endif
+
+#if !defined(CAN_HANDLE_FORK) && !defined(HAVE_NO_FORK) \
+    && (defined(MSWIN32) || defined(MSWINCE) || defined(DOS4GW) \
+        || defined(OS2) || defined(SYMBIAN) /* and probably others ... */)
+# define HAVE_NO_FORK
+#endif
+
+#if !defined(USE_MARK_BITS) && !defined(USE_MARK_BYTES) \
+    && defined(PARALLEL_MARK)
+   /* Minimize compare-and-swap usage.  */
+#  define USE_MARK_BYTES
+#endif
+
+#if defined(MSWINCE) && !defined(__CEGCC__) && !defined(NO_GETENV)
+# define NO_GETENV
+#endif
+
+#if (defined(NO_GETENV) || defined(MSWINCE)) && !defined(NO_GETENV_WIN32)
+# define NO_GETENV_WIN32
+#endif
+
+#ifndef STRTOULL
+# if defined(_WIN64) && !defined(__GNUC__)
+#   define STRTOULL _strtoui64
+# elif defined(_LLP64) || defined(__LLP64__) || defined(_WIN64)
+#   define STRTOULL strtoull
+# else
+    /* strtoul() fits since sizeof(long) >= sizeof(word).       */
+#   define STRTOULL strtoul
+# endif
+#endif /* !STRTOULL */
+
+#ifndef GC_WORD_C
+# if defined(_WIN64) && !defined(__GNUC__)
+#   define GC_WORD_C(val) val##ui64
+# elif defined(_LLP64) || defined(__LLP64__) || defined(_WIN64)
+#   define GC_WORD_C(val) val##ULL
+# else
+#   define GC_WORD_C(val) ((word)val##UL)
+# endif
+#endif /* !GC_WORD_C */
+
+#if defined(SPARC)
+# define ASM_CLEAR_CODE /* Stack clearing is crucial, and we    */
+                        /* include assembly code to do it well. */
+#endif
+
+/* Can we save call chain in objects for debugging?                     */
+/* SET NFRAMES (# of saved frames) and NARGS (#of args for each         */
+/* frame) to reasonable values for the platform.                        */
+/* Set SAVE_CALL_CHAIN if we can.  SAVE_CALL_COUNT can be specified     */
+/* at build time, though we feel free to adjust it slightly.            */
+/* Define NEED_CALLINFO if we either save the call stack or             */
+/* GC_ADD_CALLER is defined.                                            */
+/* GC_CAN_SAVE_CALL_STACKS is set in gc.h.                              */
+#if defined(SPARC)
+# define CAN_SAVE_CALL_ARGS
+#endif
+#if (defined(I386) || defined(X86_64)) \
+    && (defined(LINUX) || defined(__GLIBC__))
+  /* SAVE_CALL_CHAIN is supported if the code is compiled to save       */
+  /* frame pointers by default, i.e. no -fomit-frame-pointer flag.      */
+# define CAN_SAVE_CALL_ARGS
+#endif
+
+#if defined(SAVE_CALL_COUNT) && !defined(GC_ADD_CALLER) \
+    && defined(GC_CAN_SAVE_CALL_STACKS)
+# define SAVE_CALL_CHAIN
+#endif
+#ifdef SAVE_CALL_CHAIN
+# if defined(SAVE_CALL_NARGS) && defined(CAN_SAVE_CALL_ARGS)
+#   define NARGS SAVE_CALL_NARGS
+# else
+#   define NARGS 0      /* Number of arguments to save for each call.   */
+# endif
+#endif
+#ifdef SAVE_CALL_CHAIN
+# ifndef SAVE_CALL_COUNT
+#   define NFRAMES 6    /* Number of frames to save. Even for   */
+                        /* alignment reasons.                   */
+# else
+#   define NFRAMES ((SAVE_CALL_COUNT + 1) & ~1)
+# endif
+# define NEED_CALLINFO
+#endif /* SAVE_CALL_CHAIN */
+#ifdef GC_ADD_CALLER
+# define NFRAMES 1
+# define NARGS 0
+# define NEED_CALLINFO
+#endif
+
+#if (defined(FREEBSD) || (defined(DARWIN) && !defined(_POSIX_C_SOURCE)) \
+        || (defined(SOLARIS) && (!defined(_XOPEN_SOURCE) \
+                                 || defined(__EXTENSIONS__))) \
+        || defined(LINUX)) && !defined(HAVE_DLADDR)
+# define HAVE_DLADDR
+#endif
+
+#if defined(MAKE_BACK_GRAPH) && !defined(DBG_HDRS_ALL)
+# define DBG_HDRS_ALL
+#endif
+
+#if defined(POINTER_MASK) && !defined(POINTER_SHIFT)
+# define POINTER_SHIFT 0
+#endif
+
+#if defined(POINTER_SHIFT) && !defined(POINTER_MASK)
+# define POINTER_MASK ((GC_word)(-1))
+#endif
+
+#if !defined(FIXUP_POINTER) && defined(POINTER_MASK)
+# define FIXUP_POINTER(p) (p = ((p) & POINTER_MASK) << POINTER_SHIFT)
+#endif
+
+#if defined(FIXUP_POINTER)
+# define NEED_FIXUP_POINTER 1
+#else
+# define NEED_FIXUP_POINTER 0
+# define FIXUP_POINTER(p)
+#endif
+
+#if !defined(MARK_BIT_PER_GRANULE) && !defined(MARK_BIT_PER_OBJ)
+# define MARK_BIT_PER_GRANULE   /* Usually faster       */
+#endif
+
+/* Some static sanity tests.    */
+#if defined(MARK_BIT_PER_GRANULE) && defined(MARK_BIT_PER_OBJ)
+# error Define only one of MARK_BIT_PER_GRANULE and MARK_BIT_PER_OBJ.
+#endif
+
+#if defined(STACK_GROWS_UP) && defined(STACK_GROWS_DOWN)
+# error "Only one of STACK_GROWS_UP and STACK_GROWS_DOWN should be defd."
+#endif
+#if !defined(STACK_GROWS_UP) && !defined(STACK_GROWS_DOWN)
+# error "One of STACK_GROWS_UP and STACK_GROWS_DOWN should be defd."
+#endif
+
+#if defined(REDIRECT_MALLOC) && defined(THREADS) && !defined(LINUX)
+# error "REDIRECT_MALLOC with THREADS works at most on Linux."
+#endif
+
+#ifdef GC_PRIVATE_H
+        /* This relies on some type definitions from gc_priv.h, from    */
+        /* where it's normally included.                                */
+        /*                                                              */
+        /* How to get heap memory from the OS:                          */
+        /* Note that sbrk()-like allocation is preferred, since it      */
+        /* usually makes it possible to merge consecutively allocated   */
+        /* chunks.  It also avoids unintended recursion with            */
+        /* REDIRECT_MALLOC macro defined.                               */
+        /* GET_MEM() returns a HLKSIZE aligned chunk.                   */
+        /* 0 is taken to mean failure.                                  */
+        /* In the case os USE_MMAP, the argument must also be a         */
+        /* physical page size.                                          */
+        /* GET_MEM is currently not assumed to retrieve 0 filled space, */
+        /* though we should perhaps take advantage of the case in which */
+        /* does.                                                        */
+        struct hblk;    /* See gc_priv.h.       */
+# if defined(PCR)
+    char * real_malloc(size_t bytes);
+#   define GET_MEM(bytes) HBLKPTR(real_malloc((size_t)(bytes) + GC_page_size) \
+                                          + GC_page_size-1)
+# elif defined(OS2)
+    void * os2_alloc(size_t bytes);
+#   define GET_MEM(bytes) HBLKPTR((ptr_t)os2_alloc((size_t)(bytes) \
+                                            + GC_page_size) + GC_page_size-1)
+# elif defined(NEXT) || defined(DOS4GW) || defined(NONSTOP) \
+        || (defined(AMIGA) && !defined(GC_AMIGA_FASTALLOC)) \
+        || (defined(SOLARIS) && !defined(USE_MMAP)) || defined(RTEMS) \
+        || defined(__CC_ARM)
+#   define GET_MEM(bytes) HBLKPTR((size_t)calloc(1, \
+                                            (size_t)(bytes) + GC_page_size) \
+                                  + GC_page_size - 1)
+# elif defined(MSWIN32) || defined(CYGWIN32)
+    ptr_t GC_win32_get_mem(GC_word bytes);
+#   define GET_MEM(bytes) (struct hblk *)GC_win32_get_mem(bytes)
+# elif defined(MACOS)
+#   if defined(USE_TEMPORARY_MEMORY)
+      Ptr GC_MacTemporaryNewPtr(size_t size, Boolean clearMemory);
+#     define GET_MEM(bytes) HBLKPTR( \
+                        GC_MacTemporaryNewPtr((bytes) + GC_page_size, true) \
+                        + GC_page_size-1)
+#   else
+#     define GET_MEM(bytes) HBLKPTR(NewPtrClear((bytes) + GC_page_size) \
+                                    + GC_page_size-1)
+#   endif
+# elif defined(MSWINCE)
+    ptr_t GC_wince_get_mem(GC_word bytes);
+#   define GET_MEM(bytes) (struct hblk *)GC_wince_get_mem(bytes)
+# elif defined(AMIGA) && defined(GC_AMIGA_FASTALLOC)
+    void *GC_amiga_get_mem(size_t size);
+#   define GET_MEM(bytes) HBLKPTR((size_t) \
+                          GC_amiga_get_mem((size_t)(bytes) + GC_page_size) \
+                          + GC_page_size-1)
+# elif defined(SN_TARGET_PS3)
+    void *ps3_get_mem(size_t size);
+#   define GET_MEM(bytes) (struct hblk*)ps3_get_mem(bytes)
+# else
+    ptr_t GC_unix_get_mem(GC_word bytes);
+#   define GET_MEM(bytes) (struct hblk *)GC_unix_get_mem(bytes)
+# endif
+#endif /* GC_PRIVATE_H */
+
+#endif /* GCCONFIG_H */

+ 69 - 0
blitz.mod/bdwgc/include/private/msvc_dbg.h

@@ -0,0 +1,69 @@
+/*
+  Copyright (c) 2004-2005 Andrei Polushin
+
+  Permission is hereby granted, free of charge,  to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction,  including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+#ifndef _MSVC_DBG_H
+#define _MSVC_DBG_H
+
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if !MSVC_DBG_DLL
+#define MSVC_DBG_EXPORT
+#elif MSVC_DBG_BUILD
+#define MSVC_DBG_EXPORT __declspec(dllexport)
+#else
+#define MSVC_DBG_EXPORT __declspec(dllimport)
+#endif
+
+#ifndef MAX_SYM_NAME
+#define MAX_SYM_NAME 2000
+#endif
+
+typedef void*  HANDLE;
+typedef struct _CONTEXT CONTEXT;
+
+MSVC_DBG_EXPORT size_t GetStackFrames(size_t skip, void* frames[], size_t maxFrames);
+MSVC_DBG_EXPORT size_t GetStackFramesFromContext(HANDLE hProcess, HANDLE hThread, CONTEXT* context, size_t skip, void* frames[], size_t maxFrames);
+
+MSVC_DBG_EXPORT size_t GetModuleNameFromAddress(void* address, char* moduleName, size_t size);
+MSVC_DBG_EXPORT size_t GetModuleNameFromStack(size_t skip, char* moduleName, size_t size);
+
+MSVC_DBG_EXPORT size_t GetSymbolNameFromAddress(void* address, char* symbolName, size_t size, size_t* offsetBytes);
+MSVC_DBG_EXPORT size_t GetSymbolNameFromStack(size_t skip, char* symbolName, size_t size, size_t* offsetBytes);
+
+MSVC_DBG_EXPORT size_t GetFileLineFromAddress(void* address, char* fileName, size_t size, size_t* lineNumber, size_t* offsetBytes);
+MSVC_DBG_EXPORT size_t GetFileLineFromStack(size_t skip, char* fileName, size_t size, size_t* lineNumber, size_t* offsetBytes);
+
+MSVC_DBG_EXPORT size_t GetDescriptionFromAddress(void* address, const char* format, char* description, size_t size);
+MSVC_DBG_EXPORT size_t GetDescriptionFromStack(void*const frames[], size_t count, const char* format, char* description[], size_t size);
+
+/* Compatibility with <execinfo.h> */
+MSVC_DBG_EXPORT int    backtrace(void* addresses[], int count);
+MSVC_DBG_EXPORT char** backtrace_symbols(void*const addresses[], int count);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif/*_MSVC_DBG_H*/

+ 44 - 0
blitz.mod/bdwgc/include/private/pthread_stop_world.h

@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
+ * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
+ * Copyright (c) 2000-2009 by Hewlett-Packard Development Company.
+ * All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+#ifndef GC_PTHREAD_STOP_WORLD_H
+#define GC_PTHREAD_STOP_WORLD_H
+
+struct thread_stop_info {
+#   ifndef GC_OPENBSD_UTHREADS
+      word last_stop_count;     /* GC_last_stop_count value when thread */
+                                /* last successfully handled a suspend  */
+                                /* signal.                              */
+#   endif
+
+    ptr_t stack_ptr;            /* Valid only when stopped.             */
+
+#   ifdef NACL
+      /* Grab NACL_GC_REG_STORAGE_SIZE pointers off the stack when      */
+      /* going into a syscall.  20 is more than we need, but it's an    */
+      /* overestimate in case the instrumented function uses any callee */
+      /* saved registers, they may be pushed to the stack much earlier. */
+      /* Also, on amd64 'push' puts 8 bytes on the stack even though    */
+      /* our pointers are 4 bytes.                                      */
+#     define NACL_GC_REG_STORAGE_SIZE 20
+      ptr_t reg_storage[NACL_GC_REG_STORAGE_SIZE];
+#   endif
+};
+
+GC_INNER void GC_stop_init(void);
+
+#endif

+ 153 - 0
blitz.mod/bdwgc/include/private/pthread_support.h

@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
+ * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
+ * Copyright (c) 2000-2009 by Hewlett-Packard Development Company.
+ * All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+#ifndef GC_PTHREAD_SUPPORT_H
+#define GC_PTHREAD_SUPPORT_H
+
+#include "private/gc_priv.h"
+
+#if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS)
+
+#if defined(GC_DARWIN_THREADS)
+# include "private/darwin_stop_world.h"
+#else
+# include "private/pthread_stop_world.h"
+#endif
+
+#ifdef THREAD_LOCAL_ALLOC
+# include "thread_local_alloc.h"
+#endif
+
+/* We use the allocation lock to protect thread-related data structures. */
+
+/* The set of all known threads.  We intercept thread creation and      */
+/* joins.                                                               */
+/* Protected by allocation/GC lock.                                     */
+/* Some of this should be declared volatile, but that's inconsistent    */
+/* with some library routine declarations.                              */
+typedef struct GC_Thread_Rep {
+    struct GC_Thread_Rep * next;  /* More recently allocated threads    */
+                                  /* with a given pthread id come       */
+                                  /* first.  (All but the first are     */
+                                  /* guaranteed to be dead, but we may  */
+                                  /* not yet have registered the join.) */
+    pthread_t id;
+#   ifdef PLATFORM_ANDROID
+      pid_t kernel_id;
+#   endif
+    /* Extra bookkeeping information the stopping code uses */
+    struct thread_stop_info stop_info;
+
+    unsigned char flags;
+#       define FINISHED 1       /* Thread has exited.                   */
+#       define DETACHED 2       /* Thread is treated as detached.       */
+                                /* Thread may really be detached, or    */
+                                /* it may have been explicitly          */
+                                /* registered, in which case we can     */
+                                /* deallocate its GC_Thread_Rep once    */
+                                /* it unregisters itself, since it      */
+                                /* may not return a GC pointer.         */
+#       define MAIN_THREAD 4    /* True for the original thread only.   */
+#       define SUSPENDED_EXT 8  /* Thread was suspended externally      */
+                                /* (this is not used by the unmodified  */
+                                /* GC itself at present).               */
+#       define DISABLED_GC 0x10 /* Collections are disabled while the   */
+                                /* thread is exiting.                   */
+
+    unsigned char thread_blocked;
+                                /* Protected by GC lock.                */
+                                /* Treated as a boolean value.  If set, */
+                                /* thread will acquire GC lock before   */
+                                /* doing any pointer manipulations, and */
+                                /* has set its SP value.  Thus it does  */
+                                /* not need to be sent a signal to stop */
+                                /* it.                                  */
+
+    unsigned short finalizer_skipped;
+    unsigned char finalizer_nested;
+                                /* Used by GC_check_finalizer_nested()  */
+                                /* to minimize the level of recursion   */
+                                /* when a client finalizer allocates    */
+                                /* memory (initially both are 0).       */
+
+    ptr_t stack_end;            /* Cold end of the stack (except for    */
+                                /* main thread).                        */
+#   if defined(GC_DARWIN_THREADS) && !defined(DARWIN_DONT_PARSE_STACK)
+      ptr_t topOfStack;         /* Result of GC_FindTopOfStack(0);      */
+                                /* valid only if the thread is blocked; */
+                                /* non-NULL value means already set.    */
+#   endif
+#   ifdef IA64
+        ptr_t backing_store_end;
+        ptr_t backing_store_ptr;
+#   endif
+
+    struct GC_traced_stack_sect_s *traced_stack_sect;
+                        /* Points to the "frame" data held in stack by  */
+                        /* the innermost GC_call_with_gc_active() of    */
+                        /* this thread.  May be NULL.                   */
+
+    void * status;              /* The value returned from the thread.  */
+                                /* Used only to avoid premature         */
+                                /* reclamation of any data it might     */
+                                /* reference.                           */
+                                /* This is unfortunately also the       */
+                                /* reason we need to intercept join     */
+                                /* and detach.                          */
+
+#   ifdef THREAD_LOCAL_ALLOC
+        struct thread_local_freelists tlfs;
+#   endif
+} * GC_thread;
+
+# define THREAD_TABLE_SZ 256    /* Must be power of 2   */
+GC_EXTERN volatile GC_thread GC_threads[THREAD_TABLE_SZ];
+
+GC_EXTERN GC_bool GC_thr_initialized;
+
+GC_INNER GC_thread GC_lookup_thread(pthread_t id);
+
+GC_EXTERN GC_bool GC_in_thread_creation;
+        /* We may currently be in thread creation or destruction.       */
+        /* Only set to TRUE while allocation lock is held.              */
+        /* When set, it is OK to run GC from unknown thread.            */
+
+#ifdef NACL
+  GC_EXTERN __thread GC_thread GC_nacl_gc_thread_self;
+  GC_INNER void GC_nacl_initialize_gc_thread(void);
+  GC_INNER void GC_nacl_shutdown_gc_thread(void);
+#endif
+
+#ifdef GC_EXPLICIT_SIGNALS_UNBLOCK
+  GC_INNER void GC_unblock_gc_signals(void);
+#endif
+
+#ifdef GC_PTHREAD_START_STANDALONE
+# define GC_INNER_PTHRSTART /* empty */
+#else
+# define GC_INNER_PTHRSTART GC_INNER
+#endif
+
+GC_INNER_PTHRSTART GC_thread GC_start_rtn_prepare_thread(
+                                        void *(**pstart)(void *),
+                                        void **pstart_arg,
+                                        struct GC_stack_base *sb, void *arg);
+GC_INNER_PTHRSTART void GC_thread_exit_proc(void *);
+
+#endif /* GC_PTHREADS && !GC_WIN32_THREADS */
+
+#endif /* GC_PTHREAD_SUPPORT_H */

+ 98 - 0
blitz.mod/bdwgc/include/private/specific.h

@@ -0,0 +1,98 @@
+/*
+ * This is a reimplementation of a subset of the pthread_getspecific/setspecific
+ * interface. This appears to outperform the standard linuxthreads one
+ * by a significant margin.
+ * The major restriction is that each thread may only make a single
+ * pthread_setspecific call on a single key.  (The current data structure
+ * doesn't really require that.  The restriction should be easily removable.)
+ * We don't currently support the destruction functions, though that
+ * could be done.
+ * We also currently assume that only one pthread_setspecific call
+ * can be executed at a time, though that assumption would be easy to remove
+ * by adding a lock.
+ */
+
+#include <errno.h>
+#include "atomic_ops.h"
+
+/* Called during key creation or setspecific.           */
+/* For the GC we already hold lock.                     */
+/* Currently allocated objects leak on thread exit.     */
+/* That's hard to fix, but OK if we allocate garbage    */
+/* collected memory.                                    */
+#define MALLOC_CLEAR(n) GC_INTERNAL_MALLOC(n, NORMAL)
+
+#define TS_CACHE_SIZE 1024
+#define CACHE_HASH(n) ((((n) >> 8) ^ (n)) & (TS_CACHE_SIZE - 1))
+
+#define TS_HASH_SIZE 1024
+#define HASH(p) \
+          ((unsigned)((((word)(p)) >> 8) ^ (word)(p)) & (TS_HASH_SIZE - 1))
+
+/* An entry describing a thread-specific value for a given thread.      */
+/* All such accessible structures preserve the invariant that if either */
+/* thread is a valid pthread id or qtid is a valid "quick tread id"     */
+/* for a thread, then value holds the corresponding thread specific     */
+/* value.  This invariant must be preserved at ALL times, since         */
+/* asynchronous reads are allowed.                                      */
+typedef struct thread_specific_entry {
+        volatile AO_t qtid;     /* quick thread id, only for cache */
+        void * value;
+        struct thread_specific_entry *next;
+        pthread_t thread;
+} tse;
+
+/* We represent each thread-specific datum as two tables.  The first is */
+/* a cache, indexed by a "quick thread identifier".  The "quick" thread */
+/* identifier is an easy to compute value, which is guaranteed to       */
+/* determine the thread, though a thread may correspond to more than    */
+/* one value.  We typically use the address of a page in the stack.     */
+/* The second is a hash table, indexed by pthread_self().  It is used   */
+/* only as a backup.                                                    */
+
+/* Return the "quick thread id".  Default version.  Assumes page size,  */
+/* or at least thread stack separation, is at least 4K.                 */
+/* Must be defined so that it never returns 0.  (Page 0 can't really be */
+/* part of any stack, since that would make 0 a valid stack pointer.)   */
+#define quick_thread_id() (((word)GC_approx_sp()) >> 12)
+
+#define INVALID_QTID ((word)0)
+#define INVALID_THREADID ((pthread_t)0)
+
+union ptse_ao_u {
+  tse *p;
+  volatile AO_t ao;
+};
+
+typedef struct thread_specific_data {
+    tse * volatile cache[TS_CACHE_SIZE];
+                        /* A faster index to the hash table */
+    union ptse_ao_u hash[TS_HASH_SIZE];
+    pthread_mutex_t lock;
+} tsd;
+
+typedef tsd * GC_key_t;
+
+#define GC_key_create(key, d) GC_key_create_inner(key)
+GC_INNER int GC_key_create_inner(tsd ** key_ptr);
+GC_INNER int GC_setspecific(tsd * key, void * value);
+GC_INNER void GC_remove_specific(tsd * key);
+
+/* An internal version of getspecific that assumes a cache miss.        */
+GC_INNER void * GC_slow_getspecific(tsd * key, word qtid,
+                                    tse * volatile * cache_entry);
+
+/* GC_INLINE is defined in gc_priv.h. */
+GC_INLINE void * GC_getspecific(tsd * key)
+{
+    word qtid = quick_thread_id();
+    tse * volatile * entry_ptr = &key->cache[CACHE_HASH(qtid)];
+    tse * entry = *entry_ptr;   /* Must be loaded only once.    */
+
+    GC_ASSERT(qtid != INVALID_QTID);
+    if (EXPECT(entry -> qtid == qtid, TRUE)) {
+      GC_ASSERT(entry -> thread == pthread_self());
+      return entry -> value;
+    }
+    return GC_slow_getspecific(key, qtid, entry_ptr);
+}

+ 175 - 0
blitz.mod/bdwgc/include/private/thread_local_alloc.h

@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2000-2005 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.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * 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.
+ */
+
+/* Included indirectly from a thread-library-specific file.     */
+/* This is the interface for thread-local allocation, whose     */
+/* implementation is mostly thread-library-independent.         */
+/* Here we describe only the interface that needs to be known   */
+/* and invoked from the thread support layer;  the actual       */
+/* implementation also exports GC_malloc and friends, which     */
+/* are declared in gc.h.                                        */
+
+#ifndef GC_THREAD_LOCAL_ALLOC_H
+#define GC_THREAD_LOCAL_ALLOC_H
+
+#include "private/gc_priv.h"
+
+#ifdef THREAD_LOCAL_ALLOC
+
+#include "gc_inline.h"
+
+#if defined(USE_HPUX_TLS)
+# error USE_HPUX_TLS macro was replaced by USE_COMPILER_TLS
+#endif
+
+#if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_WIN32_SPECIFIC) \
+    && !defined(USE_WIN32_COMPILER_TLS) && !defined(USE_COMPILER_TLS) \
+    && !defined(USE_CUSTOM_SPECIFIC)
+# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32)
+#   if defined(CYGWIN32) && (__GNUC__ >= 4)
+#     if defined(__clang__)
+        /* As of Cygwin clang3.1, thread-local storage is unsupported.  */
+#       define USE_PTHREAD_SPECIFIC
+#     else
+#       define USE_COMPILER_TLS
+#     endif
+#   elif defined(__GNUC__) || defined(MSWINCE)
+#     define USE_WIN32_SPECIFIC
+#   else
+#     define USE_WIN32_COMPILER_TLS
+#   endif /* !GNU */
+# elif (defined(LINUX) && !defined(ARM32) && !defined(AVR32) \
+         && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) \
+         && !(defined(__clang__) && defined(PLATFORM_ANDROID))) \
+       || (defined(PLATFORM_ANDROID) && defined(ARM32) \
+            && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
+          /* As of Android NDK r8e, Clang cannot find __tls_get_addr.   */
+#   define USE_COMPILER_TLS
+# elif defined(GC_DGUX386_THREADS) || defined(GC_OSF1_THREADS) \
+       || defined(GC_AIX_THREADS) || defined(GC_DARWIN_THREADS) \
+       || defined(GC_FREEBSD_THREADS) || defined(GC_NETBSD_THREADS) \
+       || defined(GC_LINUX_THREADS) || defined(GC_RTEMS_PTHREADS)
+#   define USE_PTHREAD_SPECIFIC
+# elif defined(GC_HPUX_THREADS)
+#   ifdef __GNUC__
+#     define USE_PTHREAD_SPECIFIC
+        /* Empirically, as of gcc 3.3, USE_COMPILER_TLS doesn't work.   */
+#   else
+#     define USE_COMPILER_TLS
+#   endif
+# else
+#    define USE_CUSTOM_SPECIFIC  /* Use our own. */
+# endif
+#endif
+
+#include <stdlib.h>
+
+/* One of these should be declared as the tlfs field in the     */
+/* structure pointed to by a GC_thread.                         */
+typedef struct thread_local_freelists {
+  void * ptrfree_freelists[TINY_FREELISTS];
+  void * normal_freelists[TINY_FREELISTS];
+# ifdef GC_GCJ_SUPPORT
+    void * gcj_freelists[TINY_FREELISTS];
+#   define ERROR_FL ((void *)(word)-1)
+        /* Value used for gcj_freelist[-1]; allocation is       */
+        /* erroneous.                                           */
+# endif
+# ifdef ENABLE_DISCLAIM
+    void * finalized_freelists[TINY_FREELISTS];
+# endif
+  /* Free lists contain either a pointer or a small count       */
+  /* reflecting the number of granules allocated at that        */
+  /* size.                                                      */
+  /* 0 ==> thread-local allocation in use, free list            */
+  /*       empty.                                               */
+  /* > 0, <= DIRECT_GRANULES ==> Using global allocation,       */
+  /*       too few objects of this size have been               */
+  /*       allocated by this thread.                            */
+  /* >= HBLKSIZE  => pointer to nonempty free list.             */
+  /* > DIRECT_GRANULES, < HBLKSIZE ==> transition to            */
+  /*    local alloc, equivalent to 0.                           */
+# define DIRECT_GRANULES (HBLKSIZE/GRANULE_BYTES)
+        /* Don't use local free lists for up to this much       */
+        /* allocation.                                          */
+} *GC_tlfs;
+
+#if defined(USE_PTHREAD_SPECIFIC)
+# define GC_getspecific pthread_getspecific
+# define GC_setspecific pthread_setspecific
+# define GC_key_create pthread_key_create
+# define GC_remove_specific(key)  /* No need for cleanup on exit. */
+  typedef pthread_key_t GC_key_t;
+#elif defined(USE_COMPILER_TLS) || defined(USE_WIN32_COMPILER_TLS)
+# define GC_getspecific(x) (x)
+# define GC_setspecific(key, v) ((key) = (v), 0)
+# define GC_key_create(key, d) 0
+# define GC_remove_specific(key)  /* No need for cleanup on exit. */
+  typedef void * GC_key_t;
+#elif defined(USE_WIN32_SPECIFIC)
+# ifndef WIN32_LEAN_AND_MEAN
+#   define WIN32_LEAN_AND_MEAN 1
+# endif
+# define NOSERVICE
+# include <windows.h>
+# define GC_getspecific TlsGetValue
+# define GC_setspecific(key, v) !TlsSetValue(key, v)
+        /* We assume 0 == success, msft does the opposite.      */
+# ifndef TLS_OUT_OF_INDEXES
+        /* this is currently missing in WinCE   */
+#   define TLS_OUT_OF_INDEXES (DWORD)0xFFFFFFFF
+# endif
+# define GC_key_create(key, d) \
+        ((d) != 0 || (*(key) = TlsAlloc()) == TLS_OUT_OF_INDEXES ? -1 : 0)
+# define GC_remove_specific(key)  /* No need for cleanup on exit. */
+        /* Need TlsFree on process exit/detach?   */
+  typedef DWORD GC_key_t;
+#elif defined(USE_CUSTOM_SPECIFIC)
+# include "private/specific.h"
+#else
+# error implement me
+#endif
+
+/* Each thread structure must be initialized.   */
+/* This call must be made from the new thread.  */
+/* Caller holds allocation lock.                */
+GC_INNER void GC_init_thread_local(GC_tlfs p);
+
+/* Called when a thread is unregistered, or exits.      */
+/* We hold the allocator lock.                          */
+GC_INNER void GC_destroy_thread_local(GC_tlfs p);
+
+/* The thread support layer must arrange to mark thread-local   */
+/* free lists explicitly, since the link field is often         */
+/* invisible to the marker.  It knows how to find all threads;  */
+/* we take care of an individual thread freelist structure.     */
+GC_INNER void GC_mark_thread_local_fls_for(GC_tlfs p);
+
+#ifdef ENABLE_DISCLAIM
+  GC_EXTERN ptr_t * GC_finalized_objfreelist;
+#endif
+
+extern
+#if defined(USE_COMPILER_TLS)
+  __thread
+#elif defined(USE_WIN32_COMPILER_TLS)
+  __declspec(thread)
+#endif
+GC_key_t GC_thread_key;
+/* This is set up by the thread_local_alloc implementation.  No need    */
+/* for cleanup on thread exit.  But the thread support layer makes sure */
+/* that GC_thread_key is traced, if necessary.                          */
+
+#endif /* THREAD_LOCAL_ALLOC */
+
+#endif /* GC_THREAD_LOCAL_ALLOC_H */

+ 217 - 0
blitz.mod/bdwgc/include/weakpointer.h

@@ -0,0 +1,217 @@
+#ifndef _weakpointer_h_
+#define _weakpointer_h_
+
+/****************************************************************************
+
+WeakPointer and CleanUp
+
+    Copyright (c) 1991 by Xerox Corporation.  All rights reserved.
+
+    THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+    OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+
+    Permission is hereby granted to copy this code for any purpose,
+    provided the above notices are retained on all copies.
+
+****************************************************************************/
+
+/****************************************************************************
+
+WeakPointer
+
+A weak pointer is a pointer to a heap-allocated object that doesn't
+prevent the object from being garbage collected. Weak pointers can be
+used to track which objects haven't yet been reclaimed by the
+collector. A weak pointer is deactivated when the collector discovers
+its referent object is unreachable by normal pointers (reachability
+and deactivation are defined more precisely below). A deactivated weak
+pointer remains deactivated forever.
+
+****************************************************************************/
+
+
+template< class T > class WeakPointer {
+public:
+
+WeakPointer( T* t = 0 )
+    /* Constructs a weak pointer for *t. t may be null. It is an error
+       if t is non-null and *t is not a collected object. */
+    {impl = _WeakPointer_New( t );}
+
+T* Pointer()
+    /* wp.Pointer() returns a pointer to the referent object of wp or
+       null if wp has been deactivated (because its referent object
+       has been discovered unreachable by the collector). */
+    {return (T*) _WeakPointer_Pointer( this->impl );}
+
+int operator==( WeakPointer< T > wp2 )
+    /* Given weak pointers wp1 and wp2, if wp1 == wp2, then wp1 and
+       wp2 refer to the same object. If wp1 != wp2, then either wp1
+       and wp2 don't refer to the same object, or if they do, one or
+       both of them has been deactivated. (Note: If objects t1 and t2
+       are never made reachable by their clean-up functions, then
+       WeakPointer<T>(t1) == WeakPointer<T>(t2) if and only t1 == t2.) */
+    {return _WeakPointer_Equal( this->impl, wp2.impl );}
+
+int Hash()
+    /* Returns a hash code suitable for use by multiplicative- and
+       division-based hash tables. If wp1 == wp2, then wp1.Hash() ==
+       wp2.Hash(). */
+    {return _WeakPointer_Hash( this->impl );}
+
+private:
+void* impl;
+};
+
+/*****************************************************************************
+
+CleanUp
+
+A garbage-collected object can have an associated clean-up function
+that will be invoked some time after the collector discovers the
+object is unreachable via normal pointers. Clean-up functions can be
+used to release resources such as open-file handles or window handles
+when their containing objects become unreachable.  If a C++ object has
+a non-empty explicit destructor (i.e. it contains programmer-written
+code), the destructor will be automatically registered as the object's
+initial clean-up function.
+
+There is no guarantee that the collector will detect every unreachable
+object (though it will find almost all of them). Clients should not
+rely on clean-up to cause some action to occur immediately -- clean-up
+is only a mechanism for improving resource usage.
+
+Every object with a clean-up function also has a clean-up queue. When
+the collector finds the object is unreachable, it enqueues it on its
+queue. The clean-up function is applied when the object is removed
+from the queue. By default, objects are enqueued on the garbage
+collector's queue, and the collector removes all objects from its
+queue after each collection. If a client supplies another queue for
+objects, it is his responsibility to remove objects (and cause their
+functions to be called) by polling it periodically.
+
+Clean-up queues allow clean-up functions accessing global data to
+synchronize with the main program. Garbage collection can occur at any
+time, and clean-ups invoked by the collector might access data in an
+inconsistent state. A client can control this by defining an explicit
+queue for objects and polling it at safe points.
+
+The following definitions are used by the specification below:
+
+Given a pointer t to a collected object, the base object BO(t) is the
+value returned by new when it created the object. (Because of multiple
+inheritance, t and BO(t) may not be the same address.)
+
+A weak pointer wp references an object *t if BO(wp.Pointer()) ==
+BO(t).
+
+***************************************************************************/
+
+template< class T, class Data > class CleanUp {
+public:
+
+static void Set( T* t, void c( Data* d, T* t ), Data* d = 0 )
+    /* Sets the clean-up function of object BO(t) to be <c, d>,
+       replacing any previously defined clean-up function for BO(t); c
+       and d can be null, but t cannot. Sets the clean-up queue for
+       BO(t) to be the collector's queue. When t is removed from its
+       clean-up queue, its clean-up will be applied by calling c(d,
+       t). It is an error if *t is not a collected object. */
+       {_CleanUp_Set( t, c, d );}
+
+static void Call( T* t )
+    /* Sets the new clean-up function for BO(t) to be null and, if the
+       old one is non-null, calls it immediately, even if BO(t) is
+       still reachable. Deactivates any weak pointers to BO(t). */
+       {_CleanUp_Call( t );}
+
+class Queue {public:
+    Queue()
+        /* Constructs a new queue. */
+            {this->head = _CleanUp_Queue_NewHead();}
+
+    void Set( T* t )
+        /* q.Set(t) sets the clean-up queue of BO(t) to be q. */
+            {_CleanUp_Queue_Set( this->head, t );}
+
+    int Call()
+        /* If q is non-empty, q.Call() removes the first object and
+           calls its clean-up function; does nothing if q is
+           empty. Returns true if there are more objects in the
+           queue. */
+           {return _CleanUp_Queue_Call( this->head );}
+
+    private:
+    void* head;
+    };
+};
+
+/**********************************************************************
+
+Reachability and Clean-up
+
+An object O is reachable if it can be reached via a non-empty path of
+normal pointers from the registers, stacks, global variables, or an
+object with a non-null clean-up function (including O itself),
+ignoring pointers from an object to itself.
+
+This definition of reachability ensures that if object B is accessible
+from object A (and not vice versa) and if both A and B have clean-up
+functions, then A will always be cleaned up before B. Note that as
+long as an object with a clean-up function is contained in a cycle of
+pointers, it will always be reachable and will never be cleaned up or
+collected.
+
+When the collector finds an unreachable object with a null clean-up
+function, it atomically deactivates all weak pointers referencing the
+object and recycles its storage. If object B is accessible from object
+A via a path of normal pointers, A will be discovered unreachable no
+later than B, and a weak pointer to A will be deactivated no later
+than a weak pointer to B.
+
+When the collector finds an unreachable object with a non-null
+clean-up function, the collector atomically deactivates all weak
+pointers referencing the object, redefines its clean-up function to be
+null, and enqueues it on its clean-up queue. The object then becomes
+reachable again and remains reachable at least until its clean-up
+function executes.
+
+The clean-up function is assured that its argument is the only
+accessible pointer to the object. Nothing prevents the function from
+redefining the object's clean-up function or making the object
+reachable again (for example, by storing the pointer in a global
+variable).
+
+If the clean-up function does not make its object reachable again and
+does not redefine its clean-up function, then the object will be
+collected by a subsequent collection (because the object remains
+unreachable and now has a null clean-up function). If the clean-up
+function does make its object reachable again and a clean-up function
+is subsequently redefined for the object, then the new clean-up
+function will be invoked the next time the collector finds the object
+unreachable.
+
+Note that a destructor for a collected object cannot safely redefine a
+clean-up function for its object, since after the destructor executes,
+the object has been destroyed into "raw memory". (In most
+implementations, destroying an object mutates its vtbl.)
+
+Finally, note that calling delete t on a collected object first
+deactivates any weak pointers to t and then invokes its clean-up
+function (destructor).
+
+**********************************************************************/
+
+extern "C" {
+    void* _WeakPointer_New( void* t );
+    void* _WeakPointer_Pointer( void* wp );
+    int _WeakPointer_Equal( void* wp1, void* wp2 );
+    int _WeakPointer_Hash( void* wp );
+    void _CleanUp_Set( void* t, void (*c)( void* d, void* t ), void* d );
+    void _CleanUp_Call( void* t );
+    void* _CleanUp_Queue_NewHead ();
+    void _CleanUp_Queue_Set( void* h, void* t );
+    int _CleanUp_Queue_Call( void* h );
+}
+
+#endif /* _weakpointer_h_ */

+ 7 - 0
blitz.mod/bdwgc/libatomic_ops/.gitattributes

@@ -0,0 +1,7 @@
+# Git repo attributes.
+
+# Ensure all text files have normalized (LF) line endings in the repository.
+* text=auto
+
+# Note: "core.eol" configuration variable controls which line endings to use
+# for the normalized files in the working directory (the default is native).

+ 66 - 0
blitz.mod/bdwgc/libatomic_ops/.gitignore

@@ -0,0 +1,66 @@
+# Ignored files in libatomic_ops Git repo.
+
+Makefile
+
+/pkgconfig/atomic_ops.pc
+/pkgconfig/atomic_ops-uninstalled.pc
+/autom4te.cache/
+/config.cache
+/config.log
+/config.status
+/libatomic_ops-*
+
+*.a
+*.dll
+*.exe
+*.gcda
+*.gch
+*.gcno
+*.la
+*.lib
+*.lo
+*.o
+*.obj
+*.so
+
+/src/.deps/
+/src/.libs/
+/src/config.h
+/src/config.h.in~
+/src/stamp-h1
+
+/tests/.deps/
+/tests/core
+/tests/list_atomic.i
+/tests/test_atomic
+/tests/test_atomic_pthreads
+/tests/test_malloc
+/tests/test_stack
+
+# External library (without trailing slash to allow symlinks):
+/pthreads-w32*
+
+# These files are generated by autoreconf:
+/aclocal.m4
+/compile
+/config.guess
+/config.sub
+/configure
+/depcomp
+/install-sh
+/missing
+/mkinstalldirs
+/src/config.h.in
+/test-driver
+Makefile.in
+
+# Generated by libtoolize:
+/libtool
+/ltmain.sh
+/m4/*.m4
+
+# These files are generated by make check:
+/tests/list_atomic.c
+/tests/test_atomic_include.h
+/tests/test*.log
+/tests/test*.trs

+ 40 - 0
blitz.mod/bdwgc/libatomic_ops/AUTHORS

@@ -0,0 +1,40 @@
+Originally written by Hans Boehm, with some platform-dependent code
+imported from the Boehm-Demers-Weiser GC, where it was contributed
+by many others.
+Currently maintained by Ivan Maidanski.
+
+Andreas Tobler <[email protected]>
+Andrew Agno <[email protected]>
+Bradley Smith <[email protected]>
+Bruce Mitchener <[email protected]>
+Carlos O'Donell <[email protected]>
+Daniel Grayson <[email protected]>
+Doug Lea <[email protected]>
+Earl Chew <[email protected]>
+Emmanuel Stapf <[email protected]>
+Gilles Talis <[email protected]>
+Gregory Farnum <[email protected]>
+H.J. Lu <[email protected]>
+Hans Boehm <[email protected]>
+Hans-Peter Nilsson <[email protected]>
+Ian Wienand <[email protected]>
+Ivan Maidanski <[email protected]>
+Jeremy Huddleston <[email protected]>
+Jim Marshall <[email protected]>
+Joerg Wagner <[email protected]>
+Linas Vepstas <[email protected]>
+Luca Barbato <[email protected]>
+Maged Michael <[email protected]>
+Manuel Serrano <[email protected]>
+Michael Hope <[email protected]>
+Patrick Marlier <[email protected]>
+Petter Urkedal <[email protected]>
+Philipp Zambelli <[email protected]>
+Ranko Zivojnovic <[email protected]>
+Roger Hoover <[email protected]>
+Sebastian Siewior <[email protected]>
+Takashi Yoshii <[email protected]>
+Thiemo Seufer <[email protected]>
+Thorsten Glaser <[email protected]>
+Tony Mantler <[email protected]>
+Yvan Roux <[email protected]>

+ 340 - 0
blitz.mod/bdwgc/libatomic_ops/COPYING

@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.

+ 312 - 0
blitz.mod/bdwgc/libatomic_ops/ChangeLog

@@ -0,0 +1,312 @@
+
+== [7.4.0] 2013-11-17 ==
+
+* Add and/or/xor entries to list_atomic (tests).
+* Add char/short/int/AO_double_t and dd_acquire cases to list_atomic (tests).
+* Add compile-time assertion for size of 'standard' AO_double_t.
+* Add double_store pthread-based implementation and tests.
+* Add generalized CAS primitives of char/short/int size.
+* Add generalized atomic and/or/xor operations for char/short/int types.
+* Add generalized fetch_and_add_acquire/release (for ARMv6+).
+* Add generic implementation of double_load primitives.
+* Add information about AO_ASSUME_VISTA to README_win32.
+* Add internal header containing only char/short/int/AO_t atomic loads.
+* Add load/store primitives generalization based on CAS.
+* Add lock-based implementation of char/short/int_fetch_compare_and_swap.
+* Add makefile rule to test list_atomic.template syntax (tests).
+* Add missing 'const' in aligned-atomic XSIZE_load implementation.
+* Add missing double_compare_and_swap to generalization.
+* Add missing generalization of no-barrier CAS in template.
+* Add negative double-CAS test cases to test_atomic_include (tests).
+* Add test_stack to Makefile.msft (tests).
+* Adjust fprintf arguments type matching specifier in test_stack (tests).
+* Adjust included filenames in atomic_ops_malloc and test_stack.
+* Adjust quotes in echo command of Makefile.msft (Win32).
+* Always use 'mfence' for nop_full if target CPU supports SSE2 (gcc/x86).
+* Better document configure THREADDLLIBS variable.
+* Cast away volatile on dereference in CAS-based generalization primitives.
+* Change policy regarding version numbers ("micro" part instead of "alpha").
+* Convert README to Markdown format.
+* Define AO_NO_PTHREADS in configure if no pthreads (Win32 and VxWorks).
+* Define AO_int_X operations for ARM and avr32.
+* Define double-wide ordered loads/stores for x86.
+* Define int_and/or/xor primitives in ao_t_is_int header.
+* Define nop_full as compiler barrier for pre-ARMv6 single-core case.
+* Do not duplicate BUILT_SOURCES entries in nobase_private_HEADERS (Makefile).
+* Do not include standard_ao_double_t.h where double-CAS is unimplemented.
+* Do not report absence of meaningless nop, load and store in test_atomic.
+* Do not use deprecated AO_T and AO_TS_T (tests).
+* Eliminate 'missing initializer' warning for AO_stack_t value initializer.
+* Eliminate 64-bit compiler warnings in atomic_ops_malloc.
+* Eliminate arithmetic shifts in double-CAS (gcc/arm, msftc/x86).
+* Eliminate warning for fetch_and_add argument in test_atomic_include (tests).
+* Enable Makefile.msft for Win64.
+* Enable build using toolchain without pthreads.
+* Enable double_compare_and_swap for non-cpp code (msftc/x86.h).
+* Enable generalization of all variants of CAS via fetch_compare_and_swap.
+* Enable test_stack for pthreads-w32 and Win32 with native threads.
+* Fix generalized AO_char/short_compare_and_swap args (missing 'unsigned').
+* Fix makefile sed rule for list_atomic (tests).
+* Fix missing abort() usage in atomic_ops_malloc and tests on WinCE.
+* Generalize compare_double_and_swap_double using double_compare_and_swap.
+* Generalize double_load/store for x86_64 (GCC).
+* Generate ao_t_is_int, 'loadstore' headers from templates.
+* Generate generalized AO_t load/store/fetch_and_add primitives from template.
+* Generate ordered_loads/stores_only headers from templates.
+* Group all X_acquire_release_volatile.h and X_[aligned_]atomic_load_store.h.
+* Implement and/or/xor, AO_double_load for ARM.
+* Implement atomic store using direct write by default on ARMv6+.
+* Implement char/short/int-wide primitives using GCC built-in atomic/sync.
+* Implement char/short/int_fetch_and_add for msftc/x86[_64] (Win32).
+* Implement char/short_fetch_and_add, char/short_load for ARMv6+ (GCC).
+* Implement char/short_store primitives at aligned addresses for ARM.
+* Implement compare_double_and_swap_double for SunCC/x86.
+* Implement double_load/store based on guaranteed x86 access atomicity.
+* Implement double_store for ARMv7 using LDREXD/STREXD.
+* Implement load/store via simple LDR/STR for ARMv6+ (msftc).
+* Implement nop_full/write using 'dmb' instruction if available (gcc/arm).
+* Improve debug printing in test_stack (tests).
+* Log messages to stdout instead of stderr (tests).
+* Make AO_ASSUME_VISTA also enables Win98 code in msftc/x86.h (Win32).
+* Minimize gcc/generic-arithm template by factoring out barriers.
+* Move 'unsigned' keyword to XCTYPE in generalize-small template.
+* Move default compiler options to CFLAGS in Makefile.msft (Win32).
+* Move definitions of ordered loads/stores to inner separate headers.
+* Move gcc-generic AO_t-wide primitives to generic-small/arithm headers.
+* Move generalized arithmetical primitives to 'generalize-arithm' template.
+* Optimize AO_spin manually to minimize compiler influence on its duration.
+* Parameterize list_atomic template with XSIZE (tests).
+* Perform only few list reversals in test_malloc if AO based on pthreads.
+* Put autogen.sh to 'dist' package (Automake).
+* Remote duplicate definition of test_and_set_acquire in generalize.h.
+* Remove X_aligned_atomic_load_store headers and template.
+* Remove duplicate AO_spin and AO_pause definition in atomic_ops_stack.
+* Remove gcc/x86_64.h eliminating code duplication of gcc/x86.h.
+* Remove nested AO_USE_PTHREAD_DEFS macro check in atomic_ops.h (gcc/arm).
+* Remove redundant 'cc' clobber for LDREXD instruction (gcc/arm).
+* Remove store_full from msftc/arm.h in favor of generalized primitive.
+* Remove sunc/x86_64.h eliminating code duplication of sunc/x86.h.
+* Remove unsafe emulation-based implementation of double CAS (SunCC/x86_64).
+* Remove useless 'perror' call in run_parallel.h (tests).
+* Reorder AO_double_t union elements for AO_DOUBLE_T_INITIALIZER portability.
+* Replace atomic_load_store.template with atomic_load and atomic_store ones.
+* Replace some FIXME items with TODO in atomic_ops.c and sysdeps headers.
+* Specify fetch_and_add/sub1 result as unused in test_atomic (tests).
+* Support AArch64 (64-bit ARM) target (GCC).
+* Support ARMv8 target (gcc/arm).
+* Test double_compare_and_swap in test_atomic (tests).
+* Use AO_ prefix for internal functions in arm_v6.h, hppa.h.
+* Use __atomic GCC built-in to implement generic double-wide CAS.
+* Use built-in __sync CAS for double-CAS if AO_USE_SYNC_CAS_BUILTIN for x86.
+* Workaround GCC 4.4.3 warning reported for 'val' of list_atomic.c (tests).
+
+
+== [7.3alpha2] 2012-05-11 ==
+
+* Add '-no-undefined' to LDFLAGS in src/Makefile.am.
+* Add AO_and, AO_xor atomic operations.
+* Add AO_fetch_compare_and_swap primitives.
+* Add and fill in AUTHORS, TODO files.
+* Add autogen.sh file.
+* Adjust AO_..._H macros in public headers.
+* Code refactoring of gcc/arm.h by introducing AO_ARM_HAVE_x macros.
+* Define AO macros for libatomic_ops version identification.
+* Do not define NDEBUG if '--enable-assertions' passed to configure.
+* Eliminate compiler warnings in various functions and macros.
+* Generalize AO_compare_and_swap primitives via AO_fetch_compare_and_swap.
+* Generalize acquire/release/full CAS primitives for MIPS
+* Implement fetch_and_add, test_and_set primitives for MIPS.
+* Improve Makefile for MS VC++; pass '-W3' option to MS compiler.
+* Include ao_t_is_int.h from atomic_ops.h after first generalization pass
+* Merge all Makefile.am files in src tree.
+* Minor code refactoring of atomic_ops.c, generic_pthread.h.
+* Minor configure build improvements (e.g., ensure proper autoconf version).
+* Place only major per-release changes description to ChangeLog (this file).
+* Recognize AO_PREFER_GENERALIZED macro to favor generalization over assembly.
+* Remove all auto-generated files except for generalize-small.h from the repo.
+* Remove duplicate doc/COPYING and empty NEWS files.
+* Replace atomic_ops_malloc static mmap-related empty functions with macros.
+* Replace pointer relational comparisons with non-pointer ones.
+* Require autoconf 2.61 instead of v2.64.
+* Show extra compiler warnings (GCC only).
+* Turn off AO primitives inlining if AO_NO_INLINE defined.
+* Use __builtin_expect in CAS failure loop condition checks (GCC only).
+
+
+== [7.2e] 2013-11-10 ==
+
+* Fix (remove) invalid include of read_ordered.h for ARM.
+* Fix AM_CONFIG_HEADER in configure for autoconf-2.69-1.
+* Fix AO_pause sleep delay for particular argument values (Win32).
+* Fix ARMv7 LDREXD/STREXD double-wide operand specification (GCC/Clang).
+* Fix LDREXD/STREXD use for pre-Clang3.3/arm.
+* Fix README regarding _acquire_read barrier.
+* Fix XSIZE_load/store definition order in generalize-small template.
+* Fix asm constraint of CAS memory operand for gcc/alpha, clang-3.1/mips.
+* Fix asm constraints of primitives in sunc/x86.h.
+* Fix cmpxchg16b-based compare_double_and_swap_double for SunCC/x86_64.
+* Fix compare_double_and_swap_double and double_ptr_storage for gcc/x32.
+* Fix compare_double_and_swap_double for clang3.0/x86 in PIC mode.
+* Fix compare_double_and_swap_double_full definition condition in emul_cas.
+* Fix generalize-small template adding missed CAS-based fetch_and_add.
+* Fix generalized fetch_and_add function.
+* Fix missing compiler barrier in nop_full for uniprocessor ARM.
+* Fix ordered_except_wr header inclusion for s390.
+* Fix return type of AO_int_X primitives defined in ao_t_is_int header.
+* Fix return type of char/short/int_load_read() in read_ordered.h.
+* Fix template-based headers regeneration order in src/Makefile.
+* Fix typos in ao_t_is_int, atomic_ops.h, generalize.h, msftc/arm.h comments.
+* Fix variable type to match printf format specifier in test_stack.
+* Fix visibility and initial value of 'dummy' variable in atomic_ops_stack.
+* Terminate tests with abort after error reported.
+
+
+== [7.2d] 2012-08-09 ==
+
+* Fix AO_compare_double_and_swap_double_full for gcc-4.2.1/x86 in PIC mode.
+* Fix AO_compiler_barrier missing parentheses.
+* Fix missing 'unsigned' for generalized AO_char/short_fetch_and_add result.
+
+
+== [7.2] 2012-05-11 ==
+
+* Add atomic_ops.pc.in and atomic_ops-uninstalled.pc.in to pkgconfig folder.
+* Define and use AO_PTRDIFF_T in tests for casts between pointer and integer.
+* Fix AO_compare_and_swap return type for s390 and PowerPC.
+* Fix AO_compare_double_and_swap_double_full for gcc/x86 (PIC mode).
+* Fix AO_stack_push_release to workaround bug in clang-1.1/x86 compiler.
+* Fix AO_test_and_setXX in tests/list_atomic.template.
+* Fix AO_test_and_set_full (gcc/x86[_64].h) to work-around a bug in LLVM v2.7.
+* Fix AO_test_and_set_full on m68k.
+* Fix __ARM_ARCH_5__ macro handling for Android NDK (ARMv7).
+* Fix configure for Cygwin, mingw-w64/32.
+* Fix configure to define __PIC__ macro explicitly if needed (GCC).
+* Fix double_ptr_storage definition for GCC pre-v4 (x86_64).
+* Fix for x32 by removing 'q' suffix in x86-64 instructions.
+* Fix generalization for IA-64 (regarding AO_or, AO_..._read/write primitives)
+* Fix generalized AO_<type>_fetch_and_add() return type.
+* Fix test_atomic_include for the case of missing CAS primitive.
+* Fix test_malloc - allocate less memory in case of missing mmap.
+* Implement the basic atomic primitives for the hexagon CPU.
+
+
+== [7.2alpha6] 2011-06-14 ==
+
+* Add missing AO_HAVE_ macros.
+* Add support of avr32 CPU.
+* Better support of various models of ARM.
+* Disable AO_compare_double_and_swap_double_full for SunCC x86 as not working.
+* Enable ARM Thumb-2 mode.
+* Fix AO_test_and_set_full for SunCC (x86).
+* Fix bugs in tests.
+* Fix clobbers in AO_compare_and_swap_full (x86.h).
+* Fix typos in identifiers and comments.
+* Improve AO_sync for PowerPC.
+* Improve make scripts (configure.ac).
+* Make get_mmaped() in atomic_ops_malloc.c more portable.
+* Support Intel compiler.
+* Support NaCl target.
+* Suppress compiler warnings in various places.
+* Test more predefined macros (ARM, PowerPC).
+* Use assembly code only for MS VC if available (x86_64).
+* Use built-in __sync_bool_compare_and_swap if available (x86_64).
+* Workaround bugs in LLVM GCC and SunCC regarding XCHG (x86, x86_64).
+
+
+== [7.2alpha4] 2009-12-02 ==
+
+* Fix typos in comments, identifiers and documentation.
+* Implement AO_compare_and_swap_full for SPARC.
+* Refine ARM-specific code.
+* Refine code and comments for MS VC.
+* Regenerate make scripts.
+* Share common code for all 32-bit CPUs (MS VC).
+* Support DigitalMars and Watcom compilers.
+* Support MS VC for ARM (WinCE).
+* Support SH CPU.
+* Support win32-pthreads.
+* Support x86 and x86_64 for SunCC compiler.
+
+
+== [7.2alpha2] 2009-05-27 ==
+
+* Add MIPS support.
+* Add better support for m68k.
+* Add "const" to first parameter of load calls.
+* Add parentheses around address argument for various macros.
+* Add some platform-specific documentation to INSTALL.
+* Add untested 64-bit support for PowerPC.
+* Fix AO_compare_and_swap_double_acquire.
+* Fix AO_int_fetch_and_add_full (x86_64).
+* Fix comments.
+* Fix s390 include paths.
+* Fix use of lwz instruction (PowerPC).
+* Refine clobbers (PowerPC).
+* Remove outdated info about Windows support in README.
+* Replace K&R-style function definition with ANSI C one.
+* add AO_compare_double_and_swap_double for ARMv6.
+* gcc/powerpc.h: Consider __NO_LWSYNC__.
+
+
+== [7.1] 2008-02-11 ==
+
+* Add test_and_set, AO_double_compare_and_swap generalizations.
+* Conditionally add compare_double_and_swap_double (x86).
+* Conditionally add compare_double_and_swap_double (x86).
+* Fix AO_compare_double_and_swap_double_full (x86) for PIC mode.
+* Fix AO_load_acquire for PowerPC.
+* Fix double-width CAS (x86).
+* Refine README (add more warnings about data dependencies).
+* Refine double_ptr_storage type definition.
+* Support ARMv6+ in GCC.
+* Support ArmCC compiler.
+* Use _InterlockedExchangeAdd for MS VC (x86).
+
+
+== [7.0] 2007-06-28 ==
+
+* Add 64-bit version of AO_load_acquire for PowerPC.
+* Add support of x86 and x86_64 for MS VC.
+* Do not assume that "mfence" is always present (x86.h).
+* Fix ARM AO_test_and_set_full.
+* Include windows.h (MS VC).
+* Update README to reflect C++0x effort.
+
+
+== [1.2] 2006-07-11 ==
+
+* Add prototypes to suppress compiler warnings.
+* Add simple VxWorks support.
+* Fix InterlockedCompareExchange proto usage.
+* Fix typos (ia64).
+* Include all_acquire_release_volatile.h and all_atomic_load_store.h (ia64).
+* Initial support for 64-bit targets.
+* Use "=q" for AO_test_and_set_full (x86).
+* Use inline assembler to generate "mfence" and byte sized XCHG.
+* Use new intrinsics available in MSVC 2003 and MSVC 2005.
+
+
+== [1.1] 2005-09-27 ==
+
+* Add and use read_ordered.h.
+* Change function naming from "byte" to "char".
+* Fix AO_test_and_set for ARM; define AO_CAN_EMUL_CAS.
+
+
+== [1.0] 2005-03-21 ==
+
+* Fix various bugs.
+* Add atomic_ops primitives for different sized data.
+* Add compare_double_and_swap_double and compare_and_swap_double.
+* Add initial support for atomic_ops for VC++/Windows/X86 and HP/UX.
+* Add minimal support for the Sun SPARC compiler.
+* Add support for platforms that require out-of-line assembly code.
+* Add support of int-wide operations on platforms with int-sized pointers.
+* Added libatomic_ops_gpl library with support for lock-free stack and malloc.
+* Attempt to support PowerPC.
+* Change atomic_ops include file structure.
+* Change most platforms to use byte-wide test-and-set locations.
+* Install under "atomic_ops" instead of "ao".
+* Remove compiler_barrier workaround for gcc 3.4+.
+* Renamed various types to end in _t.
+* Use autoconf, automake.

+ 13 - 0
blitz.mod/bdwgc/libatomic_ops/Makefile.am

@@ -0,0 +1,13 @@
+SUBDIRS = src doc tests
+
+ACLOCAL_AMFLAGS = -I m4
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = pkgconfig/atomic_ops.pc
+noinst_DATA = pkgconfig/atomic_ops-uninstalled.pc
+
+dist_pkgdata_DATA = COPYING README.md
+
+EXTRA_DIST = autogen.sh
+
+#distclean-local:

+ 63 - 0
blitz.mod/bdwgc/libatomic_ops/README.md

@@ -0,0 +1,63 @@
+# The atomic_ops library (libatomic_ops)
+
+This is version 7.4.0 of libatomic_ops.
+
+You might find a more recent version
+[here](http://www.hpl.hp.com/personal/Hans_Boehm/gc), or
+[here](http://www.hpl.hp.com/research/linux/atomic_ops/).
+
+
+## Overview
+
+This package provides semi-portable access to hardware-provided
+atomic memory update operations on a number architectures.  These might
+allow you to write code:
+
+* That does more interesting things in signal handlers.
+
+* Makes more effective use of multiprocessors by allowing you to write
+  clever lock-free code.  Note that such code is very difficult to get
+  right, and will unavoidably be less portable than lock-based code.  It
+  is also not always faster than lock-based code.  But it may occasionally
+  be a large performance win.
+
+* To experiment with new and much better thread programming paradigms, etc.
+
+For details and licensing restrictions see the files in the "doc"
+subdirectory.
+
+Please address bug reports [here](mailto:[email protected]).
+
+
+## Installation and Usage
+
+The configuration and build scripts for this package were generated by
+Automake/Autoconf.  "./configure; make; sudo make install" in this
+directory should work.  For a more customized build, see the output of
+"./configure --help".
+
+Note that much of the content of this library is in the header files.
+However, two small libraries are built and installed:
+
+* libatomic_ops.a is a support library, which is not needed on some platforms.
+  This is intended to be usable, under some mild restrictions, in free or
+  proprietary code, as are all the header files.  See doc/LICENSING.txt.
+
+* libatomic_ops_gpl.a contains some higher level facilities.  This code is
+  currently covered by the GPL.  The contents currently correspond to
+  the headers atomic_ops_stack.h and atomic_ops_malloc.h.
+
+
+## Platform Specific Notes
+
+Win32/64: src/Makefile.msft contains a very simple Makefile for building
+and running tests and building the gpl library.  The core atomic_ops
+implementation is entirely in header files.
+
+HP-UX/PA-RISC: aCC -Ae won't work as a C compiler, since it doesn't support
+inline assembly code.  Use cc.
+
+
+## Copyright & Warranty
+
+See doc/LICENSING.txt file.

+ 13 - 0
blitz.mod/bdwgc/libatomic_ops/TODO

@@ -0,0 +1,13 @@
+== TODO tasks ==
+
+Add C++0x ATM (atomic memory operations) layer.
+
+
+== FIXME tasks ==
+
+RHELinux6/POWER7 (gcc-4.4.7-3/ppc64), Fedora16/POWER7 (gcc-4.6.2-1/ppc64),
+Debian/powerpc (gcc 4.6.3-7):
+test_stack failed (Debian Bug #680100).
+
+Debian/m68k (Linux 3.2.0-2-atari):
+test_stack failed (Bus error), regression (Debian Bug #680066).

+ 10 - 0
blitz.mod/bdwgc/libatomic_ops/autogen.sh

@@ -0,0 +1,10 @@
+#!/bin/sh
+set -e
+
+# This script creates (or regenerates) configure (as well as aclocal.m4,
+# config.h.in, Makefile.in, etc.) missing in the source repository.
+
+autoreconf -i
+
+echo
+echo "Ready to run './configure'."

+ 191 - 0
blitz.mod/bdwgc/libatomic_ops/configure.ac

@@ -0,0 +1,191 @@
+# Process this file with autoconf to produce a configure script.
+AC_INIT([libatomic_ops],[7.4.0],[email protected])
+
+AC_PREREQ(2.61)
+AC_CANONICAL_TARGET([])
+AC_CONFIG_SRCDIR(src/atomic_ops.c)
+AC_CONFIG_MACRO_DIR([m4])
+AM_INIT_AUTOMAKE([foreign dist-bzip2 nostdinc])
+AM_MAINTAINER_MODE
+
+AC_CONFIG_HEADERS([src/config.h])
+
+# Checks for programs.
+AM_PROG_CC_C_O
+AM_PROG_AS
+LT_INIT([disable-shared])
+
+# Checks for functions.
+AC_FUNC_MMAP
+
+# Determine PIC flag.
+need_asm=false
+PICFLAG=
+AC_MSG_CHECKING(for PIC compiler flag)
+if test "$GCC" = yes; then
+  case "$host" in
+    *-*-cygwin* | *-*-mingw*)
+      # Cygwin and Mingw[-w32/64] do not need -fPIC.
+      AC_MSG_RESULT("<none>")
+      ;;
+    *)
+      AC_MSG_RESULT(-fPIC)
+      PICFLAG=-fPIC
+      AC_MSG_CHECKING(whether gcc -fPIC causes __PIC__ definition)
+      # Workaround: at least GCC 3.4.6 (Solaris) does not define this macro.
+      old_CFLAGS="$CFLAGS"
+      CFLAGS="$PICFLAG $CFLAGS"
+      AC_TRY_COMPILE([],[
+ #ifndef __PIC__
+ # error
+ #endif
+      ], [ac_cv_pic_macro=yes], [ac_cv_pic_macro=no])
+      CFLAGS="$old_CFLAGS"
+      AC_MSG_RESULT($ac_cv_pic_macro)
+      AS_IF([test "$ac_cv_pic_macro" = yes], [],
+            [PICFLAG="-D__PIC__=1 $PICFLAG"])
+      ;;
+  esac
+
+  # Output all warnings.
+  AC_MSG_CHECKING(for gcc -Wextra)
+  old_CFLAGS="$CFLAGS"
+  CFLAGS="-Wextra $CFLAGS"
+  AC_TRY_COMPILE([],[], [ac_cv_cc_wextra=yes], [ac_cv_cc_wextra=no])
+  CFLAGS="$old_CFLAGS"
+  AC_MSG_RESULT($ac_cv_cc_wextra)
+  AS_IF([test "$ac_cv_cc_wextra" = yes], [WEXTRA="-Wextra"], [WEXTRA="-W"])
+  CFLAGS="-Wall $WEXTRA $CFLAGS"
+else
+  case "$host" in
+    *-*-hpux*)
+      AC_MSG_RESULT("+Z")
+      PICFLAG="+Z"
+      CFLAGS="+O2 -mt $CFLAGS"
+      ;;
+    *-*-solaris*)
+      AC_MSG_RESULT(-Kpic)
+      PICFLAG=-Kpic
+      CFLAGS="-O $CFLAGS"
+      need_asm=true
+      ;;
+    *-*-linux*)
+      AC_MSG_RESULT(-fPIC)
+      PICFLAG=-fPIC
+      # Any Linux compiler had better be gcc compatible.
+      ;;
+    *)
+      AC_MSG_RESULT("<none>")
+      ;;
+  esac
+fi
+
+AC_ARG_ENABLE(assertions,
+        [AC_HELP_STRING([--enable-assertions], [Assertion checking])])
+if test "$enable_assertions" != yes; then
+  AC_DEFINE([NDEBUG], 1, [Define to disable assertion checking.])
+fi
+
+AC_SUBST(PICFLAG)
+AC_SUBST(DEFS)
+
+AH_TEMPLATE([_PTHREADS], [Indicates the use of pthreads (NetBSD).])
+
+AH_TEMPLATE([AO_USE_NANOSLEEP],
+        [Use nanosleep() instead of select() (only if atomic operations \
+         are emulated)])
+AH_TEMPLATE([AO_USE_NO_SIGNALS],
+        [Do not block signals in compare_and_swap (only if atomic operations \
+         are emulated)])
+AH_TEMPLATE([AO_USE_WIN32_PTHREADS],
+        [Use Win32 Sleep() instead of select() (only if atomic operations \
+         are emulated)])
+AH_TEMPLATE([AO_TRACE_MALLOC], [Trace AO_malloc/free calls (for debug only)])
+
+# These macros are tested in public headers
+AH_TEMPLATE([AO_GENERALIZE_ASM_BOOL_CAS],
+        [Force compare_and_swap definition via fetch_compare_and_swap])
+AH_TEMPLATE([AO_PREFER_GENERALIZED],
+        [Prefer generalized definitions to direct assembly-based ones])
+AH_TEMPLATE([AO_USE_PTHREAD_DEFS],
+        [Emulate atomic operations via slow and async-signal-unsafe \
+         pthread locking])
+AH_TEMPLATE([AO_ASM_X64_AVAILABLE],
+        [Inline assembly avalable (only VC/x86_64)])
+AH_TEMPLATE([AO_ASSUME_VISTA],
+        [Assume Windows Server 2003, Vista or later target (only VC/x86)])
+AH_TEMPLATE([AO_ASSUME_WINDOWS98],
+        [Assume hardware compare-and-swap functionality available \
+         on target (only VC/x86)])
+AH_TEMPLATE([AO_CMPXCHG16B_AVAILABLE],
+        [Assume target is not old AMD Opteron chip (only x86_64)])
+AH_TEMPLATE([AO_FORCE_USE_SWP],
+        [Force test_and_set to use SWP instruction instead of LDREX/STREX \
+         (only arm v6+)])
+AH_TEMPLATE([AO_NO_SPARC_V9], [Assume target is not sparc v9+ (only sparc)])
+AH_TEMPLATE([AO_OLD_STYLE_INTERLOCKED_COMPARE_EXCHANGE],
+        [Assume ancient MS VS Win32 headers (only VC/arm v6+, VC/x86)])
+AH_TEMPLATE([AO_UNIPROCESSOR], [Assume single-core target (only arm v6+)])
+AH_TEMPLATE([AO_USE_INTERLOCKED_INTRINSICS],
+        [Assume Win32 _Interlocked primitives available as intrinsics \
+         (only VC/arm)])
+AH_TEMPLATE([AO_USE_PENTIUM4_INSTRS],
+        [Use Pentium 4 'mfence' instruction (only x86)])
+AH_TEMPLATE([AO_USE_SYNC_CAS_BUILTIN],
+        [Prefer GCC built-in CAS intrinsics in favor of inline assembly \
+         (only gcc/x86, gcc/x86_64)])
+AH_TEMPLATE([AO_WEAK_DOUBLE_CAS_EMULATION],
+        [Emulate double-width CAS via pthread locking in case of no hardware \
+         support (only gcc/x86_64, the emulation is unsafe)])
+
+AC_DEFINE(_REENTRANT, 1, [Required define if using POSIX threads.])
+
+# Libraries needed to support threads (if any).
+have_pthreads=false
+AC_CHECK_LIB(pthread, pthread_self, have_pthreads=true)
+if test x$have_pthreads = xtrue; then
+  THREADDLLIBS=-lpthread
+  case "$host" in
+    *-*-netbsd*)
+      # Indicates the use of pthreads.
+      AC_DEFINE(_PTHREADS)
+      ;;
+    *-*-openbsd* | *-*-kfreebsd*-gnu | *-*-dgux*)
+      THREADDLLIBS=-pthread
+      ;;
+    *-*-cygwin* | *-*-darwin*)
+      # Cygwin does not have a real libpthread, so Libtool cannot link
+      # against it.
+      THREADDLLIBS=
+      ;;
+    *-*-mingw*)
+      # Use Win32 threads for tests anyway.
+      THREADDLLIBS=
+      # Skip test_atomic_pthreads.
+      have_pthreads=false
+      ;;
+  esac
+else
+  AC_DEFINE([AO_NO_PTHREADS], 1, [No pthreads library available])
+  # Assume VxWorks or Win32.
+  THREADDLLIBS=
+fi
+AC_SUBST(THREADDLLIBS)
+
+AM_CONDITIONAL(HAVE_PTHREAD_H, test x$have_pthreads = xtrue)
+AM_CONDITIONAL(NEED_ASM, test x$need_asm = xtrue)
+
+AC_CONFIG_FILES([
+        Makefile
+        doc/Makefile
+        src/Makefile
+        tests/Makefile
+        pkgconfig/atomic_ops.pc
+        pkgconfig/atomic_ops-uninstalled.pc ])
+
+AC_CONFIG_COMMANDS([default],[[]],[[
+PICFLAG="${PICFLAG}"
+CC="${CC}"
+DEFS="${DEFS}"
+]])
+AC_OUTPUT

+ 63 - 0
blitz.mod/bdwgc/libatomic_ops/doc/LICENSING.txt

@@ -0,0 +1,63 @@
+Our intent is to make it easy to use libatomic_ops, in
+both free and proprietary software.  Hence most code that we expect to be
+linked into a client application is covered by an MIT-style license.
+
+A few library routines are covered by the GNU General Public License.
+These are put into a separate library, libatomic_ops_gpl.a .
+
+The low-level part of the library is mostly covered by the following
+license:
+
+----------------------------------------
+
+Copyright (c) ...
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+--------------------------------
+
+A few files in the sysdeps directory were inherited in part from the
+Boehm-Demers-Weiser conservative garbage collector, and are covered by
+its license, which is similar in spirit:
+
+--------------------------------
+
+Copyright (c) ...
+
+THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+
+Permission is hereby granted to use or copy this program
+for any purpose,  provided the above notices are retained on all copies.
+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.
+
+----------------------------------
+
+A few files are covered by the GNU General Public License.  (See file
+"COPYING".) This applies only to test code, sample applications,
+and the libatomic_ops_gpl portion of the library.
+Thus libatomic_ops_gpl should generally not be linked into proprietary code.
+(This distinction was motivated by patent considerations.)
+
+It is possible that the license of the GPL pieces may be changed for
+future versions to make them more consistent with the rest of the package.
+If you submit patches, and have strong preferences about licensing, please
+express them.

+ 4 - 0
blitz.mod/bdwgc/libatomic_ops/doc/Makefile.am

@@ -0,0 +1,4 @@
+# installed documentation
+#
+dist_pkgdata_DATA=LICENSING.txt README.txt README_stack.txt \
+    README_malloc.txt README_win32.txt

+ 246 - 0
blitz.mod/bdwgc/libatomic_ops/doc/README.txt

@@ -0,0 +1,246 @@
+Usage:
+
+0) If possible, do this on a multiprocessor, especially if you are planning
+on modifying or enhancing the package.  It will work on a uniprocessor,
+but the tests are much more likely to pass in the presence of serious problems.
+
+1) Type ./configure --prefix=<install dir>; make; make check
+in the directory containing unpacked source.  The usual GNU build machinery
+is used, except that only static, but position-independent, libraries
+are normally built.  On Windows, read README_win32.txt instead.
+
+2) Applications should include atomic_ops.h.  Nearly all operations
+are implemented by header files included from it.  It is sometimes
+necessary, and always recommended to also link against libatomic_ops.a.
+To use the almost non-blocking stack or malloc implementations,
+see the corresponding README files, and also link against libatomic_gpl.a
+before linking against libatomic_ops.a.
+
+OVERVIEW:
+Atomic_ops.h defines a large collection of operations, each one of which is
+a combination of an (optional) atomic memory operation, and a memory barrier.
+Also defines associated feature-test macros to determine whether a particular
+operation is available on the current target hardware (either directly or
+by synthesis).  This is an attempt to replace various existing files with
+similar goals, since they usually do not handle differences in memory
+barrier styles with sufficient generality.
+
+If this is included after defining AO_REQUIRE_CAS, then the package
+will make an attempt to emulate compare-and-swap in a way that (at least
+on Linux) should still be async-signal-safe.  As a result, most other
+atomic operations will then be defined using the compare-and-swap
+emulation.  This emulation is slow, since it needs to disable signals.
+And it needs to block in case of contention.  If you care about performance
+on a platform that can't directly provide compare-and-swap, there are
+probably better alternatives.  But this allows easy ports to some such
+platforms (e.g. PA_RISC).  The option is ignored if compare-and-swap
+can be implemented directly.
+
+If atomic_ops.h is included after defining AO_USE_PTHREAD_DEFS, then all
+atomic operations will be emulated with pthread locking.  This is NOT
+async-signal-safe.  And it is slow.  It is intended primarily for debugging
+of the atomic_ops package itself.
+
+Note that the implementation reflects our understanding of real processor
+behavior.  This occasionally diverges from the documented behavior.  (E.g.
+the documented X86 behavior seems to be weak enough that it is impractical
+to use.  Current real implementations appear to be much better behaved.)
+We of course are in no position to guarantee that future processors
+(even HPs) will continue to behave this way, though we hope they will.
+
+This is a work in progress.  Corrections/additions for other platforms are
+greatly appreciated.  It passes rudimentary tests on X86, Itanium, and
+Alpha.
+
+OPERATIONS:
+
+Most operations operate on values of type AO_t, which are unsigned integers
+whose size matches that of pointers on the given architecture.  Exceptions
+are:
+
+- AO_test_and_set operates on AO_TS_t, which is whatever size the hardware
+supports with good performance.  In some cases this is the length of a cache
+line.  In some cases it is a byte.  In many cases it is equivalent to AO_t.
+
+- A few operations are implemented on smaller or larger size integers.
+Such operations are indicated by the appropriate prefix:
+
+AO_char_... Operates on unsigned char values.
+AO_short_... Operates on unsigned short values.
+AO_int_... Operates on unsigned int values.
+
+(Currently a very limited selection of these is implemented.  We're
+working on it.)
+
+The defined operations are all of the form AO_[<size>_]<op><barrier>(<args>).
+
+The <op> component specifies an atomic memory operation.  It may be
+one of the following, where the corresponding argument and result types
+are also specified:
+
+void nop()
+        No atomic operation.  The barrier may still be useful.
+AO_t load(const volatile AO_t * addr)
+        Atomic load of *addr.
+void store(volatile AO_t * addr, AO_t new_val)
+        Atomically store new_val to *addr.
+AO_t fetch_and_add(volatile AO_t *addr, AO_t incr)
+        Atomically add incr to *addr, and return the original value of *addr.
+AO_t fetch_and_add1(volatile AO_t *addr)
+        Equivalent to AO_fetch_and_add(addr, 1).
+AO_t fetch_and_sub1(volatile AO_t *addr)
+        Equivalent to AO_fetch_and_add(addr, (AO_t)(-1)).
+void and(volatile AO_t *addr, AO_t value)
+        Atomically 'and' value into *addr.
+void or(volatile AO_t *addr, AO_t value)
+        Atomically 'or' value into *addr.
+void xor(volatile AO_t *addr, AO_t value)
+        Atomically 'xor' value into *addr.
+int compare_and_swap(volatile AO_t * addr, AO_t old_val, AO_t new_val)
+        Atomically compare *addr to old_val, and replace *addr by new_val
+        if the first comparison succeeds.  Returns nonzero if the comparison
+        succeeded and *addr was updated.
+AO_t fetch_compare_and_swap(volatile AO_t * addr, AO_t old_val, AO_t new_val)
+        Atomically compare *addr to old_val, and replace *addr by new_val
+        if the first comparison succeeds; returns the original value of *addr.
+AO_TS_VAL_t test_and_set(volatile AO_TS_t * addr)
+        Atomically read the binary value at *addr, and set it.  AO_TS_VAL_t
+        is an enumeration type which includes two values AO_TS_SET and
+        AO_TS_CLEAR.  An AO_TS_t location is capable of holding an
+        AO_TS_VAL_t, but may be much larger, as dictated by hardware
+        constraints.  Test_and_set logically sets the value to AO_TS_SET.
+        It may be reset to AO_TS_CLEAR with the AO_CLEAR(AO_TS_t *) macro.
+        AO_TS_t locations should be initialized to AO_TS_INITIALIZER.
+        The values of AO_TS_SET and AO_TS_CLEAR are hardware dependent.
+        (On PA-RISC, AO_TS_SET is zero!)
+
+Test_and_set is a more limited version of compare_and_swap.  Its only
+advantage is that it is more easily implementable on some hardware.  It
+should thus be used if only binary test-and-set functionality is needed.
+
+If available, we also provide compare_and_swap operations that operate
+on wider values.  Since standard data types for double width values
+may not be available, these explicitly take pairs of arguments for the
+new and/or old value.  Unfortunately, there are two common variants,
+neither of which can easily and efficiently emulate the other.
+The first performs a comparison against the entire value being replaced,
+where the second replaces a double-width replacement, but performs
+a single-width comparison:
+
+int compare_double_and_swap_double(volatile AO_double_t * addr,
+                                   AO_t old_val1, AO_t old_val2,
+                                   AO_t new_val1, AO_t new_val2);
+
+int compare_and_swap_double(volatile AO_double_t * addr,
+                            AO_t old_val1,
+                            AO_t new_val1, AO_t new_val2);
+
+where AO_double_t is a structure containing AO_val1 and AO_val2 fields,
+both of type AO_t.  For compare_and_swap_double, we compare against
+the val1 field.  AO_double_t exists only if AO_HAVE_double_t
+is defined.
+
+ORDERING CONSTRAINTS:
+
+Each operation name also includes a suffix that specifies the associated
+ordering semantics.  The ordering constraint limits reordering of this
+operation with respect to other atomic operations and ordinary memory
+references.  The current implementation assumes that all memory references
+are to ordinary cacheable memory; the ordering guarantee is with respect
+to other threads or processes, not I/O devices.  (Whether or not this
+distinction is important is platform-dependent.)
+
+Ordering suffixes are one of the following:
+
+<none>: No memory barrier.  A plain AO_nop() really does nothing.
+_release: Earlier operations must become visible to other threads
+          before the atomic operation.
+_acquire: Later operations must become visible after this operation.
+_read: Subsequent reads must become visible after reads included in
+       the atomic operation or preceding it.  Rarely useful for clients?
+_write: Earlier writes become visible before writes during or after
+        the atomic operation.  Rarely useful for clients?
+_full: Ordered with respect to both earlier and later memory ops.
+       AO_store_full or AO_nop_full are the normal ways to force a store
+       to be ordered with respect to a later load.
+_release_write: Ordered with respect to earlier writes.  This is
+                normally implemented as either a _write or _release
+                barrier.
+_acquire_read: Ordered with respect to later reads. This is
+                normally implemented as either a _read or _acquire barrier.
+_dd_acquire_read: Ordered with respect to later reads that are data
+               dependent on this one.  This is needed on
+               a pointer read, which is later dereferenced to read a
+               second value, with the expectation that the second
+               read is ordered after the first one.  On most architectures,
+               this is equivalent to no barrier.  (This is very
+               hard to define precisely.  It should probably be avoided.
+               A major problem is that optimizers tend to try to
+               eliminate dependencies from the generated code, since
+               dependencies force the hardware to execute the code
+               serially.)
+
+We assume that if a store is data-dependent on an a previous load, then
+the two are always implicitly ordered.
+
+It is possible to test whether AO_<op><barrier> is available on the
+current platform by checking whether AO_HAVE_<op>_<barrier> is defined
+as a macro.
+
+Note that we generally don't implement operations that are either
+meaningless (e.g. AO_nop_acquire, AO_nop_release) or which appear to
+have no clear use (e.g. AO_load_release, AO_store_acquire, AO_load_write,
+AO_store_read).  On some platforms (e.g. PA-RISC) many operations
+will remain undefined unless AO_REQUIRE_CAS is defined before including
+the package.
+
+When typed in the package build directory, the following command
+will print operations that are unimplemented on the platform:
+
+make test_atomic; ./test_atomic
+
+The following command generates a file "list_atomic.i" containing the
+macro expansions of all implemented operations on the platform:
+
+make list_atomic.i
+
+Future directions:
+
+It currently appears that something roughly analogous to this is very likely
+to become part of the C++0x standard.  That effort has pointed out a number
+of issues that we expect to address there.  Since some of the solutions
+really require compiler support, they may not be completely addressed here.
+
+Known issues include:
+
+We should be more precise in defining the semantics of the ordering
+constraints, and if and how we can guarantee sequential consistency.
+
+Dd_acquire_read is very hard or impossible to define in a way that cannot
+be invalidated by reasonably standard compiler transformations.
+
+There is probably no good reason to provide operations on standard
+integer types, since those may have the wrong alignment constraints.
+
+
+Example:
+
+If you want to initialize an object, and then "publish" a pointer to it
+in a global location p, such that other threads reading the new value of
+p are guaranteed to see an initialized object, it suffices to use
+AO_release_write(p, ...) to write the pointer to the object, and to
+retrieve it in other threads with AO_acquire_read(p).
+
+Platform notes:
+
+All X86: We quietly assume 486 or better.
+
+Microsoft compilers:
+Define AO_ASSUME_WINDOWS98 to get access to hardware compare-and-swap
+functionality.  This relies on the InterlockedCompareExchange() function
+which was apparently not supported in Windows95.  (There may be a better
+way to get access to this.)
+
+Gcc on x86:
+Define AO_USE_PENTIUM4_INSTRS to use the Pentium 4 mfence instruction.
+Currently this is appears to be of marginal benefit.

+ 57 - 0
blitz.mod/bdwgc/libatomic_ops/doc/README_malloc.txt

@@ -0,0 +1,57 @@
+The libatomic_ops_gpl includes a simple almost-lock-free malloc implementation.
+
+This is intended as a safe way to allocate memory from a signal handler,
+or to allocate memory in the context of a library that does not know what
+thread library it will be used with.  In either case locking is impossible.
+
+Note that the operations are only guaranteed to be 1-lock-free, i.e. a
+single blocked thread will not prevent progress, but multiple blocked
+threads may.  To safely use these operations in a signal handler,
+the handler should be non-reentrant, i.e. it should not be interruptable
+by another handler using these operations.  Furthermore use outside
+of signal handlers in a multithreaded application should be protected
+by a lock, so that at most one invocation may be interrupted by a signal.
+The header will define the macro "AO_MALLOC_IS_LOCK_FREE" on platforms
+on which malloc is completely lock-free, and hence these restrictions
+do not apply.
+
+In the presence of threads, but absence of contention, the time performance
+of this package should be as good, or slightly better than, most system
+malloc implementations.  Its space performance
+is theoretically optimal (to within a constant factor), but probably
+quite poor in practice.  In particular, no attempt is made to
+coalesce free small memory blocks.  Something like Doug Lea's malloc is
+likely to use significantly less memory for complex applications.
+
+Performance on platforms without an efficient compare-and-swap implementation
+will be poor.
+
+This package was not designed for processor-scalability in the face of
+high allocation rates.  If all threads happen to allocate different-sized
+objects, you might get lucky.  Otherwise expect contention and false-sharing
+problems.  If this is an issue, something like Maged Michael's algorithm
+(PLDI 2004) would be technically a far better choice.  If you are concerned
+only with scalability, and not signal-safety, you might also consider
+using Hoard instead.  We have seen a factor of 3 to 4 slowdown from the
+standard glibc malloc implementation with contention, even when the
+performance without contention was faster.  (To make the implementation
+more scalable, one would need to replicate at least the free list headers,
+so that concurrent access is possible without cache conflicts.)
+
+Unfortunately there is no portable async-signal-safe way to obtain large
+chunks of memory from the OS.  Based on reading of the source code,
+mmap-based allocation appears safe under Linux, and probably BSD variants.
+It is probably unsafe for operating systems built on Mach, such as
+Apple's Darwin.  Without use of mmap, the allocator is
+limited to a fixed size, statically preallocated heap (2MB by default),
+and will fail to allocate objects above a certain size (just under 64K
+by default).  Use of mmap to circumvent these limitations requires an
+explicit call.
+
+The entire interface to the AO_malloc package currently consists of:
+
+#include <atomic_ops_malloc.h> /* includes atomic_ops.h */
+
+void *AO_malloc(size_t sz);
+void AO_free(void *p);
+void AO_malloc_enable_mmap(void);

+ 78 - 0
blitz.mod/bdwgc/libatomic_ops/doc/README_stack.txt

@@ -0,0 +1,78 @@
+Note that the AO_stack implementation is licensed under the GPL,
+unlike the lower level routines.
+
+The header file atomic_ops_stack.h defines a linked stack abstraction.
+Stacks may be accessed by multiple concurrent threads.  The implementation
+is 1-lock-free, i.e. it will continue to make progress if at most one
+thread becomes inactive while operating on the data structure.
+
+(The implementation can be built to be N-lock-free for any given N.  But that
+seems to rarely be useful, especially since larger N involve some slowdown.)
+
+This makes it safe to access these data structures from non-reentrant
+signal handlers, provided at most one non-signal-handler thread is
+accessing the data structure at once.  This latter condition can be
+ensured by acquiring an ordinary lock around the non-handler accesses
+to the data structure.
+
+For details see:
+
+Hans-J. Boehm, "An Almost Non-Blocking Stack", PODC 2004,
+http://portal.acm.org/citation.cfm?doid=1011767.1011774, or
+http://www.hpl.hp.com/techreports/2004/HPL-2004-105.html
+(This is not exactly the implementation described there, since the
+interface was cleaned up in the interim.  But it should perform
+very similarly.)
+
+We use a fully lock-free implementation when the underlying hardware
+makes that less expensive, i.e. when we have a double-wide compare-and-swap
+operation available.  (The fully lock-free implementation uses an AO_t-
+sized version count, and assumes it does not wrap during the time any
+given operation is active.  This seems reasonably safe on 32-bit hardware,
+and very safe on 64-bit hardware.) If a fully lock-free implementation
+is used, the macro AO_STACK_IS_LOCK_FREE will be defined.
+
+The implementation is interesting only because it allows reuse of
+existing nodes.  This is necessary, for example, to implement a memory
+allocator.
+
+Since we want to leave the precise stack node type up to the client,
+we insist only that each stack node contains a link field of type AO_t.
+When a new node is pushed on the stack, the push operation expects to be
+passed the pointer to this link field, which will then be overwritten by
+this link field.  Similarly, the pop operation returns a pointer to the
+link field of the object that previously was on the top of the stack.
+
+The cleanest way to use these routines is probably to define the stack node
+type with an initial AO_t link field, so that the conversion between the
+link-field pointer and the stack element pointer is just a compile-time
+cast.  But other possibilities exist.  (This would be cleaner in C++ with
+templates.)
+
+A stack is represented by an AO_stack_t structure.  (This is normally
+2 or 3 times the size of a pointer.)  It may be statically initialized
+by setting it to AO_STACK_INITIALIZER, or dynamically initialized to
+an empty stack with AO_stack_init.  There are only three operations for
+accessing stacks:
+
+void AO_stack_init(AO_stack_t *list);
+void AO_stack_push_release(AO_stack_t *list, AO_t *new_element);
+AO_t * AO_stack_pop_acquire(volatile AO_stack_t *list);
+
+We require that the objects pushed as list elements remain addressable
+as long as any push or pop operation are in progress.  (It is OK for an object
+to be "pop"ped off a stack and "deallocated" with a concurrent "pop" on
+the same stack still in progress, but only if "deallocation" leaves the
+object addressable.  The second "pop" may still read the object, but
+the value it reads will not matter.)
+
+We require that the headers (AO_stack objects) remain allocated and
+valid as long as any operations on them are still in-flight.
+
+We also provide macros AO_REAL_HEAD_PTR that converts an AO_stack_t
+to a pointer to the link field in the next element, and AO_REAL_NEXT_PTR
+that converts a link field to a real, dereferencable, pointer to the link field
+in the next element.  This is intended only for debugging, or to traverse
+the list after modification has ceased.  There is otherwise no guarantee that
+walking a stack using this macro will produce any kind of consistent
+picture of the data structure.

+ 31 - 0
blitz.mod/bdwgc/libatomic_ops/doc/README_win32.txt

@@ -0,0 +1,31 @@
+Most of the atomic_ops functionality is available under Win32 with
+the Microsoft tools, but the build process currently is considerably more
+primitive than on Linux/Unix platforms.
+
+To build:
+
+1) Go to the src directory in the distribution.
+2) Make sure the Microsoft command-line tools (e.g. nmake) are available.
+3) Run "nmake -f Makefile.msft".  This should run some tests, which
+may print warnings about the types of the "Interlocked" functions.
+I haven't been able to make all versions of VC++ happy.  If you know
+how to, please send a patch.
+4) To compile applications, you will need to retain or copy the following
+pieces from the resulting src directory contents:
+        "atomic_ops.h" - Header file defining low-level primitives.  This
+                         includes files from:
+        "atomic_ops"- Subdirectory containing implementation header files.
+        "atomic_ops_stack.h" - Header file describing almost lock-free stack.
+        "atomic_ops_malloc.h" - Header file describing almost lock-free malloc.
+        "libatomic_ops_gpl.lib" - Library containing implementation of the
+                        above two (plus AO_pause() defined in atomic_ops.c).
+                        The atomic_ops.h implementation is entirely in the
+                        header files in Win32.
+
+Most clients of atomic_ops.h will need to define AO_ASSUME_WINDOWS98 before
+including it.  Compare_and_swap is otherwise not available.
+Defining AO_ASSUME_VISTA will make compare_double_and_swap_double available
+as well.
+
+Note that the library is covered by the GNU General Public License, while
+the top 2 of these pieces allow use in proprietary code.

+ 3 - 0
blitz.mod/bdwgc/libatomic_ops/m4/.gitignore

@@ -0,0 +1,3 @@
+# Place holder to keep this directory in the Git repository.
+*
+!.gitignore

+ 10 - 0
blitz.mod/bdwgc/libatomic_ops/pkgconfig/atomic_ops-uninstalled.pc.in

@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+top_builddir=@abs_top_builddir@
+top_srcdir=@abs_top_srcdir@
+
+Name: The atomic_ops library (uninstalled)
+Description: Atomic memory update operations
+Version: @PACKAGE_VERSION@
+Libs: ${top_builddir}/src/libatomic_ops.la
+Cflags: -I${top_builddir}/src -I${top_srcdir}/src

+ 10 - 0
blitz.mod/bdwgc/libatomic_ops/pkgconfig/atomic_ops.pc.in

@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: The atomic_ops library
+Description: Atomic memory update operations portable implementation
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -latomic_ops
+Cflags: -I${includedir}

+ 229 - 0
blitz.mod/bdwgc/libatomic_ops/src/Makefile.am

@@ -0,0 +1,229 @@
+AM_CFLAGS=@PICFLAG@
+AM_CPPFLAGS = -I$(top_builddir)/src -I$(top_srcdir)/src
+
+include_HEADERS = atomic_ops.h atomic_ops_stack.h atomic_ops_malloc.h
+lib_LTLIBRARIES = libatomic_ops.la libatomic_ops_gpl.la
+if NEED_ASM
+libatomic_ops_la_SOURCES = atomic_ops.c atomic_ops_sysdeps.S
+else
+libatomic_ops_la_SOURCES = atomic_ops.c
+endif
+libatomic_ops_la_LDFLAGS = -version-info 1:3:0 -no-undefined
+
+libatomic_ops_gpl_la_SOURCES = atomic_ops_stack.c atomic_ops_malloc.c
+libatomic_ops_gpl_la_LDFLAGS = -version-info 1:3:0 -no-undefined
+
+EXTRA_DIST = Makefile.msft atomic_ops/sysdeps/README \
+        atomic_ops/generalize-arithm.template \
+        atomic_ops/generalize-small.template \
+        atomic_ops/sysdeps/ao_t_is_int.template \
+        atomic_ops/sysdeps/gcc/generic-arithm.template \
+        atomic_ops/sysdeps/gcc/generic-small.template \
+        atomic_ops/sysdeps/loadstore/acquire_release_volatile.template \
+        atomic_ops/sysdeps/loadstore/atomic_load.template \
+        atomic_ops/sysdeps/loadstore/atomic_store.template \
+        atomic_ops/sysdeps/loadstore/ordered_loads_only.template \
+        atomic_ops/sysdeps/loadstore/ordered_stores_only.template \
+        atomic_ops/sysdeps/sunc/sparc.S
+
+BUILT_SOURCES = atomic_ops/generalize-arithm.h \
+        atomic_ops/generalize-small.h \
+        atomic_ops/sysdeps/ao_t_is_int.h \
+        atomic_ops/sysdeps/gcc/generic-arithm.h \
+        atomic_ops/sysdeps/gcc/generic-small.h \
+        atomic_ops/sysdeps/loadstore/acquire_release_volatile.h \
+        atomic_ops/sysdeps/loadstore/atomic_load.h \
+        atomic_ops/sysdeps/loadstore/atomic_store.h \
+        atomic_ops/sysdeps/loadstore/char_acquire_release_volatile.h \
+        atomic_ops/sysdeps/loadstore/char_atomic_load.h \
+        atomic_ops/sysdeps/loadstore/char_atomic_store.h \
+        atomic_ops/sysdeps/loadstore/int_acquire_release_volatile.h \
+        atomic_ops/sysdeps/loadstore/int_atomic_load.h \
+        atomic_ops/sysdeps/loadstore/int_atomic_store.h \
+        atomic_ops/sysdeps/loadstore/ordered_loads_only.h \
+        atomic_ops/sysdeps/loadstore/ordered_stores_only.h \
+        atomic_ops/sysdeps/loadstore/short_acquire_release_volatile.h \
+        atomic_ops/sysdeps/loadstore/short_atomic_load.h \
+        atomic_ops/sysdeps/loadstore/short_atomic_store.h
+
+#Private Headers
+privatedir=${includedir}/
+nobase_private_HEADERS = atomic_ops/ao_version.h \
+          atomic_ops/generalize.h \
+          $(BUILT_SOURCES) \
+        \
+          atomic_ops/sysdeps/all_acquire_release_volatile.h \
+          atomic_ops/sysdeps/all_aligned_atomic_load_store.h \
+          atomic_ops/sysdeps/all_atomic_load_store.h \
+          atomic_ops/sysdeps/all_atomic_only_load.h \
+          atomic_ops/sysdeps/emul_cas.h \
+          atomic_ops/sysdeps/generic_pthread.h \
+          atomic_ops/sysdeps/ordered.h \
+          atomic_ops/sysdeps/ordered_except_wr.h \
+          atomic_ops/sysdeps/read_ordered.h \
+          atomic_ops/sysdeps/standard_ao_double_t.h \
+          atomic_ops/sysdeps/test_and_set_t_is_ao_t.h \
+          atomic_ops/sysdeps/test_and_set_t_is_char.h \
+        \
+          atomic_ops/sysdeps/armcc/arm_v6.h \
+        \
+          atomic_ops/sysdeps/gcc/aarch64.h \
+          atomic_ops/sysdeps/gcc/alpha.h \
+          atomic_ops/sysdeps/gcc/arm.h \
+          atomic_ops/sysdeps/gcc/avr32.h \
+          atomic_ops/sysdeps/gcc/cris.h \
+          atomic_ops/sysdeps/gcc/generic.h \
+          atomic_ops/sysdeps/gcc/hexagon.h \
+          atomic_ops/sysdeps/gcc/hppa.h \
+          atomic_ops/sysdeps/gcc/ia64.h \
+          atomic_ops/sysdeps/gcc/m68k.h \
+          atomic_ops/sysdeps/gcc/mips.h \
+          atomic_ops/sysdeps/gcc/powerpc.h \
+          atomic_ops/sysdeps/gcc/s390.h \
+          atomic_ops/sysdeps/gcc/sh.h \
+          atomic_ops/sysdeps/gcc/sparc.h \
+          atomic_ops/sysdeps/gcc/x86.h \
+        \
+          atomic_ops/sysdeps/hpc/hppa.h \
+          atomic_ops/sysdeps/hpc/ia64.h \
+        \
+          atomic_ops/sysdeps/ibmc/powerpc.h \
+        \
+          atomic_ops/sysdeps/icc/ia64.h \
+        \
+          atomic_ops/sysdeps/loadstore/double_atomic_load_store.h \
+        \
+          atomic_ops/sysdeps/msftc/arm.h \
+          atomic_ops/sysdeps/msftc/common32_defs.h \
+          atomic_ops/sysdeps/msftc/x86.h \
+          atomic_ops/sysdeps/msftc/x86_64.h \
+        \
+          atomic_ops/sysdeps/sunc/sparc.h \
+          atomic_ops/sysdeps/sunc/x86.h
+
+atomic_ops/generalize-small.h: atomic_ops/generalize-small.template
+	sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? > $@
+	sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? >> $@
+	sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? >> $@
+	sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? >> $@
+	sed -e s:XSIZE:double:g -e s:XCTYPE:AO_double_t:g $? >> $@
+
+atomic_ops/generalize-arithm.h: atomic_ops/generalize-arithm.template
+	sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? > $@
+	sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? >> $@
+	sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? >> $@
+	sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? >> $@
+
+atomic_ops/sysdeps/ao_t_is_int.h: atomic_ops/sysdeps/ao_t_is_int.template
+	sed -e s:_XBAR::g $? > $@
+	sed -e s:XBAR:full:g $? >> $@
+	sed -e s:XBAR:acquire:g $? >> $@
+	sed -e s:XBAR:release:g $? >> $@
+	sed -e s:XBAR:write:g $? >> $@
+	sed -e s:XBAR:read:g $? >> $@
+
+atomic_ops/sysdeps/gcc/generic-arithm.h: \
+        atomic_ops/sysdeps/gcc/generic-arithm.template
+	sed -e s:_XBAR::g -e s:XGCCBAR:RELAXED:g \
+		-e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? > $@
+	sed -e s:_XBAR::g -e s:XGCCBAR:RELAXED:g \
+		-e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? >> $@
+	sed -e s:_XBAR::g -e s:XGCCBAR:RELAXED:g \
+		-e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? >> $@
+	sed -e s:_XBAR::g -e s:XGCCBAR:RELAXED:g \
+		-e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? >> $@
+	sed -e s:XBAR:acquire:g -e s:XGCCBAR:ACQUIRE:g \
+		-e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? >> $@
+	sed -e s:XBAR:acquire:g -e s:XGCCBAR:ACQUIRE:g \
+		-e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? >> $@
+	sed -e s:XBAR:acquire:g -e s:XGCCBAR:ACQUIRE:g \
+		-e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? >> $@
+	sed -e s:XBAR:acquire:g -e s:XGCCBAR:ACQUIRE:g \
+		-e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? >> $@
+	sed -e s:XBAR:release:g -e s:XGCCBAR:RELEASE:g \
+		-e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? >> $@
+	sed -e s:XBAR:release:g -e s:XGCCBAR:RELEASE:g \
+		-e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? >> $@
+	sed -e s:XBAR:release:g -e s:XGCCBAR:RELEASE:g \
+		-e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? >> $@
+	sed -e s:XBAR:release:g -e s:XGCCBAR:RELEASE:g \
+		-e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? >> $@
+	sed -e s:XBAR:full:g -e s:XGCCBAR:SEQ_CST:g \
+		-e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? >> $@
+	sed -e s:XBAR:full:g -e s:XGCCBAR:SEQ_CST:g \
+		-e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? >> $@
+	sed -e s:XBAR:full:g -e s:XGCCBAR:SEQ_CST:g \
+		-e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? >> $@
+	sed -e s:XBAR:full:g -e s:XGCCBAR:SEQ_CST:g \
+		-e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? >> $@
+
+atomic_ops/sysdeps/gcc/generic-small.h: \
+        atomic_ops/sysdeps/gcc/generic-small.template
+	sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? > $@
+	sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? >> $@
+	sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? >> $@
+	sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? >> $@
+
+atomic_ops/sysdeps/loadstore/ordered_loads_only.h: \
+        atomic_ops/sysdeps/loadstore/ordered_loads_only.template
+	sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? > $@
+	sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? >> $@
+	sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? >> $@
+	sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? >> $@
+	sed -e s:XSIZE:double:g -e s:XCTYPE:AO_double_t:g $? >> $@
+
+atomic_ops/sysdeps/loadstore/ordered_stores_only.h: \
+        atomic_ops/sysdeps/loadstore/ordered_stores_only.template
+	sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? > $@
+	sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? >> $@
+	sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? >> $@
+	sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? >> $@
+	sed -e s:XSIZE:double:g -e s:XCTYPE:AO_double_t:g $? >> $@
+
+atomic_ops/sysdeps/loadstore/acquire_release_volatile.h: \
+        atomic_ops/sysdeps/loadstore/acquire_release_volatile.template
+	sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? > $@
+
+atomic_ops/sysdeps/loadstore/char_acquire_release_volatile.h: \
+        atomic_ops/sysdeps/loadstore/acquire_release_volatile.template
+	sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? > $@
+
+atomic_ops/sysdeps/loadstore/int_acquire_release_volatile.h: \
+        atomic_ops/sysdeps/loadstore/acquire_release_volatile.template
+	sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? > $@
+
+atomic_ops/sysdeps/loadstore/short_acquire_release_volatile.h: \
+        atomic_ops/sysdeps/loadstore/acquire_release_volatile.template
+	sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? > $@
+
+atomic_ops/sysdeps/loadstore/atomic_load.h: \
+        atomic_ops/sysdeps/loadstore/atomic_load.template
+	sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? > $@
+
+atomic_ops/sysdeps/loadstore/char_atomic_load.h: \
+        atomic_ops/sysdeps/loadstore/atomic_load.template
+	sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? > $@
+
+atomic_ops/sysdeps/loadstore/int_atomic_load.h: \
+        atomic_ops/sysdeps/loadstore/atomic_load.template
+	sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? > $@
+
+atomic_ops/sysdeps/loadstore/short_atomic_load.h: \
+        atomic_ops/sysdeps/loadstore/atomic_load.template
+	sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? > $@
+
+atomic_ops/sysdeps/loadstore/atomic_store.h: \
+        atomic_ops/sysdeps/loadstore/atomic_store.template
+	sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? > $@
+
+atomic_ops/sysdeps/loadstore/char_atomic_store.h: \
+        atomic_ops/sysdeps/loadstore/atomic_store.template
+	sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? > $@
+
+atomic_ops/sysdeps/loadstore/int_atomic_store.h: \
+        atomic_ops/sysdeps/loadstore/atomic_store.template
+	sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? > $@
+
+atomic_ops/sysdeps/loadstore/short_atomic_store.h: \
+        atomic_ops/sysdeps/loadstore/atomic_store.template
+	sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? > $@

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä