1/*
2 * Copyright (C) 2013-2018 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 "DFGCommonData.h"
28
29#if ENABLE(DFG_JIT)
30
31#include "CodeBlock.h"
32#include "DFGNode.h"
33#include "DFGPlan.h"
34#include "InlineCallFrame.h"
35#include "JSCInlines.h"
36#include "TrackedReferences.h"
37#include "VM.h"
38
39#include <wtf/NeverDestroyed.h>
40
41namespace JSC { namespace DFG {
42
43void CommonData::notifyCompilingStructureTransition(Plan& plan, CodeBlock* codeBlock, Node* node)
44{
45 plan.transitions().addLazily(
46 codeBlock,
47 node->origin.semantic.codeOriginOwner(),
48 node->transition()->previous.get(),
49 node->transition()->next.get());
50}
51
52CallSiteIndex CommonData::addCodeOrigin(CodeOrigin codeOrigin)
53{
54 if (codeOrigins.isEmpty()
55 || codeOrigins.last() != codeOrigin)
56 codeOrigins.append(codeOrigin);
57 unsigned index = codeOrigins.size() - 1;
58 ASSERT(codeOrigins[index] == codeOrigin);
59 return CallSiteIndex(index);
60}
61
62CallSiteIndex CommonData::addUniqueCallSiteIndex(CodeOrigin codeOrigin)
63{
64 if (callSiteIndexFreeList.size())
65 return CallSiteIndex(callSiteIndexFreeList.takeAny());
66
67 codeOrigins.append(codeOrigin);
68 unsigned index = codeOrigins.size() - 1;
69 ASSERT(codeOrigins[index] == codeOrigin);
70 return CallSiteIndex(index);
71}
72
73CallSiteIndex CommonData::lastCallSite() const
74{
75 RELEASE_ASSERT(codeOrigins.size());
76 return CallSiteIndex(codeOrigins.size() - 1);
77}
78
79void CommonData::removeCallSiteIndex(CallSiteIndex callSite)
80{
81 RELEASE_ASSERT(callSite.bits() < codeOrigins.size());
82 callSiteIndexFreeList.add(callSite.bits());
83}
84
85void CommonData::shrinkToFit()
86{
87 codeOrigins.shrinkToFit();
88 dfgIdentifiers.shrinkToFit();
89 weakReferences.shrinkToFit();
90 weakStructureReferences.shrinkToFit();
91 transitions.shrinkToFit();
92 catchEntrypoints.shrinkToFit();
93 jumpReplacements.shrinkToFit();
94}
95
96static Lock pcCodeBlockMapLock;
97inline HashMap<void*, CodeBlock*>& pcCodeBlockMap(AbstractLocker&)
98{
99 static NeverDestroyed<HashMap<void*, CodeBlock*>> pcCodeBlockMap;
100 return pcCodeBlockMap;
101}
102
103bool CommonData::invalidate()
104{
105 if (!isStillValid)
106 return false;
107
108 if (UNLIKELY(hasVMTrapsBreakpointsInstalled)) {
109 LockHolder locker(pcCodeBlockMapLock);
110 auto& map = pcCodeBlockMap(locker);
111 for (auto& jumpReplacement : jumpReplacements)
112 map.remove(jumpReplacement.dataLocation());
113 hasVMTrapsBreakpointsInstalled = false;
114 }
115
116 for (unsigned i = jumpReplacements.size(); i--;)
117 jumpReplacements[i].fire();
118 isStillValid = false;
119 return true;
120}
121
122CommonData::~CommonData()
123{
124 if (UNLIKELY(hasVMTrapsBreakpointsInstalled)) {
125 LockHolder locker(pcCodeBlockMapLock);
126 auto& map = pcCodeBlockMap(locker);
127 for (auto& jumpReplacement : jumpReplacements)
128 map.remove(jumpReplacement.dataLocation());
129 }
130}
131
132void CommonData::installVMTrapBreakpoints(CodeBlock* owner)
133{
134 LockHolder locker(pcCodeBlockMapLock);
135 if (!isStillValid || hasVMTrapsBreakpointsInstalled)
136 return;
137 hasVMTrapsBreakpointsInstalled = true;
138
139 auto& map = pcCodeBlockMap(locker);
140#if !defined(NDEBUG)
141 // We need to be able to handle more than one invalidation point at the same pc
142 // but we want to make sure we don't forget to remove a pc from the map.
143 HashSet<void*> newReplacements;
144#endif
145 for (auto& jumpReplacement : jumpReplacements) {
146 jumpReplacement.installVMTrapBreakpoint();
147 void* source = jumpReplacement.dataLocation();
148 auto result = map.add(source, owner);
149 UNUSED_PARAM(result);
150#if !defined(NDEBUG)
151 ASSERT(result.isNewEntry || newReplacements.contains(source));
152 newReplacements.add(source);
153#endif
154 }
155}
156
157CodeBlock* codeBlockForVMTrapPC(void* pc)
158{
159 ASSERT(isJITPC(pc));
160 LockHolder locker(pcCodeBlockMapLock);
161 auto& map = pcCodeBlockMap(locker);
162 auto result = map.find(pc);
163 if (result == map.end())
164 return nullptr;
165 return result->value;
166}
167
168bool CommonData::isVMTrapBreakpoint(void* address)
169{
170 if (!isStillValid)
171 return false;
172 for (unsigned i = jumpReplacements.size(); i--;) {
173 if (address == jumpReplacements[i].dataLocation())
174 return true;
175 }
176 return false;
177}
178
179void CommonData::validateReferences(const TrackedReferences& trackedReferences)
180{
181 if (InlineCallFrameSet* set = inlineCallFrames.get()) {
182 for (InlineCallFrame* inlineCallFrame : *set) {
183 for (ValueRecovery& recovery : inlineCallFrame->argumentsWithFixup) {
184 if (recovery.isConstant())
185 trackedReferences.check(recovery.constant());
186 }
187
188 if (CodeBlock* baselineCodeBlock = inlineCallFrame->baselineCodeBlock.get())
189 trackedReferences.check(baselineCodeBlock);
190
191 if (inlineCallFrame->calleeRecovery.isConstant())
192 trackedReferences.check(inlineCallFrame->calleeRecovery.constant());
193 }
194 }
195
196 for (AdaptiveStructureWatchpoint* watchpoint : adaptiveStructureWatchpoints)
197 watchpoint->key().validateReferences(trackedReferences);
198}
199
200void CommonData::finalizeCatchEntrypoints()
201{
202 std::sort(catchEntrypoints.begin(), catchEntrypoints.end(),
203 [] (const CatchEntrypointData& a, const CatchEntrypointData& b) { return a.bytecodeIndex < b.bytecodeIndex; });
204
205#if !ASSERT_DISABLED
206 for (unsigned i = 0; i + 1 < catchEntrypoints.size(); ++i)
207 ASSERT(catchEntrypoints[i].bytecodeIndex <= catchEntrypoints[i + 1].bytecodeIndex);
208#endif
209}
210
211void CommonData::clearWatchpoints()
212{
213 watchpoints.clear();
214 adaptiveStructureWatchpoints.clear();
215 adaptiveInferredPropertyValueWatchpoints.clear();
216}
217
218} } // namespace JSC::DFG
219
220#endif // ENABLE(DFG_JIT)
221
222