1/*
2 * Copyright (C) 2008, 2014-2016 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 "StructureStubInfo.h"
28
29#include "JSObject.h"
30#include "JSCInlines.h"
31#include "PolymorphicAccess.h"
32#include "Repatch.h"
33
34namespace JSC {
35
36#if ENABLE(JIT)
37
38namespace StructureStubInfoInternal {
39static const bool verbose = false;
40}
41
42StructureStubInfo::StructureStubInfo(AccessType accessType)
43 : callSiteIndex(UINT_MAX)
44 , accessType(accessType)
45 , cacheType(CacheType::Unset)
46 , countdown(1) // For a totally clear stub, we'll patch it after the first execution.
47 , repatchCount(0)
48 , numberOfCoolDowns(0)
49 , bufferingCountdown(Options::repatchBufferingCountdown())
50 , resetByGC(false)
51 , tookSlowPath(false)
52 , everConsidered(false)
53 , prototypeIsKnownObject(false)
54 , sawNonCell(false)
55{
56}
57
58StructureStubInfo::~StructureStubInfo()
59{
60}
61
62void StructureStubInfo::initGetByIdSelf(CodeBlock* codeBlock, Structure* baseObjectStructure, PropertyOffset offset)
63{
64 cacheType = CacheType::GetByIdSelf;
65
66 u.byIdSelf.baseObjectStructure.set(
67 *codeBlock->vm(), codeBlock, baseObjectStructure);
68 u.byIdSelf.offset = offset;
69}
70
71void StructureStubInfo::initArrayLength()
72{
73 cacheType = CacheType::ArrayLength;
74}
75
76void StructureStubInfo::initStringLength()
77{
78 cacheType = CacheType::StringLength;
79}
80
81void StructureStubInfo::initPutByIdReplace(CodeBlock* codeBlock, Structure* baseObjectStructure, PropertyOffset offset)
82{
83 cacheType = CacheType::PutByIdReplace;
84
85 u.byIdSelf.baseObjectStructure.set(
86 *codeBlock->vm(), codeBlock, baseObjectStructure);
87 u.byIdSelf.offset = offset;
88}
89
90void StructureStubInfo::initInByIdSelf(CodeBlock* codeBlock, Structure* baseObjectStructure, PropertyOffset offset)
91{
92 cacheType = CacheType::InByIdSelf;
93
94 u.byIdSelf.baseObjectStructure.set(
95 *codeBlock->vm(), codeBlock, baseObjectStructure);
96 u.byIdSelf.offset = offset;
97}
98
99void StructureStubInfo::deref()
100{
101 switch (cacheType) {
102 case CacheType::Stub:
103 delete u.stub;
104 return;
105 case CacheType::Unset:
106 case CacheType::GetByIdSelf:
107 case CacheType::PutByIdReplace:
108 case CacheType::InByIdSelf:
109 case CacheType::ArrayLength:
110 case CacheType::StringLength:
111 return;
112 }
113
114 RELEASE_ASSERT_NOT_REACHED();
115}
116
117void StructureStubInfo::aboutToDie()
118{
119 switch (cacheType) {
120 case CacheType::Stub:
121 u.stub->aboutToDie();
122 return;
123 case CacheType::Unset:
124 case CacheType::GetByIdSelf:
125 case CacheType::PutByIdReplace:
126 case CacheType::InByIdSelf:
127 case CacheType::ArrayLength:
128 case CacheType::StringLength:
129 return;
130 }
131
132 RELEASE_ASSERT_NOT_REACHED();
133}
134
135AccessGenerationResult StructureStubInfo::addAccessCase(
136 const GCSafeConcurrentJSLocker& locker, CodeBlock* codeBlock, const Identifier& ident, std::unique_ptr<AccessCase> accessCase)
137{
138 VM& vm = *codeBlock->vm();
139
140 if (StructureStubInfoInternal::verbose)
141 dataLog("Adding access case: ", accessCase, "\n");
142
143 if (!accessCase)
144 return AccessGenerationResult::GaveUp;
145
146 AccessGenerationResult result;
147
148 if (cacheType == CacheType::Stub) {
149 result = u.stub->addCase(locker, vm, codeBlock, *this, ident, WTFMove(accessCase));
150
151 if (StructureStubInfoInternal::verbose)
152 dataLog("Had stub, result: ", result, "\n");
153
154 if (result.shouldResetStubAndFireWatchpoints())
155 return result;
156
157 if (!result.buffered()) {
158 bufferedStructures.clear();
159 return result;
160 }
161 } else {
162 std::unique_ptr<PolymorphicAccess> access = std::make_unique<PolymorphicAccess>();
163
164 Vector<std::unique_ptr<AccessCase>, 2> accessCases;
165
166 std::unique_ptr<AccessCase> previousCase =
167 AccessCase::fromStructureStubInfo(vm, codeBlock, *this);
168 if (previousCase)
169 accessCases.append(WTFMove(previousCase));
170
171 accessCases.append(WTFMove(accessCase));
172
173 result = access->addCases(locker, vm, codeBlock, *this, ident, WTFMove(accessCases));
174
175 if (StructureStubInfoInternal::verbose)
176 dataLog("Created stub, result: ", result, "\n");
177
178 if (result.shouldResetStubAndFireWatchpoints())
179 return result;
180
181 if (!result.buffered()) {
182 bufferedStructures.clear();
183 return result;
184 }
185
186 cacheType = CacheType::Stub;
187 u.stub = access.release();
188 }
189
190 RELEASE_ASSERT(!result.generatedSomeCode());
191
192 // If we didn't buffer any cases then bail. If this made no changes then we'll just try again
193 // subject to cool-down.
194 if (!result.buffered()) {
195 if (StructureStubInfoInternal::verbose)
196 dataLog("Didn't buffer anything, bailing.\n");
197 bufferedStructures.clear();
198 return result;
199 }
200
201 // The buffering countdown tells us if we should be repatching now.
202 if (bufferingCountdown) {
203 if (StructureStubInfoInternal::verbose)
204 dataLog("Countdown is too high: ", bufferingCountdown, ".\n");
205 return result;
206 }
207
208 // Forget the buffered structures so that all future attempts to cache get fully handled by the
209 // PolymorphicAccess.
210 bufferedStructures.clear();
211
212 result = u.stub->regenerate(locker, vm, codeBlock, *this, ident);
213
214 if (StructureStubInfoInternal::verbose)
215 dataLog("Regeneration result: ", result, "\n");
216
217 RELEASE_ASSERT(!result.buffered());
218
219 if (!result.generatedSomeCode())
220 return result;
221
222 // If we generated some code then we don't want to attempt to repatch in the future until we
223 // gather enough cases.
224 bufferingCountdown = Options::repatchBufferingCountdown();
225 return result;
226}
227
228void StructureStubInfo::reset(CodeBlock* codeBlock)
229{
230 bufferedStructures.clear();
231
232 if (cacheType == CacheType::Unset)
233 return;
234
235 if (Options::verboseOSR()) {
236 // This can be called from GC destructor calls, so we don't try to do a full dump
237 // of the CodeBlock.
238 dataLog("Clearing structure cache (kind ", static_cast<int>(accessType), ") in ", RawPointer(codeBlock), ".\n");
239 }
240
241 switch (accessType) {
242 case AccessType::TryGet:
243 resetGetByID(codeBlock, *this, GetByIDKind::Try);
244 break;
245 case AccessType::Get:
246 resetGetByID(codeBlock, *this, GetByIDKind::Normal);
247 break;
248 case AccessType::GetWithThis:
249 resetGetByID(codeBlock, *this, GetByIDKind::WithThis);
250 break;
251 case AccessType::GetDirect:
252 resetGetByID(codeBlock, *this, GetByIDKind::Direct);
253 break;
254 case AccessType::Put:
255 resetPutByID(codeBlock, *this);
256 break;
257 case AccessType::In:
258 resetInByID(codeBlock, *this);
259 break;
260 case AccessType::InstanceOf:
261 resetInstanceOf(*this);
262 break;
263 }
264
265 deref();
266 cacheType = CacheType::Unset;
267}
268
269void StructureStubInfo::visitWeakReferences(CodeBlock* codeBlock)
270{
271 VM& vm = *codeBlock->vm();
272
273 bufferedStructures.genericFilter(
274 [&] (Structure* structure) -> bool {
275 return vm.heap.isMarked(structure);
276 });
277
278 switch (cacheType) {
279 case CacheType::GetByIdSelf:
280 case CacheType::PutByIdReplace:
281 case CacheType::InByIdSelf:
282 if (vm.heap.isMarked(u.byIdSelf.baseObjectStructure.get()))
283 return;
284 break;
285 case CacheType::Stub:
286 if (u.stub->visitWeak(vm))
287 return;
288 break;
289 default:
290 return;
291 }
292
293 reset(codeBlock);
294 resetByGC = true;
295}
296
297bool StructureStubInfo::propagateTransitions(SlotVisitor& visitor)
298{
299 switch (cacheType) {
300 case CacheType::Unset:
301 case CacheType::ArrayLength:
302 case CacheType::StringLength:
303 return true;
304 case CacheType::GetByIdSelf:
305 case CacheType::PutByIdReplace:
306 case CacheType::InByIdSelf:
307 return u.byIdSelf.baseObjectStructure->markIfCheap(visitor);
308 case CacheType::Stub:
309 return u.stub->propagateTransitions(visitor);
310 }
311
312 RELEASE_ASSERT_NOT_REACHED();
313 return true;
314}
315
316StubInfoSummary StructureStubInfo::summary() const
317{
318 StubInfoSummary takesSlowPath = StubInfoSummary::TakesSlowPath;
319 StubInfoSummary simple = StubInfoSummary::Simple;
320 if (cacheType == CacheType::Stub) {
321 PolymorphicAccess* list = u.stub;
322 for (unsigned i = 0; i < list->size(); ++i) {
323 const AccessCase& access = list->at(i);
324 if (access.doesCalls()) {
325 takesSlowPath = StubInfoSummary::TakesSlowPathAndMakesCalls;
326 simple = StubInfoSummary::MakesCalls;
327 break;
328 }
329 }
330 }
331
332 if (tookSlowPath || sawNonCell)
333 return takesSlowPath;
334
335 if (!everConsidered)
336 return StubInfoSummary::NoInformation;
337
338 return simple;
339}
340
341StubInfoSummary StructureStubInfo::summary(const StructureStubInfo* stubInfo)
342{
343 if (!stubInfo)
344 return StubInfoSummary::NoInformation;
345
346 return stubInfo->summary();
347}
348
349bool StructureStubInfo::containsPC(void* pc) const
350{
351 if (cacheType != CacheType::Stub)
352 return false;
353 return u.stub->containsPC(pc);
354}
355
356#endif // ENABLE(JIT)
357
358} // namespace JSC
359