Index: object.di
===================================================================
--- object.di	(revision 5489)
+++ object.di	(working copy)
@@ -49,6 +49,13 @@
     ptrdiff_t   offset;
 }
 
+struct PointerMap
+{
+    size_t[] bits = [size_t.sizeof, 1];
+}
+
+const PointerMap conservativeBitMask = PointerMap([size_t.sizeof, 1]);
+
 version (GNU){
 }else version (DigitalMars)
 {
@@ -83,6 +90,9 @@
         /// TypeInfo information about this class
         TypeInfo typeinfo;
     }
+    version (D_HavePointerMap) {
+        PointerMap pointermap;
+    }
     /// finds the classinfo of the class with the given name
     static ClassInfo find(char[] classname);
     /// creates an instance of this class (works only if there is a constructor without arguments)
@@ -114,6 +124,7 @@
     void[]   init();
     /// flags, 1: has possible pointers into GC memory
     uint     flags();
+    PointerMap pointermap();
     /// offsets of the various elements
     OffsetTypeInfo[] offTi();
 }
@@ -138,6 +149,10 @@
 {
     /// typeinfo of the elements, might be null for basic arrays, it is safer to use next()
     TypeInfo value;
+
+    //ensure derived array TypeInfos use correct method
+    //if this declaration is forgotten, e.g. TypeInfo_Ai will have a wrong vtbl entry
+    PointerMap pointermap();
 }
 
 class TypeInfo_StaticArray : TypeInfo
@@ -183,6 +198,10 @@
     char[] function()   xtoString;
 
     uint m_flags;
+
+    version (D_HavePointerMap) {
+        PointerMap m_pointermap;
+    }
 }
 
 class TypeInfo_Tuple : TypeInfo
Index: tango/core/rt/gc/basic/gcx.d
===================================================================
--- tango/core/rt/gc/basic/gcx.d	(revision 5489)
+++ tango/core/rt/gc/basic/gcx.d	(working copy)
@@ -44,6 +44,8 @@
                                 // else growing the stack means adding to the stack pointer
 version = MULTI_THREADED;       // produce multithreaded version
 
+version = ENABLE_PRECISE;       // precise heap scanning (if bitmasks available)
+
 /***************************************************/
 
 private import rt.gc.basic.gcbits;
@@ -425,7 +427,7 @@
     /**
      *
      */
-    final void *malloc(size_t size, uint bits = 0)
+    final void *malloc(size_t size, uint bits, PointerMap bitMask)
     {
         if (!size)
         {
@@ -437,7 +439,7 @@
         // when allocating.
         synchronized (gcLock)
         {
-            return mallocNoSync(size, bits);
+            return mallocNoSync(size, bits, bitMask.bits.ptr);
         }
     }
 
@@ -445,19 +447,23 @@
     //
     //
     //
-    private void *mallocNoSync(size_t size, uint bits = 0)
+    private void *mallocNoSync(size_t size, uint bits, size_t* bitMask)
     {
         assert(size != 0);
 
         void *p = null;
         Bins bin;
 
-        //debug(PRINTF) printf("GC::malloc(size = %d, gcx = %p)\n", size, gcx);
+        debug(PRINTF) printf("GC::malloc(size = %d, gcx = %p, bitMask = %p)\n", size, gcx, bitMask);
         assert(gcx);
         //debug(PRINTF) printf("gcx.self = %x, pthread_self() = %x\n", gcx.self, pthread_self());
 
         size += SENTINEL_EXTRA;
 
+        bool hasBitMask = !(bits & BlkAttr.NO_SCAN);
+        size_t bitMaskSize = (size_t*).sizeof * hasBitMask;
+        size += bitMaskSize;
+
         // Compute size bin
         // Cache previous binsize lookup - Dave Fladebo.
         static size_t lastsize = -1;
@@ -471,6 +477,7 @@
             lastbin = bin;
         }
 
+        size_t capacity;  // To figure out where to store the bitmask.
         if (bin < B_PAGE)
         {
             int  state     = gcx.disabled ? 1 : 0;
@@ -481,9 +488,19 @@
                 switch (state)
                 {
                 case 0:
-                    gcx.fullcollectshell();
+                    auto freedpages = gcx.fullcollectshell();
                     collected = true;
-                    state = 1;
+                    if (freedpages < gcx.npools * ((POOLSIZE / PAGESIZE) / 8))
+                    {   /* Didn't free much, so try allocating more anyway.
+                         * Note: freedpages is not the amount of memory freed, it's the amount
+                         * of full pages freed. Perhaps this should instead be the amount of
+                         * memory freed.
+                         */
+                        gcx.newPool(1);
+                        state = 2;
+                    }
+                    else
+                        state = 1;
                     continue;
                 case 1:
                     gcx.newPool(1);
@@ -499,6 +516,7 @@
                 }
             }
             p = gcx.bucket[bin];
+            capacity = binSize(bin);
 
             // Return next item from free list
             gcx.bucket[bin] = (cast(List*)p).next;
@@ -512,7 +530,18 @@
             p = gcx.bigAlloc(size);
             if (!p)
                 onOutOfMemoryError();
+            size_t npages = (size + PAGESIZE - 1) / PAGESIZE;
+            capacity = npages * PAGESIZE;
         }
+
+        // Store the bit mask AFTER SENTINEL_POST.
+        if(hasBitMask) {
+            size_t** endOfBlock = cast(size_t**)
+                (p + capacity - bitMaskSize);
+            *endOfBlock = bitMask;
+            size -= bitMaskSize;
+        }
+
         size -= SENTINEL_EXTRA;
         p = sentinel_add(p);
         sentinel_init(p, size);
@@ -525,6 +554,7 @@
 
             gcx.setBits(pool, cast(size_t)(p - pool.baseAddr) / 16, bits);
         }
+        debug(PRINTF) printf("Done malloc.  Ptr:  %p  Bitmask:  %p\n", p, bitMask);
         return p;
     }
 
@@ -532,7 +562,7 @@
     /**
      *
      */
