The tracking of the coarse-grain Heap state (allocating or not, collector or not, eden vs full) should respect the orthogonality between allocating and collecting
https://bugs.webkit.org/show_bug.cgi?id=163738

Reviewed by Geoffrey Garen.
Source/JavaScriptCore:

        
We need to know if we're currently in an allocation slow path, so that code can assert that
it's not being used from inside a destructor that runs during a sweep. We need to know if
we're currently collecting, because some code behaves differently during collection, and
other code wants to assert that it's not being used from inside a visitChildren method that
runs during marking. If we are collecting, we need to know if it's an eden collection or a
full collection. If we are requesting a collection, we need to know if we're requesting an
eden collection, a full collection, or any kind of collection.
        
Prior to this change, you would reason about all of these things using the HeapOperation. It
had the following states: NoOperation, Allocation, FullCollection, EdenCollection, and
AnyCollection. NoOperation versus Allocation was primarily for asserting that sweep didn't
call arbitrary JS. FullCollection versus EdenCollection was about describing generations. We
would even use HeapOperation in places where we knew that it could only be either Full or
Eden, because we just needed a variable to tell us which generation we were talking about.
It was all very confusing.
        
Where it completely breaks down is the fact that a concurrent GC has two logical threads, the
mutator and the collector, which can change state independently. The mutator can be
allocating. It can also be doing some work to help the GC. That's three states: running,
allocating, or helping GC. At the same time, the GC thread could either be running or not,
and if it's running, it could be a full collection or an eden collection. Because the mutator
and collector can run at the same time, it means that if we used one enum, we would need nine
states: every combination of mutator running, allocating, or helping GC, crossed with
collector not running, running eden, or running full. So, this change decouples mutator state
from collector state and uses two separate fields with two different types.
        
Mutator state is described using MutatorState, which can be either MutatorState::Running,
MutatorState::Allocating, or MutatorState::HelpingGC.
        
Collector state is described using Optional<CollectionScope>. CollectionScope describes how
big the scope of the collection is, and it can be either CollectionScope::Eden or
CollectionScope::Full. If the Optional is Nullopt, it means that we are not collecting. This
way, you can treat collectionScope as a boolean (an Optional is true iff it's engaged). You
can pass around just a CollectionScope if you know that you must be collecting and you just
want to know about the generation. Also, we can use Nullopt in methods that request
collection, which those methods take to mean that they can run any kind of collection (the
old AnyCollection).
        
Another use of HeapOperation was to answer questions about whether the caller is running as
part of the GC or as part of the mutator. Optional<CollectionScope> does not answer this,
since code that runs in the mutator while the mutator is not HelpingGC at the same time as
the collector is running should run as if it was part of the mutator not as if it was part of
the GC. MutatorState is needed to answer this question, but it doesn't tell the whole story
since code that runs in the collector thread at the same time as the mutator is running
should run as if it was part of the GC not as if it was part of the mutator. So, we need to
know if we're on the collector thread or the mutator thread. We already have a WTF facility
for this, which answers if a thread is a GC thread. But we already use this to answer a
stronger question: are we part of the parallel GC helpers? Some functions in the GC, like
mark bit queries, will work fine in a concurrent collector thread so long as there is no
parallel marking. So, this change also changes WTF's mayBeGCThread to tell what kind of GC
thread we may be: either GCThreadType::Main or GCThreadType::Helper. The parallel GC safety
checks look for GCThreadType::Helper. The "should I run as mutator" query can now be answered
by checking with mayBeGCThread, which returns Optional<GCThreadType>; if engaged, then run as
GC, else run as GC if MutatorState is HelpingGC, else run as mutator.
        
This doesn't change the way that the GC behaves, but it does change how the GC represents a
fundamental piece of state. So, it's a big change. It should be perf-neutral (still testing).

* API/JSBase.cpp:
(JSSynchronousEdenCollectForDebugging):
* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::jettison):
* dfg/DFGWorklist.cpp:
* ftl/FTLCompile.cpp:
(JSC::FTL::compile):
* heap/AllocatingScope.h: Added.
(JSC::AllocatingScope::AllocatingScope):
(JSC::AllocatingScope::~AllocatingScope):
* heap/AllocationScope.h: Removed.
* heap/CodeBlockSet.cpp:
(JSC::CodeBlockSet::deleteUnmarkedAndUnreferenced):
* heap/CodeBlockSet.h:
* heap/CollectionScope.cpp: Added.
(JSC::collectionScopeName):
(WTF::printInternal):
* heap/CollectionScope.h: Added.
* heap/EdenGCActivityCallback.cpp:
(JSC::EdenGCActivityCallback::doCollection):
* heap/FullGCActivityCallback.cpp:
(JSC::FullGCActivityCallback::doCollection):
* heap/GCTypeMap.h:
(JSC::GCTypeMap::operator[]):
* heap/Heap.cpp:
(JSC::Heap::Heap):
(JSC::Heap::lastChanceToFinalize):
(JSC::Heap::markRoots):
(JSC::Heap::beginMarking):
(JSC::Heap::visitSmallStrings):
(JSC::Heap::updateObjectCounts):
(JSC::Heap::deleteAllCodeBlocks):
(JSC::Heap::deleteUnmarkedCompiledCode):
(JSC::Heap::collectAllGarbage):
(JSC::Heap::collect):
(JSC::Heap::collectWithoutAnySweep):
(JSC::Heap::collectImpl):
(JSC::Heap::willStartCollection):
(JSC::Heap::flushWriteBarrierBuffer):
(JSC::Heap::pruneStaleEntriesFromWeakGCMaps):
(JSC::Heap::notifyIncrementalSweeper):
(JSC::Heap::updateAllocationLimits):
(JSC::Heap::didFinishCollection):
(JSC::Heap::isValidAllocation):
(JSC::Heap::shouldDoFullCollection):
* heap/Heap.h:
(JSC::Heap::mutatorState):
(JSC::Heap::collectionScope):
(JSC::Heap::operationInProgress): Deleted.
* heap/HeapInlines.h:
(JSC::Heap::shouldCollect):
(JSC::Heap::isCurrentThreadBusy):
(JSC::Heap::isMarked):
(JSC::Heap::reportExtraMemoryVisited):
(JSC::Heap::reportExternalMemoryVisited):
(JSC::Heap::collectAccordingToDeferGCProbability):
(JSC::Heap::isBusy): Deleted.
(JSC::Heap::isCollecting): Deleted.
* heap/HeapObserver.h:
* heap/HeapOperation.cpp: Removed.
* heap/HeapOperation.h: Removed.
* heap/HeapVerifier.cpp:
(JSC::HeapVerifier::initializeGCCycle):
(JSC::HeapVerifier::reportObject):
(JSC::HeapVerifier::collectionTypeName): Deleted.
* heap/HeapVerifier.h:
(JSC::HeapVerifier::GCCycle::collectionTypeName): Deleted.
* heap/HelpingGCScope.h: Added.
(JSC::HelpingGCScope::HelpingGCScope):
(JSC::HelpingGCScope::~HelpingGCScope):
* heap/LargeAllocation.cpp:
(JSC::LargeAllocation::flip):
* heap/MarkedAllocator.cpp:
(JSC::MarkedAllocator::doTestCollectionsIfNeeded):
(JSC::MarkedAllocator::allocateSlowCaseImpl):
* heap/MarkedBlock.h:
* heap/MarkedSpace.cpp:
(JSC::MarkedSpace::prepareForAllocation):
(JSC::MarkedSpace::visitWeakSets):
(JSC::MarkedSpace::reapWeakSets):
(JSC::MarkedSpace::prepareForMarking):
(JSC::MarkedSpace::beginMarking):
(JSC::MarkedSpace::snapshotUnswept):
* heap/MutatorState.cpp: Added.
(WTF::printInternal):
* heap/MutatorState.h: Added.
* heap/SlotVisitor.cpp:
(JSC::SlotVisitor::didStartMarking):
* inspector/agents/InspectorHeapAgent.cpp:
(Inspector::protocolTypeForHeapOperation):
(Inspector::InspectorHeapAgent::didGarbageCollect):
* inspector/agents/InspectorHeapAgent.h:
* interpreter/Interpreter.cpp:
(JSC::Interpreter::execute):
(JSC::Interpreter::executeCall):
(JSC::Interpreter::executeConstruct):
(JSC::Interpreter::prepareForRepeatCall):
* jsc.cpp:
(functionFullGC):
(functionEdenGC):
* runtime/Completion.cpp:
(JSC::evaluate):
(JSC::loadAndEvaluateModule):
(JSC::loadModule):
(JSC::linkAndEvaluateModule):
* runtime/JSLock.cpp:
(JSC::JSLock::DropAllLocks::DropAllLocks):
* runtime/SmallStrings.h:
(JSC::SmallStrings::needsToBeVisited):
* runtime/VM.h:
(JSC::VM::isCollectorBusyOnCurrentThread):
(JSC::VM::isCollectorBusy): Deleted.
* tools/JSDollarVMPrototype.cpp:
(JSC::JSDollarVMPrototype::edenGC):

Source/WebCore:


No new tests because no change in behavior.

* bindings/js/GCController.cpp:
(WebCore::GCController::garbageCollectNow):

Source/WTF:

        
There will soon be different kinds of GC threads, and WTF's "are you a GC thread" thing
should know about this.

* wtf/MainThread.cpp:
(WTF::initializeGCThreads):
(WTF::registerGCThread):
(WTF::mayBeGCThread):
* wtf/MainThread.h:



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@207653 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/API/JSBase.cpp b/Source/JavaScriptCore/API/JSBase.cpp
index 6b2a907..cbad507 100644
--- a/Source/JavaScriptCore/API/JSBase.cpp
+++ b/Source/JavaScriptCore/API/JSBase.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006, 2007, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2006, 2007, 2013, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -165,7 +165,7 @@
 
     ExecState* exec = toJS(ctx);
     JSLockHolder locker(exec);
-    exec->vm().heap.collect(EdenCollection);
+    exec->vm().heap.collect(CollectionScope::Eden);
 }
 
 void JSDisableGCTimer(void)
diff --git a/Source/JavaScriptCore/CMakeLists.txt b/Source/JavaScriptCore/CMakeLists.txt
index 97741fe..395fe2c 100644
--- a/Source/JavaScriptCore/CMakeLists.txt
+++ b/Source/JavaScriptCore/CMakeLists.txt
@@ -451,6 +451,7 @@
 
     heap/AllocatorAttributes.cpp
     heap/CodeBlockSet.cpp
+    heap/CollectionScope.cpp
     heap/ConservativeRoots.cpp
     heap/DeferGC.cpp
     heap/DestructionMode.cpp
@@ -464,7 +465,6 @@
     heap/Heap.cpp
     heap/HeapCell.cpp
     heap/HeapHelperPool.cpp
-    heap/HeapOperation.cpp
     heap/HeapProfiler.cpp
     heap/HeapSnapshot.cpp
     heap/HeapSnapshotBuilder.cpp
@@ -480,6 +480,7 @@
     heap/MarkedAllocator.cpp
     heap/MarkedBlock.cpp
     heap/MarkedSpace.cpp
