Replace WTF::move with WTFMove
[WebKit-https.git] / Source / JavaScriptCore / bytecode / GetByIdStatus.cpp
1 /*
2  * Copyright (C) 2012-2015 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 "GetByIdStatus.h"
28
29 #include "CodeBlock.h"
30 #include "ComplexGetStatus.h"
31 #include "JSCInlines.h"
32 #include "JSScope.h"
33 #include "LLIntData.h"
34 #include "LowLevelInterpreter.h"
35 #include "PolymorphicAccess.h"
36 #include <wtf/ListDump.h>
37
38 namespace JSC {
39
40 bool GetByIdStatus::appendVariant(const GetByIdVariant& variant)
41 {
42     // Attempt to merge this variant with an already existing variant.
43     for (unsigned i = 0; i < m_variants.size(); ++i) {
44         if (m_variants[i].attemptToMerge(variant))
45             return true;
46     }
47     
48     // Make sure there is no overlap. We should have pruned out opportunities for
49     // overlap but it's possible that an inline cache got into a weird state. We are
50     // defensive and bail if we detect crazy.
51     for (unsigned i = 0; i < m_variants.size(); ++i) {
52         if (m_variants[i].structureSet().overlaps(variant.structureSet()))
53             return false;
54     }
55     
56     m_variants.append(variant);
57     return true;
58 }
59
60 #if ENABLE(DFG_JIT)
61 bool GetByIdStatus::hasExitSite(const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, unsigned bytecodeIndex)
62 {
63     return profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadCache))
64         || profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadConstantCache));
65 }
66 #endif
67
68 GetByIdStatus GetByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned bytecodeIndex, UniquedStringImpl* uid)
69 {
70     UNUSED_PARAM(profiledBlock);
71     UNUSED_PARAM(bytecodeIndex);
72     UNUSED_PARAM(uid);
73
74     VM& vm = *profiledBlock->vm();
75     
76     Instruction* instruction = profiledBlock->instructions().begin() + bytecodeIndex;
77     
78     if (instruction[0].u.opcode == LLInt::getOpcode(op_get_array_length))
79         return GetByIdStatus(NoInformation, false);
80
81     StructureID structureID = instruction[4].u.structureID;
82     if (!structureID)
83         return GetByIdStatus(NoInformation, false);
84
85     Structure* structure = vm.heap.structureIDTable().get(structureID);
86
87     if (structure->takesSlowPathInDFGForImpureProperty())
88         return GetByIdStatus(NoInformation, false);
89
90     unsigned attributesIgnored;
91     PropertyOffset offset = structure->getConcurrently(uid, attributesIgnored);
92     if (!isValidOffset(offset))
93         return GetByIdStatus(NoInformation, false);
94     
95     return GetByIdStatus(Simple, false, GetByIdVariant(StructureSet(structure), offset));
96 }
97
98 GetByIdStatus GetByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& map, unsigned bytecodeIndex, UniquedStringImpl* uid)
99 {
100     ConcurrentJITLocker locker(profiledBlock->m_lock);
101
102     GetByIdStatus result;
103
104 #if ENABLE(DFG_JIT)
105     result = computeForStubInfoWithoutExitSiteFeedback(
106         locker, profiledBlock, map.get(CodeOrigin(bytecodeIndex)), uid,
107         CallLinkStatus::computeExitSiteData(locker, profiledBlock, bytecodeIndex));
108     
109     if (!result.takesSlowPath()
110         && hasExitSite(locker, profiledBlock, bytecodeIndex))
111         return GetByIdStatus(result.makesCalls() ? MakesCalls : TakesSlowPath, true);
112 #else
113     UNUSED_PARAM(map);
114 #endif
115
116     if (!result)
117         return computeFromLLInt(profiledBlock, bytecodeIndex, uid);
118     
119     return result;
120 }
121
122 #if ENABLE(DFG_JIT)
123 GetByIdStatus GetByIdStatus::computeForStubInfo(const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, StructureStubInfo* stubInfo, CodeOrigin codeOrigin, UniquedStringImpl* uid)
124 {
125     GetByIdStatus result = GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback(
126         locker, profiledBlock, stubInfo, uid,
127         CallLinkStatus::computeExitSiteData(locker, profiledBlock, codeOrigin.bytecodeIndex));
128
129     if (!result.takesSlowPath() && GetByIdStatus::hasExitSite(locker, profiledBlock, codeOrigin.bytecodeIndex))
130         return GetByIdStatus(result.makesCalls() ? GetByIdStatus::MakesCalls : GetByIdStatus::TakesSlowPath, true);
131     return result;
132 }
133 #endif // ENABLE(DFG_JIT)
134
135 #if ENABLE(JIT)
136 GetByIdStatus GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback(
137     const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, StructureStubInfo* stubInfo, UniquedStringImpl* uid,
138     CallLinkStatus::ExitSiteData callExitSiteData)
139 {
140     if (!stubInfo || !stubInfo->everConsidered)
141         return GetByIdStatus(NoInformation);
142
143     PolymorphicAccess* list = 0;
144     State slowPathState = TakesSlowPath;
145     if (stubInfo->cacheType == CacheType::Stub) {
146         list = stubInfo->u.stub;
147         for (unsigned i = 0; i < list->size(); ++i) {
148             const AccessCase& access = list->at(i);
149             if (access.doesCalls())
150                 slowPathState = MakesCalls;
151         }
152     }
153     
154     if (stubInfo->tookSlowPath)
155         return GetByIdStatus(slowPathState);
156     
157     // Finally figure out if we can derive an access strategy.
158     GetByIdStatus result;
159     result.m_state = Simple;
160     result.m_wasSeenInJIT = true; // This is interesting for bytecode dumping only.
161     switch (stubInfo->cacheType) {
162     case CacheType::Unset:
163         return GetByIdStatus(NoInformation);
164         
165     case CacheType::GetByIdSelf: {
166         Structure* structure = stubInfo->u.byIdSelf.baseObjectStructure.get();
167         if (structure->takesSlowPathInDFGForImpureProperty())
168             return GetByIdStatus(slowPathState, true);
169         unsigned attributesIgnored;
170         GetByIdVariant variant;
171         variant.m_offset = structure->getConcurrently(uid, attributesIgnored);
172         if (!isValidOffset(variant.m_offset))
173             return GetByIdStatus(slowPathState, true);
174         
175         variant.m_structureSet.add(structure);
176         bool didAppend = result.appendVariant(variant);
177         ASSERT_UNUSED(didAppend, didAppend);
178         return result;
179     }
180         
181     case CacheType::Stub: {
182         for (unsigned listIndex = 0; listIndex < list->size(); ++listIndex) {
183             const AccessCase& access = list->at(listIndex);
184             if (access.viaProxy())
185                 return GetByIdStatus(slowPathState, true);
186             
187             Structure* structure = access.structure();
188             if (!structure) {
189                 // The null structure cases arise due to array.length and string.length. We have no way
190                 // of creating a GetByIdVariant for those, and we don't really have to since the DFG
191                 // handles those cases in FixupPhase using value profiling. That's a bit awkward - we
192                 // shouldn't have to use value profiling to discover something that the AccessCase
193                 // could have told us. But, it works well enough. So, our only concern here is to not
194                 // crash on null structure.
195                 return GetByIdStatus(slowPathState, true);
196             }
197             
198             ComplexGetStatus complexGetStatus = ComplexGetStatus::computeFor(
199                 structure, access.conditionSet(), uid);
200              
201             switch (complexGetStatus.kind()) {
202             case ComplexGetStatus::ShouldSkip:
203                 continue;
204                  
205             case ComplexGetStatus::TakesSlowPath:
206                 return GetByIdStatus(slowPathState, true);
207                  
208             case ComplexGetStatus::Inlineable: {
209                 std::unique_ptr<CallLinkStatus> callLinkStatus;
210                 JSFunction* intrinsicFunction = nullptr;
211
212                 switch (access.type()) {
213                 case AccessCase::Load: {
214                     break;
215                 }
216                 case AccessCase::IntrinsicGetter: {
217                     intrinsicFunction = access.intrinsicFunction();
218                     break;
219                 }
220                 case AccessCase::Getter: {
221                     CallLinkInfo* callLinkInfo = access.callLinkInfo();
222                     ASSERT(callLinkInfo);
223                     callLinkStatus = std::make_unique<CallLinkStatus>(
224                         CallLinkStatus::computeFor(
225                             locker, profiledBlock, *callLinkInfo, callExitSiteData));
226                     break;
227                 }
228                 default: {
229                     // FIXME: It would be totally sweet to support more of these at some point in the
230                     // future. https://bugs.webkit.org/show_bug.cgi?id=133052
231                     return GetByIdStatus(slowPathState, true);
232                 } }
233                  
234                 GetByIdVariant variant(
235                     StructureSet(structure), complexGetStatus.offset(),
236                     complexGetStatus.conditionSet(), WTFMove(callLinkStatus),
237                     intrinsicFunction);
238
239                 if (!result.appendVariant(variant))
240                     return GetByIdStatus(slowPathState, true);
241                 break;
242             } }
243         }
244         
245         return result;
246     }
247         
248     default:
249         return GetByIdStatus(slowPathState, true);
250     }
251     
252     RELEASE_ASSERT_NOT_REACHED();
253     return GetByIdStatus();
254 }
255 #endif // ENABLE(JIT)
256
257 GetByIdStatus GetByIdStatus::computeFor(
258     CodeBlock* profiledBlock, CodeBlock* dfgBlock, StubInfoMap& baselineMap,
259     StubInfoMap& dfgMap, CodeOrigin codeOrigin, UniquedStringImpl* uid)
260 {
261 #if ENABLE(DFG_JIT)
262     if (dfgBlock) {
263         CallLinkStatus::ExitSiteData exitSiteData;
264         {
265             ConcurrentJITLocker locker(profiledBlock->m_lock);
266             exitSiteData = CallLinkStatus::computeExitSiteData(
267                 locker, profiledBlock, codeOrigin.bytecodeIndex);
268         }
269         
270         GetByIdStatus result;
271         {
272             ConcurrentJITLocker locker(dfgBlock->m_lock);
273             result = computeForStubInfoWithoutExitSiteFeedback(
274                 locker, dfgBlock, dfgMap.get(codeOrigin), uid, exitSiteData);
275         }
276
277         if (result.takesSlowPath())
278             return result;
279     
280         {
281             ConcurrentJITLocker locker(profiledBlock->m_lock);
282             if (hasExitSite(locker, profiledBlock, codeOrigin.bytecodeIndex))
283                 return GetByIdStatus(TakesSlowPath, true);
284         }
285         
286         if (result.isSet())
287             return result;
288     }
289 #else
290     UNUSED_PARAM(dfgBlock);
291     UNUSED_PARAM(dfgMap);
292 #endif
293
294     return computeFor(profiledBlock, baselineMap, codeOrigin.bytecodeIndex, uid);
295 }
296
297 GetByIdStatus GetByIdStatus::computeFor(const StructureSet& set, UniquedStringImpl* uid)
298 {
299     // For now we only handle the super simple self access case. We could handle the
300     // prototype case in the future.
301     
302     if (set.isEmpty())
303         return GetByIdStatus();
304
305     if (parseIndex(*uid))
306         return GetByIdStatus(TakesSlowPath);
307     
308     GetByIdStatus result;
309     result.m_state = Simple;
310     result.m_wasSeenInJIT = false;
311     for (unsigned i = 0; i < set.size(); ++i) {
312         Structure* structure = set[i];
313         if (structure->typeInfo().overridesGetOwnPropertySlot() && structure->typeInfo().type() != GlobalObjectType)
314             return GetByIdStatus(TakesSlowPath);
315         
316         if (!structure->propertyAccessesAreCacheable())
317             return GetByIdStatus(TakesSlowPath);
318         
319         unsigned attributes;
320         PropertyOffset offset = structure->getConcurrently(uid, attributes);
321         if (!isValidOffset(offset))
322             return GetByIdStatus(TakesSlowPath); // It's probably a prototype lookup. Give up on life for now, even though we could totally be way smarter about it.
323         if (attributes & Accessor)
324             return GetByIdStatus(MakesCalls); // We could be smarter here, like strength-reducing this to a Call.
325         
326         if (!result.appendVariant(GetByIdVariant(structure, offset)))
327             return GetByIdStatus(TakesSlowPath);
328     }
329     
330     return result;
331 }
332
333 bool GetByIdStatus::makesCalls() const
334 {
335     switch (m_state) {
336     case NoInformation:
337     case TakesSlowPath:
338         return false;
339     case Simple:
340         for (unsigned i = m_variants.size(); i--;) {
341             if (m_variants[i].callLinkStatus())
342                 return true;
343         }
344         return false;
345     case MakesCalls:
346         return true;
347     }
348     RELEASE_ASSERT_NOT_REACHED();
349
350     return false;
351 }
352
353 void GetByIdStatus::dump(PrintStream& out) const
354 {
355     out.print("(");
356     switch (m_state) {
357     case NoInformation:
358         out.print("NoInformation");
359         break;
360     case Simple:
361         out.print("Simple");
362         break;
363     case TakesSlowPath:
364         out.print("TakesSlowPath");
365         break;
366     case MakesCalls:
367         out.print("MakesCalls");
368         break;
369     }
370     out.print(", ", listDump(m_variants), ", seenInJIT = ", m_wasSeenInJIT, ")");
371 }
372
373 } // namespace JSC
374