-    final void *calloc(size_t size, uint bits = 0)
+    final void *calloc(size_t size, uint bits, PointerMap bitMask)
     {
         if (!size)
         {
@@ -544,7 +574,7 @@
         // when allocating.
         synchronized (gcLock)
         {
-            return callocNoSync(size, bits);
+            return callocNoSync(size, bits, bitMask.bits.ptr);
         }
     }
 
@@ -552,12 +582,12 @@
     //
     //
     //
-    private void *callocNoSync(size_t size, uint bits = 0)
+    private void *callocNoSync(size_t size, uint bits, size_t* bitMask)
     {
         assert(size != 0);
 
         //debug(PRINTF) printf("calloc: %x len %d\n", p, len);
-        void *p = mallocNoSync(size, bits);
+        void *p = mallocNoSync(size, bits, bitMask);
         cstring.memset(p, 0, size);
         return p;
     }
@@ -566,151 +596,186 @@
     /**
      *
      */
-    final void *realloc(void *p, size_t size, uint bits = 0)
+    void *realloc(void *p, size_t size, uint bits, PointerMap bitMask)
     {
         // Since a finalizer could launch a new thread, we always need to lock
         // when collecting.  The safest way to do this is to simply always lock
         // when allocating.
         synchronized (gcLock)
         {
-            return reallocNoSync(p, size, bits);
+            return reallocNoSync(p, size, bits, bitMask.bits.ptr);
         }
     }
 
+    // Boilerplate code dealing with bitmasks, used in both realloc() and
+    // extend().
+    const char[] bitMaskMixin = `
+        // Finding the pool is expensive so do it once and ONLY
+        // once, then keep recycling.
+        Pool* pool = gcx.findPool(p);
+        size_t psize = gcx.findSize(p, pool);        // find allocated size
+        void* baseAddr = gcx.findBase(p, pool);
 
+        if(pool !is null) {
+            auto biti = cast(size_t)(p - pool.baseAddr) / 16;
+
+            if(bits) {
+                gcx.clrBits(pool, biti, BlkAttr.ALL_BITS);
+                gcx.setBits(pool, biti, bits);
+            } else {
+                bits = gcx.getBits(pool, biti);
+            }
+        }
+
+        bool hasBitMask = !(bits & BlkAttr.NO_SCAN);
+
+        void setBitMask() {
+            if(baseAddr is null && !hasBitMask) {
+                // Use the already-declared bit mask.
+                return;
+            }
+
+            // Don't get the bitmask from the block if the user specified
+            // a new one.
+            if(bitMask !is null) {
+                return;
+            }
+
+            size_t** bitMaskPtr = cast(size_t**)
+                (baseAddr + psize - (size_t*).sizeof);
+            bitMask = *bitMaskPtr;
+        }
+
+        setBitMask();
+        size_t bitMaskSize = (hasBitMask) ? (size_t*).sizeof : 0;
+    `;
+
+
     //
     //
     //
-    private void *reallocNoSync(void *p, size_t size, uint bits = 0)
+    private void *reallocNoSync(void *p, size_t size, uint bits,
+        size_t* bitMask)
     {
         if (!size)
         {   if (p)
             {   freeNoSync(p);
-                p = null;
+                return null;
             }
         }
         else if (!p)
         {
-            p = mallocNoSync(size, bits);
+            if(bitMask is null) {
+                // Default to conservative mask.
+                return mallocNoSync(size, bits, PointerMap.init.bits.ptr);
+            }
+            return mallocNoSync(size, bits, bitMask);
         }
-        else
-        {   void *p2;
-            size_t psize;
 
-            //debug(PRINTF) printf("GC::realloc(p = %x, size = %u)\n", p, size);
-            version (SENTINEL)
+        mixin(bitMaskMixin);
+        void *p2;
+
+        if(hasBitMask) {  // Add in stuff for bit mask.
+            size += (size_t*).sizeof;
+        }
+
+        debug(PRINTF) {
+            printf("GC::realloc(p = %x, size = %u)\n", p, size);
+            scope(exit) printf("Done realloc.");
+        }
+
+        version (SENTINEL)
+        {
+            sentinel_Invariant(p);
+            psize = *sentinel_size(p);
+            if (psize != size)
             {
-                sentinel_Invariant(p);
-                psize = *sentinel_size(p);
-                if (psize != size)
-                {
-                    if (psize)
-                    {
-                        Pool *pool = gcx.findPool(p);
+                p2 = mallocNoSync(size, bits, bitMask);
 
-                        if (pool)
-                        {
-                            auto biti = cast(size_t)(p - pool.baseAddr) / 16;
-
-                            if (bits)
-                            {
-                                gcx.clrBits(pool, biti, BlkAttr.ALL_BITS);
-                                gcx.setBits(pool, biti, bits);
-                            }
-                            else
-                            {
-                                bits = gcx.getBits(pool, biti);
-                            }
-                        }
-                    }
-                    p2 = mallocNoSync(size, bits);
-                    if (psize < size)
-                        size = psize;
-                    //debug(PRINTF) printf("\tcopying %d bytes\n",size);
-                    cstring.memcpy(p2, p, size);
-                    p = p2;
-                }
+                size -= bitMaskSize;
+                psize -= bitMaskSize;
+                if (psize < size)
+                    size = psize;
+                //debug(PRINTF) printf("\tcopying %d bytes\n",size);
+                memcpy(p2, p, size);
+                p = p2;
             }
-            else
+        }
+        else
+        {
+            if (psize >= PAGESIZE && size >= PAGESIZE)
             {
-                psize = gcx.findSize(p);        // find allocated size
-                if (psize >= PAGESIZE && size >= PAGESIZE)
-                {
-                    auto psz = psize / PAGESIZE;
-                    auto newsz = (size + PAGESIZE - 1) / PAGESIZE;
-                    if (newsz == psz)
-                        return p;
+                auto psz = psize / PAGESIZE;
+                auto newsz = (size + PAGESIZE - 1) / PAGESIZE;
+                if (newsz == psz)
+                    return p;
 
-                    auto pool = gcx.findPool(p);
-                    auto pagenum = (p - pool.baseAddr) / PAGESIZE;
+                auto pagenum = (p - pool.baseAddr) / PAGESIZE;
 
-                    if (newsz < psz)
-                    {   // Shrink in place
-                        synchronized (gcLock)
-                        {
-                            debug (MEMSTOMP) cstring.memset(p + size, 0xF2, psize - size);
-                            pool.freePages(pagenum + newsz, psz - newsz);
-                        }
-                        return p;
-                    }
-                    else if (pagenum + newsz <= pool.npages)
+                if (newsz < psz)
+                {   // Shrink in place
+                    synchronized (gcLock)
                     {
-                        // Attempt to expand in place
-                        synchronized (gcLock)
-                        {
-                            for (size_t i = pagenum + psz; 1;)
-                            {
-                                if (i == pagenum + newsz)
-                                {
-                                    debug (MEMSTOMP) cstring.memset(p + psize, 0xF0, size - psize);
-                                    cstring.memset(&pool.pagetable[pagenum + psz], B_PAGEPLUS, newsz - psz);
-                                    return p;
-                                }
-                                if (i == pool.ncommitted)
-                                {
-                                    auto u = pool.extendPages(pagenum + newsz - pool.ncommitted);
-                                    if (u == OPFAIL)
-                                        break;
-                                    i = pagenum + newsz;
-                                    continue;
-                                }
-                                if (pool.pagetable[i] != B_FREE)
-                                    break;
-                                i++;
-                            }
-                        }
+                        debug (MEMSTOMP) memset(p + size, 0xF2, psize - size);
+                        pool.freePages(pagenum + newsz, psz - newsz);
                     }
+
+                    if(hasBitMask) {
+                        size_t** newBitmaskPtr = cast(size_t**) (baseAddr +
+                            (PAGESIZE * newsz) - (size_t*).sizeof);
+                        *newBitmaskPtr = bitMask;
+                    }
+
+                    return p;
                 }
-                if (psize < size ||             // if new size is bigger
-                    psize > size * 2)           // or less than half
+                else if (pagenum + newsz <= pool.npages)
                 {
-                    if (psize)
+                    // Attempt to expand in place
+                    synchronized (gcLock)
                     {
-                        Pool *pool = gcx.findPool(p);
-
-                        if (pool)
+                        for (size_t i = pagenum + psz; 1;)
                         {
-                            auto biti = cast(size_t)(p - pool.baseAddr) / 16;
+                            if (i == pagenum + newsz)
+                            {
+                                debug (MEMSTOMP) memset(p + psize, 0xF0, size - psize);
+                                memset(&pool.pagetable[pagenum + psz], B_PAGEPLUS, newsz - psz);
 
-                            if (bits)
-                            {
-                                gcx.clrBits(pool, biti, BlkAttr.ALL_BITS);
-                                gcx.setBits(pool, biti, bits);
+                                if(hasBitMask) {
+                                    size_t** newBitmaskPtr = cast(size_t**) (baseAddr +
+                                        (PAGESIZE * newsz) - (size_t*).sizeof);
+                                    *newBitmaskPtr = bitMask;
+                                }
+
+                                return p;
                             }
-                            else
+                            if (i == pool.ncommitted)
                             {
-                                bits = gcx.getBits(pool, biti);
+                                auto u = pool.extendPages(pagenum + newsz - pool.ncommitted);
+                                if (u == OPFAIL)
+                                    break;
+                                i = pagenum + newsz;
+                                continue;
                             }
+                            if (pool.pagetable[i] != B_FREE)
+                                break;
+                            i++;
                         }
                     }
-                    p2 = mallocNoSync(size, bits);
-                    if (psize < size)
-                        size = psize;
-                    //debug(PRINTF) printf("\tcopying %d bytes\n",size);
-                    cstring.memcpy(p2, p, size);
-                    p = p2;
                 }
             }
+            if (psize < size ||             // if new size is bigger
+                psize > size * 2)           // or less than half
+            {
+                p2 = mallocNoSync(size, bits, bitMask);
+
+                psize -= bitMaskSize;
+                size -= bitMaskSize;
+                if (psize < size)
+                    size = psize;
+                //debug(PRINTF) printf("\tcopying %d bytes\n",size);
+                memcpy(p2, p, size);
+                p = p2;
+            }
         }
         return p;
     }
@@ -753,7 +818,16 @@
         {
             return 0;
         }
-        auto psize = gcx.findSize(p);   // find allocated size
+
+        uint bits;  // Used in the mixin.
+        size_t* bitMask;
+        mixin(bitMaskMixin);
+
+        if(hasBitMask) {
+            minsize += bitMaskSize;
+            maxsize += bitMaskSize;
+        }
+
         if (psize < PAGESIZE)
             return 0;                   // cannot extend buckets
 
@@ -761,7 +835,6 @@
         auto minsz = (minsize + PAGESIZE - 1) / PAGESIZE;
         auto maxsz = (maxsize + PAGESIZE - 1) / PAGESIZE;
 
-        auto pool = gcx.findPool(p);
         auto pagenum = (p - pool.baseAddr) / PAGESIZE;
 
         size_t sz;
@@ -794,7 +867,14 @@
             gcx.cached_size_val = (psz + sz) * PAGESIZE;
         if (p == gcx.cached_info_key)
             gcx.cached_info_val.size = (psz + sz) * PAGESIZE;
-        return (psz + sz) * PAGESIZE;
+
+        size_t newSize = (psz + sz) * PAGESIZE;
+        if(hasBitMask) {
+            newSize -= bitMaskSize;
+            size_t** bitMaskPtr = cast(size_t**) (baseAddr + newSize);
+            *bitMaskPtr = bitMask;
+        }
+        return newSize;
     }
 
 
@@ -964,31 +1044,42 @@
     private size_t sizeOfNoSync(void *p)
     {
         assert (p);
+        p = sentinel_sub(p);
 
+        Pool* pool = gcx.findPool(p);
+        uint bits;
+        if(pool !is null) {
+            auto biti = cast(size_t)(p - pool.baseAddr) / 16;
+            bits = gcx.getBits(pool, biti);
+        } else {
+            return 0;
+        }
+
+        size_t size = gcx.findSize(p, pool);
+        size_t bitMaskSize = (size_t*).sizeof * !(BlkAttr.NO_SCAN & bits);
+
         version (SENTINEL)
         {
-            p = sentinel_sub(p);
-            size_t size = gcx.findSize(p);
-
             // Check for interior pointer
             // This depends on:
             // 1) size is a power of 2 for less than PAGESIZE values
             // 2) base of memory pool is aligned on PAGESIZE boundary
-            if (cast(size_t)p & (size - 1) & (PAGESIZE - 1))
-                size = 0;
-            return size ? size - SENTINEL_EXTRA : 0;
+            if (cast(size_t)p & (size - 1) & (PAGESIZE - 1)) {
+                return 0;
+            }
+
+            return size - SENTINEL_EXTRA - bitMaskSize;
         }
         else
         {
-            size_t size = gcx.findSize(p);
-
             // Check for interior pointer
             // This depends on:
             // 1) size is a power of 2 for less than PAGESIZE values
             // 2) base of memory pool is aligned on PAGESIZE boundary
-            if (cast(size_t)p & (size - 1) & (PAGESIZE - 1))
+            if (cast(size_t)p & (size - 1) & (PAGESIZE - 1)) {
                 return 0;
-            return size;
+            }
+            return size - bitMaskSize;
         }
     }
 
@@ -1023,7 +1114,11 @@
     {
         assert(p);
 
-        return gcx.getInfo(p);
+        auto ret = gcx.getInfo(p);
+        if( !(ret.attr & BlkAttr.NO_SCAN)) {
+            ret.size -= (size_t*).sizeof;
+        }
+        return ret;
     }
 
 
@@ -1211,14 +1306,12 @@
     {
         debug(PRINTF) printf("GC.fullCollect()\n");
 
-        if (!thread_needLock())
+        // Since a finalizer could launch a new thread, we always need to lock
+        // when collecting.
+        synchronized (gcLock)
         {
             gcx.fullcollectshell();
         }
-        else synchronized (gcLock)
-        {
-            gcx.fullcollectshell();
-        }
 
         version (none)
         {
@@ -1444,7 +1537,14 @@
     B_MAX
 }
 
+/// Translates a bin number in the enum above into a bin size.
+size_t binSize(size_t binNum) {
+    assert(binNum <= B_2048);
+    size_t result = 16 << binNum;
+    return result;
+}
 
+
 alias ubyte Bins;
 
 
@@ -1717,20 +1817,26 @@
     {
         if (p >= minAddr && p < maxAddr)
         {
-            if (npools == 1)
+            if (npools <= 1)
             {
-                return pooltable[0];
+                return npools == 0 ? null : pooltable[0];
             }
 
-            for (size_t i = 0; i < npools; i++)
-            {   Pool *pool;
-
-                pool = pooltable[i];
-                if (p < pool.topAddr)
-                {   if (pool.baseAddr <= p)
-                        return pool;
-                    break;
-                }
+            /* The pooltable[] is sorted by address, so do a binary search
+             */
+            auto pt = pooltable;
+            int low = 0;
+            int high = npools - 1;
+            while (low <= high)
+            {
+                size_t mid = (low + high) >> 1;
+                auto pool = pt[mid];
+                if (p < pool.baseAddr)
+                high = mid - 1;
+                else if (p >= pool.topAddr)
+                low = mid + 1;
+                else
+                    return pool;
             }
         }
         return null;
@@ -1743,9 +1849,10 @@
      */
     void* findBase(void *p)
     {
-        Pool *pool;
+        return findBase(p, findPool(p));
+    }
 
-        pool = findPool(p);
+    void* findBase(void* p, Pool* pool) {
         if (pool)
         {
             size_t offset = cast(size_t)(p - pool.baseAddr);
@@ -1781,13 +1888,15 @@
      */
     size_t findSize(void *p)
     {
-        Pool*  pool;
+        if (USE_CACHE && p == cached_size_key) {
+            return cached_size_val;
+        } else {
+            return findSize(p, findPool(p));
+        }
+    }
+
+    size_t findSize(void *p, Pool* pool) {
         size_t size = 0;
-
-        if (USE_CACHE && p == cached_size_key)
-            return cached_size_val;
-            
-        pool = findPool(p);
         if (pool)
         {
             size_t pagenum;
@@ -1840,7 +1949,7 @@
 
             if (bin <= B_PAGE)
             {
-                info.base = pool.baseAddr + (offset & notbinsize[bin]);
+                info.base = cast(void*)((cast(size_t)p) & notbinsize[bin]);
             }
             else if (bin == B_PAGEPLUS)
             {
@@ -1878,7 +1987,10 @@
             ////////////////////////////////////////////////////////////////////
 
             info.attr = getBits(pool, cast(size_t)(offset / 16));
-            
+            if( !(info.attr & BlkAttr.NO_SCAN)) {
+                info.size -= (size_t*).sizeof;  // bitmask.
+            }
+
             cached_info_key = p;
             cached_info_val = info;
         }
@@ -2105,8 +2217,10 @@
         {   size_t n;
 
             n = npools;
-            if (n > 8)
-                n = 8;                  // cap pool size at 8 megs
+            if (n > 32)
+                n = 32;         // cap pool size at 32 megs
+            else if (n > 8)
+                n = 16;
             n *= (POOLSIZE / PAGESIZE);
             if (npages < n)
                 npages = n;
@@ -2188,32 +2302,56 @@
         return 1;
     }
 
+    /**Marks a range of memory using the conservative bit mask.  Used for
+     * the stack, for the data segment, and additional memory ranges.
+     */
+    void markConservative(void* p1, void* p2) {
+        mark(p1, p2, PointerMap.init.bits.ptr);
+    }
 
+
     /**
      * Search a range of memory values and mark any pointers into the GC pool.
      */
-    void mark(void *pbot, void *ptop)
+    void mark(void *pbot, void *ptop, size_t* bitMask)
     {
+        const BITS = size_t.sizeof * 8;
+
         void **p1 = cast(void **)pbot;
         void **p2 = cast(void **)ptop;
         size_t pcache = 0;
         uint changes = 0;
 
+        version (ENABLE_PRECISE) {
+        } else {
+            //force conservative scanning
+            bitMask = PointerMap.init.bits.ptr;
+        }
+
+        size_t tSize = bitMask[0] / size_t.sizeof;
+        size_t* bits = bitMask + 1;
+
         //printf("marking range: %p -> %p\n", pbot, ptop);
-        for (; p1 < p2; p1++)
+
+        while(p1 + tSize <= p2)
         {
-            Pool *pool;
-            byte *p = cast(byte *)(*p1);
-
-            //if (log) debug(PRINTF) printf("\tmark %x\n", p);
-            if (p >= minAddr && p < maxAddr)
+            for (size_t n = 0; n < tSize; n++)
             {
-                if ((cast(size_t)p & ~(PAGESIZE-1)) == pcache)
- 	            continue;
+                if ((bits[n / BITS] & (1 << (n % BITS)))) {
+                    debug(COLLECT_LOOP_PRINTF) printf("Marking %p\n", p1 + n);
+                    void* p = *(p1 + n);
 
-                pool = findPool(p);
-                if (pool)
-                {
+                    //if (log) debug(PRINTF) printf("\tmark %x\n", p);
+                    if (!(p >= minAddr && p < maxAddr))
+                        goto noscan;
+
+                    if ((cast(size_t)p & ~(PAGESIZE-1)) == pcache)
+                        goto noscan;
+
+                    Pool* pool = findPool(p);
+                    if (!pool)
+                        goto noscan;
+
                     size_t offset = cast(size_t)(p - pool.baseAddr);
                     size_t biti;
                     size_t pn = offset / PAGESIZE;
@@ -2222,31 +2360,39 @@
                     //debug(PRINTF) printf("\t\tfound pool %x, base=%x, pn = %d, bin = %d, biti = x%x\n", pool, pool.baseAddr, pn, bin, biti);
 
                     // Adjust bit to be at start of allocated memory block
-                    if (bin <= B_PAGE)
+                    if (bin < B_PAGE)
                     {
                         biti = (offset & notbinsize[bin]) >> 4;
                         //debug(PRINTF) printf("\t\tbiti = x%x\n", biti);
                     }
+                    else if (bin == B_PAGE)
+                    {
+                        biti = (offset & notbinsize[bin]) >> 4;
+                        //debug(PRINTF) printf("\t\tbiti = x%x\n", biti);
+
+                        pcache = cast(size_t)p & ~(PAGESIZE-1);
+                    }
                     else if (bin == B_PAGEPLUS)
                     {
                         do
                         {   --pn;
                         } while (cast(Bins)pool.pagetable[pn] == B_PAGEPLUS);
                         biti = pn * (PAGESIZE / 16);
+
+                        pcache = cast(size_t)p & ~(PAGESIZE-1);
+
+                        bin = B_PAGE;
                     }
                     else
                     {
                         // Don't mark bits in B_FREE or B_UNCOMMITTED pages
-                        continue;
+                        goto noscan;
                     }
 
-                    if (bin >= B_PAGE) // Cache B_PAGE and B_PAGEPLUS lookups
-                        pcache = cast(size_t)p & ~(PAGESIZE-1);
-
                     //debug(PRINTF) printf("\t\tmark(x%x) = %d\n", biti, pool.mark.test(biti));
-                    if (!pool.mark.test(biti))
+                    if (!pool.mark.testSet(biti))
                     {
-                        //if (log) debug(PRINTF) printf("\t\tmarking %x\n", p);
+                        debug(PRINTF) printf("\t\tmarking %p\n", p);
                         pool.mark.set(biti);
                         if (!pool.noscan.test(biti))
                         {
@@ -2256,8 +2402,13 @@
                         log_parent(sentinel_add(pool.baseAddr + biti * 16), sentinel_add(pbot));
                     }
                 }
+
+                //jump target if the pointer isn't scanned
+                noscan: ;
             }
+            p1 += tSize;
         }
+
         anychanges |= changes;
     }
 
@@ -2394,14 +2545,16 @@
             pool.mark.copy(&pool.freebits);
         }
 
-        rt_scanStaticData( &mark );
+        rt_scanStaticData( &markConservative );
 
         version (MULTI_THREADED)
         {
             if (!noStack)
             {
+                debug(PRINTF) printf(" scan stack bot = %x, top = %x\n", stackTop, stackBottom);
+
                 // Scan stacks and registers for each paused thread
-                thread_scanAll( &mark, stackTop );
+                thread_scanAll( &markConservative, stackTop );
             }
         }
         else
@@ -2411,15 +2564,15 @@
                 // Scan stack for main thread
                 debug(PRINTF) printf(" scan stack bot = %x, top = %x\n", stackTop, stackBottom);
                 version (STACKGROWSDOWN)
-                    mark(stackTop, stackBottom);
+                    markConservative(stackTop, stackBottom);
                 else
-                    mark(stackBottom, stackTop);
+                    markConservative(stackBottom, stackTop);
             }
         }
 
         // Scan roots[]
         debug(COLLECT_PRINTF) printf("scan roots[]\n");
-        mark(roots, roots + nroots);
+        markConservative(roots, roots + nroots);
 
         // Scan ranges[]
         debug(COLLECT_PRINTF) printf("scan ranges[]\n");
@@ -2427,7 +2580,7 @@
         for (n = 0; n < nranges; n++)
         {
             debug(COLLECT_PRINTF) printf("\t%x .. %x\n", ranges[n].pbot, ranges[n].ptop);
-            mark(ranges[n].pbot, ranges[n].ptop);
+            markConservative(ranges[n].pbot, ranges[n].ptop);
         }
         //log--;
 
@@ -2437,44 +2590,45 @@
             anychanges = 0;
             for (n = 0; n < npools; n++)
             {
-                uint *bbase;
-                uint *b;
-                uint *btop;
-
                 pool = pooltable[n];
 
-                bbase = pool.scan.base();
-                btop = bbase + pool.scan.nwords;
-                for (b = bbase; b < btop;)
-                {   Bins   bin;
-                    size_t pn;
-                    size_t u;
-                    size_t bitm;
-                    byte*  o;
-
-                    bitm = *b;
+                auto bbase = pool.scan.base();
+                auto btop = bbase + pool.scan.nwords;
+                for (auto b = bbase; b < btop;)
+                {
+                    auto bitm = *b;
                     if (!bitm)
                     {   b++;
                         continue;
                     }
                     *b = 0;
 
-                    o = pool.baseAddr + (b - bbase) * 32 * 16;
+                    auto o = pool.baseAddr + (b - bbase) * 32 * 16;
                     if (!(bitm & 0xFFFF))
                     {
                         bitm >>= 16;
                         o += 16 * 16;
                     }
+                    if (!(bitm & 0xFF))
+                    {
+                        bitm >>= 8;
+                        o += 8 * 16;
+                    }
                     for (; bitm; o += 16, bitm >>= 1)
                     {
                         if (!(bitm & 1))
                             continue;
 
-                        pn = cast(size_t)(o - pool.baseAddr) / PAGESIZE;
-                        bin = cast(Bins)pool.pagetable[pn];
+                        auto pn = cast(size_t)(o - pool.baseAddr) / PAGESIZE;
+                        auto bin = cast(Bins)pool.pagetable[pn];
                         if (bin < B_PAGE)
                         {
-                            mark(o, o + binsize[bin]);
+                            size_t bitMaskOffset = binsize[bin] - (size_t*).sizeof;
+                            size_t* bitMask = *(cast(size_t**) (o + bitMaskOffset));
+
+                            debug(PRINTF) printf("Bitmask:  %p\n", bitMask);
+                            mark(o, o + binsize[bin] - (size_t*).sizeof, bitMask);
+                            //mark(o, o + binsize[bin] - (size_t*).sizeof, conservativeBitMask.ptr);
                         }
                         else if (bin == B_PAGE || bin == B_PAGEPLUS)
                         {
@@ -2483,10 +2637,17 @@
                                 while (pool.pagetable[pn - 1] != B_PAGE)
                                     pn--;
                             }
-                            u = 1;
+                            auto u = 1;
                             while (pn + u < pool.ncommitted && pool.pagetable[pn + u] == B_PAGEPLUS)
                                 u++;
-                            mark(o, o + u * PAGESIZE);
+
+                            size_t blockSize = u * PAGESIZE;
+                            size_t bitMaskOffset = blockSize - (size_t*).sizeof;
+                            size_t* bitMask = *(cast(size_t**) (o + bitMaskOffset));
+
+                            debug(PRINTF) printf("Bitmask:  %p\n", bitMask);
+                            mark(o, o + u * PAGESIZE - (size_t*).sizeof, bitMask);
+                            //mark(o, o + u * PAGESIZE - (size_t*).sizeof, conservativeBitMask.ptr);
                         }
                     }
                 }
Index: tango/core/rt/gc/basic/gc.d
===================================================================
--- tango/core/rt/gc/basic/gc.d	(revision 5489)
+++ tango/core/rt/gc/basic/gc.d	(working copy)
@@ -144,19 +144,19 @@
     return _gc.clrAttr( p, a );
 }
 
-extern (C) void* gc_malloc( size_t sz, uint ba = 0 )
+extern (C) void* gc_malloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init)
 {
-    return _gc.malloc( sz, ba );
+    return _gc.malloc( sz, ba, bitMask );
 }
 
-extern (C) void* gc_calloc( size_t sz, uint ba = 0 )
+extern (C) void* gc_calloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init)
 {
-    return _gc.calloc( sz, ba );
+    return _gc.calloc( sz, ba, bitMask );
 }
 
-extern (C) void* gc_realloc( void* p, size_t sz, uint ba = 0 )
+extern (C) void* gc_realloc( void* p, size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init)
 {
-    return _gc.realloc( p, sz, ba );
+    return _gc.realloc( p, sz, ba, bitMask );
 }
 
 extern (C) size_t gc_extend( void* p, size_t mx, size_t sz )
Index: tango/core/rt/gc/basic/gcbits.d
===================================================================
--- tango/core/rt/gc/basic/gcbits.d	(revision 5489)
+++ tango/core/rt/gc/basic/gcbits.d	(working copy)
@@ -149,6 +149,38 @@
         }
     }
 
+    uint testSet(size_t i)
+    {
+        version (bitops)
+        {
+            return std.intrinsic.bts(data + 1, i);
+        }
+        else version (Asm86)
+        {
+            asm
+            {
+                naked                   ;
+                mov     EAX,data[EAX]   ;
+                mov     ECX,i-4[ESP]    ;
+                bts     4[EAX],ECX      ;
+                sbb     EAX,EAX         ;
+                ret     4               ;
+            }
+        }
+        else
+        {   uint result;
+
+            //result = (cast(bit *)(data + 1))[i];
+            //(cast(bit *)(data + 1))[i] = 0;
+
+            uint* p = &data[1 + (i >> BITS_SHIFT)];
+            uint  mask = (1 << (i & BITS_MASK));
+            result = *p & mask;
+            *p |= mask;
+            return result;
+        }
+    }
+
     void zero()
     {
         version(MEMCPY_NON_SIG_SAFE) {
Index: tango/core/rt/compiler/dmd/rt/adi.d
===================================================================
--- tango/core/rt/compiler/dmd/rt/adi.d	(revision 5489)
+++ tango/core/rt/compiler/dmd/rt/adi.d	(working copy)
@@ -50,8 +50,8 @@
         ALL_BITS = 0b1111_1111
     }
 
-    extern (C) void* gc_malloc( size_t sz, uint ba = 0 );
-    extern (C) void* gc_calloc( size_t sz, uint ba = 0 );
+    extern (C) void*  gc_malloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init);
+    extern (C) void*  gc_calloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init);
     extern (C) void  gc_free( void* p );
 }
 
Index: tango/core/rt/compiler/dmd/rt/lifetime.d
===================================================================
--- tango/core/rt/compiler/dmd/rt/lifetime.d	(revision 5489)
+++ tango/core/rt/compiler/dmd/rt/lifetime.d	(working copy)
@@ -57,8 +57,8 @@
     extern (C) uint gc_setAttr( void* p, uint a );
     extern (C) uint gc_clrAttr( void* p, uint a );
 
-    extern (C) void*  gc_malloc( size_t sz, uint ba = 0 );
-    extern (C) void*  gc_calloc( size_t sz, uint ba = 0 );
+    extern (C) void*  gc_malloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init);
+    extern (C) void*  gc_calloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init);
     extern (C) size_t gc_extend( void* p, size_t mx, size_t sz );
     extern (C) void   gc_free( void* p );
 
@@ -101,8 +101,13 @@
     }
     else
     {
+        PointerMap pm;
+        version (D_HavePointerMap) {
+            pm = ci.pointermap;
+        }
         p = gc_malloc(ci.init.length,
-                      BlkAttr.FINALIZE | (ci.flags & 2 ? BlkAttr.NO_SCAN : 0));
+                      BlkAttr.FINALIZE | (ci.flags & 2 ? BlkAttr.NO_SCAN : 0),
+                      pm);
         debug(PRINTF) printf(" p = %p\n", p);
     }
 
