Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (236882 => 236883)
--- trunk/Source/_javascript_Core/ChangeLog 2018-10-05 19:30:42 UTC (rev 236882)
+++ trunk/Source/_javascript_Core/ChangeLog 2018-10-05 19:59:04 UTC (rev 236883)
@@ -1,3 +1,41 @@
+2018-10-05 Yusuke Suzuki <[email protected]>
+
+ [JSC][Linux] Support Perf JITDump logging
+ https://bugs.webkit.org/show_bug.cgi?id=189893
+
+ Reviewed by Mark Lam.
+
+ This patch adds Linux `perf` command's JIT Dump support. It allows JSC to tell perf about JIT code information.
+ We add a command line option, `--logJITCodeForPerf`, which dumps `jit-%pid.dump` in the current directory.
+ By using this dump and perf.data output, we can annotate JIT code with profiling information.
+
+ $ echo "(function f() { var s = 0; for (var i = 0; i < 1000000000; i++) { s += i; } return s; })();" > test.js
+ $ perf record -k mono ../../WebKitBuild/perf/Release/bin/jsc test.js --logJITCodeForPerf=true
+ [ perf record: Woken up 1 times to write data ]
+ [ perf record: Captured and wrote 0.182 MB perf.data (4346 samples) ]
+ $ perf inject --jit -i perf.data -o perf.jit.data
+ $ perf report -i perf.jit.data
+
+ * Sources.txt:
+ * assembler/LinkBuffer.cpp:
+ (JSC::LinkBuffer::finalizeCodeWithDisassemblyImpl):
+ * assembler/LinkBuffer.h:
+ (JSC::LinkBuffer::finalizeCodeWithDisassembly):
+ * assembler/PerfLog.cpp: Added.
+ (JSC::PerfLog::singleton):
+ (JSC::generateTimestamp):
+ (JSC::getCurrentThreadID):
+ (JSC::PerfLog::PerfLog):
+ (JSC::PerfLog::write):
+ (JSC::PerfLog::flush):
+ (JSC::PerfLog::log):
+ * assembler/PerfLog.h: Added.
+ * jit/ExecutableAllocator.cpp:
+ (JSC::FixedVMPoolExecutableAllocator::FixedVMPoolExecutableAllocator):
+ * runtime/Options.cpp:
+ (JSC::Options::isAvailable):
+ * runtime/Options.h:
+
2018-10-05 Mark Lam <[email protected]>
Gardening: Build fix after r236880.
Modified: trunk/Source/_javascript_Core/Sources.txt (236882 => 236883)
--- trunk/Source/_javascript_Core/Sources.txt 2018-10-05 19:30:42 UTC (rev 236882)
+++ trunk/Source/_javascript_Core/Sources.txt 2018-10-05 19:59:04 UTC (rev 236883)
@@ -52,6 +52,7 @@
assembler/MacroAssemblerMIPS.cpp
assembler/MacroAssemblerPrinter.cpp
assembler/MacroAssemblerX86Common.cpp
+assembler/PerfLog.cpp
assembler/Printer.cpp
assembler/ProbeContext.cpp
assembler/ProbeStack.cpp
Modified: trunk/Source/_javascript_Core/assembler/LinkBuffer.cpp (236882 => 236883)
--- trunk/Source/_javascript_Core/assembler/LinkBuffer.cpp 2018-10-05 19:30:42 UTC (rev 236882)
+++ trunk/Source/_javascript_Core/assembler/LinkBuffer.cpp 2018-10-05 19:59:04 UTC (rev 236883)
@@ -35,6 +35,10 @@
#include "Options.h"
#include <wtf/CompilationThread.h>
+#if OS(LINUX)
+#include "PerfLog.h"
+#endif
+
namespace JSC {
bool shouldDumpDisassemblyFor(CodeBlock* codeBlock)
@@ -55,11 +59,23 @@
return CodeRef<LinkBufferPtrTag>::createSelfManagedCodeRef(m_code);
}
-LinkBuffer::CodeRef<LinkBufferPtrTag> LinkBuffer::finalizeCodeWithDisassemblyImpl(const char* format, ...)
+LinkBuffer::CodeRef<LinkBufferPtrTag> LinkBuffer::finalizeCodeWithDisassemblyImpl(bool dumpDisassembly, const char* format, ...)
{
CodeRef<LinkBufferPtrTag> result = finalizeCodeWithoutDisassemblyImpl();
- if (m_alreadyDisassembled)
+#if OS(LINUX)
+ if (Options::logJITCodeForPerf()) {
+ StringPrintStream out;
+ va_list argList;
+ va_start(argList, format);
+ va_start(argList, format);
+ out.vprintf(format, argList);
+ va_end(argList);
+ PerfLog::log(out.toCString(), result.code().untaggedExecutableAddress<const uint8_t*>(), result.size());
+ }
+#endif
+
+ if (!dumpDisassembly || m_alreadyDisassembled)
return result;
StringPrintStream out;
Modified: trunk/Source/_javascript_Core/assembler/LinkBuffer.h (236882 => 236883)
--- trunk/Source/_javascript_Core/assembler/LinkBuffer.h 2018-10-05 19:30:42 UTC (rev 236882)
+++ trunk/Source/_javascript_Core/assembler/LinkBuffer.h 2018-10-05 19:59:04 UTC (rev 236883)
@@ -261,11 +261,11 @@
}
template<PtrTag tag, typename... Args>
- CodeRef<tag> finalizeCodeWithDisassembly(const char* format, Args... args)
+ CodeRef<tag> finalizeCodeWithDisassembly(bool dumpDisassembly, const char* format, Args... args)
{
ALLOW_NONLITERAL_FORMAT_BEGIN
IGNORE_WARNINGS_BEGIN("format-security")
- return finalizeCodeWithDisassemblyImpl(format, args...).template retagged<tag>();
+ return finalizeCodeWithDisassemblyImpl(dumpDisassembly, format, args...).template retagged<tag>();
IGNORE_WARNINGS_END
ALLOW_NONLITERAL_FORMAT_END
}
@@ -288,7 +288,7 @@
private:
JS_EXPORT_PRIVATE CodeRef<LinkBufferPtrTag> finalizeCodeWithoutDisassemblyImpl();
- JS_EXPORT_PRIVATE CodeRef<LinkBufferPtrTag> finalizeCodeWithDisassemblyImpl(const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3);
+ JS_EXPORT_PRIVATE CodeRef<LinkBufferPtrTag> finalizeCodeWithDisassemblyImpl(bool dumpDisassembly, const char* format, ...) WTF_ATTRIBUTE_PRINTF(3, 4);
#if ENABLE(BRANCH_COMPACTION)
int executableOffsetFor(int location)
@@ -350,10 +350,19 @@
Vector<RefPtr<SharedTask<void(LinkBuffer&)>>> m_linkTasks;
};
+#if OS(LINUX)
#define FINALIZE_CODE_IF(condition, linkBufferReference, resultPtrTag, ...) \
(UNLIKELY((condition)) \
- ? (linkBufferReference).finalizeCodeWithDisassembly<resultPtrTag>(__VA_ARGS__) \
+ ? (linkBufferReference).finalizeCodeWithDisassembly<resultPtrTag>(true, __VA_ARGS__) \
+ : (UNLIKELY(JSC::Options::logJITCodeForPerf()) \
+ ? (linkBufferReference).finalizeCodeWithDisassembly<resultPtrTag>(false, __VA_ARGS__) \
+ : (linkBufferReference).finalizeCodeWithoutDisassembly<resultPtrTag>()))
+#else
+#define FINALIZE_CODE_IF(condition, linkBufferReference, resultPtrTag, ...) \
+ (UNLIKELY((condition)) \
+ ? (linkBufferReference).finalizeCodeWithDisassembly<resultPtrTag>(true, __VA_ARGS__) \
: (linkBufferReference).finalizeCodeWithoutDisassembly<resultPtrTag>())
+#endif
bool shouldDumpDisassemblyFor(CodeBlock*);
Added: trunk/Source/_javascript_Core/assembler/PerfLog.cpp (0 => 236883)
--- trunk/Source/_javascript_Core/assembler/PerfLog.cpp (rev 0)
+++ trunk/Source/_javascript_Core/assembler/PerfLog.cpp 2018-10-05 19:59:04 UTC (rev 236883)
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2018 Yusuke Suzuki <[email protected]>.
+ *
+ * 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.
+ */
+
+#include "config.h"
+#include "PerfLog.h"
+
+#if ENABLE(ASSEMBLER) && OS(LINUX)
+
+#include <elf.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <wtf/MonotonicTime.h>
+#include <wtf/PageBlock.h>
+#include <wtf/ProcessID.h>
+
+namespace JSC {
+
+namespace PerfLogInternal {
+static constexpr bool verbose = false;
+} // namespace PerfLogInternal
+
+namespace JITDump {
+namespace Constants {
+
+// Perf jit-dump formats are specified here.
+// https://raw.githubusercontent.com/torvalds/linux/master/tools/perf/Documentation/jitdump-specification.txt
+
+// The latest version 2, but it is too new at that time.
+static constexpr uint32_t version = 1;
+
+#if CPU(LITTLE_ENDIAN)
+static constexpr uint32_t magic = 0x4a695444;
+#else
+static constexpr uint32_t magic = 0x4454694a;
+#endif
+
+#if CPU(X86)
+static constexpr uint32_t elfMachine = EM_386;
+#elif CPU(X86_64)
+static constexpr uint32_t elfMachine = EM_X86_64;
+#elif CPU(ARM64)
+static constexpr uint32_t elfMachine = EM_AARCH64;
+#elif CPU(ARM)
+static constexpr uint32_t elfMachine = EM_ARM;
+#elif CPU(MIPS)
+#if CPU(LITTLE_ENDIAN)
+static constexpr uint32_t elfMachine = EM_MIPS_RS3_LE;
+#else
+static constexpr uint32_t elfMachine = EM_MIPS;
+#endif
+#endif
+
+} // namespace Constants
+
+struct FileHeader {
+ uint32_t magic { Constants::magic };
+ uint32_t version { Constants::version };
+ uint32_t totalSize { sizeof(FileHeader) };
+ uint32_t elfMachine { Constants::elfMachine };
+ uint32_t padding1 { 0 };
+ uint32_t pid { 0 };
+ uint64_t timestamp { 0 };
+ uint64_t flags { 0 };
+};
+
+enum class RecordType : uint32_t {
+ JITCodeLoad = 0,
+ JITCodeMove = 1,
+ JITCodeDebugInfo = 2,
+ JITCodeClose = 3,
+ JITCodeUnwindingInfo = 4,
+};
+
+struct RecordHeader {
+ RecordType type { RecordType::JITCodeLoad };
+ uint32_t totalSize { 0 };
+ uint64_t timestamp { 0 };
+};
+
+struct CodeLoadRecord {
+ RecordHeader header {
+ RecordType::JITCodeLoad,
+ 0,
+ 0,
+ };
+ uint32_t pid { 0 };
+ uint32_t tid { 0 };
+ uint64_t vma { 0 };
+ uint64_t codeAddress { 0 };
+ uint64_t codeSize { 0 };
+ uint64_t codeIndex { 0 };
+};
+
+} // namespace JITDump
+
+PerfLog& PerfLog::singleton()
+{
+ static PerfLog* logger;
+ static std::once_flag onceKey;
+ std::call_once(onceKey, [] {
+ logger = new PerfLog;
+ });
+ return *logger;
+}
+
+static inline uint64_t generateTimestamp()
+{
+ return MonotonicTime::now().secondsSinceEpoch().nanosecondsAs<uint64_t>();
+}
+
+static inline pid_t getCurrentThreadID()
+{
+ return static_cast<pid_t>(syscall(__NR_gettid));
+}
+
+PerfLog::PerfLog()
+{
+ {
+ std::array<char, 1024> filename;
+ snprintf(filename.data(), filename.size() - 1, "jit-%d.dump", getCurrentProcessID());
+ filename[filename.size() - 1] = '\0';
+ m_fd = open(filename.data(), O_CREAT | O_TRUNC | O_RDWR, 0666);
+ RELEASE_ASSERT(m_fd != -1);
+
+ // Linux perf command records this mmap operation in perf.data as a metadata to the JIT perf annotations.
+ // We do not use this mmap-ed memory region actually.
+ m_marker = mmap(nullptr, pageSize(), PROT_READ | PROT_EXEC, MAP_PRIVATE, m_fd, 0);
+ RELEASE_ASSERT(m_marker != MAP_FAILED);
+
+ m_file = fdopen(m_fd, "wb");
+ RELEASE_ASSERT(m_file);
+ }
+
+ JITDump::FileHeader header;
+ header.timestamp = generateTimestamp();
+ header.pid = getCurrentProcessID();
+
+ auto locker = holdLock(m_lock);
+ write(locker, &header, sizeof(JITDump::FileHeader));
+ flush(locker);
+}
+
+void PerfLog::write(const AbstractLocker&, const void* data, size_t size)
+{
+ size_t result = fwrite(data, 1, size, m_file);
+ RELEASE_ASSERT(result == size);
+}
+
+void PerfLog::flush(const AbstractLocker&)
+{
+ fflush(m_file);
+}
+
+void PerfLog::log(CString&& name, const uint8_t* executableAddress, size_t size)
+{
+ if (!size) {
+ dataLogLnIf(PerfLogInternal::verbose, "0 size record ", name, " ", RawPointer(executableAddress));
+ return;
+ }
+
+ PerfLog& logger = singleton();
+ auto locker = holdLock(logger.m_lock);
+
+ JITDump::CodeLoadRecord record;
+ record.header.timestamp = generateTimestamp();
+ record.header.totalSize = sizeof(JITDump::CodeLoadRecord) + (name.length() + 1) + size;
+ record.pid = getCurrentProcessID();
+ record.tid = getCurrentThreadID();
+ record.vma = bitwise_cast<uintptr_t>(executableAddress);
+ record.codeAddress = bitwise_cast<uintptr_t>(executableAddress);
+ record.codeSize = size;
+ record.codeIndex = logger.m_codeIndex++;
+
+ logger.write(locker, &record, sizeof(JITDump::CodeLoadRecord));
+ logger.write(locker, name.data(), name.length() + 1);
+ logger.write(locker, executableAddress, size);
+ logger.flush(locker);
+
+ dataLogLnIf(PerfLogInternal::verbose, name, " [", record.codeIndex, "] ", RawPointer(executableAddress), "-", RawPointer(executableAddress + size), " ", size);
+}
+
+} // namespace JSC
+
+#endif // ENABLE(ASSEMBLER) && OS(LINUX)
Added: trunk/Source/_javascript_Core/assembler/PerfLog.h (0 => 236883)
--- trunk/Source/_javascript_Core/assembler/PerfLog.h (rev 0)
+++ trunk/Source/_javascript_Core/assembler/PerfLog.h 2018-10-05 19:59:04 UTC (rev 236883)
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 Yusuke Suzuki <[email protected]>.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#if ENABLE(ASSEMBLER) && OS(LINUX)
+
+#include <stdio.h>
+#include <wtf/Lock.h>
+
+namespace JSC {
+
+class PerfLog {
+ WTF_MAKE_FAST_ALLOCATED;
+ WTF_MAKE_NONCOPYABLE(PerfLog);
+public:
+ static void log(CString&&, const uint8_t* executableAddress, size_t);
+
+private:
+ PerfLog();
+ static PerfLog& singleton();
+
+ void write(const AbstractLocker&, const void*, size_t);
+ void flush(const AbstractLocker&);
+
+ FILE* m_file { nullptr };
+ void* m_marker { nullptr };
+ uint64_t m_codeIndex { 0 };
+ int m_fd { -1 };
+ Lock m_lock;
+};
+
+} // namespace JSC
+
+#endif // ENABLE(ASSEMBLER) && OS(LINUX)
Modified: trunk/Source/_javascript_Core/jit/ExecutableAllocator.cpp (236882 => 236883)
--- trunk/Source/_javascript_Core/jit/ExecutableAllocator.cpp 2018-10-05 19:30:42 UTC (rev 236882)
+++ trunk/Source/_javascript_Core/jit/ExecutableAllocator.cpp 2018-10-05 19:59:04 UTC (rev 236883)
@@ -137,7 +137,19 @@
else
reservationSize = fixedExecutableMemoryPoolSize;
reservationSize = std::max(roundUpToMultipleOf(pageSize(), reservationSize), pageSize() * 2);
- m_reservation = PageReservation::reserveWithGuardPages(reservationSize, OSAllocator::JSJITCodePages, EXECUTABLE_POOL_WRITABLE, true);
+
+ auto tryCreatePageReservation = [] (size_t reservationSize) {
+#if OS(LINUX)
+ // If we use uncommitted reservation, mmap operation is recorded with small page size in perf command's output.
+ // This makes the following JIT code logging broken and some of JIT code is not recorded correctly.
+ // To avoid this problem, we use committed reservation if we need perf JITDump logging.
+ if (Options::logJITCodeForPerf())
+ return PageReservation::reserveAndCommitWithGuardPages(reservationSize, OSAllocator::JSJITCodePages, EXECUTABLE_POOL_WRITABLE, true);
+#endif
+ return PageReservation::reserveWithGuardPages(reservationSize, OSAllocator::JSJITCodePages, EXECUTABLE_POOL_WRITABLE, true);
+ };
+
+ m_reservation = tryCreatePageReservation(reservationSize);
if (m_reservation) {
ASSERT(m_reservation.size() == reservationSize);
void* reservationBase = m_reservation.base();
Modified: trunk/Source/_javascript_Core/runtime/Options.cpp (236882 => 236883)
--- trunk/Source/_javascript_Core/runtime/Options.cpp 2018-10-05 19:30:42 UTC (rev 236882)
+++ trunk/Source/_javascript_Core/runtime/Options.cpp 2018-10-05 19:59:04 UTC (rev 236883)
@@ -159,6 +159,10 @@
if (id == useSigillCrashAnalyzerID)
return true;
#endif
+#if ENABLE(ASSEMBLER) && OS(LINUX)
+ if (id == logJITCodeForPerfID)
+ return true;
+#endif
if (id == traceLLIntExecutionID)
return !!LLINT_TRACING;
if (id == traceLLIntSlowPathID)
Modified: trunk/Source/_javascript_Core/runtime/Options.h (236882 => 236883)
--- trunk/Source/_javascript_Core/runtime/Options.h 2018-10-05 19:30:42 UTC (rev 236882)
+++ trunk/Source/_javascript_Core/runtime/Options.h 2018-10-05 19:59:04 UTC (rev 236883)
@@ -179,6 +179,7 @@
v(bool, dumpFTLDisassembly, false, Normal, "dumps disassembly of FTL function upon compilation") \
v(bool, dumpRegExpDisassembly, false, Normal, "dumps disassembly of RegExp upon compilation") \
v(bool, dumpAllDFGNodes, false, Normal, nullptr) \
+ v(bool, logJITCodeForPerf, false, Configurable, nullptr) \
v(optionRange, bytecodeRangeToJITCompile, 0, Normal, "bytecode size range to allow compilation on, e.g. 1:100") \
v(optionRange, bytecodeRangeToDFGCompile, 0, Normal, "bytecode size range to allow DFG compilation on, e.g. 1:100") \
v(optionRange, bytecodeRangeToFTLCompile, 0, Normal, "bytecode size range to allow FTL compilation on, e.g. 1:100") \
Modified: trunk/Source/WTF/ChangeLog (236882 => 236883)
--- trunk/Source/WTF/ChangeLog 2018-10-05 19:30:42 UTC (rev 236882)
+++ trunk/Source/WTF/ChangeLog 2018-10-05 19:59:04 UTC (rev 236883)
@@ -1,3 +1,13 @@
+2018-10-05 Yusuke Suzuki <[email protected]>
+
+ [JSC][Linux] Support Perf JITDump logging
+ https://bugs.webkit.org/show_bug.cgi?id=189893
+
+ Reviewed by Mark Lam.
+
+ * wtf/PageReservation.h:
+ (WTF::PageReservation::reserveAndCommitWithGuardPages):
+
2018-10-03 Dan Bernstein <[email protected]>
WTF part of [Xcode] Update some build settings as recommended by Xcode 10
Modified: trunk/Source/WTF/wtf/PageReservation.h (236882 => 236883)
--- trunk/Source/WTF/wtf/PageReservation.h 2018-10-05 19:30:42 UTC (rev 236882)
+++ trunk/Source/WTF/wtf/PageReservation.h 2018-10-05 19:59:04 UTC (rev 236883)
@@ -106,6 +106,12 @@
return PageReservation(OSAllocator::reserveUncommitted(size + pageSize() * 2, usage, writable, executable, true), size, writable, executable, true);
}
+ static PageReservation reserveAndCommitWithGuardPages(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false)
+ {
+ ASSERT(isPageAligned(size));
+ return PageReservation(OSAllocator::reserveAndCommit(size + pageSize() * 2, usage, writable, executable, true), size, writable, executable, true);
+ }
+
void deallocate()
{
ASSERT(!m_committed);