Title: [264105] trunk/Source/_javascript_Core
Revision
264105
Author
[email protected]
Date
2020-07-08 09:42:54 -0700 (Wed, 08 Jul 2020)

Log Message

Add a way to return early from detected infinite loops to aid the fuzzer
https://bugs.webkit.org/show_bug.cgi?id=214067

Reviewed by Yusuke Suzuki.

It's useful for the fuzzer to not get stuck in infinite loops so its
test cases can make forward progress trying to find bugs. This patch
adds a new mechanism where we can early return if we've exceeded a total
execution count for a static loop in bytecode. Note: this is not on a
per-frame basis, but it's a way to implement this in a non-invasive way
which is also practical for the fuzzer to use.

* b3/air/AirAllocateRegistersAndStackAndGenerateCode.cpp:
(JSC::B3::Air::GenerateAndAllocateRegisters::generate):
* b3/air/AirCode.cpp:
(JSC::B3::Air::Code::emitEpilogue):
* b3/air/AirCode.h:
* b3/air/AirGenerate.cpp:
(JSC::B3::Air::generateWithAlreadyAllocatedRegisters):
* bytecode/BytecodeList.rb:
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::finishCreation):
(JSC::CodeBlock::~CodeBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileLoopHint):
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_loop_hint):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* offlineasm/mips.rb:
* runtime/OptionsList.h:
* runtime/VM.cpp:
(JSC::VM::addLoopHintExecutionCounter):
(JSC::VM::getLoopHintExecutionCounter):
(JSC::VM::removeLoopHintExecutionCounter):
* runtime/VM.h:

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (264104 => 264105)


--- trunk/Source/_javascript_Core/ChangeLog	2020-07-08 16:42:35 UTC (rev 264104)
+++ trunk/Source/_javascript_Core/ChangeLog	2020-07-08 16:42:54 UTC (rev 264105)
@@ -1,3 +1,49 @@
+2020-07-08  Saam Barati  <[email protected]>
+
+        Add a way to return early from detected infinite loops to aid the fuzzer
+        https://bugs.webkit.org/show_bug.cgi?id=214067
+
+        Reviewed by Yusuke Suzuki.
+
+        It's useful for the fuzzer to not get stuck in infinite loops so its
+        test cases can make forward progress trying to find bugs. This patch
+        adds a new mechanism where we can early return if we've exceeded a total
+        execution count for a static loop in bytecode. Note: this is not on a
+        per-frame basis, but it's a way to implement this in a non-invasive way
+        which is also practical for the fuzzer to use.
+
+        * b3/air/AirAllocateRegistersAndStackAndGenerateCode.cpp:
+        (JSC::B3::Air::GenerateAndAllocateRegisters::generate):
+        * b3/air/AirCode.cpp:
+        (JSC::B3::Air::Code::emitEpilogue):
+        * b3/air/AirCode.h:
+        * b3/air/AirGenerate.cpp:
+        (JSC::B3::Air::generateWithAlreadyAllocatedRegisters):
+        * bytecode/BytecodeList.rb:
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::finishCreation):
+        (JSC::CodeBlock::~CodeBlock):
+        * dfg/DFGCapabilities.cpp:
+        (JSC::DFG::capabilityLevel):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileLoopHint):
+        * jit/JITOpcodes.cpp:
+        (JSC::JIT::emit_op_loop_hint):
+        * llint/LLIntSlowPaths.cpp:
+        (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+        * offlineasm/mips.rb:
+        * runtime/OptionsList.h:
+        * runtime/VM.cpp:
+        (JSC::VM::addLoopHintExecutionCounter):
+        (JSC::VM::getLoopHintExecutionCounter):
+        (JSC::VM::removeLoopHintExecutionCounter):
+        * runtime/VM.h:
+
 2020-07-07  Yusuke Suzuki  <[email protected]>
 
         [JSC] BytecodeGenerator should be robust against failed constant generation

Modified: trunk/Source/_javascript_Core/b3/air/AirAllocateRegistersAndStackAndGenerateCode.cpp (264104 => 264105)


--- trunk/Source/_javascript_Core/b3/air/AirAllocateRegistersAndStackAndGenerateCode.cpp	2020-07-08 16:42:35 UTC (rev 264104)
+++ trunk/Source/_javascript_Core/b3/air/AirAllocateRegistersAndStackAndGenerateCode.cpp	2020-07-08 16:42:54 UTC (rev 264105)
@@ -787,12 +787,7 @@
 
                     // We currently don't represent the full epilogue in Air, so we need to
                     // have this override.
-                    if (m_code.frameSize()) {
-                        m_jit->emitRestore(m_code.calleeSaveRegisterAtOffsetList());
-                        m_jit->emitFunctionEpilogue();
-                    } else
-                        m_jit->emitFunctionEpilogueWithEmptyFrame();
-                    m_jit->ret();
+                    m_code.emitEpilogue(*m_jit);
                 }
                 
                 if (needsToGenerate) {

Modified: trunk/Source/_javascript_Core/b3/air/AirCode.cpp (264104 => 264105)


--- trunk/Source/_javascript_Core/b3/air/AirCode.cpp	2020-07-08 16:42:35 UTC (rev 264104)
+++ trunk/Source/_javascript_Core/b3/air/AirCode.cpp	2020-07-08 16:42:54 UTC (rev 264105)
@@ -105,6 +105,16 @@
     defaultPrologueGenerator(jit, *this);
 }
 
