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