1 From 9d610d0a42cfef7b6379b9f0ee86356fcda10e19 Mon Sep 17 00:00:00 2001
2 From: Leandro Lucarella <llucax@gmail.com>
3 Date: Wed, 28 Jul 2010 18:17:08 -0300
4 Subject: [PATCH 3/4] Add precise heap scanning support
6 This is based on a patch[1] written by Vincent Lang (AKA wm4), which was
7 based in a David Simcha idea. The GC part of the patch is not applied, and
8 only the GC interface is patched, keeping the GC fully conservative.
10 [1] http://d.puremagic.com/issues/attachment.cgi?id=701
13 tango/core/Memory.d | 36 ++++--
14 tango/core/rt/compiler/dmd/object_.d | 229 +++++++++++++++++++++++++++++-
15 tango/core/rt/compiler/dmd/rt/aaA.d | 4 +-
16 tango/core/rt/compiler/dmd/rt/adi.d | 4 +-
17 tango/core/rt/compiler/dmd/rt/lifetime.d | 85 +++++++++--
18 tango/core/rt/gc/basic/gc.d | 6 +-
19 7 files changed, 364 insertions(+), 35 deletions(-)
21 diff --git a/object.di b/object.di
22 index 2131c16..979ed34 100644
25 @@ -42,6 +42,29 @@ struct Interface
31 + size_t[] bits = [1, 1, 0];
34 + bool mustScanWordAt(size_t offset);
35 + bool isPointerAt(size_t offset);
36 + bool canUpdatePointers();
39 +struct PointerMapBuilder
41 + private size_t[] m_bits = null;
42 + private size_t m_size = 0;
44 + void size(size_t bytes);
45 + void mustScanWordAt(size_t offset);
46 + void inlineAt(size_t offset, PointerMap pm);
47 + PointerMap convertToPointerMap();
50 +//const PointerMap conservativeBitMask = PointerMap([1, 1, 0]);
53 }else version (DigitalMars)
55 @@ -76,6 +99,9 @@ class ClassInfo : Object
56 /// TypeInfo information about this class
59 + version (D_HavePointerMap) {
60 + PointerMap pointermap;
62 /// finds the classinfo of the class with the given name
63 static ClassInfo find(char[] classname);
64 /// creates an instance of this class (works only if there is a constructor without arguments)
65 @@ -107,6 +133,7 @@ class TypeInfo
67 /// flags, 1: has possible pointers into GC memory
69 + PointerMap pointermap();
70 /// offsets of the various elements
71 OffsetTypeInfo[] offTi();
73 @@ -131,6 +158,10 @@ class TypeInfo_Array : TypeInfo
75 /// typeinfo of the elements, might be null for basic arrays, it is safer to use next()
78 + //ensure derived array TypeInfos use correct method
79 + //if this declaration is forgotten, e.g. TypeInfo_Ai will have a wrong vtbl entry
80 + PointerMap pointermap();
83 class TypeInfo_StaticArray : TypeInfo
84 @@ -176,6 +207,10 @@ class TypeInfo_Struct : TypeInfo
85 char[] function() xtoString;
89 + version (D_HavePointerMap) {
90 + PointerMap m_pointermap;
94 class TypeInfo_Tuple : TypeInfo
95 diff --git a/tango/core/Memory.d b/tango/core/Memory.d
96 index 37da0e7..44dee1f 100644
97 --- a/tango/core/Memory.d
98 +++ b/tango/core/Memory.d
101 module tango.core.Memory;
106 extern (C) void gc_init();
107 @@ -23,9 +22,9 @@ private
108 extern (C) uint gc_setAttr( void* p, uint a );
109 extern (C) uint gc_clrAttr( void* p, uint a );
111 - extern (C) void* gc_malloc( size_t sz, uint ba = 0 );
112 - extern (C) void* gc_calloc( size_t sz, uint ba = 0 );
113 - extern (C) void* gc_realloc( void* p, size_t sz, uint ba = 0 );
114 + extern (C) void* gc_malloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init);
115 + extern (C) void* gc_calloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init);
116 + extern (C) void* gc_realloc( void* p, size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init);
117 extern (C) size_t gc_extend( void* p, size_t mx, size_t sz );
118 extern (C) size_t gc_reserve( size_t sz );
119 extern (C) void gc_free( void* p );
120 @@ -57,6 +56,13 @@ private
122 * This struct encapsulates all garbage collection functionality for the D
123 * programming language.
125 + * Documentation of runtime configuration:
127 + * The environment variable D_PRECISE_HEAP can be used to control the behavior
128 + * of the GC at runtime.
129 + * D_PRECISE_HEAP=1 enable precise scanning
130 + * D_PRECISE_HEAP=0 disable precise scanning (may save space because no bitmasks need to be stored)
134 @@ -196,6 +202,7 @@ struct GC
136 * sz = The desired allocation size in bytes.
137 * ba = A bitmask of the attributes to set on this block.
138 + * bitMask = The pointer offset information for precise heap scanning.
141 * A reference to the allocated memory or null if insufficient memory
142 @@ -204,9 +211,10 @@ struct GC
144 * OutOfMemoryException on allocation failure.
146 - static void* malloc( size_t sz, uint ba = 0 )
147 + static void* malloc( size_t sz, uint ba = 0,
148 + PointerMap bitMask = PointerMap.init )
150 - return gc_malloc( sz, ba );
151 + return gc_malloc( sz, ba, bitMask );
155 @@ -221,6 +229,7 @@ struct GC
157 * sz = The desired allocation size in bytes.
158 * ba = A bitmask of the attributes to set on this block.
159 + * bitMask = The pointer offset information for precise heap scanning.
162 * A reference to the allocated memory or null if insufficient memory
163 @@ -229,9 +238,10 @@ struct GC
165 * OutOfMemoryException on allocation failure.
167 - static void* calloc( size_t sz, uint ba = 0 )
168 + static void* calloc( size_t sz, uint ba = 0,
169 + PointerMap bitMask = PointerMap.init )
171 - return gc_calloc( sz, ba );
172 + return gc_calloc( sz, ba, bitMask );
176 @@ -253,12 +263,15 @@ struct GC
177 * reallocation is required. If ba is not zero and p references the head
178 * of a valid, known memory block then the bits in ba will replace those on
179 * the current memory block and will also be set on the new block if a
180 - * reallocation is required.
181 + * reallocation is required. Similarly, if bitMask is non-null, then
182 + * the bitmask for the current block will propagated to the new block.
183 + * Otherwise, the bitmask provided will be propagated to the old block.
186 * p = A pointer to the root of a valid memory block or to null.
187 * sz = The desired allocation size in bytes.
188 * ba = A bitmask of the attributes to set on this block.
189 + * bitMask = The pointer offset information for precise heap scanning.
192 * A reference to the allocated memory on success or null if sz is
193 @@ -267,9 +280,10 @@ struct GC
195 * OutOfMemoryException on allocation failure.
197 - static void* realloc( void* p, size_t sz, uint ba = 0 )
198 + static void* realloc( void* p, size_t sz, uint ba = 0,
199 + PointerMap bitMask = PointerMap.init )
201 - return gc_realloc( p, sz, ba );
202 + return gc_realloc( p, sz, ba, bitMask );
206 diff --git a/tango/core/rt/compiler/dmd/object_.d b/tango/core/rt/compiler/dmd/object_.d
207 index 6a32856..51bea3c 100644
208 --- a/tango/core/rt/compiler/dmd/object_.d
209 +++ b/tango/core/rt/compiler/dmd/object_.d
210 @@ -147,6 +147,140 @@ struct Interface
214 + * Pointer map for precise heap scanning.
216 + * PointerMap pm = typeid(T).pointermap;
217 + * pm.bits = [header] ~ scan_bits ~ pointer_bits
218 + * size_t header is the number of pointer sized units in T (T.sizeof/size_t.sizeof)
219 + * size_t[] scan_bits is the bitmap; each bit covers size_t bytes of T, meaning:
220 + * 0: guaranteed not to be a pointer, don't scan
221 + * 1: possibly a pointer, must scan
222 + * size_t[] pointer_bits is a second bitmap similar to scan_bits. If the
223 + * corrsponding bit in scan_bits is 0, the bit is 0; otherwise its meaning is:
224 + * 0: pointer can not be moved, because it's possibly an integer
225 + * 1: pointer can be moved, the corresponding word is always a pointer
226 + * Note that not the bit-arrays are concatenated, but the size_t arrays.
227 + * This implies all GC-aware pointers must be aligned on size_t boundaries.
228 + * The compiler won't set any bits for unaligned pointer fields.
229 + * The least significant bit of a size_t item is considered the first bit.
230 + * PointerMap.init is a conservative scanning mask equivelant to void*[]
234 + size_t[] bits = [1, 1, 0];
236 + private const size_t BITS = size_t.sizeof * 8;
238 + /// return size in bytes (aligned)
241 + return bits[0] * size_t.sizeof;
244 + private bool getbit(size_t offset, bool pointer_bit)
246 + assert(offset < size);
248 + if ((offset & (size_t.sizeof - 1)) != 0)
251 + size_t elem = offset / size_t.sizeof;
252 + size_t start = 1; //scan_bits offset
254 + start += (bits[0] + BITS - 1) / BITS; //pointer_bits offset
255 + return !!(bits[start + elem / BITS] & (1 << (elem % BITS)));
258 + /// return if the (aligned) field starting at byte offset is a pointer
259 + /// Warning: the GC may access the internal data structure directly instead
260 + /// of using this method to make scanning faster
261 + bool mustScanWordAt(size_t offset)
263 + return getbit(offset, false);
266 + /// return if the (aligned) field starting at byte offset is a moveable pointer
267 + /// "moveable pointer" means that the memory block referenced by the pointer can
268 + /// be moved by the GC (the pointer field will be updated with the new address)
269 + bool isPointerAt(size_t offset)
271 + return getbit(offset, true);
274 + /// return true if and only if there are integer fields overlapping with pointer
275 + /// fields in this type
276 + bool canUpdatePointers()
278 + auto len = (bits.length - 1) / 2;
279 + return bits[1 .. 1 + len] == bits[1 + len .. $];
284 +/// code for manually building PointerMaps
285 +/// separate struct from PointerMap because for some representations, it may be
286 +/// hard to handle arbitrary pointerAt() calls to update the internal data structure
287 +/// (think of pointer maps encoded as lists of runs etc.)
289 +struct PointerMapBuilder
291 + private size_t[] m_bits = null;
292 + private size_t m_size = 0;
294 + private const size_t BITS = size_t.sizeof * 8;
296 + /// set the underlying type's size in bytes
297 + void size(size_t bytes)
299 + size_t nelem = bytes / size_t.sizeof;
300 + m_bits.length = 1 + ((nelem + BITS - 1) / BITS) * 2;
306 + /// mark the pointer sized field at byte offset as pointer
307 + /// if the offset is unaligned, it does nothing
308 + void mustScanWordAt(size_t offset)
310 + assert(offset < m_size);
312 + if ((offset & (size_t.sizeof - 1)) != 0)
315 + size_t elem = offset / size_t.sizeof;
316 + m_bits[1 + elem / BITS] |= 1 << (elem % BITS);
319 + /// starting at the given byte offset, call pointerAt() for each pointer in pm
320 + void inlineAt(size_t offset, PointerMap pm)
322 + assert(offset + pm.size <= m_size);
324 + for (size_t n = 0; n < pm.size; n += size_t.sizeof)
326 + if (pm.mustScanWordAt(n))
327 + mustScanWordAt(offset + n);
331 + /// create a PointerMap instance
332 + /// accessing this PointerMapBuilder after calling this method is not allowed
333 + PointerMap convertToPointerMap() {
334 + //no un-moveable pointer stuff supported => imply all pointers are moveable
335 + size_t len = (m_bits[0] + BITS - 1) / BITS;
336 + assert(len == (m_bits.length - 1) / 2);
337 + m_bits[1 + len .. $] = m_bits[1 .. 1 + len];
339 + auto res = PointerMap(m_bits);
340 + *this = PointerMapBuilder.init; //invalidate this instance
345 +//static const PointerMap cPointerMapNoScan = PointerMap([1, 0, 0]);
348 * Runtime type information about a class. Can be retrieved for any class type
349 * or instance by using the .classinfo property.
350 * A pointer to this appears as the first entry in the class's vtbl[].
351 @@ -174,7 +308,11 @@ class ClassInfo : Object
352 static if (__VERSION__ >= 1045) {
357 + version (D_HavePointerMap) {
358 + PointerMap pointermap;
362 * Search all modules for ClassInfo corresponding to classname.
363 * Returns: null if not found
364 @@ -294,6 +432,35 @@ class TypeInfo
365 /// Get flags for type: 1 means GC should scan for pointers
366 uint flags() { return 0; }
368 + /// Get a pointer to PointerMap; used for GC scanning
369 + PointerMap pointermap() {
371 + return PointerMap.init;
373 + //return cPointerMapNoScan;
374 + //work around for dmd bug #4397 (triggers infinite recursion)
375 + static size_t[3] g_arr;
376 + static PointerMap pm;
385 + //return PointerMap for a single, moveable pointer
386 + //also just a workaround for dmd bug #4397; should be a const variable
387 + private PointerMap exactpointer() {
388 + static size_t[3] g_arr;
389 + static PointerMap pm;
397 /// Get type information on the contents of the type; null if not available
398 OffsetTypeInfo[] offTi() { return null; }
400 @@ -319,6 +486,7 @@ class TypeInfo_Typedef : TypeInfo
402 override TypeInfo next() { return base; }
403 override uint flags() { return base.flags(); }
404 + override PointerMap pointermap() { return base.pointermap(); }
405 override void[] init() { return m_init.length ? m_init : base.init(); }
408 @@ -377,6 +545,7 @@ class TypeInfo_Pointer : TypeInfo
410 override TypeInfo next() { return m_next; }
411 override uint flags() { return 1; }
412 + override PointerMap pointermap() { return exactpointer(); }
416 @@ -456,6 +625,21 @@ class TypeInfo_Array : TypeInfo
419 override uint flags() { return 1; }
421 + override PointerMap pointermap()
423 + //return static mask for arrays
426 + //work around for dmd bug #4397 (triggers infinite recursion)
427 + static size_t[3] g_arr;
428 + static PointerMap pm;
432 + pm.bits[2] = 0b10; //moveable
437 class TypeInfo_StaticArray : TypeInfo
438 @@ -540,6 +724,20 @@ class TypeInfo_StaticArray : TypeInfo
439 override TypeInfo next() { return value; }
440 override uint flags() { return value.flags(); }
442 + override PointerMap pointermap()
445 + //this is kind of a hack to make arrays of static arrays work
446 + //e.g. T[2][] (typeid(T[2]) would be this instance)
447 + //because the GC repeats GC bitmasks shorter than the allocation size,
448 + // this should work well
449 + //it's a hack because pointermap() is supposed to return a map that
450 + // covers the whole type (i.e. doesn't rely on repeat)
451 + //this also might prevent subtle bugs, when a static array is resized
452 + // as dynamic array, and the bitmask is reused (can that happen at all?)
453 + return value.pointermap();
459 @@ -606,6 +804,7 @@ class TypeInfo_AssociativeArray : TypeInfo
461 override TypeInfo next() { return value; }
462 override uint flags() { return 1; }
463 + override PointerMap pointermap() { return exactpointer(); }
467 @@ -677,6 +876,21 @@ class TypeInfo_Delegate : TypeInfo
469 override uint flags() { return 1; }
471 + override PointerMap pointermap()
473 + //return static mask for delegates
474 + // word 0: context pointer
475 + // word 1: function pointer (not scanned)
476 + //work around for dmd bug #4397 (triggers infinite recursion)
477 + static size_t[3] g_arr;
478 + static PointerMap pm;
482 + pm.bits[2] = 0b01; //moveable
489 @@ -734,6 +948,7 @@ class TypeInfo_Class : TypeInfo
492 override uint flags() { return 1; }
493 + override PointerMap pointermap() { return exactpointer(); }
495 override OffsetTypeInfo[] offTi()
497 @@ -803,6 +1018,7 @@ class TypeInfo_Interface : TypeInfo
500 override uint flags() { return 1; }
501 + override PointerMap pointermap() { return exactpointer(); }
505 @@ -898,6 +1114,12 @@ class TypeInfo_Struct : TypeInfo
506 char[] function() xtoString;
510 + version (D_HavePointerMap) {
511 + PointerMap m_pointermap;
513 + override PointerMap pointermap() { return m_pointermap; }
517 class TypeInfo_Tuple : TypeInfo
518 @@ -960,6 +1182,11 @@ class TypeInfo_Tuple : TypeInfo
523 + override PointerMap pointermap()
530 diff --git a/tango/core/rt/compiler/dmd/rt/aaA.d b/tango/core/rt/compiler/dmd/rt/aaA.d
531 index e043524..9c0e04c 100644
532 --- a/tango/core/rt/compiler/dmd/rt/aaA.d
533 +++ b/tango/core/rt/compiler/dmd/rt/aaA.d
534 @@ -46,8 +46,8 @@ private
535 ALL_BITS = 0b1111_1111
538 - extern (C) void* gc_malloc( size_t sz, uint ba = 0 );
539 - extern (C) void* gc_calloc( size_t sz, uint ba = 0 );
540 + extern (C) void* gc_malloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init);
541 + extern (C) void* gc_calloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init);
542 extern (C) void gc_free( void* p );
545 diff --git a/tango/core/rt/compiler/dmd/rt/adi.d b/tango/core/rt/compiler/dmd/rt/adi.d
546 index 0ed0ac9..d294f84 100644
547 --- a/tango/core/rt/compiler/dmd/rt/adi.d
548 +++ b/tango/core/rt/compiler/dmd/rt/adi.d
549 @@ -50,8 +50,8 @@ private
550 ALL_BITS = 0b1111_1111
553 - extern (C) void* gc_malloc( size_t sz, uint ba = 0 );
554 - extern (C) void* gc_calloc( size_t sz, uint ba = 0 );
555 + extern (C) void* gc_malloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init);
556 + extern (C) void* gc_calloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init);
557 extern (C) void gc_free( void* p );
560 diff --git a/tango/core/rt/compiler/dmd/rt/lifetime.d b/tango/core/rt/compiler/dmd/rt/lifetime.d
561 index fd89ce7..022e452 100644
562 --- a/tango/core/rt/compiler/dmd/rt/lifetime.d
563 +++ b/tango/core/rt/compiler/dmd/rt/lifetime.d
564 @@ -57,8 +57,8 @@ private
565 extern (C) uint gc_setAttr( void* p, uint a );
566 extern (C) uint gc_clrAttr( void* p, uint a );
568 - extern (C) void* gc_malloc( size_t sz, uint ba = 0 );
569 - extern (C) void* gc_calloc( size_t sz, uint ba = 0 );
570 + extern (C) void* gc_malloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init);
571 + extern (C) void* gc_calloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init);
572 extern (C) size_t gc_extend( void* p, size_t mx, size_t sz );
573 extern (C) void gc_free( void* p );
575 @@ -101,8 +101,13 @@ extern (C) Object _d_newclass(ClassInfo ci)
580 + version (D_HavePointerMap) {
581 + pm = ci.pointermap;
583 p = gc_malloc(ci.init.length,
584 - BlkAttr.FINALIZE | (ci.flags & 2 ? BlkAttr.NO_SCAN : 0));
585 + BlkAttr.FINALIZE | (ci.flags & 2 ? BlkAttr.NO_SCAN : 0),
587 debug(PRINTF) printf(" p = %p\n", p);
590 @@ -210,7 +215,11 @@ extern (C) ulong _d_newarrayT(TypeInfo ti, size_t length)
594 - p = gc_malloc(size + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
596 + version (D_HavePointerMap) {
597 + pm = ti.next.pointermap();
599 + p = gc_malloc(size + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
600 debug(PRINTF) printf(" p = %p\n", p);
602 result = cast(ulong)length + (cast(ulong)cast(uint)p << 32);
603 @@ -250,7 +259,11 @@ extern (C) ulong _d_newarrayiT(TypeInfo ti, size_t length)
607 - auto p = gc_malloc(size + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
609 + version (D_HavePointerMap) {
610 + pm = ti.next.pointermap();
612 + auto p = gc_malloc(size + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
613 debug(PRINTF) printf(" p = %p\n", p);
615 memset(p, *cast(ubyte*)q, size);
616 @@ -580,7 +593,11 @@ body
620 - newdata = cast(byte *)gc_malloc(newsize + 1, info.attr);
622 + version (D_HavePointerMap) {
623 + pm = ti.next.pointermap();
625 + newdata = cast(byte *)gc_malloc(newsize + 1, info.attr, pm);
626 newdata[0 .. size] = p.data[0 .. size];
629 @@ -589,7 +606,11 @@ body
633 - newdata = cast(byte *)gc_calloc(newsize + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
635 + version (D_HavePointerMap) {
636 + pm = ti.next.pointermap();
638 + newdata = cast(byte *)gc_calloc(newsize + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
642 @@ -680,7 +701,11 @@ body
646 - newdata = cast(byte *)gc_malloc(newsize + 1, info.attr);
648 + version (D_HavePointerMap) {
649 + pm = ti.next.pointermap();
651 + newdata = cast(byte *)gc_malloc(newsize + 1, info.attr, pm);
652 newdata[0 .. size] = p.data[0 .. size];
655 @@ -688,7 +713,11 @@ body
659 - newdata = cast(byte *)gc_malloc(newsize + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
661 + version (D_HavePointerMap) {
662 + pm = ti.next.pointermap();
664 + newdata = cast(byte *)gc_malloc(newsize + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
667 auto q = initializer.ptr; // pointer to initializer
668 @@ -746,7 +775,11 @@ extern (C) long _d_arrayappendT(TypeInfo ti, Array *px, byte[] y)
672 - newdata = cast(byte *)gc_malloc(newCapacity(newlength, sizeelem) + 1, info.attr);
674 + version (D_HavePointerMap) {
675 + pm = ti.next.pointermap();
677 + newdata = cast(byte *)gc_malloc(newCapacity(newlength, sizeelem) + 1, info.attr, pm);
678 memcpy(newdata, px.data, length * sizeelem);
681 @@ -844,10 +877,14 @@ extern (C) byte[] _d_arrayappendcT(TypeInfo ti, ref byte[] x, ...)
685 - debug(PRINTF) printf("_d_arrayappendcT(length = %d, newlength = %d, cap = %d)\n", length, newlength, info.size);
686 + debug(PRINTF) printf("_d_arrayappendcT(length = %d, newlength = %d, cap = %d, ti=%.*s)\n", length, newlength, info.size, ti.toString());
687 auto newcap = newCapacity(newlength, sizeelem);
688 assert(newcap >= newlength * sizeelem);
689 - newdata = cast(byte *)gc_malloc(newcap + 1, info.attr);
691 + version (D_HavePointerMap) {
692 + pm = ti.next.pointermap();
694 + newdata = cast(byte *)gc_malloc(newcap + 1, info.attr, pm);
695 memcpy(newdata, x.ptr, length * sizeelem);
696 (cast(void**)(&x))[1] = newdata;
698 @@ -1049,7 +1086,11 @@ body
702 - byte* p = cast(byte*)gc_malloc(len + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
704 + version (D_HavePointerMap) {
705 + pm = ti.next.pointermap();
707 + byte* p = cast(byte*)gc_malloc(len + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
708 memcpy(p, x.ptr, xlen);
709 memcpy(p + xlen, y.ptr, ylen);
711 @@ -1078,7 +1119,11 @@ extern (C) byte[] _d_arraycatnT(TypeInfo ti, uint n, ...)
715 - a = gc_malloc(length * size, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
717 + version (D_HavePointerMap) {
718 + pm = ti.next.pointermap();
720 + a = gc_malloc(length * size, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
721 p = cast(byte[]*)(&n + 1);
724 @@ -1112,7 +1157,11 @@ extern (C) void* _d_arrayliteralT(TypeInfo ti, size_t length, ...)
728 - result = gc_malloc(length * sizeelem, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
730 + version (D_HavePointerMap) {
731 + pm = ti.next.pointermap();
733 + result = gc_malloc(length * sizeelem, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
736 va_start!(size_t)(q, length);
737 @@ -1165,7 +1214,11 @@ body
739 auto sizeelem = ti.next.tsize(); // array element size
740 auto size = a.length * sizeelem;
741 - r.ptr = gc_malloc(size, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
743 + version (D_HavePointerMap) {
744 + pm = ti.next.pointermap();
746 + r.ptr = gc_malloc(size, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
748 memcpy(r.ptr, a.ptr, size);
750 diff --git a/tango/core/rt/gc/basic/gc.d b/tango/core/rt/gc/basic/gc.d
751 index bcc91b8..9ed4197 100644
752 --- a/tango/core/rt/gc/basic/gc.d
753 +++ b/tango/core/rt/gc/basic/gc.d
754 @@ -144,17 +144,17 @@ extern (C) uint gc_clrAttr( void* p, uint a )
755 return _gc.clrAttr( p, a );
758 -extern (C) void* gc_malloc( size_t sz, uint ba = 0 )
759 +extern (C) void* gc_malloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init)
761 return _gc.malloc( sz, ba );
764 -extern (C) void* gc_calloc( size_t sz, uint ba = 0 )
765 +extern (C) void* gc_calloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init)
767 return _gc.calloc( sz, ba );
770 -extern (C) void* gc_realloc( void* p, size_t sz, uint ba = 0 )
771 +extern (C) void* gc_realloc( void* p, size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init)
773 return _gc.realloc( p, sz, ba );