@@ -210,7 +215,11 @@
         }
         else
             size *= length;
-        p = gc_malloc(size + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
+        PointerMap pm;
+        version (D_HavePointerMap) {
+            pm = ti.next.pointermap();
+        }
+        p = gc_malloc(size + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
         debug(PRINTF) printf(" p = %p\n", p);
         memset(p, 0, size);
         result = cast(ulong)length + (cast(ulong)cast(uint)p << 32);
@@ -250,7 +259,11 @@
         }
         else
             size *= length;
-        auto p = gc_malloc(size + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
+        PointerMap pm;
+        version (D_HavePointerMap) {
+            pm = ti.next.pointermap();
+        }
+        auto p = gc_malloc(size + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
         debug(PRINTF) printf(" p = %p\n", p);
         if (isize == 1)
             memset(p, *cast(ubyte*)q, size);
@@ -580,7 +593,11 @@
                             goto L1;
                         }
                     }
-                    newdata = cast(byte *)gc_malloc(newsize + 1, info.attr);
+                    PointerMap pm;
+                    version (D_HavePointerMap) {
+                        pm = ti.next.pointermap();
+                    }
+                    newdata = cast(byte *)gc_malloc(newsize + 1, info.attr, pm);
                     newdata[0 .. size] = p.data[0 .. size];
                 }
              L1:
