[JSC] Reduce size of memory used for ShadowChicken
https://bugs.webkit.org/show_bug.cgi?id=193546

Reviewed by Mark Lam.

This patch lazily instantiate ShadowChicken. We do not need this until we start logging ShadowChicken packets.
The removal of ShadowChicken saves 55KB memory.

* debugger/DebuggerCallFrame.cpp:
(JSC::DebuggerCallFrame::create):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::ensureShadowChickenPacket):
* heap/Heap.cpp:
(JSC::Heap::stopThePeriphery):
(JSC::Heap::addCoreConstraints):
* jit/CCallHelpers.cpp:
(JSC::CCallHelpers::ensureShadowChickenPacket):
* jit/JITExceptions.cpp:
(JSC::genericUnwind):
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_log_shadow_chicken_prologue):
(JSC::JIT::emit_op_log_shadow_chicken_tail):
* jit/JITOpcodes32_64.cpp:
(JSC::JIT::emit_op_log_shadow_chicken_prologue):
(JSC::JIT::emit_op_log_shadow_chicken_tail):
* jit/JITOperations.cpp:
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::setDebugger):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::setDebugger): Deleted.
* runtime/VM.cpp:
(JSC::VM::VM):
(JSC::VM::ensureShadowChicken):
* runtime/VM.h:
(JSC::VM::shadowChicken):
* tools/JSDollarVM.cpp:
(JSC::functionShadowChickenFunctionsOnStack):
(JSC::changeDebuggerModeWhenIdle):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@240637 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 0857da5..b446191 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,46 @@
+2019-01-28  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        [JSC] Reduce size of memory used for ShadowChicken
+        https://bugs.webkit.org/show_bug.cgi?id=193546
+
+        Reviewed by Mark Lam.
+
+        This patch lazily instantiate ShadowChicken. We do not need this until we start logging ShadowChicken packets.
+        The removal of ShadowChicken saves 55KB memory.
+
+        * debugger/DebuggerCallFrame.cpp:
+        (JSC::DebuggerCallFrame::create):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::ensureShadowChickenPacket):
+        * heap/Heap.cpp:
+        (JSC::Heap::stopThePeriphery):
+        (JSC::Heap::addCoreConstraints):
+        * jit/CCallHelpers.cpp:
+        (JSC::CCallHelpers::ensureShadowChickenPacket):
+        * jit/JITExceptions.cpp:
+        (JSC::genericUnwind):
+        * jit/JITOpcodes.cpp:
+        (JSC::JIT::emit_op_log_shadow_chicken_prologue):
+        (JSC::JIT::emit_op_log_shadow_chicken_tail):
+        * jit/JITOpcodes32_64.cpp:
+        (JSC::JIT::emit_op_log_shadow_chicken_prologue):
+        (JSC::JIT::emit_op_log_shadow_chicken_tail):
+        * jit/JITOperations.cpp:
+        * llint/LLIntSlowPaths.cpp:
+        (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::setDebugger):
+        * runtime/JSGlobalObject.h:
+        (JSC::JSGlobalObject::setDebugger): Deleted.
+        * runtime/VM.cpp:
+        (JSC::VM::VM):
+        (JSC::VM::ensureShadowChicken):
+        * runtime/VM.h:
+        (JSC::VM::shadowChicken):
+        * tools/JSDollarVM.cpp:
+        (JSC::functionShadowChickenFunctionsOnStack):
+        (JSC::changeDebuggerModeWhenIdle):
+
 2019-01-28  Andy Estes  <aestes@apple.com>
 
         [watchOS] Enable Parental Controls content filtering
diff --git a/Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp b/Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp
index 630f8f0..b30a862 100644
--- a/Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp
+++ b/Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp
@@ -70,7 +70,8 @@
     }
 
     Vector<ShadowChicken::Frame> frames;
