WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
another version
blah.patch (text/plain), 24.75 KB, created by
Filip Pizlo
on 2014-02-04 22:05:55 PST
(
hide
)
Description:
another version
Filename:
MIME Type:
Creator:
Filip Pizlo
Created:
2014-02-04 22:05:55 PST
Size:
24.75 KB
patch
obsolete
>Index: Source/JavaScriptCore/ChangeLog >=================================================================== >--- Source/JavaScriptCore/ChangeLog (revision 163269) >+++ Source/JavaScriptCore/ChangeLog (working copy) >@@ -1,3 +1,82 @@ >+2014-02-02 Filip Pizlo <fpizlo@apple.com> >+ >+ GC should keep structures alive if they are inlined into optimized code (DFG or FTL) and their globalObject is alive >+ https://bugs.webkit.org/show_bug.cgi?id=128072 >+ >+ Reviewed by Oliver Hunt. >+ >+ The original role of the structure transition fixpoint was to guard against cases >+ where we had code that cached on a now-dead structure with a now-dead global object. >+ Originally cached structures were strong references and so such a case could lead to >+ a massive memory leak. >+ >+ Of course, we couldn't turn these references into simply weak references because then >+ we would be throwing away code too frequently. It's surprisingly common to have >+ idioms that reduce to something like: >+ >+ for (forever) { >+ var o = new SuperImportantStateObject(); >+ // Do a bunch of work involving o. >+ o = null; // Something that kills o. >+ // Do some more work, and we GC here. >+ } >+ >+ Usually the "o = null" arises because the allocation of the object is inside of a >+ function that the loop calls. This is particularly frequent on the Web because that >+ outer loop is actually the runloop and the GC is typically scheduled when the runloop >+ is idle. So, if we treated all cached structures as weak and the app had any types >+ that existed only during event handling but typically didn't remain reachable after >+ the event got handled, then we would be throwing away a lot of code. >+ >+ So, instead of making those structures completely weak, we threw in a clever trick: >+ we would fixpoint over all cached transitions that create new structures, and if any >+ of those transitions was a part of code that appeared live, then we would mark the >+ target structure. This tended to keep alive structures for objects that hot code >+ would tend to create. >+ >+ But now it looks like this isn't enough. Those transition caches are just that - >+ caches. They are imperfect. So, this patch adds another rule for keeping structures >+ alive: >+ >+ If a structure is cached and inlined by an optimizing compiler and its global >+ object is otherwise still live, then mark the structure. >+ >+ This rule will usually dominate the transition rule, but the transition rule is still >+ critical for when we haven't created optimized code yet, or for structures that >+ aren't associated with a global object, or for structures cached but not _inlined_ in >+ optimized code. The philosophy here is that it's generally safe, and likely >+ profitable, to throw away structures if the worst case outcome is just that some >+ inline caches have to repatch themselves. By contrast, the "inlined into optimized >+ code" rule means that we don't throw away structures if the worst case is that we >+ have to relaunch the Battlestar (i.e. run an optimizing compiler). >+ >+ This fix introduces two possibilities for simplifying the code, but they probably >+ require some more investigation: >+ >+ - https://bugs.webkit.org/show_bug.cgi?id=128078 >+ Consider getting rid of the GC transition fixpoint for optimized code, since the >+ global object rule is likely to dominate it >+ >+ - https://bugs.webkit.org/show_bug.cgi?id=128079 >+ Consider getting rid of the StructureStubInfo::resetByGC flag because the global >+ object rule implies that caching on possibly-GCable structures is no longer >+ disastrous >+ >+ This looks like a 50% speed-up on Octane2/gbemu in the FTL. >+ >+ This also includes a bunch of debug support for tracking what is going on inside of >+ the GC's structure clearing. That's actually the bulk of this patch. >+ >+ * bytecode/CodeBlock.cpp: >+ (JSC::dumpStructure): >+ (JSC::dumpChain): >+ (JSC::dumpStructureStubInfo): >+ (JSC::CodeBlock::printGetByIdCacheStatus): >+ (JSC::forceStructureLiveness): >+ (JSC::CodeBlock::propagateTransitions): >+ (JSC::CodeBlock::finalizeUnconditionally): >+ * runtime/Options.h: >+ > 2014-02-01 Filip Pizlo <fpizlo@apple.com> > > JSC profiler's stub info profiling support should work again >Index: Source/JavaScriptCore/bytecode/CodeBlock.cpp >=================================================================== >--- Source/JavaScriptCore/bytecode/CodeBlock.cpp (revision 163265) >+++ Source/JavaScriptCore/bytecode/CodeBlock.cpp (working copy) >@@ -312,21 +312,23 @@ void CodeBlock::printGetByIdOp(PrintStre > } > > #if ENABLE(JIT) || ENABLE(LLINT) // unused in some configurations >-static void dumpStructure(PrintStream& out, const char* name, ExecState* exec, Structure* structure, const Identifier& ident) >+static void dumpStructure(PrintStream& out, const char* name, VM& vm, Structure* structure, const Identifier* ident) > { > if (!structure) > return; > > out.printf("%s = %p", name, structure); > >- PropertyOffset offset = structure->getConcurrently(exec->vm(), ident.impl()); >- if (offset != invalidOffset) >- out.printf(" (offset = %d)", offset); >+ if (ident) { >+ PropertyOffset offset = structure->getConcurrently(vm, ident->impl()); >+ if (offset != invalidOffset) >+ out.printf(" (offset = %d)", offset); >+ } > } > #endif > > #if ENABLE(JIT) // unused when not ENABLE(JIT), leading to silly warnings >-static void dumpChain(PrintStream& out, ExecState* exec, StructureChain* chain, const Identifier& ident) >+static void dumpChain(PrintStream& out, VM& vm, StructureChain* chain, const Identifier* ident) > { > out.printf("chain = %p: [", chain); > bool first = true; >@@ -337,10 +339,112 @@ static void dumpChain(PrintStream& out, > first = false; > else > out.printf(", "); >- dumpStructure(out, "struct", exec, currentStructure->get(), ident); >+ dumpStructure(out, "struct", vm, currentStructure->get(), ident); > } > out.printf("]"); > } >+ >+static void dumpStructureStubInfo(PrintStream& out, VM& vm, StructureStubInfo& stubInfo, const Identifier* ident) >+{ >+ Structure* baseStructure = 0; >+ Structure* prototypeStructure = 0; >+ StructureChain* chain = 0; >+ PolymorphicAccessStructureList* structureList = 0; >+ int listSize = 0; >+ >+ switch (stubInfo.accessType) { >+ case access_get_by_id_self: >+ out.printf("self"); >+ baseStructure = stubInfo.u.getByIdSelf.baseObjectStructure.get(); >+ break; >+ case access_get_by_id_proto: >+ out.printf("proto"); >+ baseStructure = stubInfo.u.getByIdProto.baseObjectStructure.get(); >+ prototypeStructure = stubInfo.u.getByIdProto.prototypeStructure.get(); >+ break; >+ case access_get_by_id_chain: >+ out.printf("chain"); >+ baseStructure = stubInfo.u.getByIdChain.baseObjectStructure.get(); >+ chain = stubInfo.u.getByIdChain.chain.get(); >+ break; >+ case access_get_by_id_self_list: >+ out.printf("self_list"); >+ structureList = stubInfo.u.getByIdSelfList.structureList; >+ listSize = stubInfo.u.getByIdSelfList.listSize; >+ break; >+ case access_get_by_id_proto_list: >+ out.printf("proto_list"); >+ structureList = stubInfo.u.getByIdProtoList.structureList; >+ listSize = stubInfo.u.getByIdProtoList.listSize; >+ break; >+ case access_unset: >+ out.printf("unset"); >+ break; >+ case access_get_by_id_generic: >+ out.printf("generic"); >+ break; >+ case access_get_array_length: >+ out.printf("array_length"); >+ break; >+ case access_get_string_length: >+ out.printf("string_length"); >+ break; >+ case access_in_list: >+ out.printf("in_list"); >+ structureList = stubInfo.u.inList.structureList; >+ listSize = stubInfo.u.inList.listSize; >+ break; >+ case access_put_by_id_transition_normal: >+ case access_put_by_id_transition_direct: >+ case access_put_by_id_replace: >+ case access_put_by_id_list: >+ case access_put_by_id_generic: >+ // FIXME: Support dumping these. >+ // https://bugs.webkit.org/show_bug.cgi?id=128062 >+ return; >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ break; >+ } >+ >+ if (baseStructure) { >+ out.printf(", "); >+ dumpStructure(out, "struct", vm, baseStructure, ident); >+ } >+ >+ if (prototypeStructure) { >+ out.printf(", "); >+ dumpStructure(out, "prototypeStruct", vm, baseStructure, ident); >+ } >+ >+ if (chain) { >+ out.printf(", "); >+ dumpChain(out, vm, chain, ident); >+ } >+ >+ if (structureList) { >+ out.printf(", list = %p: [", structureList); >+ for (int i = 0; i < listSize; ++i) { >+ if (i) >+ out.printf(", "); >+ out.printf("("); >+ dumpStructure(out, "base", vm, structureList->list[i].base.get(), ident); >+ if (structureList->list[i].isChain) { >+ if (structureList->list[i].u.chain.get()) { >+ out.printf(", "); >+ dumpChain(out, vm, structureList->list[i].u.chain.get(), ident); >+ } >+ } else { >+ if (structureList->list[i].u.proto.get()) { >+ out.printf(", "); >+ dumpStructure(out, "proto", vm, structureList->list[i].u.proto.get(), ident); >+ } >+ } >+ out.printf(")"); >+ } >+ out.printf("]"); >+ } >+} > #endif > > void CodeBlock::printGetByIdCacheStatus(PrintStream& out, ExecState* exec, int location, const StubInfoMap& map) >@@ -356,7 +460,7 @@ void CodeBlock::printGetByIdCacheStatus( > out.printf(" llint(array_length)"); > else if (Structure* structure = instruction[4].u.structure.get()) { > out.printf(" llint("); >- dumpStructure(out, "struct", exec, structure, ident); >+ dumpStructure(out, "struct", exec->vm(), structure, &ident); > out.printf(")"); > } > #endif >@@ -369,92 +473,7 @@ void CodeBlock::printGetByIdCacheStatus( > > if (stubInfo.seen) { > out.printf(" jit("); >- >- Structure* baseStructure = 0; >- Structure* prototypeStructure = 0; >- StructureChain* chain = 0; >- PolymorphicAccessStructureList* structureList = 0; >- int listSize = 0; >- >- switch (stubInfo.accessType) { >- case access_get_by_id_self: >- out.printf("self"); >- baseStructure = stubInfo.u.getByIdSelf.baseObjectStructure.get(); >- break; >- case access_get_by_id_proto: >- out.printf("proto"); >- baseStructure = stubInfo.u.getByIdProto.baseObjectStructure.get(); >- prototypeStructure = stubInfo.u.getByIdProto.prototypeStructure.get(); >- break; >- case access_get_by_id_chain: >- out.printf("chain"); >- baseStructure = stubInfo.u.getByIdChain.baseObjectStructure.get(); >- chain = stubInfo.u.getByIdChain.chain.get(); >- break; >- case access_get_by_id_self_list: >- out.printf("self_list"); >- structureList = stubInfo.u.getByIdSelfList.structureList; >- listSize = stubInfo.u.getByIdSelfList.listSize; >- break; >- case access_get_by_id_proto_list: >- out.printf("proto_list"); >- structureList = stubInfo.u.getByIdProtoList.structureList; >- listSize = stubInfo.u.getByIdProtoList.listSize; >- break; >- case access_unset: >- out.printf("unset"); >- break; >- case access_get_by_id_generic: >- out.printf("generic"); >- break; >- case access_get_array_length: >- out.printf("array_length"); >- break; >- case access_get_string_length: >- out.printf("string_length"); >- break; >- default: >- RELEASE_ASSERT_NOT_REACHED(); >- break; >- } >- >- if (baseStructure) { >- out.printf(", "); >- dumpStructure(out, "struct", exec, baseStructure, ident); >- } >- >- if (prototypeStructure) { >- out.printf(", "); >- dumpStructure(out, "prototypeStruct", exec, baseStructure, ident); >- } >- >- if (chain) { >- out.printf(", "); >- dumpChain(out, exec, chain, ident); >- } >- >- if (structureList) { >- out.printf(", list = %p: [", structureList); >- for (int i = 0; i < listSize; ++i) { >- if (i) >- out.printf(", "); >- out.printf("("); >- dumpStructure(out, "base", exec, structureList->list[i].base.get(), ident); >- if (structureList->list[i].isChain) { >- if (structureList->list[i].u.chain.get()) { >- out.printf(", "); >- dumpChain(out, exec, structureList->list[i].u.chain.get(), ident); >- } >- } else { >- if (structureList->list[i].u.proto.get()) { >- out.printf(", "); >- dumpStructure(out, "proto", exec, structureList->list[i].u.proto.get(), ident); >- } >- } >- out.printf(")"); >- } >- out.printf("]"); >- } >+ dumpStructureStubInfo(out, exec->vm(), stubInfo, &ident); > out.printf(")"); > } > } >@@ -2036,6 +2055,24 @@ void CodeBlock::visitAggregate(SlotVisit > #endif // ENABLE(DFG_JIT) > } > >+static void forceStructureLiveness( >+ SlotVisitor& visitor, WriteBarrier<JSCell>& possibleStructure) >+{ >+ JSCell* cell = possibleStructure.get(); >+ if (Heap::isMarked(cell)) >+ return; >+ >+ if (cell->classInfo() != Structure::info()) >+ return; >+ >+ Structure* structure = jsCast<Structure*>(cell); >+ if (!Heap::isMarked(structure->globalObject())) >+ return; >+ >+ visitor.append(&possibleStructure); >+ return; >+} >+ > void CodeBlock::propagateTransitions(SlotVisitor& visitor) > { > UNUSED_PARAM(visitor); >@@ -2114,7 +2151,24 @@ void CodeBlock::propagateTransitions(Slo > #if ENABLE(DFG_JIT) > if (JITCode::isOptimizingJIT(jitType())) { > DFG::CommonData* dfgCommon = m_jitCode->dfgCommon(); >+ >+ // This does the following: >+ // - Forces liveness of structures with live global objects if we're in >+ // the FTL. >+ // - Forces liveness of structures that are known to be transitioned-to, >+ // and where the sources of those transitions are live. >+ >+ bool shouldForceStructureLiveness = jitType() == JITCode::FTLJIT; >+ >+ if (shouldForceStructureLiveness) { >+ for (unsigned i = 0; i < dfgCommon->weakReferences.size(); ++i) >+ forceStructureLiveness(visitor, dfgCommon->weakReferences[i]); >+ } >+ > for (unsigned i = 0; i < dfgCommon->transitions.size(); ++i) { >+ // FIXME: Consider getting rid of the transition fixpoint part of this. >+ // https://bugs.webkit.org/show_bug.cgi?id=128078 >+ > if ((!dfgCommon->transitions[i].m_codeOrigin > || Heap::isMarked(dfgCommon->transitions[i].m_codeOrigin.get())) > && Heap::isMarked(dfgCommon->transitions[i].m_from.get())) { >@@ -2130,7 +2184,14 @@ void CodeBlock::propagateTransitions(Slo > // heap location holds the source, and if so, stores the target. > // Hence the source must be live for the transition to be live. > visitor.append(&dfgCommon->transitions[i].m_to); >- } else >+ } >+ >+ if (shouldForceStructureLiveness) { >+ forceStructureLiveness(visitor, dfgCommon->transitions[i].m_from); >+ forceStructureLiveness(visitor, dfgCommon->transitions[i].m_to); >+ } >+ >+ if (!Heap::isMarked(dfgCommon->transitions[i].m_to.get())) > allAreMarkedSoFar = false; > } > } >@@ -2184,6 +2245,8 @@ void CodeBlock::visitWeakReferences(Slot > > void CodeBlock::finalizeUnconditionally() > { >+ bool verbose = Options::verboseStructureClearingInGC() || Options::verboseOSR(); >+ > Interpreter* interpreter = m_vm->interpreter; > if (JITCode::couldBeInterpreted(jitType())) { > const Vector<unsigned>& propertyAccessInstructions = m_unlinkedCode->propertyAccessInstructions(); >@@ -2196,7 +2259,7 @@ void CodeBlock::finalizeUnconditionally( > case op_put_by_id_out_of_line: > if (!curInstruction[4].u.structure || Heap::isMarked(curInstruction[4].u.structure.get())) > break; >- if (Options::verboseOSR()) >+ if (verbose) > dataLogF("Clearing LLInt property access with structure %p.\n", curInstruction[4].u.structure.get()); > curInstruction[4].u.structure.clear(); > curInstruction[5].u.operand = 0; >@@ -2209,7 +2272,7 @@ void CodeBlock::finalizeUnconditionally( > && Heap::isMarked(curInstruction[6].u.structure.get()) > && Heap::isMarked(curInstruction[7].u.structureChain.get())) > break; >- if (Options::verboseOSR()) { >+ if (verbose) { > dataLogF("Clearing LLInt put transition with structures %p -> %p, chain %p.\n", > curInstruction[4].u.structure.get(), > curInstruction[6].u.structure.get(), >@@ -2225,14 +2288,14 @@ void CodeBlock::finalizeUnconditionally( > case op_to_this: > if (!curInstruction[2].u.structure || Heap::isMarked(curInstruction[2].u.structure.get())) > break; >- if (Options::verboseOSR()) >+ if (verbose) > dataLogF("Clearing LLInt to_this with structure %p.\n", curInstruction[2].u.structure.get()); > curInstruction[2].u.structure.clear(); > break; > case op_get_callee: > if (!curInstruction[2].u.jsCell || Heap::isMarked(curInstruction[2].u.jsCell.get())) > break; >- if (Options::verboseOSR()) >+ if (verbose) > dataLogF("Clearing LLInt get callee with function %p.\n", curInstruction[2].u.jsCell.get()); > curInstruction[2].u.jsCell.clear(); > break; >@@ -2240,7 +2303,7 @@ void CodeBlock::finalizeUnconditionally( > WriteBarrierBase<JSActivation>& activation = curInstruction[5].u.activation; > if (!activation || Heap::isMarked(activation.get())) > break; >- if (Options::verboseOSR()) >+ if (verbose) > dataLogF("Clearing dead activation %p.\n", activation.get()); > activation.clear(); > break; >@@ -2254,7 +2317,7 @@ void CodeBlock::finalizeUnconditionally( > WriteBarrierBase<Structure>& structure = curInstruction[5].u.structure; > if (!structure || Heap::isMarked(structure.get())) > break; >- if (Options::verboseOSR()) >+ if (verbose) > dataLogF("Clearing scope access with structure %p.\n", structure.get()); > structure.clear(); > break; >@@ -2267,7 +2330,7 @@ void CodeBlock::finalizeUnconditionally( > #if ENABLE(LLINT) > for (unsigned i = 0; i < m_llintCallLinkInfos.size(); ++i) { > if (m_llintCallLinkInfos[i].isLinked() && !Heap::isMarked(m_llintCallLinkInfos[i].callee.get())) { >- if (Options::verboseOSR()) >+ if (verbose) > dataLog("Clearing LLInt call from ", *this, "\n"); > m_llintCallLinkInfos[i].unlink(); > } >@@ -2280,10 +2343,10 @@ void CodeBlock::finalizeUnconditionally( > #if ENABLE(DFG_JIT) > // Check if we're not live. If we are, then jettison. > if (!(shouldImmediatelyAssumeLivenessDuringScan() || m_jitCode->dfgCommon()->livenessHasBeenProved)) { >- if (Options::verboseOSR()) >+ if (verbose) > dataLog(*this, " has dead weak references, jettisoning during GC.\n"); > >- if (DFG::shouldShowDisassembly()) { >+ if (verbose || DFG::shouldShowDisassembly()) { > dataLog(*this, " will be jettisoned because of the following dead references:\n"); > DFG::CommonData* dfgCommon = m_jitCode->dfgCommon(); > for (unsigned i = 0; i < dfgCommon->transitions.size(); ++i) { >@@ -2317,7 +2380,7 @@ void CodeBlock::finalizeUnconditionally( > if (ClosureCallStubRoutine* stub = callLinkInfo(i).stub.get()) { > if (!Heap::isMarked(stub->structure()) > || !Heap::isMarked(stub->executable())) { >- if (Options::verboseOSR()) { >+ if (verbose) { > dataLog( > "Clearing closure call from ", *this, " to ", > stub->executable()->hashFor(callLinkInfo(i).specializationKind()), >@@ -2326,7 +2389,7 @@ void CodeBlock::finalizeUnconditionally( > callLinkInfo(i).unlink(*m_vm, repatchBuffer); > } > } else if (!Heap::isMarked(callLinkInfo(i).callee.get())) { >- if (Options::verboseOSR()) { >+ if (verbose) { > dataLog( > "Clearing call from ", *this, " to ", > RawPointer(callLinkInfo(i).callee.get()), " (", >@@ -2338,8 +2401,11 @@ void CodeBlock::finalizeUnconditionally( > } > } > if (!!callLinkInfo(i).lastSeenCallee >- && !Heap::isMarked(callLinkInfo(i).lastSeenCallee.get())) >+ && !Heap::isMarked(callLinkInfo(i).lastSeenCallee.get())) { >+ if (verbose) >+ dataLog("Clearing lastSeenCallee from ", *this, " to ", RawPointer(callLinkInfo(i).lastSeenCallee.get()), "\n"); > callLinkInfo(i).lastSeenCallee.clear(); >+ } > } > for (Bag<StructureStubInfo>::iterator iter = m_stubInfos.begin(); !!iter; ++iter) { > StructureStubInfo& stubInfo = **iter; >@@ -2347,6 +2413,12 @@ void CodeBlock::finalizeUnconditionally( > if (stubInfo.visitWeakReferences()) > continue; > >+ if (verbose) { >+ dataLog("Clearing stubInfo from ", *this, " at ", stubInfo.codeOrigin, " with state: "); >+ dumpStructureStubInfo(WTF::dataFile(), *vm(), stubInfo, 0); >+ dataLog("\n"); >+ } >+ > resetStubDuringGCInternal(repatchBuffer, stubInfo); > } > } >@@ -2413,6 +2485,9 @@ void CodeBlock::resetStubInternal(Repatc > void CodeBlock::resetStubDuringGCInternal(RepatchBuffer& repatchBuffer, StructureStubInfo& stubInfo) > { > resetStubInternal(repatchBuffer, stubInfo); >+ >+ // FIXME: Consider getting rid of this flag; it might not be needed anymore. >+ // https://bugs.webkit.org/show_bug.cgi?id=128079 > stubInfo.resetByGC = true; > } > #endif >@@ -2787,7 +2862,7 @@ void CodeBlock::jettison(Profiler::Jetti > RELEASE_ASSERT(reason != Profiler::NotJettisoned); > > #if ENABLE(DFG_JIT) >- if (DFG::shouldShowDisassembly()) { >+ if (Options::verboseOSR() || DFG::shouldShowDisassembly()) { > dataLog("Jettisoning ", *this); > if (mode == CountReoptimization) > dataLog(" and counting reoptimization"); >Index: Source/JavaScriptCore/runtime/Options.h >=================================================================== >--- Source/JavaScriptCore/runtime/Options.h (revision 163265) >+++ Source/JavaScriptCore/runtime/Options.h (working copy) >@@ -132,6 +132,7 @@ typedef OptionRange optionRange; > v(bool, alwaysComputeHash, false) \ > v(bool, testTheFTL, false) \ > v(bool, verboseSanitizeStack, false) \ >+ v(bool, verboseStructureClearingInGC, false) \ > \ > v(bool, enableOSREntryToDFG, true) \ > v(bool, enableOSREntryToFTL, true) \
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 128072
:
222934
|
222935
|
223213
|
233923
|
233951