Changeset 246272 in webkit


Ignore:
Timestamp:
Jun 10, 2019 12:49:58 PM (5 years ago)
Author:
ysuzuki@apple.com
Message:

[JSC] UnlinkedCodeBlock should be eventually jettisoned in VM mini mode
https://bugs.webkit.org/show_bug.cgi?id=198023

Reviewed by Saam Barati.

JSTests:

  • stress/reparsing-unlinked-codeblock.js: Added.

(shouldBe):
(hello):

Source/JavaScriptCore:

While CodeBlock is periodically jettisoned, UnlinkedCodeBlock and UnlinkedFunctionExecutable can be retained almost forever in certain type of applications.
When we execute a program, which has UnlinkedProgramCodeBlock retained in CodeCache. And UnlinkedProgramCodeBlock holds array of UnlinkedFunctionExecutable.
And UnlinkedFunctionExecutables hold UnlinkedFunctionCodeBlocks once it is generated. So eventually, this tree gets larger and larger until we purge
UnlinkedProgramCodeBlock from CodeCache. This is OK in the browser case. We navigate to various other pages, and UnlinkedProgramCodeBlocks should eventually
be pruned from CodeCache with the new ones. So this tree won't be retained forever. But the behavior is different in the other applications that do not have
navigations. If they only have one program which holds all, we basically retain this tree during executing this application. The same thing can happen in
web applications which does not have navigation and keeps alive for a long time. Once we hit CodeCache limit by periodically executing a new script, we will
hit the uppermost of memory footprint. But until that, we increase our memory footprint.

However, destroying these UnlinkedCodeBlocks and UnlinkedFunctionExecutables causes a tricky problem. In the browser environment, navigation can happen at any
time. So even if the given UnlinkedCodeBlock seems unused in the current page, it can be used when navigating to a new page which is under the same domain.
One example is initializing function in a script. It is only executed once per page. So once it is executed, it seems that this UnlinkedCodeBlock is unused.
But this will be used when we navigate to a new page. Pruning code blocks based on usage could cause performance regression.

But if our VM is mini VM mode, the story is different. In mini VM mode, we focus on memory footprint rather than performance e.g. daemons. The daemon never
reuse these CodeCache since we do not have the navigation.

This patch logically makes UnlinkedFunctionExecutable -> UnlinkedCodeBlock reference weak when VM is mini mode. If UnlinkedCodeBlock is used in previous GC
cycle, we retain it. But if it is not used, and if UnlinkedFunctionExecutable is only the cell keeping UnlinkedCodeBlock alive, we destroy it. It is a
heuristic. In a super pathological case, it could increase memory footprint. Consider the following example.

UnlinkedFunctionExecutable(A1) -> UnlinkedCodeBlock(B1) -> UnlinkedFunctionExecutable(C1) -> UnlinkedCodeBlock(D1)


CodeBlock(E1)

We could delete A1, B1, and C1 while keeping D1. But if we eventually re-execute the same code corresponding to A1, B1, C1, they will be newly created, and
we will create duplicate UnlinkedCodeBlock and instructions stream for D1.

UnlinkedCodeBlock(D1)


CodeBlock(E1)

UnlinkedFunctionExecutable(A2) -> UnlinkedCodeBlock(B2) -> UnlinkedFunctionExecutable(C2) -> UnlinkedCodeBlock(D2)

But this does not happen in practice and even it happens, we eventually discard D1 and D2 since CodeBlock E1 will be jettisoned anyway. So in practice, we do
not see memory footprint increase. We tested it in Gmail and the target application, but both said memory footprint reduction (30 MB / 400 MB and 1 MB /6 MB).
While this affects on performance much on tests which has navigation (1-3 % regression in Speedometer2, note that JetStream2 does not show regression in x64,
while it is not enabling mini mode), we do not apply this to non mini mode VM until we come up with a good strategy to fasten performance of re-generation.
Personally I think flushing destroyed UnlinkedCodeBlock to the disk sounds promising.

If UnlinkedCodeBlock is generated from bytecode cache, we do not make UnlinkedFunctionExecutable -> UnlinkedCodeBlock link weak because the decoder of the bytecode
cache assumes that generated JSCells won't be destroyed while the parent cells of that cell are live. This is true in the current implementation, and this assumption
will be broken with this patch. So, for now, we do not make this link weak. Currently, our target application does not use bytecode cache so it is OK.

