Skip to content
Permalink
Browse files
Fixed VM pool allocation should have a reserve for allocations that c…
…annot fail

https://bugs.webkit.org/show_bug.cgi?id=147154
rdar://problem/21847618

Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

This adds the notion of a JIT pool reserve fraction. Some fraction, currently 1/4, of
the JIT pool is reserved for allocations that cannot fail. It makes sense to make this
a fraction rather than a constant because each allocation that can fail may cause some
number of allocations that cannot fail (for example, the OSR exit thunks that we
compile when we exit from some CodeBlock cannot fail).

I've tested this by adding a test mode where we artificially limit the JIT pool size.
Prior to the fix, we had >20 failures. Now we have none.

* heap/GCLogging.cpp:
(WTF::printInternal): I needed a dump method on Options members when debugging this.
* heap/GCLogging.h:
* jit/ExecutableAllocator.h: Raise the ARM64 limit to 32MB because 16MB is cutting it too close.
* jit/ExecutableAllocatorFixedVMPool.cpp:
(JSC::FixedVMPoolExecutableAllocator::FixedVMPoolExecutableAllocator): Add the ability to artificially limit JIT pool size for testing.
(JSC::ExecutableAllocator::memoryPressureMultiplier): Implement the reserve when computing memory pressure for JIT tier-up heuristics.
(JSC::ExecutableAllocator::allocate): Implement the reserve when allocating can-fail things.
* jsc.cpp: Rewire some options parsing so that CommandLine happens before we create the JIT pool.
(main):
(CommandLine::parseArguments):
(jscmain):
* runtime/Options.cpp:
(JSC::OptionRange::dump): I needed a dump method on Options members when debugging this.
(JSC::Options::initialize): This can now be called more than once.
* runtime/Options.h:

Tools:

Add a new test mode where we artificially limit JIT memory to 50KB. If our JIT OOM
mitigations work, these should all pass. Prior to this patch there were >20 failures.

* Scripts/run-jsc-stress-tests:



Canonical link: https://commits.webkit.org/165205@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@187125 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
pizlonator committed Jul 21, 2015
1 parent b27abfe commit b9ee90212183002cd52e680b51ffe58ec0fce377
@@ -1,3 +1,37 @@
2015-07-21 Filip Pizlo <fpizlo@apple.com>

Fixed VM pool allocation should have a reserve for allocations that cannot fail
https://bugs.webkit.org/show_bug.cgi?id=147154
rdar://problem/21847618

Reviewed by Geoffrey Garen.

This adds the notion of a JIT pool reserve fraction. Some fraction, currently 1/4, of
the JIT pool is reserved for allocations that cannot fail. It makes sense to make this
a fraction rather than a constant because each allocation that can fail may cause some
number of allocations that cannot fail (for example, the OSR exit thunks that we
compile when we exit from some CodeBlock cannot fail).

I've tested this by adding a test mode where we artificially limit the JIT pool size.
Prior to the fix, we had >20 failures. Now we have none.

* heap/GCLogging.cpp:
(WTF::printInternal): I needed a dump method on Options members when debugging this.
* heap/GCLogging.h:
* jit/ExecutableAllocator.h: Raise the ARM64 limit to 32MB because 16MB is cutting it too close.
* jit/ExecutableAllocatorFixedVMPool.cpp:
(JSC::FixedVMPoolExecutableAllocator::FixedVMPoolExecutableAllocator): Add the ability to artificially limit JIT pool size for testing.
(JSC::ExecutableAllocator::memoryPressureMultiplier): Implement the reserve when computing memory pressure for JIT tier-up heuristics.
(JSC::ExecutableAllocator::allocate): Implement the reserve when allocating can-fail things.
* jsc.cpp: Rewire some options parsing so that CommandLine happens before we create the JIT pool.
(main):
(CommandLine::parseArguments):
(jscmain):
* runtime/Options.cpp:
(JSC::OptionRange::dump): I needed a dump method on Options members when debugging this.
(JSC::Options::initialize): This can now be called more than once.
* runtime/Options.h:

2015-07-21 Saam barati <saambarati1@gmail.com>

ObjectPatternNode's entry should use "const Identifier&" instead of "Identifier"
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2014 Apple Inc. All rights reserved.
* Copyright (C) 2014, 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
@@ -31,6 +31,7 @@
#include "HeapIterationScope.h"
#include "JSCell.h"
#include "JSCellInlines.h"
#include <wtf/PrintStream.h>

namespace JSC {

@@ -113,3 +114,26 @@ void GCLogging::dumpObjectGraph(Heap* heap)
}

} // namespace JSC

