/*
- * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2009, 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
*/
#include "config.h"
-
#include "ExecutableAllocator.h"
+#include "JSCInlines.h"
+
#if ENABLE(EXECUTABLE_ALLOCATOR_FIXED)
#include "CodeProfiling.h"
+#include "ExecutableAllocationFuzz.h"
#include <errno.h>
-#include <sys/mman.h>
+#if !PLATFORM(WIN)
#include <unistd.h>
+#endif
#include <wtf/MetaAllocator.h>
#include <wtf/PageReservation.h>
#include <wtf/VMTags.h>
+#if OS(DARWIN)
+#include <sys/mman.h>
+#endif
+
#if OS(LINUX)
#include <stdio.h>
#endif
- #define MMAP_FLAGS (MAP_PRIVATE | MAP_ANON | MAP_JIT)
-
using namespace WTF;
namespace JSC {
-#if CPU(ARM)
-static const size_t fixedPoolSize = 16 * 1024 * 1024;
-#elif CPU(X86_64)
-static const size_t fixedPoolSize = 1024 * 1024 * 1024;
-#else
-static const size_t fixedPoolSize = 32 * 1024 * 1024;
-#endif
+uintptr_t startOfFixedExecutableMemoryPool;
class FixedVMPoolExecutableAllocator : public MetaAllocator {
+ WTF_MAKE_FAST_ALLOCATED;
public:
FixedVMPoolExecutableAllocator()
- : MetaAllocator(32) // round up all allocations to 32 bytes
+ : MetaAllocator(jitAllocationGranule) // round up all allocations to 32 bytes
{
- m_reservation = PageReservation::reserveWithGuardPages(fixedPoolSize, OSAllocator::JSJITCodePages, EXECUTABLE_POOL_WRITABLE, true);
-#if !(ENABLE(CLASSIC_INTERPRETER) || ENABLE(LLINT))
- if (!m_reservation)
- CRASH();
-#endif
+ size_t reservationSize;
+ if (Options::jitMemoryReservationSize())
+ reservationSize = Options::jitMemoryReservationSize();
+ else
+ reservationSize = fixedExecutableMemoryPoolSize;
+ m_reservation = PageReservation::reserveWithGuardPages(reservationSize, OSAllocator::JSJITCodePages, EXECUTABLE_POOL_WRITABLE, true);
if (m_reservation) {
- ASSERT(m_reservation.size() == fixedPoolSize);
+ ASSERT(m_reservation.size() == reservationSize);
addFreshFreeSpace(m_reservation.base(), m_reservation.size());
+
+ startOfFixedExecutableMemoryPool = reinterpret_cast<uintptr_t>(m_reservation.base());
}
}
+
+ virtual ~FixedVMPoolExecutableAllocator();
protected:
- virtual void* allocateNewSpace(size_t&)
+ virtual void* allocateNewSpace(size_t&) override
{
// We're operating in a fixed pool, so new allocation is always prohibited.
return 0;
}
- virtual void notifyNeedPage(void* page)
+ virtual void notifyNeedPage(void* page) override
{
-#if OS(DARWIN)
+#if USE(MADV_FREE_FOR_JIT_MEMORY)
UNUSED_PARAM(page);
#else
m_reservation.commit(page, pageSize());
#endif
}
- virtual void notifyPageIsFree(void* page)
+ virtual void notifyPageIsFree(void* page) override
{
-#if OS(DARWIN)
+#if USE(MADV_FREE_FOR_JIT_MEMORY)
for (;;) {
int result = madvise(page, pageSize(), MADV_FREE);
if (!result)
return;
ASSERT(result == -1);
if (errno != EAGAIN) {
- ASSERT_NOT_REACHED(); // In debug mode, this should be a hard failure.
+ RELEASE_ASSERT_NOT_REACHED(); // In debug mode, this should be a hard failure.
break; // In release mode, we should just ignore the error - not returning memory to the OS is better than crashing, especially since we _will_ be able to reuse the memory internally anyway.
}
}
CodeProfiling::notifyAllocator(allocator);
}
-ExecutableAllocator::ExecutableAllocator(JSGlobalData&)
+ExecutableAllocator::ExecutableAllocator(VM&)
{
ASSERT(allocator);
}
{
}
+FixedVMPoolExecutableAllocator::~FixedVMPoolExecutableAllocator()
+{
+ m_reservation.deallocate();
+}
+
bool ExecutableAllocator::isValid() const
{
return !!allocator->bytesReserved();
MetaAllocator::Statistics statistics = allocator->currentStatistics();
ASSERT(statistics.bytesAllocated <= statistics.bytesReserved);
size_t bytesAllocated = statistics.bytesAllocated + addedMemoryUsage;
- if (bytesAllocated >= statistics.bytesReserved)
- bytesAllocated = statistics.bytesReserved;
+ size_t bytesAvailable = static_cast<size_t>(
+ statistics.bytesReserved * (1 - executablePoolReservationFraction));
+ if (bytesAllocated >= bytesAvailable)
+ bytesAllocated = bytesAvailable;
double result = 1.0;
- size_t divisor = statistics.bytesReserved - bytesAllocated;
+ size_t divisor = bytesAvailable - bytesAllocated;
if (divisor)
- result = static_cast<double>(statistics.bytesReserved) / divisor;
+ result = static_cast<double>(bytesAvailable) / divisor;
if (result < 1.0)
result = 1.0;
return result;
}
-PassRefPtr<ExecutableMemoryHandle> ExecutableAllocator::allocate(JSGlobalData& globalData, size_t sizeInBytes, void* ownerUID, JITCompilationEffort effort)
+RefPtr<ExecutableMemoryHandle> ExecutableAllocator::allocate(VM&, size_t sizeInBytes, void* ownerUID, JITCompilationEffort effort)
{
+ if (effort != JITCompilationCanFail && Options::reportMustSucceedExecutableAllocations()) {
+ dataLog("Allocating ", sizeInBytes, " bytes of executable memory with JITCompilationMustSucceed.\n");
+ WTFReportBacktrace();
+ }
+
+ if (effort == JITCompilationCanFail
+ && doExecutableAllocationFuzzingIfEnabled() == PretendToFailExecutableAllocation)
+ return nullptr;
+
+ if (effort == JITCompilationCanFail) {
+ // Don't allow allocations if we are down to reserve.
+ MetaAllocator::Statistics statistics = allocator->currentStatistics();
+ size_t bytesAllocated = statistics.bytesAllocated + sizeInBytes;
+ size_t bytesAvailable = static_cast<size_t>(
+ statistics.bytesReserved * (1 - executablePoolReservationFraction));
+ if (bytesAllocated > bytesAvailable)
+ return nullptr;
+ }
+
RefPtr<ExecutableMemoryHandle> result = allocator->allocate(sizeInBytes, ownerUID);
if (!result) {
- if (effort == JITCompilationCanFail)
- return result;
- releaseExecutableMemory(globalData);
- result = allocator->allocate(sizeInBytes, ownerUID);
- if (!result)
+ if (effort != JITCompilationCanFail) {
+ dataLog("Ran out of executable memory while allocating ", sizeInBytes, " bytes.\n");
CRASH();
+ }
+ return nullptr;
}
- return result.release();
+ return result;
}
size_t ExecutableAllocator::committedByteCount()
#endif // ENABLE(EXECUTABLE_ALLOCATOR_FIXED)
-
-#if !ENABLE(ASSEMBLER)
-// FIXME: Needed to satisfy JavaScriptCore.exp requirements when building only the interpreter.
-namespace JSC {
-size_t ExecutableAllocator::committedByteCount()
-{
- return 0;
-}
-} // namespace JSC
-#endif // !ENABLE(JIT)