@@ -589,7 +606,11 @@
         }
         else
         {
-            newdata = cast(byte *)gc_calloc(newsize + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
+            PointerMap pm;
+            version (D_HavePointerMap) {
+                pm = ti.next.pointermap();
+            }
+            newdata = cast(byte *)gc_calloc(newsize + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
         }
     }
     else
@@ -680,7 +701,11 @@
                             goto L1;
                         }
                     }
-                    newdata = cast(byte *)gc_malloc(newsize + 1, info.attr);
+                    PointerMap pm;
+                    version (D_HavePointerMap) {
+                        pm = ti.next.pointermap();
+                    }
+                    newdata = cast(byte *)gc_malloc(newsize + 1, info.attr, pm);
                     newdata[0 .. size] = p.data[0 .. size];
                 L1: ;
                 }
@@ -688,7 +713,11 @@
         }
         else
         {
-            newdata = cast(byte *)gc_malloc(newsize + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
+            PointerMap pm;
+            version (D_HavePointerMap) {
+                pm = ti.next.pointermap();
+            }
+            newdata = cast(byte *)gc_malloc(newsize + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
         }
 
         auto q = initializer.ptr; // pointer to initializer
@@ -746,7 +775,11 @@
                 goto L1;
             }
         }
-        newdata = cast(byte *)gc_malloc(newCapacity(newlength, sizeelem) + 1, info.attr);
+        PointerMap pm;
+        version (D_HavePointerMap) {
+            pm = ti.next.pointermap();
+        }
+        newdata = cast(byte *)gc_malloc(newCapacity(newlength, sizeelem) + 1, info.attr, pm);
         memcpy(newdata, px.data, length * sizeelem);
         px.data = newdata;
     }
