bmalloc: refactored XLarge allocation for better alignment
authorggaren@apple.com <ggaren@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 17 Jan 2015 02:53:28 +0000 (02:53 +0000)
committerggaren@apple.com <ggaren@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 17 Jan 2015 02:53:28 +0000 (02:53 +0000)
https://bugs.webkit.org/show_bug.cgi?id=140582

Reviewed by Andreas Kling.

XLarge objects used to be Large objects with an extra bit of metadata
that said "actually, I'm not large -- I'm extra large".

The metadata header in an XLarge allocation made it impossible for the
XLarge object to honor a very large alignment request.

The solution is to stop using a metadata header for XLarge objects, and
instead to store explicit metadata on the side.

This is a bit less astonishing, which is also nice.

Finding XLarge metadata is now a linear search. That's probably OK, since
it was always so in TCMalloc, and the usual number of XLarge allocations
in a process is 0.

This design makes it possible for the heap to cache XLarge allocations
with and/or without physical pages. I haven't actually done that yet
because the tradeoffs are subtle, so I don't want to do anything without
a motivating test case.

* bmalloc.xcodeproj/project.pbxproj:
* bmalloc/Allocator.cpp:
(bmalloc::Allocator::reallocate): Removed the concept of an XLargeChunk,
since an XLarge allocation is now just a naked buffer without a header.

(bmalloc::Allocator::allocateXLarge): Added an explicit qualifier for
XLarge alignment, since XLargeChunk won't give this to us implicitly
anymore.

* bmalloc/BoundaryTag.h:
(bmalloc::BoundaryTag::setRange):
(bmalloc::BoundaryTag::isXLarge): Deleted.
(bmalloc::BoundaryTag::setXLarge): Deleted.
* bmalloc/BoundaryTagInlines.h:
(bmalloc::validate):
(bmalloc::BoundaryTag::deallocate): Removed the XLarge hacks from Large allocations.

* bmalloc/Deallocator.cpp:
(bmalloc::Deallocator::deallocateXLarge):
(bmalloc::Deallocator::deallocateSlowCase):
* bmalloc/Heap.cpp:
(bmalloc::Heap::findXLarge):
(bmalloc::Heap::allocateXLarge):
(bmalloc::Heap::deallocateXLarge):
* bmalloc/Heap.h: Updated for interface changes.

* bmalloc/ObjectType.cpp:
(bmalloc::objectType):
* bmalloc/ObjectType.h:
(bmalloc::isXLarge): We can now tell if a pointer is XLarge just by
examining its bit pattern -- just like we do for other kinds of
allocations -- which is nice.

* bmalloc/Sizes.h:
* bmalloc/VMHeap.h:
(bmalloc::VMHeap::allocateXLarge):
(bmalloc::VMHeap::findXLarge):
(bmalloc::VMHeap::deallocateXLarge): Keep an explicit vector of metadata
for XLarge allocations.

* bmalloc/XLargeChunk.h: Removed.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@178621 268f45cc-cd09-0410-ab3c-d52691b4dbfc

13 files changed:
Source/bmalloc/ChangeLog
Source/bmalloc/bmalloc.xcodeproj/project.pbxproj
Source/bmalloc/bmalloc/Allocator.cpp
Source/bmalloc/bmalloc/BoundaryTag.h
Source/bmalloc/bmalloc/BoundaryTagInlines.h
Source/bmalloc/bmalloc/Deallocator.cpp
Source/bmalloc/bmalloc/Heap.cpp
Source/bmalloc/bmalloc/Heap.h
Source/bmalloc/bmalloc/ObjectType.cpp
Source/bmalloc/bmalloc/ObjectType.h
Source/bmalloc/bmalloc/Sizes.h
Source/bmalloc/bmalloc/VMHeap.h
Source/bmalloc/bmalloc/XLargeChunk.h [deleted file]

index c06f22f..df6e056 100644 (file)
@@ -1,5 +1,74 @@
 2015-01-16  Geoffrey Garen  <ggaren@apple.com>
 
