1/*
2 * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "CodeBlockSet.h"
28
29#include "CodeBlock.h"
30#include "JSCInlines.h"
31#include "SlotVisitor.h"
32#include <wtf/CommaPrinter.h>
33
34namespace JSC {
35
36static const bool verbose = false;
37
38CodeBlockSet::CodeBlockSet(BlockAllocator& blockAllocator)
39    : m_currentlyExecuting(blockAllocator)
40{
41}
42
43CodeBlockSet::~CodeBlockSet()
44{
45    for (CodeBlock* codeBlock : m_oldCodeBlocks)
46        codeBlock->deref();
47
48    for (CodeBlock* codeBlock : m_newCodeBlocks)
49        codeBlock->deref();
50}
51
52void CodeBlockSet::add(PassRefPtr<CodeBlock> codeBlock)
53{
54    CodeBlock* block = codeBlock.leakRef();
55    bool isNewEntry = m_newCodeBlocks.add(block).isNewEntry;
56    ASSERT_UNUSED(isNewEntry, isNewEntry);
57}
58
59void CodeBlockSet::promoteYoungCodeBlocks()
60{
61    m_oldCodeBlocks.add(m_newCodeBlocks.begin(), m_newCodeBlocks.end());
62    m_newCodeBlocks.clear();
63}
64
65void CodeBlockSet::clearMarksForFullCollection()
66{
67    for (CodeBlock* codeBlock : m_oldCodeBlocks) {
68        codeBlock->m_mayBeExecuting = false;
69        codeBlock->m_visitAggregateHasBeenCalled = false;
70    }
71
72    // We promote after we clear marks on the old generation CodeBlocks because
73    // none of the young generations CodeBlocks need to be cleared.
74    promoteYoungCodeBlocks();
75}
76
77void CodeBlockSet::clearMarksForEdenCollection(const Vector<const JSCell*>& rememberedSet)
78{
79    // This ensures that we will revisit CodeBlocks in remembered Executables even if they were previously marked.
80    for (const JSCell* cell : rememberedSet) {
81        ScriptExecutable* executable = const_cast<ScriptExecutable*>(jsDynamicCast<const ScriptExecutable*>(cell));
82        if (!executable)
83            continue;
84        executable->forEachCodeBlock([](CodeBlock* codeBlock) {
85            codeBlock->m_mayBeExecuting = false;
86            codeBlock->m_visitAggregateHasBeenCalled = false;
87        });
88    }
89}
90
91void CodeBlockSet::deleteUnmarkedAndUnreferenced(HeapOperation collectionType)
92{
93    HashSet<CodeBlock*>& set = collectionType == EdenCollection ? m_newCodeBlocks : m_oldCodeBlocks;
94
95    // This needs to be a fixpoint because code blocks that are unmarked may
96    // refer to each other. For example, a DFG code block that is owned by
97    // the GC may refer to an FTL for-entry code block that is also owned by
98    // the GC.
99    Vector<CodeBlock*, 16> toRemove;
100    if (verbose)
101        dataLog("Fixpointing over unmarked, set size = ", set.size(), "...\n");
102    for (;;) {
103        for (CodeBlock* codeBlock : set) {
104            if (!codeBlock->hasOneRef())
105                continue;
106            if (codeBlock->m_mayBeExecuting)
107                continue;
108            codeBlock->deref();
109            toRemove.append(codeBlock);
110        }
111        if (verbose)
112            dataLog("    Removing ", toRemove.size(), " blocks.\n");
113        if (toRemove.isEmpty())
114            break;
115        for (CodeBlock* codeBlock : toRemove)
116            set.remove(codeBlock);
117        toRemove.resize(0);
118    }
119
120    // Any remaining young CodeBlocks are live and need to be promoted to the set of old CodeBlocks.
121    if (collectionType == EdenCollection)
122        promoteYoungCodeBlocks();
123}
124
125void CodeBlockSet::remove(CodeBlock* codeBlock)
126{
127    codeBlock->deref();
128    if (m_oldCodeBlocks.contains(codeBlock)) {
129        m_oldCodeBlocks.remove(codeBlock);
130        return;
131    }
132    ASSERT(m_newCodeBlocks.contains(codeBlock));
133    m_newCodeBlocks.remove(codeBlock);
134}
135
136void CodeBlockSet::traceMarked(SlotVisitor& visitor)
137{
138    if (verbose)
139        dataLog("Tracing ", m_currentlyExecuting.size(), " code blocks.\n");
140    for (CodeBlock* codeBlock : m_currentlyExecuting) {
141        ASSERT(codeBlock->m_mayBeExecuting);
142        codeBlock->visitAggregate(visitor);
143    }
144}
145
146void CodeBlockSet::rememberCurrentlyExecutingCodeBlocks(Heap* heap)
147{
148#if ENABLE(GGC)
149    if (verbose)
150        dataLog("Remembering ", m_currentlyExecuting.size(), " code blocks.\n");
151    for (CodeBlock* codeBlock : m_currentlyExecuting) {
152        heap->addToRememberedSet(codeBlock->ownerExecutable());
153        ASSERT(codeBlock->m_mayBeExecuting);
154    }
155    m_currentlyExecuting.clear();
156#else
157    UNUSED_PARAM(heap);
158#endif // ENABLE(GGC)
159}
160
161void CodeBlockSet::dump(PrintStream& out) const
162{
163    CommaPrinter comma;
164    out.print("{old = [");
165    for (CodeBlock* codeBlock : m_oldCodeBlocks)
166        out.print(comma, pointerDump(codeBlock));
167    out.print("], new = [");
168    comma = CommaPrinter();
169    for (CodeBlock* codeBlock : m_newCodeBlocks)
170        out.print(comma, pointerDump(codeBlock));
171    out.print("], currentlyExecuting = [");
172    comma = CommaPrinter();
173    for (CodeBlock* codeBlock : m_currentlyExecuting)
174        out.print(comma, pointerDump(codeBlock));
175    out.print("]}");
176}
177
178} // namespace JSC
179
180