1/*
2 * Copyright (C) 2012-2018 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 "ConcurrentJSLock.h"
29#include "Structure.h"
30#include <wtf/SegmentedVector.h>
31
32namespace JSC {
33
34class CodeBlock;
35class LLIntOffsetsExtractor;
36
37// This is a bitfield where each bit represents an type of array access that we have seen.
38// There are 19 indexing types that use the lower bits.
39// There are 9 typed array types taking the bits 16 to 25.
40typedef unsigned ArrayModes;
41
42// The possible IndexingTypes are limited within (0 - 16, 21, 23, 25).
43// This is because CoW types only appear for JSArrays.
44static_assert(CopyOnWriteArrayWithInt32 == 21, "");
45static_assert(CopyOnWriteArrayWithDouble == 23, "");
46static_assert(CopyOnWriteArrayWithContiguous == 25, "");
47const ArrayModes CopyOnWriteArrayWithInt32ArrayMode = 1 << CopyOnWriteArrayWithInt32;
48const ArrayModes CopyOnWriteArrayWithDoubleArrayMode = 1 << CopyOnWriteArrayWithDouble;
49const ArrayModes CopyOnWriteArrayWithContiguousArrayMode = 1 << CopyOnWriteArrayWithContiguous;
50
51const ArrayModes Int8ArrayMode = 1 << 16;
52const ArrayModes Int16ArrayMode = 1 << 17;
53const ArrayModes Int32ArrayMode = 1 << 18;
54const ArrayModes Uint8ArrayMode = 1 << 19;
55const ArrayModes Uint8ClampedArrayMode = 1 << 20; // 21 - 25 are used for CoW arrays.
56const ArrayModes Uint16ArrayMode = 1 << 26;
57const ArrayModes Uint32ArrayMode = 1 << 27;
58const ArrayModes Float32ArrayMode = 1 << 28;
59const ArrayModes Float64ArrayMode = 1 << 29;
60
61JS_EXPORT_PRIVATE extern const ArrayModes typedArrayModes[NumberOfTypedArrayTypesExcludingDataView];
62
63constexpr ArrayModes asArrayModesIgnoringTypedArrays(IndexingType indexingMode)
64{
65 return static_cast<unsigned>(1) << static_cast<unsigned>(indexingMode);
66}
67
68#define ALL_TYPED_ARRAY_MODES \
69 (Int8ArrayMode \
70 | Int16ArrayMode \
71 | Int32ArrayMode \
72 | Uint8ArrayMode \
73 | Uint8ClampedArrayMode \
74 | Uint16ArrayMode \
75 | Uint32ArrayMode \
76 | Float32ArrayMode \
77 | Float64ArrayMode \
78 )
79
80#define ALL_NON_ARRAY_ARRAY_MODES \
81 (asArrayModesIgnoringTypedArrays(NonArray) \
82 | asArrayModesIgnoringTypedArrays(NonArrayWithInt32) \
83 | asArrayModesIgnoringTypedArrays(NonArrayWithDouble) \
84 | asArrayModesIgnoringTypedArrays(NonArrayWithContiguous) \
85 | asArrayModesIgnoringTypedArrays(NonArrayWithArrayStorage) \
86 | asArrayModesIgnoringTypedArrays(NonArrayWithSlowPutArrayStorage) \
87 | ALL_TYPED_ARRAY_MODES)
88
89#define ALL_COPY_ON_WRITE_ARRAY_MODES \
90 (CopyOnWriteArrayWithInt32ArrayMode \
91 | CopyOnWriteArrayWithDoubleArrayMode \
92 | CopyOnWriteArrayWithContiguousArrayMode)
93
94#define ALL_WRITABLE_ARRAY_ARRAY_MODES \
95 (asArrayModesIgnoringTypedArrays(ArrayClass) \
96 | asArrayModesIgnoringTypedArrays(ArrayWithUndecided) \
97 | asArrayModesIgnoringTypedArrays(ArrayWithInt32) \
98 | asArrayModesIgnoringTypedArrays(ArrayWithDouble) \
99 | asArrayModesIgnoringTypedArrays(ArrayWithContiguous) \
100 | asArrayModesIgnoringTypedArrays(ArrayWithArrayStorage) \
101 | asArrayModesIgnoringTypedArrays(ArrayWithSlowPutArrayStorage))
102
103#define ALL_ARRAY_ARRAY_MODES \
104 (ALL_WRITABLE_ARRAY_ARRAY_MODES \
105 | ALL_COPY_ON_WRITE_ARRAY_MODES)
106
107#define ALL_ARRAY_MODES (ALL_NON_ARRAY_ARRAY_MODES | ALL_ARRAY_ARRAY_MODES)
108
109inline ArrayModes arrayModesFromStructure(Structure* structure)
110{
111 JSType type = structure->typeInfo().type();
112 if (isTypedArrayType(type))
113 return typedArrayModes[type - FirstTypedArrayType];
114 return asArrayModesIgnoringTypedArrays(structure->indexingMode());
115}
116
117void dumpArrayModes(PrintStream&, ArrayModes);
118MAKE_PRINT_ADAPTOR(ArrayModesDump, ArrayModes, dumpArrayModes);
119
120inline bool mergeArrayModes(ArrayModes& left, ArrayModes right)
121{
122 ArrayModes newModes = left | right;
123 if (newModes == left)
124 return false;
125 left = newModes;
126 return true;
127}
128
129inline bool arrayModesAreClearOrTop(ArrayModes modes)
130{
131 return !modes || modes == ALL_ARRAY_MODES;
132}
133
134// Checks if proven is a subset of expected.
135inline bool arrayModesAlreadyChecked(ArrayModes proven, ArrayModes expected)
136{
137 return (expected | proven) == expected;
138}
139
140inline bool arrayModesIncludeIgnoringTypedArrays(ArrayModes arrayModes, IndexingType shape)
141{
142 ArrayModes modes = asArrayModesIgnoringTypedArrays(NonArray | shape) | asArrayModesIgnoringTypedArrays(ArrayClass | shape);
143 if (hasInt32(shape) || hasDouble(shape) || hasContiguous(shape))
144 modes |= asArrayModesIgnoringTypedArrays(ArrayClass | shape | CopyOnWrite);
145 return !!(arrayModes & modes);
146}
147
148inline bool shouldUseSlowPutArrayStorage(ArrayModes arrayModes)
149{
150 return arrayModesIncludeIgnoringTypedArrays(arrayModes, SlowPutArrayStorageShape);
151}
152
153inline bool shouldUseFastArrayStorage(ArrayModes arrayModes)
154{
155 return arrayModesIncludeIgnoringTypedArrays(arrayModes, ArrayStorageShape);
156}
157
158inline bool shouldUseContiguous(ArrayModes arrayModes)
159{
160 return arrayModesIncludeIgnoringTypedArrays(arrayModes, ContiguousShape);
161}
162
163inline bool shouldUseDouble(ArrayModes arrayModes)
164{
165 return arrayModesIncludeIgnoringTypedArrays(arrayModes, DoubleShape);
166}
167
168inline bool shouldUseInt32(ArrayModes arrayModes)
169{
170 return arrayModesIncludeIgnoringTypedArrays(arrayModes, Int32Shape);
171}
172
173inline bool hasSeenArray(ArrayModes arrayModes)
174{
175 return arrayModes & ALL_ARRAY_ARRAY_MODES;
176}
177
178inline bool hasSeenNonArray(ArrayModes arrayModes)
179{
180 return arrayModes & ALL_NON_ARRAY_ARRAY_MODES;
181}
182
183inline bool hasSeenWritableArray(ArrayModes arrayModes)
184{
185 return arrayModes & ALL_WRITABLE_ARRAY_ARRAY_MODES;
186}
187
188inline bool hasSeenCopyOnWriteArray(ArrayModes arrayModes)
189{
190 return arrayModes & ALL_COPY_ON_WRITE_ARRAY_MODES;
191}
192
193class ArrayProfile {
194 friend class CodeBlock;
195
196public:
197 ArrayProfile()
198 : ArrayProfile(std::numeric_limits<unsigned>::max())
199 {
200 }
201
202 explicit ArrayProfile(unsigned bytecodeOffset)
203 : m_bytecodeOffset(bytecodeOffset)
204 , m_mayInterceptIndexedAccesses(false)
205 , m_usesOriginalArrayStructures(true)
206 , m_didPerformFirstRunPruning(false)
207 {
208 }
209
210 unsigned bytecodeOffset() const { return m_bytecodeOffset; }
211
212 StructureID* addressOfLastSeenStructureID() { return &m_lastSeenStructureID; }
213 ArrayModes* addressOfArrayModes() { return &m_observedArrayModes; }
214 bool* addressOfMayStoreToHole() { return &m_mayStoreToHole; }
215
216 void setOutOfBounds() { m_outOfBounds = true; }
217 bool* addressOfOutOfBounds() { return &m_outOfBounds; }
218
219 void observeStructure(Structure* structure)
220 {
221 m_lastSeenStructureID = structure->id();
222 }
223
224 void computeUpdatedPrediction(const ConcurrentJSLocker&, CodeBlock*);
225 void computeUpdatedPrediction(const ConcurrentJSLocker&, CodeBlock*, Structure* lastSeenStructure);
226
227 void observeArrayMode(ArrayModes mode) { m_observedArrayModes |= mode; }
228 void observeIndexedRead(VM&, JSCell*, unsigned index);
229
230 ArrayModes observedArrayModes(const ConcurrentJSLocker&) const { return m_observedArrayModes; }
231 bool mayInterceptIndexedAccesses(const ConcurrentJSLocker&) const { return m_mayInterceptIndexedAccesses; }
232
233 bool mayStoreToHole(const ConcurrentJSLocker&) const { return m_mayStoreToHole; }
234 bool outOfBounds(const ConcurrentJSLocker&) const { return m_outOfBounds; }
235
236 bool usesOriginalArrayStructures(const ConcurrentJSLocker&) const { return m_usesOriginalArrayStructures; }
237
238 CString briefDescription(const ConcurrentJSLocker&, CodeBlock*);
239 CString briefDescriptionWithoutUpdating(const ConcurrentJSLocker&);
240
241#if !ASSERT_DISABLED
242 inline bool isValid() const { return m_typeName == s_typeName; }
243#endif
244
245private:
246 friend class LLIntOffsetsExtractor;
247
248 static Structure* polymorphicStructure() { return static_cast<Structure*>(reinterpret_cast<void*>(1)); }
249
250 unsigned m_bytecodeOffset;
251 StructureID m_lastSeenStructureID { 0 };
252 bool m_mayStoreToHole { false }; // This flag may become overloaded to indicate other special cases that were encountered during array access, as it depends on indexing type. Since we currently have basically just one indexing type (two variants of ArrayStorage), this flag for now just means exactly what its name implies.
253 bool m_outOfBounds { false };
254 bool m_mayInterceptIndexedAccesses : 1;
255 bool m_usesOriginalArrayStructures : 1;
256 bool m_didPerformFirstRunPruning : 1;
257 ArrayModes m_observedArrayModes { 0 };
258
259#if !ASSERT_DISABLED
260 static const char* const s_typeName;
261 const char* m_typeName { s_typeName };
262#endif
263};
264
265typedef SegmentedVector<ArrayProfile, 4> ArrayProfileVector;
266
267} // namespace JSC
268