+        bmalloc: refactored XLarge allocation for better alignment
+        https://bugs.webkit.org/show_bug.cgi?id=140582
+
+        Reviewed by Andreas Kling.
+
+        XLarge objects used to be Large objects with an extra bit of metadata
+        that said "actually, I'm not large -- I'm extra large".
+
+        The metadata header in an XLarge allocation made it impossible for the
+        XLarge object to honor a very large alignment request.
+
+        The solution is to stop using a metadata header for XLarge objects, and
+        instead to store explicit metadata on the side.
+
+        This is a bit less astonishing, which is also nice.
+
+        Finding XLarge metadata is now a linear search. That's probably OK, since
+        it was always so in TCMalloc, and the usual number of XLarge allocations
+        in a process is 0.
+
+        This design makes it possible for the heap to cache XLarge allocations
+        with and/or without physical pages. I haven't actually done that yet
+        because the tradeoffs are subtle, so I don't want to do anything without
+        a motivating test case.
+
+        * bmalloc.xcodeproj/project.pbxproj:
+        * bmalloc/Allocator.cpp:
+        (bmalloc::Allocator::reallocate): Removed the concept of an XLargeChunk,
+        since an XLarge allocation is now just a naked buffer without a header.
+
+        (bmalloc::Allocator::allocateXLarge): Added an explicit qualifier for
+        XLarge alignment, since XLargeChunk won't give this to us implicitly
+        anymore.
+
+        * bmalloc/BoundaryTag.h:
+        (bmalloc::BoundaryTag::setRange):
+        (bmalloc::BoundaryTag::isXLarge): Deleted.
+        (bmalloc::BoundaryTag::setXLarge): Deleted.
+        * bmalloc/BoundaryTagInlines.h:
+        (bmalloc::validate):
+        (bmalloc::BoundaryTag::deallocate): Removed the XLarge hacks from Large allocations.
+
+        * bmalloc/Deallocator.cpp:
+        (bmalloc::Deallocator::deallocateXLarge):
+        (bmalloc::Deallocator::deallocateSlowCase):
+        * bmalloc/Heap.cpp:
+        (bmalloc::Heap::findXLarge):
+        (bmalloc::Heap::allocateXLarge):
+        (bmalloc::Heap::deallocateXLarge):
+        * bmalloc/Heap.h: Updated for interface changes.
+
+        * bmalloc/ObjectType.cpp:
+        (bmalloc::objectType):
+        * bmalloc/ObjectType.h:
+        (bmalloc::isXLarge): We can now tell if a pointer is XLarge just by
+        examining its bit pattern -- just like we do for other kinds of
+        allocations -- which is nice.
+
+        * bmalloc/Sizes.h:
+        * bmalloc/VMHeap.h:
+        (bmalloc::VMHeap::allocateXLarge):
+        (bmalloc::VMHeap::findXLarge):
+        (bmalloc::VMHeap::deallocateXLarge): Keep an explicit vector of metadata
+        for XLarge allocations.
+
+        * bmalloc/XLargeChunk.h: Removed.
+
+2015-01-16  Geoffrey Garen  <ggaren@apple.com>
+
         bmalloc: added some infrastructure for aligned allocation
         https://bugs.webkit.org/show_bug.cgi?id=140572
 
index eb72837..663211d 100644 (file)
@@ -54,7 +54,6 @@
                14DD78CE18F48D7500950702 /* Syscall.h in Headers */ = {isa = PBXBuildFile; fileRef = 1417F64F18B7280C0076FA3F /* Syscall.h */; settings = {ATTRIBUTES = (Private, ); }; };
                14DD78CF18F48D7500950702 /* Vector.h in Headers */ = {isa = PBXBuildFile; fileRef = 1479E21217A1A255006D4E9D /* Vector.h */; settings = {ATTRIBUTES = (Private, ); }; };
                14DD78D018F48D7500950702 /* VMAllocate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1479E21417A1A63E006D4E9D /* VMAllocate.h */; settings = {ATTRIBUTES = (Private, ); }; };