+    heap/MutatorState.cpp
     heap/SlotVisitor.cpp
     heap/Weak.cpp
     heap/WeakBlock.cpp
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 1dd5217..9d67564 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,185 @@
+2016-10-20  Filip Pizlo  <fpizlo@apple.com>
+
+        The tracking of the coarse-grain Heap state (allocating or not, collector or not, eden vs full) should respect the orthogonality between allocating and collecting
+        https://bugs.webkit.org/show_bug.cgi?id=163738
+
+        Reviewed by Geoffrey Garen.
+        
+        We need to know if we're currently in an allocation slow path, so that code can assert that
+        it's not being used from inside a destructor that runs during a sweep. We need to know if
+        we're currently collecting, because some code behaves differently during collection, and
+        other code wants to assert that it's not being used from inside a visitChildren method that
+        runs during marking. If we are collecting, we need to know if it's an eden collection or a
+        full collection. If we are requesting a collection, we need to know if we're requesting an
+        eden collection, a full collection, or any kind of collection.
+        
+        Prior to this change, you would reason about all of these things using the HeapOperation. It
+        had the following states: NoOperation, Allocation, FullCollection, EdenCollection, and
+        AnyCollection. NoOperation versus Allocation was primarily for asserting that sweep didn't
+        call arbitrary JS. FullCollection versus EdenCollection was about describing generations. We
+        would even use HeapOperation in places where we knew that it could only be either Full or
+        Eden, because we just needed a variable to tell us which generation we were talking about.
+        It was all very confusing.
+        
+        Where it completely breaks down is the fact that a concurrent GC has two logical threads, the
+        mutator and the collector, which can change state independently. The mutator can be
+        allocating. It can also be doing some work to help the GC. That's three states: running,
+        allocating, or helping GC. At the same time, the GC thread could either be running or not,
+        and if it's running, it could be a full collection or an eden collection. Because the mutator
+        and collector can run at the same time, it means that if we used one enum, we would need nine
+        states: every combination of mutator running, allocating, or helping GC, crossed with
+        collector not running, running eden, or running full. So, this change decouples mutator state
+        from collector state and uses two separate fields with two different types.
+        
+        Mutator state is described using MutatorState, which can be either MutatorState::Running,
+        MutatorState::Allocating, or MutatorState::HelpingGC.
+        
+        Collector state is described using Optional<CollectionScope>. CollectionScope describes how
+        big the scope of the collection is, and it can be either CollectionScope::Eden or
+        CollectionScope::Full. If the Optional is Nullopt, it means that we are not collecting. This
+        way, you can treat collectionScope as a boolean (an Optional is true iff it's engaged). You
+        can pass around just a CollectionScope if you know that you must be collecting and you just
+        want to know about the generation. Also, we can use Nullopt in methods that request
+        collection, which those methods take to mean that they can run any kind of collection (the
+        old AnyCollection).
+        
+        Another use of HeapOperation was to answer questions about whether the caller is running as
+        part of the GC or as part of the mutator. Optional<CollectionScope> does not answer this,
+        since code that runs in the mutator while the mutator is not HelpingGC at the same time as
+        the collector is running should run as if it was part of the mutator not as if it was part of
+        the GC. MutatorState is needed to answer this question, but it doesn't tell the whole story
+        since code that runs in the collector thread at the same time as the mutator is running
+        should run as if it was part of the GC not as if it was part of the mutator. So, we need to
+        know if we're on the collector thread or the mutator thread. We already have a WTF facility
+        for this, which answers if a thread is a GC thread. But we already use this to answer a
+        stronger question: are we part of the parallel GC helpers? Some functions in the GC, like
+        mark bit queries, will work fine in a concurrent collector thread so long as there is no
+        parallel marking. So, this change also changes WTF's mayBeGCThread to tell what kind of GC
+        thread we may be: either GCThreadType::Main or GCThreadType::Helper. The parallel GC safety
+        checks look for GCThreadType::Helper. The "should I run as mutator" query can now be answered
+        by checking with mayBeGCThread, which returns Optional<GCThreadType>; if engaged, then run as
+        GC, else run as GC if MutatorState is HelpingGC, else run as mutator.
+        
+        This doesn't change the way that the GC behaves, but it does change how the GC represents a
+        fundamental piece of state. So, it's a big change. It should be perf-neutral (still testing).
+
+        * API/JSBase.cpp:
+        (JSSynchronousEdenCollectForDebugging):
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::jettison):
+        * dfg/DFGWorklist.cpp:
+        * ftl/FTLCompile.cpp:
+        (JSC::FTL::compile):
+        * heap/AllocatingScope.h: Added.
+        (JSC::AllocatingScope::AllocatingScope):
+        (JSC::AllocatingScope::~AllocatingScope):
+        * heap/AllocationScope.h: Removed.
+        * heap/CodeBlockSet.cpp:
+        (JSC::CodeBlockSet::deleteUnmarkedAndUnreferenced):
+        * heap/CodeBlockSet.h:
+        * heap/CollectionScope.cpp: Added.
+        (JSC::collectionScopeName):
+        (WTF::printInternal):
+        * heap/CollectionScope.h: Added.
+        * heap/EdenGCActivityCallback.cpp:
+        (JSC::EdenGCActivityCallback::doCollection):
+        * heap/FullGCActivityCallback.cpp:
+        (JSC::FullGCActivityCallback::doCollection):
+        * heap/GCTypeMap.h:
+        (JSC::GCTypeMap::operator[]):
+        * heap/Heap.cpp:
+        (JSC::Heap::Heap):
+        (JSC::Heap::lastChanceToFinalize):
+        (JSC::Heap::markRoots):
+        (JSC::Heap::beginMarking):
+        (JSC::Heap::visitSmallStrings):
+        (JSC::Heap::updateObjectCounts):
+        (JSC::Heap::deleteAllCodeBlocks):
+        (JSC::Heap::deleteUnmarkedCompiledCode):
+        (JSC::Heap::collectAllGarbage):
+        (JSC::Heap::collect):
+        (JSC::Heap::collectWithoutAnySweep):
+        (JSC::Heap::collectImpl):
+        (JSC::Heap::willStartCollection):
+        (JSC::Heap::flushWriteBarrierBuffer):
+        (JSC::Heap::pruneStaleEntriesFromWeakGCMaps):
+        (JSC::Heap::notifyIncrementalSweeper):
+        (JSC::Heap::updateAllocationLimits):
+        (JSC::Heap::didFinishCollection):
+        (JSC::Heap::isValidAllocation):
+        (JSC::Heap::shouldDoFullCollection):
+        * heap/Heap.h:
+        (JSC::Heap::mutatorState):
+        (JSC::Heap::collectionScope):
+        (JSC::Heap::operationInProgress): Deleted.
+        * heap/HeapInlines.h:
+        (JSC::Heap::shouldCollect):
+        (JSC::Heap::isCurrentThreadBusy):
+        (JSC::Heap::isMarked):
+        (JSC::Heap::reportExtraMemoryVisited):
+        (JSC::Heap::reportExternalMemoryVisited):
+        (JSC::Heap::collectAccordingToDeferGCProbability):
+        (JSC::Heap::isBusy): Deleted.
+        (JSC::Heap::isCollecting): Deleted.
+        * heap/HeapObserver.h:
+        * heap/HeapOperation.cpp: Removed.
+        * heap/HeapOperation.h: Removed.
+        * heap/HeapVerifier.cpp:
+        (JSC::HeapVerifier::initializeGCCycle):
+        (JSC::HeapVerifier::reportObject):
+        (JSC::HeapVerifier::collectionTypeName): Deleted.
+        * heap/HeapVerifier.h:
+        (JSC::HeapVerifier::GCCycle::collectionTypeName): Deleted.
+        * heap/HelpingGCScope.h: Added.
+        (JSC::HelpingGCScope::HelpingGCScope):
+        (JSC::HelpingGCScope::~HelpingGCScope):
+        * heap/LargeAllocation.cpp:
+        (JSC::LargeAllocation::flip):
+        * heap/MarkedAllocator.cpp:
+        (JSC::MarkedAllocator::doTestCollectionsIfNeeded):
+        (JSC::MarkedAllocator::allocateSlowCaseImpl):
+        * heap/MarkedBlock.h:
+        * heap/MarkedSpace.cpp:
+        (JSC::MarkedSpace::prepareForAllocation):
+        (JSC::MarkedSpace::visitWeakSets):
+        (JSC::MarkedSpace::reapWeakSets):
+        (JSC::MarkedSpace::prepareForMarking):
+        (JSC::MarkedSpace::beginMarking):
+        (JSC::MarkedSpace::snapshotUnswept):
+        * heap/MutatorState.cpp: Added.
+        (WTF::printInternal):
+        * heap/MutatorState.h: Added.
+        * heap/SlotVisitor.cpp:
+        (JSC::SlotVisitor::didStartMarking):
+        * inspector/agents/InspectorHeapAgent.cpp:
+        (Inspector::protocolTypeForHeapOperation):
+        (Inspector::InspectorHeapAgent::didGarbageCollect):
+        * inspector/agents/InspectorHeapAgent.h:
+        * interpreter/Interpreter.cpp:
+        (JSC::Interpreter::execute):
+        (JSC::Interpreter::executeCall):
+        (JSC::Interpreter::executeConstruct):
+        (JSC::Interpreter::prepareForRepeatCall):
+        * jsc.cpp:
+        (functionFullGC):
+        (functionEdenGC):
+        * runtime/Completion.cpp:
+        (JSC::evaluate):
+        (JSC::loadAndEvaluateModule):
+        (JSC::loadModule):
+        (JSC::linkAndEvaluateModule):
+        * runtime/JSLock.cpp:
+        (JSC::JSLock::DropAllLocks::DropAllLocks):
+        * runtime/SmallStrings.h:
+        (JSC::SmallStrings::needsToBeVisited):
+        * runtime/VM.h:
+        (JSC::VM::isCollectorBusyOnCurrentThread):
+        (JSC::VM::isCollectorBusy): Deleted.
+        * tools/JSDollarVMPrototype.cpp:
+        (JSC::JSDollarVMPrototype::edenGC):
+
 2016-10-20  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [JSC] Drop isEnvironmentRecord type info flag and use JSType information instead
diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
index 0966e47..fd42b9c 100644
--- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
@@ -510,7 +510,6 @@
 		0F8335B71639C1E6001443B5 /* ArrayAllocationProfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F8335B41639C1E3001443B5 /* ArrayAllocationProfile.cpp */; };
 		0F8335B81639C1EA001443B5 /* ArrayAllocationProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F8335B51639C1E3001443B5 /* ArrayAllocationProfile.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		0F8364B7164B0C110053329A /* DFGBranchDirection.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F8364B5164B0C0E0053329A /* DFGBranchDirection.h */; };
-		0F86A26D1D6F796500CB0C92 /* HeapOperation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F86A26C1D6F796200CB0C92 /* HeapOperation.cpp */; };
 		0F86A26F1D6F7B3300CB0C92 /* GCTypeMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F86A26E1D6F7B3100CB0C92 /* GCTypeMap.h */; };
 		0F86AE201C5311C5006BE8EC /* B3ComputeDivisionMagic.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F86AE1F1C5311C5006BE8EC /* B3ComputeDivisionMagic.h */; };
 		0F885E111849A3BE00F1E3FA /* BytecodeUseDef.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F885E101849A3BE00F1E3FA /* BytecodeUseDef.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -592,6 +591,12 @@
 		0FA581BA150E952C00B9A2D9 /* DFGNodeFlags.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FA581B7150E952A00B9A2D9 /* DFGNodeFlags.cpp */; };
 		0FA581BB150E953000B9A2D9 /* DFGNodeFlags.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FA581B8150E952A00B9A2D9 /* DFGNodeFlags.h */; };
 		0FA581BC150E953000B9A2D9 /* DFGNodeType.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FA581B9150E952A00B9A2D9 /* DFGNodeType.h */; };
+		0FA762041DB9242600B7A2FD /* CollectionScope.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FA762001DB9242300B7A2FD /* CollectionScope.cpp */; };
+		0FA762051DB9242900B7A2FD /* CollectionScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FA762011DB9242300B7A2FD /* CollectionScope.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		0FA762061DB9243100B7A2FD /* MutatorState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FA762021DB9242300B7A2FD /* MutatorState.cpp */; };
+		0FA762071DB9243300B7A2FD /* MutatorState.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FA762031DB9242300B7A2FD /* MutatorState.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		0FA762091DB9283E00B7A2FD /* HelpingGCScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FA762081DB9283C00B7A2FD /* HelpingGCScope.h */; };
+		0FA7620B1DB959F900B7A2FD /* AllocatingScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FA7620A1DB959F600B7A2FD /* AllocatingScope.h */; };
 		0FA7A8EB18B413C80052371D /* Reg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FA7A8E918B413C80052371D /* Reg.cpp */; };
 		0FA7A8EC18B413C80052371D /* Reg.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FA7A8EA18B413C80052371D /* Reg.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		0FA7A8EE18CE4FD80052371D /* ScratchRegisterAllocator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FA7A8ED18CE4FD80052371D /* ScratchRegisterAllocator.cpp */; };
@@ -623,7 +628,6 @@
 		0FB4FB731BC843140025CA5A /* FTLLazySlowPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FB4FB701BC843140025CA5A /* FTLLazySlowPath.cpp */; };
 		0FB4FB741BC843140025CA5A /* FTLLazySlowPath.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB4FB711BC843140025CA5A /* FTLLazySlowPath.h */; };
 		0FB4FB751BC843140025CA5A /* FTLLazySlowPathCall.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB4FB721BC843140025CA5A /* FTLLazySlowPathCall.h */; };
