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 | |
38 | namespace JSC { |
39 | |
40 | bool InByIdStatus::appendVariant(const InByIdVariant& variant) |
41 | { |
42 | return appendICStatusVariant(m_variants, variant); |
43 | } |
44 | |
45 | #if ENABLE(JIT) |
46 | InByIdStatus 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 | |
67 | InByIdStatus InByIdStatus::computeFor(CodeBlock* profiledBlock, ICStatusMap& map, BytecodeIndex bytecodeIndex, UniquedStringImpl* uid) |
68 | { |
69 | return computeFor(profiledBlock, map, bytecodeIndex, uid, hasBadCacheExitSite(profiledBlock, bytecodeIndex)); |
70 | } |
71 | |
72 | InByIdStatus 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) |
115 | InByIdStatus 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 | |
124 | InByIdStatus 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 | |
216 | void 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 | |
246 | void 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 | |
255 | void InByIdStatus::markIfCheap(SlotVisitor& visitor) |
256 | { |
257 | for (InByIdVariant& variant : m_variants) |
258 | variant.markIfCheap(visitor); |
259 | } |
260 | |
261 | bool 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 | |
270 | void 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 | |