2 * Copyright (C) 2008, 2014-2016 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "StructureStubInfo.h"
30 #include "PolymorphicAccess.h"
37 static const bool verbose = false;
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.
45 , numberOfCoolDowns(0)
46 , bufferingCountdown(Options::repatchBufferingCountdown())
49 , everConsidered(false)
53 StructureStubInfo::~StructureStubInfo()
57 void StructureStubInfo::initGetByIdSelf(CodeBlock* codeBlock, Structure* baseObjectStructure, PropertyOffset offset)
59 cacheType = CacheType::GetByIdSelf;
61 u.byIdSelf.baseObjectStructure.set(
62 *codeBlock->vm(), codeBlock, baseObjectStructure);
63 u.byIdSelf.offset = offset;
66 void StructureStubInfo::initPutByIdReplace(CodeBlock* codeBlock, Structure* baseObjectStructure, PropertyOffset offset)
68 cacheType = CacheType::PutByIdReplace;
70 u.byIdSelf.baseObjectStructure.set(
71 *codeBlock->vm(), codeBlock, baseObjectStructure);
72 u.byIdSelf.offset = offset;
75 void StructureStubInfo::initStub(CodeBlock*, std::unique_ptr<PolymorphicAccess> stub)
77 cacheType = CacheType::Stub;
78 u.stub = stub.release();
81 void StructureStubInfo::deref()
87 case CacheType::Unset:
88 case CacheType::GetByIdSelf:
89 case CacheType::PutByIdReplace:
93 RELEASE_ASSERT_NOT_REACHED();
96 void StructureStubInfo::aboutToDie()
100 u.stub->aboutToDie();
102 case CacheType::Unset:
103 case CacheType::GetByIdSelf:
104 case CacheType::PutByIdReplace:
108 RELEASE_ASSERT_NOT_REACHED();
111 AccessGenerationResult StructureStubInfo::addAccessCase(
112 CodeBlock* codeBlock, const Identifier& ident, std::unique_ptr<AccessCase> accessCase)
114 VM& vm = *codeBlock->vm();
117 dataLog("Adding access case: ", accessCase, "\n");
120 return AccessGenerationResult::GaveUp;
122 AccessGenerationResult result;
124 if (cacheType == CacheType::Stub) {
125 result = u.stub->addCase(vm, codeBlock, *this, ident, WTFMove(accessCase));
128 dataLog("Had stub, result: ", result, "\n");
130 if (!result.buffered()) {
131 bufferedStructures.clear();
135 std::unique_ptr<PolymorphicAccess> access = std::make_unique<PolymorphicAccess>();
137 Vector<std::unique_ptr<AccessCase>> accessCases;
139 std::unique_ptr<AccessCase> previousCase =
140 AccessCase::fromStructureStubInfo(vm, codeBlock, *this);
142 accessCases.append(WTFMove(previousCase));
144 accessCases.append(WTFMove(accessCase));
146 result = access->addCases(vm, codeBlock, *this, ident, WTFMove(accessCases));
149 dataLog("Created stub, result: ", result, "\n");
151 if (!result.buffered()) {
152 bufferedStructures.clear();
156 initStub(codeBlock, WTFMove(access));
159 RELEASE_ASSERT(!result.generatedSomeCode());
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()) {
165 dataLog("Didn't buffer anything, bailing.\n");
166 bufferedStructures.clear();
170 // The buffering countdown tells us if we should be repatching now.
171 if (bufferingCountdown) {
173 dataLog("Countdown is too high: ", bufferingCountdown, ".\n");
177 // Forget the buffered structures so that all future attempts to cache get fully handled by the
178 // PolymorphicAccess.
179 bufferedStructures.clear();
181 result = u.stub->regenerate(vm, codeBlock, *this, ident);
184 dataLog("Regeneration result: ", result, "\n");
186 RELEASE_ASSERT(!result.buffered());
188 if (!result.generatedSomeCode())
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();
197 void StructureStubInfo::reset(CodeBlock* codeBlock)
199 bufferedStructures.clear();
201 if (cacheType == CacheType::Unset)
204 if (Options::verboseOSR()) {
205 // This can be called from GC destructor calls, so we don't try to do a full dump
207 dataLog("Clearing structure cache (kind ", static_cast<int>(accessType), ") in ", RawPointer(codeBlock), ".\n");
210 switch (accessType) {
211 case AccessType::GetPure:
212 resetGetByID(codeBlock, *this, GetByIDKind::Pure);
214 case AccessType::Get:
215 resetGetByID(codeBlock, *this, GetByIDKind::Normal);
217 case AccessType::Put:
218 resetPutByID(codeBlock, *this);
221 resetIn(codeBlock, *this);
226 cacheType = CacheType::Unset;
229 void StructureStubInfo::visitWeakReferences(CodeBlock* codeBlock)
231 VM& vm = *codeBlock->vm();
233 bufferedStructures.genericFilter(
234 [&] (Structure* structure) -> bool {
235 return Heap::isMarked(structure);
239 case CacheType::GetByIdSelf:
240 case CacheType::PutByIdReplace:
241 if (Heap::isMarked(u.byIdSelf.baseObjectStructure.get()))
244 case CacheType::Stub:
245 if (u.stub->visitWeak(vm))
256 bool StructureStubInfo::propagateTransitions(SlotVisitor& visitor)
259 case CacheType::Unset:
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);
268 RELEASE_ASSERT_NOT_REACHED();
272 bool StructureStubInfo::containsPC(void* pc) const
274 if (cacheType != CacheType::Stub)
276 return u.stub->containsPC(pc);