-		0FB530391D90404C00F28AE3 /* AllocationScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB530381D90404600F28AE3 /* AllocationScope.h */; };
 		0FB5467714F59B5C002C2989 /* LazyOperandValueProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB5467614F59AD1002C2989 /* LazyOperandValueProfile.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		0FB5467914F5C46B002C2989 /* LazyOperandValueProfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FB5467814F5C468002C2989 /* LazyOperandValueProfile.cpp */; };
 		0FB5467B14F5C7E1002C2989 /* MethodOfGettingAValueProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB5467A14F5C7D4002C2989 /* MethodOfGettingAValueProfile.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -1156,7 +1160,6 @@
 		2A4BB7F318A41179008A0FCD /* JSManagedValueInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A4BB7F218A41179008A0FCD /* JSManagedValueInternal.h */; };
 		2A4EC90B1860D6C20094F782 /* WriteBarrierBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2A4EC9091860D6C20094F782 /* WriteBarrierBuffer.cpp */; };
 		2A4EC90C1860D6C20094F782 /* WriteBarrierBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A4EC90A1860D6C20094F782 /* WriteBarrierBuffer.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		2A6F462617E959CE00C45C98 /* HeapOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A6F462517E959CE00C45C98 /* HeapOperation.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		2A7A58EF1808A4C40020BDF7 /* DeferGC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2A7A58EE1808A4C40020BDF7 /* DeferGC.cpp */; };
 		2A83638518D7D0EE0000EBCC /* EdenGCActivityCallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2A83638318D7D0EE0000EBCC /* EdenGCActivityCallback.cpp */; };
 		2A83638618D7D0EE0000EBCC /* EdenGCActivityCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A83638418D7D0EE0000EBCC /* EdenGCActivityCallback.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -2836,7 +2839,6 @@
 		0F8335B41639C1E3001443B5 /* ArrayAllocationProfile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ArrayAllocationProfile.cpp; sourceTree = "<group>"; };
 		0F8335B51639C1E3001443B5 /* ArrayAllocationProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArrayAllocationProfile.h; sourceTree = "<group>"; };
 		0F8364B5164B0C0E0053329A /* DFGBranchDirection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGBranchDirection.h; path = dfg/DFGBranchDirection.h; sourceTree = "<group>"; };
-		0F86A26C1D6F796200CB0C92 /* HeapOperation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HeapOperation.cpp; sourceTree = "<group>"; };
 		0F86A26E1D6F7B3100CB0C92 /* GCTypeMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCTypeMap.h; sourceTree = "<group>"; };
 		0F86AE1F1C5311C5006BE8EC /* B3ComputeDivisionMagic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = B3ComputeDivisionMagic.h; path = b3/B3ComputeDivisionMagic.h; sourceTree = "<group>"; };
 		0F885E101849A3BE00F1E3FA /* BytecodeUseDef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BytecodeUseDef.h; sourceTree = "<group>"; };
@@ -2917,6 +2919,12 @@
 		0FA581B7150E952A00B9A2D9 /* DFGNodeFlags.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGNodeFlags.cpp; path = dfg/DFGNodeFlags.cpp; sourceTree = "<group>"; };
 		0FA581B8150E952A00B9A2D9 /* DFGNodeFlags.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGNodeFlags.h; path = dfg/DFGNodeFlags.h; sourceTree = "<group>"; };
 		0FA581B9150E952A00B9A2D9 /* DFGNodeType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGNodeType.h; path = dfg/DFGNodeType.h; sourceTree = "<group>"; };
+		0FA762001DB9242300B7A2FD /* CollectionScope.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CollectionScope.cpp; sourceTree = "<group>"; };
+		0FA762011DB9242300B7A2FD /* CollectionScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CollectionScope.h; sourceTree = "<group>"; };
+		0FA762021DB9242300B7A2FD /* MutatorState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MutatorState.cpp; sourceTree = "<group>"; };
+		0FA762031DB9242300B7A2FD /* MutatorState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MutatorState.h; sourceTree = "<group>"; };
+		0FA762081DB9283C00B7A2FD /* HelpingGCScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HelpingGCScope.h; sourceTree = "<group>"; };
+		0FA7620A1DB959F600B7A2FD /* AllocatingScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AllocatingScope.h; sourceTree = "<group>"; };
 		0FA7A8E918B413C80052371D /* Reg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Reg.cpp; sourceTree = "<group>"; };
 		0FA7A8EA18B413C80052371D /* Reg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reg.h; sourceTree = "<group>"; };
 		0FA7A8ED18CE4FD80052371D /* ScratchRegisterAllocator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScratchRegisterAllocator.cpp; sourceTree = "<group>"; };
@@ -2958,7 +2966,6 @@
 		0FB4FB701BC843140025CA5A /* FTLLazySlowPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FTLLazySlowPath.cpp; path = ftl/FTLLazySlowPath.cpp; sourceTree = "<group>"; };
 		0FB4FB711BC843140025CA5A /* FTLLazySlowPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLLazySlowPath.h; path = ftl/FTLLazySlowPath.h; sourceTree = "<group>"; };
 		0FB4FB721BC843140025CA5A /* FTLLazySlowPathCall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLLazySlowPathCall.h; path = ftl/FTLLazySlowPathCall.h; sourceTree = "<group>"; };
-		0FB530381D90404600F28AE3 /* AllocationScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AllocationScope.h; sourceTree = "<group>"; };
 		0FB5467614F59AD1002C2989 /* LazyOperandValueProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LazyOperandValueProfile.h; sourceTree = "<group>"; };
 		0FB5467814F5C468002C2989 /* LazyOperandValueProfile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LazyOperandValueProfile.cpp; sourceTree = "<group>"; };
 		0FB5467A14F5C7D4002C2989 /* MethodOfGettingAValueProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MethodOfGettingAValueProfile.h; sourceTree = "<group>"; };
@@ -3452,7 +3459,6 @@
 		2A4BB7F218A41179008A0FCD /* JSManagedValueInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSManagedValueInternal.h; sourceTree = "<group>"; };
 		2A4EC9091860D6C20094F782 /* WriteBarrierBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WriteBarrierBuffer.cpp; sourceTree = "<group>"; };
 		2A4EC90A1860D6C20094F782 /* WriteBarrierBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WriteBarrierBuffer.h; sourceTree = "<group>"; };
-		2A6F462517E959CE00C45C98 /* HeapOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HeapOperation.h; sourceTree = "<group>"; };
 		2A7A58EE1808A4C40020BDF7 /* DeferGC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DeferGC.cpp; sourceTree = "<group>"; };
 		2A83638318D7D0EE0000EBCC /* EdenGCActivityCallback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EdenGCActivityCallback.cpp; sourceTree = "<group>"; };
 		2A83638418D7D0EE0000EBCC /* EdenGCActivityCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EdenGCActivityCallback.h; sourceTree = "<group>"; };
@@ -5451,7 +5457,7 @@
 		142E312A134FF0A600AFADB5 /* heap */ = {
 			isa = PBXGroup;
 			children = (
-				0FB530381D90404600F28AE3 /* AllocationScope.h */,
+				0FA7620A1DB959F600B7A2FD /* AllocatingScope.h */,
 				0F9630351D4192C3005609D9 /* AllocatorAttributes.cpp */,
 				0F9630361D4192C3005609D9 /* AllocatorAttributes.h */,
 				0F070A421D543A89006E7232 /* CellContainer.h */,
@@ -5460,6 +5466,8 @@
 				0FD8A31117D4326C00CA2C40 /* CodeBlockSet.cpp */,
 				0FD8A31217D4326C00CA2C40 /* CodeBlockSet.h */,
 				0F664CE71DA304ED00B00A11 /* CodeBlockSetInlines.h */,
+				0FA762001DB9242300B7A2FD /* CollectionScope.cpp */,
+				0FA762011DB9242300B7A2FD /* CollectionScope.h */,
 				146B14DB12EB5B12001BEC1B /* ConservativeRoots.cpp */,
 				149DAAF212EB559D0083B12B /* ConservativeRoots.h */,
 				2A7A58EE1808A4C40020BDF7 /* DeferGC.cpp */,
@@ -5504,8 +5512,6 @@
 				C2DA778218E259990066FCB6 /* HeapInlines.h */,
 				2AD8932917E3868F00668276 /* HeapIterationScope.h */,
 				A5339EC81BB4B4510054F005 /* HeapObserver.h */,
-				0F86A26C1D6F796200CB0C92 /* HeapOperation.cpp */,
-				2A6F462517E959CE00C45C98 /* HeapOperation.h */,
 				A5398FA91C750D950060A963 /* HeapProfiler.cpp */,
 				A5398FAA1C750D950060A963 /* HeapProfiler.h */,
 				14F97446138C853E00DA1C67 /* HeapRootVisitor.h */,
@@ -5520,6 +5526,7 @@
 				0FADE6721D4D23BC00768457 /* HeapUtil.h */,
 				FE7BA60D1A1A7CEC00F1F7B4 /* HeapVerifier.cpp */,
 				FE7BA60E1A1A7CEC00F1F7B4 /* HeapVerifier.h */,
+				0FA762081DB9283C00B7A2FD /* HelpingGCScope.h */,
 				C25F8BCB157544A900245B71 /* IncrementalSweeper.cpp */,
 				C25F8BCC157544A900245B71 /* IncrementalSweeper.h */,
 				0F766D2915A8CC34008F363E /* JITStubRoutineSet.cpp */,
@@ -5545,6 +5552,8 @@
 				0F7C5FB91D8895050044F5E2 /* MarkedSpaceInlines.h */,
 				142D6F0E13539A4100B02E86 /* MarkStack.cpp */,
 				142D6F0F13539A4100B02E86 /* MarkStack.h */,
+				0FA762021DB9242300B7A2FD /* MutatorState.cpp */,
+				0FA762031DB9242300B7A2FD /* MutatorState.h */,
 				ADDB1F6218D77DB7009B58A8 /* OpaqueRootSet.h */,
 				C225494215F7DBAA0065E898 /* SlotVisitor.cpp */,
 				14BA78F013AAB88F005B7C2C /* SlotVisitor.h */,
@@ -7506,6 +7515,7 @@
 				0FEC85031BDACDAC0080FF74 /* B3BasicBlockUtils.h in Headers */,
 				0FEC85041BDACDAC0080FF74 /* B3BlockWorklist.h in Headers */,
 				0FEC85061BDACDAC0080FF74 /* B3CheckSpecial.h in Headers */,
+				0FA762071DB9243300B7A2FD /* MutatorState.h in Headers */,
 				0FEC85081BDACDAC0080FF74 /* B3CheckValue.h in Headers */,
 				0FEC850A1BDACDAC0080FF74 /* B3Common.h in Headers */,
 				DC454B8D1D00E824004C18AF /* AirDumpAsJS.h in Headers */,
@@ -7606,7 +7616,6 @@
 				62D755D71B84FB4A001801FA /* CallFrameShuffler.h in Headers */,
 				0F0B83B114BCF71800885B4F /* CallLinkInfo.h in Headers */,
 				0F93329E14CA7DC50085F3C6 /* CallLinkStatus.h in Headers */,
-				0FB530391D90404C00F28AE3 /* AllocationScope.h in Headers */,
 				627673241B680C1E00FD9F2E /* CallMode.h in Headers */,
 				0F0B83B914BCF95F00885B4F /* CallReturnOffsetToBytecodeOffset.h in Headers */,
 				0F3B7E2B19A11B8000D9BC56 /* CallVariant.h in Headers */,
@@ -7911,6 +7920,8 @@
 				2AD2EDFB19799E38004D6478 /* EnumerationMode.h in Headers */,
 				BC3046070E1F497F003232CF /* Error.h in Headers */,
 				BC02E90D0E1839DB000F9297 /* ErrorConstructor.h in Headers */,
+				0FA7620B1DB959F900B7A2FD /* AllocatingScope.h in Headers */,
+				0FA762091DB9283E00B7A2FD /* HelpingGCScope.h in Headers */,
 				FEB58C15187B8B160098EF0B /* ErrorHandlingScope.h in Headers */,
 				BC02E98D0E183E38000F9297 /* ErrorInstance.h in Headers */,
 				BC02E90F0E1839DB000F9297 /* ErrorPrototype.h in Headers */,
@@ -8060,7 +8071,6 @@
 				2AD8932B17E3868F00668276 /* HeapIterationScope.h in Headers */,
 				A5339EC91BB4B4600054F005 /* HeapObserver.h in Headers */,
 				AD2FCBEB1DB58DAD00B3E736 /* JSWebAssemblyTable.h in Headers */,
-				2A6F462617E959CE00C45C98 /* HeapOperation.h in Headers */,
 				14F97447138C853E00DA1C67 /* HeapRootVisitor.h in Headers */,
 				C24D31E3161CD695002AA4DB /* HeapStatistics.h in Headers */,
 				C2E526BE1590EF000054E48D /* HeapTimer.h in Headers */,
@@ -8123,6 +8133,7 @@
 				A18193E31B4E0CDB00FC1029 /* IntlCollatorConstructor.lut.h in Headers */,
 				A1B9E23E1B4E0D6700BC7FED /* IntlCollatorPrototype.h in Headers */,
 				A18193E41B4E0CDB00FC1029 /* IntlCollatorPrototype.lut.h in Headers */,
+				0FA762051DB9242900B7A2FD /* CollectionScope.h in Headers */,
 				A1587D6E1B4DC14100D69849 /* IntlDateTimeFormat.h in Headers */,
 				FE187A0F1C030D6C0038BBCA /* SnippetOperand.h in Headers */,
 				A1587D701B4DC14100D69849 /* IntlDateTimeFormatConstructor.h in Headers */,
@@ -9489,7 +9500,6 @@
 				A78A977A179738B8009DF744 /* DFGPlan.cpp in Sources */,
 				0FBE0F7416C1DB090082C5E8 /* DFGPredictionInjectionPhase.cpp in Sources */,
 				0FFFC95D14EF90B300C72532 /* DFGPredictionPropagationPhase.cpp in Sources */,
-				0F86A26D1D6F796500CB0C92 /* HeapOperation.cpp in Sources */,
 				0F3E01AA19D353A500F61B7F /* DFGPrePostNumbering.cpp in Sources */,
 				0F2B9CEC19D0BA7D00B1D1B5 /* DFGPromotedHeapLocation.cpp in Sources */,
 				0FB17662196B8F9E0091052A /* DFGPureValue.cpp in Sources */,
@@ -9636,6 +9646,7 @@
 				A58E35911860DECF001F24FE /* InjectedScriptHost.cpp in Sources */,
 				A513E5CA185F9624007E95AD /* InjectedScriptManager.cpp in Sources */,
 				A5840E20187B7B8600843B10 /* InjectedScriptModule.cpp in Sources */,
+				0FA762041DB9242600B7A2FD /* CollectionScope.cpp in Sources */,
 				148A7BEF1B82975A002D9157 /* InlineCallFrame.cpp in Sources */,
 				0F24E55517F0B71C00ABB217 /* InlineCallFrameSet.cpp in Sources */,
 				A5CEEE14187F3BAD00E55C99 /* InspectorAgent.cpp in Sources */,
@@ -9791,6 +9802,7 @@
 				DC17E8171C9C91D6008A6AB3 /* ShadowChicken.cpp in Sources */,
 				A7299D9D17D12837005F5FF9 /* JSSet.cpp in Sources */,
 				A790DD6F182F499700588807 /* JSSetIterator.cpp in Sources */,
+				0FA762061DB9243100B7A2FD /* MutatorState.cpp in Sources */,
 				1428083A107EC0750013E7B2 /* CLoopStack.cpp in Sources */,
 				147F39D5107EC37600427A48 /* JSString.cpp in Sources */,
 				70EC0EC21AA0D7DA00B6AAFA /* JSStringIterator.cpp in Sources */,
diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
index 947985d..b152112 100644
--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp
+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
@@ -3474,7 +3474,7 @@
         // This accomplishes (1), and does its own book-keeping about whether it has already happened.
         if (!jitCode()->dfgCommon()->invalidate()) {
             // We've already been invalidated.
-            RELEASE_ASSERT(this != replacement() || (m_vm->heap.isCollecting() && !Heap::isMarked(ownerScriptExecutable())));
+            RELEASE_ASSERT(this != replacement() || (m_vm->heap.isCurrentThreadBusy() && !Heap::isMarked(ownerScriptExecutable())));
             return;
         }
     }
@@ -3506,7 +3506,7 @@
 
     // Jettison can happen during GC. We don't want to install code to a dead executable
     // because that would add a dead object to the remembered set.
-    if (m_vm->heap.isCollecting() && !Heap::isMarked(ownerScriptExecutable()))
+    if (m_vm->heap.isCurrentThreadBusy() && !Heap::isMarked(ownerScriptExecutable()))
         return;
 
     // This accomplishes (2).
diff --git a/Source/JavaScriptCore/dfg/DFGWorklist.cpp b/Source/JavaScriptCore/dfg/DFGWorklist.cpp
index 5a01612..0ce6979 100644
--- a/Source/JavaScriptCore/dfg/DFGWorklist.cpp
+++ b/Source/JavaScriptCore/dfg/DFGWorklist.cpp
@@ -103,9 +103,10 @@
         if (Options::verboseCompilationQueue())
             dataLog(m_worklist, ": Compiling ", m_plan->key(), " asynchronously\n");
         
-        RELEASE_ASSERT(!m_plan->vm->heap.isCollecting());
+        // There's no way for the GC to be safepointing since we own rightToRun.
+        RELEASE_ASSERT(m_plan->vm->heap.mutatorState() != MutatorState::HelpingGC);
         m_plan->compileInThread(*m_longLivedState, &m_data);
-        RELEASE_ASSERT(m_plan->stage == Plan::Cancelled || !m_plan->vm->heap.isCollecting());
+        RELEASE_ASSERT(m_plan->stage == Plan::Cancelled || m_plan->vm->heap.mutatorState() != MutatorState::HelpingGC);
         
         {
             LockHolder locker(*m_worklist.m_lock);
@@ -123,7 +124,7 @@
             
             m_worklist.m_planCompiled.notifyAll();
         }
-        RELEASE_ASSERT(!m_plan->vm->heap.isCollecting());
+        RELEASE_ASSERT(m_plan->vm->heap.mutatorState() != MutatorState::HelpingGC);
         
         return WorkResult::Continue;
     }