namespace WTF {

void printInternal(PrintStream& out, JSC::GCLogging::Level level)
{
switch (level) {
case JSC::GCLogging::Level::None:
out.print("None");
return;
case JSC::GCLogging::Level::Basic:
out.print("Basic");
return;
case JSC::GCLogging::Level::Verbose:
out.print("Verbose");
return;
default:
out.print("Level=", level - JSC::GCLogging::Level::None);
return;
}
}

} // namespace WTF

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2014 Apple Inc. All rights reserved.
* Copyright (C) 2014, 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
@@ -48,4 +48,12 @@ typedef GCLogging::Level gcLogLevel;

} // namespace JSC

namespace WTF {

class PrintStream;

void printInternal(PrintStream&, JSC::GCLogging::Level);

} // namespace WTF

#endif // GCLogging_h
@@ -80,13 +80,16 @@ class DemandExecutableAllocator;
#endif

#if ENABLE(EXECUTABLE_ALLOCATOR_FIXED)
#if CPU(ARM) || CPU(ARM64)
#if CPU(ARM)
static const size_t fixedExecutableMemoryPoolSize = 16 * 1024 * 1024;
#elif CPU(ARM64)
static const size_t fixedExecutableMemoryPoolSize = 32 * 1024 * 1024;
#elif CPU(X86_64)
static const size_t fixedExecutableMemoryPoolSize = 1024 * 1024 * 1024;
#else
static const size_t fixedExecutableMemoryPoolSize = 32 * 1024 * 1024;
#endif
static const double executablePoolReservationFraction = 0.25;

