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#include "config.h"
27#include "DFGArrayMode.h"
28
29#if ENABLE(DFG_JIT)
30
31#include "ArrayPrototype.h"
32#include "DFGAbstractValue.h"
33#include "DFGGraph.h"
34#include "JSCInlines.h"
35
36namespace JSC { namespace DFG {
37
38ArrayMode ArrayMode::fromObserved(const ConcurrentJSLocker& locker, ArrayProfile* profile, Array::Action action, bool makeSafe)
39{
40 Array::Class nonArray;
41 if (profile->usesOriginalArrayStructures(locker))
42 nonArray = Array::OriginalNonArray;
43 else
44 nonArray = Array::NonArray;
45
46 auto handleContiguousModes = [&] (Array::Type type, ArrayModes observed) {
47 Array::Class isArray;
48 Array::Conversion converts;
49
50 RELEASE_ASSERT((observed & (asArrayModesIgnoringTypedArrays(toIndexingShape(type)) | asArrayModesIgnoringTypedArrays(toIndexingShape(type) | ArrayClass) | asArrayModesIgnoringTypedArrays(toIndexingShape(type) | ArrayClass | CopyOnWrite))) == observed);
51
52 if (observed & asArrayModesIgnoringTypedArrays(toIndexingShape(type))) {
53 if ((observed & asArrayModesIgnoringTypedArrays(toIndexingShape(type))) == observed)
54 isArray = nonArray;
55 else
56 isArray = Array::PossiblyArray;
57 } else
58 isArray = Array::Array;
59
60 if (action == Array::Write && (observed & asArrayModesIgnoringTypedArrays(toIndexingShape(type) | ArrayClass | CopyOnWrite)))
61 converts = Array::Convert;
62 else
63 converts = Array::AsIs;
64
65 return ArrayMode(type, isArray, converts, action).withProfile(locker, profile, makeSafe);
66 };
67
68 ArrayModes observed = profile->observedArrayModes(locker);
69 switch (observed) {
70 case 0:
71 return ArrayMode(Array::Unprofiled);
72 case asArrayModesIgnoringTypedArrays(NonArray):
73 if (action == Array::Write && !profile->mayInterceptIndexedAccesses(locker))
74 return ArrayMode(Array::SelectUsingArguments, nonArray, Array::OutOfBounds, Array::Convert, action);
75 return ArrayMode(Array::SelectUsingPredictions, nonArray, action).withSpeculationFromProfile(locker, profile, makeSafe);
76
77 case asArrayModesIgnoringTypedArrays(ArrayWithUndecided):
78 if (action == Array::Write)
79 return ArrayMode(Array::SelectUsingArguments, Array::Array, Array::OutOfBounds, Array::Convert, action);
80 return ArrayMode(Array::Undecided, Array::Array, Array::OutOfBounds, Array::AsIs, action).withProfile(locker, profile, makeSafe);
81
82 case asArrayModesIgnoringTypedArrays(NonArray) | asArrayModesIgnoringTypedArrays(ArrayWithUndecided):
83 if (action == Array::Write && !profile->mayInterceptIndexedAccesses(locker))
84 return ArrayMode(Array::SelectUsingArguments, Array::PossiblyArray, Array::OutOfBounds, Array::Convert, action);
85 return ArrayMode(Array::SelectUsingPredictions, action).withSpeculationFromProfile(locker, profile, makeSafe);
86
87 case asArrayModesIgnoringTypedArrays(NonArrayWithInt32):
88 case asArrayModesIgnoringTypedArrays(ArrayWithInt32):
89 case asArrayModesIgnoringTypedArrays(CopyOnWriteArrayWithInt32):
90 case asArrayModesIgnoringTypedArrays(NonArrayWithInt32) | asArrayModesIgnoringTypedArrays(ArrayWithInt32):
91 case asArrayModesIgnoringTypedArrays(NonArrayWithInt32) | asArrayModesIgnoringTypedArrays(CopyOnWriteArrayWithInt32):
92 case asArrayModesIgnoringTypedArrays(ArrayWithInt32) | asArrayModesIgnoringTypedArrays(CopyOnWriteArrayWithInt32):
93 case asArrayModesIgnoringTypedArrays(NonArrayWithInt32) | asArrayModesIgnoringTypedArrays(ArrayWithInt32) | asArrayModesIgnoringTypedArrays(CopyOnWriteArrayWithInt32):
94 return handleContiguousModes(Array::Int32, observed);
95
96 case asArrayModesIgnoringTypedArrays(NonArrayWithDouble):
97 case asArrayModesIgnoringTypedArrays(ArrayWithDouble):
98 case asArrayModesIgnoringTypedArrays(CopyOnWriteArrayWithDouble):
99 case asArrayModesIgnoringTypedArrays(NonArrayWithDouble) | asArrayModesIgnoringTypedArrays(ArrayWithDouble):
100 case asArrayModesIgnoringTypedArrays(NonArrayWithDouble) | asArrayModesIgnoringTypedArrays(CopyOnWriteArrayWithDouble):
101 case asArrayModesIgnoringTypedArrays(ArrayWithDouble) | asArrayModesIgnoringTypedArrays(CopyOnWriteArrayWithDouble):
102 case asArrayModesIgnoringTypedArrays(NonArrayWithDouble) | asArrayModesIgnoringTypedArrays(ArrayWithDouble) | asArrayModesIgnoringTypedArrays(CopyOnWriteArrayWithDouble):
103 return handleContiguousModes(Array::Double, observed);
104
105 case asArrayModesIgnoringTypedArrays(NonArrayWithContiguous):
106 case asArrayModesIgnoringTypedArrays(ArrayWithContiguous):
107 case asArrayModesIgnoringTypedArrays(CopyOnWriteArrayWithContiguous):
108 case asArrayModesIgnoringTypedArrays(NonArrayWithContiguous) | asArrayModesIgnoringTypedArrays(ArrayWithContiguous):
109 case asArrayModesIgnoringTypedArrays(NonArrayWithContiguous) | asArrayModesIgnoringTypedArrays(CopyOnWriteArrayWithContiguous):
110 case asArrayModesIgnoringTypedArrays(ArrayWithContiguous) | asArrayModesIgnoringTypedArrays(CopyOnWriteArrayWithContiguous):
111 case asArrayModesIgnoringTypedArrays(NonArrayWithContiguous) | asArrayModesIgnoringTypedArrays(ArrayWithContiguous) | asArrayModesIgnoringTypedArrays(CopyOnWriteArrayWithContiguous):
112 return handleContiguousModes(Array::Contiguous, observed);
113
114 case asArrayModesIgnoringTypedArrays(NonArrayWithArrayStorage):
115 return ArrayMode(Array::ArrayStorage, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
116 case asArrayModesIgnoringTypedArrays(NonArrayWithSlowPutArrayStorage):
117 case asArrayModesIgnoringTypedArrays(NonArrayWithArrayStorage) | asArrayModesIgnoringTypedArrays(NonArrayWithSlowPutArrayStorage):
118 return ArrayMode(Array::SlowPutArrayStorage, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
119 case asArrayModesIgnoringTypedArrays(ArrayWithArrayStorage):
120 return ArrayMode(Array::ArrayStorage, Array::Array, Array::AsIs, action).withProfile(locker, profile, makeSafe);
121 case asArrayModesIgnoringTypedArrays(ArrayWithSlowPutArrayStorage):
122 case asArrayModesIgnoringTypedArrays(ArrayWithArrayStorage) | asArrayModesIgnoringTypedArrays(ArrayWithSlowPutArrayStorage):
123 return ArrayMode(Array::SlowPutArrayStorage, Array::Array, Array::AsIs, action).withProfile(locker, profile, makeSafe);
124 case asArrayModesIgnoringTypedArrays(NonArrayWithArrayStorage) | asArrayModesIgnoringTypedArrays(ArrayWithArrayStorage):
125 return ArrayMode(Array::ArrayStorage, Array::PossiblyArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
126 case asArrayModesIgnoringTypedArrays(NonArrayWithSlowPutArrayStorage) | asArrayModesIgnoringTypedArrays(ArrayWithSlowPutArrayStorage):
127 case asArrayModesIgnoringTypedArrays(NonArrayWithArrayStorage) | asArrayModesIgnoringTypedArrays(ArrayWithArrayStorage) | asArrayModesIgnoringTypedArrays(NonArrayWithSlowPutArrayStorage) | asArrayModesIgnoringTypedArrays(ArrayWithSlowPutArrayStorage):
128 return ArrayMode(Array::SlowPutArrayStorage, Array::PossiblyArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
129 case Int8ArrayMode:
130 return ArrayMode(Array::Int8Array, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
131 case Int16ArrayMode:
132 return ArrayMode(Array::Int16Array, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
133 case Int32ArrayMode:
134 return ArrayMode(Array::Int32Array, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
135 case Uint8ArrayMode:
136 return ArrayMode(Array::Uint8Array, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
137 case Uint8ClampedArrayMode:
138 return ArrayMode(Array::Uint8ClampedArray, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
139 case Uint16ArrayMode:
140 return ArrayMode(Array::Uint16Array, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
141 case Uint32ArrayMode:
142 return ArrayMode(Array::Uint32Array, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
143 case Float32ArrayMode:
144 return ArrayMode(Array::Float32Array, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
145 case Float64ArrayMode:
146 return ArrayMode(Array::Float64Array, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
147
148 default:
149 // If we have seen multiple TypedArray types, or a TypedArray and non-typed array, it doesn't make sense to try to convert the object since you can't convert typed arrays.
150 if (observed & ALL_TYPED_ARRAY_MODES)
151 return ArrayMode(Array::Generic, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
152
153 if ((observed & asArrayModesIgnoringTypedArrays(NonArray)) && profile->mayInterceptIndexedAccesses(locker))
154 return ArrayMode(Array::SelectUsingPredictions).withSpeculationFromProfile(locker, profile, makeSafe);
155
156 Array::Type type;
157 Array::Class arrayClass;
158
159 if (shouldUseSlowPutArrayStorage(observed))
160 type = Array::SlowPutArrayStorage;
161 else if (shouldUseFastArrayStorage(observed))
162 type = Array::ArrayStorage;
163 else if (shouldUseContiguous(observed))
164 type = Array::Contiguous;
165 else if (shouldUseDouble(observed))
166 type = Array::Double;
167 else if (shouldUseInt32(observed))
168 type = Array::Int32;
169 else
170 type = Array::SelectUsingArguments;
171
172 if (hasSeenArray(observed) && hasSeenNonArray(observed))
173 arrayClass = Array::PossiblyArray;
174 else if (hasSeenArray(observed))
175 arrayClass = Array::Array;
176 else if (hasSeenNonArray(observed))
177 arrayClass = nonArray;
178 else
179 arrayClass = Array::PossiblyArray;
180
181 return ArrayMode(type, arrayClass, Array::Convert, action).withProfile(locker, profile, makeSafe);
182 }
183}
184
185static bool canBecomeGetArrayLength(Graph& graph, Node* node)
186{
187 if (node->op() != GetById)
188 return false;
189 auto uid = graph.identifiers()[node->identifierNumber()];
190 return uid == graph.m_vm.propertyNames->length.impl();
191}
192
193ArrayMode ArrayMode::refine(
194 Graph& graph, Node* node,
195 SpeculatedType base, SpeculatedType index, SpeculatedType value) const
196{
197 if (!base || !index) {
198 // It can be that we had a legitimate arrayMode but no incoming predictions. That'll
199 // happen if we inlined code based on, say, a global variable watchpoint, but later
200 // realized that the callsite could not have possibly executed. It may be worthwhile
201 // to fix that, but for now I'm leaving it as-is.
202 return ArrayMode(Array::ForceExit, action());
203 }
204
205 if (!isInt32Speculation(index))
206 return ArrayMode(Array::Generic, action());
207
208 // If we had exited because of an exotic object behavior, then don't try to specialize.
209 if (graph.hasExitSite(node->origin.semantic, ExoticObjectMode))
210 return ArrayMode(Array::Generic, action());
211
212 // Note: our profiling currently doesn't give us good information in case we have
213 // an unlikely control flow path that sets the base to a non-cell value. Value
214 // profiling and prediction propagation will probably tell us that the value is
215 // either a cell or not, but that doesn't tell us which is more likely: that this
216 // is an array access on a cell (what we want and can optimize) or that the user is
217 // doing a crazy by-val access on a primitive (we can't easily optimize this and
218 // don't want to). So, for now, we assume that if the base is not a cell according
219 // to value profiling, but the array profile tells us something else, then we
220 // should just trust the array profile.
221
222 auto typedArrayResult = [&] (ArrayMode result) -> ArrayMode {
223 if (node->op() == PutByValDirect) {
224 // This is semantically identical to defineOwnProperty({configurable: true, writable:true, enumerable:true}),
225 // which we can't model as a simple store to the typed array since typed array indexed properties
226 // are non-configurable.
227 return ArrayMode(Array::Generic, action());
228 }
229 return result;
230 };
231
232 switch (type()) {
233 case Array::SelectUsingArguments:
234 if (!value)
235 return withType(Array::ForceExit);
236 if (isInt32Speculation(value))
237 return withTypeAndConversion(Array::Int32, Array::Convert);
238 if (isFullNumberSpeculation(value))
239 return withTypeAndConversion(Array::Double, Array::Convert);
240 return withTypeAndConversion(Array::Contiguous, Array::Convert);
241 case Array::Undecided: {
242 // If we have an OriginalArray and the JSArray prototype chain is sane,
243 // any indexed access always return undefined. We have a fast path for that.
244 JSGlobalObject* globalObject = graph.globalObjectFor(node->origin.semantic);
245 Structure* arrayPrototypeStructure = globalObject->arrayPrototype()->structure(graph.m_vm);
246 Structure* objectPrototypeStructure = globalObject->objectPrototype()->structure(graph.m_vm);
247 if ((node->op() == GetByVal || canBecomeGetArrayLength(graph, node))
248 && isJSArrayWithOriginalStructure()
249 && !graph.hasExitSite(node->origin.semantic, OutOfBounds)
250 && arrayPrototypeStructure->transitionWatchpointSetIsStillValid()
251 && objectPrototypeStructure->transitionWatchpointSetIsStillValid()
252 && globalObject->arrayPrototypeChainIsSane()) {
253 graph.registerAndWatchStructureTransition(arrayPrototypeStructure);
254 graph.registerAndWatchStructureTransition(objectPrototypeStructure);
255 if (globalObject->arrayPrototypeChainIsSane())
256 return withSpeculation(Array::SaneChain);
257 }
258 return ArrayMode(Array::Generic, action());
259 }
260 case Array::Int32:
261 if (!value || isInt32Speculation(value))
262 return *this;
263 if (isFullNumberSpeculation(value))
264 return withTypeAndConversion(Array::Double, Array::Convert);
265 return withTypeAndConversion(Array::Contiguous, Array::Convert);
266
267 case Array::Double:
268 if (!value || isFullNumberSpeculation(value))
269 return *this;
270 return withTypeAndConversion(Array::Contiguous, Array::Convert);
271
272 case Array::Contiguous:
273 return *this;
274
275 case Array::Int8Array:
276 case Array::Int16Array:
277 case Array::Int32Array:
278 case Array::Uint8Array:
279 case Array::Uint8ClampedArray:
280 case Array::Uint16Array:
281 case Array::Uint32Array:
282 case Array::Float32Array:
283 case Array::Float64Array:
284 if (node->op() == PutByVal) {
285 if (graph.hasExitSite(node->origin.semantic, OutOfBounds) || !isInBounds())
286 return typedArrayResult(withSpeculation(Array::OutOfBounds));
287 }
288 return typedArrayResult(withSpeculation(Array::InBounds));
289 case Array::Unprofiled:
290 case Array::SelectUsingPredictions: {
291 base &= ~SpecOther;
292
293 if (isStringSpeculation(base))
294 return withType(Array::String);
295
296 if (isDirectArgumentsSpeculation(base) || isScopedArgumentsSpeculation(base)) {
297 // Handle out-of-bounds accesses as generic accesses.
298 Array::Type type = isDirectArgumentsSpeculation(base) ? Array::DirectArguments : Array::ScopedArguments;
299 if (graph.hasExitSite(node->origin.semantic, OutOfBounds) || !isInBounds()) {
300 // FIXME: Support OOB access for ScopedArguments.
301 // https://bugs.webkit.org/show_bug.cgi?id=179596
302 if (type == Array::DirectArguments)
303 return ArrayMode(type, Array::NonArray, Array::OutOfBounds, Array::AsIs, action());
304 return ArrayMode(Array::Generic, action());
305 }
306 return withType(type);
307 }
308
309 ArrayMode result;
310 switch (node->op()) {
311 case PutByVal:
312 if (graph.hasExitSite(node->origin.semantic, OutOfBounds) || !isInBounds())
313 result = withSpeculation(Array::OutOfBounds);
314 else
315 result = withSpeculation(Array::InBounds);
316 break;
317
318 default:
319 result = withSpeculation(Array::InBounds);
320 break;
321 }
322
323 if (isInt8ArraySpeculation(base))
324 return typedArrayResult(result.withType(Array::Int8Array));
325
326 if (isInt16ArraySpeculation(base))
327 return typedArrayResult(result.withType(Array::Int16Array));
328
329 if (isInt32ArraySpeculation(base))
330 return typedArrayResult(result.withType(Array::Int32Array));
331
332 if (isUint8ArraySpeculation(base))
333 return typedArrayResult(result.withType(Array::Uint8Array));
334
335 if (isUint8ClampedArraySpeculation(base))
336 return typedArrayResult(result.withType(Array::Uint8ClampedArray));
337
338 if (isUint16ArraySpeculation(base))
339 return typedArrayResult(result.withType(Array::Uint16Array));
340
341 if (isUint32ArraySpeculation(base))
342 return typedArrayResult(result.withType(Array::Uint32Array));
343
344 if (isFloat32ArraySpeculation(base))
345 return typedArrayResult(result.withType(Array::Float32Array));
346
347 if (isFloat64ArraySpeculation(base))
348 return typedArrayResult(result.withType(Array::Float64Array));
349
350 if (type() == Array::Unprofiled)
351 return ArrayMode(Array::ForceExit, action());
352 return ArrayMode(Array::Generic, action());
353 }
354
355 default:
356 return *this;
357 }
358}
359
360Structure* ArrayMode::originalArrayStructure(Graph& graph, const CodeOrigin& codeOrigin) const
361{
362 JSGlobalObject* globalObject = graph.globalObjectFor(codeOrigin);
363
364 switch (arrayClass()) {
365 case Array::OriginalCopyOnWriteArray: {
366 if (conversion() == Array::AsIs) {
367 switch (type()) {
368 case Array::Int32:
369 return globalObject->originalArrayStructureForIndexingType(CopyOnWriteArrayWithInt32);
370 case Array::Double:
371 return globalObject->originalArrayStructureForIndexingType(CopyOnWriteArrayWithDouble);
372 case Array::Contiguous:
373 return globalObject->originalArrayStructureForIndexingType(CopyOnWriteArrayWithContiguous);
374 default:
375 CRASH();
376 return nullptr;
377 }
378 }
379 FALLTHROUGH;
380 }
381
382 case Array::OriginalArray: {
383 switch (type()) {
384 case Array::Int32:
385 return globalObject->originalArrayStructureForIndexingType(ArrayWithInt32);
386 case Array::Double:
387 return globalObject->originalArrayStructureForIndexingType(ArrayWithDouble);
388 case Array::Contiguous:
389 return globalObject->originalArrayStructureForIndexingType(ArrayWithContiguous);
390 case Array::Undecided:
391 return globalObject->originalArrayStructureForIndexingType(ArrayWithUndecided);
392 case Array::ArrayStorage:
393 return globalObject->originalArrayStructureForIndexingType(ArrayWithArrayStorage);
394 default:
395 CRASH();
396 return nullptr;
397 }
398 }
399
400 case Array::OriginalNonArray: {
401 TypedArrayType type = typedArrayType();
402 if (type == NotTypedArray)
403 return nullptr;
404
405 return globalObject->typedArrayStructureConcurrently(type);
406 }
407
408 default:
409 return nullptr;
410 }
411}
412
413Structure* ArrayMode::originalArrayStructure(Graph& graph, Node* node) const
414{
415 return originalArrayStructure(graph, node->origin.semantic);
416}
417
418bool ArrayMode::alreadyChecked(Graph& graph, Node* node, const AbstractValue& value, IndexingType shape) const
419{
420 ASSERT(isSpecific());
421
422 IndexingType indexingModeMask = IsArray | IndexingShapeMask;
423 if (action() == Array::Write)
424 indexingModeMask |= CopyOnWrite;
425
426 switch (arrayClass()) {
427 case Array::Array: {
428 if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModesIgnoringTypedArrays(shape | IsArray)))
429 return true;
430 if (!value.m_structure.isFinite())
431 return false;
432 for (unsigned i = value.m_structure.size(); i--;) {
433 RegisteredStructure structure = value.m_structure[i];
434 if ((structure->indexingMode() & indexingModeMask) != (shape | IsArray))
435 return false;
436 }
437 return true;
438 }
439
440 // Array::OriginalNonArray can be shown when the value is a TypedArray with original structure.
441 // But here, we already filtered TypedArrays. So, just handle it like a NonArray.
442 case Array::OriginalNonArray:
443 case Array::NonArray: {
444 if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModesIgnoringTypedArrays(shape)))
445 return true;
446 if (!value.m_structure.isFinite())
447 return false;
448 for (unsigned i = value.m_structure.size(); i--;) {
449 RegisteredStructure structure = value.m_structure[i];
450 if ((structure->indexingMode() & indexingModeMask) != shape)
451 return false;
452 }
453 return true;
454 }
455
456 case Array::PossiblyArray: {
457 if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModesIgnoringTypedArrays(shape) | asArrayModesIgnoringTypedArrays(shape | IsArray)))
458 return true;
459 if (!value.m_structure.isFinite())
460 return false;
461 for (unsigned i = value.m_structure.size(); i--;) {
462 RegisteredStructure structure = value.m_structure[i];
463 if ((structure->indexingMode() & (indexingModeMask & ~IsArray)) != shape)
464 return false;
465 }
466 return true;
467 }
468
469 // If ArrayMode is Array::OriginalCopyOnWriteArray or Array::OriginalArray, CheckArray is never emitted. Instead, we always emit CheckStructure.
470 // So, we should perform the same check to the CheckStructure here.
471 case Array::OriginalArray:
472 case Array::OriginalCopyOnWriteArray: {
473 if (!value.m_structure.isFinite())
474 return false;
475 Structure* originalStructure = originalArrayStructure(graph, node);
476 if (value.m_structure.size() != 1)
477 return false;
478 return value.m_structure.onlyStructure().get() == originalStructure;
479 }
480 }
481 return false;
482}
483
484bool ArrayMode::alreadyChecked(Graph& graph, Node* node, const AbstractValue& value) const
485{
486 switch (type()) {
487 case Array::Generic:
488 return true;
489
490 case Array::ForceExit:
491 return false;
492
493 case Array::String:
494 return speculationChecked(value.m_type, SpecString);
495
496 case Array::Int32:
497 return alreadyChecked(graph, node, value, Int32Shape);
498
499 case Array::Double:
500 return alreadyChecked(graph, node, value, DoubleShape);
501
502 case Array::Contiguous:
503 return alreadyChecked(graph, node, value, ContiguousShape);
504
505 case Array::ArrayStorage:
506 return alreadyChecked(graph, node, value, ArrayStorageShape);
507
508 case Array::Undecided:
509 return alreadyChecked(graph, node, value, UndecidedShape);
510
511 case Array::SlowPutArrayStorage:
512 switch (arrayClass()) {
513 case Array::OriginalArray: {
514 CRASH();
515 return false;
516 }
517
518 case Array::Array: {
519 if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModesIgnoringTypedArrays(ArrayWithArrayStorage) | asArrayModesIgnoringTypedArrays(ArrayWithSlowPutArrayStorage)))
520 return true;
521 if (value.m_structure.isTop())
522 return false;
523 for (unsigned i = value.m_structure.size(); i--;) {
524 RegisteredStructure structure = value.m_structure[i];
525 if (!hasAnyArrayStorage(structure->indexingType()))
526 return false;
527 if (!(structure->indexingType() & IsArray))
528 return false;
529 }
530 return true;
531 }
532
533 default: {
534 if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModesIgnoringTypedArrays(NonArrayWithArrayStorage) | asArrayModesIgnoringTypedArrays(ArrayWithArrayStorage) | asArrayModesIgnoringTypedArrays(NonArrayWithSlowPutArrayStorage) | asArrayModesIgnoringTypedArrays(ArrayWithSlowPutArrayStorage)))
535 return true;
536 if (value.m_structure.isTop())
537 return false;
538 for (unsigned i = value.m_structure.size(); i--;) {
539 RegisteredStructure structure = value.m_structure[i];
540 if (!hasAnyArrayStorage(structure->indexingType()))
541 return false;
542 }
543 return true;
544 } }
545
546 case Array::DirectArguments:
547 return speculationChecked(value.m_type, SpecDirectArguments);
548
549 case Array::ScopedArguments:
550 return speculationChecked(value.m_type, SpecScopedArguments);
551
552 case Array::Int8Array:
553 return speculationChecked(value.m_type, SpecInt8Array);
554
555 case Array::Int16Array:
556 return speculationChecked(value.m_type, SpecInt16Array);
557
558 case Array::Int32Array:
559 return speculationChecked(value.m_type, SpecInt32Array);
560
561 case Array::Uint8Array:
562 return speculationChecked(value.m_type, SpecUint8Array);
563
564 case Array::Uint8ClampedArray:
565 return speculationChecked(value.m_type, SpecUint8ClampedArray);
566
567 case Array::Uint16Array:
568 return speculationChecked(value.m_type, SpecUint16Array);
569
570 case Array::Uint32Array:
571 return speculationChecked(value.m_type, SpecUint32Array);
572
573 case Array::Float32Array:
574 return speculationChecked(value.m_type, SpecFloat32Array);
575
576 case Array::Float64Array:
577 return speculationChecked(value.m_type, SpecFloat64Array);
578
579 case Array::AnyTypedArray:
580 return speculationChecked(value.m_type, SpecTypedArrayView);
581
582 case Array::SelectUsingPredictions:
583 case Array::Unprofiled:
584 case Array::SelectUsingArguments:
585 break;
586 }
587
588 CRASH();
589 return false;
590}
591
592const char* arrayActionToString(Array::Action action)
593{
594 switch (action) {
595 case Array::Read:
596 return "Read";
597 case Array::Write:
598 return "Write";
599 default:
600 return "Unknown!";
601 }
602}
603
604const char* arrayTypeToString(Array::Type type)
605{
606 switch (type) {
607 case Array::SelectUsingPredictions:
608 return "SelectUsingPredictions";
609 case Array::SelectUsingArguments:
610 return "SelectUsingArguments";
611 case Array::Unprofiled:
612 return "Unprofiled";
613 case Array::Generic:
614 return "Generic";
615 case Array::ForceExit:
616 return "ForceExit";
617 case Array::String:
618 return "String";
619 case Array::Undecided:
620 return "Undecided";
621 case Array::Int32:
622 return "Int32";
623 case Array::Double:
624 return "Double";
625 case Array::Contiguous:
626 return "Contiguous";
627 case Array::ArrayStorage:
628 return "ArrayStorage";
629 case Array::SlowPutArrayStorage:
630 return "SlowPutArrayStorage";
631 case Array::DirectArguments:
632 return "DirectArguments";
633 case Array::ScopedArguments:
634 return "ScopedArguments";
635 case Array::Int8Array:
636 return "Int8Array";
637 case Array::Int16Array:
638 return "Int16Array";
639 case Array::Int32Array:
640 return "Int32Array";
641 case Array::Uint8Array:
642 return "Uint8Array";
643 case Array::Uint8ClampedArray:
644 return "Uint8ClampedArray";
645 case Array::Uint16Array:
646 return "Uint16Array";
647 case Array::Uint32Array:
648 return "Uint32Array";
649 case Array::Float32Array:
650 return "Float32Array";
651 case Array::Float64Array:
652 return "Float64Array";
653 case Array::AnyTypedArray:
654 return "AnyTypedArray";
655 default:
656 // Better to return something then it is to crash. Remember, this method
657 // is being called from our main diagnostic tool, the IR dumper. It's like
658 // a stack trace. So if we get here then probably something has already
659 // gone wrong.
660 return "Unknown!";
661 }
662}
663
664const char* arrayClassToString(Array::Class arrayClass)
665{
666 switch (arrayClass) {
667 case Array::Array:
668 return "Array";
669 case Array::OriginalArray:
670 return "OriginalArray";
671 case Array::OriginalCopyOnWriteArray:
672 return "OriginalCopyOnWriteArray";
673 case Array::NonArray:
674 return "NonArray";
675 case Array::OriginalNonArray:
676 return "OriginalNonArray";
677 case Array::PossiblyArray:
678 return "PossiblyArray";
679 default:
680 return "Unknown!";
681 }
682}
683
684const char* arraySpeculationToString(Array::Speculation speculation)
685{
686 switch (speculation) {
687 case Array::SaneChain:
688 return "SaneChain";
689 case Array::InBounds:
690 return "InBounds";
691 case Array::ToHole:
692 return "ToHole";
693 case Array::OutOfBounds:
694 return "OutOfBounds";
695 default:
696 return "Unknown!";
697 }
698}
699
700const char* arrayConversionToString(Array::Conversion conversion)
701{
702 switch (conversion) {
703 case Array::AsIs:
704 return "AsIs";
705 case Array::Convert:
706 return "Convert";
707 default:
708 return "Unknown!";
709 }
710}
711
712IndexingType toIndexingShape(Array::Type type)
713{
714 switch (type) {
715 case Array::Int32:
716 return Int32Shape;
717 case Array::Double:
718 return DoubleShape;
719 case Array::Contiguous:
720 return ContiguousShape;
721 case Array::Undecided:
722 return UndecidedShape;
723 case Array::ArrayStorage:
724 return ArrayStorageShape;
725 case Array::SlowPutArrayStorage:
726 return SlowPutArrayStorageShape;
727 default:
728 return NoIndexingShape;
729 }
730}
731
732TypedArrayType toTypedArrayType(Array::Type type)
733{
734 switch (type) {
735 case Array::Int8Array:
736 return TypeInt8;
737 case Array::Int16Array:
738 return TypeInt16;
739 case Array::Int32Array:
740 return TypeInt32;
741 case Array::Uint8Array:
742 return TypeUint8;
743 case Array::Uint8ClampedArray:
744 return TypeUint8Clamped;
745 case Array::Uint16Array:
746 return TypeUint16;
747 case Array::Uint32Array:
748 return TypeUint32;
749 case Array::Float32Array:
750 return TypeFloat32;
751 case Array::Float64Array:
752 return TypeFloat64;
753 case Array::AnyTypedArray:
754 RELEASE_ASSERT_NOT_REACHED();
755 return NotTypedArray;
756 default:
757 return NotTypedArray;
758 }
759}
760
761Array::Type toArrayType(TypedArrayType type)
762{
763 switch (type) {
764 case TypeInt8:
765 return Array::Int8Array;
766 case TypeInt16:
767 return Array::Int16Array;
768 case TypeInt32:
769 return Array::Int32Array;
770 case TypeUint8:
771 return Array::Uint8Array;
772 case TypeUint8Clamped:
773 return Array::Uint8ClampedArray;
774 case TypeUint16:
775 return Array::Uint16Array;
776 case TypeUint32:
777 return Array::Uint32Array;
778 case TypeFloat32:
779 return Array::Float32Array;
780 case TypeFloat64:
781 return Array::Float64Array;
782 default:
783 return Array::Generic;
784 }
785}
786
787Array::Type refineTypedArrayType(Array::Type oldType, TypedArrayType newType)
788{
789 if (oldType == Array::Generic)
790 return oldType;
791 Array::Type newArrayType = toArrayType(newType);
792 if (newArrayType == Array::Generic)
793 return newArrayType;
794
795 if (oldType != newArrayType)
796 return Array::AnyTypedArray;
797 return oldType;
798}
799
800bool permitsBoundsCheckLowering(Array::Type type)
801{
802 switch (type) {
803 case Array::Int32:
804 case Array::Double:
805 case Array::Contiguous:
806 case Array::ArrayStorage:
807 case Array::SlowPutArrayStorage:
808 case Array::Int8Array:
809 case Array::Int16Array:
810 case Array::Int32Array:
811 case Array::Uint8Array:
812 case Array::Uint8ClampedArray:
813 case Array::Uint16Array:
814 case Array::Uint32Array:
815 case Array::Float32Array:
816 case Array::Float64Array:
817 case Array::AnyTypedArray:
818 return true;
819 default:
820 // These don't allow for bounds check lowering either because the bounds
821 // check isn't a speculation (like String, sort of) or because the type implies an impure access.
822 return false;
823 }
824}
825
826bool ArrayMode::permitsBoundsCheckLowering() const
827{
828 return DFG::permitsBoundsCheckLowering(type()) && isInBounds();
829}
830
831void ArrayMode::dump(PrintStream& out) const
832{
833 out.print(type(), "+", arrayClass(), "+", speculation(), "+", conversion(), "+", action());
834}
835
836} } // namespace JSC::DFG
837
838namespace WTF {
839
840void printInternal(PrintStream& out, JSC::DFG::Array::Action action)
841{
842 out.print(JSC::DFG::arrayActionToString(action));
843}
844
845void printInternal(PrintStream& out, JSC::DFG::Array::Type type)
846{
847 out.print(JSC::DFG::arrayTypeToString(type));
848}
849
850void printInternal(PrintStream& out, JSC::DFG::Array::Class arrayClass)
851{
852 out.print(JSC::DFG::arrayClassToString(arrayClass));
853}
854
855void printInternal(PrintStream& out, JSC::DFG::Array::Speculation speculation)
856{
857 out.print(JSC::DFG::arraySpeculationToString(speculation));
858}
859
860void printInternal(PrintStream& out, JSC::DFG::Array::Conversion conversion)
861{
862 out.print(JSC::DFG::arrayConversionToString(conversion));
863}
864
865} // namespace WTF
866
867#endif // ENABLE(DFG_JIT)
868
869