+void Code::emitEpilogue(CCallHelpers& jit)
+{
+    if (frameSize()) {
+        jit.emitRestore(calleeSaveRegisterAtOffsetList());
+        jit.emitFunctionEpilogue();
+    } else
+        jit.emitFunctionEpilogueWithEmptyFrame();
+    jit.ret();
+}
+
 void Code::setRegsInPriorityOrder(Bank bank, const Vector<Reg>& regs)
 {
     regsInPriorityOrderImpl(bank) = regs;

Modified: trunk/Source/_javascript_Core/b3/air/AirCode.h (264104 => 264105)


--- trunk/Source/_javascript_Core/b3/air/AirCode.h	2020-07-08 16:42:35 UTC (rev 264104)
+++ trunk/Source/_javascript_Core/b3/air/AirCode.h	2020-07-08 16:42:54 UTC (rev 264105)
@@ -346,6 +346,7 @@
     WeakRandom& weakRandom() { return m_weakRandom; }
 
     void emitDefaultPrologue(CCallHelpers&);
+    void emitEpilogue(CCallHelpers&);
 
     std::unique_ptr<GenerateAndAllocateRegisters> m_generateAndAllocateRegisters;
     

Modified: trunk/Source/_javascript_Core/b3/air/AirGenerate.cpp (264104 => 264105)


--- trunk/Source/_javascript_Core/b3/air/AirGenerate.cpp	2020-07-08 16:42:35 UTC (rev 264104)
+++ trunk/Source/_javascript_Core/b3/air/AirGenerate.cpp	2020-07-08 16:42:54 UTC (rev 264105)
@@ -270,12 +270,7 @@
             // We currently don't represent the full prologue/epilogue in Air, so we need to
             // have this override.
             auto start = jit.labelIgnoringWatchpoints();
-            if (code.frameSize()) {
-                jit.emitRestore(code.calleeSaveRegisterAtOffsetList());
-                jit.emitFunctionEpilogue();
-            } else
-                jit.emitFunctionEpilogueWithEmptyFrame();
-            jit.ret();
+            code.emitEpilogue(jit);
             addItem(block->last());
             auto end = jit.labelIgnoringWatchpoints();
             if (disassembler)

Modified: trunk/Source/_javascript_Core/bytecode/BytecodeList.rb (264104 => 264105)


--- trunk/Source/_javascript_Core/bytecode/BytecodeList.rb	2020-07-08 16:42:35 UTC (rev 264104)
+++ trunk/Source/_javascript_Core/bytecode/BytecodeList.rb	2020-07-08 16:42:54 UTC (rev 264105)
@@ -1389,6 +1389,7 @@
 op :llint_internal_function_construct_trampoline
 op :checkpoint_osr_exit_from_inlined_call_trampoline
 op :checkpoint_osr_exit_trampoline
+op :fuzzer_return_early_from_loop_hint
 op :handleUncaughtException
 op :op_call_return_location
 op :op_construct_return_location

Modified: trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp (264104 => 264105)


--- trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp	2020-07-08 16:42:35 UTC (rev 264104)
+++ trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp	2020-07-08 16:42:54 UTC (rev 264105)
@@ -768,6 +768,12 @@
             m_numberOfArgumentsToSkip = numberOfArgumentsToSkip;
             break;
         }