-               14DD78D118F48EC600950702 /* XLargeChunk.h in Headers */ = {isa = PBXBuildFile; fileRef = 147AAA8918CD17CE002201E4 /* XLargeChunk.h */; settings = {ATTRIBUTES = (Private, ); }; };
                14F271C318EA3978008C152F /* Allocator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 145F6855179DC8CA00D65598 /* Allocator.cpp */; };
                14F271C418EA397B008C152F /* Cache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 144469E417A46BFE00F9EA1D /* Cache.cpp */; };
                14F271C518EA397E008C152F /* Deallocator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 145F6859179DC90200D65598 /* Deallocator.cpp */; };
                1479E21217A1A255006D4E9D /* Vector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = Vector.h; path = bmalloc/Vector.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
                1479E21417A1A63E006D4E9D /* VMAllocate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = VMAllocate.h; path = bmalloc/VMAllocate.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
                147AAA8818CD17CE002201E4 /* LargeChunk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LargeChunk.h; path = bmalloc/LargeChunk.h; sourceTree = "<group>"; };
-               147AAA8918CD17CE002201E4 /* XLargeChunk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XLargeChunk.h; path = bmalloc/XLargeChunk.h; sourceTree = "<group>"; };
                147AAA8C18CD36A7002201E4 /* SmallChunk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SmallChunk.h; path = bmalloc/SmallChunk.h; sourceTree = "<group>"; };
                147AAA8E18CD89E3002201E4 /* MediumChunk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MediumChunk.h; path = bmalloc/MediumChunk.h; sourceTree = "<group>"; };
                147AAA9418CE5CA6002201E4 /* Chunk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Chunk.h; path = bmalloc/Chunk.h; sourceTree = "<group>"; };
                                147AAA8818CD17CE002201E4 /* LargeChunk.h */,
                                146BEE2118C845AE0002D5A2 /* SegregatedFreeList.cpp */,
                                146BEE1E18C841C50002D5A2 /* SegregatedFreeList.h */,
-                               147AAA8918CD17CE002201E4 /* XLargeChunk.h */,
                        );
                        name = "heap: large | xlarge";
                        sourceTree = "<group>";
                                14DD78B418F48D6B00950702 /* Chunk.h in Headers */,
                                14DD78CA18F48D7500950702 /* Mutex.h in Headers */,
                                143CB81D19022BC900B16A45 /* StaticMutex.h in Headers */,
-                               14DD78D118F48EC600950702 /* XLargeChunk.h in Headers */,
                                14DD78B918F48D6B00950702 /* MediumTraits.h in Headers */,
                                1448C30118F3754C00502839 /* bmalloc.h in Headers */,
                                14DD789A18F48D4A00950702 /* Deallocator.h in Headers */,
index 9353029..9ea3b3c 100644 (file)
@@ -30,7 +30,6 @@
 #include "LargeChunk.h"
 #include "PerProcess.h"
 #include "Sizes.h"
-#include "XLargeChunk.h"
 #include <algorithm>
 #include <cstdlib>
 
@@ -90,8 +89,10 @@ void* Allocator::reallocate(void* object, size_t newSize)
         break;
     }
     case XLarge: {
-        XLargeChunk* chunk = XLargeChunk::get(object);
-        oldSize = chunk->size();
+        std::lock_guard<StaticMutex> lock(PerProcess<Heap>::mutex());
+        Range range = PerProcess<Heap>::getFastCase()->findXLarge(lock, object);
+        RELEASE_BASSERT(range);
+        oldSize = range.size();
         break;
     }
     }
@@ -151,7 +152,7 @@ NO_INLINE void* Allocator::allocateLarge(size_t size)
 
 NO_INLINE void* Allocator::allocateXLarge(size_t size)
 {
-    size = roundUpToMultipleOf<largeAlignment>(size);
+    size = roundUpToMultipleOf<xLargeAlignment>(size);
     std::lock_guard<StaticMutex> lock(PerProcess<Heap>::mutex());
     return PerProcess<Heap>::getFastCase()->allocateXLarge(lock, size);
 }
