1/*
2 * Copyright (C) 2018 Yusuke Suzuki <[email protected]>.
3 * Copyright (C) 2018 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "InByIdStatus.h"
29
30#include "CodeBlock.h"
31#include "ComplexGetStatus.h"
32#include "ICStatusUtils.h"
33#include "JSCInlines.h"
34#include "PolymorphicAccess.h"
35#include "StructureStubInfo.h"
36#include <wtf/ListDump.h>
37
38namespace JSC {
39
40bool InByIdStatus::appendVariant(const InByIdVariant& variant)
41{
42 return appendICStatusVariant(m_variants, variant);
43}
44
45#if ENABLE(JIT)
46InByIdStatus InByIdStatus::computeFor(CodeBlock* profiledBlock, ICStatusMap& map, BytecodeIndex bytecodeIndex, UniquedStringImpl* uid, ExitFlag didExit)
47{
48 ConcurrentJSLocker locker(profiledBlock->m_lock);
49
50 InByIdStatus result;
51
52#if ENABLE(DFG_JIT)
53 result = computeForStubInfoWithoutExitSiteFeedback(locker, map.get(CodeOrigin(bytecodeIndex)).stubInfo, uid);
54
55 if (!result.takesSlowPath() && didExit)
56 return InByIdStatus(TakesSlowPath);
57#else
58 UNUSED_PARAM(map);
59 UNUSED_PARAM(bytecodeIndex);
60 UNUSED_PARAM(uid);
61 UNUSED_PARAM(didExit);
62#endif
63
64 return result;
65}
66
67InByIdStatus InByIdStatus::computeFor(CodeBlock* profiledBlock, ICStatusMap& map, BytecodeIndex bytecodeIndex, UniquedStringImpl* uid)
68{
69 return computeFor(profiledBlock, map, bytecodeIndex, uid, hasBadCacheExitSite(profiledBlock, bytecodeIndex));
70}
71
72InByIdStatus InByIdStatus::computeFor(
73 CodeBlock* profiledBlock, ICStatusMap& baselineMap,
74 ICStatusContextStack& contextStack, CodeOrigin codeOrigin, UniquedStringImpl* uid)
75{
76 BytecodeIndex bytecodeIndex = codeOrigin.bytecodeIndex();
77 ExitFlag didExit = hasBadCacheExitSite(profiledBlock, bytecodeIndex);
78
79 for (ICStatusContext* context : contextStack) {
80 ICStatus status = context->get(codeOrigin);
81
82 auto bless = [&] (const InByIdStatus& result) -> InByIdStatus {
83 if (!context->isInlined(codeOrigin)) {
84 InByIdStatus baselineResult = computeFor(
85 profiledBlock, baselineMap, bytecodeIndex, uid, didExit);
86 baselineResult.merge(result);
87 return baselineResult;
88 }
89 if (didExit.isSet(ExitFromInlined))
90 return InByIdStatus(TakesSlowPath);
91 return result;
92 };
93
94#if ENABLE(DFG_JIT)
95 if (status.stubInfo) {
96 InByIdStatus result;
97 {
98 ConcurrentJSLocker locker(context->optimizedCodeBlock->m_lock);
99 result = computeForStubInfoWithoutExitSiteFeedback(locker, status.stubInfo, uid);
100 }
101 if (result.isSet())
102 return bless(result);
103 }
104#endif
105
106 if (status.inStatus)
107 return bless(*status.inStatus);
108 }
109
110 return computeFor(profiledBlock, baselineMap, bytecodeIndex, uid, didExit);
111}
112#endif // ENABLE(JIT)
113
114#if ENABLE(DFG_JIT)
115InByIdStatus InByIdStatus::computeForStubInfo(const ConcurrentJSLocker& locker, CodeBlock* profiledBlock, StructureStubInfo* stubInfo, CodeOrigin codeOrigin, UniquedStringImpl* uid)
116{
117 InByIdStatus result = InByIdStatus::computeForStubInfoWithoutExitSiteFeedback(locker, stubInfo, uid);
118
119 if (!result.takesSlowPath() && hasBadCacheExitSite(profiledBlock, codeOrigin.bytecodeIndex()))
120 return InByIdStatus(TakesSlowPath);
121 return result;
122}
123
124InByIdStatus InByIdStatus::computeForStubInfoWithoutExitSiteFeedback(const ConcurrentJSLocker&, StructureStubInfo* stubInfo, UniquedStringImpl* uid)
125{
126 StubInfoSummary summary = StructureStubInfo::summary(stubInfo);
127 if (!isInlineable(summary))
128 return InByIdStatus(summary);
129
130 // Finally figure out if we can derive an access strategy.
131 InByIdStatus result;
132 result.m_state = Simple;
133 switch (stubInfo->cacheType()) {
134 case CacheType::Unset:
135 return InByIdStatus(NoInformation);
136
137 case CacheType::InByIdSelf: {
138 Structure* structure = stubInfo->u.byIdSelf.baseObjectStructure.get();
139 if (structure->takesSlowPathInDFGForImpureProperty())
140 return InByIdStatus(TakesSlowPath);
141 unsigned attributes;
142 InByIdVariant variant;
143 variant.m_offset = structure->getConcurrently(uid, attributes);
144 if (!isValidOffset(variant.m_offset))
145 return InByIdStatus(TakesSlowPath);
146 if (attributes & PropertyAttribute::CustomAccessorOrValue)
147 return InByIdStatus(TakesSlowPath);
148
149 variant.m_structureSet.add(structure);
150 bool didAppend = result.appendVariant(variant);
151 ASSERT_UNUSED(didAppend, didAppend);
152 return result;
153 }
154
155 case CacheType::Stub: {
156 PolymorphicAccess* list = stubInfo->u.stub;
157 for (unsigned listIndex = 0; listIndex < list->size(); ++listIndex) {
158 const AccessCase& access = list->at(listIndex);
159 if (access.viaProxy())
160 return InByIdStatus(TakesSlowPath);
161
162 if (access.usesPolyProto())
163 return InByIdStatus(TakesSlowPath);
164
165 Structure* structure = access.structure();
166 if (!structure) {
167 // The null structure cases arise due to array.length. We have no way of creating a
168 // InByIdVariant for those, and we don't really have to since the DFG handles those
169 // cases in FixupPhase using value profiling. That's a bit awkward - we shouldn't
170 // have to use value profiling to discover something that the AccessCase could have
171 // told us. But, it works well enough. So, our only concern here is to not
172 // crash on null structure.
173 return InByIdStatus(TakesSlowPath);
174 }
175
176 ComplexGetStatus complexGetStatus = ComplexGetStatus::computeFor(structure, access.conditionSet(), uid);
177 switch (complexGetStatus.kind()) {
178 case ComplexGetStatus::ShouldSkip:
179 continue;
180
181 case ComplexGetStatus::TakesSlowPath:
182 return InByIdStatus(TakesSlowPath);
183
184 case ComplexGetStatus::Inlineable: {
185 switch (access.type()) {
186 case AccessCase::InHit:
187 case AccessCase::InMiss:
188 break;
189 default:
190 return InByIdStatus(TakesSlowPath);
191 }
192
193 InByIdVariant variant(
194 StructureSet(structure), complexGetStatus.offset(),
195 complexGetStatus.conditionSet());
196
197 if (!result.appendVariant(variant))
198 return InByIdStatus(TakesSlowPath);
199 break;
200 }
201 }
202 }
203
204 return result;
205 }
206
207 default:
208 return InByIdStatus(TakesSlowPath);
209 }
210
211 RELEASE_ASSERT_NOT_REACHED();
212 return InByIdStatus();
213}
214#endif
215
216void InByIdStatus::merge(const InByIdStatus& other)
217{
218 if (other.m_state == NoInformation)
219 return;
220
221 switch (m_state) {
222 case NoInformation:
223 *this = other;
224 return;
225
226 case Simple:
227 if (other.m_state != Simple) {
228 *this = InByIdStatus(TakesSlowPath);
229 return;
230 }
231 for (const InByIdVariant& otherVariant : other.m_variants) {
232 if (!appendVariant(otherVariant)) {
233 *this = InByIdStatus(TakesSlowPath);
234 return;
235 }
236 }
237 return;
238
239 case TakesSlowPath:
240 return;
241 }
242
243 RELEASE_ASSERT_NOT_REACHED();
244}
245
246void InByIdStatus::filter(const StructureSet& structureSet)
247{
248 if (m_state != Simple)
249 return;
250 filterICStatusVariants(m_variants, structureSet);
251 if (m_variants.isEmpty())
252 m_state = NoInformation;
253}
254
255void InByIdStatus::markIfCheap(SlotVisitor& visitor)
256{
257 for (InByIdVariant& variant : m_variants)
258 variant.markIfCheap(visitor);
259}
260
261bool InByIdStatus::finalize(VM& vm)
262{
263 for (InByIdVariant& variant : m_variants) {
264 if (!variant.finalize(vm))
265 return false;
266 }
267 return true;
268}
269
270void InByIdStatus::dump(PrintStream& out) const
271{
272 out.print("(");
273 switch (m_state) {
274 case NoInformation:
275 out.print("NoInformation");
276 break;
277 case Simple:
278 out.print("Simple");
279 break;
280 case TakesSlowPath:
281 out.print("TakesSlowPath");
282 break;
283 }
284 out.print(", ", listDump(m_variants), ")");
285}
286
287} // namespace JSC
288
289