+
+        case op_loop_hint: {
+            if (Options::returnEarlyFromInfiniteLoopsForFuzzing())
+                vm.addLoopHintExecutionCounter(instruction.ptr());
+            break;
+        }
         
         default:
             break;
@@ -811,6 +817,13 @@
 {
     VM& vm = *m_vm;
 
+    if (Options::returnEarlyFromInfiniteLoopsForFuzzing() && JITCode::isBaselineCode(jitType())) {
+        for (const auto& instruction : instructions()) {
+            if (instruction->is<OpLoopHint>())
+                vm.removeLoopHintExecutionCounter(instruction.ptr());
+        }
+    }
+
 #if ENABLE(DFG_JIT)
     // The JITCode (and its corresponding DFG::CommonData) may outlive the CodeBlock by
     // a short amount of time after the CodeBlock is destructed. For example, the

Modified: trunk/Source/_javascript_Core/bytecode/CodeBlock.h (264104 => 264105)


--- trunk/Source/_javascript_Core/bytecode/CodeBlock.h	2020-07-08 16:42:35 UTC (rev 264104)
+++ trunk/Source/_javascript_Core/bytecode/CodeBlock.h	2020-07-08 16:42:54 UTC (rev 264105)
@@ -922,6 +922,12 @@
     MetadataTable* metadataTable() { return m_metadata.get(); }
     const void* instructionsRawPointer() { return m_instructionsRawPointer; }
 
+    bool loopHintsAreEligibleForFuzzingEarlyReturn()
+    {
+        // Some builtins are required to always complete the loops they run.
+        return !m_unlinkedCode->isBuiltinFunction();
+    }
+
 protected:
     void finalizeLLIntInlineCaches();
 #if ENABLE(JIT)

Modified: trunk/Source/_javascript_Core/dfg/DFGCapabilities.cpp (264104 => 264105)


--- trunk/Source/_javascript_Core/dfg/DFGCapabilities.cpp	2020-07-08 16:42:35 UTC (rev 264104)
+++ trunk/Source/_javascript_Core/dfg/DFGCapabilities.cpp	2020-07-08 16:42:54 UTC (rev 264105)
@@ -317,6 +317,7 @@
     case checkpoint_osr_exit_from_inlined_call_trampoline:
     case checkpoint_osr_exit_trampoline:
     case handleUncaughtException:
+    case fuzzer_return_early_from_loop_hint:
     case op_iterator_open_return_location:
     case op_iterator_next_return_location:
     case op_call_return_location:

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (264104 => 264105)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2020-07-08 16:42:35 UTC (rev 264104)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2020-07-08 16:42:54 UTC (rev 264105)
@@ -4963,7 +4963,35 @@
         break;
         
     case PhantomLocal:
+        // This is a no-op.
+        noResult(node);
+        break;
+
     case LoopHint:
+        if (Options::returnEarlyFromInfiniteLoopsForFuzzing()) {
+            CodeBlock* baselineCodeBlock = m_jit.graph().baselineCodeBlockFor(node->origin.semantic);
+            if (baselineCodeBlock->loopHintsAreEligibleForFuzzingEarlyReturn()) {
+                BytecodeIndex bytecodeIndex = node->origin.semantic.bytecodeIndex();
+                const Instruction* instruction = baselineCodeBlock->instructions().at(bytecodeIndex.offset()).ptr();
+
+                uint64_t* ptr = vm().getLoopHintExecutionCounter(instruction);
+                m_jit.pushToSave(GPRInfo::regT0);
+                m_jit.load64(ptr, GPRInfo::regT0);
+                auto skipEarlyReturn = m_jit.branch64(CCallHelpers::Below, GPRInfo::regT0, CCallHelpers::TrustedImm64(Options::earlyReturnFromInfiniteLoopsLimit()));
+
+                m_jit.popToRestore(GPRInfo::regT0);
+                m_jit.move(CCallHelpers::TrustedImm64(JSValue::encode(jsUndefined())), GPRInfo::returnValueGPR);
+                m_jit.emitRestoreCalleeSaves();
+                m_jit.emitFunctionEpilogue();
+                m_jit.ret();
+
+                skipEarlyReturn.link(&m_jit);
+                m_jit.add64(CCallHelpers::TrustedImm32(1), GPRInfo::regT0);
+                m_jit.store64(GPRInfo::regT0, ptr);
+                m_jit.popToRestore(GPRInfo::regT0);
+            }
+        }
+
         // This is a no-op.
         noResult(node);
         break;

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (264104 => 264105)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2020-07-08 16:42:35 UTC (rev 264104)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2020-07-08 16:42:54 UTC (rev 264105)
@@ -1620,8 +1620,12 @@
             compileDataViewSet();
             break;
 
+        case LoopHint: {
+            compileLoopHint();
+            break;
+        }
+
         case PhantomLocal:
-        case LoopHint:
         case MovHint:
         case ZombieHint:
         case ExitOK:
@@ -14697,6 +14701,50 @@
             RELEASE_ASSERT_NOT_REACHED();
         }
     }