index f4a5054..dd8b3c6 100644 (file)
@@ -44,9 +44,6 @@ public:
     static void allocate(size_t, Range&, Range& leftover, bool& hasPhysicalPages);
     static unsigned compactBegin(const Range&);
 
-    bool isXLarge() { return m_size == xLargeMarker; }
-    void setXLarge() { m_size = xLargeMarker; }
-
     bool isFree() { return m_isFree; }
     void setFree(bool isFree) { m_isFree = isFree; }
     
@@ -71,9 +68,7 @@ private:
     static const size_t flagBits = 3;
     static const size_t compactBeginBits = 5;
     static const size_t sizeBits = bitCount<unsigned>() - flagBits - compactBeginBits;
-    static const size_t xLargeMarker = 1; // This size is unused because our minimum object size is greater than it.
 
-    static_assert(largeMin > xLargeMarker, "largeMin must provide enough umbrella to fit xLargeMarker.");
     static_assert((1 << compactBeginBits) - 1 >= largeMin / largeAlignment, "compactBegin must be encodable in a BoundaryTag.");
     static_assert((1 << sizeBits) - 1 >= largeMax, "largeMax must be encodable in a BoundaryTag.");
 
@@ -102,7 +97,6 @@ inline void BoundaryTag::setRange(const Range& range)
     m_compactBegin = compactBegin(range);
     m_size = static_cast<unsigned>(range.size());
     BASSERT(this->size() == range.size());
-    BASSERT(!isXLarge());
 }
 
 inline EndTag* BoundaryTag::prev()
index 5120467..89a16fd 100644 (file)
@@ -42,17 +42,14 @@ IF_DEBUG(
     EndTag* endTag = LargeChunk::endTag(range.begin(), range.size());
 
     BASSERT(!beginTag->isEnd());
-    if (beginTag->isXLarge())
-        return;
-)
     BASSERT(range.size() >= largeMin);
     BASSERT(beginTag->size() == range.size());
 
     BASSERT(beginTag->size() == endTag->size());
     BASSERT(beginTag->isFree() == endTag->isFree());
     BASSERT(beginTag->hasPhysicalPages() == endTag->hasPhysicalPages());
-    BASSERT(beginTag->isXLarge() == endTag->isXLarge());
     BASSERT(static_cast<BoundaryTag*>(endTag) == static_cast<BoundaryTag*>(beginTag) || endTag->isEnd());
+);
 }
 
 static inline void validatePrev(EndTag* prev, void* object)
@@ -164,7 +161,6 @@ inline Range BoundaryTag::deallocate(void* object)
 {
     BeginTag* beginTag = LargeChunk::beginTag(object);
     BASSERT(!beginTag->isFree());
-    BASSERT(!beginTag->isXLarge())
 
     Range range(object, beginTag->size());
     EndTag* endTag = LargeChunk::endTag(range.begin(), range.size());
index d7964c4..bc1c4e3 100644 (file)
@@ -68,7 +68,7 @@ void Deallocator::deallocateLarge(void* object)
 
 void Deallocator::deallocateXLarge(void* object)
 {
-    std::lock_guard<StaticMutex> lock(PerProcess<Heap>::mutex());
+    std::unique_lock<StaticMutex> lock(PerProcess<Heap>::mutex());
     PerProcess<Heap>::getFastCase()->deallocateXLarge(lock, object);
 }
 
@@ -109,8 +109,7 @@ void Deallocator::deallocateSlowCase(void* object)
         return;
     }
 
-    BeginTag* beginTag = LargeChunk::beginTag(object);
-    if (!beginTag->isXLarge())
+    if (!isXLarge(object))
         return deallocateLarge(object);
     
     return deallocateXLarge(object);
index e1c4cc8..5a5dc3a 100644 (file)
@@ -31,7 +31,6 @@
 #include "Page.h"
 #include "PerProcess.h"
 #include "SmallChunk.h"
-#include "XLargeChunk.h"
 #include <thread>
 
 namespace bmalloc {
@@ -320,22 +319,23 @@ void Heap::deallocateMediumLine(std::lock_guard<StaticMutex>& lock, MediumLine*
     }
 }
 
