JSC should have InstanceOf inline caching
[WebKit-https.git] / Source / JavaScriptCore / bytecode / StructureStubInfo.cpp
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
34 namespace JSC {
35
36 #if ENABLE(JIT)
37
38 namespace StructureStubInfoInternal {
39 static const bool verbose = false;
40 }
41
42 StructureStubInfo::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 {
55 }
56
57 StructureStubInfo::~StructureStubInfo()
58 {
59 }
60
61 void StructureStubInfo::initGetByIdSelf(CodeBlock* codeBlock, Structure* baseObjectStructure, PropertyOffset offset)
62 {
63     cacheType = CacheType::GetByIdSelf;
64     
65     u.byIdSelf.baseObjectStructure.set(
66         *codeBlock->vm(), codeBlock, baseObjectStructure);
67     u.byIdSelf.offset = offset;
68 }
69
70 void StructureStubInfo::initArrayLength()
71 {
72     cacheType = CacheType::ArrayLength;
73 }
74
75 void StructureStubInfo::initPutByIdReplace(CodeBlock* codeBlock, Structure* baseObjectStructure, PropertyOffset offset)
76 {
77     cacheType = CacheType::PutByIdReplace;
78     
79     u.byIdSelf.baseObjectStructure.set(
80         *codeBlock->vm(), codeBlock, baseObjectStructure);
81     u.byIdSelf.offset = offset;
82 }
83
84 void StructureStubInfo::deref()
85 {
86     switch (cacheType) {
87     case CacheType::Stub:
88         delete u.stub;
89         return;
90     case CacheType::Unset:
91     case CacheType::GetByIdSelf:
92     case CacheType::PutByIdReplace:
93     case CacheType::ArrayLength:
94         return;
95     }
96
97     RELEASE_ASSERT_NOT_REACHED();
98 }
99
100 void StructureStubInfo::aboutToDie()
101 {
102     switch (cacheType) {
103     case CacheType::Stub:
104         u.stub->aboutToDie();
105         return;
106     case CacheType::Unset:
107     case CacheType::GetByIdSelf:
108     case CacheType::PutByIdReplace:
109     case CacheType::ArrayLength:
110         return;
111     }
112
113     RELEASE_ASSERT_NOT_REACHED();
114 }
115
116 AccessGenerationResult StructureStubInfo::addAccessCase(
117     const GCSafeConcurrentJSLocker& locker, CodeBlock* codeBlock, const Identifier& ident, std::unique_ptr<AccessCase> accessCase)
118 {
119     VM& vm = *codeBlock->vm();
120     
121     if (StructureStubInfoInternal::verbose)
122         dataLog("Adding access case: ", accessCase, "\n");
123     
124     if (!accessCase)
125         return AccessGenerationResult::GaveUp;
126     
127     AccessGenerationResult result;
128     
129     if (cacheType == CacheType::Stub) {
130         result = u.stub->addCase(locker, vm, codeBlock, *this, ident, WTFMove(accessCase));
131         
132         if (StructureStubInfoInternal::verbose)
133             dataLog("Had stub, result: ", result, "\n");
134
135         if (result.shouldResetStubAndFireWatchpoints())
136             return result;
137
138         if (!result.buffered()) {
139             bufferedStructures.clear();
140             return result;
141         }
142     } else {
143         std::unique_ptr<PolymorphicAccess> access = std::make_unique<PolymorphicAccess>();
144         
145         Vector<std::unique_ptr<AccessCase>, 2> accessCases;
146         
147         std::unique_ptr<AccessCase> previousCase =
148             AccessCase::fromStructureStubInfo(vm, codeBlock, *this);
149         if (previousCase)
150             accessCases.append(WTFMove(previousCase));
151         
152         accessCases.append(WTFMove(accessCase));
153         
154         result = access->addCases(locker, vm, codeBlock, *this, ident, WTFMove(accessCases));
155         
156         if (StructureStubInfoInternal::verbose)
157             dataLog("Created stub, result: ", result, "\n");
158
159         if (result.shouldResetStubAndFireWatchpoints())
160             return result;
161
162         if (!result.buffered()) {
163             bufferedStructures.clear();
164             return result;
165         }
166         
167         cacheType = CacheType::Stub;
168         u.stub = access.release();
169     }
170     
171     RELEASE_ASSERT(!result.generatedSomeCode());
172     
173     // If we didn't buffer any cases then bail. If this made no changes then we'll just try again
174     // subject to cool-down.
175     if (!result.buffered()) {
176         if (StructureStubInfoInternal::verbose)
177             dataLog("Didn't buffer anything, bailing.\n");
178         bufferedStructures.clear();
179         return result;
180     }
181     
182     // The buffering countdown tells us if we should be repatching now.
183     if (bufferingCountdown) {
184         if (StructureStubInfoInternal::verbose)
185             dataLog("Countdown is too high: ", bufferingCountdown, ".\n");
186         return result;
187     }
188     
189     // Forget the buffered structures so that all future attempts to cache get fully handled by the
190     // PolymorphicAccess.
191     bufferedStructures.clear();
192     
193     result = u.stub->regenerate(locker, vm, codeBlock, *this, ident);
194     
195     if (StructureStubInfoInternal::verbose)
196         dataLog("Regeneration result: ", result, "\n");
197     
198     RELEASE_ASSERT(!result.buffered());
199     
200     if (!result.generatedSomeCode())
201         return result;
202     
203     // If we generated some code then we don't want to attempt to repatch in the future until we
204     // gather enough cases.
205     bufferingCountdown = Options::repatchBufferingCountdown();
206     return result;
207 }
208
209 void StructureStubInfo::reset(CodeBlock* codeBlock)
210 {
211     bufferedStructures.clear();
212
213     if (cacheType == CacheType::Unset)
214         return;
215
216     if (Options::verboseOSR()) {
217         // This can be called from GC destructor calls, so we don't try to do a full dump
218         // of the CodeBlock.
219         dataLog("Clearing structure cache (kind ", static_cast<int>(accessType), ") in ", RawPointer(codeBlock), ".\n");
220     }
221
222     switch (accessType) {
223     case AccessType::TryGet:
224         resetGetByID(codeBlock, *this, GetByIDKind::Try);
225         break;
226     case AccessType::Get:
227         resetGetByID(codeBlock, *this, GetByIDKind::Normal);
228         break;
229     case AccessType::GetWithThis:
230         resetGetByID(codeBlock, *this, GetByIDKind::WithThis);
231         break;
232     case AccessType::GetDirect:
233         resetGetByID(codeBlock, *this, GetByIDKind::Direct);
234         break;
235     case AccessType::Put:
236         resetPutByID(codeBlock, *this);
237         break;
238     case AccessType::In:
239         resetIn(*this);
240         break;
241     case AccessType::InstanceOf:
242         resetInstanceOf(*this);
243         break;
244     }
245     
246     deref();
247     cacheType = CacheType::Unset;
248 }
249
250 void StructureStubInfo::visitWeakReferences(CodeBlock* codeBlock)
251 {
252     VM& vm = *codeBlock->vm();
253     
254     bufferedStructures.genericFilter(
255         [&] (Structure* structure) -> bool {
256             return Heap::isMarked(structure);
257         });
258
259     switch (cacheType) {
260     case CacheType::GetByIdSelf:
261     case CacheType::PutByIdReplace:
262         if (Heap::isMarked(u.byIdSelf.baseObjectStructure.get()))
263             return;
264         break;
265     case CacheType::Stub:
266         if (u.stub->visitWeak(vm))
267             return;
268         break;
269     default:
270         return;
271     }
272
273     reset(codeBlock);
274     resetByGC = true;
275 }
276
277 bool StructureStubInfo::propagateTransitions(SlotVisitor& visitor)
278 {
279     switch (cacheType) {
280     case CacheType::Unset:
281     case CacheType::ArrayLength:
282         return true;
283     case CacheType::GetByIdSelf:
284     case CacheType::PutByIdReplace:
285         return u.byIdSelf.baseObjectStructure->markIfCheap(visitor);
286     case CacheType::Stub:
287         return u.stub->propagateTransitions(visitor);
288     }
289     
290     RELEASE_ASSERT_NOT_REACHED();
291     return true;
292 }
293
294 bool StructureStubInfo::containsPC(void* pc) const
295 {
296     if (cacheType != CacheType::Stub)
297         return false;
298     return u.stub->containsPC(pc);
299 }
300
301 #endif // ENABLE(JIT)
302
303 } // namespace JSC