diff --git a/Source/JavaScriptCore/ftl/FTLCompile.cpp b/Source/JavaScriptCore/ftl/FTLCompile.cpp
index 39eb666..68659a8 100644
--- a/Source/JavaScriptCore/ftl/FTLCompile.cpp
+++ b/Source/JavaScriptCore/ftl/FTLCompile.cpp
@@ -65,7 +65,7 @@
 
     if (safepointResult.didGetCancelled())
         return;
-    RELEASE_ASSERT(!state.graph.m_vm.heap.isCollecting());
+    RELEASE_ASSERT(state.graph.m_vm.heap.mutatorState() != MutatorState::HelpingGC);
     
     if (state.allocationFailed)
         return;
diff --git a/Source/JavaScriptCore/heap/HeapOperation.h b/Source/JavaScriptCore/heap/AllocatingScope.h
similarity index 72%
copy from Source/JavaScriptCore/heap/HeapOperation.h
copy to Source/JavaScriptCore/heap/AllocatingScope.h
index fbb8283..1d13ae0 100644
--- a/Source/JavaScriptCore/heap/HeapOperation.h
+++ b/Source/JavaScriptCore/heap/AllocatingScope.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -25,16 +25,28 @@
 
 #pragma once
 
+#include "Heap.h"
+
 namespace JSC {
 
-enum HeapOperation { NoOperation, Allocation, FullCollection, EdenCollection, AnyCollection };
+class AllocatingScope {
+public:
+    AllocatingScope(Heap& heap)
+        : m_heap(heap)
+    {
+        RELEASE_ASSERT(m_heap.m_mutatorState == MutatorState::Running);
+        m_heap.m_mutatorState = MutatorState::Allocating;
+    }
+    
+    ~AllocatingScope()
+    {
+        RELEASE_ASSERT(m_heap.m_mutatorState == MutatorState::Allocating);
+        m_heap.m_mutatorState = MutatorState::Running;
+    }
+
+private:
+    Heap& m_heap;
+};
 
 } // namespace JSC
 
-namespace WTF {
-
-class PrintStream;
-
-void printInternal(PrintStream& out, JSC::HeapOperation);
-
-} // namespace WTF
diff --git a/Source/JavaScriptCore/heap/AllocationScope.h b/Source/JavaScriptCore/heap/AllocationScope.h
deleted file mode 100644
index 2e4a654..0000000
--- a/Source/JavaScriptCore/heap/AllocationScope.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2016 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. 
- */
-
-#pragma once
-
-#include "Heap.h"
-
-namespace JSC {
-
-class AllocationScope {
-public:
-    AllocationScope(Heap& heap)
-        : m_heap(heap)
-        , m_lastOperation(m_heap.m_operationInProgress)
-    {
-        ASSERT(m_lastOperation == NoOperation || m_lastOperation == Allocation);
-        m_heap.m_operationInProgress = Allocation;
-    }
-    
-    ~AllocationScope()
-    {
-        ASSERT(m_heap.m_operationInProgress == Allocation);
-        m_heap.m_operationInProgress = m_lastOperation;
-    }
-private:
-    Heap& m_heap;
-    HeapOperation m_lastOperation;
-};
-
-} // namespace JSC
-
diff --git a/Source/JavaScriptCore/heap/CodeBlockSet.cpp b/Source/JavaScriptCore/heap/CodeBlockSet.cpp
index c9ab0be..ec07db2 100644
--- a/Source/JavaScriptCore/heap/CodeBlockSet.cpp
+++ b/Source/JavaScriptCore/heap/CodeBlockSet.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -77,10 +77,10 @@
         codeBlock->classInfo()->methodTable.destroy(codeBlock);
 }
 
-void CodeBlockSet::deleteUnmarkedAndUnreferenced(HeapOperation collectionType)
+void CodeBlockSet::deleteUnmarkedAndUnreferenced(CollectionScope scope)
 {
     LockHolder locker(&m_lock);
-    HashSet<CodeBlock*>& set = collectionType == EdenCollection ? m_newCodeBlocks : m_oldCodeBlocks;
+    HashSet<CodeBlock*>& set = scope == CollectionScope::Eden ? m_newCodeBlocks : m_oldCodeBlocks;
     Vector<CodeBlock*> unmarked;
     for (CodeBlock* codeBlock : set) {
         if (Heap::isMarked(codeBlock))
@@ -94,7 +94,7 @@
     }
 
     // Any remaining young CodeBlocks are live and need to be promoted to the set of old CodeBlocks.
-    if (collectionType == EdenCollection)
+    if (scope == CollectionScope::Eden)
         promoteYoungCodeBlocks(locker);
 }
 
diff --git a/Source/JavaScriptCore/heap/CodeBlockSet.h b/Source/JavaScriptCore/heap/CodeBlockSet.h
index fd3800e..68caf30 100644
--- a/Source/JavaScriptCore/heap/CodeBlockSet.h
+++ b/Source/JavaScriptCore/heap/CodeBlockSet.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2014, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -25,8 +25,8 @@
 
 #pragma once
 
+#include "CollectionScope.h"
 #include "GCSegmentedArray.h"
-#include "HeapOperation.h"
 #include <wtf/HashSet.h>
 #include <wtf/Lock.h>
 #include <wtf/Noncopyable.h>
@@ -67,7 +67,7 @@
     
     // Delete all code blocks that are only referenced by this set (i.e. owned
     // by this set), and that have not been marked.
-    void deleteUnmarkedAndUnreferenced(HeapOperation);
+    void deleteUnmarkedAndUnreferenced(CollectionScope);
     
     // Add all currently executing CodeBlocks to the remembered set to be 
     // re-scanned during the next collection.
diff --git a/Source/JavaScriptCore/heap/HeapOperation.cpp b/Source/JavaScriptCore/heap/CollectionScope.cpp
similarity index 75%
copy from Source/JavaScriptCore/heap/HeapOperation.cpp
copy to Source/JavaScriptCore/heap/CollectionScope.cpp
index 8715314..b2990e7 100644
--- a/Source/JavaScriptCore/heap/HeapOperation.cpp
+++ b/Source/JavaScriptCore/heap/CollectionScope.cpp
@@ -24,37 +24,32 @@
  */
 
 #include "config.h"
-#include "HeapOperation.h"
+#include "CollectionScope.h"
 
 #include <wtf/PrintStream.h>
 
-namespace WTF {
+namespace JSC {
 
-using namespace JSC;
-
-void printInternal(PrintStream& out, HeapOperation operation)
+const char* collectionScopeName(CollectionScope scope)
 {
-    switch (operation) {
-    case NoOperation:
-        out.print("None");
-        return;
-    case Allocation:
-        out.print("Alloc");
-        return;
-    case FullCollection:
-        out.print("Full");
-        return;
-    case EdenCollection:
-        out.print("Eden");
-        return;
-    case AnyCollection:
-        out.print("Any");
-        return;
+    switch (scope) {
+    case CollectionScope::Eden:
+        return "Eden";
+    case CollectionScope::Full:
+        return "Full";
     }
     RELEASE_ASSERT_NOT_REACHED();
+    return nullptr;
+}
+
+} // namespace JSC
+
+namespace WTF {
+
+void printInternal(PrintStream& out, JSC::CollectionScope scope)
+{
+    out.print(JSC::collectionScopeName(scope));
 }
 
 } // namespace WTF
 
-
-
diff --git a/Source/JavaScriptCore/heap/HeapOperation.h b/Source/JavaScriptCore/heap/CollectionScope.h
similarity index 86%
rename from Source/JavaScriptCore/heap/HeapOperation.h
rename to Source/JavaScriptCore/heap/CollectionScope.h
index fbb8283..9b4f487 100644
--- a/Source/JavaScriptCore/heap/HeapOperation.h
+++ b/Source/JavaScriptCore/heap/CollectionScope.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -27,7 +27,9 @@
 
 namespace JSC {
 
-enum HeapOperation { NoOperation, Allocation, FullCollection, EdenCollection, AnyCollection };
+enum class CollectionScope { Eden, Full };
+
+const char* collectionScopeName(CollectionScope);
 
 } // namespace JSC
 
@@ -35,6 +37,7 @@
 
 class PrintStream;
 
-void printInternal(PrintStream& out, JSC::HeapOperation);
+void printInternal(PrintStream& out, JSC::CollectionScope);
 
 } // namespace WTF
+
diff --git a/Source/JavaScriptCore/heap/EdenGCActivityCallback.cpp b/Source/JavaScriptCore/heap/EdenGCActivityCallback.cpp
index 7774075..1d2a6fc 100644
--- a/Source/JavaScriptCore/heap/EdenGCActivityCallback.cpp
+++ b/Source/JavaScriptCore/heap/EdenGCActivityCallback.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -39,7 +39,7 @@
 
 void EdenGCActivityCallback::doCollection()
 {
-    m_vm->heap.collect(EdenCollection);
+    m_vm->heap.collect(CollectionScope::Eden);
 }
 
 double EdenGCActivityCallback::lastGCLength()
diff --git a/Source/JavaScriptCore/heap/FullGCActivityCallback.cpp b/Source/JavaScriptCore/heap/FullGCActivityCallback.cpp
index 91051a6..5ddfa87 100644
--- a/Source/JavaScriptCore/heap/FullGCActivityCallback.cpp
+++ b/Source/JavaScriptCore/heap/FullGCActivityCallback.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -55,7 +55,7 @@
     }
 #endif
 
-    heap.collect(FullCollection);
+    heap.collect(CollectionScope::Full);
 }
 
 double FullGCActivityCallback::lastGCLength()
diff --git a/Source/JavaScriptCore/heap/GCTypeMap.h b/Source/JavaScriptCore/heap/GCTypeMap.h
index d0df25c..9e19e67 100644
--- a/Source/JavaScriptCore/heap/GCTypeMap.h
+++ b/Source/JavaScriptCore/heap/GCTypeMap.h
@@ -25,7 +25,7 @@
 
 #pragma once
 
-#include "HeapOperation.h"
+#include "CollectionScope.h"
 #include <wtf/Assertions.h>
 
 namespace JSC {
@@ -35,20 +35,28 @@
     T eden;
     T full;
     
-    T& operator[](HeapOperation operation)
+    T& operator[](CollectionScope scope)
     {
-        if (operation == FullCollection)
+        switch (scope) {
+        case CollectionScope::Full:
             return full;
-        ASSERT(operation == EdenCollection);
-        return eden;
+        case CollectionScope::Eden:
+            return eden;
+        }
+        ASSERT_NOT_REACHED();
+        return full;
     }
     
-    const T& operator[](HeapOperation operation) const
+    const T& operator[](CollectionScope scope) const
     {
-        if (operation == FullCollection)
+        switch (scope) {
+        case CollectionScope::Full:
             return full;
-        ASSERT(operation == EdenCollection);
-        return eden;
+        case CollectionScope::Eden:
+            return eden;
+        }
+        RELEASE_ASSERT_NOT_REACHED();
+        return full;
     }
 };
 
diff --git a/Source/JavaScriptCore/heap/Heap.cpp b/Source/JavaScriptCore/heap/Heap.cpp
index 3e408f6..35a6b48 100644
--- a/Source/JavaScriptCore/heap/Heap.cpp
+++ b/Source/JavaScriptCore/heap/Heap.cpp
@@ -39,6 +39,7 @@
 #include "HeapSnapshot.h"
 #include "HeapStatistics.h"
 #include "HeapVerifier.h"
+#include "HelpingGCScope.h"
 #include "IncrementalSweeper.h"
 #include "Interpreter.h"
 #include "JITStubRoutineSet.h"
@@ -140,15 +141,15 @@
     return *result;
 }
 
-SimpleStats& timingStats(const char* name, HeapOperation operation)
+SimpleStats& timingStats(const char* name, CollectionScope scope)
 {
-    return timingStats().add(name, GCTypeMap<SimpleStats>()).iterator->value[operation];
+    return timingStats().add(name, GCTypeMap<SimpleStats>()).iterator->value[scope];
 }
 
 class TimingScope {
 public:
-    TimingScope(HeapOperation operation, const char* name)
-        : m_operation(operation)
+    TimingScope(Optional<CollectionScope> scope, const char* name)
+        : m_scope(scope)
         , m_name(name)
     {
         if (measurePhaseTiming())
@@ -156,18 +157,18 @@
     }
     
     TimingScope(Heap& heap, const char* name)
-        : TimingScope(heap.operationInProgress(), name)
+        : TimingScope(heap.collectionScope(), name)
     {
     }
     
-    void setOperation(HeapOperation operation)
+    void setScope(Optional<CollectionScope> scope)
     {
-        m_operation = operation;
+        m_scope = scope;
     }
     
-    void setOperation(Heap& heap)
+    void setScope(Heap& heap)
     {
-        setOperation(heap.operationInProgress());
+        setScope(heap.collectionScope());
     }
     
     ~TimingScope()
@@ -175,13 +176,13 @@
         if (measurePhaseTiming()) {
             double after = monotonicallyIncreasingTimeMS();
             double timing = after - m_before;
-            SimpleStats& stats = timingStats(m_name, m_operation);
+            SimpleStats& stats = timingStats(m_name, *m_scope);
             stats.add(timing);
-            dataLog("[GC:", m_operation, "] ", m_name, " took: ", timing, " ms (average ", stats.mean(), " ms).\n");
+            dataLog("[GC:", *m_scope, "] ", m_name, " took: ", timing, " ms (average ", stats.mean(), " ms).\n");
         }
     }
 private:
-    HeapOperation m_operation;
+    Optional<CollectionScope> m_scope;
     double m_before;
     const char* m_name;
 };
@@ -203,7 +204,6 @@
     , m_maxHeapSize(m_minBytesPerCycle)
     , m_shouldDoFullCollection(false)
     , m_totalBytesVisited(0)
