We should support CreateThis in the FTL
[WebKit-https.git] / Source / JavaScriptCore / bytecode / InByIdStatus.cpp
1 /*
2  * Copyright (C) 2018 Yusuke Suzuki <utatane.tea@gmail.com>.
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 InByIdStatus InByIdStatus::computeFor(CodeBlock* profiledBlock, ICStatusMap& map, unsigned bytecodeIndex, UniquedStringImpl* uid, ExitFlag didExit)
46 {
47     ConcurrentJSLocker locker(profiledBlock->m_lock);
48
49     InByIdStatus result;
50
51 #if ENABLE(DFG_JIT)
52     result = computeForStubInfoWithoutExitSiteFeedback(locker, map.get(CodeOrigin(bytecodeIndex)).stubInfo, uid);
53
54     if (!result.takesSlowPath() && didExit)
55         return InByIdStatus(TakesSlowPath);
56 #else
57     UNUSED_PARAM(map);
58     UNUSED_PARAM(bytecodeIndex);
59     UNUSED_PARAM(uid);
60 #endif
61
62     return result;
63 }
64
65 InByIdStatus InByIdStatus::computeFor(CodeBlock* profiledBlock, ICStatusMap& map, unsigned bytecodeIndex, UniquedStringImpl* uid)
66 {
67     return computeFor(profiledBlock, map, bytecodeIndex, uid, hasBadCacheExitSite(profiledBlock, bytecodeIndex));
68 }
69
70 InByIdStatus InByIdStatus::computeFor(
71     CodeBlock* profiledBlock, ICStatusMap& baselineMap,
72     ICStatusContextStack& contextStack, CodeOrigin codeOrigin, UniquedStringImpl* uid)
73 {
74     ExitFlag didExit = hasBadCacheExitSite(profiledBlock, codeOrigin.bytecodeIndex);
75     
76     for (ICStatusContext* context : contextStack) {
77         ICStatus status = context->get(codeOrigin);
78         
79         auto bless = [&] (const InByIdStatus& result) -> InByIdStatus {
80             if (!context->isInlined(codeOrigin)) {
81                 InByIdStatus baselineResult = computeFor(
82                     profiledBlock, baselineMap, codeOrigin.bytecodeIndex, uid, didExit);
83                 baselineResult.merge(result);
84                 return baselineResult;
85             }
86             if (didExit.isSet(ExitFromInlined))
87                 return InByIdStatus(TakesSlowPath);
88             return result;
89         };
90         
91         if (status.stubInfo) {
92             InByIdStatus result;
93             {
94                 ConcurrentJSLocker locker(context->optimizedCodeBlock->m_lock);
95                 result = computeForStubInfoWithoutExitSiteFeedback(locker, status.stubInfo, uid);
96             }
97             if (result.isSet())
98                 return bless(result);
99         }
100         
101         if (status.inStatus)
102             return bless(*status.inStatus);
103     }
104     
105     return computeFor(profiledBlock, baselineMap, codeOrigin.bytecodeIndex, uid, didExit);
106 }
107
108 #if ENABLE(DFG_JIT)
109 InByIdStatus InByIdStatus::computeForStubInfo(const ConcurrentJSLocker& locker, CodeBlock* profiledBlock, StructureStubInfo* stubInfo, CodeOrigin codeOrigin, UniquedStringImpl* uid)
110 {
111     InByIdStatus result = InByIdStatus::computeForStubInfoWithoutExitSiteFeedback(locker, stubInfo, uid);
112
113     if (!result.takesSlowPath() && hasBadCacheExitSite(profiledBlock, codeOrigin.bytecodeIndex))
114         return InByIdStatus(TakesSlowPath);
115     return result;
116 }
117
118 InByIdStatus InByIdStatus::computeForStubInfoWithoutExitSiteFeedback(const ConcurrentJSLocker&, StructureStubInfo* stubInfo, UniquedStringImpl* uid)
119 {
120     StubInfoSummary summary = StructureStubInfo::summary(stubInfo);
121     if (!isInlineable(summary))
122         return InByIdStatus(summary);
123     
124     // Finally figure out if we can derive an access strategy.
125     InByIdStatus result;
126     result.m_state = Simple;
127     switch (stubInfo->cacheType) {
128     case CacheType::Unset:
129         return InByIdStatus(NoInformation);
130
131     case CacheType::InByIdSelf: {
132         Structure* structure = stubInfo->u.byIdSelf.baseObjectStructure.get();
133         if (structure->takesSlowPathInDFGForImpureProperty())
134             return InByIdStatus(TakesSlowPath);
135         unsigned attributes;
136         InByIdVariant variant;
137         variant.m_offset = structure->getConcurrently(uid, attributes);
138         if (!isValidOffset(variant.m_offset))
139             return InByIdStatus(TakesSlowPath);
140         if (attributes & PropertyAttribute::CustomAccessor)
141             return InByIdStatus(TakesSlowPath);
142
143         variant.m_structureSet.add(structure);
144         bool didAppend = result.appendVariant(variant);
145         ASSERT_UNUSED(didAppend, didAppend);
146         return result;
147     }
148
149     case CacheType::Stub: {
150         PolymorphicAccess* list = stubInfo->u.stub;
151         for (unsigned listIndex = 0; listIndex < list->size(); ++listIndex) {
152             const AccessCase& access = list->at(listIndex);
153             if (access.viaProxy())
154                 return InByIdStatus(TakesSlowPath);
155
156             if (access.usesPolyProto())
157                 return InByIdStatus(TakesSlowPath);
158
159             Structure* structure = access.structure();
160             if (!structure) {
161                 // The null structure cases arise due to array.length. We have no way of creating a
162                 // InByIdVariant for those, and we don't really have to since the DFG handles those
163                 // cases in FixupPhase using value profiling. That's a bit awkward - we shouldn't
164                 // have to use value profiling to discover something that the AccessCase could have
165                 // told us. But, it works well enough. So, our only concern here is to not
166                 // crash on null structure.
167                 return InByIdStatus(TakesSlowPath);
168             }
169
170             ComplexGetStatus complexGetStatus = ComplexGetStatus::computeFor(structure, access.conditionSet(), uid);
171             switch (complexGetStatus.kind()) {
172             case ComplexGetStatus::ShouldSkip:
173                 continue;
174
175             case ComplexGetStatus::TakesSlowPath:
176                 return InByIdStatus(TakesSlowPath);
177
178             case ComplexGetStatus::Inlineable: {
179                 switch (access.type()) {
180                 case AccessCase::InHit:
181                 case AccessCase::InMiss:
182                     break;
183                 default:
184                     return InByIdStatus(TakesSlowPath);
185                 }
186
187                 InByIdVariant variant(
188                     StructureSet(structure), complexGetStatus.offset(),
189                     complexGetStatus.conditionSet());
190
191                 if (!result.appendVariant(variant))
192                     return InByIdStatus(TakesSlowPath);
193                 break;
194             }
195             }
196         }
197
198         return result;
199     }
200
201     default:
202         return InByIdStatus(TakesSlowPath);
203     }
204
205     RELEASE_ASSERT_NOT_REACHED();
206     return InByIdStatus();
207 }
208 #endif
209
210 void InByIdStatus::merge(const InByIdStatus& other)
211 {
212     if (other.m_state == NoInformation)
213         return;
214     
215     switch (m_state) {
216     case NoInformation:
217         *this = other;
218         return;
219         
220     case Simple:
221         if (other.m_state != Simple) {
222             *this = InByIdStatus(TakesSlowPath);
223             return;
224         }
225         for (const InByIdVariant& otherVariant : other.m_variants) {
226             if (!appendVariant(otherVariant)) {
227                 *this = InByIdStatus(TakesSlowPath);
228                 return;
229             }
230         }
231         return;
232         
233     case TakesSlowPath:
234         return;
235     }
236     
237     RELEASE_ASSERT_NOT_REACHED();
238 }
239
240 void InByIdStatus::filter(const StructureSet& structureSet)
241 {
242     if (m_state != Simple)
243         return;
244     filterICStatusVariants(m_variants, structureSet);
245     if (m_variants.isEmpty())
246         m_state = NoInformation;
247 }
248
249 void InByIdStatus::markIfCheap(SlotVisitor& visitor)
250 {
251     for (InByIdVariant& variant : m_variants)
252         variant.markIfCheap(visitor);
253 }
254
255 bool InByIdStatus::finalize()
256 {
257     for (InByIdVariant& variant : m_variants) {
258         if (!variant.finalize())
259             return false;
260     }
261     return true;
262 }
263
264 void InByIdStatus::dump(PrintStream& out) const
265 {
266     out.print("(");
267     switch (m_state) {
268     case NoInformation:
269         out.print("NoInformation");
270         break;
271     case Simple:
272         out.print("Simple");
273         break;
274     case TakesSlowPath:
275         out.print("TakesSlowPath");
276         break;
277     }
278     out.print(", ", listDump(m_variants), ")");
279 }
280
281 } // namespace JSC
282