@@ -844,10 +877,14 @@
                 goto L1;
             }
         }
-        debug(PRINTF) printf("_d_arrayappendcT(length = %d, newlength = %d, cap = %d)\n", length, newlength, info.size);
+        debug(PRINTF) printf("_d_arrayappendcT(length = %d, newlength = %d, cap = %d, ti=%.*s)\n", length, newlength, info.size, ti.toString());
         auto newcap = newCapacity(newlength, sizeelem);
         assert(newcap >= newlength * sizeelem);
-        newdata = cast(byte *)gc_malloc(newcap + 1, info.attr);
+        PointerMap pm;
+        version (D_HavePointerMap) {
+            pm = ti.next.pointermap();
+        }
+        newdata = cast(byte *)gc_malloc(newcap + 1, info.attr, pm);
         memcpy(newdata, x.ptr, length * sizeelem);
         (cast(void**)(&x))[1] = newdata;
     }
@@ -1049,7 +1086,11 @@
     if (!len)
         return null;
 
-    byte* p = cast(byte*)gc_malloc(len + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
+    PointerMap pm;
+    version (D_HavePointerMap) {
+        pm = ti.next.pointermap();
+    }
+    byte* p = cast(byte*)gc_malloc(len + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
     memcpy(p, x.ptr, xlen);
     memcpy(p + xlen, y.ptr, ylen);
     p[len] = 0;
@@ -1078,7 +1119,11 @@
     if (!length)
         return null;
 
-    a = gc_malloc(length * size, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
+    PointerMap pm;
+    version (D_HavePointerMap) {
+        pm = ti.next.pointermap();
+    }
+    a = gc_malloc(length * size, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
     p = cast(byte[]*)(&n + 1);
 
     uint j = 0;
@@ -1112,7 +1157,11 @@
         result = null;
     else
     {
-        result = gc_malloc(length * sizeelem, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
+        PointerMap pm;
+        version (D_HavePointerMap) {
+            pm = ti.next.pointermap();
+        }
+        result = gc_malloc(length * sizeelem, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
 
         va_list q;
         va_start!(size_t)(q, length);
@@ -1165,7 +1214,11 @@
     {
         auto sizeelem = ti.next.tsize();                // array element size
         auto size = a.length * sizeelem;
-        r.ptr = gc_malloc(size, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
+        PointerMap pm;
+        version (D_HavePointerMap) {
+            pm = ti.next.pointermap();
+        }
+        r.ptr = gc_malloc(size, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
         r.length = a.length;
         memcpy(r.ptr, a.ptr, size);
     }
Index: tango/core/rt/compiler/dmd/rt/aaA.d
===================================================================
--- tango/core/rt/compiler/dmd/rt/aaA.d	(revision 5489)
+++ tango/core/rt/compiler/dmd/rt/aaA.d	(working copy)
@@ -46,8 +46,8 @@
         ALL_BITS = 0b1111_1111
     }
 
-    extern (C) void* gc_malloc( size_t sz, uint ba = 0 );
-    extern (C) void* gc_calloc( size_t sz, uint ba = 0 );
+    extern (C) void*  gc_malloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init);
+    extern (C) void*  gc_calloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init);
     extern (C) void  gc_free( void* p );
 }
 
Index: tango/core/rt/compiler/dmd/object_.d
===================================================================
--- tango/core/rt/compiler/dmd/object_.d	(revision 5489)
+++ tango/core/rt/compiler/dmd/object_.d	(working copy)
@@ -157,6 +157,27 @@
 }
 
 /**
+ * Pointer map for precise heap scanning.
+ * Format:
+ *  PointerMap pm = typeid(T).pointermap;
+ *  pm.bits[0] is the size of T in bytes
+ *  pm.bits[1..$] is the bitmap; each bit covers size_t bytes of T, meaning:
+ *      0: guaranteed not to be a pointer, don't scan
+ *      1: possibly a pointer, must scan
+ * This implies all GC-aware pointers must be aligned on size_t boundaries.
+ * The compiler won't set any bits for unaligned pointer fields.
+ * The least significant bit of a size_t item is considered the first bit.
+ * PointerMap.init is a conservative scanning mask equivelant to void*[]
+ * XXX: add helper methods for constructing bitmasks?
+ */
+struct PointerMap
+{
+    size_t[] bits = [size_t.sizeof, 1];
+}
+
+//static const PointerMap cPointerMapNoScan = PointerMap([size_t.sizeof, 0]);
+
+/**
  * Runtime type information about a class. Can be retrieved for any class type
  * or instance by using the .classinfo property.
  * A pointer to this appears as the first entry in the class's vtbl[].
@@ -184,7 +205,11 @@
     static if (__VERSION__ >= 1045) {
         TypeInfo typeinfo;
     }
-    
+
+    version (D_HavePointerMap) {
+        PointerMap pointermap;
+    }
+
     /**
      * Search all modules for ClassInfo corresponding to classname.
      * Returns: null if not found
@@ -304,6 +329,22 @@
     /// Get flags for type: 1 means GC should scan for pointers
     uint flags() { return 0; }
 
+    /// Get a pointer to PointerMap; used for GC scanning
+    PointerMap pointermap() {
+        if (flags() & 1) {
+            return PointerMap.init;
+        } else {
+            //return cPointerMapNoScan;
+            //work around for dmd bug #4397 (triggers infinite recursion)
+            static size_t[2] g_arr;
+            static PointerMap pm;
+            pm.bits = g_arr;
+            pm.bits[0] = size_t.sizeof;
+            pm.bits[1] = 0;
+            return pm;
+        }
+    }
+
     /// Get type information on the contents of the type; null if not available
     OffsetTypeInfo[] offTi() { return null; }
 }
@@ -466,6 +507,18 @@
     }
 
     override uint flags() { return 1; }
+
+    override PointerMap pointermap()
+    {
+        //return static mask for arrays
+        //work around for dmd bug #4397 (triggers infinite recursion)
+        static size_t[2] g_arr;
+        static PointerMap pm;
+        pm.bits = g_arr;
+        pm.bits[0] = size_t.sizeof * 2;
+        pm.bits[1] = 0b10;
+        return pm;
+    }
 }
 
 class TypeInfo_StaticArray : TypeInfo
@@ -550,6 +603,20 @@
     override TypeInfo next() { return value; }
     override uint flags() { return value.flags(); }
 
+    override PointerMap pointermap()
+    {
+        //assert(0);
+        //this is kind of a hack to make arrays of static arrays work
+        //e.g. T[2][] (typeid(T[2]) would be this instance)
+        //because the GC repeats GC bitmasks shorter than the allocation size,
+        //  this should work well
+        //it's a hack because pointermap() is supposed to return a map that
+        //  covers the whole type (i.e. doesn't rely on repeat)
+        //this also might prevent subtle bugs, when a static array is resized
+        //  as dynamic array, and the bitmask is reused (can that happen at all?)
+        return value.pointermap();
+    }
+
     TypeInfo value;
     size_t   len;
 }
@@ -687,6 +754,18 @@
 
     override uint flags() { return 1; }
 
+    override PointerMap pointermap()
+    {
+        //return static mask for delegates
+        //work around for dmd bug #4397 (triggers infinite recursion)
+        static size_t[2] g_arr;
+        static PointerMap pm;
+        pm.bits = g_arr;
+        pm.bits[0] = size_t.sizeof * 2;
+        pm.bits[1] = 0b01;
+        return pm;
+    }
+
     TypeInfo next;
 }
 
@@ -908,6 +987,12 @@
     char[] function()   xtoString;
 
     uint m_flags;
+
+    version (D_HavePointerMap) {
+        PointerMap m_pointermap;
+
+        override PointerMap pointermap() { return m_pointermap; }
+    }
 }
 
 class TypeInfo_Tuple : TypeInfo
@@ -970,6 +1055,11 @@
     {
         assert(0);
     }
+
+    override PointerMap pointermap()
+    {
+        assert(0);
+    }
 }
 
 
Index: tango/core/Memory.d
===================================================================
--- tango/core/Memory.d	(revision 5489)
+++ tango/core/Memory.d	(working copy)
@@ -8,7 +8,6 @@
  */
 module tango.core.Memory;
 
-
 private
 {
     extern (C) void gc_init();
@@ -23,9 +22,9 @@
     extern (C) uint gc_setAttr( void* p, uint a );
     extern (C) uint gc_clrAttr( void* p, uint a );
 
-    extern (C) void*  gc_malloc( size_t sz, uint ba = 0 );
-    extern (C) void*  gc_calloc( size_t sz, uint ba = 0 );
-    extern (C) void*  gc_realloc( void* p, size_t sz, uint ba = 0 );
+    extern (C) void*  gc_malloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init);
+    extern (C) void*  gc_calloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init);
+    extern (C) void*  gc_realloc( void* p, size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init);
     extern (C) size_t gc_extend( void* p, size_t mx, size_t sz );
     extern (C) size_t gc_reserve( size_t sz );
     extern (C) void   gc_free( void* p );
@@ -201,6 +200,7 @@
      * Params:
      *  sz = The desired allocation size in bytes.
      *  ba = A bitmask of the attributes to set on this block.
+     *  bitMask = The pointer offset information for precise heap scanning.
      *
      * Returns:
      *  A reference to the allocated memory or null if insufficient memory
@@ -209,9 +209,10 @@
      * Throws:
      *  OutOfMemoryException on allocation failure.
      */
-    static void* malloc( size_t sz, uint ba = 0 )
+    static void* malloc( size_t sz, uint ba = 0,
+        PointerMap bitMask = PointerMap.init )
     {
-        return gc_malloc( sz, ba );
+        return gc_malloc( sz, ba, bitMask );
     }
 
 
@@ -226,6 +227,7 @@
      * Params:
      *  sz = The desired allocation size in bytes.
      *  ba = A bitmask of the attributes to set on this block.
+     *  bitMask = The pointer offset information for precise heap scanning.
      *
      * Returns:
      *  A reference to the allocated memory or null if insufficient memory
@@ -234,9 +236,10 @@
      * Throws:
      *  OutOfMemoryException on allocation failure.
      */
-    static void* calloc( size_t sz, uint ba = 0 )
+    static void* calloc( size_t sz, uint ba = 0,
+        PointerMap bitMask = PointerMap.init )
     {
-        return gc_calloc( sz, ba );
+        return gc_calloc( sz, ba, bitMask );
     }
 
 
@@ -258,12 +261,15 @@
      * reallocation is required.  If ba is not zero and p references the head
      * of a valid, known memory block then the bits in ba will replace those on
      * the current memory block and will also be set on the new block if a
-     * reallocation is required.
+     * reallocation is required.  Similarly, if bitMask is non-null, then
+     * the bitmask for the current block will propagated to the new block.
+     * Otherwise, the bitmask provided will be propagated to the old block.
      *
      * Params:
      *  p  = A pointer to the root of a valid memory block or to null.
      *  sz = The desired allocation size in bytes.
      *  ba = A bitmask of the attributes to set on this block.
+     *  bitMask = The pointer offset information for precise heap scanning.
      *
      * Returns:
      *  A reference to the allocated memory on success or null if sz is
@@ -272,9 +278,10 @@
      * Throws:
      *  OutOfMemoryException on allocation failure.
      */
-    static void* realloc( void* p, size_t sz, uint ba = 0 )
+    static void* realloc( void* p, size_t sz, uint ba = 0,
+        PointerMap bitMask = PointerMap.init )
     {
-        return gc_realloc( p, sz, ba );
+        return gc_realloc( p, sz, ba, bitMask );
     }
 
 