This patch also introduce simple heuristic. We are counting UnlinkedCodeBlock's age. And once the age becomes maximum size, we make UnlinkedFunctionExecutable ->
UnlinkedCodeBlock link weak. We also use execution counter information to reset this age: CodeBlock will reset undelying UnlinkedCodeBlock's age if it has executed
While this heuristic is quite simple, it has some effect in practice. Basically what happens with this heuristic is that UnlinkedFunctionExecutable ->
UnlinkedCodeBlock link strong. When GC happens, we are executing some CodeBlocks, which become live. And ScriptExecutables -> UnlinkedFunctionExecutables held
by this CodeBlock become also live. Then UnlinkedFunctionExecutables can mark the child UnlinkedCodeBlocks if it is not so old.
If some of parent UnlinkedFunctionExecutable becomes dead, child UnlinkedCodeBlocks tends to be dead unless some live CodeBlock holds it. But it is OK for a first
heuristics since this means that parent code block is now considered old, reachable UnlinkedCodeBlock will be used when the parent is executed again. So destroying
the tree is OK even if the tree may include some new UnlinkedCodeBlock. While we could make more sophisticated mechanism to manage these lifetime, I think this is a
good starting point.

Based on measurement, we pick 7 as a maximum age. If we pick 0, we can get more memory reduction (1 - 1.5 MB!), while we ends up reparsing codes so many times.
It seems that 7 can reduce fair amount of memory while doing small # of reparsing on average (usually, 1, 2. Sometimes, 100. But not 300, which is the case in 0).
If we want to get more memory reduction for the sake of performance, we could decrease this age limit.

Since we do not have an automated script right now so it is a bit difficult to measure memory footprint precisely. But manual testing shows that this patch improves
memory footprint of our target application from about 6.5 MB to about 5.9 MB.

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::finalizeUnconditionally):

  • bytecode/CodeBlock.h:
  • bytecode/UnlinkedCodeBlock.cpp:

(JSC::UnlinkedCodeBlock::UnlinkedCodeBlock):
(JSC::UnlinkedCodeBlock::visitChildren):

  • bytecode/UnlinkedCodeBlock.h:

(JSC::UnlinkedCodeBlock::age const):
(JSC::UnlinkedCodeBlock::resetAge):

  • bytecode/UnlinkedFunctionExecutable.cpp:

(JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):
(JSC::UnlinkedFunctionExecutable::visitChildren):
(JSC::UnlinkedFunctionExecutable::unlinkedCodeBlockFor):
(JSC::UnlinkedFunctionExecutable::decodeCachedCodeBlocks):
(JSC::UnlinkedFunctionExecutable::finalizeUnconditionally):

  • bytecode/UnlinkedFunctionExecutable.h:
  • heap/Heap.cpp:

(JSC::Heap::finalizeUnconditionalFinalizers):

  • runtime/CachedTypes.cpp:

(JSC::UnlinkedCodeBlock::UnlinkedCodeBlock):
(JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):

  • runtime/CodeSpecializationKind.h:
  • runtime/Options.h:
  • runtime/VM.cpp:

(JSC::VM::isInMiniMode): Deleted.

  • runtime/VM.h:

(JSC::VM::isInMiniMode):
(JSC::VM::useUnlinkedCodeBlockJettisoning):

Tools:

  • Scripts/run-jsc-stress-tests:
