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 | , sawNonCell(false) |
55 | { |
56 | } |
57 | |
58 | StructureStubInfo::~StructureStubInfo() |
59 | { |
60 | } |
61 | |
62 | void 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 | |
71 | void StructureStubInfo::initArrayLength() |
72 | { |
73 | cacheType = CacheType::ArrayLength; |
74 | } |
75 | |
76 | void StructureStubInfo::initStringLength() |
77 | { |
78 | cacheType = CacheType::StringLength; |
79 | } |
80 | |
81 | void 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 | |
90 | void 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 | |
99 | void 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 | |
117 | void 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 | |
135 | AccessGenerationResult 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 | |
228 | void 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 | |
269 | void 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 | |
297 | bool 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 | |
316 | StubInfoSummary 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 | |
341 | StubInfoSummary StructureStubInfo::summary(const StructureStubInfo* stubInfo) |
342 | { |
343 | if (!stubInfo) |
344 | return StubInfoSummary::NoInformation; |
345 | |
346 | return stubInfo->summary(); |
347 | } |
348 | |
349 | bool 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 | |