-    , m_operationInProgress(NoOperation)
     , m_objectSpace(this)
     , m_extraMemorySize(0)
     , m_deprecatedExtraMemorySize(0)
@@ -252,7 +252,8 @@
 void Heap::lastChanceToFinalize()
 {
     RELEASE_ASSERT(!m_vm->entryScope);
-    RELEASE_ASSERT(m_operationInProgress == NoOperation);
+    RELEASE_ASSERT(!m_collectionScope);
+    RELEASE_ASSERT(m_mutatorState == MutatorState::Running);
 
     m_arrayBuffers.lastChanceToFinalize();
     m_codeBlocks->lastChanceToFinalize();
@@ -408,7 +409,7 @@
         }
 #endif // ENABLE(SAMPLING_PROFILER)
 
-        if (m_operationInProgress == FullCollection) {
+        if (m_collectionScope == CollectionScope::Full) {
             m_opaqueRoots.clear();
             m_slotVisitor.clearMarkStack();
         }
@@ -431,7 +432,7 @@
                         slotVisitor = m_availableParallelSlotVisitors.takeLast();
                 }
 
-                WTF::registerGCThread();
+                WTF::registerGCThread(GCThreadType::Helper);
 
                 {
                     ParallelModeEnabler parallelModeEnabler(*slotVisitor);
@@ -526,7 +527,7 @@
 void Heap::beginMarking()
 {
     TimingScope timingScope(*this, "Heap::beginMarking");
-    if (m_operationInProgress == FullCollection)
+    if (m_collectionScope == CollectionScope::Full)
         m_codeBlocks->clearMarksForFullCollection();
     
     {
@@ -544,7 +545,7 @@
 
 void Heap::visitSmallStrings()
 {
-    if (!m_vm->smallStrings.needsToBeVisited(m_operationInProgress))
+    if (!m_vm->smallStrings.needsToBeVisited(*m_collectionScope))
         return;
 
     m_vm->smallStrings.visitStrongReferences(m_slotVisitor);
@@ -763,7 +764,7 @@
         dataLogF("\nNumber of live Objects after GC %lu, took %.6f secs\n", static_cast<unsigned long>(visitCount), WTF::monotonicallyIncreasingTime() - gcStartTime);
     }
     
-    if (m_operationInProgress == FullCollection)
+    if (m_collectionScope == CollectionScope::Full)
         m_totalBytesVisited = 0;
 
     m_totalBytesVisitedThisCycle = m_slotVisitor.bytesVisited() + threadBytesVisited();
@@ -871,7 +872,7 @@
     // If JavaScript is running, it's not safe to delete all JavaScript code, since
     // we'll end up returning to deleted code.
     RELEASE_ASSERT(!m_vm->entryScope);
-    ASSERT(m_operationInProgress == NoOperation);
+    ASSERT(!m_collectionScope);
 
     completeAllJITPlans();
 
@@ -908,7 +909,7 @@
 void Heap::deleteUnmarkedCompiledCode()
 {
     clearUnmarkedExecutables();
-    m_codeBlocks->deleteUnmarkedAndUnreferenced(m_operationInProgress);
+    m_codeBlocks->deleteUnmarkedAndUnreferenced(*m_collectionScope);
     m_jitStubRoutines->deleteUnmarkedJettisonedStubRoutines();
 }
 
@@ -932,7 +933,7 @@
     if (!m_isSafeToCollect)
         return;
 
-    collectWithoutAnySweep(FullCollection);
+    collectWithoutAnySweep(CollectionScope::Full);
 
     DeferGCForAWhile deferGC(*this);
     if (UNLIKELY(Options::useImmortalObjects()))
@@ -955,29 +956,29 @@
     sweepAllLogicallyEmptyWeakBlocks();
 }
 
-void Heap::collect(HeapOperation collectionType)
+void Heap::collect(Optional<CollectionScope> scope)
 {
     SuperSamplerScope superSamplerScope(false);
     if (!m_isSafeToCollect)
         return;
-
-    collectWithoutAnySweep(collectionType);
+    
+    collectWithoutAnySweep(scope);
 }
 
-NEVER_INLINE void Heap::collectWithoutAnySweep(HeapOperation collectionType)
+NEVER_INLINE void Heap::collectWithoutAnySweep(Optional<CollectionScope> scope)
 {
     void* stackTop;
     ALLOCATE_AND_GET_REGISTER_STATE(registers);
 
-    collectImpl(collectionType, wtfThreadData().stack().origin(), &stackTop, registers);
+    collectImpl(scope, wtfThreadData().stack().origin(), &stackTop, registers);
 
     sanitizeStackForVM(m_vm);
 }
 
-NEVER_INLINE void Heap::collectImpl(HeapOperation collectionType, void* stackOrigin, void* stackTop, MachineThreads::RegisterState& calleeSavedRegisters)
+NEVER_INLINE void Heap::collectImpl(Optional<CollectionScope> scope, void* stackOrigin, void* stackTop, MachineThreads::RegisterState& calleeSavedRegisters)
 {
     SuperSamplerScope superSamplerScope(false);
-    TimingScope collectImplTimingScope(collectionType, "Heap::collectImpl");
+    TimingScope collectImplTimingScope(scope, "Heap::collectImpl");
     
 #if ENABLE(ALLOCATION_LOGGING)
     dataLogF("JSC GC starting collection.\n");
@@ -990,81 +991,78 @@
     }
     
     double gcStartTime;
-    {
-        TimingScope earlyTimingScope(collectionType, "Heap::collectImpl before markRoots");
-
-        if (vm()->typeProfiler()) {
-            DeferGCForAWhile awhile(*this);
-            vm()->typeProfilerLog()->processLogEntries(ASCIILiteral("GC"));
-        }
-
+    
+    if (vm()->typeProfiler()) {
+        DeferGCForAWhile awhile(*this);
+        vm()->typeProfilerLog()->processLogEntries(ASCIILiteral("GC"));
+    }
+    
 #if ENABLE(JIT)
-        {
-            DeferGCForAWhile awhile(*this);
-            JITWorklist::instance()->completeAllForVM(*m_vm);
-        }
+    {
+        DeferGCForAWhile awhile(*this);
+        JITWorklist::instance()->completeAllForVM(*m_vm);
+    }
 #endif // ENABLE(JIT)
-
-        vm()->shadowChicken().update(*vm(), vm()->topCallFrame);
-
-        RELEASE_ASSERT(!m_deferralDepth);
-        ASSERT(vm()->currentThreadIsHoldingAPILock());
-        RELEASE_ASSERT(vm()->atomicStringTable() == wtfThreadData().atomicStringTable());
-        ASSERT(m_isSafeToCollect);
-        RELEASE_ASSERT(m_operationInProgress == NoOperation);
-
-        suspendCompilerThreads();
-        willStartCollection(collectionType);
+    
+    vm()->shadowChicken().update(*vm(), vm()->topCallFrame);
+    
+    RELEASE_ASSERT(!m_deferralDepth);
+    ASSERT(vm()->currentThreadIsHoldingAPILock());
+    RELEASE_ASSERT(vm()->atomicStringTable() == wtfThreadData().atomicStringTable());
+    ASSERT(m_isSafeToCollect);
+    RELEASE_ASSERT(!m_collectionScope);
+    
+    suspendCompilerThreads();
+    willStartCollection(scope);
+    {
+        HelpingGCScope helpingHeapScope(*this);
         
-        collectImplTimingScope.setOperation(*this);
-        earlyTimingScope.setOperation(*this);
-
+        collectImplTimingScope.setScope(*this);
+        
         gcStartTime = WTF::monotonicallyIncreasingTime();
         if (m_verifier) {
             // Verify that live objects from the last GC cycle haven't been corrupted by
             // mutators before we begin this new GC cycle.
             m_verifier->verify(HeapVerifier::Phase::BeforeGC);
-
+            
             m_verifier->initializeGCCycle();
             m_verifier->gatherLiveObjects(HeapVerifier::Phase::BeforeMarking);
         }
-
+        
         flushOldStructureIDTables();
         stopAllocation();
         prepareForMarking();
         flushWriteBarrierBuffer();
-
+        
         if (HasOwnPropertyCache* cache = vm()->hasOwnPropertyCache())
             cache->clear();
+        
+        markRoots(gcStartTime, stackOrigin, stackTop, calleeSavedRegisters);
+        
+        if (m_verifier) {
+            m_verifier->gatherLiveObjects(HeapVerifier::Phase::AfterMarking);
+            m_verifier->verify(HeapVerifier::Phase::AfterMarking);
+        }
+        
+        if (vm()->typeProfiler())
+            vm()->typeProfiler()->invalidateTypeSetCache();
+        
+        reapWeakHandles();
+        pruneStaleEntriesFromWeakGCMaps();
+        sweepArrayBuffers();
+        snapshotUnswept();
+        finalizeUnconditionalFinalizers();
+        removeDeadCompilerWorklistEntries();
+        deleteUnmarkedCompiledCode();
+        deleteSourceProviderCaches();
+        
+        notifyIncrementalSweeper();
+        m_codeBlocks->writeBarrierCurrentlyExecuting(this);
+        m_codeBlocks->clearCurrentlyExecuting();
+        
+        prepareForAllocation();
+        updateAllocationLimits();
     }
-
-    markRoots(gcStartTime, stackOrigin, stackTop, calleeSavedRegisters);
-    
-    TimingScope lateTimingScope(*this, "Heap::collectImpl after markRoots");
-
-    if (m_verifier) {
-        m_verifier->gatherLiveObjects(HeapVerifier::Phase::AfterMarking);
-        m_verifier->verify(HeapVerifier::Phase::AfterMarking);
-    }
-
-    if (vm()->typeProfiler())
-        vm()->typeProfiler()->invalidateTypeSetCache();
-
-    reapWeakHandles();
-    pruneStaleEntriesFromWeakGCMaps();
-    sweepArrayBuffers();
-    snapshotUnswept();
-    finalizeUnconditionalFinalizers();
-    removeDeadCompilerWorklistEntries();
-    deleteUnmarkedCompiledCode();
-    deleteSourceProviderCaches();
-
-    notifyIncrementalSweeper();
-    m_codeBlocks->writeBarrierCurrentlyExecuting(this);
-    m_codeBlocks->clearCurrentlyExecuting();
-
-    prepareForAllocation();
-    updateAllocationLimits();
     didFinishCollection(gcStartTime);
     resumeCompilerThreads();
     sweepLargeAllocations();
@@ -1103,22 +1101,22 @@
 #endif
 }
 
-void Heap::willStartCollection(HeapOperation collectionType)
+void Heap::willStartCollection(Optional<CollectionScope> scope)
 {
     if (Options::logGC())
         dataLog("=> ");
     
-    if (shouldDoFullCollection(collectionType)) {
-        m_operationInProgress = FullCollection;
+    if (shouldDoFullCollection(scope)) {
+        m_collectionScope = CollectionScope::Full;
         m_shouldDoFullCollection = false;
         if (Options::logGC())
             dataLog("FullCollection, ");
     } else {
-        m_operationInProgress = EdenCollection;
+        m_collectionScope = CollectionScope::Eden;
         if (Options::logGC())
             dataLog("EdenCollection, ");
     }
-    if (m_operationInProgress == FullCollection) {
+    if (m_collectionScope == CollectionScope::Full) {
         m_sizeBeforeLastFullCollect = m_sizeAfterLastCollect + m_bytesAllocatedThisCycle;
         m_extraMemorySize = 0;
         m_deprecatedExtraMemorySize = 0;
@@ -1129,7 +1127,7 @@
         if (m_fullActivityCallback)
             m_fullActivityCallback->willCollect();
     } else {
-        ASSERT(m_operationInProgress == EdenCollection);
+        ASSERT(m_collectionScope == CollectionScope::Eden);
         m_sizeBeforeLastEdenCollect = m_sizeAfterLastCollect + m_bytesAllocatedThisCycle;
     }
 
@@ -1147,7 +1145,7 @@
 
 void Heap::flushWriteBarrierBuffer()
 {
-    if (m_operationInProgress == EdenCollection) {
+    if (m_collectionScope == CollectionScope::Eden) {
         m_writeBarrierBuffer.flush(*this);
         return;
     }
@@ -1171,7 +1169,7 @@
 
 void Heap::pruneStaleEntriesFromWeakGCMaps()
 {
-    if (m_operationInProgress != FullCollection)
+    if (m_collectionScope != CollectionScope::Full)
         return;
     for (auto& pruneCallback : m_weakGCMaps.values())
         pruneCallback();
@@ -1195,7 +1193,7 @@
 
 void Heap::notifyIncrementalSweeper()
 {
-    if (m_operationInProgress == FullCollection) {
+    if (m_collectionScope == CollectionScope::Full) {
         if (!m_logicallyEmptyWeakBlocks.isEmpty())
             m_indexOfNextLogicallyEmptyWeakBlockToSweep = 0;
     }
@@ -1243,7 +1241,7 @@
     if (Options::gcMaxHeapSize() && currentHeapSize > Options::gcMaxHeapSize())
         HeapStatistics::exitWithFailure();
 
-    if (m_operationInProgress == FullCollection) {
+    if (m_collectionScope == CollectionScope::Full) {
         // To avoid pathological GC churn in very small and very large heaps, we set
         // the new allocation limit based on the current size of the heap, with a
         // fixed minimum.
@@ -1298,8 +1296,8 @@
 void Heap::didFinishCollection(double gcStartTime)
 {
     double gcEndTime = WTF::monotonicallyIncreasingTime();
-    HeapOperation operation = m_operationInProgress;
-    if (m_operationInProgress == FullCollection)
+    CollectionScope scope = *m_collectionScope;
+    if (scope == CollectionScope::Full)
         m_lastFullGCLength = gcEndTime - gcStartTime;
     else
         m_lastEdenGCLength = gcEndTime - gcStartTime;
@@ -1322,11 +1320,11 @@
         removeDeadHeapSnapshotNodes(*heapProfiler);
     }
 
-    RELEASE_ASSERT(m_operationInProgress == EdenCollection || m_operationInProgress == FullCollection);
-    m_operationInProgress = NoOperation;
+    RELEASE_ASSERT(m_collectionScope);
+    m_collectionScope = Nullopt;
 
     for (auto* observer : m_observers)
-        observer->didGarbageCollect(operation);
+        observer->didGarbageCollect(scope);
 }
 
 void Heap::resumeCompilerThreads()
@@ -1388,7 +1386,7 @@
     if (!isValidThreadState(m_vm))
         return false;
 
-    if (m_operationInProgress != NoOperation)
+    if (isCurrentThreadBusy())
         return false;
     
     return true;
@@ -1465,24 +1463,14 @@
     m_writeBarrierBuffer.add(cell);
 }
 
-bool Heap::shouldDoFullCollection(HeapOperation requestedCollectionType) const
+bool Heap::shouldDoFullCollection(Optional<CollectionScope> scope) const
 {
     if (!Options::useGenerationalGC())
         return true;
 
-    switch (requestedCollectionType) {
-    case EdenCollection:
-        return false;
-    case FullCollection:
-        return true;
-    case AnyCollection:
+    if (!scope)
         return m_shouldDoFullCollection;
-    default:
-        RELEASE_ASSERT_NOT_REACHED();
-        return false;
-    }
-    RELEASE_ASSERT_NOT_REACHED();
-    return false;
+    return *scope == CollectionScope::Full;
 }
 
 void Heap::addLogicallyEmptyWeakBlock(WeakBlock* block)
diff --git a/Source/JavaScriptCore/heap/Heap.h b/Source/JavaScriptCore/heap/Heap.h
index 85b3525..0a490de 100644
--- a/Source/JavaScriptCore/heap/Heap.h
+++ b/Source/JavaScriptCore/heap/Heap.h
@@ -22,17 +22,18 @@
 #pragma once
 
 #include "ArrayBuffer.h"
+#include "CollectionScope.h"
 #include "GCIncomingRefCountedSet.h"
 #include "HandleSet.h"
 #include "HandleStack.h"
 #include "HeapObserver.h"
-#include "HeapOperation.h"
 #include "ListableHandler.h"
 #include "MachineStackMarker.h"
 #include "MarkedAllocator.h"
 #include "MarkedBlock.h"
 #include "MarkedBlockSet.h"
 #include "MarkedSpace.h"
+#include "MutatorState.h"
 #include "Options.h"
 #include "SlotVisitor.h"
 #include "StructureIDTable.h"
@@ -48,7 +49,6 @@
 
 namespace JSC {
 
-class AllocationScope;
 class CodeBlock;
 class CodeBlockSet;
 class GCDeferralContext;
@@ -61,6 +61,7 @@
 class HeapProfiler;
 class HeapRootVisitor;
 class HeapVerifier;
+class HelpingGCScope;
 class IncrementalSweeper;
 class JITStubRoutine;
 class JITStubRoutineSet;
@@ -139,11 +140,13 @@
     void addObserver(HeapObserver* observer) { m_observers.append(observer); }
     void removeObserver(HeapObserver* observer) { m_observers.removeFirst(observer); }
 
-    // true if collection is in progress
-    bool isCollecting();
-    HeapOperation operationInProgress() { return m_operationInProgress; }
-    // true if an allocation or collection is in progress
-    bool isBusy();
+    MutatorState mutatorState() const { return m_mutatorState; }
+    Optional<CollectionScope> collectionScope() const { return m_collectionScope; }
+
+    // We're always busy on the collection threads. On the main thread, this returns true if we're
+    // helping heap.
+    bool isCurrentThreadBusy();
+    
     MarkedSpace::Subspace& subspaceForObjectWithoutDestructor() { return m_objectSpace.subspaceForObjectsWithoutDestructor(); }
     MarkedSpace::Subspace& subspaceForObjectDestructor() { return m_objectSpace.subspaceForObjectsWithDestructor(); }
     MarkedSpace::Subspace& subspaceForAuxiliaryData() { return m_objectSpace.subspaceForAuxiliaryData(); }
@@ -171,7 +174,7 @@
     JS_EXPORT_PRIVATE void collectAllGarbage();
 
     bool shouldCollect();
-    JS_EXPORT_PRIVATE void collect(HeapOperation collectionType = AnyCollection);
+    JS_EXPORT_PRIVATE void collect(Optional<CollectionScope> = Nullopt);
     bool collectIfNecessaryOrDefer(GCDeferralContext* = nullptr); // Returns true if it did collect.
     void collectAccordingToDeferGCProbability();
 
@@ -268,7 +271,7 @@
     const unsigned* addressOfBarrierThreshold() const { return &m_barrierThreshold; }
 
 private:
-    friend class AllocationScope;
+    friend class AllocatingScope;
     friend class CodeBlock;
     friend class DeferGC;
     friend class DeferGCForAWhile;
@@ -278,6 +281,7 @@
     friend class HandleSet;
     friend class HeapUtil;
     friend class HeapVerifier;
+    friend class HelpingGCScope;
     friend class JITStubRoutine;
     friend class LLIntOffsetsExtractor;
     friend class MarkedSpace;
@@ -293,7 +297,7 @@
     template<typename T> friend void* allocateCell(Heap&, GCDeferralContext*);
     template<typename T> friend void* allocateCell(Heap&, GCDeferralContext*, size_t);
 
-    void collectWithoutAnySweep(HeapOperation collectionType = AnyCollection);
+    void collectWithoutAnySweep(Optional<CollectionScope> = Nullopt);
 
     void* allocateWithDestructor(size_t); // For use with objects with destructors.
     void* allocateWithoutDestructor(size_t); // For use with objects without destructors.
@@ -312,10 +316,10 @@
     JS_EXPORT_PRIVATE void reportExtraMemoryAllocatedSlowCase(size_t);
     JS_EXPORT_PRIVATE void deprecatedReportExtraMemorySlowCase(size_t);
 
-    void collectImpl(HeapOperation, void* stackOrigin, void* stackTop, MachineThreads::RegisterState&);
+    void collectImpl(Optional<CollectionScope>, void* stackOrigin, void* stackTop, MachineThreads::RegisterState&);
 
     void suspendCompilerThreads();
-    void willStartCollection(HeapOperation collectionType);
+    void willStartCollection(Optional<CollectionScope>);
     void flushOldStructureIDTables();
     void flushWriteBarrierBuffer();
     void stopAllocation();
@@ -366,7 +370,7 @@
     void sweepAllLogicallyEmptyWeakBlocks();
     bool sweepNextLogicallyEmptyWeakBlock();
 
-    bool shouldDoFullCollection(HeapOperation requestedCollectionType) const;
+    bool shouldDoFullCollection(Optional<CollectionScope> requestedCollectionScope) const;
 
     void incrementDeferralDepth();
     void decrementDeferralDepth();
@@ -394,7 +398,8 @@
     size_t m_totalBytesVisited;
     size_t m_totalBytesVisitedThisCycle;
     
-    HeapOperation m_operationInProgress;
+    Optional<CollectionScope> m_collectionScope;
+    MutatorState m_mutatorState { MutatorState::Running };
     StructureIDTable m_structureIDTable;
     MarkedSpace m_objectSpace;
     GCIncomingRefCountedSet<ArrayBuffer> m_arrayBuffers;
diff --git a/Source/JavaScriptCore/heap/HeapInlines.h b/Source/JavaScriptCore/heap/HeapInlines.h
index d8c0db0..e9a8e92 100644
--- a/Source/JavaScriptCore/heap/HeapInlines.h
+++ b/Source/JavaScriptCore/heap/HeapInlines.h
@@ -45,21 +45,16 @@
         return false;
     if (!m_isSafeToCollect)
         return false;
-    if (m_operationInProgress != NoOperation)
+    if (collectionScope() || mutatorState() == MutatorState::HelpingGC)
         return false;
     if (Options::gcMaxHeapSize())
         return m_bytesAllocatedThisCycle > Options::gcMaxHeapSize();
     return m_bytesAllocatedThisCycle > m_maxEdenSize;
 }
 
-inline bool Heap::isBusy()
+inline bool Heap::isCurrentThreadBusy()
 {
-    return m_operationInProgress != NoOperation;
-}
-
-inline bool Heap::isCollecting()
-{
-    return m_operationInProgress == FullCollection || m_operationInProgress == EdenCollection;
+    return mayBeGCThread() || mutatorState() != MutatorState::Running;
 }
 
 ALWAYS_INLINE Heap* Heap::heap(const HeapCell* cell)
@@ -76,7 +71,7 @@
 
 ALWAYS_INLINE bool Heap::isMarked(const void* rawCell)
 {
-    ASSERT(!mayBeGCThread());
+    ASSERT(mayBeGCThread() != GCThreadType::Helper);
     HeapCell* cell = bitwise_cast<HeapCell*>(rawCell);
     if (cell->isLargeAllocation())
         return cell->largeAllocation().isMarked();
@@ -161,7 +156,7 @@
 inline void Heap::reportExtraMemoryVisited(CellState oldState, size_t size)
 {
     // We don't want to double-count the extra memory that was reported in previous collections.
-    if (operationInProgress() == EdenCollection && oldState == CellState::OldGrey)
+    if (collectionScope() == CollectionScope::Eden && oldState == CellState::OldGrey)
         return;
 
     size_t* counter = &m_extraMemorySize;
@@ -177,7 +172,7 @@
 inline void Heap::reportExternalMemoryVisited(CellState oldState, size_t size)
 {
     // We don't want to double-count the external memory that was reported in previous collections.
-    if (operationInProgress() == EdenCollection && oldState == CellState::OldGrey)
+    if (collectionScope() == CollectionScope::Eden && oldState == CellState::OldGrey)
         return;
 
     size_t* counter = &m_externalMemorySize;
@@ -372,7 +367,7 @@
 
 inline void Heap::collectAccordingToDeferGCProbability()
 {
-    if (isDeferred() || !m_isSafeToCollect || m_operationInProgress != NoOperation)
+    if (isDeferred() || !m_isSafeToCollect || collectionScope() || mutatorState() == MutatorState::HelpingGC)
         return;
 
     if (randomNumber() < Options::deferGCProbability()) {
diff --git a/Source/JavaScriptCore/heap/HeapObserver.h b/Source/JavaScriptCore/heap/HeapObserver.h
index 7abe60d..ccbb7e6 100644
--- a/Source/JavaScriptCore/heap/HeapObserver.h
+++ b/Source/JavaScriptCore/heap/HeapObserver.h
@@ -25,7 +25,7 @@
 
 #pragma once
 
-#include "HeapOperation.h"
+#include "CollectionScope.h"
 
 namespace JSC {
 
@@ -33,7 +33,7 @@
 public:
     virtual ~HeapObserver() { }
     virtual void willGarbageCollect() = 0;
-    virtual void didGarbageCollect(HeapOperation) = 0;
+    virtual void didGarbageCollect(CollectionScope) = 0;
 };
 
 } // namespace JSC
diff --git a/Source/JavaScriptCore/heap/HeapVerifier.cpp b/Source/JavaScriptCore/heap/HeapVerifier.cpp
index 153881f..c779cf1 100644
--- a/Source/JavaScriptCore/heap/HeapVerifier.cpp
+++ b/Source/JavaScriptCore/heap/HeapVerifier.cpp
@@ -43,24 +43,6 @@
     m_cycles = std::make_unique<GCCycle[]>(m_numberOfCycles);
 }
 
-const char* HeapVerifier::collectionTypeName(HeapOperation type)
-{
-    switch (type) {
-    case NoOperation:
-        return "NoOperation";
-    case AnyCollection:
-        return "AnyCollection";
-    case Allocation:
-        return "Allocation";
-    case EdenCollection:
-        return "EdenCollection";
-    case FullCollection:
-        return "FullCollection";
-    }
-    RELEASE_ASSERT_NOT_REACHED();
-    return nullptr; // Silencing a compiler warning.
-}
-
 const char* HeapVerifier::phaseName(HeapVerifier::Phase phase)
 {
     switch (phase) {
@@ -81,7 +63,7 @@
 {
     Heap* heap = m_heap;
     incrementCycle();
-    currentCycle().collectionType = heap->operationInProgress();
+    currentCycle().scope = *heap->collectionScope();
 }
 
 struct GatherLiveObjFunctor : MarkedBlock::CountFunctor {
@@ -195,7 +177,7 @@
 
     if (objData.isConfirmedDead) {
         dataLogF("FOUND dead obj %p in GC[%d] %s list '%s'\n",
-            obj, cycleIndex, cycle.collectionTypeName(), list.name);
+            obj, cycleIndex, collectionScopeName(cycle.scope), list.name);
         return;
     }
 
@@ -206,7 +188,7 @@
     dataLogF("FOUND obj %p type '%s' butterfly %p (base %p) in GC[%d] %s list '%s'\n",
         obj, structure->classInfo()->className,
         butterfly, butterflyBase,
-        cycleIndex, cycle.collectionTypeName(), list.name);
+        cycleIndex, collectionScopeName(cycle.scope), list.name);
 }
 
 void HeapVerifier::checkIfRecorded(JSObject* obj)
diff --git a/Source/JavaScriptCore/heap/HeapVerifier.h b/Source/JavaScriptCore/heap/HeapVerifier.h
index 50551ec..d736b47 100644
--- a/Source/JavaScriptCore/heap/HeapVerifier.h
+++ b/Source/JavaScriptCore/heap/HeapVerifier.h
@@ -54,7 +54,6 @@
     // object was in any of those lists.
     JS_EXPORT_PRIVATE void checkIfRecorded(JSObject*);
 
-    static const char* collectionTypeName(HeapOperation);
     static const char* phaseName(Phase);
 
 private:
@@ -65,14 +64,9 @@
         {
         }
 
-        HeapOperation collectionType;
+        CollectionScope scope;
         LiveObjectList before;
         LiveObjectList after;
-
-        const char* collectionTypeName() const
-        {
-            return HeapVerifier::collectionTypeName(collectionType);
-        }
     };
 
     void incrementCycle() { m_currentCycle = (m_currentCycle + 1) % m_numberOfCycles; }
diff --git a/Source/JavaScriptCore/heap/HeapOperation.h b/Source/JavaScriptCore/heap/HelpingGCScope.h
similarity index 76%
copy from Source/JavaScriptCore/heap/HeapOperation.h
copy to Source/JavaScriptCore/heap/HelpingGCScope.h
index fbb8283..e8f4085 100644
--- a/Source/JavaScriptCore/heap/HeapOperation.h
+++ b/Source/JavaScriptCore/heap/HelpingGCScope.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -25,16 +25,28 @@
 
 #pragma once
 
+#include "Heap.h"
+
 namespace JSC {
 
-enum HeapOperation { NoOperation, Allocation, FullCollection, EdenCollection, AnyCollection };
+class HelpingGCScope {
+public:
+    HelpingGCScope(Heap& heap)
+        : m_heap(heap)
+        , m_oldState(m_heap.m_mutatorState)
+    {
+        m_heap.m_mutatorState = MutatorState::HelpingGC;
+    }
+    
+    ~HelpingGCScope()
+    {
+        m_heap.m_mutatorState = m_oldState;
+    }
+
+private:
+    Heap& m_heap;
+    MutatorState m_oldState;
+};
 
 } // namespace JSC
 
-namespace WTF {
-
-class PrintStream;
-
-void printInternal(PrintStream& out, JSC::HeapOperation);
-
-} // namespace WTF
diff --git a/Source/JavaScriptCore/heap/LargeAllocation.cpp b/Source/JavaScriptCore/heap/LargeAllocation.cpp
index 09ca109..eb839dd 100644
--- a/Source/JavaScriptCore/heap/LargeAllocation.cpp
+++ b/Source/JavaScriptCore/heap/LargeAllocation.cpp
@@ -77,7 +77,7 @@
 
 void LargeAllocation::flip()
 {
-    ASSERT(heap()->operationInProgress() == FullCollection);
+    ASSERT(heap()->collectionScope() == CollectionScope::Full);
     clearMarked();
 }
 
diff --git a/Source/JavaScriptCore/heap/MarkedAllocator.cpp b/Source/JavaScriptCore/heap/MarkedAllocator.cpp
index 72d9230..2ceadaa 100644
--- a/Source/JavaScriptCore/heap/MarkedAllocator.cpp
+++ b/Source/JavaScriptCore/heap/MarkedAllocator.cpp
@@ -26,7 +26,7 @@
 #include "config.h"
 #include "MarkedAllocator.h"
 
-#include "AllocationScope.h"
+#include "AllocatingScope.h"
 #include "GCActivityCallback.h"
 #include "Heap.h"
 #include "IncrementalSweeper.h"
@@ -185,7 +185,6 @@
             else
                 m_heap->collectAllGarbage();
         }
-        ASSERT(m_heap->m_operationInProgress == NoOperation);
     }
     if (++allocationCount >= Options::slowPathAllocsBetweenGCs())
         allocationCount = 0;
@@ -214,10 +213,10 @@
     
     didConsumeFreeList();
     
+    AllocatingScope healpingHeap(*m_heap);
+
     m_heap->collectIfNecessaryOrDefer(deferralContext);
     
-    AllocationScope allocationScope(*m_heap);
-
     void* result = tryAllocateWithoutCollecting();
     
     if (LIKELY(result != 0))
diff --git a/Source/JavaScriptCore/heap/MarkedBlock.h b/Source/JavaScriptCore/heap/MarkedBlock.h
index 8db44c466..90a02b8 100644
--- a/Source/JavaScriptCore/heap/MarkedBlock.h
+++ b/Source/JavaScriptCore/heap/MarkedBlock.h
@@ -25,7 +25,6 @@
 #include "DestructionMode.h"
 #include "FreeList.h"
 #include "HeapCell.h"
-#include "HeapOperation.h"
 #include "IterationStatus.h"
 #include "WeakSet.h"
 #include <wtf/Atomics.h>
diff --git a/Source/JavaScriptCore/heap/MarkedSpace.cpp b/Source/JavaScriptCore/heap/MarkedSpace.cpp
index a5b0397..b0500e3 100644
--- a/Source/JavaScriptCore/heap/MarkedSpace.cpp
+++ b/Source/JavaScriptCore/heap/MarkedSpace.cpp
@@ -338,7 +338,7 @@
 
     m_activeWeakSets.takeFrom(m_newActiveWeakSets);
     
-    if (m_heap->operationInProgress() == EdenCollection)
+    if (m_heap->collectionScope() == CollectionScope::Eden)
         m_largeAllocationsNurseryOffsetForSweep = m_largeAllocationsNurseryOffset;
     else
         m_largeAllocationsNurseryOffsetForSweep = 0;
@@ -355,7 +355,7 @@
     
     m_newActiveWeakSets.forEach(visit);
     
-    if (m_heap->operationInProgress() == FullCollection)
+    if (m_heap->collectionScope() == CollectionScope::Full)
         m_activeWeakSets.forEach(visit);
 }
 
@@ -367,7 +367,7 @@
     
     m_newActiveWeakSets.forEach(visit);
     
-    if (m_heap->operationInProgress() == FullCollection)
+    if (m_heap->collectionScope() == CollectionScope::Full)
         m_activeWeakSets.forEach(visit);
 }
 
@@ -383,7 +383,7 @@
 
 void MarkedSpace::prepareForMarking()
 {
-    if (m_heap->operationInProgress() == EdenCollection)
+    if (m_heap->collectionScope() == CollectionScope::Eden)
         m_largeAllocationsOffsetForThisCollection = m_largeAllocationsNurseryOffset;
     else
         m_largeAllocationsOffsetForThisCollection = 0;
@@ -453,7 +453,7 @@
 
 void MarkedSpace::beginMarking()
 {
-    if (m_heap->operationInProgress() == FullCollection) {
+    if (m_heap->collectionScope() == CollectionScope::Full) {
         forEachAllocator(
             [&] (MarkedAllocator& allocator) -> IterationStatus {
                 allocator.beginMarkingForFullCollection();
@@ -597,7 +597,7 @@
 
 void MarkedSpace::snapshotUnswept()
 {
-    if (m_heap->operationInProgress() == EdenCollection) {
+    if (m_heap->collectionScope() == CollectionScope::Eden) {
         forEachAllocator(
             [&] (MarkedAllocator& allocator) -> IterationStatus {
                 allocator.snapshotUnsweptForEdenCollection();
diff --git a/Source/JavaScriptCore/heap/HeapOperation.cpp b/Source/JavaScriptCore/heap/MutatorState.cpp
similarity index 79%
rename from Source/JavaScriptCore/heap/HeapOperation.cpp
rename to Source/JavaScriptCore/heap/MutatorState.cpp
index 8715314..8368f43 100644
--- a/Source/JavaScriptCore/heap/HeapOperation.cpp
+++ b/Source/JavaScriptCore/heap/MutatorState.cpp
@@ -24,31 +24,25 @@
  */
 
 #include "config.h"
-#include "HeapOperation.h"
+#include "MutatorState.h"
 
 #include <wtf/PrintStream.h>
 
-namespace WTF {
-
 using namespace JSC;
 
-void printInternal(PrintStream& out, HeapOperation operation)
+namespace WTF {
+
+void printInternal(PrintStream& out, MutatorState state)
 {
-    switch (operation) {
-    case NoOperation:
-        out.print("None");
+    switch (state) {
+    case MutatorState::Running:
+        out.print("Running");
         return;
-    case Allocation:
-        out.print("Alloc");
+    case MutatorState::Allocating:
+        out.print("Allocating");
         return;
-    case FullCollection:
-        out.print("Full");
-        return;
-    case EdenCollection:
-        out.print("Eden");
-        return;
-    case AnyCollection:
-        out.print("Any");
+    case MutatorState::HelpingGC:
+        out.print("HelpingGC");
         return;
     }
     RELEASE_ASSERT_NOT_REACHED();
@@ -56,5 +50,3 @@
 
 } // namespace WTF
 
-
-
diff --git a/Source/JavaScriptCore/heap/HeapOperation.h b/Source/JavaScriptCore/heap/MutatorState.h
similarity index 79%
copy from Source/JavaScriptCore/heap/HeapOperation.h
copy to Source/JavaScriptCore/heap/MutatorState.h
index fbb8283..65b61f7 100644
--- a/Source/JavaScriptCore/heap/HeapOperation.h
+++ b/Source/JavaScriptCore/heap/MutatorState.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -27,7 +27,16 @@
 
 namespace JSC {
 
-enum HeapOperation { NoOperation, Allocation, FullCollection, EdenCollection, AnyCollection };
+enum class MutatorState {
+    // The mutator is running when it's not inside a Heap slow path.
+    Running,
+    
+    // The mutator is in an allocation slow path.
+    Allocating,
+    
+    // The mutator was asked by the GC to do some work.
+    HelpingGC
+};
 
 } // namespace JSC
 
@@ -35,6 +44,7 @@
 
 class PrintStream;
 
-void printInternal(PrintStream& out, JSC::HeapOperation);
+void printInternal(PrintStream&, JSC::MutatorState);
 
 } // namespace WTF
+
diff --git a/Source/JavaScriptCore/heap/SlotVisitor.cpp b/Source/JavaScriptCore/heap/SlotVisitor.cpp
index 6856d8f..a804e89 100644
--- a/Source/JavaScriptCore/heap/SlotVisitor.cpp
+++ b/Source/JavaScriptCore/heap/SlotVisitor.cpp
@@ -96,7 +96,7 @@
 
 void SlotVisitor::didStartMarking()
 {
-    if (heap()->operationInProgress() == FullCollection)
+    if (heap()->collectionScope() == CollectionScope::Full)
         ASSERT(m_opaqueRoots.isEmpty()); // Should have merged by now.
     else
         reset();
diff --git a/Source/JavaScriptCore/inspector/agents/InspectorHeapAgent.cpp b/Source/JavaScriptCore/inspector/agents/InspectorHeapAgent.cpp
index 17e5e4d..404169f 100644
--- a/Source/JavaScriptCore/inspector/agents/InspectorHeapAgent.cpp
+++ b/Source/JavaScriptCore/inspector/agents/InspectorHeapAgent.cpp
@@ -320,17 +320,16 @@
     result = injectedScript.wrapObject(cell, objectGroup, true);
 }
 
-static Inspector::Protocol::Heap::GarbageCollection::Type protocolTypeForHeapOperation(HeapOperation operation)
+static Inspector::Protocol::Heap::GarbageCollection::Type protocolTypeForHeapOperation(CollectionScope scope)
 {
-    switch (operation) {
-    case FullCollection:
+    switch (scope) {
+    case CollectionScope::Full:
         return Inspector::Protocol::Heap::GarbageCollection::Type::Full;
-    case EdenCollection:
+    case CollectionScope::Eden:
         return Inspector::Protocol::Heap::GarbageCollection::Type::Partial;
-    default:
-        ASSERT_NOT_REACHED();
-        return Inspector::Protocol::Heap::GarbageCollection::Type::Full;
     }
+    ASSERT_NOT_REACHED();
+    return Inspector::Protocol::Heap::GarbageCollection::Type::Full;
 }
 
 void InspectorHeapAgent::willGarbageCollect()
@@ -341,7 +340,7 @@
     m_gcStartTime = m_environment.executionStopwatch()->elapsedTime();
 }
 
-void InspectorHeapAgent::didGarbageCollect(HeapOperation operation)
+void InspectorHeapAgent::didGarbageCollect(CollectionScope scope)
 {
     ASSERT(m_enabled);
     ASSERT(!std::isnan(m_gcStartTime));
@@ -356,7 +355,7 @@
     // VM as the inspected page.
 
     GarbageCollectionData data;
-    data.type = protocolTypeForHeapOperation(operation);
+    data.type = protocolTypeForHeapOperation(scope);
     data.startTime = m_gcStartTime;
     data.endTime = m_environment.executionStopwatch()->elapsedTime();
 
diff --git a/Source/JavaScriptCore/inspector/agents/InspectorHeapAgent.h b/Source/JavaScriptCore/inspector/agents/InspectorHeapAgent.h
index 18e8dc3..ba7382b 100644
--- a/Source/JavaScriptCore/inspector/agents/InspectorHeapAgent.h
+++ b/Source/JavaScriptCore/inspector/agents/InspectorHeapAgent.h
@@ -60,7 +60,7 @@
 
     // HeapObserver
     void willGarbageCollect() override;
-    void didGarbageCollect(JSC::HeapOperation) override;
+    void didGarbageCollect(JSC::CollectionScope) override;
 
 protected:
     void clearHeapSnapshots();
diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp
index 91920dc..71cfa1a 100644
--- a/Source/JavaScriptCore/interpreter/Interpreter.cpp
+++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp
@@ -755,9 +755,9 @@
     auto throwScope = DECLARE_THROW_SCOPE(vm);
 
     ASSERT(!throwScope.exception());
-    ASSERT(!vm.isCollectorBusy());
+    ASSERT(!vm.isCollectorBusyOnCurrentThread());
     RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock());
-    if (vm.isCollectorBusy())
+    if (vm.isCollectorBusyOnCurrentThread())
         return jsNull();
 
     if (UNLIKELY(!vm.isSafeToRecurseSoft()))
@@ -900,8 +900,8 @@
     auto throwScope = DECLARE_THROW_SCOPE(vm);
 
     ASSERT(!throwScope.exception());
-    ASSERT(!vm.isCollectorBusy());
-    if (vm.isCollectorBusy())
+    ASSERT(!vm.isCollectorBusyOnCurrentThread());
+    if (vm.isCollectorBusyOnCurrentThread())
         return jsNull();
 
     bool isJSCall = (callType == CallType::JS);
@@ -962,10 +962,10 @@
     auto throwScope = DECLARE_THROW_SCOPE(vm);
 
     ASSERT(!throwScope.exception());
-    ASSERT(!vm.isCollectorBusy());
+    ASSERT(!vm.isCollectorBusyOnCurrentThread());
     // We throw in this case because we have to return something "valid" but we're
     // already in an invalid state.
-    if (vm.isCollectorBusy())
+    if (vm.isCollectorBusyOnCurrentThread())
         return checkedReturn(throwStackOverflowError(callFrame, throwScope));
 
     bool isJSConstruct = (constructType == ConstructType::JS);
@@ -1028,7 +1028,7 @@
     auto throwScope = DECLARE_THROW_SCOPE(vm);
     ASSERT(!throwScope.exception());
     
-    if (vm.isCollectorBusy())
+    if (vm.isCollectorBusyOnCurrentThread())
         return CallFrameClosure();
 
     // Compile the callee:
@@ -1053,9 +1053,9 @@
     VM& vm = *closure.vm;
     auto throwScope = DECLARE_THROW_SCOPE(vm);
 
-    ASSERT(!vm.isCollectorBusy());
+    ASSERT(!vm.isCollectorBusyOnCurrentThread());
     RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock());
-    if (vm.isCollectorBusy())
+    if (vm.isCollectorBusyOnCurrentThread())
         return jsNull();
 
     StackStats::CheckPoint stackCheckPoint;
@@ -1076,9 +1076,9 @@
 
     ASSERT(scope->vm() == &callFrame->vm());
     ASSERT(!throwScope.exception());
-    ASSERT(!vm.isCollectorBusy());
+    ASSERT(!vm.isCollectorBusyOnCurrentThread());
     RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock());
-    if (vm.isCollectorBusy())
+    if (vm.isCollectorBusyOnCurrentThread())
         return jsNull();
 
     VMEntryScope entryScope(vm, scope->globalObject());
@@ -1182,9 +1182,9 @@
 
     ASSERT(scope->vm() == &callFrame->vm());
     ASSERT(!throwScope.exception());
-    ASSERT(!vm.isCollectorBusy());
+    ASSERT(!vm.isCollectorBusyOnCurrentThread());
     RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock());
-    if (vm.isCollectorBusy())
+    if (vm.isCollectorBusyOnCurrentThread())
         return jsNull();
 
     VMEntryScope entryScope(vm, scope->globalObject());
diff --git a/Source/JavaScriptCore/jsc.cpp b/Source/JavaScriptCore/jsc.cpp
index b17c25f..a8e7da4 100644
--- a/Source/JavaScriptCore/jsc.cpp
+++ b/Source/JavaScriptCore/jsc.cpp
@@ -1677,14 +1677,14 @@
 EncodedJSValue JSC_HOST_CALL functionFullGC(ExecState* exec)
 {
     JSLockHolder lock(exec);
-    exec->heap()->collect(FullCollection);
+    exec->heap()->collect(CollectionScope::Full);
     return JSValue::encode(jsNumber(exec->heap()->sizeAfterLastFullCollection()));
 }
 
 EncodedJSValue JSC_HOST_CALL functionEdenGC(ExecState* exec)
 {
     JSLockHolder lock(exec);
-    exec->heap()->collect(EdenCollection);
+    exec->heap()->collect(CollectionScope::Eden);
     return JSValue::encode(jsNumber(exec->heap()->sizeAfterLastEdenCollection()));
 }
 
diff --git a/Source/JavaScriptCore/runtime/Completion.cpp b/Source/JavaScriptCore/runtime/Completion.cpp
index d0aea68..731d861 100644
--- a/Source/JavaScriptCore/runtime/Completion.cpp
+++ b/Source/JavaScriptCore/runtime/Completion.cpp
@@ -91,7 +91,7 @@
     JSLockHolder lock(vm);
     auto scope = DECLARE_CATCH_SCOPE(vm);
     RELEASE_ASSERT(vm.atomicStringTable() == wtfThreadData().atomicStringTable());
-    RELEASE_ASSERT(!vm.isCollectorBusy());
+    RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread());
 
     CodeProfiling profile(source);
 
@@ -174,7 +174,7 @@
 {
     JSLockHolder lock(exec);
     RELEASE_ASSERT(exec->vm().atomicStringTable() == wtfThreadData().atomicStringTable());
-    RELEASE_ASSERT(!exec->vm().isCollectorBusy());
+    RELEASE_ASSERT(!exec->vm().isCollectorBusyOnCurrentThread());
 
     return loadAndEvaluateModule(lock, exec, exec->vmEntryGlobalObject(), Identifier::fromString(exec, moduleName), initiator);
 }
@@ -185,7 +185,7 @@
     JSLockHolder lock(vm);
     auto scope = DECLARE_THROW_SCOPE(vm);
     RELEASE_ASSERT(vm.atomicStringTable() == wtfThreadData().atomicStringTable());
-    RELEASE_ASSERT(!vm.isCollectorBusy());
+    RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread());
 
     Symbol* key = createSymbolForEntryPointModule(vm);
 
@@ -212,7 +212,7 @@
 {
     JSLockHolder lock(exec);
     RELEASE_ASSERT(exec->vm().atomicStringTable() == wtfThreadData().atomicStringTable());
-    RELEASE_ASSERT(!exec->vm().isCollectorBusy());
+    RELEASE_ASSERT(!exec->vm().isCollectorBusyOnCurrentThread());
 
     return loadModule(lock, exec, exec->vmEntryGlobalObject(), Identifier::fromString(exec, moduleName), initiator);
 }
@@ -223,7 +223,7 @@
     JSLockHolder lock(vm);
     auto scope = DECLARE_THROW_SCOPE(vm);
     RELEASE_ASSERT(vm.atomicStringTable() == wtfThreadData().atomicStringTable());
-    RELEASE_ASSERT(!vm.isCollectorBusy());
+    RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread());
 
     Symbol* key = createSymbolForEntryPointModule(vm);
 
@@ -241,7 +241,7 @@
 {
     JSLockHolder lock(exec);
     RELEASE_ASSERT(exec->vm().atomicStringTable() == wtfThreadData().atomicStringTable());
-    RELEASE_ASSERT(!exec->vm().isCollectorBusy());
+    RELEASE_ASSERT(!exec->vm().isCollectorBusyOnCurrentThread());
 
     JSGlobalObject* globalObject = exec->vmEntryGlobalObject();
     return globalObject->moduleLoader()->linkAndEvaluateModule(exec, identifierToJSValue(exec->vm(), moduleKey), initiator);
diff --git a/Source/JavaScriptCore/runtime/JSLock.cpp b/Source/JavaScriptCore/runtime/JSLock.cpp
index 09250f6..82a2171 100644
--- a/Source/JavaScriptCore/runtime/JSLock.cpp
+++ b/Source/JavaScriptCore/runtime/JSLock.cpp
@@ -267,7 +267,7 @@
 {
     if (!m_vm)
         return;
-    RELEASE_ASSERT(!m_vm->apiLock().currentThreadIsHoldingLock() || !m_vm->isCollectorBusy());
+    RELEASE_ASSERT(!m_vm->apiLock().currentThreadIsHoldingLock() || !m_vm->isCollectorBusyOnCurrentThread());
     m_droppedLockCount = m_vm->apiLock().dropAllLocks(this);
 }
 
diff --git a/Source/JavaScriptCore/runtime/SmallStrings.h b/Source/JavaScriptCore/runtime/SmallStrings.h
index 0e02c1d..263b427 100644
--- a/Source/JavaScriptCore/runtime/SmallStrings.h
+++ b/Source/JavaScriptCore/runtime/SmallStrings.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2009, 2015 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2008-2009, 2015-2016 Apple Inc. All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -113,9 +113,9 @@
     JSString* nullObjectString() const { return m_nullObjectString; }
     JSString* undefinedObjectString() const { return m_undefinedObjectString; }
 
-    bool needsToBeVisited(HeapOperation collectionType) const
+    bool needsToBeVisited(CollectionScope scope) const
     {
-        if (collectionType == FullCollection)
+        if (scope == CollectionScope::Full)
             return true;
         return m_needsToBeVisited;
     }
diff --git a/Source/JavaScriptCore/runtime/VM.h b/Source/JavaScriptCore/runtime/VM.h
index f55f3d3..a97f1810 100644
--- a/Source/JavaScriptCore/runtime/VM.h
+++ b/Source/JavaScriptCore/runtime/VM.h
@@ -576,7 +576,7 @@
 #endif
     JS_EXPORT_PRIVATE void dumpRegExpTrace();
 
-    bool isCollectorBusy() { return heap.isBusy(); }
+    bool isCollectorBusyOnCurrentThread() { return heap.isCurrentThreadBusy(); }
 
 #if ENABLE(GC_VALIDATION)
     bool isInitializingObject() const; 
diff --git a/Source/JavaScriptCore/tools/JSDollarVMPrototype.cpp b/Source/JavaScriptCore/tools/JSDollarVMPrototype.cpp
index b52840a..bc5e869 100644
--- a/Source/JavaScriptCore/tools/JSDollarVMPrototype.cpp
+++ b/Source/JavaScriptCore/tools/JSDollarVMPrototype.cpp
@@ -130,7 +130,7 @@
 {
     if (!ensureCurrentThreadOwnsJSLock(exec))
         return;
-    exec->heap()->collect(EdenCollection);
+    exec->heap()->collect(CollectionScope::Eden);
 }
 
 static EncodedJSValue JSC_HOST_CALL functionEdenGC(ExecState* exec)
diff --git a/Source/WTF/ChangeLog b/Source/WTF/ChangeLog
index 30ec9d9..fb77570 100644
--- a/Source/WTF/ChangeLog
+++ b/Source/WTF/ChangeLog
@@ -1,3 +1,19 @@
+2016-10-20  Filip Pizlo  <fpizlo@apple.com>
+
+        The tracking of the coarse-grain Heap state (allocating or not, collector or not, eden vs full) should respect the orthogonality between allocating and collecting
+        https://bugs.webkit.org/show_bug.cgi?id=163738
+
+        Reviewed by Geoffrey Garen.
+        
+        There will soon be different kinds of GC threads, and WTF's "are you a GC thread" thing
+        should know about this.
+
+        * wtf/MainThread.cpp:
+        (WTF::initializeGCThreads):
+        (WTF::registerGCThread):
+        (WTF::mayBeGCThread):
+        * wtf/MainThread.h:
+
 2016-10-19  Sam Weinig  <sam@webkit.org>
 
         Add convenience function that combines WTF::visit(...) with WTF::makeVisitor(...)
diff --git a/Source/WTF/wtf/MainThread.cpp b/Source/WTF/wtf/MainThread.cpp
index 1e4daae..946f02e 100644
--- a/Source/WTF/wtf/MainThread.cpp
+++ b/Source/WTF/wtf/MainThread.cpp
@@ -190,14 +190,14 @@
 }
 #endif
 
-static ThreadSpecific<bool>* isGCThread;
+static ThreadSpecific<Optional<GCThreadType>>* isGCThread;
 
 void initializeGCThreads()
 {
-    isGCThread = new ThreadSpecific<bool>();
+    isGCThread = new ThreadSpecific<Optional<GCThreadType>>();
 }
 
-void registerGCThread()
+void registerGCThread(GCThreadType type)
 {
     if (!isGCThread) {
         // This happens if we're running in a process that doesn't care about
@@ -205,7 +205,7 @@
         return;
     }
 
-    **isGCThread = true;
+    **isGCThread = type;
 }
 
 bool isMainThreadOrGCThread()
@@ -216,9 +216,13 @@
     return isMainThread();
 }
 
-bool mayBeGCThread()
+Optional<GCThreadType> mayBeGCThread()
 {
-    return isGCThread && isGCThread->isSet() && **isGCThread;
+    if (!isGCThread)
+        return Nullopt;
+    if (!isGCThread->isSet())
+        return Nullopt;
+    return **isGCThread;
 }
 
 } // namespace WTF
diff --git a/Source/WTF/wtf/MainThread.h b/Source/WTF/wtf/MainThread.h
index d7adc6c..402a18c 100644
--- a/Source/WTF/wtf/MainThread.h
+++ b/Source/WTF/wtf/MainThread.h
@@ -33,9 +33,12 @@
 #include <functional>
 #include <stdint.h>
 #include <wtf/Function.h>
+#include <wtf/Optional.h>
 
 namespace WTF {
 
+class PrintStream;
+
 typedef uint32_t ThreadIdentifier;
 
 // Must be called from the main thread.
@@ -67,8 +70,15 @@
 
 void initializeGCThreads();
 
-WTF_EXPORT_PRIVATE void registerGCThread();
-WTF_EXPORT_PRIVATE bool mayBeGCThread();
+enum class GCThreadType {
+    Main,
+    Helper
+};
+
+void printInternal(PrintStream&, GCThreadType);
+
+WTF_EXPORT_PRIVATE void registerGCThread(GCThreadType);
+WTF_EXPORT_PRIVATE Optional<GCThreadType> mayBeGCThread();
 WTF_EXPORT_PRIVATE bool isMainThreadOrGCThread();
 
 // NOTE: these functions are internal to the callOnMainThread implementation.
@@ -88,6 +98,7 @@
 
 } // namespace WTF
 
+using WTF::GCThreadType;
 using WTF::callOnMainThread;
 using WTF::canAccessThreadLocalDataForThread;
 using WTF::isMainThread;
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 12329aa..b7bf90b 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,15 @@
+2016-10-20  Filip Pizlo  <fpizlo@apple.com>
+
+        The tracking of the coarse-grain Heap state (allocating or not, collector or not, eden vs full) should respect the orthogonality between allocating and collecting
+        https://bugs.webkit.org/show_bug.cgi?id=163738
+
+        Reviewed by Geoffrey Garen.
+
+        No new tests because no change in behavior.
+
+        * bindings/js/GCController.cpp:
+        (WebCore::GCController::garbageCollectNow):
+
 2016-10-20  Chris Dumez  <cdumez@apple.com>
 
         [Bindings] Start using signature->idlType instead of signature->type in the overload resolution code
diff --git a/Source/WebCore/bindings/js/GCController.cpp b/Source/WebCore/bindings/js/GCController.cpp
index acfba39..bff3b0c 100644
--- a/Source/WebCore/bindings/js/GCController.cpp
+++ b/Source/WebCore/bindings/js/GCController.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2007, 2014, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -82,7 +82,7 @@
 void GCController::garbageCollectNow()
 {
     JSLockHolder lock(JSDOMWindow::commonVM());
-    if (!JSDOMWindow::commonVM().heap.isBusy()) {
+    if (!JSDOMWindow::commonVM().heap.isCurrentThreadBusy()) {
         JSDOMWindow::commonVM().heap.collectAllGarbage();
         WTF::releaseFastMallocFreeMemory();
     }
@@ -92,7 +92,7 @@
 {
 #if USE(CF) || USE(GLIB)
     JSLockHolder lock(JSDOMWindow::commonVM());
-    if (!JSDOMWindow::commonVM().heap.isBusy())
+    if (!JSDOMWindow::commonVM().heap.isCurrentThreadBusy())
         JSDOMWindow::commonVM().heap.collectAllGarbageIfNotDoneRecently();
 #else
     garbageCollectSoon();