+
+    void compileLoopHint()
+    {
+        if (!Options::returnEarlyFromInfiniteLoopsForFuzzing())
+            return;
+
+        CodeBlock* baselineCodeBlock = m_graph.baselineCodeBlockFor(m_node->origin.semantic);
+        if (!baselineCodeBlock->loopHintsAreEligibleForFuzzingEarlyReturn())
+            return;
+
+        BytecodeIndex bytecodeIndex = m_node->origin.semantic.bytecodeIndex();
+        const Instruction* instruction = baselineCodeBlock->instructions().at(bytecodeIndex.offset()).ptr();
+        uint64_t* ptr = vm().getLoopHintExecutionCounter(instruction);
+
+        PatchpointValue* patchpoint = m_out.patchpoint(Void);
+        patchpoint->effects = Effects::none();
+        patchpoint->effects.exitsSideways = true;
+        patchpoint->effects.writesLocalState = true;
+        patchpoint->setGenerator([ptr] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+            auto restore = [&] {
+                jit.popToRestore(GPRInfo::regT2);
+                jit.popToRestore(GPRInfo::regT1);
+                jit.popToRestore(GPRInfo::regT0);
+            };
+
+            jit.pushToSave(GPRInfo::regT0);
+            jit.pushToSave(GPRInfo::regT1);
+            jit.pushToSave(GPRInfo::regT2);
+
+            jit.move(CCallHelpers::TrustedImm64(Options::earlyReturnFromInfiniteLoopsLimit()), GPRInfo::regT2);
+            jit.move(CCallHelpers::TrustedImmPtr(ptr), GPRInfo::regT0);
+            jit.load64(CCallHelpers::Address(GPRInfo::regT0), GPRInfo::regT1);
+            auto skipEarlyReturn = jit.branch64(CCallHelpers::Below, GPRInfo::regT1, GPRInfo::regT2);
+
+            restore();
+            jit.move(CCallHelpers::TrustedImm64(JSValue::encode(jsUndefined())), GPRInfo::returnValueGPR);
+            params.code().emitEpilogue(jit); 
+
+            skipEarlyReturn.link(&jit);
+            jit.add64(CCallHelpers::TrustedImm32(1), GPRInfo::regT1);
+            jit.store64(GPRInfo::regT1, CCallHelpers::Address(GPRInfo::regT0));
+            restore();
+        });
+    }
     
     void emitSwitchForMultiByOffset(LValue base, bool structuresChecked, Vector<SwitchCase, 2>& cases, LBasicBlock exit)
     {

Modified: trunk/Source/_javascript_Core/jit/JITOpcodes.cpp (264104 => 264105)


--- trunk/Source/_javascript_Core/jit/JITOpcodes.cpp	2020-07-08 16:42:35 UTC (rev 264104)
+++ trunk/Source/_javascript_Core/jit/JITOpcodes.cpp	2020-07-08 16:42:54 UTC (rev 264105)
@@ -1160,8 +1160,26 @@
 
 #endif // USE(JSVALUE64)
 
-void JIT::emit_op_loop_hint(const Instruction*)
+void JIT::emit_op_loop_hint(const Instruction* instruction)
 {
+#if USE(JSVALUE64)
+    if (Options::returnEarlyFromInfiniteLoopsForFuzzing() && m_codeBlock->loopHintsAreEligibleForFuzzingEarlyReturn()) {
+        uint64_t* ptr = vm().getLoopHintExecutionCounter(instruction);
+        load64(ptr, regT0);
+        auto skipEarlyReturn = branch64(Below, regT0, TrustedImm64(Options::earlyReturnFromInfiniteLoopsLimit()));
+
+        moveValue(jsUndefined(), JSValueRegs { GPRInfo::returnValueGPR });
+        checkStackPointerAlignment();
+        emitRestoreCalleeSaves();
+        emitFunctionEpilogue();
+        ret();
+
+        skipEarlyReturn.link(this);
+        add64(TrustedImm32(1), regT0);
+        store64(regT0, ptr);
+    }
+#endif
+
     // Emit the JIT optimization check: 
     if (canBeOptimized()) {
         addSlowCase(branchAdd32(PositiveOrZero, TrustedImm32(Options::executionCounterIncrementForLoop()),

Modified: trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp (264104 => 264105)


--- trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp	2020-07-08 16:42:35 UTC (rev 264104)
+++ trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp	2020-07-08 16:42:54 UTC (rev 264105)
@@ -462,7 +462,15 @@
     dataLogLnIf(Options::verboseOSR(),
             *codeBlock, ": Entered loop_osr with executeCounter = ",
             codeBlock->llintExecuteCounter());
+
+    if (UNLIKELY(Options::returnEarlyFromInfiniteLoopsForFuzzing() && codeBlock->loopHintsAreEligibleForFuzzingEarlyReturn())) {
+        uint64_t* ptr = vm.getLoopHintExecutionCounter(pc);
+        *ptr += codeBlock->llintExecuteCounter().m_activeThreshold;
+        if (*ptr >= Options::earlyReturnFromInfiniteLoopsLimit())
+            LLINT_RETURN_TWO(LLInt::getCodePtr<JSEntryPtrTag>(fuzzer_return_early_from_loop_hint).executableAddress(), callFrame->topOfFrame());
+    }
     
+    
     auto loopOSREntryBytecodeIndex = BytecodeIndex(codeBlock->bytecodeOffset(pc));
 
     if (!shouldJIT(codeBlock)) {

Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm (264104 => 264105)


--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm	2020-07-08 16:42:35 UTC (rev 264104)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm	2020-07-08 16:42:54 UTC (rev 264105)
@@ -2702,3 +2702,8 @@
     callSlowPath(_llint_slow_path_log_shadow_chicken_tail)
     dispatch()
 end)
+
+
+op(fuzzer_return_early_from_loop_hint, macro ()
+    notSupported()
+end)

Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm (264104 => 264105)


--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm	2020-07-08 16:42:35 UTC (rev 264104)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm	2020-07-08 16:42:54 UTC (rev 264105)
@@ -2944,3 +2944,9 @@
 llintOpWithReturn(op_in_structure_property, OpInStructureProperty, macro (size, get, dispatch, return)
     hasStructurePropertyImpl(size, get, dispatch,  return, _slow_path_in_structure_property)
 end)
+
+op(fuzzer_return_early_from_loop_hint, macro ()
+    move ValueUndefined, r0
+    doReturn()
+end)
+

Modified: trunk/Source/_javascript_Core/offlineasm/mips.rb (264104 => 264105)


--- trunk/Source/_javascript_Core/offlineasm/mips.rb	2020-07-08 16:42:35 UTC (rev 264104)
+++ trunk/Source/_javascript_Core/offlineasm/mips.rb	2020-07-08 16:42:54 UTC (rev 264105)
@@ -684,7 +684,7 @@
             # FIXME: [JSC] checkpoint_osr_exit_from_inlined_call_trampoline is a return location
             # and we should name it properly.
             # https://bugs.webkit.org/show_bug.cgi?id=208236
-            if node.name =~ /^.*_return_location(?:_(?:wide16|wide32))?$/ or node.name.start_with?("_checkpoint_osr_exit_from_inlined_call_trampoline")
+            if node.name =~ /^.*_return_location(?:_(?:wide16|wide32))?$/ or node.name.start_with?("_checkpoint_osr_exit_from_inlined_call_trampoline") or node.name.start_with?("_fuzzer_return_early_from_loop_hint")
                 # We need to have a special case for return location labels because they are always
                 # reached from a `ret` instruction. In this case, we need to proper reconfigure `$gp`
                 # using `$ra` instead of using `$t9`.

Modified: trunk/Source/_javascript_Core/runtime/OptionsList.h (264104 => 264105)


--- trunk/Source/_javascript_Core/runtime/OptionsList.h	2020-07-08 16:42:35 UTC (rev 264104)
+++ trunk/Source/_javascript_Core/runtime/OptionsList.h	2020-07-08 16:42:54 UTC (rev 264105)
@@ -510,6 +510,8 @@
     v(Bool, exposeProfilersOnGlobalObject, false, Normal, "If true, we will expose functions to enable/disable both the sampling profiler and the super sampler") \
     v(Bool, allowUnsupportedTiers, false, Normal, "If true, we will not disable DFG or FTL when an experimental feature is enabled.") \
     v(Bool, usePrivateClassFields, false, Normal, "If true, the parser will understand private data fields inside classes.") \
+    v(Bool, returnEarlyFromInfiniteLoopsForFuzzing, false, Normal, nullptr) \
+    v(Size, earlyReturnFromInfiniteLoopsLimit, 1300000000, Normal, "When returnEarlyFromInfiniteLoopsForFuzzing is true, this determines the number of executions a loop can run for before just returning. This is helpful for the fuzzer so it doesn't get stuck in infinite loops.") \
 
 enum OptionEquivalence {
     SameOption,

Modified: trunk/Source/_javascript_Core/runtime/VM.cpp (264104 => 264105)


--- trunk/Source/_javascript_Core/runtime/VM.cpp	2020-07-08 16:42:35 UTC (rev 264104)
+++ trunk/Source/_javascript_Core/runtime/VM.cpp	2020-07-08 16:42:54 UTC (rev 264105)
@@ -1552,4 +1552,33 @@
     vmCreationShouldCrash = shouldCrash;
 }
 
+void VM::addLoopHintExecutionCounter(const Instruction* instruction)
+{
+    auto locker = holdLock(m_loopHintExecutionCountLock);
+    auto addResult = m_loopHintExecutionCounts.add(instruction, std::pair<unsigned, std::unique_ptr<uint64_t>>(0, nullptr));
+    if (addResult.isNewEntry) {
+        auto ptr = WTF::makeUniqueWithoutFastMallocCheck<uint64_t>();
+        *ptr = 0;
+        addResult.iterator->value.second = WTFMove(ptr);
+    }
+    ++addResult.iterator->value.first;
+}
+
+uint64_t* VM::getLoopHintExecutionCounter(const Instruction* instruction)
+{
+    auto locker = holdLock(m_loopHintExecutionCountLock);
+    auto iter = m_loopHintExecutionCounts.find(instruction);
+    return iter->value.second.get();
+}
+
+void VM::removeLoopHintExecutionCounter(const Instruction* instruction)
+{
+    auto locker = holdLock(m_loopHintExecutionCountLock);
+    auto iter = m_loopHintExecutionCounts.find(instruction);
+    RELEASE_ASSERT(!!iter->value.first);
+    --iter->value.first;
+    if (!iter->value.first)
+        m_loopHintExecutionCounts.remove(iter);
+}
+
 } // namespace JSC

Modified: trunk/Source/_javascript_Core/runtime/VM.h (264104 => 264105)


--- trunk/Source/_javascript_Core/runtime/VM.h	2020-07-08 16:42:35 UTC (rev 264104)
+++ trunk/Source/_javascript_Core/runtime/VM.h	2020-07-08 16:42:54 UTC (rev 264105)
@@ -1099,6 +1099,10 @@
         SetForScope<Exception*> m_savedLastException;
     };
 
+    void addLoopHintExecutionCounter(const Instruction*);
+    uint64_t* getLoopHintExecutionCounter(const Instruction*);
+    void removeLoopHintExecutionCounter(const Instruction*);
+
 private:
     friend class LLIntOffsetsExtractor;
 
@@ -1224,6 +1228,9 @@
     WTF::Function<void(VM&)> m_onEachMicrotaskTick;
     uintptr_t m_currentWeakRefVersion { 0 };
 
+    Lock m_loopHintExecutionCountLock;
+    HashMap<const Instruction*, std::pair<unsigned, std::unique_ptr<uint64_t>>> m_loopHintExecutionCounts;
+
     VM* m_prev; // Required by DoublyLinkedListNode.
     VM* m_next; // Required by DoublyLinkedListNode.
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to