-    vm.shadowChicken().iterate(vm, callFrame, [&] (const ShadowChicken::Frame& frame) -> bool {
+    vm.ensureShadowChicken();
+    vm.shadowChicken()->iterate(vm, callFrame, [&] (const ShadowChicken::Frame& frame) -> bool {
         frames.append(frame);
         return true;
     });
diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
index d7618dd..078e12a 100644
--- a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
+++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
@@ -13469,16 +13469,18 @@
     
     LValue ensureShadowChickenPacket()
     {
+        ShadowChicken* shadowChicken = vm().shadowChicken();
+        RELEASE_ASSERT(shadowChicken);
         LBasicBlock slowCase = m_out.newBlock();
         LBasicBlock continuation = m_out.newBlock();
         
-        TypedPointer addressOfLogCursor = m_out.absolute(vm().shadowChicken().addressOfLogCursor());
+        TypedPointer addressOfLogCursor = m_out.absolute(shadowChicken->addressOfLogCursor());
         LValue logCursor = m_out.loadPtr(addressOfLogCursor);
         
         ValueFromBlock fastResult = m_out.anchor(logCursor);
         
         m_out.branch(
-            m_out.below(logCursor, m_out.constIntPtr(vm().shadowChicken().logEnd())),
+            m_out.below(logCursor, m_out.constIntPtr(shadowChicken->logEnd())),
             usually(continuation), rarely(slowCase));
         
         LBasicBlock lastNext = m_out.appendTo(slowCase, continuation);
diff --git a/Source/JavaScriptCore/heap/Heap.cpp b/Source/JavaScriptCore/heap/Heap.cpp
index 8da70dd..06ea3b6 100644
--- a/Source/JavaScriptCore/heap/Heap.cpp
+++ b/Source/JavaScriptCore/heap/Heap.cpp
@@ -1588,7 +1588,8 @@
 #endif // ENABLE(JIT)
     UNUSED_PARAM(conn);
     
-    vm()->shadowChicken().update(*vm(), vm()->topCallFrame);
+    if (auto* shadowChicken = vm()->shadowChicken())
+        shadowChicken->update(*vm(), vm()->topCallFrame);
     
     m_structureIDTable.flushOldTables();
     m_objectSpace.stopAllocating();
@@ -2699,7 +2700,8 @@
             if (m_vm->typeProfiler())
                 m_vm->typeProfilerLog()->visit(slotVisitor);
             
-            m_vm->shadowChicken().visitChildren(slotVisitor);
+            if (auto* shadowChicken = m_vm->shadowChicken())
+                shadowChicken->visitChildren(slotVisitor);
         },
         ConstraintVolatility::GreyedByExecution);
     
diff --git a/Source/JavaScriptCore/jit/CCallHelpers.cpp b/Source/JavaScriptCore/jit/CCallHelpers.cpp
index 74d740f..de063b9 100644
--- a/Source/JavaScriptCore/jit/CCallHelpers.cpp
+++ b/Source/JavaScriptCore/jit/CCallHelpers.cpp
@@ -54,14 +54,16 @@
 
 void CCallHelpers::ensureShadowChickenPacket(VM& vm, GPRReg shadowPacket, GPRReg scratch1NonArgGPR, GPRReg scratch2)
 {
+    ShadowChicken* shadowChicken = vm.shadowChicken();
+    RELEASE_ASSERT(shadowChicken);
     ASSERT(!RegisterSet::argumentGPRS().get(scratch1NonArgGPR));
-    move(TrustedImmPtr(vm.shadowChicken().addressOfLogCursor()), scratch1NonArgGPR);
+    move(TrustedImmPtr(shadowChicken->addressOfLogCursor()), scratch1NonArgGPR);
     loadPtr(Address(scratch1NonArgGPR), shadowPacket);
-    Jump ok = branchPtr(Below, shadowPacket, TrustedImmPtr(vm.shadowChicken().logEnd()));
+    Jump ok = branchPtr(Below, shadowPacket, TrustedImmPtr(shadowChicken->logEnd()));
     setupArguments<decltype(operationProcessShadowChickenLog)>();
     move(TrustedImmPtr(tagCFunctionPtr<OperationPtrTag>(operationProcessShadowChickenLog)), scratch1NonArgGPR);
     call(scratch1NonArgGPR, OperationPtrTag);
-    move(TrustedImmPtr(vm.shadowChicken().addressOfLogCursor()), scratch1NonArgGPR);
+    move(TrustedImmPtr(shadowChicken->addressOfLogCursor()), scratch1NonArgGPR);
     loadPtr(Address(scratch1NonArgGPR), shadowPacket);
     ok.link(this);
     addPtr(TrustedImm32(sizeof(ShadowChicken::Packet)), shadowPacket, scratch2);
diff --git a/Source/JavaScriptCore/jit/JITExceptions.cpp b/Source/JavaScriptCore/jit/JITExceptions.cpp
index 3aaa87f..7fb225b 100644
--- a/Source/JavaScriptCore/jit/JITExceptions.cpp
+++ b/Source/JavaScriptCore/jit/JITExceptions.cpp
@@ -53,7 +53,8 @@
         CRASH();
     }
     
-    vm->shadowChicken().log(*vm, topJSCallFrame, ShadowChicken::Packet::throwPacket());
+    if (auto* shadowChicken = vm->shadowChicken())
+        shadowChicken->log(*vm, topJSCallFrame, ShadowChicken::Packet::throwPacket());
 
     Exception* exception = scope.exception();
     RELEASE_ASSERT(exception);
diff --git a/Source/JavaScriptCore/jit/JITOpcodes.cpp b/Source/JavaScriptCore/jit/JITOpcodes.cpp
index c4dced9..378dc50 100644
--- a/Source/JavaScriptCore/jit/JITOpcodes.cpp
+++ b/Source/JavaScriptCore/jit/JITOpcodes.cpp
@@ -1460,6 +1460,7 @@
 
 void JIT::emit_op_log_shadow_chicken_prologue(const Instruction* currentInstruction)
 {
+    RELEASE_ASSERT(vm()->shadowChicken());
     updateTopCallFrame();
     static_assert(nonArgGPR0 != regT0 && nonArgGPR0 != regT2, "we will have problems if this is true.");
     auto bytecode = currentInstruction->as<OpLogShadowChickenPrologue>();
@@ -1473,6 +1474,7 @@
 
 void JIT::emit_op_log_shadow_chicken_tail(const Instruction* currentInstruction)
 {
+    RELEASE_ASSERT(vm()->shadowChicken());
     updateTopCallFrame();
     static_assert(nonArgGPR0 != regT0 && nonArgGPR0 != regT2, "we will have problems if this is true.");
     auto bytecode = currentInstruction->as<OpLogShadowChickenTail>();
diff --git a/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp b/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
index 6462f6d..f36cd43 100644
--- a/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
+++ b/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
@@ -1335,6 +1335,7 @@
 
 void JIT::emit_op_log_shadow_chicken_prologue(const Instruction* currentInstruction)
 {
+    RELEASE_ASSERT(vm()->shadowChicken());
     updateTopCallFrame();
     static_assert(nonArgGPR0 != regT0 && nonArgGPR0 != regT2, "we will have problems if this is true.");
     auto bytecode = currentInstruction->as<OpLogShadowChickenPrologue>();
@@ -1350,6 +1351,7 @@
 
 void JIT::emit_op_log_shadow_chicken_tail(const Instruction* currentInstruction)
 {
+    RELEASE_ASSERT(vm()->shadowChicken());
     updateTopCallFrame();
     static_assert(nonArgGPR0 != regT0 && nonArgGPR0 != regT2, "we will have problems if this is true.");
     auto bytecode = currentInstruction->as<OpLogShadowChickenTail>();
diff --git a/Source/JavaScriptCore/jit/JITOperations.cpp b/Source/JavaScriptCore/jit/JITOperations.cpp
index 95bfb81..6292db2 100644
--- a/Source/JavaScriptCore/jit/JITOperations.cpp
+++ b/Source/JavaScriptCore/jit/JITOperations.cpp
@@ -2882,7 +2882,8 @@
 {
     VM& vm = exec->vm();
     NativeCallFrameTracer tracer(&vm, exec);
-    vm.shadowChicken().update(vm, exec);
+    RELEASE_ASSERT(vm.shadowChicken());
+    vm.shadowChicken()->update(vm, exec);
 }
 
 int32_t JIT_OPERATION operationCheckIfExceptionIsUncatchableAndNotifyProfiler(ExecState* exec)
diff --git a/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp b/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
index 0facee6..0028b13 100644
--- a/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
+++ b/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
@@ -1891,7 +1891,9 @@
     
     auto bytecode = pc->as<OpLogShadowChickenPrologue>();
     JSScope* scope = exec->uncheckedR(bytecode.m_scope).Register::scope();
-    vm.shadowChicken().log(vm, exec, ShadowChicken::Packet::prologue(exec->jsCallee(), exec, exec->callerFrame(), scope));
+    ShadowChicken* shadowChicken = vm.shadowChicken();
+    RELEASE_ASSERT(shadowChicken);
+    shadowChicken->log(vm, exec, ShadowChicken::Packet::prologue(exec->jsCallee(), exec, exec->callerFrame(), scope));
     
     LLINT_END();
 }
@@ -1909,7 +1911,9 @@
 #else
     CallSiteIndex callSiteIndex(pc);
 #endif
-    vm.shadowChicken().log(vm, exec, ShadowChicken::Packet::tail(exec, thisValue, scope, exec->codeBlock(), callSiteIndex));
+    ShadowChicken* shadowChicken = vm.shadowChicken();
+    RELEASE_ASSERT(shadowChicken);
+    shadowChicken->log(vm, exec, ShadowChicken::Packet::tail(exec, thisValue, scope, exec->codeBlock(), callSiteIndex));
     
     LLINT_END();
 }
diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
index 676ec0e..019bf9b 100644
--- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
@@ -1897,6 +1897,13 @@
     vm().queueMicrotask(*this, WTFMove(task));
 }
 
+void JSGlobalObject::setDebugger(Debugger* debugger)
+{
+    m_debugger = debugger;
+    if (debugger)
+        vm().ensureShadowChicken();
+}
+
 bool JSGlobalObject::hasDebugger() const
 { 
     return m_debugger;
diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.h b/Source/JavaScriptCore/runtime/JSGlobalObject.h
index 60c2426..c3ab151 100644
--- a/Source/JavaScriptCore/runtime/JSGlobalObject.h
+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.h
@@ -914,7 +914,7 @@
     }
 
     Debugger* debugger() const { return m_debugger; }
-    void setDebugger(Debugger* debugger) { m_debugger = debugger; }
+    void setDebugger(Debugger*);
 
     const GlobalObjectMethodTable* globalObjectMethodTable() const { return m_globalObjectMethodTable; }
 
diff --git a/Source/JavaScriptCore/runtime/VM.cpp b/Source/JavaScriptCore/runtime/VM.cpp
index 0a640b2..5f4aa78 100644
--- a/Source/JavaScriptCore/runtime/VM.cpp
+++ b/Source/JavaScriptCore/runtime/VM.cpp
@@ -360,7 +360,6 @@
     , m_typeProfilerEnabledCount(0)
     , m_primitiveGigacageEnabled(IsWatched)
     , m_controlFlowProfilerEnabledCount(0)
-    , m_shadowChicken(std::make_unique<ShadowChicken>())
 {
     interpreter = new Interpreter(*this);
     StackBounds stack = Thread::current().stack();
@@ -509,6 +508,9 @@
     if (!canUseJIT())
         noJITValueProfileSingleton = std::make_unique<ValueProfile>(0);
 
+    if (Options::forceDebuggerBytecodeGeneration() || Options::alwaysUseShadowChicken())
+        ensureShadowChicken();
+
     VMInspector::instance().add(this);
 }
 
@@ -1239,6 +1241,13 @@
         scratchBuffer->setActiveLength(0);
 }
 
+void VM::ensureShadowChicken()
+{
+    if (m_shadowChicken)
+        return;
+    m_shadowChicken = std::make_unique<ShadowChicken>();
+}
+
 JSGlobalObject* VM::vmEntryGlobalObject(const CallFrame* callFrame) const
 {
     if (callFrame && callFrame->isGlobalExec()) {
diff --git a/Source/JavaScriptCore/runtime/VM.h b/Source/JavaScriptCore/runtime/VM.h
index df5886d..5839e20 100644
--- a/Source/JavaScriptCore/runtime/VM.h
+++ b/Source/JavaScriptCore/runtime/VM.h
@@ -857,7 +857,8 @@
 
     BytecodeIntrinsicRegistry& bytecodeIntrinsicRegistry() { return *m_bytecodeIntrinsicRegistry; }
     
-    ShadowChicken& shadowChicken() { return *m_shadowChicken; }
+    ShadowChicken* shadowChicken() { return m_shadowChicken.get(); }
+    void ensureShadowChicken();
     
     template<typename Func>
     void logEvent(CodeBlock*, const char* summary, const Func& func);
diff --git a/Source/JavaScriptCore/tools/JSDollarVM.cpp b/Source/JavaScriptCore/tools/JSDollarVM.cpp
index 53c5c46..ac4e00f 100644
--- a/Source/JavaScriptCore/tools/JSDollarVM.cpp
+++ b/Source/JavaScriptCore/tools/JSDollarVM.cpp
@@ -1932,7 +1932,22 @@
 static EncodedJSValue JSC_HOST_CALL functionShadowChickenFunctionsOnStack(ExecState* exec)
 {
     VM& vm = exec->vm();
-    return JSValue::encode(vm.shadowChicken().functionsOnStack(exec));
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    if (auto* shadowChicken = vm.shadowChicken())
+        return JSValue::encode(shadowChicken->functionsOnStack(exec));
+
+    JSArray* result = constructEmptyArray(exec, 0);
+    RETURN_IF_EXCEPTION(scope, { });
+    StackVisitor::visit(exec, &vm, [&] (StackVisitor& visitor) -> StackVisitor::Status {
+        if (visitor->isInlinedFrame())
+            return StackVisitor::Continue;
+        if (visitor->isWasmFrame())
+            return StackVisitor::Continue;
+        result->push(exec, jsCast<JSObject*>(visitor->callee().asCell()));
+        scope.releaseAssertNoException(); // This function is only called from tests.
+        return StackVisitor::Continue;
+    });
+    return JSValue::encode(result);
 }
 
 static EncodedJSValue JSC_HOST_CALL functionSetGlobalConstRedeclarationShouldNotThrow(ExecState* exec)
@@ -2047,6 +2062,8 @@
     vm->whenIdle([=] () {
         Options::forceDebuggerBytecodeGeneration() = newDebuggerMode;
         vm->deleteAllCode(PreventCollectionAndDeleteAllCode);
+        if (mode == DebuggerMode::DebuggerOn)
+            vm->ensureShadowChicken();
     });
     return JSValue::encode(jsUndefined());
 }