+Range Heap::findXLarge(std::lock_guard<StaticMutex>&, void* object)
+{
+    return m_vmHeap.findXLarge(object);
+}
+
 void* Heap::allocateXLarge(std::lock_guard<StaticMutex>&, size_t size)
 {
-    XLargeChunk* chunk = XLargeChunk::create(size);
+    m_isAllocatingPages = true;
 
-    BeginTag* beginTag = LargeChunk::beginTag(chunk->begin());
-    beginTag->setXLarge();
-    beginTag->setFree(false);
-    beginTag->setHasPhysicalPages(true);
-    
-    return chunk->begin();
+    void* result = m_vmHeap.allocateXLarge(size);
+    vmAllocatePhysicalPagesSloppy(result, size);
+    return result;
 }
 
-void Heap::deallocateXLarge(std::lock_guard<StaticMutex>&, void* object)
+void Heap::deallocateXLarge(std::unique_lock<StaticMutex>& lock, void* object)
 {
-    XLargeChunk* chunk = XLargeChunk::get(object);
-    XLargeChunk::destroy(chunk);
+    m_vmHeap.deallocateXLarge(lock, object);
 }
 
 void* Heap::allocateLarge(std::lock_guard<StaticMutex>&, size_t size)
index 4f37f55..6b78255 100644 (file)
@@ -63,7 +63,8 @@ public:
     void deallocateLarge(std::lock_guard<StaticMutex>&, void*);
 
     void* allocateXLarge(std::lock_guard<StaticMutex>&, size_t);
-    void deallocateXLarge(std::lock_guard<StaticMutex>&, void*);
+    Range findXLarge(std::lock_guard<StaticMutex>&, void*);
+    void deallocateXLarge(std::unique_lock<StaticMutex>&, void*);
 
     void scavenge(std::unique_lock<StaticMutex>&, std::chrono::milliseconds sleepDuration);
 
index df8c900..7e167c9 100644 (file)
@@ -36,9 +36,9 @@ ObjectType objectType(void* object)
         return Medium;
     }
     
-    BeginTag* beginTag = LargeChunk::beginTag(object);
-    if (!beginTag->isXLarge())
+    if (!isXLarge(object))
         return Large;
+    
     return XLarge;
 }
 
index 8d5f744..4ec08a2 100644 (file)
@@ -51,6 +51,11 @@ inline bool isMedium(void* smallOrMedium)
     return !isSmall(smallOrMedium);
 }
 
+inline bool isXLarge(void* object)
+{
+    return !test(object, superChunkSize - 1);
+}
+
 } // namespace bmalloc
 
 #endif // ObjectType_h
index d345e82..55db28a 100644 (file)
@@ -80,6 +80,8 @@ namespace Sizes {
     static_assert(1 << largeAlignmentShift == largeAlignment, "largeAlignmentShift be log2(largeAlignment).");
     static const size_t largeMax = largeChunkSize * 99 / 100; // Plenty of room for metadata.
     static const size_t largeMin = mediumMax;
+    
+    static const size_t xLargeAlignment = vmPageSize;
 
     static const size_t segregatedFreeListSearchDepth = 16;
 
index 99f1a24..1f01862 100644 (file)
@@ -48,10 +48,14 @@ public:
     SmallPage* allocateSmallPage();
     MediumPage* allocateMediumPage();
     Range allocateLargeRange(size_t);
+    void* allocateXLarge(size_t);
+
+    Range findXLarge(void*);
 
     void deallocateSmallPage(std::unique_lock<StaticMutex>&, SmallPage*);
     void deallocateMediumPage(std::unique_lock<StaticMutex>&, MediumPage*);
     void deallocateLargeRange(std::unique_lock<StaticMutex>&, Range);
+    void deallocateXLarge(std::unique_lock<StaticMutex>&, void*);
 
 private:
     void allocateSuperChunk();
@@ -59,6 +63,7 @@ private:
     Vector<SmallPage*> m_smallPages;
     Vector<MediumPage*> m_mediumPages;
     SegregatedFreeList m_largeRanges;
+    Vector<Range> m_xLargeRanges;
 };
 
 inline SmallPage* VMHeap::allocateSmallPage()