Location:
trunk
Files:
1 added
16 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r246237 r246272  
     12019-06-10  Yusuke Suzuki  <ysuzuki@apple.com>
     2
     3        [JSC] UnlinkedCodeBlock should be eventually jettisoned in VM mini mode
     4        https://bugs.webkit.org/show_bug.cgi?id=198023
     5
     6        Reviewed by Saam Barati.
     7
     8        * stress/reparsing-unlinked-codeblock.js: Added.
     9        (shouldBe):
     10        (hello):
     11
    1122019-06-09  Yusuke Suzuki  <ysuzuki@apple.com>
    213
  • trunk/Source/JavaScriptCore/ChangeLog

    r246270 r246272  
     12019-06-10  Yusuke Suzuki  <ysuzuki@apple.com>
     2
     3        [JSC] UnlinkedCodeBlock should be eventually jettisoned in VM mini mode
     4        https://bugs.webkit.org/show_bug.cgi?id=198023
     5
     6        Reviewed by Saam Barati.
     7
     8        While CodeBlock is periodically jettisoned, UnlinkedCodeBlock and UnlinkedFunctionExecutable can be retained almost forever in certain type of applications.
     9        When we execute a program, which has UnlinkedProgramCodeBlock retained in CodeCache. And UnlinkedProgramCodeBlock holds array of UnlinkedFunctionExecutable.
     10        And UnlinkedFunctionExecutables hold UnlinkedFunctionCodeBlocks once it is generated. So eventually, this tree gets larger and larger until we purge
     11        UnlinkedProgramCodeBlock from CodeCache. This is OK in the browser case. We navigate to various other pages, and UnlinkedProgramCodeBlocks should eventually
     12        be pruned from CodeCache with the new ones. So this tree won't be retained forever. But the behavior is different in the other applications that do not have
     13        navigations. If they only have one program which holds all, we basically retain this tree during executing this application. The same thing can happen in
     14        web applications which does not have navigation and keeps alive for a long time. Once we hit CodeCache limit by periodically executing a new script, we will
     15        hit the uppermost of memory footprint. But until that, we increase our memory footprint.
     16
     17        However, destroying these UnlinkedCodeBlocks and UnlinkedFunctionExecutables causes a tricky problem. In the browser environment, navigation can happen at any
     18        time. So even if the given UnlinkedCodeBlock seems unused in the current page, it can be used when navigating to a new page which is under the same domain.
     19        One example is initializing function in a script. It is only executed once per page. So once it is executed, it seems that this UnlinkedCodeBlock is unused.
     20        But this will be used when we navigate to a new page. Pruning code blocks based on usage could cause performance regression.
     21
     22        But if our VM is mini VM mode, the story is different. In mini VM mode, we focus on memory footprint rather than performance e.g. daemons. The daemon never
     23        reuse these CodeCache since we do not have the navigation.
     24
     25        This patch logically makes UnlinkedFunctionExecutable -> UnlinkedCodeBlock reference weak when VM is mini mode. If UnlinkedCodeBlock is used in previous GC
     26        cycle, we retain it. But if it is not used, and if UnlinkedFunctionExecutable is only the cell keeping UnlinkedCodeBlock alive, we destroy it. It is a
     27        heuristic. In a super pathological case, it could increase memory footprint. Consider the following example.
     28
     29            UnlinkedFunctionExecutable(A1) -> UnlinkedCodeBlock(B1) -> UnlinkedFunctionExecutable(C1) -> UnlinkedCodeBlock(D1)
     30                                                                                                             ^
     31                                                                                                         CodeBlock(E1)
     32
     33        We could delete A1, B1, and C1 while keeping D1. But if we eventually re-execute the same code corresponding to A1, B1, C1, they will be newly created, and
     34        we will create duplicate UnlinkedCodeBlock and instructions stream for D1.
     35
     36                                                                                                         UnlinkedCodeBlock(D1)
     37                                                                                                             ^
     38                                                                                                         CodeBlock(E1)
     39
     40            UnlinkedFunctionExecutable(A2) -> UnlinkedCodeBlock(B2) -> UnlinkedFunctionExecutable(C2) -> UnlinkedCodeBlock(D2)
     41
     42        But this does not happen in practice and even it happens, we eventually discard D1 and D2 since CodeBlock E1 will be jettisoned anyway. So in practice, we do
     43        not see memory footprint increase. We tested it in Gmail and the target application, but both said memory footprint reduction (30 MB / 400 MB and 1 MB /6 MB).
     44        While this affects on performance much on tests which has navigation (1-3 % regression in Speedometer2, note that JetStream2 does not show regression in x64,
     45        while it is not enabling mini mode), we do not apply this to non mini mode VM until we come up with a good strategy to fasten performance of re-generation.
     46        Personally I think flushing destroyed UnlinkedCodeBlock to the disk sounds promising.
     47
     48        If UnlinkedCodeBlock is generated from bytecode cache, we do not make UnlinkedFunctionExecutable -> UnlinkedCodeBlock link weak because the decoder of the bytecode
     49        cache assumes that generated JSCells won't be destroyed while the parent cells of that cell are live. This is true in the current implementation, and this assumption
     50        will be broken with this patch. So, for now, we do not make this link weak. Currently, our target application does not use bytecode cache so it is OK.
     51
     52        This patch also introduce simple heuristic. We are counting UnlinkedCodeBlock's age. And once the age becomes maximum size, we make UnlinkedFunctionExecutable ->
     53        UnlinkedCodeBlock link weak. We also use execution counter information to reset this age: CodeBlock will reset undelying UnlinkedCodeBlock's age if it has executed
     54        While this heuristic is quite simple, it has some effect in practice. Basically what happens with this heuristic is that UnlinkedFunctionExecutable ->
     55        UnlinkedCodeBlock link strong. When GC happens, we are executing some CodeBlocks, which become live. And ScriptExecutables -> UnlinkedFunctionExecutables held
     56        by this CodeBlock become also live. Then UnlinkedFunctionExecutables can mark the child UnlinkedCodeBlocks if it is not so old.
     57        If some of parent UnlinkedFunctionExecutable becomes dead, child UnlinkedCodeBlocks tends to be dead unless some live CodeBlock holds it. But it is OK for a first
     58        heuristics since this means that parent code block is now considered old, reachable UnlinkedCodeBlock will be used when the parent is executed again. So destroying
     59        the tree is OK even if the tree may include some new UnlinkedCodeBlock. While we could make more sophisticated mechanism to manage these lifetime, I think this is a
     60        good starting point.
     61
     62        Based on measurement, we pick 7 as a maximum age. If we pick 0, we can get more memory reduction (1 - 1.5 MB!), while we ends up reparsing codes so many times.
     63        It seems that 7 can reduce fair amount of memory while doing small # of reparsing on average (usually, 1, 2. Sometimes, 100. But not 300, which is the case in 0).
     64        If we want to get more memory reduction for the sake of performance, we could decrease this age limit.
     65
     66        Since we do not have an automated script right now so it is a bit difficult to measure memory footprint precisely. But manual testing shows that this patch improves
     67        memory footprint of our target application from about 6.5 MB to about 5.9 MB.
     68
     69        * bytecode/CodeBlock.cpp:
     70        (JSC::CodeBlock::finalizeUnconditionally):
     71        * bytecode/CodeBlock.h:
     72        * bytecode/UnlinkedCodeBlock.cpp:
     73        (JSC::UnlinkedCodeBlock::UnlinkedCodeBlock):
     74        (JSC::UnlinkedCodeBlock::visitChildren):
     75        * bytecode/UnlinkedCodeBlock.h:
     76        (JSC::UnlinkedCodeBlock::age const):
     77        (JSC::UnlinkedCodeBlock::resetAge):
     78        * bytecode/UnlinkedFunctionExecutable.cpp:
     79        (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):
     80        (JSC::UnlinkedFunctionExecutable::visitChildren):
     81        (JSC::UnlinkedFunctionExecutable::unlinkedCodeBlockFor):
     82        (JSC::UnlinkedFunctionExecutable::decodeCachedCodeBlocks):
     83        (JSC::UnlinkedFunctionExecutable::finalizeUnconditionally):
     84        * bytecode/UnlinkedFunctionExecutable.h:
     85        * heap/Heap.cpp:
     86        (JSC::Heap::finalizeUnconditionalFinalizers):
     87        * runtime/CachedTypes.cpp:
     88        (JSC::UnlinkedCodeBlock::UnlinkedCodeBlock):
     89        (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):
     90        * runtime/CodeSpecializationKind.h:
     91        * runtime/Options.h:
     92        * runtime/VM.cpp:
     93        (JSC::VM::isInMiniMode): Deleted.
     94        * runtime/VM.h:
     95        (JSC::VM::isInMiniMode):
     96        (JSC::VM::useUnlinkedCodeBlockJettisoning):
     97
    1982019-06-10  Timothy Hatcher  <timothy@apple.com>
    299
  • trunk/Source/JavaScriptCore/bytecode/CodeBlock.cpp

    r245906 r246272  
    13741374#endif // ENABLE(DFG_JIT)
    13751375
     1376    auto updateActivity = [&] {
     1377        if (!VM::useUnlinkedCodeBlockJettisoning())
     1378            return;
     1379        JITCode* jitCode = m_jitCode.get();
     1380        double count = 0;
     1381        bool alwaysActive = false;
     1382        switch (JITCode::jitTypeFor(jitCode)) {
     1383        case JITType::None:
     1384        case JITType::HostCallThunk:
     1385            return;
     1386        case JITType::InterpreterThunk:
     1387            count = m_llintExecuteCounter.count();
     1388            break;
     1389        case JITType::BaselineJIT:
     1390            count = m_jitExecuteCounter.count();
     1391            break;
     1392        case JITType::DFGJIT:
     1393            count = static_cast<DFG::JITCode*>(jitCode)->tierUpCounter.count();
     1394            break;
     1395        case JITType::FTLJIT:
     1396            alwaysActive = true;
     1397            break;
     1398        }
     1399        if (alwaysActive || m_previousCounter < count) {
     1400            // CodeBlock is active right now, so resetting UnlinkedCodeBlock's age.
     1401            m_unlinkedCode->resetAge();
     1402        }
     1403        m_previousCounter = count;
     1404    };
     1405    updateActivity();
     1406
    13761407    VM::SpaceAndSet::setFor(*subspace()).remove(this);
    13771408}
  • trunk/Source/JavaScriptCore/bytecode/CodeBlock.h

    r245906 r246272  
    10111011
    10121012    MonotonicTime m_creationTime;
     1013    double m_previousCounter { 0 };
    10131014
    10141015    std::unique_ptr<RareData> m_rareData;
  • trunk/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp

    r244915 r246272  
    7272    , m_codeType(static_cast<unsigned>(codeType))
    7373    , m_didOptimize(static_cast<unsigned>(MixedTriState))
     74    , m_age(0)
    7475    , m_parseMode(info.parseMode())
    7576    , m_codeGenerationMode(codeGenerationMode)
     
    8990    Base::visitChildren(thisObject, visitor);
    9091    auto locker = holdLock(thisObject->cellLock());
     92    thisObject->m_age = std::min<unsigned>(static_cast<unsigned>(thisObject->m_age) + 1, maxAge);
    9193    for (FunctionExpressionVector::iterator ptr = thisObject->m_functionDecls.begin(), end = thisObject->m_functionDecls.end(); ptr != end; ++ptr)
    9294        visitor.append(*ptr);
  • trunk/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h

    r244915 r246272  
    340340    void setDidOptimize(TriState didOptimize) { m_didOptimize = static_cast<unsigned>(didOptimize); }
    341341
     342    static constexpr unsigned maxAge = 7;
     343
     344    unsigned age() const { return m_age; }
     345    void resetAge() { m_age = 0; }
     346
    342347    void dump(PrintStream&) const;
    343348
     
    404409    void getLineAndColumn(const ExpressionRangeInfo&, unsigned& line, unsigned& column) const;
    405410    BytecodeLivenessAnalysis& livenessAnalysisSlow(CodeBlock*);
     411
    406412
    407413    VirtualRegister m_thisRegister;
     
    425431    unsigned m_codeType : 2;
    426432    unsigned m_didOptimize : 2;
     433    unsigned m_age : 3;
     434    static_assert(((1U << 3) - 1) >= maxAge);
    427435public:
    428436    ConcurrentJSLock m_lock;
  • trunk/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp

    r245288 r246272  
    108108    , m_functionMode(static_cast<unsigned>(node->functionMode()))
    109109    , m_derivedContextType(static_cast<unsigned>(derivedContextType))
     110    , m_isGeneratedFromCache(false)
    110111    , m_unlinkedCodeBlockForCall()
    111112    , m_unlinkedCodeBlockForConstruct()
     
    143144    ASSERT_GC_OBJECT_INHERITS(thisObject, info());
    144145    Base::visitChildren(thisObject, visitor);
    145     if (!thisObject->m_isCached) {
     146
     147    if (thisObject->codeBlockEdgeMayBeWeak()) {
     148        auto markIfProfitable = [&] (WriteBarrier<UnlinkedFunctionCodeBlock>& unlinkedCodeBlock) {
     149            if (!unlinkedCodeBlock)
     150                return;
     151            if (unlinkedCodeBlock->didOptimize() == TrueTriState)
     152                visitor.append(unlinkedCodeBlock);
     153            else if (unlinkedCodeBlock->age() < UnlinkedCodeBlock::maxAge)
     154                visitor.append(unlinkedCodeBlock);
     155        };
     156        markIfProfitable(thisObject->m_unlinkedCodeBlockForCall);
     157        markIfProfitable(thisObject->m_unlinkedCodeBlockForConstruct);
     158    } else if (!thisObject->m_isCached) {
    146159        visitor.append(thisObject->m_unlinkedCodeBlockForCall);
    147160        visitor.append(thisObject->m_unlinkedCodeBlockForConstruct);
     
    198211}
    199212
    200 UnlinkedFunctionCodeBlock* UnlinkedFunctionExecutable::unlinkedCodeBlockFor(CodeSpecializationKind specializationKind)
    201 {
    202     switch (specializationKind) {
    203     case CodeForCall:
    204         return m_unlinkedCodeBlockForCall.get();
    205     case CodeForConstruct:
    206         return m_unlinkedCodeBlockForConstruct.get();
    207     }
    208     ASSERT_NOT_REACHED();
    209     return nullptr;
    210 }
    211 
    212213UnlinkedFunctionCodeBlock* UnlinkedFunctionExecutable::unlinkedCodeBlockFor(
    213214    VM& vm, const SourceCode& source, CodeSpecializationKind specializationKind,
     
    215216{
    216217    if (m_isCached)
    217         decodeCachedCodeBlocks();
     218        decodeCachedCodeBlocks(vm);
    218219    switch (specializationKind) {
    219220    case CodeForCall:
     
    247248}
    248249
    249 void UnlinkedFunctionExecutable::decodeCachedCodeBlocks()
     250void UnlinkedFunctionExecutable::decodeCachedCodeBlocks(VM& vm)
    250251{
    251252    ASSERT(m_isCached);
     
    257258    int32_t cachedCodeBlockForConstructOffset = m_cachedCodeBlockForConstructOffset;
    258259
    259     DeferGC deferGC(decoder->vm().heap);
     260    DeferGC deferGC(vm.heap);
    260261
    261262    // No need to clear m_unlinkedCodeBlockForCall here, since we moved the decoder out of the same slot
     
    269270    WTF::storeStoreFence();
    270271    m_isCached = false;
    271     decoder->vm().heap.writeBarrier(this);
     272    vm.heap.writeBarrier(this);
    272273}
    273274
     
    285286}
    286287
     288void UnlinkedFunctionExecutable::finalizeUnconditionally(VM& vm)
     289{
     290    if (codeBlockEdgeMayBeWeak()) {
     291        bool isCleared = false;
     292        bool isStillValid = false;
     293        auto clearIfDead = [&] (WriteBarrier<UnlinkedFunctionCodeBlock>& unlinkedCodeBlock) {
     294            if (!unlinkedCodeBlock)
     295                return;
     296            if (!vm.heap.isMarked(unlinkedCodeBlock.get())) {
     297                unlinkedCodeBlock.clear();
     298                isCleared = true;
     299            } else
     300                isStillValid = true;
     301        };
     302        clearIfDead(m_unlinkedCodeBlockForCall);
     303        clearIfDead(m_unlinkedCodeBlockForConstruct);
     304        if (isCleared && !isStillValid)
     305            vm.unlinkedFunctionExecutableSpace.set.remove(this);
     306    }
     307}
     308
    287309} // namespace JSC
  • trunk/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h

    r245288 r246272  
    115115    void setInvalidTypeProfilingOffsets();
    116116
    117     UnlinkedFunctionCodeBlock* unlinkedCodeBlockFor(CodeSpecializationKind);
    118 
    119117    UnlinkedFunctionCodeBlock* unlinkedCodeBlockFor(
    120118        VM&, const SourceCode&, CodeSpecializationKind, OptionSet<CodeGenerationMode>,
     
    190188    }
    191189
     190    void finalizeUnconditionally(VM&);
     191
    192192    struct RareData {
    193193        WTF_MAKE_STRUCT_FAST_ALLOCATED;
     
    203203    UnlinkedFunctionExecutable(Decoder&, const CachedFunctionExecutable&);
    204204
    205     void decodeCachedCodeBlocks();
     205    void decodeCachedCodeBlocks(VM&);
     206
     207    bool codeBlockEdgeMayBeWeak() const
     208    {
     209        // Currently, bytecode cache assumes that the tree of UnlinkedFunctionExecutable and UnlinkedCodeBlock will not be destroyed while the parent is live.
     210        // Bytecode cache uses this asumption to avoid duplicate materialization by bookkeeping the heap cells in the offste-to-pointer map.
     211        return VM::useUnlinkedCodeBlockJettisoning() && !m_isGeneratedFromCache;
     212    }
    206213
    207214    unsigned m_firstLineOffset : 31;
     
    229236    unsigned m_functionMode : 2; // FunctionMode
    230237    unsigned m_derivedContextType: 2;
     238    unsigned m_isGeneratedFromCache : 1;
    231239
    232240    union {
  • trunk/Source/JavaScriptCore/heap/Heap.cpp

    r246073 r246272  
    607607    finalizeMarkedUnconditionalFinalizers<ExecutableToCodeBlockEdge>(vm()->executableToCodeBlockEdgesWithFinalizers);
    608608    finalizeMarkedUnconditionalFinalizers<StructureRareData>(vm()->structureRareDataSpace);
     609    finalizeMarkedUnconditionalFinalizers<UnlinkedFunctionExecutable>(vm()->unlinkedFunctionExecutableSpace.set);
    609610    if (vm()->m_weakSetSpace)
    610611        finalizeMarkedUnconditionalFinalizers<JSWeakSet>(*vm()->m_weakSetSpace);
  • trunk/Source/JavaScriptCore/runtime/CachedTypes.cpp

    r246060 r246272  
    20322032
    20332033    , m_didOptimize(static_cast<unsigned>(MixedTriState))
     2034    , m_age(0)
    20342035
    20352036    , m_features(cachedCodeBlock.features())
     
    21592160    , m_functionMode(cachedExecutable.functionMode())
    21602161    , m_derivedContextType(cachedExecutable.derivedContextType())
     2162    , m_isGeneratedFromCache(true)
    21612163    , m_unlinkedCodeBlockForCall()
    21622164    , m_unlinkedCodeBlockForConstruct()
  • trunk/Source/JavaScriptCore/runtime/CodeSpecializationKind.h

    r206525 r246272  
    2828namespace JSC {
    2929
    30 enum CodeSpecializationKind { CodeForCall, CodeForConstruct };
     30enum CodeSpecializationKind : uint8_t { CodeForCall, CodeForConstruct };
    3131
    3232inline CodeSpecializationKind specializationFromIsCall(bool isCall)
  • trunk/Source/JavaScriptCore/runtime/Options.h

    r246003 r246272  
    521521    v(optionString, dumpJITMemoryPath, nullptr, Restricted, nullptr) \
    522522    v(double, dumpJITMemoryFlushInterval, 10, Restricted, "Maximum time in between flushes of the JIT memory dump in seconds.") \
     523    v(bool, useUnlinkedCodeBlockJettisoning, false, Normal, "If true, UnlinkedCodeBlock can be jettisoned.") \
    523524
    524525
  • trunk/Source/JavaScriptCore/runtime/VM.cpp

    r246073 r246272  
    233233}
    234234
    235 bool VM::isInMiniMode()
    236 {
    237     return !canUseJIT() || Options::forceMiniVMMode();
    238 }
    239 
    240235inline unsigned VM::nextID()
    241236{
  • trunk/Source/JavaScriptCore/runtime/VM.h

    r246073 r246272  
    633633
    634634    static JS_EXPORT_PRIVATE bool canUseAssembler();
    635     static JS_EXPORT_PRIVATE bool isInMiniMode();
     635    static bool isInMiniMode()
     636    {
     637        return !canUseJIT() || Options::forceMiniVMMode();
     638    }
     639
     640    static bool useUnlinkedCodeBlockJettisoning()
     641    {
     642        return Options::useUnlinkedCodeBlockJettisoning() || isInMiniMode();
     643    }
    636644
    637645    static void computeCanUseJIT();
  • trunk/Tools/ChangeLog

    r246270 r246272  
     12019-06-10  Yusuke Suzuki  <ysuzuki@apple.com>
     2
     3        [JSC] UnlinkedCodeBlock should be eventually jettisoned in VM mini mode
     4        https://bugs.webkit.org/show_bug.cgi?id=198023
     5
     6        Reviewed by Saam Barati.
     7
     8        * Scripts/run-jsc-stress-tests:
     9
    1102019-06-10  Timothy Hatcher  <timothy@apple.com>
    211
  • trunk/Tools/Scripts/run-jsc-stress-tests

    r245879 r246272  
    781781end
    782782
     783def runMiniMode(*optionalTestSpecificOptions)
     784    run("mini-mode", "--forceMiniVMMode=true", *optionalTestSpecificOptions)
     785end
     786
    783787def defaultRun
    784788    if $mode == "quick"
     
    787791        runDefault
    788792        runBytecodeCache
     793        runMiniMode
    789794        if $jitTests
    790795            runNoLLInt
Note: See TracChangeset for help on using the changeset viewer.