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