[WTF] Add makeUnique<T>, which ensures T is fast-allocated, makeUnique / makeUniqueWi...
[WebKit-https.git] / Source / JavaScriptCore / bytecode / PutByIdStatus.cpp
1 /*
2  * Copyright (C) 2012-2019 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 "PutByIdStatus.h"
28
29 #include "BytecodeStructs.h"
30 #include "CodeBlock.h"
31 #include "ComplexGetStatus.h"
32 #include "GetterSetterAccessCase.h"
33 #include "ICStatusUtils.h"
34 #include "LLIntData.h"
35 #include "LowLevelInterpreter.h"
36 #include "JSCInlines.h"
37 #include "PolymorphicAccess.h"
38 #include "Structure.h"
39 #include "StructureChain.h"
40 #include "StructureStubInfo.h"
41 #include <wtf/ListDump.h>
42
43 namespace JSC {
44
45 bool PutByIdStatus::appendVariant(const PutByIdVariant& variant)
46 {
47     return appendICStatusVariant(m_variants, variant);
48 }
49
50 PutByIdStatus PutByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned bytecodeIndex, UniquedStringImpl* uid)
51 {
52     VM& vm = *profiledBlock->vm();
53     
54     auto instruction = profiledBlock->instructions().at(bytecodeIndex);
55     auto bytecode = instruction->as<OpPutById>();
56     auto& metadata = bytecode.metadata(profiledBlock);
57
58     StructureID structureID = metadata.m_oldStructureID;
59     if (!structureID)
60         return PutByIdStatus(NoInformation);
61     
62     Structure* structure = vm.heap.structureIDTable().get(structureID);
63
64     StructureID newStructureID = metadata.m_newStructureID;
65     if (!newStructureID) {
66         PropertyOffset offset = structure->getConcurrently(uid);
67         if (!isValidOffset(offset))
68             return PutByIdStatus(NoInformation);
69         
70         return PutByIdVariant::replace(structure, offset);
71     }
72
73     Structure* newStructure = vm.heap.structureIDTable().get(newStructureID);
74     
75     ASSERT(structure->transitionWatchpointSetHasBeenInvalidated());
76     
77     PropertyOffset offset = newStructure->getConcurrently(uid);
78     if (!isValidOffset(offset))
79         return PutByIdStatus(NoInformation);
80     
81     ObjectPropertyConditionSet conditionSet;
82     if (!(bytecode.m_flags & PutByIdIsDirect)) {
83         conditionSet =
84             generateConditionsForPropertySetterMissConcurrently(
85                 vm, profiledBlock->globalObject(), structure, uid);
86         if (!conditionSet.isValid())
87             return PutByIdStatus(NoInformation);
88     }
89     
90     return PutByIdVariant::transition(
91         structure, newStructure, conditionSet, offset);
92 }
93
94 #if ENABLE(JIT)
95 PutByIdStatus PutByIdStatus::computeFor(CodeBlock* profiledBlock, ICStatusMap& map, unsigned bytecodeIndex, UniquedStringImpl* uid, ExitFlag didExit, CallLinkStatus::ExitSiteData callExitSiteData)
96 {
97     ConcurrentJSLocker locker(profiledBlock->m_lock);
98     
99     UNUSED_PARAM(profiledBlock);
100     UNUSED_PARAM(bytecodeIndex);
101     UNUSED_PARAM(uid);
102 #if ENABLE(DFG_JIT)
103     if (didExit)
104         return PutByIdStatus(TakesSlowPath);
105     
106     StructureStubInfo* stubInfo = map.get(CodeOrigin(bytecodeIndex)).stubInfo;
107     PutByIdStatus result = computeForStubInfo(
108         locker, profiledBlock, stubInfo, uid, callExitSiteData);
109     if (!result)
110         return computeFromLLInt(profiledBlock, bytecodeIndex, uid);
111     
112     return result;
113 #else // ENABLE(JIT)
114     UNUSED_PARAM(map);
115     UNUSED_PARAM(didExit);
116     UNUSED_PARAM(callExitSiteData);
117     return PutByIdStatus(NoInformation);
118 #endif // ENABLE(JIT)
119 }
120
121 PutByIdStatus PutByIdStatus::computeForStubInfo(const ConcurrentJSLocker& locker, CodeBlock* baselineBlock, StructureStubInfo* stubInfo, CodeOrigin codeOrigin, UniquedStringImpl* uid)
122 {
123     return computeForStubInfo(
124         locker, baselineBlock, stubInfo, uid,
125         CallLinkStatus::computeExitSiteData(baselineBlock, codeOrigin.bytecodeIndex()));
126 }
127
128 PutByIdStatus PutByIdStatus::computeForStubInfo(
129     const ConcurrentJSLocker& locker, CodeBlock* profiledBlock, StructureStubInfo* stubInfo,
130     UniquedStringImpl* uid, CallLinkStatus::ExitSiteData callExitSiteData)
131 {
132     StubInfoSummary summary = StructureStubInfo::summary(stubInfo);
133     if (!isInlineable(summary))
134         return PutByIdStatus(summary);
135     
136     switch (stubInfo->cacheType) {
137     case CacheType::Unset:
138         // This means that we attempted to cache but failed for some reason.
139         return PutByIdStatus(JSC::slowVersion(summary));
140         
141     case CacheType::PutByIdReplace: {
142         PropertyOffset offset =
143             stubInfo->u.byIdSelf.baseObjectStructure->getConcurrently(uid);
144         if (isValidOffset(offset)) {
145             return PutByIdVariant::replace(
146                 stubInfo->u.byIdSelf.baseObjectStructure.get(), offset);
147         }
148         return PutByIdStatus(JSC::slowVersion(summary));
149     }
150         
151     case CacheType::Stub: {
152         PolymorphicAccess* list = stubInfo->u.stub;
153         
154         PutByIdStatus result;
155         result.m_state = Simple;
156         
157         for (unsigned i = 0; i < list->size(); ++i) {
158             const AccessCase& access = list->at(i);
159             if (access.viaProxy())
160                 return PutByIdStatus(JSC::slowVersion(summary));
161             if (access.usesPolyProto())
162                 return PutByIdStatus(JSC::slowVersion(summary));
163             
164             PutByIdVariant variant;
165             
166             switch (access.type()) {
167             case AccessCase::Replace: {
168                 Structure* structure = access.structure();
169                 PropertyOffset offset = structure->getConcurrently(uid);
170                 if (!isValidOffset(offset))
171                     return PutByIdStatus(JSC::slowVersion(summary));
172                 variant = PutByIdVariant::replace(
173                     structure, offset);
174                 break;
175             }
176                 
177             case AccessCase::Transition: {
178                 PropertyOffset offset =
179                     access.newStructure()->getConcurrently(uid);
180                 if (!isValidOffset(offset))
181                     return PutByIdStatus(JSC::slowVersion(summary));
182                 ObjectPropertyConditionSet conditionSet = access.conditionSet();
183                 if (!conditionSet.structuresEnsureValidity())
184                     return PutByIdStatus(JSC::slowVersion(summary));
185                 variant = PutByIdVariant::transition(
186                     access.structure(), access.newStructure(), conditionSet, offset);
187                 break;
188             }
189                 
190             case AccessCase::Setter: {
191                 Structure* structure = access.structure();
192                 
193                 ComplexGetStatus complexGetStatus = ComplexGetStatus::computeFor(
194                     structure, access.conditionSet(), uid);
195                 
196                 switch (complexGetStatus.kind()) {
197                 case ComplexGetStatus::ShouldSkip:
198                     continue;
199                     
200                 case ComplexGetStatus::TakesSlowPath:
201                     return PutByIdStatus(JSC::slowVersion(summary));
202                     
203                 case ComplexGetStatus::Inlineable: {
204                     std::unique_ptr<CallLinkStatus> callLinkStatus =
205                         makeUnique<CallLinkStatus>();
206                     if (CallLinkInfo* callLinkInfo = access.as<GetterSetterAccessCase>().callLinkInfo()) {
207                         *callLinkStatus = CallLinkStatus::computeFor(
208                             locker, profiledBlock, *callLinkInfo, callExitSiteData);
209                     }
210                     
211                     variant = PutByIdVariant::setter(
212                         structure, complexGetStatus.offset(), complexGetStatus.conditionSet(),
213                         WTFMove(callLinkStatus));
214                 } }
215                 break;
216             }
217                 
218             case AccessCase::CustomValueSetter:
219             case AccessCase::CustomAccessorSetter:
220                 return PutByIdStatus(MakesCalls);
221
222             default:
223                 return PutByIdStatus(JSC::slowVersion(summary));
224             }
225             
226             if (!result.appendVariant(variant))
227                 return PutByIdStatus(JSC::slowVersion(summary));
228         }
229         
230         return result;
231     }
232         
233     default:
234         return PutByIdStatus(JSC::slowVersion(summary));
235     }
236 }
237
238 PutByIdStatus PutByIdStatus::computeFor(CodeBlock* baselineBlock, ICStatusMap& baselineMap, ICStatusContextStack& contextStack, CodeOrigin codeOrigin, UniquedStringImpl* uid)
239 {
240     unsigned bytecodeIndex = codeOrigin.bytecodeIndex();
241     CallLinkStatus::ExitSiteData callExitSiteData = CallLinkStatus::computeExitSiteData(baselineBlock, bytecodeIndex);
242     ExitFlag didExit = hasBadCacheExitSite(baselineBlock, bytecodeIndex);
243
244     for (ICStatusContext* context : contextStack) {
245         ICStatus status = context->get(codeOrigin);
246         
247         auto bless = [&] (const PutByIdStatus& result) -> PutByIdStatus {
248             if (!context->isInlined(codeOrigin)) {
249                 PutByIdStatus baselineResult = computeFor(
250                     baselineBlock, baselineMap, bytecodeIndex, uid, didExit,
251                     callExitSiteData);
252                 baselineResult.merge(result);
253                 return baselineResult;
254             }
255             if (didExit.isSet(ExitFromInlined))
256                 return result.slowVersion();
257             return result;
258         };
259         
260         if (status.stubInfo) {
261             PutByIdStatus result;
262             {
263                 ConcurrentJSLocker locker(context->optimizedCodeBlock->m_lock);
264                 result = computeForStubInfo(
265                     locker, context->optimizedCodeBlock, status.stubInfo, uid, callExitSiteData);
266             }
267             if (result.isSet())
268                 return bless(result);
269         }
270         
271         if (status.putStatus)
272             return bless(*status.putStatus);
273     }
274     
275     return computeFor(baselineBlock, baselineMap, bytecodeIndex, uid, didExit, callExitSiteData);
276 }
277
278 PutByIdStatus PutByIdStatus::computeFor(JSGlobalObject* globalObject, const StructureSet& set, UniquedStringImpl* uid, bool isDirect)
279 {
280     if (parseIndex(*uid))
281         return PutByIdStatus(TakesSlowPath);
282
283     if (set.isEmpty())
284         return PutByIdStatus();
285     
286     VM& vm = globalObject->vm();
287     PutByIdStatus result;
288     result.m_state = Simple;
289     for (unsigned i = 0; i < set.size(); ++i) {
290         Structure* structure = set[i];
291         
292         if (structure->typeInfo().overridesGetOwnPropertySlot() && structure->typeInfo().type() != GlobalObjectType)
293             return PutByIdStatus(TakesSlowPath);
294
295         if (!structure->propertyAccessesAreCacheable())
296             return PutByIdStatus(TakesSlowPath);
297     
298         unsigned attributes;
299         PropertyOffset offset = structure->getConcurrently(uid, attributes);
300         if (isValidOffset(offset)) {
301             if (attributes & PropertyAttribute::CustomAccessorOrValue)
302                 return PutByIdStatus(MakesCalls);
303
304             if (attributes & (PropertyAttribute::Accessor | PropertyAttribute::ReadOnly))
305                 return PutByIdStatus(TakesSlowPath);
306             
307             WatchpointSet* replaceSet = structure->propertyReplacementWatchpointSet(offset);
308             if (!replaceSet || replaceSet->isStillValid()) {
309                 // When this executes, it'll create, and fire, this replacement watchpoint set.
310                 // That means that  this has probably never executed or that something fishy is
311                 // going on. Also, we cannot create or fire the watchpoint set from the concurrent
312                 // JIT thread, so even if we wanted to do this, we'd need to have a lazy thingy.
313                 // So, better leave this alone and take slow path.
314                 return PutByIdStatus(TakesSlowPath);
315             }
316
317             PutByIdVariant variant =
318                 PutByIdVariant::replace(structure, offset);
319             if (!result.appendVariant(variant))
320                 return PutByIdStatus(TakesSlowPath);
321             continue;
322         }
323     
324         // Our hypothesis is that we're doing a transition. Before we prove that this is really
325         // true, we want to do some sanity checks.
326     
327         // Don't cache put transitions on dictionaries.
328         if (structure->isDictionary())
329             return PutByIdStatus(TakesSlowPath);
330
331         // If the structure corresponds to something that isn't an object, then give up, since
332         // we don't want to be adding properties to strings.
333         if (!structure->typeInfo().isObject())
334             return PutByIdStatus(TakesSlowPath);
335     
336         ObjectPropertyConditionSet conditionSet;
337         if (!isDirect) {
338             conditionSet = generateConditionsForPropertySetterMissConcurrently(
339                 vm, globalObject, structure, uid);
340             if (!conditionSet.isValid())
341                 return PutByIdStatus(TakesSlowPath);
342         }
343     
344         // We only optimize if there is already a structure that the transition is cached to.
345         Structure* transition =
346             Structure::addPropertyTransitionToExistingStructureConcurrently(structure, uid, 0, offset);
347         if (!transition)
348             return PutByIdStatus(TakesSlowPath);
349         ASSERT(isValidOffset(offset));
350     
351         bool didAppend = result.appendVariant(
352             PutByIdVariant::transition(
353                 structure, transition, conditionSet, offset));
354         if (!didAppend)
355             return PutByIdStatus(TakesSlowPath);
356     }
357     
358     return result;
359 }
360 #endif
361
362 bool PutByIdStatus::makesCalls() const
363 {
364     if (m_state == MakesCalls)
365         return true;
366     
367     if (m_state != Simple)
368         return false;
369     
370     for (unsigned i = m_variants.size(); i--;) {
371         if (m_variants[i].makesCalls())
372             return true;
373     }
374     
375     return false;
376 }
377
378 PutByIdStatus PutByIdStatus::slowVersion() const
379 {
380     return PutByIdStatus(makesCalls() ? MakesCalls : TakesSlowPath);
381 }
382
383 void PutByIdStatus::markIfCheap(SlotVisitor& visitor)
384 {
385     for (PutByIdVariant& variant : m_variants)
386         variant.markIfCheap(visitor);
387 }
388
389 bool PutByIdStatus::finalize(VM& vm)
390 {
391     for (PutByIdVariant& variant : m_variants) {
392         if (!variant.finalize(vm))
393             return false;
394     }
395     return true;
396 }
397
398 void PutByIdStatus::merge(const PutByIdStatus& other)
399 {
400     if (other.m_state == NoInformation)
401         return;
402     
403     auto mergeSlow = [&] () {
404         *this = PutByIdStatus((makesCalls() || other.makesCalls()) ? MakesCalls : TakesSlowPath);
405     };
406     
407     switch (m_state) {
408     case NoInformation:
409         *this = other;
410         return;
411         
412     case Simple:
413         if (other.m_state != Simple)
414             return mergeSlow();
415         
416         for (const PutByIdVariant& other : other.m_variants) {
417             if (!appendVariant(other))
418                 return mergeSlow();
419         }
420         return;
421         
422     case TakesSlowPath:
423     case MakesCalls:
424         return mergeSlow();
425     }
426     
427     RELEASE_ASSERT_NOT_REACHED();
428 }
429
430 void PutByIdStatus::filter(const StructureSet& set)
431 {
432     if (m_state != Simple)
433         return;
434     filterICStatusVariants(m_variants, set);
435     for (PutByIdVariant& variant : m_variants)
436         variant.fixTransitionToReplaceIfNecessary();
437     if (m_variants.isEmpty())
438         m_state = NoInformation;
439 }
440
441 void PutByIdStatus::dump(PrintStream& out) const
442 {
443     switch (m_state) {
444     case NoInformation:
445         out.print("(NoInformation)");
446         return;
447         
448     case Simple:
449         out.print("(", listDump(m_variants), ")");
450         return;
451         
452     case TakesSlowPath:
453         out.print("(TakesSlowPath)");
454         return;
455     case MakesCalls:
456         out.print("(MakesCalls)");
457         return;
458     }
459     
460     RELEASE_ASSERT_NOT_REACHED();
461 }
462
463 } // namespace JSC
464