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.