@@ -88,6 +93,24 @@ inline Range VMHeap::allocateLargeRange(size_t size)
     return range;
 }
 
+inline void* VMHeap::allocateXLarge(size_t size)
+{
+    void* result = vmAllocate(size, superChunkSize);
+    m_xLargeRanges.push(Range(result, size));
+    return result;
+}
+
+inline Range VMHeap::findXLarge(void* object)
+{
+    for (auto& range : m_xLargeRanges) {
+        if (range.begin() != object)
+            continue;
+        return range;
+    }
+
+    return Range();
+}
+
 inline void VMHeap::deallocateSmallPage(std::unique_lock<StaticMutex>& lock, SmallPage* page)
 {
     lock.unlock();
@@ -129,6 +152,23 @@ inline void VMHeap::deallocateLargeRange(std::unique_lock<StaticMutex>& lock, Ra
     m_largeRanges.insert(range);
 }
 
+inline void VMHeap::deallocateXLarge(std::unique_lock<StaticMutex>& lock, void* object)
+{
+    for (size_t i = 0; i < m_xLargeRanges.size(); ++i) {
+        Range range = m_xLargeRanges[i];
+        if (range.begin() != object)
+            continue;
+
+        m_xLargeRanges.pop(i);
+
+        lock.unlock();
+        vmDeallocate(range.begin(), range.size());
+        lock.lock();
+        
+        break;
+    }
+}
+
 } // namespace bmalloc
 
 #endif // VMHeap_h
diff --git a/Source/bmalloc/bmalloc/XLargeChunk.h b/Source/bmalloc/bmalloc/XLargeChunk.h
deleted file mode 100644 (file)
index 275d0df..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
- */
-
-#ifndef XLargeChunk_h
-#define XLargeChunk_h
-
-#include "Sizes.h"
-
-namespace bmalloc {
-
-class XLargeChunk {
-public:
-    static XLargeChunk* create(size_t);
-    static void destroy(XLargeChunk*);
-    
-    static XLargeChunk* get(void* object) { return reinterpret_cast<XLargeChunk*>(LargeChunk::get(object)); }
-
-    char* begin() { return m_largeChunk.begin(); }
-    size_t& size();
-
-private:
-    XLargeChunk(const Range&, size_t);
-    Range& range();
-    
-    LargeChunk m_largeChunk;
-};
-
-inline XLargeChunk::XLargeChunk(const Range& range, size_t size)
-{
-    this->range() = range;
-    this->size() = size;
-}
-
-inline XLargeChunk* XLargeChunk::create(size_t size)
-{
-    size_t vmSize = bmalloc::vmSize(sizeof(XLargeChunk) + size);
-    auto xlargeChunk = vmAllocate(vmSize, superChunkSize);
-    return new (xlargeChunk) XLargeChunk(Range(xlargeChunk, vmSize), size);
-}
-
-inline void XLargeChunk::destroy(XLargeChunk* chunk)
-{
-    const Range& range = chunk->range();
-    vmDeallocate(range.begin(), range.size());
-}
-
-inline Range& XLargeChunk::range()
-{
-    // Since we hold only one object, we only use our first BoundaryTag. So, we
-    // can stuff our range into the remaining metadata.
-    Range& result = *reinterpret_cast<Range*>(roundUpToMultipleOf<alignment>(LargeChunk::beginTag(begin()) + 1));
-    BASSERT(static_cast<void*>(&result) < static_cast<void*>(begin()));
-    return result;
-}
-
-inline size_t& XLargeChunk::size()
-{
-    // Since we hold only one object, we only use our first BoundaryTag. So, we
-    // can stuff our size into the remaining metadata.
-    size_t& result = *reinterpret_cast<size_t*>(roundUpToMultipleOf<alignment>(&range() + 1));
-    BASSERT(static_cast<void*>(&result) < static_cast<void*>(begin()));
-    return result;
-}
-
-}; // namespace bmalloc
-
-#endif // XLargeChunk