[JSC] StructureStubInfo::bufferedStructures should not ref/deref UniquedStringImpl
[WebKit-https.git] / Source / JavaScriptCore / bytecode / StructureStubInfo.h
1 /*
2  * Copyright (C) 2008-2020 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 #pragma once
27
28 #include "CacheableIdentifier.h"
29 #include "CodeBlock.h"
30 #include "CodeOrigin.h"
31 #include "Instruction.h"
32 #include "JITStubRoutine.h"
33 #include "MacroAssembler.h"
34 #include "Options.h"
35 #include "RegisterSet.h"
36 #include "Structure.h"
37 #include "StructureSet.h"
38 #include "StructureStubClearingWatchpoint.h"
39 #include "StubInfoSummary.h"
40 #include <wtf/Box.h>
41
42 namespace JSC {
43
44 #if ENABLE(JIT)
45
46 class AccessCase;
47 class AccessGenerationResult;
48 class PolymorphicAccess;
49
50 enum class AccessType : int8_t {
51     GetById,
52     GetByIdWithThis,
53     GetByIdDirect,
54     TryGetById,
55     GetByVal,
56     Put,
57     In,
58     InstanceOf,
59     DeleteByID,
60     DeleteByVal
61 };
62
63 enum class CacheType : int8_t {
64     Unset,
65     GetByIdSelf,
66     PutByIdReplace,
67     InByIdSelf,
68     Stub,
69     ArrayLength,
70     StringLength
71 };
72
73 class StructureStubInfo {
74     WTF_MAKE_NONCOPYABLE(StructureStubInfo);
75     WTF_MAKE_FAST_ALLOCATED;
76 public:
77     StructureStubInfo(AccessType);
78     ~StructureStubInfo();
79
80     void initGetByIdSelf(CodeBlock*, Structure* baseObjectStructure, PropertyOffset, CacheableIdentifier);
81     void initArrayLength();
82     void initStringLength();
83     void initPutByIdReplace(CodeBlock*, Structure* baseObjectStructure, PropertyOffset);
84     void initInByIdSelf(CodeBlock*, Structure* baseObjectStructure, PropertyOffset);
85
86     AccessGenerationResult addAccessCase(const GCSafeConcurrentJSLocker&, CodeBlock*, CacheableIdentifier, std::unique_ptr<AccessCase>);
87
88     void reset(CodeBlock*);
89
90     void deref();
91     void aboutToDie();
92
93     void visitAggregate(SlotVisitor&);
94
95     // Check if the stub has weak references that are dead. If it does, then it resets itself,
96     // either entirely or just enough to ensure that those dead pointers don't get used anymore.
97     void visitWeakReferences(CodeBlock*);
98     
99     // This returns true if it has marked everything that it will ever mark.
100     bool propagateTransitions(SlotVisitor&);
101         
102     StubInfoSummary summary(VM&) const;
103     
104     static StubInfoSummary summary(VM&, const StructureStubInfo*);
105
106     CacheableIdentifier getByIdSelfIdentifier()
107     {
108         RELEASE_ASSERT(m_cacheType == CacheType::GetByIdSelf);
109         return m_getByIdSelfIdentifier;
110     }
111
112     bool containsPC(void* pc) const;
113
114     uint32_t inlineSize() const
115     {
116         int32_t inlineSize = MacroAssembler::differenceBetweenCodePtr(start, doneLocation);
117         ASSERT(inlineSize >= 0);
118         return inlineSize;
119     }
120
121     CodeLocationJump<JSInternalPtrTag> patchableJump()
122     { 
123         ASSERT(accessType == AccessType::InstanceOf);
124         return start.jumpAtOffset<JSInternalPtrTag>(0);
125     }
126
127     JSValueRegs valueRegs() const
128     {
129         return JSValueRegs(
130 #if USE(JSVALUE32_64)
131             valueTagGPR,
132 #endif
133             valueGPR);
134     }
135
136     JSValueRegs propertyRegs() const
137     {
138         return JSValueRegs(
139 #if USE(JSVALUE32_64)
140             v.propertyTagGPR,
141 #endif
142             regs.propertyGPR);
143     }
144
145     JSValueRegs baseRegs() const
146     {
147         return JSValueRegs(
148 #if USE(JSVALUE32_64)
149             baseTagGPR,
150 #endif
151             baseGPR);
152     }
153
154     bool thisValueIsInThisGPR() const { return accessType == AccessType::GetByIdWithThis; }
155
156 #if ASSERT_ENABLED
157     void checkConsistency();
158 #else
159     ALWAYS_INLINE void checkConsistency() { }
160 #endif
161
162     CacheType cacheType() const { return m_cacheType; }
163
164     // Not ByVal and ById case: e.g. instanceof, by-index etc.
165     ALWAYS_INLINE bool considerCachingGeneric(VM& vm, CodeBlock* codeBlock, Structure* structure)
166     {
167         return considerCaching(vm, codeBlock, structure, CacheableIdentifier());
168     }
169
170     ALWAYS_INLINE bool considerCachingById(VM& vm, CodeBlock* codeBlock, Structure* structure)
171     {
172         return considerCaching(vm, codeBlock, structure, CacheableIdentifier());
173     }
174
175     ALWAYS_INLINE bool considerCachingByVal(VM& vm, CodeBlock* codeBlock, Structure* structure, CacheableIdentifier impl)
176     {
177         return considerCaching(vm, codeBlock, structure, impl);
178     }
179
180 private:
181     ALWAYS_INLINE bool considerCaching(VM& vm, CodeBlock* codeBlock, Structure* structure, CacheableIdentifier impl)
182     {
183         DisallowGC disallowGC;
184
185         // We never cache non-cells.
186         if (!structure) {
187             sawNonCell = true;
188             return false;
189         }
190         
191         // This method is called from the Optimize variants of IC slow paths. The first part of this
192         // method tries to determine if the Optimize variant should really behave like the
193         // non-Optimize variant and leave the IC untouched.
194         //
195         // If we determine that we should do something to the IC then the next order of business is
196         // to determine if this Structure would impact the IC at all. We know that it won't, if we
197         // have already buffered something on its behalf. That's what the m_bufferedStructures set is
198         // for.
199         
200         everConsidered = true;
201         if (!countdown) {
202             // Check if we have been doing repatching too frequently. If so, then we should cool off
203             // for a while.
204             WTF::incrementWithSaturation(repatchCount);
205             if (repatchCount > Options::repatchCountForCoolDown()) {
206                 // We've been repatching too much, so don't do it now.
207                 repatchCount = 0;
208                 // The amount of time we require for cool-down depends on the number of times we've
209                 // had to cool down in the past. The relationship is exponential. The max value we
210                 // allow here is 2^256 - 2, since the slow paths may increment the count to indicate
211                 // that they'd like to temporarily skip patching just this once.
212                 countdown = WTF::leftShiftWithSaturation(
213                     static_cast<uint8_t>(Options::initialCoolDownCount()),
214                     numberOfCoolDowns,
215                     static_cast<uint8_t>(std::numeric_limits<uint8_t>::max() - 1));
216                 WTF::incrementWithSaturation(numberOfCoolDowns);
217                 
218                 // We may still have had something buffered. Trigger generation now.
219                 bufferingCountdown = 0;
220                 return true;
221             }
222             
223             // We don't want to return false due to buffering indefinitely.
224             if (!bufferingCountdown) {
225                 // Note that when this returns true, it's possible that we will not even get an
226                 // AccessCase because this may cause Repatch.cpp to simply do an in-place
227                 // repatching.
228                 return true;
229             }
230             
231             bufferingCountdown--;
232             
233             // Now protect the IC buffering. We want to proceed only if this is a structure that
234             // we don't already have a case buffered for. Note that if this returns true but the
235             // bufferingCountdown is not zero then we will buffer the access case for later without
236             // immediately generating code for it.
237             //
238             // NOTE: This will behave oddly for InstanceOf if the user varies the prototype but not
239             // the base's structure. That seems unlikely for the canonical use of instanceof, where
240             // the prototype is fixed.
241             bool isNewlyAdded = false;
242             {
243                 auto locker = holdLock(m_bufferedStructuresLock);
244                 isNewlyAdded = m_bufferedStructures.add({ structure, impl }).isNewEntry;
245             }
246             if (isNewlyAdded)
247                 vm.heap.writeBarrier(codeBlock);
248             return isNewlyAdded;
249         }
250         countdown--;
251         return false;
252     }
253
254     void setCacheType(CacheType);
255
256     void clearBufferedStructures()
257     {
258         auto locker = holdLock(m_bufferedStructuresLock);
259         m_bufferedStructures.clear();
260     }
261
262     class BufferedStructure {
263     public:
264         static constexpr uintptr_t hashTableDeletedValue = 0x2;
265         BufferedStructure() = default;
266         BufferedStructure(Structure* structure, CacheableIdentifier byValId)
267             : m_structure(structure)
268             , m_byValId(byValId)
269         { }
270         BufferedStructure(WTF::HashTableDeletedValueType)
271             : m_structure(bitwise_cast<Structure*>(hashTableDeletedValue))
272         { }
273
274         bool isHashTableDeletedValue() const { return bitwise_cast<uintptr_t>(m_structure) == hashTableDeletedValue; }
275
276         unsigned hash() const
277         {
278             unsigned hash = PtrHash<Structure*>::hash(m_structure);
279             if (m_byValId)
280                 hash += m_byValId.hash();
281             return hash;
282         }
283
284         friend bool operator==(const BufferedStructure& a, const BufferedStructure& b)
285         {
286             return a.m_structure == b.m_structure && a.m_byValId == b.m_byValId;
287         }
288
289         friend bool operator!=(const BufferedStructure& a, const BufferedStructure& b)
290         {
291             return !(a == b);
292         }
293
294         struct Hash {
295             static unsigned hash(const BufferedStructure& key)
296             {
297                 return key.hash();
298             }
299
300             static bool equal(const BufferedStructure& a, const BufferedStructure& b)
301             {
302                 return a == b;
303             }
304
305             static constexpr bool safeToCompareToEmptyOrDeleted = false;
306         };
307         using KeyTraits = SimpleClassHashTraits<BufferedStructure>;
308         static_assert(KeyTraits::emptyValueIsZero, "Structure* and CacheableIdentifier are empty if they are zero-initialized");
309
310         Structure* structure() const { return m_structure; }
311         const CacheableIdentifier& byValId() const { return m_byValId; }
312
313     private:
314         Structure* m_structure { nullptr };
315         CacheableIdentifier m_byValId;
316     };
317
318 public:
319     CodeOrigin codeOrigin;
320     union {
321         struct {
322             WriteBarrierBase<Structure> baseObjectStructure;
323             PropertyOffset offset;
324         } byIdSelf;
325         PolymorphicAccess* stub;
326     } u;
327 private:
328     CacheableIdentifier m_getByIdSelfIdentifier;
329     // Represents those structures that already have buffered AccessCases in the PolymorphicAccess.
330     // Note that it's always safe to clear this. If we clear it prematurely, then if we see the same
331     // structure again during this buffering countdown, we will create an AccessCase object for it.
332     // That's not so bad - we'll get rid of the redundant ones once we regenerate.
333     HashSet<BufferedStructure, BufferedStructure::Hash, BufferedStructure::KeyTraits> m_bufferedStructures;
334 public:
335     CodeLocationLabel<JITStubRoutinePtrTag> start; // This is either the start of the inline IC for *byId caches. or the location of patchable jump for 'instanceof' caches.
336     CodeLocationLabel<JSInternalPtrTag> doneLocation;
337     CodeLocationCall<JSInternalPtrTag> slowPathCallLocation;
338     CodeLocationLabel<JITStubRoutinePtrTag> slowPathStartLocation;
339
340     RegisterSet usedRegisters;
341
342     GPRReg baseGPR;
343     GPRReg valueGPR;
344     union {
345         GPRReg thisGPR;
346         GPRReg prototypeGPR;
347         GPRReg propertyGPR;
348     } regs;
349 #if USE(JSVALUE32_64)
350     GPRReg valueTagGPR;
351     // FIXME: [32-bits] Check if StructureStubInfo::baseTagGPR is used somewhere.
352     // https://bugs.webkit.org/show_bug.cgi?id=204726
353     GPRReg baseTagGPR;
354     union {
355         GPRReg thisTagGPR;
356         GPRReg propertyTagGPR;
357     } v;
358 #endif
359
360     AccessType accessType;
361 private:
362     CacheType m_cacheType { CacheType::Unset };
363 public:
364     // We repatch only when this is zero. If not zero, we decrement.
365     // Setting 1 for a totally clear stub, we'll patch it after the first execution.
366     uint8_t countdown { 1 };
367     uint8_t repatchCount { 0 };
368     uint8_t numberOfCoolDowns { 0 };
369
370     CallSiteIndex callSiteIndex;
371
372     uint8_t bufferingCountdown;
373     bool resetByGC : 1;
374     bool tookSlowPath : 1;
375     bool everConsidered : 1;
376     bool prototypeIsKnownObject : 1; // Only relevant for InstanceOf.
377     bool sawNonCell : 1;
378     bool hasConstantIdentifier : 1;
379     bool propertyIsString : 1;
380     bool propertyIsInt32 : 1;
381     bool propertyIsSymbol : 1;
382 private:
383     Lock m_bufferedStructuresLock;
384 };
385
386 inline CodeOrigin getStructureStubInfoCodeOrigin(StructureStubInfo& structureStubInfo)
387 {
388     return structureStubInfo.codeOrigin;
389 }
390
391 inline auto appropriateOptimizingGetByIdFunction(AccessType type) -> decltype(&operationGetByIdOptimize)
392 {
393     switch (type) {
394     case AccessType::GetById:
395         return operationGetByIdOptimize;
396     case AccessType::TryGetById:
397         return operationTryGetByIdOptimize;
398     case AccessType::GetByIdDirect:
399         return operationGetByIdDirectOptimize;
400     case AccessType::GetByIdWithThis:
401     default:
402         ASSERT_NOT_REACHED();
403         return nullptr;
404     }
405 }
406
407 inline auto appropriateGenericGetByIdFunction(AccessType type) -> decltype(&operationGetByIdGeneric)
408 {
409     switch (type) {
410     case AccessType::GetById:
411         return operationGetByIdGeneric;
412     case AccessType::TryGetById:
413         return operationTryGetByIdGeneric;
414     case AccessType::GetByIdDirect:
415         return operationGetByIdDirectGeneric;
416     case AccessType::GetByIdWithThis:
417     default:
418         ASSERT_NOT_REACHED();
419         return nullptr;
420     }
421 }
422
423 #else
424
425 class StructureStubInfo;
426
427 #endif // ENABLE(JIT)
428
429 typedef HashMap<CodeOrigin, StructureStubInfo*, CodeOriginApproximateHash> StubInfoMap;
430
431 } // namespace JSC