extern uintptr_t startOfFixedExecutableMemoryPool;
#endif
@@ -1,5 +1,5 @@
/*
* 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
@@ -60,9 +60,14 @@ class FixedVMPoolExecutableAllocator : public MetaAllocator {
FixedVMPoolExecutableAllocator()
: MetaAllocator(jitAllocationGranule) // round up all allocations to 32 bytes
{
m_reservation = PageReservation::reserveWithGuardPages(fixedExecutableMemoryPoolSize, OSAllocator::JSJITCodePages, EXECUTABLE_POOL_WRITABLE, true);
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() == fixedExecutableMemoryPoolSize);
ASSERT(m_reservation.size() == reservationSize);
addFreshFreeSpace(m_reservation.base(), m_reservation.size());

startOfFixedExecutableMemoryPool = reinterpret_cast<uintptr_t>(m_reservation.base());
@@ -148,12 +153,14 @@ double ExecutableAllocator::memoryPressureMultiplier(size_t addedMemoryUsage)
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;
@@ -170,6 +177,16 @@ RefPtr<ExecutableMemoryHandle> ExecutableAllocator::allocate(VM&, size_t sizeInB
&& 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) {
@@ -1248,12 +1248,6 @@ int main(int argc, char** argv)
ecore_init();
#endif

// Initialize JSC before getting VM.
#if ENABLE(SAMPLING_REGIONS)
WTF::initializeMainThread();
#endif
JSC::initializeThreading();

if (char* timeoutString = getenv("JSC_timeout")) {
if (sscanf(timeoutString, "%lf", &s_desiredTimeout) != 1) {
dataLog(
@@ -1430,6 +1424,8 @@ static NO_RETURN void printUsageStatement(bool help = false)

void CommandLine::parseArguments(int argc, char** argv)
{
Options::initialize();

int i = 1;
bool needToDumpOptions = false;
bool needToExit = false;
@@ -1494,8 +1490,7 @@ void CommandLine::parseArguments(int argc, char** argv)
}

// See if the -- option is a JSC VM option.
// NOTE: At this point, we know that the arg starts with "--". Skip it.
if (JSC::Options::setOption(&arg[2])) {
if (strstr(arg, "--") == arg && JSC::Options::setOption(&arg[2])) {
// The arg was recognized as a VM option and has been parsed.
continue; // Just continue with the next arg.
}
@@ -1523,6 +1518,13 @@ int jscmain(int argc, char** argv)
// Note that the options parsing can affect VM creation, and thus
// comes first.
CommandLine options(argc, argv);

// Initialize JSC before getting VM.
#if ENABLE(SAMPLING_REGIONS)
WTF::initializeMainThread();
#endif
JSC::initializeThreading();

VM* vm = &VM::create(LargeHeap).leakRef();
int result;
{
@@ -30,6 +30,7 @@
#include <algorithm>
#include <limits>
#include <math.h>
#include <mutex>
#include <stdlib.h>
#include <string.h>
#include <wtf/DataLog.h>
@@ -201,6 +202,11 @@ bool OptionRange::isInRange(unsigned count)
return m_state == Normal ? false : true;
}

void OptionRange::dump(PrintStream& out) const
{
out.print(m_rangeString);
}

Options::Entry Options::s_options[Options::numberOfOptions];
Options::Entry Options::s_defaultOptions[Options::numberOfOptions];

@@ -318,60 +324,66 @@ static void recomputeDependentOptions()

void Options::initialize()
{
// Initialize each of the options with their default values:
#define FOR_EACH_OPTION(type_, name_, defaultValue_, description_) \
name_() = defaultValue_; \
name_##Default() = defaultValue_;
JSC_OPTIONS(FOR_EACH_OPTION)
static std::once_flag initializeOptionsOnceFlag;

std::call_once(
initializeOptionsOnceFlag,
[] {
// Initialize each of the options with their default values:
#define FOR_EACH_OPTION(type_, name_, defaultValue_, description_) \
name_() = defaultValue_; \
name_##Default() = defaultValue_;
JSC_OPTIONS(FOR_EACH_OPTION)
#undef FOR_EACH_OPTION

// It *probably* makes sense for other platforms to enable this.
// It *probably* makes sense for other platforms to enable this.
#if PLATFORM(IOS) && CPU(ARM64)
enableLLVMFastISel() = true;
enableLLVMFastISel() = true;
#endif

// Allow environment vars to override options if applicable.
// The evn var should be the name of the option prefixed with
// "JSC_".
#define FOR_EACH_OPTION(type_, name_, defaultValue_, description_) \
overrideOptionWithHeuristic(name_(), "JSC_" #name_);
JSC_OPTIONS(FOR_EACH_OPTION)
// Allow environment vars to override options if applicable.
// The evn var should be the name of the option prefixed with
// "JSC_".
#define FOR_EACH_OPTION(type_, name_, defaultValue_, description_) \
overrideOptionWithHeuristic(name_(), "JSC_" #name_);
JSC_OPTIONS(FOR_EACH_OPTION)
#undef FOR_EACH_OPTION

#if 0
; // Deconfuse editors that do auto indentation
; // Deconfuse editors that do auto indentation
#endif

recomputeDependentOptions();

// Do range checks where needed and make corrections to the options:
ASSERT(Options::thresholdForOptimizeAfterLongWarmUp() >= Options::thresholdForOptimizeAfterWarmUp());
ASSERT(Options::thresholdForOptimizeAfterWarmUp() >= Options::thresholdForOptimizeSoon());
ASSERT(Options::thresholdForOptimizeAfterWarmUp() >= 0);

if (Options::showOptions()) {
DumpLevel level = static_cast<DumpLevel>(Options::showOptions());
if (level > DumpLevel::Verbose)
level = DumpLevel::Verbose;

const char* title = nullptr;
switch (level) {
case DumpLevel::None:
break;
case DumpLevel::Overridden:
title = "Overridden JSC options:";
break;
case DumpLevel::All:
title = "All JSC options:";
break;
case DumpLevel::Verbose:
title = "All JSC options with descriptions:";
break;
}
dumpAllOptions(level, title);
}

ensureOptionsAreCoherent();
recomputeDependentOptions();

// Do range checks where needed and make corrections to the options:
ASSERT(Options::thresholdForOptimizeAfterLongWarmUp() >= Options::thresholdForOptimizeAfterWarmUp());
ASSERT(Options::thresholdForOptimizeAfterWarmUp() >= Options::thresholdForOptimizeSoon());
ASSERT(Options::thresholdForOptimizeAfterWarmUp() >= 0);

if (Options::showOptions()) {
DumpLevel level = static_cast<DumpLevel>(Options::showOptions());
if (level > DumpLevel::Verbose)
level = DumpLevel::Verbose;

const char* title = nullptr;
switch (level) {
case DumpLevel::None:
break;
case DumpLevel::Overridden:
title = "Overridden JSC options:";
break;
case DumpLevel::All:
title = "All JSC options:";
break;
case DumpLevel::Verbose:
title = "All JSC options with descriptions:";
break;
}
dumpAllOptions(level, title);
}

ensureOptionsAreCoherent();
});
}

// Parses a single command line option in the format "<optionName>=<value>"
@@ -30,6 +30,7 @@
#include "JSExportMacros.h"
#include <stdint.h>
#include <stdio.h>
#include <wtf/PrintStream.h>
#include <wtf/StdLibExtras.h>

namespace JSC {
@@ -80,6 +81,8 @@ class OptionRange {
bool init(const char*);
bool isInRange(unsigned);
const char* rangeString() const { return (m_state > InitError) ? m_rangeString : "<null>"; }

void dump(PrintStream& out) const;

private:
RangeState m_state;
@@ -106,6 +109,7 @@ typedef const char* optionString;
v(unsigned, errorModeReservedZoneSize, 64 * KB, nullptr) \
\
v(bool, crashIfCantAllocateJITMemory, false, nullptr) \
v(unsigned, jitMemoryReservationSize, 0, nullptr) \
\
v(bool, forceDFGCodeBlockLiveness, false, nullptr) \
v(bool, forceICFailure, false, nullptr) \
@@ -350,7 +354,7 @@ class Options {
gcLogLevelType,
};

static void initialize();
JS_EXPORT_PRIVATE static void initialize();

// Parses a single command line option in the format "<optionName>=<value>"
// (no spaces allowed) and set the specified option if appropriate.

0 comments on commit b9ee902

Please sign in to comment.