1 | /* |
2 | * Copyright (C) 2013-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 | #pragma once |
27 | |
28 | #if ENABLE(DFG_JIT) |
29 | |
30 | #include "ArrayConstructor.h" |
31 | #include "ArrayPrototype.h" |
32 | #include "DFGAbstractInterpreter.h" |
33 | #include "DFGAbstractInterpreterClobberState.h" |
34 | #include "DOMJITGetterSetter.h" |
35 | #include "DOMJITSignature.h" |
36 | #include "GetByStatus.h" |
37 | #include "GetterSetter.h" |
38 | #include "HashMapImpl.h" |
39 | #include "JITOperations.h" |
40 | #include "JSAsyncGenerator.h" |
41 | #include "JSGenerator.h" |
42 | #include "JSImmutableButterfly.h" |
43 | #include "JSInternalPromise.h" |
44 | #include "JSInternalPromiseConstructor.h" |
45 | #include "JSPromiseConstructor.h" |
46 | #include "MathCommon.h" |
47 | #include "NumberConstructor.h" |
48 | #include "Operations.h" |
49 | #include "PutByIdStatus.h" |
50 | #include "StringObject.h" |
51 | #include "StructureCache.h" |
52 | #include "StructureRareDataInlines.h" |
53 | #include <wtf/BooleanLattice.h> |
54 | #include <wtf/CheckedArithmetic.h> |
55 | |
56 | namespace JSC { namespace DFG { |
57 | |
58 | template<typename AbstractStateType> |
59 | AbstractInterpreter<AbstractStateType>::AbstractInterpreter(Graph& graph, AbstractStateType& state) |
60 | : m_codeBlock(graph.m_codeBlock) |
61 | , m_graph(graph) |
62 | , m_vm(m_graph.m_vm) |
63 | , m_state(state) |
64 | { |
65 | if (m_graph.m_form == SSA) |
66 | m_phiChildren = makeUnique<PhiChildren>(m_graph); |
67 | } |
68 | |
69 | template<typename AbstractStateType> |
70 | AbstractInterpreter<AbstractStateType>::~AbstractInterpreter() |
71 | { |
72 | } |
73 | |
74 | template<typename AbstractStateType> |
75 | typename AbstractInterpreter<AbstractStateType>::BooleanResult |
76 | AbstractInterpreter<AbstractStateType>::booleanResult( |
77 | Node* node, AbstractValue& value) |
78 | { |
79 | JSValue childConst = value.value(); |
80 | if (childConst) { |
81 | if (childConst.toBoolean(m_codeBlock->globalObjectFor(node->origin.semantic))) |
82 | return DefinitelyTrue; |
83 | return DefinitelyFalse; |
84 | } |
85 | |
86 | // Next check if we can fold because we know that the source is an object or string and does not equal undefined. |
87 | if (isCellSpeculation(value.m_type) && !value.m_structure.isTop()) { |
88 | bool allTrue = true; |
89 | for (unsigned i = value.m_structure.size(); i--;) { |
90 | RegisteredStructure structure = value.m_structure[i]; |
91 | if (structure->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node->origin.semantic)) |
92 | || structure->typeInfo().type() == StringType) { |
93 | allTrue = false; |
94 | break; |
95 | } |
96 | } |
97 | if (allTrue) |
98 | return DefinitelyTrue; |
99 | } |
100 | |
101 | return UnknownBooleanResult; |
102 | } |
103 | |
104 | template<typename AbstractStateType> |
105 | void AbstractInterpreter<AbstractStateType>::startExecuting() |
106 | { |
107 | ASSERT(m_state.block()); |
108 | ASSERT(m_state.isValid()); |
109 | |
110 | m_state.setClobberState(AbstractInterpreterClobberState::NotClobbered); |
111 | } |
112 | |
113 | template<typename AbstractStateType> |
114 | class AbstractInterpreterExecuteEdgesFunc { |
115 | public: |
116 | AbstractInterpreterExecuteEdgesFunc(AbstractInterpreter<AbstractStateType>& interpreter) |
117 | : m_interpreter(interpreter) |
118 | { |
119 | } |
120 | |
121 | // This func is manually written out so that we can put ALWAYS_INLINE on it. |
122 | ALWAYS_INLINE void operator()(Edge& edge) const |
123 | { |
124 | m_interpreter.filterEdgeByUse(edge); |
125 | } |
126 | |
127 | private: |
128 | AbstractInterpreter<AbstractStateType>& m_interpreter; |
129 | }; |
130 | |
131 | template<typename AbstractStateType> |
132 | void AbstractInterpreter<AbstractStateType>::executeEdges(Node* node) |
133 | { |
134 | m_graph.doToChildren(node, AbstractInterpreterExecuteEdgesFunc<AbstractStateType>(*this)); |
135 | } |
136 | |
137 | template<typename AbstractStateType> |
138 | void AbstractInterpreter<AbstractStateType>::executeKnownEdgeTypes(Node* node) |
139 | { |
140 | // Some use kinds are required to not have checks, because we know somehow that the incoming |
141 | // value will already have the type we want. In those cases, AI may not be smart enough to |
142 | // prove that this is indeed the case. But the existance of the edge is enough to prove that |
143 | // it is indeed the case. Taking advantage of this is not optional, since otherwise the DFG |
144 | // and FTL backends may emit checks in a node that lacks a valid exit origin. |
145 | m_graph.doToChildren( |
146 | node, |
147 | [&] (Edge& edge) { |
148 | if (mayHaveTypeCheck(edge.useKind())) |
149 | return; |
150 | |
151 | filterEdgeByUse(edge); |
152 | }); |
153 | } |
154 | |
155 | template<typename AbstractStateType> |
156 | ALWAYS_INLINE void AbstractInterpreter<AbstractStateType>::filterByType(Edge& edge, SpeculatedType type) |
157 | { |
158 | AbstractValue& value = m_state.forNodeWithoutFastForward(edge); |
159 | if (value.isType(type)) { |
160 | m_state.setProofStatus(edge, IsProved); |
161 | return; |
162 | } |
163 | m_state.setProofStatus(edge, NeedsCheck); |
164 | m_state.fastForwardAndFilterUnproven(value, type); |
165 | } |
166 | |
167 | template<typename AbstractStateType> |
168 | void AbstractInterpreter<AbstractStateType>::verifyEdge(Node* node, Edge edge) |
169 | { |
170 | if (!(m_state.forNodeWithoutFastForward(edge).m_type & ~typeFilterFor(edge.useKind()))) |
171 | return; |
172 | |
173 | DFG_CRASH(m_graph, node, toCString("Edge verification error: " , node, "->" , edge, " was expected to have type " , SpeculationDump(typeFilterFor(edge.useKind())), " but has type " , SpeculationDump(forNode(edge).m_type), " (" , forNode(edge).m_type, ")" ).data(), AbstractInterpreterInvalidType, node->op(), edge->op(), edge.useKind(), forNode(edge).m_type); |
174 | } |
175 | |
176 | template<typename AbstractStateType> |
177 | void AbstractInterpreter<AbstractStateType>::verifyEdges(Node* node) |
178 | { |
179 | DFG_NODE_DO_TO_CHILDREN(m_graph, node, verifyEdge); |
180 | } |
181 | |
182 | enum class ToThisResult { |
183 | Identity, |
184 | Undefined, |
185 | GlobalThis, |
186 | Dynamic, |
187 | }; |
188 | inline ToThisResult isToThisAnIdentity(VM& vm, bool isStrictMode, AbstractValue& valueForNode) |
189 | { |
190 | // We look at the type first since that will cover most cases and does not require iterating all the structures. |
191 | if (isStrictMode) { |
192 | if (valueForNode.m_type && !(valueForNode.m_type & SpecObjectOther)) |
193 | return ToThisResult::Identity; |
194 | } else { |
195 | if (valueForNode.m_type && !(valueForNode.m_type & (~SpecObject | SpecObjectOther))) |
196 | return ToThisResult::Identity; |
197 | } |
198 | |
199 | if (JSValue value = valueForNode.value()) { |
200 | if (value.isCell()) { |
201 | auto* toThisMethod = value.asCell()->classInfo(vm)->methodTable.toThis; |
202 | if (toThisMethod == &JSObject::toThis) |
203 | return ToThisResult::Identity; |
204 | if (toThisMethod == &JSScope::toThis) { |
205 | if (isStrictMode) |
206 | return ToThisResult::Undefined; |
207 | return ToThisResult::GlobalThis; |
208 | } |
209 | } |
210 | } |
211 | |
212 | if ((isStrictMode || (valueForNode.m_type && !(valueForNode.m_type & ~SpecObject))) && valueForNode.m_structure.isFinite()) { |
213 | bool allStructuresAreJSScope = !valueForNode.m_structure.isClear(); |
214 | bool overridesToThis = false; |
215 | valueForNode.m_structure.forEach([&](RegisteredStructure structure) { |
216 | TypeInfo type = structure->typeInfo(); |
217 | ASSERT(type.isObject() || type.type() == StringType || type.type() == SymbolType || type.type() == BigIntType); |
218 | if (!isStrictMode) |
219 | ASSERT(type.isObject()); |
220 | // We don't need to worry about strings/symbols here since either: |
221 | // 1) We are in strict mode and strings/symbols are not wrapped |
222 | // 2) The AI has proven that the type of this is a subtype of object |
223 | if (type.isObject() && type.overridesToThis()) |
224 | overridesToThis = true; |
225 | |
226 | // If all the structures are JSScope's ones, we know the details of JSScope::toThis() operation. |
227 | allStructuresAreJSScope &= structure->classInfo()->methodTable.toThis == JSScope::info()->methodTable.toThis; |
228 | }); |
229 | if (!overridesToThis) |
230 | return ToThisResult::Identity; |
231 | if (allStructuresAreJSScope) { |
232 | if (isStrictMode) |
233 | return ToThisResult::Undefined; |
234 | return ToThisResult::GlobalThis; |
235 | } |
236 | } |
237 | |
238 | return ToThisResult::Dynamic; |
239 | } |
240 | |
241 | template<typename AbstractStateType> |
242 | bool AbstractInterpreter<AbstractStateType>::handleConstantBinaryBitwiseOp(Node* node) |
243 | { |
244 | JSValue left = forNode(node->child1()).value(); |
245 | JSValue right = forNode(node->child2()).value(); |
246 | if (left && right && left.isInt32() && right.isInt32()) { |
247 | int32_t a = left.asInt32(); |
248 | int32_t b = right.asInt32(); |
249 | if (node->isBinaryUseKind(UntypedUse)) |
250 | didFoldClobberWorld(); |
251 | NodeType op = node->op(); |
252 | switch (op) { |
253 | case ValueBitAnd: |
254 | case ArithBitAnd: |
255 | setConstant(node, JSValue(a & b)); |
256 | break; |
257 | case ValueBitOr: |
258 | case ArithBitOr: |
259 | setConstant(node, JSValue(a | b)); |
260 | break; |
261 | case ValueBitXor: |
262 | case ArithBitXor: |
263 | setConstant(node, JSValue(a ^ b)); |
264 | break; |
265 | case ArithBitRShift: |
266 | case ValueBitRShift: |
267 | setConstant(node, JSValue(a >> (static_cast<uint32_t>(b) & 0x1f))); |
268 | break; |
269 | case ValueBitLShift: |
270 | case ArithBitLShift: |
271 | setConstant(node, JSValue(a << (static_cast<uint32_t>(b) & 0x1f))); |
272 | break; |
273 | case BitURShift: |
274 | setConstant(node, JSValue(static_cast<int32_t>(static_cast<uint32_t>(a) >> (static_cast<uint32_t>(b) & 0x1f)))); |
275 | break; |
276 | default: |
277 | RELEASE_ASSERT_NOT_REACHED(); |
278 | break; |
279 | } |
280 | |
281 | return true; |
282 | } |
283 | |
284 | return false; |
285 | } |
286 | |
287 | template<typename AbstractStateType> |
288 | bool AbstractInterpreter<AbstractStateType>::handleConstantDivOp(Node* node) |
289 | { |
290 | JSValue left = forNode(node->child1()).value(); |
291 | JSValue right = forNode(node->child2()).value(); |
292 | |
293 | if (left && right) { |
294 | NodeType op = node->op(); |
295 | bool isDivOperation = op == ValueDiv || op == ArithDiv; |
296 | |
297 | // Only possible case of ValueOp below is UntypedUse, |
298 | // so we need to reflect clobberize rules. |
299 | bool isClobbering = op == ValueDiv || op == ValueMod; |
300 | |
301 | if (left.isInt32() && right.isInt32()) { |
302 | double doubleResult; |
303 | if (isDivOperation) |
304 | doubleResult = left.asNumber() / right.asNumber(); |
305 | else |
306 | doubleResult = fmod(left.asNumber(), right.asNumber()); |
307 | |
308 | if (node->hasArithMode()) { |
309 | if (!shouldCheckOverflow(node->arithMode())) |
310 | doubleResult = toInt32(doubleResult); |
311 | else if (!shouldCheckNegativeZero(node->arithMode())) |
312 | doubleResult += 0; // Sanitizes zero. |
313 | } |
314 | |
315 | JSValue valueResult = jsNumber(doubleResult); |
316 | if (valueResult.isInt32()) { |
317 | if (isClobbering) |
318 | didFoldClobberWorld(); |
319 | setConstant(node, valueResult); |
320 | return true; |
321 | } |
322 | } else if (left.isNumber() && right.isNumber()) { |
323 | if (isClobbering) |
324 | didFoldClobberWorld(); |
325 | |
326 | if (isDivOperation) |
327 | setConstant(node, jsDoubleNumber(left.asNumber() / right.asNumber())); |
328 | else |
329 | setConstant(node, jsDoubleNumber(fmod(left.asNumber(), right.asNumber()))); |
330 | |
331 | return true; |
332 | } |
333 | } |
334 | |
335 | return false; |
336 | } |
337 | |
338 | template<typename AbstractStateType> |
339 | bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimit, Node* node) |
340 | { |
341 | verifyEdges(node); |
342 | |
343 | m_state.createValueForNode(node); |
344 | |
345 | switch (node->op()) { |
346 | case JSConstant: |
347 | case DoubleConstant: |
348 | case Int52Constant: { |
349 | setBuiltInConstant(node, *node->constant()); |
350 | break; |
351 | } |
352 | |
353 | case LazyJSConstant: { |
354 | LazyJSValue value = node->lazyJSValue(); |
355 | switch (value.kind()) { |
356 | case LazyJSValue::KnownValue: |
357 | setConstant(node, value.value()->value()); |
358 | break; |
359 | case LazyJSValue::SingleCharacterString: |
360 | case LazyJSValue::KnownStringImpl: |
361 | case LazyJSValue::NewStringImpl: |
362 | setTypeForNode(node, SpecString); |
363 | break; |
364 | } |
365 | break; |
366 | } |
367 | |
368 | case IdentityWithProfile: |
369 | case Identity: { |
370 | setForNode(node, forNode(node->child1())); |
371 | if (forNode(node).value()) |
372 | m_state.setShouldTryConstantFolding(true); |
373 | break; |
374 | } |
375 | |
376 | case ExtractCatchLocal: |
377 | case ExtractOSREntryLocal: { |
378 | makeBytecodeTopForNode(node); |
379 | break; |
380 | } |
381 | |
382 | case GetLocal: { |
383 | VariableAccessData* variableAccessData = node->variableAccessData(); |
384 | AbstractValue value = m_state.operand(variableAccessData->local().offset()); |
385 | // The value in the local should already be checked. |
386 | DFG_ASSERT(m_graph, node, value.isType(typeFilterFor(variableAccessData->flushFormat()))); |
387 | if (value.value()) |
388 | m_state.setShouldTryConstantFolding(true); |
389 | setForNode(node, value); |
390 | break; |
391 | } |
392 | |
393 | case GetStack: { |
394 | StackAccessData* data = node->stackAccessData(); |
395 | AbstractValue value = m_state.operand(data->local); |
396 | // The value in the local should already be checked. |
397 | DFG_ASSERT(m_graph, node, value.isType(typeFilterFor(data->format))); |
398 | if (value.value()) |
399 | m_state.setShouldTryConstantFolding(true); |
400 | setForNode(node, value); |
401 | break; |
402 | } |
403 | |
404 | case SetLocal: { |
405 | m_state.operand(node->local()) = forNode(node->child1()); |
406 | break; |
407 | } |
408 | |
409 | case PutStack: { |
410 | m_state.operand(node->stackAccessData()->local) = forNode(node->child1()); |
411 | break; |
412 | } |
413 | |
414 | case MovHint: { |
415 | // Don't need to do anything. A MovHint only informs us about what would have happened |
416 | // in bytecode, but this code is just concerned with what is actually happening during |
417 | // DFG execution. |
418 | break; |
419 | } |
420 | |
421 | case KillStack: { |
422 | // This is just a hint telling us that the OSR state of the local is no longer inside the |
423 | // flushed data. |
424 | break; |
425 | } |
426 | |
427 | case SetArgumentDefinitely: |
428 | case SetArgumentMaybe: |
429 | // Assert that the state of arguments has been set. SetArgumentDefinitely/SetArgumentMaybe means |
430 | // that someone set the argument values out-of-band, and currently this always means setting to a |
431 | // non-clear value. |
432 | ASSERT(!m_state.operand(node->local()).isClear()); |
433 | break; |
434 | |
435 | case InitializeEntrypointArguments: { |
436 | unsigned entrypointIndex = node->entrypointIndex(); |
437 | const Vector<FlushFormat>& argumentFormats = m_graph.m_argumentFormats[entrypointIndex]; |
438 | for (unsigned argument = 0; argument < argumentFormats.size(); ++argument) { |
439 | AbstractValue& value = m_state.argument(argument); |
440 | switch (argumentFormats[argument]) { |
441 | case FlushedInt32: |
442 | value.setNonCellType(SpecInt32Only); |
443 | break; |
444 | case FlushedBoolean: |
445 | value.setNonCellType(SpecBoolean); |
446 | break; |
447 | case FlushedCell: |
448 | value.setType(m_graph, SpecCellCheck); |
449 | break; |
450 | case FlushedJSValue: |
451 | value.makeBytecodeTop(); |
452 | break; |
453 | default: |
454 | DFG_CRASH(m_graph, node, "Bad flush format for argument" ); |
455 | break; |
456 | } |
457 | } |
458 | break; |
459 | } |
460 | |
461 | case LoadVarargs: |
462 | case ForwardVarargs: { |
463 | // FIXME: ForwardVarargs should check if the count becomes known, and if it does, it should turn |
464 | // itself into a straight-line sequence of GetStack/PutStack. |
465 | // https://bugs.webkit.org/show_bug.cgi?id=143071 |
466 | switch (node->op()) { |
467 | case LoadVarargs: |
468 | clobberWorld(); |
469 | break; |
470 | case ForwardVarargs: |
471 | break; |
472 | default: |
473 | DFG_CRASH(m_graph, node, "Bad opcode" ); |
474 | break; |
475 | } |
476 | LoadVarargsData* data = node->loadVarargsData(); |
477 | m_state.operand(data->count).setNonCellType(SpecInt32Only); |
478 | for (unsigned i = data->limit - 1; i--;) |
479 | m_state.operand(data->start.offset() + i).makeHeapTop(); |
480 | break; |
481 | } |
482 | |
483 | case ValueBitNot: { |
484 | JSValue operand = forNode(node->child1()).value(); |
485 | if (operand && operand.isInt32()) { |
486 | didFoldClobberWorld(); |
487 | int32_t a = operand.asInt32(); |
488 | setConstant(node, JSValue(~a)); |
489 | break; |
490 | } |
491 | |
492 | if (node->child1().useKind() == BigIntUse) |
493 | setTypeForNode(node, SpecBigInt); |
494 | else { |
495 | clobberWorld(); |
496 | setTypeForNode(node, SpecInt32Only | SpecBigInt); |
497 | } |
498 | |
499 | break; |
500 | } |
501 | |
502 | case ArithBitNot: { |
503 | JSValue operand = forNode(node->child1()).value(); |
504 | if (operand && operand.isInt32()) { |
505 | int32_t a = operand.asInt32(); |
506 | setConstant(node, JSValue(~a)); |
507 | break; |
508 | } |
509 | |
510 | setNonCellTypeForNode(node, SpecInt32Only); |
511 | break; |
512 | } |
513 | |
514 | case ValueBitXor: |
515 | case ValueBitAnd: |
516 | case ValueBitOr: |
517 | case ValueBitRShift: |
518 | case ValueBitLShift: { |
519 | if (handleConstantBinaryBitwiseOp(node)) |
520 | break; |
521 | |
522 | if (node->binaryUseKind() == BigIntUse) |
523 | setTypeForNode(node, SpecBigInt); |
524 | else { |
525 | clobberWorld(); |
526 | setTypeForNode(node, SpecInt32Only | SpecBigInt); |
527 | } |
528 | break; |
529 | } |
530 | |
531 | case ArithBitAnd: |
532 | case ArithBitOr: |
533 | case ArithBitXor: |
534 | case ArithBitRShift: |
535 | case ArithBitLShift: |
536 | case BitURShift: { |
537 | if (node->child1().useKind() == UntypedUse || node->child2().useKind() == UntypedUse) { |
538 | clobberWorld(); |
539 | setNonCellTypeForNode(node, SpecInt32Only); |
540 | break; |
541 | } |
542 | |
543 | if (handleConstantBinaryBitwiseOp(node)) |
544 | break; |
545 | |
546 | if (node->op() == ArithBitAnd |
547 | && (isBoolInt32Speculation(forNode(node->child1()).m_type) || |
548 | isBoolInt32Speculation(forNode(node->child2()).m_type))) { |
549 | setNonCellTypeForNode(node, SpecBoolInt32); |
550 | break; |
551 | } |
552 | |
553 | setNonCellTypeForNode(node, SpecInt32Only); |
554 | break; |
555 | } |
556 | |
557 | case UInt32ToNumber: { |
558 | JSValue child = forNode(node->child1()).value(); |
559 | if (doesOverflow(node->arithMode())) { |
560 | if (enableInt52()) { |
561 | if (child && child.isAnyInt()) { |
562 | int64_t machineInt = child.asAnyInt(); |
563 | setConstant(node, jsNumber(static_cast<uint32_t>(machineInt))); |
564 | break; |
565 | } |
566 | setNonCellTypeForNode(node, SpecInt52Any); |
567 | break; |
568 | } |
569 | if (child && child.isInt32()) { |
570 | uint32_t value = child.asInt32(); |
571 | setConstant(node, jsNumber(value)); |
572 | break; |
573 | } |
574 | setNonCellTypeForNode(node, SpecAnyIntAsDouble); |
575 | break; |
576 | } |
577 | if (child && child.isInt32()) { |
578 | int32_t value = child.asInt32(); |
579 | if (value >= 0) { |
580 | setConstant(node, jsNumber(value)); |
581 | break; |
582 | } |
583 | } |
584 | setNonCellTypeForNode(node, SpecInt32Only); |
585 | break; |
586 | } |
587 | |
588 | case BooleanToNumber: { |
589 | JSValue concreteValue = forNode(node->child1()).value(); |
590 | if (concreteValue) { |
591 | if (concreteValue.isBoolean()) |
592 | setConstant(node, jsNumber(concreteValue.asBoolean())); |
593 | else |
594 | setConstant(node, *m_graph.freeze(concreteValue)); |
595 | break; |
596 | } |
597 | AbstractValue& value = forNode(node); |
598 | value = forNode(node->child1()); |
599 | if (node->child1().useKind() == UntypedUse && !(value.m_type & ~SpecBoolean)) |
600 | m_state.setShouldTryConstantFolding(true); |
601 | if (value.m_type & SpecBoolean) { |
602 | value.merge(SpecBoolInt32); |
603 | value.filter(~SpecBoolean); |
604 | } |
605 | break; |
606 | } |
607 | |
608 | case DoubleAsInt32: { |
609 | JSValue child = forNode(node->child1()).value(); |
610 | if (child && child.isNumber()) { |
611 | double asDouble = child.asNumber(); |
612 | int32_t asInt = JSC::toInt32(asDouble); |
613 | if (bitwise_cast<int64_t>(static_cast<double>(asInt)) == bitwise_cast<int64_t>(asDouble)) { |
614 | setConstant(node, JSValue(asInt)); |
615 | break; |
616 | } |
617 | } |
618 | setNonCellTypeForNode(node, SpecInt32Only); |
619 | break; |
620 | } |
621 | |
622 | case ValueToInt32: { |
623 | JSValue child = forNode(node->child1()).value(); |
624 | if (child) { |
625 | if (child.isNumber()) { |
626 | if (child.isInt32()) |
627 | setConstant(node, child); |
628 | else |
629 | setConstant(node, JSValue(JSC::toInt32(child.asDouble()))); |
630 | break; |
631 | } |
632 | if (child.isBoolean()) { |
633 | setConstant(node, jsNumber(child.asBoolean())); |
634 | break; |
635 | } |
636 | if (child.isUndefinedOrNull()) { |
637 | setConstant(node, jsNumber(0)); |
638 | break; |
639 | } |
640 | } |
641 | |
642 | if (isBooleanSpeculation(forNode(node->child1()).m_type)) { |
643 | setNonCellTypeForNode(node, SpecBoolInt32); |
644 | break; |
645 | } |
646 | |
647 | setNonCellTypeForNode(node, SpecInt32Only); |
648 | break; |
649 | } |
650 | |
651 | case DoubleRep: { |
652 | JSValue child = forNode(node->child1()).value(); |
653 | if (Optional<double> number = child.toNumberFromPrimitive()) { |
654 | setConstant(node, jsDoubleNumber(*number)); |
655 | break; |
656 | } |
657 | |
658 | SpeculatedType type = forNode(node->child1()).m_type; |
659 | switch (node->child1().useKind()) { |
660 | case NotCellUse: { |
661 | if (type & SpecOther) { |
662 | type &= ~SpecOther; |
663 | type |= SpecDoublePureNaN | SpecBoolInt32; // Null becomes zero, undefined becomes NaN. |
664 | } |
665 | if (type & SpecBoolean) { |
666 | type &= ~SpecBoolean; |
667 | type |= SpecBoolInt32; // True becomes 1, false becomes 0. |
668 | } |
669 | type &= SpecBytecodeNumber; |
670 | break; |
671 | } |
672 | |
673 | case Int52RepUse: |
674 | case NumberUse: |
675 | case RealNumberUse: |
676 | break; |
677 | |
678 | default: |
679 | RELEASE_ASSERT_NOT_REACHED(); |
680 | } |
681 | setNonCellTypeForNode(node, type); |
682 | forNode(node).fixTypeForRepresentation(m_graph, node); |
683 | break; |
684 | } |
685 | |
686 | case Int52Rep: { |
687 | JSValue child = forNode(node->child1()).value(); |
688 | if (child && child.isAnyInt()) { |
689 | setConstant(node, child); |
690 | break; |
691 | } |
692 | |
693 | setTypeForNode(node, forNode(node->child1()).m_type); |
694 | forNode(node).fixTypeForRepresentation(m_graph, node); |
695 | break; |
696 | } |
697 | |
698 | case ValueRep: { |
699 | JSValue value = forNode(node->child1()).value(); |
700 | if (value) { |
701 | setConstant(node, value); |
702 | break; |
703 | } |
704 | |
705 | setTypeForNode(node, forNode(node->child1()).m_type & ~SpecDoubleImpureNaN); |
706 | forNode(node).fixTypeForRepresentation(m_graph, node); |
707 | break; |
708 | } |
709 | |
710 | case ValueSub: |
711 | case ValueAdd: { |
712 | DFG_ASSERT(m_graph, node, node->binaryUseKind() == UntypedUse || node->binaryUseKind() == BigIntUse); |
713 | if (node->binaryUseKind() == BigIntUse) |
714 | setTypeForNode(node, SpecBigInt); |
715 | else { |
716 | clobberWorld(); |
717 | setTypeForNode(node, SpecString | SpecBytecodeNumber | SpecBigInt); |
718 | } |
719 | break; |
720 | } |
721 | |
722 | case StrCat: { |
723 | setTypeForNode(node, SpecString); |
724 | break; |
725 | } |
726 | |
727 | case ArithAdd: { |
728 | JSValue left = forNode(node->child1()).value(); |
729 | JSValue right = forNode(node->child2()).value(); |
730 | switch (node->binaryUseKind()) { |
731 | case Int32Use: |
732 | if (left && right && left.isInt32() && right.isInt32()) { |
733 | if (!shouldCheckOverflow(node->arithMode())) { |
734 | setConstant(node, jsNumber(left.asInt32() + right.asInt32())); |
735 | break; |
736 | } |
737 | JSValue result = jsNumber(left.asNumber() + right.asNumber()); |
738 | if (result.isInt32()) { |
739 | setConstant(node, result); |
740 | break; |
741 | } |
742 | } |
743 | setNonCellTypeForNode(node, SpecInt32Only); |
744 | break; |
745 | case Int52RepUse: |
746 | if (left && right && left.isAnyInt() && right.isAnyInt()) { |
747 | JSValue result = jsNumber(left.asAnyInt() + right.asAnyInt()); |
748 | if (result.isAnyInt()) { |
749 | setConstant(node, result); |
750 | break; |
751 | } |
752 | } |
753 | setNonCellTypeForNode(node, SpecInt52Any); |
754 | break; |
755 | case DoubleRepUse: |
756 | if (left && right && left.isNumber() && right.isNumber()) { |
757 | setConstant(node, jsDoubleNumber(left.asNumber() + right.asNumber())); |
758 | break; |
759 | } |
760 | setNonCellTypeForNode(node, |
761 | typeOfDoubleSum( |
762 | forNode(node->child1()).m_type, forNode(node->child2()).m_type)); |
763 | break; |
764 | default: |
765 | RELEASE_ASSERT_NOT_REACHED(); |
766 | break; |
767 | } |
768 | break; |
769 | } |
770 | |
771 | case AtomicsIsLockFree: { |
772 | if (node->child1().useKind() != Int32Use) |
773 | clobberWorld(); |
774 | setNonCellTypeForNode(node, SpecBoolInt32); |
775 | break; |
776 | } |
777 | |
778 | case ArithClz32: { |
779 | JSValue operand = forNode(node->child1()).value(); |
780 | if (Optional<double> number = operand.toNumberFromPrimitive()) { |
781 | switch (node->child1().useKind()) { |
782 | case Int32Use: |
783 | case KnownInt32Use: |
784 | break; |
785 | default: |
786 | didFoldClobberWorld(); |
787 | break; |
788 | } |
789 | uint32_t value = toUInt32(*number); |
790 | setConstant(node, jsNumber(clz(value))); |
791 | break; |
792 | } |
793 | switch (node->child1().useKind()) { |
794 | case Int32Use: |
795 | case KnownInt32Use: |
796 | break; |
797 | default: |
798 | clobberWorld(); |
799 | break; |
800 | } |
801 | setNonCellTypeForNode(node, SpecInt32Only); |
802 | break; |
803 | } |
804 | |
805 | case MakeRope: { |
806 | unsigned numberOfRemovedChildren = 0; |
807 | for (unsigned i = 0; i < AdjacencyList::Size; ++i) { |
808 | Edge& edge = node->children.child(i); |
809 | if (!edge) |
810 | break; |
811 | JSValue childConstant = m_state.forNode(edge).value(); |
812 | if (!childConstant) |
813 | continue; |
814 | if (!childConstant.isString()) |
815 | continue; |
816 | if (asString(childConstant)->length()) |
817 | continue; |
818 | ++numberOfRemovedChildren; |
819 | } |
820 | |
821 | if (numberOfRemovedChildren) |
822 | m_state.setShouldTryConstantFolding(true); |
823 | setForNode(node, m_vm.stringStructure.get()); |
824 | break; |
825 | } |
826 | |
827 | case ArithSub: { |
828 | JSValue left = forNode(node->child1()).value(); |
829 | JSValue right = forNode(node->child2()).value(); |
830 | switch (node->binaryUseKind()) { |
831 | case Int32Use: |
832 | if (left && right && left.isInt32() && right.isInt32()) { |
833 | if (!shouldCheckOverflow(node->arithMode())) { |
834 | setConstant(node, jsNumber(left.asInt32() - right.asInt32())); |
835 | break; |
836 | } |
837 | JSValue result = jsNumber(left.asNumber() - right.asNumber()); |
838 | if (result.isInt32()) { |
839 | setConstant(node, result); |
840 | break; |
841 | } |
842 | } |
843 | setNonCellTypeForNode(node, SpecInt32Only); |
844 | break; |
845 | case Int52RepUse: |
846 | if (left && right && left.isAnyInt() && right.isAnyInt()) { |
847 | JSValue result = jsNumber(left.asAnyInt() - right.asAnyInt()); |
848 | if (result.isAnyInt()) { |
849 | setConstant(node, result); |
850 | break; |
851 | } |
852 | } |
853 | setNonCellTypeForNode(node, SpecInt52Any); |
854 | break; |
855 | case DoubleRepUse: |
856 | if (left && right && left.isNumber() && right.isNumber()) { |
857 | setConstant(node, jsDoubleNumber(left.asNumber() - right.asNumber())); |
858 | break; |
859 | } |
860 | setNonCellTypeForNode(node, |
861 | typeOfDoubleDifference( |
862 | forNode(node->child1()).m_type, forNode(node->child2()).m_type)); |
863 | break; |
864 | case UntypedUse: |
865 | clobberWorld(); |
866 | setNonCellTypeForNode(node, SpecBytecodeNumber); |
867 | break; |
868 | default: |
869 | RELEASE_ASSERT_NOT_REACHED(); |
870 | break; |
871 | } |
872 | break; |
873 | } |
874 | |
875 | case ValueNegate: { |
876 | clobberWorld(); |
877 | setTypeForNode(node, SpecBytecodeNumber | SpecBigInt); |
878 | break; |
879 | } |
880 | |
881 | case ArithNegate: { |
882 | JSValue child = forNode(node->child1()).value(); |
883 | switch (node->child1().useKind()) { |
884 | case Int32Use: |
885 | if (child && child.isInt32()) { |
886 | if (!shouldCheckOverflow(node->arithMode())) { |
887 | setConstant(node, jsNumber(-child.asInt32())); |
888 | break; |
889 | } |
890 | double doubleResult; |
891 | if (shouldCheckNegativeZero(node->arithMode())) |
892 | doubleResult = -child.asNumber(); |
893 | else |
894 | doubleResult = 0 - child.asNumber(); |
895 | JSValue valueResult = jsNumber(doubleResult); |
896 | if (valueResult.isInt32()) { |
897 | setConstant(node, valueResult); |
898 | break; |
899 | } |
900 | } |
901 | setNonCellTypeForNode(node, SpecInt32Only); |
902 | break; |
903 | case Int52RepUse: |
904 | if (child && child.isAnyInt()) { |
905 | double doubleResult; |
906 | if (shouldCheckNegativeZero(node->arithMode())) |
907 | doubleResult = -child.asNumber(); |
908 | else |
909 | doubleResult = 0 - child.asNumber(); |
910 | JSValue valueResult = jsNumber(doubleResult); |
911 | if (valueResult.isAnyInt()) { |
912 | setConstant(node, valueResult); |
913 | break; |
914 | } |
915 | } |
916 | setNonCellTypeForNode(node, SpecInt52Any); |
917 | break; |
918 | case DoubleRepUse: |
919 | if (child && child.isNumber()) { |
920 | setConstant(node, jsDoubleNumber(-child.asNumber())); |
921 | break; |
922 | } |
923 | setNonCellTypeForNode(node, |
924 | typeOfDoubleNegation( |
925 | forNode(node->child1()).m_type)); |
926 | break; |
927 | default: |
928 | RELEASE_ASSERT_NOT_REACHED(); |
929 | break; |
930 | } |
931 | break; |
932 | } |
933 | |
934 | case Inc: |
935 | case Dec: { |
936 | // FIXME: support some form of constant folding here. |
937 | // https://bugs.webkit.org/show_bug.cgi?id=204258 |
938 | switch (node->child1().useKind()) { |
939 | case Int32Use: |
940 | setNonCellTypeForNode(node, SpecInt32Only); |
941 | break; |
942 | case Int52RepUse: |
943 | setNonCellTypeForNode(node, SpecInt52Any); |
944 | break; |
945 | case DoubleRepUse: |
946 | setNonCellTypeForNode(node, typeOfDoubleIncOrDec(forNode(node->child1()).m_type)); |
947 | break; |
948 | case BigIntUse: |
949 | setTypeForNode(node, SpecBigInt); |
950 | break; |
951 | default: |
952 | setTypeForNode(node, SpecBytecodeNumber | SpecBigInt); |
953 | clobberWorld(); // Because of the call to ToNumeric() |
954 | break; |
955 | } |
956 | break; |
957 | } |
958 | |
959 | case ValuePow: { |
960 | JSValue childX = forNode(node->child1()).value(); |
961 | JSValue childY = forNode(node->child2()).value(); |
962 | if (childX && childY && childX.isNumber() && childY.isNumber()) { |
963 | // We need to call `didFoldClobberWorld` here because this path is only possible |
964 | // when node->useKind is UntypedUse. In the case of BigIntUse, children will be |
965 | // cleared by `AbstractInterpreter::executeEffects`. |
966 | didFoldClobberWorld(); |
967 | // Our boxing scheme here matches what we do in operationValuePow. |
968 | setConstant(node, jsNumber(operationMathPow(childX.asNumber(), childY.asNumber()))); |
969 | break; |
970 | } |
971 | |
972 | if (node->binaryUseKind() == BigIntUse) |
973 | setTypeForNode(node, SpecBigInt); |
974 | else { |
975 | clobberWorld(); |
976 | setTypeForNode(node, SpecBytecodeNumber | SpecBigInt); |
977 | } |
978 | break; |
979 | } |
980 | |
981 | case ValueMul: { |
982 | if (node->binaryUseKind() == BigIntUse) |
983 | setTypeForNode(node, SpecBigInt); |
984 | else { |
985 | clobberWorld(); |
986 | setTypeForNode(node, SpecBytecodeNumber | SpecBigInt); |
987 | } |
988 | break; |
989 | } |
990 | |
991 | case ArithMul: { |
992 | JSValue left = forNode(node->child1()).value(); |
993 | JSValue right = forNode(node->child2()).value(); |
994 | switch (node->binaryUseKind()) { |
995 | case Int32Use: |
996 | if (left && right && left.isInt32() && right.isInt32()) { |
997 | if (!shouldCheckOverflow(node->arithMode())) { |
998 | setConstant(node, jsNumber(left.asInt32() * right.asInt32())); |
999 | break; |
1000 | } |
1001 | double doubleResult = left.asNumber() * right.asNumber(); |
1002 | if (!shouldCheckNegativeZero(node->arithMode())) |
1003 | doubleResult += 0; // Sanitizes zero. |
1004 | JSValue valueResult = jsNumber(doubleResult); |
1005 | if (valueResult.isInt32()) { |
1006 | setConstant(node, valueResult); |
1007 | break; |
1008 | } |
1009 | } |
1010 | setNonCellTypeForNode(node, SpecInt32Only); |
1011 | break; |
1012 | case Int52RepUse: |
1013 | if (left && right && left.isAnyInt() && right.isAnyInt()) { |
1014 | double doubleResult = left.asNumber() * right.asNumber(); |
1015 | if (!shouldCheckNegativeZero(node->arithMode())) |
1016 | doubleResult += 0; |
1017 | JSValue valueResult = jsNumber(doubleResult); |
1018 | if (valueResult.isAnyInt()) { |
1019 | setConstant(node, valueResult); |
1020 | break; |
1021 | } |
1022 | } |
1023 | setNonCellTypeForNode(node, SpecInt52Any); |
1024 | break; |
1025 | case DoubleRepUse: |
1026 | if (left && right && left.isNumber() && right.isNumber()) { |
1027 | setConstant(node, jsDoubleNumber(left.asNumber() * right.asNumber())); |
1028 | break; |
1029 | } |
1030 | setNonCellTypeForNode(node, |
1031 | typeOfDoubleProduct( |
1032 | forNode(node->child1()).m_type, forNode(node->child2()).m_type)); |
1033 | break; |
1034 | default: |
1035 | RELEASE_ASSERT_NOT_REACHED(); |
1036 | break; |
1037 | } |
1038 | break; |
1039 | } |
1040 | |
1041 | case ValueMod: |
1042 | case ValueDiv: { |
1043 | if (handleConstantDivOp(node)) |
1044 | break; |
1045 | |
1046 | if (node->binaryUseKind() == BigIntUse) |
1047 | setTypeForNode(node, SpecBigInt); |
1048 | else { |
1049 | clobberWorld(); |
1050 | setTypeForNode(node, SpecBytecodeNumber | SpecBigInt); |
1051 | } |
1052 | break; |
1053 | } |
1054 | |
1055 | case ArithMod: |
1056 | case ArithDiv: { |
1057 | if (handleConstantDivOp(node)) |
1058 | break; |
1059 | |
1060 | switch (node->binaryUseKind()) { |
1061 | case Int32Use: |
1062 | setNonCellTypeForNode(node, SpecInt32Only); |
1063 | break; |
1064 | case DoubleRepUse: |
1065 | if (node->op() == ArithDiv) { |
1066 | setNonCellTypeForNode(node, |
1067 | typeOfDoubleQuotient( |
1068 | forNode(node->child1()).m_type, forNode(node->child2()).m_type)); |
1069 | } else { |
1070 | setNonCellTypeForNode(node, |
1071 | typeOfDoubleBinaryOp( |
1072 | forNode(node->child1()).m_type, forNode(node->child2()).m_type)); |
1073 | } |
1074 | |
1075 | break; |
1076 | default: |
1077 | RELEASE_ASSERT_NOT_REACHED(); |
1078 | break; |
1079 | } |
1080 | break; |
1081 | } |
1082 | |
1083 | case ArithMin: { |
1084 | JSValue left = forNode(node->child1()).value(); |
1085 | JSValue right = forNode(node->child2()).value(); |
1086 | switch (node->binaryUseKind()) { |
1087 | case Int32Use: |
1088 | if (left && right && left.isInt32() && right.isInt32()) { |
1089 | setConstant(node, jsNumber(std::min(left.asInt32(), right.asInt32()))); |
1090 | break; |
1091 | } |
1092 | setNonCellTypeForNode(node, SpecInt32Only); |
1093 | break; |
1094 | case DoubleRepUse: |
1095 | if (left && right && left.isNumber() && right.isNumber()) { |
1096 | double a = left.asNumber(); |
1097 | double b = right.asNumber(); |
1098 | setConstant(node, jsDoubleNumber(a < b ? a : (b <= a ? b : a + b))); |
1099 | break; |
1100 | } |
1101 | setNonCellTypeForNode(node, |
1102 | typeOfDoubleMinMax( |
1103 | forNode(node->child1()).m_type, forNode(node->child2()).m_type)); |
1104 | break; |
1105 | default: |
1106 | RELEASE_ASSERT_NOT_REACHED(); |
1107 | break; |
1108 | } |
1109 | break; |
1110 | } |
1111 | |
1112 | case ArithMax: { |
1113 | JSValue left = forNode(node->child1()).value(); |
1114 | JSValue right = forNode(node->child2()).value(); |
1115 | switch (node->binaryUseKind()) { |
1116 | case Int32Use: |
1117 | if (left && right && left.isInt32() && right.isInt32()) { |
1118 | setConstant(node, jsNumber(std::max(left.asInt32(), right.asInt32()))); |
1119 | break; |
1120 | } |
1121 | setNonCellTypeForNode(node, SpecInt32Only); |
1122 | break; |
1123 | case DoubleRepUse: |
1124 | if (left && right && left.isNumber() && right.isNumber()) { |
1125 | double a = left.asNumber(); |
1126 | double b = right.asNumber(); |
1127 | setConstant(node, jsDoubleNumber(a > b ? a : (b >= a ? b : a + b))); |
1128 | break; |
1129 | } |
1130 | setNonCellTypeForNode(node, |
1131 | typeOfDoubleMinMax( |
1132 | forNode(node->child1()).m_type, forNode(node->child2()).m_type)); |
1133 | break; |
1134 | default: |
1135 | RELEASE_ASSERT_NOT_REACHED(); |
1136 | break; |
1137 | } |
1138 | break; |
1139 | } |
1140 | |
1141 | case ArithAbs: { |
1142 | JSValue child = forNode(node->child1()).value(); |
1143 | switch (node->child1().useKind()) { |
1144 | case Int32Use: |
1145 | if (Optional<double> number = child.toNumberFromPrimitive()) { |
1146 | JSValue result = jsNumber(fabs(*number)); |
1147 | if (result.isInt32()) { |
1148 | setConstant(node, result); |
1149 | break; |
1150 | } |
1151 | } |
1152 | setNonCellTypeForNode(node, SpecInt32Only); |
1153 | break; |
1154 | case DoubleRepUse: |
1155 | if (Optional<double> number = child.toNumberFromPrimitive()) { |
1156 | setConstant(node, jsDoubleNumber(fabs(*number))); |
1157 | break; |
1158 | } |
1159 | setNonCellTypeForNode(node, typeOfDoubleAbs(forNode(node->child1()).m_type)); |
1160 | break; |
1161 | default: |
1162 | DFG_ASSERT(m_graph, node, node->child1().useKind() == UntypedUse, node->child1().useKind()); |
1163 | clobberWorld(); |
1164 | setNonCellTypeForNode(node, SpecBytecodeNumber); |
1165 | break; |
1166 | } |
1167 | break; |
1168 | } |
1169 | |
1170 | case ArithPow: { |
1171 | JSValue childY = forNode(node->child2()).value(); |
1172 | if (childY && childY.isNumber()) { |
1173 | if (!childY.asNumber()) { |
1174 | setConstant(node, jsDoubleNumber(1)); |
1175 | break; |
1176 | } |
1177 | |
1178 | JSValue childX = forNode(node->child1()).value(); |
1179 | if (childX && childX.isNumber()) { |
1180 | setConstant(node, jsDoubleNumber(operationMathPow(childX.asNumber(), childY.asNumber()))); |
1181 | break; |
1182 | } |
1183 | } |
1184 | setNonCellTypeForNode(node, typeOfDoublePow(forNode(node->child1()).m_type, forNode(node->child2()).m_type)); |
1185 | break; |
1186 | } |
1187 | |
1188 | case ArithRandom: { |
1189 | setNonCellTypeForNode(node, SpecDoubleReal); |
1190 | break; |
1191 | } |
1192 | |
1193 | case ArithRound: |
1194 | case ArithFloor: |
1195 | case ArithCeil: |
1196 | case ArithTrunc: { |
1197 | JSValue operand = forNode(node->child1()).value(); |
1198 | if (Optional<double> number = operand.toNumberFromPrimitive()) { |
1199 | if (node->child1().useKind() != DoubleRepUse) |
1200 | didFoldClobberWorld(); |
1201 | |
1202 | double roundedValue = 0; |
1203 | if (node->op() == ArithRound) |
1204 | roundedValue = jsRound(*number); |
1205 | else if (node->op() == ArithFloor) |
1206 | roundedValue = floor(*number); |
1207 | else if (node->op() == ArithCeil) |
1208 | roundedValue = ceil(*number); |
1209 | else { |
1210 | ASSERT(node->op() == ArithTrunc); |
1211 | roundedValue = trunc(*number); |
1212 | } |
1213 | |
1214 | if (node->child1().useKind() == UntypedUse) { |
1215 | setConstant(node, jsNumber(roundedValue)); |
1216 | break; |
1217 | } |
1218 | if (producesInteger(node->arithRoundingMode())) { |
1219 | int32_t roundedValueAsInt32 = static_cast<int32_t>(roundedValue); |
1220 | if (roundedValueAsInt32 == roundedValue) { |
1221 | if (shouldCheckNegativeZero(node->arithRoundingMode())) { |
1222 | if (roundedValueAsInt32 || !std::signbit(roundedValue)) { |
1223 | setConstant(node, jsNumber(roundedValueAsInt32)); |
1224 | break; |
1225 | } |
1226 | } else { |
1227 | setConstant(node, jsNumber(roundedValueAsInt32)); |
1228 | break; |
1229 | } |
1230 | } |
1231 | } else { |
1232 | setConstant(node, jsDoubleNumber(roundedValue)); |
1233 | break; |
1234 | } |
1235 | } |
1236 | if (node->child1().useKind() == DoubleRepUse) { |
1237 | if (producesInteger(node->arithRoundingMode())) |
1238 | setNonCellTypeForNode(node, SpecInt32Only); |
1239 | else if (node->child1().useKind() == DoubleRepUse) |
1240 | setNonCellTypeForNode(node, typeOfDoubleRounding(forNode(node->child1()).m_type)); |
1241 | } else { |
1242 | DFG_ASSERT(m_graph, node, node->child1().useKind() == UntypedUse, node->child1().useKind()); |
1243 | clobberWorld(); |
1244 | setNonCellTypeForNode(node, SpecBytecodeNumber); |
1245 | } |
1246 | break; |
1247 | } |
1248 | |
1249 | case ArithSqrt: |
1250 | executeDoubleUnaryOpEffects(node, sqrt); |
1251 | break; |
1252 | |
1253 | case ArithFRound: |
1254 | executeDoubleUnaryOpEffects(node, [](double value) -> double { return static_cast<float>(value); }); |
1255 | break; |
1256 | |
1257 | case ArithUnary: |
1258 | executeDoubleUnaryOpEffects(node, arithUnaryFunction(node->arithUnaryType())); |
1259 | break; |
1260 | |
1261 | case LogicalNot: { |
1262 | switch (booleanResult(node, forNode(node->child1()))) { |
1263 | case DefinitelyTrue: |
1264 | setConstant(node, jsBoolean(false)); |
1265 | break; |
1266 | case DefinitelyFalse: |
1267 | setConstant(node, jsBoolean(true)); |
1268 | break; |
1269 | default: |
1270 | setNonCellTypeForNode(node, SpecBoolean); |
1271 | break; |
1272 | } |
1273 | break; |
1274 | } |
1275 | |
1276 | case MapHash: { |
1277 | if (JSValue key = forNode(node->child1()).value()) { |
1278 | if (Optional<uint32_t> hash = concurrentJSMapHash(key)) { |
1279 | // Although C++ code uses uint32_t for the hash, the closest type in DFG IR is Int32 |
1280 | // and that's what MapHash returns. So, we have to cast to int32_t to avoid large |
1281 | // unsigned values becoming doubles. This casting between signed and unsigned |
1282 | // happens in the assembly code we emit when we don't constant fold this node. |
1283 | setConstant(node, jsNumber(static_cast<int32_t>(*hash))); |
1284 | break; |
1285 | } |
1286 | } |
1287 | setNonCellTypeForNode(node, SpecInt32Only); |
1288 | break; |
1289 | } |
1290 | |
1291 | case NormalizeMapKey: { |
1292 | if (JSValue key = forNode(node->child1()).value()) { |
1293 | setConstant(node, *m_graph.freeze(normalizeMapKey(key))); |
1294 | break; |
1295 | } |
1296 | |
1297 | SpeculatedType typeMaybeNormalized = (SpecFullNumber & ~SpecInt32Only); |
1298 | if (!(forNode(node->child1()).m_type & typeMaybeNormalized)) { |
1299 | m_state.setShouldTryConstantFolding(true); |
1300 | forNode(node) = forNode(node->child1()); |
1301 | break; |
1302 | } |
1303 | |
1304 | makeHeapTopForNode(node); |
1305 | break; |
1306 | } |
1307 | |
1308 | case StringValueOf: { |
1309 | clobberWorld(); |
1310 | setTypeForNode(node, SpecString); |
1311 | break; |
1312 | } |
1313 | |
1314 | case StringSlice: { |
1315 | setTypeForNode(node, SpecString); |
1316 | break; |
1317 | } |
1318 | |
1319 | case ToLowerCase: { |
1320 | setTypeForNode(node, SpecString); |
1321 | break; |
1322 | } |
1323 | |
1324 | case LoadKeyFromMapBucket: |
1325 | case LoadValueFromMapBucket: |
1326 | case ExtractValueFromWeakMapGet: |
1327 | makeHeapTopForNode(node); |
1328 | break; |
1329 | |
1330 | case GetMapBucket: |
1331 | case GetMapBucketHead: |
1332 | if (node->child1().useKind() == MapObjectUse) |
1333 | setForNode(node, m_vm.hashMapBucketMapStructure.get()); |
1334 | else { |
1335 | ASSERT(node->child1().useKind() == SetObjectUse); |
1336 | setForNode(node, m_vm.hashMapBucketSetStructure.get()); |
1337 | } |
1338 | break; |
1339 | |
1340 | case GetMapBucketNext: |
1341 | if (node->bucketOwnerType() == BucketOwnerType::Map) |
1342 | setForNode(node, m_vm.hashMapBucketMapStructure.get()); |
1343 | else { |
1344 | ASSERT(node->bucketOwnerType() == BucketOwnerType::Set); |
1345 | setForNode(node, m_vm.hashMapBucketSetStructure.get()); |
1346 | } |
1347 | break; |
1348 | |
1349 | case SetAdd: |
1350 | setForNode(node, m_vm.hashMapBucketSetStructure.get()); |
1351 | break; |
1352 | |
1353 | case MapSet: |
1354 | setForNode(node, m_vm.hashMapBucketMapStructure.get()); |
1355 | break; |
1356 | |
1357 | case WeakSetAdd: |
1358 | case WeakMapSet: |
1359 | break; |
1360 | |
1361 | case WeakMapGet: |
1362 | makeBytecodeTopForNode(node); |
1363 | break; |
1364 | |
1365 | case IsEmpty: |
1366 | case IsUndefined: |
1367 | case IsUndefinedOrNull: |
1368 | case IsBoolean: |
1369 | case IsNumber: |
1370 | case NumberIsInteger: |
1371 | case IsObject: |
1372 | case IsObjectOrNull: |
1373 | case IsFunction: |
1374 | case IsCellWithType: |
1375 | case IsTypedArrayView: { |
1376 | AbstractValue child = forNode(node->child1()); |
1377 | if (child.value()) { |
1378 | bool constantWasSet = true; |
1379 | switch (node->op()) { |
1380 | case IsCellWithType: |
1381 | setConstant(node, jsBoolean(child.value().isCell() && child.value().asCell()->type() == node->queriedType())); |
1382 | break; |
1383 | case IsUndefined: |
1384 | setConstant(node, jsBoolean( |
1385 | child.value().isCell() |
1386 | ? child.value().asCell()->structure(m_vm)->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node->origin.semantic)) |
1387 | : child.value().isUndefined())); |
1388 | break; |
1389 | case IsUndefinedOrNull: |
1390 | setConstant(node, jsBoolean(child.value().isUndefinedOrNull())); |
1391 | break; |
1392 | case IsBoolean: |
1393 | setConstant(node, jsBoolean(child.value().isBoolean())); |
1394 | break; |
1395 | case IsNumber: |
1396 | setConstant(node, jsBoolean(child.value().isNumber())); |
1397 | break; |
1398 | case NumberIsInteger: |
1399 | setConstant(node, jsBoolean(NumberConstructor::isIntegerImpl(child.value()))); |
1400 | break; |
1401 | case IsObject: |
1402 | setConstant(node, jsBoolean(child.value().isObject())); |
1403 | break; |
1404 | case IsObjectOrNull: |
1405 | if (child.value().isObject()) { |
1406 | JSObject* object = asObject(child.value()); |
1407 | if (object->type() == JSFunctionType) |
1408 | setConstant(node, jsBoolean(false)); |
1409 | else if (!(object->inlineTypeFlags() & OverridesGetCallData)) |
1410 | setConstant(node, jsBoolean(!child.value().asCell()->structure(m_vm)->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node->origin.semantic)))); |
1411 | else { |
1412 | // FIXME: This could just call getCallData. |
1413 | // https://bugs.webkit.org/show_bug.cgi?id=144457 |
1414 | constantWasSet = false; |
1415 | } |
1416 | } else |
1417 | setConstant(node, jsBoolean(child.value().isNull())); |
1418 | break; |
1419 | case IsFunction: |
1420 | if (child.value().isObject()) { |
1421 | JSObject* object = asObject(child.value()); |
1422 | if (object->type() == JSFunctionType) |
1423 | setConstant(node, jsBoolean(true)); |
1424 | else if (!(object->inlineTypeFlags() & OverridesGetCallData)) |
1425 | setConstant(node, jsBoolean(false)); |
1426 | else { |
1427 | // FIXME: This could just call getCallData. |
1428 | // https://bugs.webkit.org/show_bug.cgi?id=144457 |
1429 | constantWasSet = false; |
1430 | } |
1431 | } else |
1432 | setConstant(node, jsBoolean(false)); |
1433 | break; |
1434 | case IsEmpty: |
1435 | setConstant(node, jsBoolean(child.value().isEmpty())); |
1436 | break; |
1437 | case IsTypedArrayView: |
1438 | setConstant(node, jsBoolean(child.value().isObject() && isTypedView(child.value().getObject()->classInfo(m_vm)->typedArrayStorageType))); |
1439 | break; |
1440 | default: |
1441 | constantWasSet = false; |
1442 | break; |
1443 | } |
1444 | if (constantWasSet) |
1445 | break; |
1446 | } |
1447 | |
1448 | if (forNode(node->child1()).m_structure.isFinite()) { |
1449 | bool constantWasSet = false; |
1450 | switch (node->op()) { |
1451 | case IsCellWithType: { |
1452 | bool ok = true; |
1453 | Optional<bool> result; |
1454 | forNode(node->child1()).m_structure.forEach( |
1455 | [&](RegisteredStructure structure) { |
1456 | bool matched = structure->typeInfo().type() == node->queriedType(); |
1457 | if (!result) |
1458 | result = matched; |
1459 | else { |
1460 | if (result.value() != matched) |
1461 | ok = false; |
1462 | } |
1463 | }); |
1464 | if (ok && result) { |
1465 | setConstant(node, jsBoolean(result.value())); |
1466 | constantWasSet = true; |
1467 | } |
1468 | break; |
1469 | } |
1470 | default: |
1471 | break; |
1472 | } |
1473 | if (constantWasSet) |
1474 | break; |
1475 | } |
1476 | |
1477 | // FIXME: This code should really use AbstractValue::isType() and |
1478 | // AbstractValue::couldBeType(). |
1479 | // https://bugs.webkit.org/show_bug.cgi?id=146870 |
1480 | |
1481 | bool constantWasSet = false; |
1482 | switch (node->op()) { |
1483 | case IsEmpty: { |
1484 | if (child.m_type && !(child.m_type & SpecEmpty)) { |
1485 | setConstant(node, jsBoolean(false)); |
1486 | constantWasSet = true; |
1487 | break; |
1488 | } |
1489 | |
1490 | if (child.m_type && !(child.m_type & ~SpecEmpty)) { |
1491 | setConstant(node, jsBoolean(true)); |
1492 | constantWasSet = true; |
1493 | break; |
1494 | } |
1495 | |
1496 | break; |
1497 | } |
1498 | case IsUndefined: |
1499 | // FIXME: Use the masquerades-as-undefined watchpoint thingy. |
1500 | // https://bugs.webkit.org/show_bug.cgi?id=144456 |
1501 | |
1502 | if (!(child.m_type & (SpecOther | SpecObjectOther))) { |
1503 | setConstant(node, jsBoolean(false)); |
1504 | constantWasSet = true; |
1505 | break; |
1506 | } |
1507 | |
1508 | break; |
1509 | case IsUndefinedOrNull: |
1510 | if (!(child.m_type & ~SpecOther)) { |
1511 | setConstant(node, jsBoolean(true)); |
1512 | constantWasSet = true; |
1513 | break; |
1514 | } |
1515 | |
1516 | if (!(child.m_type & SpecOther)) { |
1517 | setConstant(node, jsBoolean(false)); |
1518 | constantWasSet = true; |
1519 | break; |
1520 | } |
1521 | break; |
1522 | case IsBoolean: |
1523 | if (!(child.m_type & ~SpecBoolean)) { |
1524 | setConstant(node, jsBoolean(true)); |
1525 | constantWasSet = true; |
1526 | break; |
1527 | } |
1528 | |
1529 | if (!(child.m_type & SpecBoolean)) { |
1530 | setConstant(node, jsBoolean(false)); |
1531 | constantWasSet = true; |
1532 | break; |
1533 | } |
1534 | |
1535 | break; |
1536 | case IsNumber: |
1537 | if (!(child.m_type & ~SpecFullNumber)) { |
1538 | setConstant(node, jsBoolean(true)); |
1539 | constantWasSet = true; |
1540 | break; |
1541 | } |
1542 | |
1543 | if (!(child.m_type & SpecFullNumber)) { |
1544 | setConstant(node, jsBoolean(false)); |
1545 | constantWasSet = true; |
1546 | break; |
1547 | } |
1548 | |
1549 | break; |
1550 | |
1551 | case NumberIsInteger: |
1552 | if (!(child.m_type & ~SpecInt32Only)) { |
1553 | setConstant(node, jsBoolean(true)); |
1554 | constantWasSet = true; |
1555 | break; |
1556 | } |
1557 | |
1558 | if (!(child.m_type & SpecFullNumber)) { |
1559 | setConstant(node, jsBoolean(false)); |
1560 | constantWasSet = true; |
1561 | break; |
1562 | } |
1563 | |
1564 | break; |
1565 | |
1566 | case IsObject: |
1567 | if (!(child.m_type & ~SpecObject)) { |
1568 | setConstant(node, jsBoolean(true)); |
1569 | constantWasSet = true; |
1570 | break; |
1571 | } |
1572 | |
1573 | if (!(child.m_type & SpecObject)) { |
1574 | setConstant(node, jsBoolean(false)); |
1575 | constantWasSet = true; |
1576 | break; |
1577 | } |
1578 | |
1579 | break; |
1580 | case IsObjectOrNull: |
1581 | // FIXME: Use the masquerades-as-undefined watchpoint thingy. |
1582 | // https://bugs.webkit.org/show_bug.cgi?id=144456 |
1583 | |
1584 | // These expressions are complicated to parse. A helpful way to parse this is that |
1585 | // "!(T & ~S)" means "T is a subset of S". Conversely, "!(T & S)" means "T is a |
1586 | // disjoint set from S". Things like "T - S" means that, provided that S is a |
1587 | // subset of T, it's the "set of all things in T but not in S". Things like "T | S" |
1588 | // mean the "union of T and S". |
1589 | |
1590 | // Is the child's type an object that isn't an other-object (i.e. object that could |
1591 | // have masquaredes-as-undefined traps) and isn't a function? Then: we should fold |
1592 | // this to true. |
1593 | if (!(child.m_type & ~(SpecObject - SpecObjectOther - SpecFunction))) { |
1594 | setConstant(node, jsBoolean(true)); |
1595 | constantWasSet = true; |
1596 | break; |
1597 | } |
1598 | |
1599 | // Is the child's type definitely not either of: an object that isn't a function, |
1600 | // or either undefined or null? Then: we should fold this to false. This means |
1601 | // for example that if it's any non-function object, including those that have |
1602 | // masquerades-as-undefined traps, then we don't fold. It also means we won't fold |
1603 | // if it's undefined-or-null, since the type bits don't distinguish between |
1604 | // undefined (which should fold to false) and null (which should fold to true). |
1605 | if (!(child.m_type & ((SpecObject - SpecFunction) | SpecOther))) { |
1606 | setConstant(node, jsBoolean(false)); |
1607 | constantWasSet = true; |
1608 | break; |
1609 | } |
1610 | |
1611 | break; |
1612 | case IsFunction: |
1613 | if (!(child.m_type & ~SpecFunction)) { |
1614 | setConstant(node, jsBoolean(true)); |
1615 | constantWasSet = true; |
1616 | break; |
1617 | } |
1618 | |
1619 | if (!(child.m_type & (SpecFunction | SpecObjectOther | SpecProxyObject))) { |
1620 | setConstant(node, jsBoolean(false)); |
1621 | constantWasSet = true; |
1622 | break; |
1623 | } |
1624 | break; |
1625 | |
1626 | case IsCellWithType: { |
1627 | Optional<SpeculatedType> filter = node->speculatedTypeForQuery(); |
1628 | if (!filter) { |
1629 | if (!(child.m_type & SpecCell)) { |
1630 | setConstant(node, jsBoolean(false)); |
1631 | constantWasSet = true; |
1632 | } |
1633 | break; |
1634 | } |
1635 | if (!(child.m_type & ~filter.value())) { |
1636 | setConstant(node, jsBoolean(true)); |
1637 | constantWasSet = true; |
1638 | break; |
1639 | } |
1640 | if (!(child.m_type & filter.value())) { |
1641 | setConstant(node, jsBoolean(false)); |
1642 | constantWasSet = true; |
1643 | break; |
1644 | } |
1645 | break; |
1646 | } |
1647 | |
1648 | case IsTypedArrayView: |
1649 | if (!(child.m_type & ~SpecTypedArrayView)) { |
1650 | setConstant(node, jsBoolean(true)); |
1651 | constantWasSet = true; |
1652 | break; |
1653 | } |
1654 | if (!(child.m_type & SpecTypedArrayView)) { |
1655 | setConstant(node, jsBoolean(false)); |
1656 | constantWasSet = true; |
1657 | break; |
1658 | } |
1659 | break; |
1660 | |
1661 | default: |
1662 | break; |
1663 | } |
1664 | if (constantWasSet) |
1665 | break; |
1666 | |
1667 | setNonCellTypeForNode(node, SpecBoolean); |
1668 | break; |
1669 | } |
1670 | |
1671 | case TypeOf: { |
1672 | JSValue child = forNode(node->child1()).value(); |
1673 | AbstractValue& abstractChild = forNode(node->child1()); |
1674 | if (child) { |
1675 | JSValue typeString = jsTypeStringForValue(m_vm, m_codeBlock->globalObjectFor(node->origin.semantic), child); |
1676 | setConstant(node, *m_graph.freeze(typeString)); |
1677 | break; |
1678 | } |
1679 | |
1680 | if (isFullNumberSpeculation(abstractChild.m_type)) { |
1681 | setConstant(node, *m_graph.freeze(m_vm.smallStrings.numberString())); |
1682 | break; |
1683 | } |
1684 | |
1685 | if (isStringSpeculation(abstractChild.m_type)) { |
1686 | setConstant(node, *m_graph.freeze(m_vm.smallStrings.stringString())); |
1687 | break; |
1688 | } |
1689 | |
1690 | // FIXME: We could use the masquerades-as-undefined watchpoint here. |
1691 | // https://bugs.webkit.org/show_bug.cgi?id=144456 |
1692 | if (!(abstractChild.m_type & ~(SpecObject - SpecObjectOther - SpecFunction))) { |
1693 | setConstant(node, *m_graph.freeze(m_vm.smallStrings.objectString())); |
1694 | break; |
1695 | } |
1696 | |
1697 | if (isFunctionSpeculation(abstractChild.m_type)) { |
1698 | setConstant(node, *m_graph.freeze(m_vm.smallStrings.functionString())); |
1699 | break; |
1700 | } |
1701 | |
1702 | if (isBooleanSpeculation(abstractChild.m_type)) { |
1703 | setConstant(node, *m_graph.freeze(m_vm.smallStrings.booleanString())); |
1704 | break; |
1705 | } |
1706 | |
1707 | if (isSymbolSpeculation(abstractChild.m_type)) { |
1708 | setConstant(node, *m_graph.freeze(m_vm.smallStrings.symbolString())); |
1709 | break; |
1710 | } |
1711 | |
1712 | if (isBigIntSpeculation(abstractChild.m_type)) { |
1713 | setConstant(node, *m_graph.freeze(m_vm.smallStrings.bigintString())); |
1714 | break; |
1715 | } |
1716 | |
1717 | setTypeForNode(node, SpecStringIdent); |
1718 | break; |
1719 | } |
1720 | |
1721 | case CompareBelow: |
1722 | case CompareBelowEq: { |
1723 | JSValue leftConst = forNode(node->child1()).value(); |
1724 | JSValue rightConst = forNode(node->child2()).value(); |
1725 | if (leftConst && rightConst) { |
1726 | if (leftConst.isInt32() && rightConst.isInt32()) { |
1727 | uint32_t a = static_cast<uint32_t>(leftConst.asInt32()); |
1728 | uint32_t b = static_cast<uint32_t>(rightConst.asInt32()); |
1729 | switch (node->op()) { |
1730 | case CompareBelow: |
1731 | setConstant(node, jsBoolean(a < b)); |
1732 | break; |
1733 | case CompareBelowEq: |
1734 | setConstant(node, jsBoolean(a <= b)); |
1735 | break; |
1736 | default: |
1737 | RELEASE_ASSERT_NOT_REACHED(); |
1738 | break; |
1739 | } |
1740 | break; |
1741 | } |
1742 | } |
1743 | |
1744 | if (node->child1() == node->child2()) { |
1745 | switch (node->op()) { |
1746 | case CompareBelow: |
1747 | setConstant(node, jsBoolean(false)); |
1748 | break; |
1749 | case CompareBelowEq: |
1750 | setConstant(node, jsBoolean(true)); |
1751 | break; |
1752 | default: |
1753 | DFG_CRASH(m_graph, node, "Unexpected node type" ); |
1754 | break; |
1755 | } |
1756 | break; |
1757 | } |
1758 | setNonCellTypeForNode(node, SpecBoolean); |
1759 | break; |
1760 | } |
1761 | |
1762 | case CompareLess: |
1763 | case CompareLessEq: |
1764 | case CompareGreater: |
1765 | case CompareGreaterEq: |
1766 | case CompareEq: { |
1767 | bool isClobbering = node->isBinaryUseKind(UntypedUse); |
1768 | |
1769 | if (isClobbering) |
1770 | didFoldClobberWorld(); |
1771 | |
1772 | JSValue leftConst = forNode(node->child1()).value(); |
1773 | JSValue rightConst = forNode(node->child2()).value(); |
1774 | if (leftConst && rightConst) { |
1775 | if (leftConst.isNumber() && rightConst.isNumber()) { |
1776 | double a = leftConst.asNumber(); |
1777 | double b = rightConst.asNumber(); |
1778 | switch (node->op()) { |
1779 | case CompareLess: |
1780 | setConstant(node, jsBoolean(a < b)); |
1781 | break; |
1782 | case CompareLessEq: |
1783 | setConstant(node, jsBoolean(a <= b)); |
1784 | break; |
1785 | case CompareGreater: |
1786 | setConstant(node, jsBoolean(a > b)); |
1787 | break; |
1788 | case CompareGreaterEq: |
1789 | setConstant(node, jsBoolean(a >= b)); |
1790 | break; |
1791 | case CompareEq: |
1792 | setConstant(node, jsBoolean(a == b)); |
1793 | break; |
1794 | default: |
1795 | RELEASE_ASSERT_NOT_REACHED(); |
1796 | break; |
1797 | } |
1798 | break; |
1799 | } |
1800 | |
1801 | if (leftConst.isString() && rightConst.isString()) { |
1802 | const StringImpl* a = asString(leftConst)->tryGetValueImpl(); |
1803 | const StringImpl* b = asString(rightConst)->tryGetValueImpl(); |
1804 | if (a && b) { |
1805 | bool result; |
1806 | if (node->op() == CompareEq) |
1807 | result = WTF::equal(a, b); |
1808 | else if (node->op() == CompareLess) |
1809 | result = codePointCompare(a, b) < 0; |
1810 | else if (node->op() == CompareLessEq) |
1811 | result = codePointCompare(a, b) <= 0; |
1812 | else if (node->op() == CompareGreater) |
1813 | result = codePointCompare(a, b) > 0; |
1814 | else if (node->op() == CompareGreaterEq) |
1815 | result = codePointCompare(a, b) >= 0; |
1816 | else |
1817 | RELEASE_ASSERT_NOT_REACHED(); |
1818 | setConstant(node, jsBoolean(result)); |
1819 | break; |
1820 | } |
1821 | } |
1822 | |
1823 | if (node->op() == CompareEq && leftConst.isSymbol() && rightConst.isSymbol()) { |
1824 | setConstant(node, jsBoolean(asSymbol(leftConst) == asSymbol(rightConst))); |
1825 | break; |
1826 | } |
1827 | } |
1828 | |
1829 | if (node->op() == CompareEq) { |
1830 | SpeculatedType leftType = forNode(node->child1()).m_type; |
1831 | SpeculatedType rightType = forNode(node->child2()).m_type; |
1832 | if (!valuesCouldBeEqual(leftType, rightType)) { |
1833 | setConstant(node, jsBoolean(false)); |
1834 | break; |
1835 | } |
1836 | |
1837 | if (leftType == SpecOther) |
1838 | std::swap(leftType, rightType); |
1839 | if (rightType == SpecOther) { |
1840 | // Undefined and Null are always equal when compared to eachother. |
1841 | if (!(leftType & ~SpecOther)) { |
1842 | setConstant(node, jsBoolean(true)); |
1843 | break; |
1844 | } |
1845 | |
1846 | // Any other type compared to Null or Undefined is always false |
1847 | // as long as the MasqueradesAsUndefined watchpoint is valid. |
1848 | // |
1849 | // MasqueradesAsUndefined only matters for SpecObjectOther, other |
1850 | // cases are always "false". |
1851 | if (!(leftType & (SpecObjectOther | SpecOther))) { |
1852 | setConstant(node, jsBoolean(false)); |
1853 | break; |
1854 | } |
1855 | |
1856 | if (!(leftType & SpecOther) && m_graph.masqueradesAsUndefinedWatchpointIsStillValid(node->origin.semantic)) { |
1857 | JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic); |
1858 | m_graph.watchpoints().addLazily(globalObject->masqueradesAsUndefinedWatchpoint()); |
1859 | setConstant(node, jsBoolean(false)); |
1860 | break; |
1861 | } |
1862 | } |
1863 | } |
1864 | |
1865 | if (node->child1() == node->child2()) { |
1866 | if (node->isBinaryUseKind(Int32Use) || |
1867 | node->isBinaryUseKind(Int52RepUse) || |
1868 | node->isBinaryUseKind(StringUse) || |
1869 | node->isBinaryUseKind(BooleanUse) || |
1870 | node->isBinaryUseKind(SymbolUse) || |
1871 | node->isBinaryUseKind(StringIdentUse) || |
1872 | node->isBinaryUseKind(ObjectUse) || |
1873 | node->isBinaryUseKind(ObjectUse, ObjectOrOtherUse) || |
1874 | node->isBinaryUseKind(ObjectOrOtherUse, ObjectUse)) { |
1875 | switch (node->op()) { |
1876 | case CompareLess: |
1877 | case CompareGreater: |
1878 | setConstant(node, jsBoolean(false)); |
1879 | break; |
1880 | case CompareLessEq: |
1881 | case CompareGreaterEq: |
1882 | case CompareEq: |
1883 | setConstant(node, jsBoolean(true)); |
1884 | break; |
1885 | default: |
1886 | DFG_CRASH(m_graph, node, "Unexpected node type" ); |
1887 | break; |
1888 | } |
1889 | break; |
1890 | } |
1891 | } |
1892 | |
1893 | if (isClobbering) |
1894 | clobberWorld(); |
1895 | setNonCellTypeForNode(node, SpecBoolean); |
1896 | break; |
1897 | } |
1898 | |
1899 | case CompareStrictEq: |
1900 | case SameValue: { |
1901 | Node* leftNode = node->child1().node(); |
1902 | Node* rightNode = node->child2().node(); |
1903 | JSValue left = forNode(leftNode).value(); |
1904 | JSValue right = forNode(rightNode).value(); |
1905 | if (left && right) { |
1906 | if (left.isString() && right.isString()) { |
1907 | // We need this case because JSValue::strictEqual is otherwise too racy for |
1908 | // string comparisons. |
1909 | const StringImpl* a = asString(left)->tryGetValueImpl(); |
1910 | const StringImpl* b = asString(right)->tryGetValueImpl(); |
1911 | if (a && b) { |
1912 | setConstant(node, jsBoolean(WTF::equal(a, b))); |
1913 | break; |
1914 | } |
1915 | } else { |
1916 | if (node->op() == CompareStrictEq) |
1917 | setConstant(node, jsBoolean(JSValue::strictEqual(nullptr, left, right))); |
1918 | else |
1919 | setConstant(node, jsBoolean(sameValue(nullptr, left, right))); |
1920 | break; |
1921 | } |
1922 | } |
1923 | |
1924 | if (node->isBinaryUseKind(UntypedUse)) { |
1925 | // FIXME: Revisit this condition when introducing BigInt to JSC. |
1926 | auto isNonStringCellConstant = [] (JSValue value) { |
1927 | return value && value.isCell() && !value.isString(); |
1928 | }; |
1929 | |
1930 | if (isNonStringCellConstant(left) || isNonStringCellConstant(right)) { |
1931 | m_state.setShouldTryConstantFolding(true); |
1932 | setNonCellTypeForNode(node, SpecBoolean); |
1933 | break; |
1934 | } |
1935 | } |
1936 | |
1937 | SpeculatedType leftLUB = leastUpperBoundOfStrictlyEquivalentSpeculations(forNode(leftNode).m_type); |
1938 | SpeculatedType rightLUB = leastUpperBoundOfStrictlyEquivalentSpeculations(forNode(rightNode).m_type); |
1939 | if (!(leftLUB & rightLUB)) { |
1940 | setConstant(node, jsBoolean(false)); |
1941 | break; |
1942 | } |
1943 | |
1944 | if (node->child1() == node->child2()) { |
1945 | if (node->isBinaryUseKind(BooleanUse) || |
1946 | node->isBinaryUseKind(Int32Use) || |
1947 | node->isBinaryUseKind(Int52RepUse) || |
1948 | node->isBinaryUseKind(StringUse) || |
1949 | node->isBinaryUseKind(StringIdentUse) || |
1950 | node->isBinaryUseKind(SymbolUse) || |
1951 | node->isBinaryUseKind(ObjectUse) || |
1952 | node->isBinaryUseKind(MiscUse, UntypedUse) || |
1953 | node->isBinaryUseKind(UntypedUse, MiscUse) || |
1954 | node->isBinaryUseKind(StringIdentUse, NotStringVarUse) || |
1955 | node->isBinaryUseKind(NotStringVarUse, StringIdentUse) || |
1956 | node->isBinaryUseKind(StringUse, UntypedUse) || |
1957 | node->isBinaryUseKind(UntypedUse, StringUse)) { |
1958 | setConstant(node, jsBoolean(true)); |
1959 | break; |
1960 | } |
1961 | } |
1962 | |
1963 | setNonCellTypeForNode(node, SpecBoolean); |
1964 | break; |
1965 | } |
1966 | |
1967 | case CompareEqPtr: { |
1968 | Node* childNode = node->child1().node(); |
1969 | JSValue childValue = forNode(childNode).value(); |
1970 | if (childValue) { |
1971 | setConstant(node, jsBoolean(childValue.isCell() && childValue.asCell() == node->cellOperand()->cell())); |
1972 | break; |
1973 | } |
1974 | |
1975 | setNonCellTypeForNode(node, SpecBoolean); |
1976 | break; |
1977 | } |
1978 | |
1979 | case StringCharCodeAt: |
1980 | case StringCodePointAt: |
1981 | setNonCellTypeForNode(node, SpecInt32Only); |
1982 | break; |
1983 | |
1984 | case StringFromCharCode: |
1985 | switch (node->child1().useKind()) { |
1986 | case Int32Use: |
1987 | break; |
1988 | case UntypedUse: |
1989 | clobberWorld(); |
1990 | break; |
1991 | default: |
1992 | DFG_CRASH(m_graph, node, "Bad use kind" ); |
1993 | break; |
1994 | } |
1995 | setTypeForNode(node, SpecString); |
1996 | break; |
1997 | |
1998 | case StringCharAt: |
1999 | setForNode(node, m_vm.stringStructure.get()); |
2000 | break; |
2001 | |
2002 | case GetByVal: |
2003 | case AtomicsAdd: |
2004 | case AtomicsAnd: |
2005 | case AtomicsCompareExchange: |
2006 | case AtomicsExchange: |
2007 | case AtomicsLoad: |
2008 | case AtomicsOr: |
2009 | case AtomicsStore: |
2010 | case AtomicsSub: |
2011 | case AtomicsXor: { |
2012 | if (node->op() == GetByVal) { |
2013 | auto foldGetByValOnConstantProperty = [&] (Edge& arrayEdge, Edge& indexEdge) { |
2014 | // FIXME: We can expand this for non x86 environments. |
2015 | // https://bugs.webkit.org/show_bug.cgi?id=134641 |
2016 | if (!isX86()) |
2017 | return false; |
2018 | |
2019 | AbstractValue& arrayValue = forNode(arrayEdge); |
2020 | |
2021 | // Check the structure set is finite. This means that this constant's structure is watched and guaranteed the one of this set. |
2022 | // When the structure is changed, this code should be invalidated. This is important since the following code relies on the |
2023 | // constant object's is not changed. |
2024 | if (!arrayValue.m_structure.isFinite()) |
2025 | return false; |
2026 | |
2027 | JSValue arrayConstant = arrayValue.value(); |
2028 | if (!arrayConstant) |
2029 | return false; |
2030 | |
2031 | JSObject* array = jsDynamicCast<JSObject*>(m_vm, arrayConstant); |
2032 | if (!array) |
2033 | return false; |
2034 | |
2035 | JSValue indexConstant = forNode(indexEdge).value(); |
2036 | if (!indexConstant || !indexConstant.isInt32() || indexConstant.asInt32() < 0) |
2037 | return false; |
2038 | uint32_t index = indexConstant.asUInt32(); |
2039 | |
2040 | // Check that the early StructureID is not nuked, get the butterfly, and check the late StructureID again. |
2041 | // And we check the indexing mode of the structure. If the indexing mode is CoW, the butterfly is |
2042 | // definitely JSImmutableButterfly. |
2043 | StructureID structureIDEarly = array->structureID(); |
2044 | if (isNuked(structureIDEarly)) |
2045 | return false; |
2046 | |
2047 | if (node->arrayMode().arrayClass() == Array::OriginalCopyOnWriteArray) { |
2048 | |
2049 | WTF::loadLoadFence(); |
2050 | Butterfly* butterfly = array->butterfly(); |
2051 | |
2052 | WTF::loadLoadFence(); |
2053 | StructureID structureIDLate = array->structureID(); |
2054 | |
2055 | if (structureIDEarly != structureIDLate) |
2056 | return false; |
2057 | |
2058 | Structure* structure = m_vm.getStructure(structureIDLate); |
2059 | switch (node->arrayMode().type()) { |
2060 | case Array::Int32: |
2061 | case Array::Contiguous: |
2062 | case Array::Double: |
2063 | if (structure->indexingMode() != (toIndexingShape(node->arrayMode().type()) | CopyOnWrite | IsArray)) |
2064 | return false; |
2065 | break; |
2066 | default: |
2067 | return false; |
2068 | } |
2069 | ASSERT(isCopyOnWrite(structure->indexingMode())); |
2070 | |
2071 | JSImmutableButterfly* immutableButterfly = JSImmutableButterfly::fromButterfly(butterfly); |
2072 | if (index < immutableButterfly->length()) { |
2073 | JSValue value = immutableButterfly->get(index); |
2074 | ASSERT(value); |
2075 | if (value.isCell()) |
2076 | setConstant(node, *m_graph.freeze(value.asCell())); |
2077 | else |
2078 | setConstant(node, value); |
2079 | return true; |
2080 | } |
2081 | |
2082 | if (node->arrayMode().isOutOfBounds()) { |
2083 | JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic); |
2084 | Structure* arrayPrototypeStructure = globalObject->arrayPrototype()->structure(m_vm); |
2085 | Structure* objectPrototypeStructure = globalObject->objectPrototype()->structure(m_vm); |
2086 | if (arrayPrototypeStructure->transitionWatchpointSetIsStillValid() |
2087 | && objectPrototypeStructure->transitionWatchpointSetIsStillValid() |
2088 | && globalObject->arrayPrototypeChainIsSane()) { |
2089 | m_graph.registerAndWatchStructureTransition(arrayPrototypeStructure); |
2090 | m_graph.registerAndWatchStructureTransition(objectPrototypeStructure); |
2091 | // Note that Array::Double and Array::Int32 return JSValue if array mode is OutOfBounds. |
2092 | setConstant(node, jsUndefined()); |
2093 | return true; |
2094 | } |
2095 | } |
2096 | return false; |
2097 | } |
2098 | |
2099 | if (node->arrayMode().type() == Array::ArrayStorage || node->arrayMode().type() == Array::SlowPutArrayStorage) { |
2100 | JSValue value; |
2101 | { |
2102 | // ArrayStorage's Butterfly can be half-broken state. |
2103 | auto locker = holdLock(array->cellLock()); |
2104 | |
2105 | WTF::loadLoadFence(); |
2106 | Butterfly* butterfly = array->butterfly(); |
2107 | |
2108 | WTF::loadLoadFence(); |
2109 | StructureID structureIDLate = array->structureID(); |
2110 | |
2111 | if (structureIDEarly != structureIDLate) |
2112 | return false; |
2113 | |
2114 | Structure* structure = m_vm.getStructure(structureIDLate); |
2115 | if (!hasAnyArrayStorage(structure->indexingMode())) |
2116 | return false; |
2117 | |
2118 | if (structure->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero()) |
2119 | return false; |
2120 | |
2121 | ArrayStorage* storage = butterfly->arrayStorage(); |
2122 | if (index >= storage->length()) |
2123 | return false; |
2124 | |
2125 | if (index < storage->vectorLength()) |
2126 | return false; |
2127 | |
2128 | SparseArrayValueMap* map = storage->m_sparseMap.get(); |
2129 | if (!map) |
2130 | return false; |
2131 | |
2132 | value = map->getConcurrently(index); |
2133 | } |
2134 | if (!value) |
2135 | return false; |
2136 | |
2137 | if (value.isCell()) |
2138 | setConstant(node, *m_graph.freeze(value.asCell())); |
2139 | else |
2140 | setConstant(node, value); |
2141 | return true; |
2142 | } |
2143 | |
2144 | return false; |
2145 | }; |
2146 | |
2147 | bool didFold = false; |
2148 | switch (node->arrayMode().type()) { |
2149 | case Array::Generic: |
2150 | case Array::Int32: |
2151 | case Array::Double: |
2152 | case Array::Contiguous: |
2153 | case Array::ArrayStorage: |
2154 | case Array::SlowPutArrayStorage: |
2155 | if (foldGetByValOnConstantProperty(m_graph.child(node, 0), m_graph.child(node, 1))) { |
2156 | if (!node->arrayMode().isInBounds()) |
2157 | didFoldClobberWorld(); |
2158 | didFold = true; |
2159 | } |
2160 | break; |
2161 | default: |
2162 | break; |
2163 | } |
2164 | |
2165 | if (didFold) |
2166 | break; |
2167 | } |
2168 | |
2169 | if (node->op() != GetByVal) { |
2170 | unsigned = numExtraAtomicsArgs(node->op()); |
2171 | Edge storageEdge = m_graph.child(node, 2 + numExtraArgs); |
2172 | if (!storageEdge) |
2173 | clobberWorld(); |
2174 | } |
2175 | switch (node->arrayMode().type()) { |
2176 | case Array::SelectUsingPredictions: |
2177 | case Array::Unprofiled: |
2178 | case Array::SelectUsingArguments: |
2179 | RELEASE_ASSERT_NOT_REACHED(); |
2180 | break; |
2181 | case Array::ForceExit: |
2182 | m_state.setIsValid(false); |
2183 | break; |
2184 | case Array::Undecided: { |
2185 | JSValue index = forNode(m_graph.child(node, 1)).value(); |
2186 | if (index && index.isInt32() && index.asInt32() >= 0) { |
2187 | setConstant(node, jsUndefined()); |
2188 | break; |
2189 | } |
2190 | setNonCellTypeForNode(node, SpecOther); |
2191 | break; |
2192 | } |
2193 | case Array::Generic: |
2194 | clobberWorld(); |
2195 | makeHeapTopForNode(node); |
2196 | break; |
2197 | case Array::String: |
2198 | if (node->arrayMode().isOutOfBounds()) { |
2199 | // If the watchpoint was still valid we could totally set this to be |
2200 | // SpecString | SpecOther. Except that we'd have to be careful. If we |
2201 | // tested the watchpoint state here then it could change by the time |
2202 | // we got to the backend. So to do this right, we'd have to get the |
2203 | // fixup phase to check the watchpoint state and then bake into the |
2204 | // GetByVal operation the fact that we're using a watchpoint, using |
2205 | // something like Array::SaneChain (except not quite, because that |
2206 | // implies an in-bounds access). None of this feels like it's worth it, |
2207 | // so we're going with TOP for now. The same thing applies to |
2208 | // clobbering the world. |
2209 | clobberWorld(); |
2210 | makeHeapTopForNode(node); |
2211 | } else |
2212 | setForNode(node, m_vm.stringStructure.get()); |
2213 | break; |
2214 | case Array::DirectArguments: |
2215 | case Array::ScopedArguments: |
2216 | if (node->arrayMode().isOutOfBounds()) |
2217 | clobberWorld(); |
2218 | makeHeapTopForNode(node); |
2219 | break; |
2220 | case Array::Int32: |
2221 | if (node->arrayMode().isOutOfBounds()) { |
2222 | clobberWorld(); |
2223 | makeHeapTopForNode(node); |
2224 | } else |
2225 | setNonCellTypeForNode(node, SpecInt32Only); |
2226 | break; |
2227 | case Array::Double: |
2228 | if (node->arrayMode().isOutOfBounds()) { |
2229 | clobberWorld(); |
2230 | makeHeapTopForNode(node); |
2231 | } else if (node->arrayMode().isSaneChain()) |
2232 | setNonCellTypeForNode(node, SpecBytecodeDouble); |
2233 | else |
2234 | setNonCellTypeForNode(node, SpecDoubleReal); |
2235 | break; |
2236 | case Array::Contiguous: |
2237 | case Array::ArrayStorage: |
2238 | case Array::SlowPutArrayStorage: |
2239 | if (node->arrayMode().isOutOfBounds()) |
2240 | clobberWorld(); |
2241 | makeHeapTopForNode(node); |
2242 | break; |
2243 | case Array::Int8Array: |
2244 | setNonCellTypeForNode(node, SpecInt32Only); |
2245 | break; |
2246 | case Array::Int16Array: |
2247 | setNonCellTypeForNode(node, SpecInt32Only); |
2248 | break; |
2249 | case Array::Int32Array: |
2250 | setNonCellTypeForNode(node, SpecInt32Only); |
2251 | break; |
2252 | case Array::Uint8Array: |
2253 | setNonCellTypeForNode(node, SpecInt32Only); |
2254 | break; |
2255 | case Array::Uint8ClampedArray: |
2256 | setNonCellTypeForNode(node, SpecInt32Only); |
2257 | break; |
2258 | case Array::Uint16Array: |
2259 | setNonCellTypeForNode(node, SpecInt32Only); |
2260 | break; |
2261 | case Array::Uint32Array: |
2262 | if (node->shouldSpeculateInt32()) |
2263 | setNonCellTypeForNode(node, SpecInt32Only); |
2264 | else if (node->shouldSpeculateInt52()) |
2265 | setNonCellTypeForNode(node, SpecInt52Any); |
2266 | else |
2267 | setNonCellTypeForNode(node, SpecAnyIntAsDouble); |
2268 | break; |
2269 | case Array::Float32Array: |
2270 | setNonCellTypeForNode(node, SpecFullDouble); |
2271 | break; |
2272 | case Array::Float64Array: |
2273 | setNonCellTypeForNode(node, SpecFullDouble); |
2274 | break; |
2275 | default: |
2276 | RELEASE_ASSERT_NOT_REACHED(); |
2277 | break; |
2278 | } |
2279 | break; |
2280 | } |
2281 | |
2282 | case PutByValDirect: |
2283 | case PutByVal: |
2284 | case PutByValAlias: { |
2285 | switch (node->arrayMode().modeForPut().type()) { |
2286 | case Array::ForceExit: |
2287 | m_state.setIsValid(false); |
2288 | break; |
2289 | case Array::Generic: |
2290 | clobberWorld(); |
2291 | break; |
2292 | case Array::Int32: |
2293 | if (node->arrayMode().isOutOfBounds()) |
2294 | clobberWorld(); |
2295 | break; |
2296 | case Array::Double: |
2297 | if (node->arrayMode().isOutOfBounds()) |
2298 | clobberWorld(); |
2299 | break; |
2300 | case Array::Contiguous: |
2301 | case Array::ArrayStorage: |
2302 | if (node->arrayMode().isOutOfBounds()) |
2303 | clobberWorld(); |
2304 | break; |
2305 | case Array::SlowPutArrayStorage: |
2306 | if (node->arrayMode().mayStoreToHole()) |
2307 | clobberWorld(); |
2308 | break; |
2309 | default: |
2310 | break; |
2311 | } |
2312 | break; |
2313 | } |
2314 | |
2315 | case ArrayPush: |
2316 | clobberWorld(); |
2317 | setNonCellTypeForNode(node, SpecBytecodeNumber); |
2318 | break; |
2319 | |
2320 | case ArraySlice: { |
2321 | JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic); |
2322 | |
2323 | // FIXME: We could do better here if we prove that the |
2324 | // incoming value has only a single structure. |
2325 | RegisteredStructureSet structureSet; |
2326 | structureSet.add(m_graph.registerStructure(globalObject->originalArrayStructureForIndexingType(ArrayWithInt32))); |
2327 | structureSet.add(m_graph.registerStructure(globalObject->originalArrayStructureForIndexingType(ArrayWithContiguous))); |
2328 | structureSet.add(m_graph.registerStructure(globalObject->originalArrayStructureForIndexingType(ArrayWithDouble))); |
2329 | |
2330 | setForNode(node, structureSet); |
2331 | break; |
2332 | } |
2333 | |
2334 | case ArrayIndexOf: { |
2335 | setNonCellTypeForNode(node, SpecInt32Only); |
2336 | break; |
2337 | } |
2338 | |
2339 | case ArrayPop: |
2340 | clobberWorld(); |
2341 | makeHeapTopForNode(node); |
2342 | break; |
2343 | |
2344 | case GetMyArgumentByVal: |
2345 | case GetMyArgumentByValOutOfBounds: { |
2346 | JSValue index = forNode(node->child2()).m_value; |
2347 | InlineCallFrame* inlineCallFrame = node->child1()->origin.semantic.inlineCallFrame(); |
2348 | |
2349 | if (index && index.isUInt32()) { |
2350 | // This pretends to return TOP for accesses that are actually proven out-of-bounds because |
2351 | // that's the conservative thing to do. Otherwise we'd need to write more code to mark such |
2352 | // paths as unreachable, or to return undefined. We could implement that eventually. |
2353 | |
2354 | Checked<unsigned, RecordOverflow> argumentIndexChecked = index.asUInt32(); |
2355 | argumentIndexChecked += node->numberOfArgumentsToSkip(); |
2356 | unsigned argumentIndex; |
2357 | if (argumentIndexChecked.safeGet(argumentIndex) != CheckedState::DidOverflow) { |
2358 | if (inlineCallFrame) { |
2359 | if (argumentIndex < inlineCallFrame->argumentCountIncludingThis - 1) { |
2360 | setForNode(node, m_state.operand( |
2361 | virtualRegisterForArgument(argumentIndex + 1) + inlineCallFrame->stackOffset)); |
2362 | m_state.setShouldTryConstantFolding(true); |
2363 | break; |
2364 | } |
2365 | } else { |
2366 | if (argumentIndex < m_state.numberOfArguments() - 1) { |
2367 | setForNode(node, m_state.argument(argumentIndex + 1)); |
2368 | m_state.setShouldTryConstantFolding(true); |
2369 | break; |
2370 | } |
2371 | } |
2372 | } |
2373 | } |
2374 | |
2375 | if (inlineCallFrame) { |
2376 | // We have a bound on the types even though it's random access. Take advantage of this. |
2377 | |
2378 | AbstractValue result; |
2379 | for (unsigned i = 1 + node->numberOfArgumentsToSkip(); i < inlineCallFrame->argumentCountIncludingThis; ++i) { |
2380 | result.merge( |
2381 | m_state.operand( |
2382 | virtualRegisterForArgument(i) + inlineCallFrame->stackOffset)); |
2383 | } |
2384 | |
2385 | if (node->op() == GetMyArgumentByValOutOfBounds) |
2386 | result.merge(SpecOther); |
2387 | |
2388 | if (result.value()) |
2389 | m_state.setShouldTryConstantFolding(true); |
2390 | |
2391 | setForNode(node, result); |
2392 | break; |
2393 | } |
2394 | |
2395 | makeHeapTopForNode(node); |
2396 | break; |
2397 | } |
2398 | |
2399 | case RegExpExec: |
2400 | case RegExpExecNonGlobalOrSticky: |
2401 | if (node->op() == RegExpExec) { |
2402 | // Even if we've proven known input types as RegExpObject and String, |
2403 | // accessing lastIndex is effectful if it's a global regexp. |
2404 | clobberWorld(); |
2405 | } |
2406 | |
2407 | if (JSValue globalObjectValue = forNode(node->child1()).m_value) { |
2408 | if (JSGlobalObject* globalObject = jsDynamicCast<JSGlobalObject*>(m_vm, globalObjectValue)) { |
2409 | if (!globalObject->isHavingABadTime()) { |
2410 | m_graph.watchpoints().addLazily(globalObject->havingABadTimeWatchpoint()); |
2411 | RegisteredStructureSet structureSet; |
2412 | structureSet.add(m_graph.registerStructure(globalObject->regExpMatchesArrayStructure())); |
2413 | setForNode(node, structureSet); |
2414 | forNode(node).merge(SpecOther); |
2415 | break; |
2416 | } |
2417 | } |
2418 | } |
2419 | setTypeForNode(node, SpecOther | SpecArray); |
2420 | break; |
2421 | |
2422 | case RegExpTest: |
2423 | // Even if we've proven known input types as RegExpObject and String, |
2424 | // accessing lastIndex is effectful if it's a global regexp. |
2425 | clobberWorld(); |
2426 | setNonCellTypeForNode(node, SpecBoolean); |
2427 | break; |
2428 | |
2429 | case RegExpMatchFast: |
2430 | ASSERT(node->child2().useKind() == RegExpObjectUse); |
2431 | ASSERT(node->child3().useKind() == StringUse || node->child3().useKind() == KnownStringUse); |
2432 | setTypeForNode(node, SpecOther | SpecArray); |
2433 | break; |
2434 | |
2435 | case RegExpMatchFastGlobal: |
2436 | ASSERT(node->child2().useKind() == StringUse || node->child2().useKind() == KnownStringUse); |
2437 | setTypeForNode(node, SpecOther | SpecArray); |
2438 | break; |
2439 | |
2440 | case StringReplace: |
2441 | case StringReplaceRegExp: |
2442 | if (node->child1().useKind() == StringUse |
2443 | && node->child2().useKind() == RegExpObjectUse |
2444 | && node->child3().useKind() == StringUse) { |
2445 | // This doesn't clobber the world. It just reads and writes regexp state. |
2446 | } else |
2447 | clobberWorld(); |
2448 | setForNode(node, m_vm.stringStructure.get()); |
2449 | break; |
2450 | |
2451 | case Jump: |
2452 | break; |
2453 | |
2454 | case Branch: { |
2455 | Node* child = node->child1().node(); |
2456 | BooleanResult result = booleanResult(node, forNode(child)); |
2457 | if (result == DefinitelyTrue) { |
2458 | m_state.setBranchDirection(TakeTrue); |
2459 | break; |
2460 | } |
2461 | if (result == DefinitelyFalse) { |
2462 | m_state.setBranchDirection(TakeFalse); |
2463 | break; |
2464 | } |
2465 | // FIXME: The above handles the trivial cases of sparse conditional |
2466 | // constant propagation, but we can do better: |
2467 | // We can specialize the source variable's value on each direction of |
2468 | // the branch. |
2469 | m_state.setBranchDirection(TakeBoth); |
2470 | break; |
2471 | } |
2472 | |
2473 | case Switch: { |
2474 | // Nothing to do for now. |
2475 | // FIXME: Do sparse conditional things. |
2476 | break; |
2477 | } |
2478 | |
2479 | case EntrySwitch: |
2480 | break; |
2481 | |
2482 | case Return: |
2483 | m_state.setIsValid(false); |
2484 | break; |
2485 | |
2486 | case Throw: |
2487 | case ThrowStaticError: |
2488 | case TailCall: |
2489 | case DirectTailCall: |
2490 | case TailCallVarargs: |
2491 | case TailCallForwardVarargs: |
2492 | clobberWorld(); |
2493 | m_state.setIsValid(false); |
2494 | break; |
2495 | |
2496 | case ToPrimitive: { |
2497 | JSValue childConst = forNode(node->child1()).value(); |
2498 | if (childConst && childConst.isNumber()) { |
2499 | didFoldClobberWorld(); |
2500 | setConstant(node, childConst); |
2501 | break; |
2502 | } |
2503 | |
2504 | ASSERT(node->child1().useKind() == UntypedUse); |
2505 | |
2506 | if (!(forNode(node->child1()).m_type & ~(SpecFullNumber | SpecBoolean | SpecString | SpecSymbol | SpecBigInt))) { |
2507 | m_state.setShouldTryConstantFolding(true); |
2508 | didFoldClobberWorld(); |
2509 | setForNode(node, forNode(node->child1())); |
2510 | break; |
2511 | } |
2512 | |
2513 | clobberWorld(); |
2514 | |
2515 | setTypeForNode(node, SpecHeapTop & ~SpecObject); |
2516 | break; |
2517 | } |
2518 | |
2519 | case ToNumber: { |
2520 | JSValue childConst = forNode(node->child1()).value(); |
2521 | if (childConst && childConst.isNumber()) { |
2522 | didFoldClobberWorld(); |
2523 | setConstant(node, childConst); |
2524 | break; |
2525 | } |
2526 | |
2527 | ASSERT(node->child1().useKind() == UntypedUse); |
2528 | |
2529 | if (!(forNode(node->child1()).m_type & ~SpecBytecodeNumber)) { |
2530 | m_state.setShouldTryConstantFolding(true); |
2531 | didFoldClobberWorld(); |
2532 | setForNode(node, forNode(node->child1())); |
2533 | break; |
2534 | } |
2535 | |
2536 | clobberWorld(); |
2537 | setNonCellTypeForNode(node, SpecBytecodeNumber); |
2538 | break; |
2539 | } |
2540 | |
2541 | case ToNumeric: { |
2542 | JSValue childConst = forNode(node->child1()).value(); |
2543 | if (childConst && (childConst.isNumber() || childConst.isBigInt())) { |
2544 | didFoldClobberWorld(); |
2545 | setConstant(node, childConst); |
2546 | break; |
2547 | } |
2548 | |
2549 | ASSERT(node->child1().useKind() == UntypedUse); |
2550 | |
2551 | if (!(forNode(node->child1()).m_type & ~(SpecBytecodeNumber | SpecBigInt))) { |
2552 | m_state.setShouldTryConstantFolding(true); |
2553 | didFoldClobberWorld(); |
2554 | setForNode(node, forNode(node->child1())); |
2555 | break; |
2556 | } |
2557 | |
2558 | clobberWorld(); |
2559 | setTypeForNode(node, SpecBytecodeNumber | SpecBigInt); |
2560 | break; |
2561 | } |
2562 | |
2563 | case ToString: |
2564 | case CallStringConstructor: { |
2565 | switch (node->child1().useKind()) { |
2566 | case StringObjectUse: |
2567 | case StringOrStringObjectUse: |
2568 | case Int32Use: |
2569 | case Int52RepUse: |
2570 | case DoubleRepUse: |
2571 | case NotCellUse: |
2572 | break; |
2573 | case CellUse: |
2574 | case UntypedUse: |
2575 | clobberWorld(); |
2576 | break; |
2577 | default: |
2578 | RELEASE_ASSERT_NOT_REACHED(); |
2579 | break; |
2580 | } |
2581 | setForNode(node, m_vm.stringStructure.get()); |
2582 | break; |
2583 | } |
2584 | |
2585 | case NumberToStringWithRadix: { |
2586 | JSValue radixValue = forNode(node->child2()).m_value; |
2587 | if (radixValue && radixValue.isInt32()) { |
2588 | int32_t radix = radixValue.asInt32(); |
2589 | if (2 <= radix && radix <= 36) { |
2590 | m_state.setShouldTryConstantFolding(true); |
2591 | didFoldClobberWorld(); |
2592 | setForNode(node, m_graph.m_vm.stringStructure.get()); |
2593 | break; |
2594 | } |
2595 | } |
2596 | clobberWorld(); |
2597 | setForNode(node, m_graph.m_vm.stringStructure.get()); |
2598 | break; |
2599 | } |
2600 | |
2601 | case NumberToStringWithValidRadixConstant: { |
2602 | setForNode(node, m_graph.m_vm.stringStructure.get()); |
2603 | break; |
2604 | } |
2605 | |
2606 | case NewStringObject: { |
2607 | ASSERT(node->structure()->classInfo() == StringObject::info()); |
2608 | setForNode(node, node->structure()); |
2609 | break; |
2610 | } |
2611 | |
2612 | case NewSymbol: { |
2613 | setForNode(node, m_vm.symbolStructure.get()); |
2614 | break; |
2615 | } |
2616 | |
2617 | case NewArray: |
2618 | ASSERT(node->indexingMode() == node->indexingType()); // Copy on write arrays should only be created by NewArrayBuffer. |
2619 | setForNode(node, |
2620 | m_graph.globalObjectFor(node->origin.semantic)->arrayStructureForIndexingTypeDuringAllocation(node->indexingType())); |
2621 | break; |
2622 | |
2623 | case NewArrayWithSpread: |
2624 | if (m_graph.isWatchingHavingABadTimeWatchpoint(node)) { |
2625 | // We've compiled assuming we're not having a bad time, so to be consistent |
2626 | // with StructureRegisterationPhase we must say we produce an original array |
2627 | // allocation structure. |
2628 | setForNode(node, |
2629 | m_graph.globalObjectFor(node->origin.semantic)->originalArrayStructureForIndexingType(ArrayWithContiguous)); |
2630 | } else { |
2631 | setForNode(node, |
2632 | m_graph.globalObjectFor(node->origin.semantic)->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous)); |
2633 | } |
2634 | |
2635 | break; |
2636 | |
2637 | case Spread: |
2638 | switch (node->child1()->op()) { |
2639 | case PhantomNewArrayBuffer: |
2640 | case PhantomCreateRest: |
2641 | break; |
2642 | default: |
2643 | if (!m_graph.canDoFastSpread(node, forNode(node->child1()))) |
2644 | clobberWorld(); |
2645 | else |
2646 | didFoldClobberWorld(); |
2647 | break; |
2648 | } |
2649 | |
2650 | setForNode(node, |
2651 | m_vm.fixedArrayStructure.get()); |
2652 | break; |
2653 | |
2654 | case NewArrayBuffer: |
2655 | setForNode(node, |
2656 | m_graph.globalObjectFor(node->origin.semantic)->arrayStructureForIndexingTypeDuringAllocation(node->indexingMode())); |
2657 | break; |
2658 | |
2659 | case NewArrayWithSize: |
2660 | setTypeForNode(node, SpecArray); |
2661 | break; |
2662 | |
2663 | case NewTypedArray: |
2664 | switch (node->child1().useKind()) { |
2665 | case Int32Use: |
2666 | break; |
2667 | case UntypedUse: |
2668 | clobberWorld(); |
2669 | break; |
2670 | default: |
2671 | RELEASE_ASSERT_NOT_REACHED(); |
2672 | break; |
2673 | } |
2674 | setForNode(node, |
2675 | m_graph.globalObjectFor(node->origin.semantic)->typedArrayStructureConcurrently( |
2676 | node->typedArrayType())); |
2677 | break; |
2678 | |
2679 | case NewRegexp: |
2680 | setForNode(node, m_graph.globalObjectFor(node->origin.semantic)->regExpStructure()); |
2681 | break; |
2682 | |
2683 | case ToThis: { |
2684 | AbstractValue& source = forNode(node->child1()); |
2685 | AbstractValue& destination = forNode(node); |
2686 | bool strictMode = m_graph.isStrictModeFor(node->origin.semantic); |
2687 | |
2688 | ToThisResult result = isToThisAnIdentity(m_vm, strictMode, source); |
2689 | switch (result) { |
2690 | case ToThisResult::Identity: |
2691 | m_state.setShouldTryConstantFolding(true); |
2692 | destination = source; |
2693 | break; |
2694 | case ToThisResult::Undefined: |
2695 | setConstant(node, jsUndefined()); |
2696 | break; |
2697 | case ToThisResult::GlobalThis: |
2698 | m_state.setShouldTryConstantFolding(true); |
2699 | destination.setType(m_graph, SpecObject); |
2700 | break; |
2701 | case ToThisResult::Dynamic: |
2702 | if (strictMode) |
2703 | destination.makeHeapTop(); |
2704 | else { |
2705 | destination = source; |
2706 | destination.merge(SpecObject); |
2707 | } |
2708 | break; |
2709 | } |
2710 | break; |
2711 | } |
2712 | |
2713 | case CreateThis: { |
2714 | if (JSValue base = forNode(node->child1()).m_value) { |
2715 | if (auto* function = jsDynamicCast<JSFunction*>(m_vm, base)) { |
2716 | if (FunctionRareData* rareData = function->rareData()) { |
2717 | if (rareData->allocationProfileWatchpointSet().isStillValid()) { |
2718 | if (Structure* structure = rareData->objectAllocationStructure()) { |
2719 | m_graph.freeze(rareData); |
2720 | m_graph.watchpoints().addLazily(rareData->allocationProfileWatchpointSet()); |
2721 | m_state.setShouldTryConstantFolding(true); |
2722 | didFoldClobberWorld(); |
2723 | setForNode(node, structure); |
2724 | break; |
2725 | } |
2726 | } |
2727 | } |
2728 | } |
2729 | } |
2730 | clobberWorld(); |
2731 | setTypeForNode(node, SpecFinalObject); |
2732 | break; |
2733 | } |
2734 | |
2735 | case CreatePromise: { |
2736 | JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic); |
2737 | if (JSValue base = forNode(node->child1()).m_value) { |
2738 | if (base == (node->isInternalPromise() ? globalObject->internalPromiseConstructor() : globalObject->promiseConstructor())) { |
2739 | m_state.setShouldTryConstantFolding(true); |
2740 | didFoldClobberWorld(); |
2741 | setForNode(node, node->isInternalPromise() ? globalObject->internalPromiseStructure() : globalObject->promiseStructure()); |
2742 | break; |
2743 | } |
2744 | if (auto* function = jsDynamicCast<JSFunction*>(m_graph.m_vm, base)) { |
2745 | if (FunctionRareData* rareData = function->rareData()) { |
2746 | if (rareData->allocationProfileWatchpointSet().isStillValid()) { |
2747 | Structure* structure = rareData->internalFunctionAllocationStructure(); |
2748 | if (structure |
2749 | && structure->classInfo() == (node->isInternalPromise() ? JSInternalPromise::info() : JSPromise::info()) |
2750 | && structure->globalObject() == globalObject |
2751 | && rareData->allocationProfileWatchpointSet().isStillValid()) { |
2752 | m_graph.freeze(rareData); |
2753 | m_graph.watchpoints().addLazily(rareData->allocationProfileWatchpointSet()); |
2754 | m_state.setShouldTryConstantFolding(true); |
2755 | didFoldClobberWorld(); |
2756 | setForNode(node, structure); |
2757 | break; |
2758 | } |
2759 | } |
2760 | } |
2761 | } |
2762 | } |
2763 | clobberWorld(); |
2764 | setTypeForNode(node, SpecPromiseObject); |
2765 | break; |
2766 | } |
2767 | |
2768 | case CreateGenerator: |
2769 | case CreateAsyncGenerator: { |
2770 | auto tryToFold = [&] (const ClassInfo* classInfo) -> bool { |
2771 | JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic); |
2772 | if (JSValue base = forNode(node->child1()).m_value) { |
2773 | if (auto* function = jsDynamicCast<JSFunction*>(m_graph.m_vm, base)) { |
2774 | if (FunctionRareData* rareData = function->rareData()) { |
2775 | if (rareData->allocationProfileWatchpointSet().isStillValid()) { |
2776 | Structure* structure = rareData->internalFunctionAllocationStructure(); |
2777 | if (structure |
2778 | && structure->classInfo() == classInfo |
2779 | && structure->globalObject() == globalObject |
2780 | && rareData->allocationProfileWatchpointSet().isStillValid()) { |
2781 | m_graph.freeze(rareData); |
2782 | m_graph.watchpoints().addLazily(rareData->allocationProfileWatchpointSet()); |
2783 | m_state.setShouldTryConstantFolding(true); |
2784 | didFoldClobberWorld(); |
2785 | setForNode(node, structure); |
2786 | return true; |
2787 | } |
2788 | } |
2789 | } |
2790 | } |
2791 | } |
2792 | return false; |
2793 | }; |
2794 | |
2795 | bool found = false; |
2796 | switch (node->op()) { |
2797 | case CreateGenerator: |
2798 | found = tryToFold(JSGenerator::info()); |
2799 | break; |
2800 | case CreateAsyncGenerator: |
2801 | found = tryToFold(JSAsyncGenerator::info()); |
2802 | break; |
2803 | default: |
2804 | RELEASE_ASSERT_NOT_REACHED(); |
2805 | break; |
2806 | } |
2807 | if (found) |
2808 | break; |
2809 | clobberWorld(); |
2810 | setTypeForNode(node, SpecObjectOther); |
2811 | break; |
2812 | } |
2813 | |
2814 | case NewPromise: |
2815 | ASSERT(!!node->structure().get()); |
2816 | setForNode(node, node->structure()); |
2817 | break; |
2818 | |
2819 | case NewGenerator: |
2820 | case NewAsyncGenerator: |
2821 | ASSERT(!!node->structure().get()); |
2822 | setForNode(node, node->structure()); |
2823 | break; |
2824 | |
2825 | case NewObject: |
2826 | ASSERT(!!node->structure().get()); |
2827 | setForNode(node, node->structure()); |
2828 | break; |
2829 | |
2830 | case ObjectCreate: { |
2831 | if (JSValue base = forNode(node->child1()).m_value) { |
2832 | JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic); |
2833 | Structure* structure = nullptr; |
2834 | if (base.isNull()) |
2835 | structure = globalObject->nullPrototypeObjectStructure(); |
2836 | else if (base.isObject()) |
2837 | structure = m_vm.structureCache.emptyObjectStructureConcurrently(globalObject, base.getObject(), JSFinalObject::defaultInlineCapacity()); |
2838 | |
2839 | if (structure) { |
2840 | m_state.setShouldTryConstantFolding(true); |
2841 | if (node->child1().useKind() == UntypedUse) |
2842 | didFoldClobberWorld(); |
2843 | setForNode(node, structure); |
2844 | break; |
2845 | } |
2846 | } |
2847 | if (node->child1().useKind() == UntypedUse) |
2848 | clobberWorld(); |
2849 | setTypeForNode(node, SpecFinalObject); |
2850 | break; |
2851 | } |
2852 | |
2853 | case ObjectKeys: { |
2854 | if (node->child1().useKind() == ObjectUse) { |
2855 | auto& structureSet = forNode(node->child1()).m_structure; |
2856 | if (structureSet.isFinite() && structureSet.size() == 1) { |
2857 | RegisteredStructure structure = structureSet.onlyStructure(); |
2858 | if (auto* rareData = structure->rareDataConcurrently()) { |
2859 | if (!!rareData->cachedOwnKeysConcurrently()) { |
2860 | if (m_graph.isWatchingHavingABadTimeWatchpoint(node)) { |
2861 | m_state.setShouldTryConstantFolding(true); |
2862 | didFoldClobberWorld(); |
2863 | setTypeForNode(node, SpecArray); |
2864 | break; |
2865 | } |
2866 | } |
2867 | } |
2868 | } |
2869 | } |
2870 | |
2871 | clobberWorld(); |
2872 | setTypeForNode(node, SpecArray); |
2873 | break; |
2874 | } |
2875 | |
2876 | case ToObject: |
2877 | case CallObjectConstructor: { |
2878 | AbstractValue& source = forNode(node->child1()); |
2879 | AbstractValue& destination = forNode(node); |
2880 | |
2881 | if (!(source.m_type & ~SpecObject)) { |
2882 | m_state.setShouldTryConstantFolding(true); |
2883 | if (node->op() == ToObject) |
2884 | didFoldClobberWorld(); |
2885 | destination = source; |
2886 | break; |
2887 | } |
2888 | |
2889 | if (node->op() == ToObject) |
2890 | clobberWorld(); |
2891 | setTypeForNode(node, SpecObject); |
2892 | break; |
2893 | } |
2894 | |
2895 | case PhantomNewObject: |
2896 | case PhantomNewFunction: |
2897 | case PhantomNewGeneratorFunction: |
2898 | case PhantomNewAsyncGeneratorFunction: |
2899 | case PhantomNewAsyncFunction: |
2900 | case PhantomCreateActivation: |
2901 | case PhantomDirectArguments: |
2902 | case PhantomClonedArguments: |
2903 | case PhantomCreateRest: |
2904 | case PhantomSpread: |
2905 | case PhantomNewArrayWithSpread: |
2906 | case PhantomNewArrayBuffer: |
2907 | case PhantomNewRegexp: |
2908 | case BottomValue: { |
2909 | clearForNode(node); |
2910 | break; |
2911 | } |
2912 | |
2913 | case PutHint: |
2914 | break; |
2915 | |
2916 | case MaterializeNewObject: { |
2917 | setForNode(node, node->structureSet()); |
2918 | break; |
2919 | } |
2920 | |
2921 | case PushWithScope: |
2922 | // We don't use the more precise withScopeStructure() here because it is a LazyProperty and may not yet be allocated. |
2923 | setTypeForNode(node, SpecObjectOther); |
2924 | break; |
2925 | |
2926 | case CreateActivation: |
2927 | case MaterializeCreateActivation: |
2928 | setForNode(node, |
2929 | m_codeBlock->globalObjectFor(node->origin.semantic)->activationStructure()); |
2930 | break; |
2931 | |
2932 | case CreateDirectArguments: |
2933 | setForNode(node, m_codeBlock->globalObjectFor(node->origin.semantic)->directArgumentsStructure()); |
2934 | break; |
2935 | |
2936 | case CreateScopedArguments: |
2937 | setForNode(node, m_codeBlock->globalObjectFor(node->origin.semantic)->scopedArgumentsStructure()); |
2938 | break; |
2939 | |
2940 | case CreateClonedArguments: |
2941 | if (!m_graph.isWatchingHavingABadTimeWatchpoint(node)) { |
2942 | setTypeForNode(node, SpecObject); |
2943 | break; |
2944 | } |
2945 | setForNode(node, m_codeBlock->globalObjectFor(node->origin.semantic)->clonedArgumentsStructure()); |
2946 | break; |
2947 | |
2948 | case NewGeneratorFunction: |
2949 | setForNode(node, |
2950 | m_codeBlock->globalObjectFor(node->origin.semantic)->generatorFunctionStructure()); |
2951 | break; |
2952 | |
2953 | case NewAsyncGeneratorFunction: |
2954 | setForNode(node, |
2955 | m_codeBlock->globalObjectFor(node->origin.semantic)->asyncGeneratorFunctionStructure()); |
2956 | break; |
2957 | |
2958 | case NewAsyncFunction: |
2959 | setForNode(node, |
2960 | m_codeBlock->globalObjectFor(node->origin.semantic)->asyncFunctionStructure()); |
2961 | break; |
2962 | |
2963 | case NewFunction: { |
2964 | JSGlobalObject* globalObject = m_codeBlock->globalObjectFor(node->origin.semantic); |
2965 | Structure* structure = JSFunction::selectStructureForNewFuncExp(globalObject, node->castOperand<FunctionExecutable*>()); |
2966 | setForNode(node, structure); |
2967 | break; |
2968 | } |
2969 | |
2970 | case GetCallee: |
2971 | if (FunctionExecutable* executable = jsDynamicCast<FunctionExecutable*>(m_vm, m_codeBlock->ownerExecutable())) { |
2972 | if (JSFunction* function = executable->singleton().inferredValue()) { |
2973 | m_graph.watchpoints().addLazily(executable); |
2974 | setConstant(node, *m_graph.freeze(function)); |
2975 | break; |
2976 | } |
2977 | } |
2978 | setTypeForNode(node, SpecFunction | SpecObjectOther); |
2979 | break; |
2980 | |
2981 | case GetArgumentCountIncludingThis: |
2982 | setTypeForNode(node, SpecInt32Only); |
2983 | break; |
2984 | |
2985 | case SetCallee: |
2986 | case SetArgumentCountIncludingThis: |
2987 | break; |
2988 | |
2989 | case GetRestLength: |
2990 | setNonCellTypeForNode(node, SpecInt32Only); |
2991 | break; |
2992 | |
2993 | case GetGetter: { |
2994 | if (JSValue base = forNode(node->child1()).m_value) { |
2995 | GetterSetter* getterSetter = jsDynamicCast<GetterSetter*>(m_vm, base); |
2996 | if (getterSetter && !getterSetter->isGetterNull()) { |
2997 | setConstant(node, *m_graph.freeze(getterSetter->getterConcurrently())); |
2998 | break; |
2999 | } |
3000 | } |
3001 | |
3002 | setTypeForNode(node, SpecObject); |
3003 | break; |
3004 | } |
3005 | |
3006 | case GetSetter: { |
3007 | if (JSValue base = forNode(node->child1()).m_value) { |
3008 | GetterSetter* getterSetter = jsDynamicCast<GetterSetter*>(m_vm, base); |
3009 | if (getterSetter && !getterSetter->isSetterNull()) { |
3010 | setConstant(node, *m_graph.freeze(getterSetter->setterConcurrently())); |
3011 | break; |
3012 | } |
3013 | } |
3014 | |
3015 | setTypeForNode(node, SpecObject); |
3016 | break; |
3017 | } |
3018 | |
3019 | case GetScope: |
3020 | if (JSValue base = forNode(node->child1()).m_value) { |
3021 | if (JSFunction* function = jsDynamicCast<JSFunction*>(m_vm, base)) { |
3022 | setConstant(node, *m_graph.freeze(function->scope())); |
3023 | break; |
3024 | } |
3025 | } |
3026 | setTypeForNode(node, SpecObjectOther); |
3027 | break; |
3028 | |
3029 | case SkipScope: { |
3030 | if (JSValue child = forNode(node->child1()).value()) { |
3031 | if (JSScope* scope = jsDynamicCast<JSScope*>(m_vm, child)) { |
3032 | if (JSScope* nextScope = scope->next()) { |
3033 | setConstant(node, *m_graph.freeze(JSValue(nextScope))); |
3034 | break; |
3035 | } |
3036 | } |
3037 | } |
3038 | setTypeForNode(node, SpecObjectOther); |
3039 | break; |
3040 | } |
3041 | |
3042 | case GetGlobalObject: { |
3043 | JSValue child = forNode(node->child1()).value(); |
3044 | if (child) { |
3045 | setConstant(node, *m_graph.freeze(JSValue(asObject(child)->globalObject(m_vm)))); |
3046 | break; |
3047 | } |
3048 | |
3049 | if (forNode(node->child1()).m_structure.isFinite()) { |
3050 | JSGlobalObject* globalObject = nullptr; |
3051 | bool ok = true; |
3052 | forNode(node->child1()).m_structure.forEach( |
3053 | [&] (RegisteredStructure structure) { |
3054 | if (!globalObject) |
3055 | globalObject = structure->globalObject(); |
3056 | else if (globalObject != structure->globalObject()) |
3057 | ok = false; |
3058 | }); |
3059 | if (globalObject && ok) { |
3060 | setConstant(node, *m_graph.freeze(JSValue(globalObject))); |
3061 | break; |
3062 | } |
3063 | } |
3064 | |
3065 | setTypeForNode(node, SpecObjectOther); |
3066 | break; |
3067 | } |
3068 | |
3069 | case GetGlobalThis: { |
3070 | setTypeForNode(node, SpecObject); |
3071 | break; |
3072 | } |
3073 | |
3074 | case GetClosureVar: |
3075 | if (JSValue value = m_graph.tryGetConstantClosureVar(forNode(node->child1()), node->scopeOffset())) { |
3076 | setConstant(node, *m_graph.freeze(value)); |
3077 | break; |
3078 | } |
3079 | makeBytecodeTopForNode(node); |
3080 | break; |
3081 | |
3082 | case PutClosureVar: |
3083 | break; |
3084 | |
3085 | case GetInternalField: |
3086 | makeBytecodeTopForNode(node); |
3087 | break; |
3088 | |
3089 | case PutInternalField: |
3090 | break; |
3091 | |
3092 | |
3093 | case GetRegExpObjectLastIndex: |
3094 | makeHeapTopForNode(node); |
3095 | break; |
3096 | |
3097 | case SetRegExpObjectLastIndex: |
3098 | case RecordRegExpCachedResult: |
3099 | break; |
3100 | |
3101 | case GetFromArguments: |
3102 | makeHeapTopForNode(node); |
3103 | break; |
3104 | |
3105 | case PutToArguments: |
3106 | break; |
3107 | |
3108 | case GetArgument: |
3109 | makeHeapTopForNode(node); |
3110 | break; |
3111 | |
3112 | case TryGetById: |
3113 | // FIXME: This should constant fold at least as well as the normal GetById case. |
3114 | // https://bugs.webkit.org/show_bug.cgi?id=156422 |
3115 | makeHeapTopForNode(node); |
3116 | break; |
3117 | |
3118 | case GetByIdDirect: |
3119 | case GetByIdDirectFlush: |
3120 | case GetById: |
3121 | case GetByIdFlush: { |
3122 | AbstractValue& value = forNode(node->child1()); |
3123 | if (value.m_structure.isFinite() |
3124 | && (node->child1().useKind() == CellUse || !(value.m_type & ~SpecCell))) { |
3125 | UniquedStringImpl* uid = m_graph.identifiers()[node->identifierNumber()]; |
3126 | GetByStatus status = GetByStatus::computeFor(value.m_structure.toStructureSet(), uid); |
3127 | if (status.isSimple()) { |
3128 | // Figure out what the result is going to be - is it TOP, a constant, or maybe |
3129 | // something more subtle? |
3130 | AbstractValue result; |
3131 | for (unsigned i = status.numVariants(); i--;) { |
3132 | // This thing won't give us a variant that involves prototypes. If it did, we'd |
3133 | // have more work to do here. |
3134 | DFG_ASSERT(m_graph, node, status[i].conditionSet().isEmpty()); |
3135 | |
3136 | result.merge( |
3137 | m_graph.inferredValueForProperty( |
3138 | value, status[i].offset(), m_state.structureClobberState())); |
3139 | } |
3140 | |
3141 | m_state.setShouldTryConstantFolding(true); |
3142 | didFoldClobberWorld(); |
3143 | forNode(node) = result; |
3144 | break; |
3145 | } |
3146 | } |
3147 | |
3148 | clobberWorld(); |
3149 | makeHeapTopForNode(node); |
3150 | break; |
3151 | } |
3152 | |
3153 | case GetByValWithThis: |
3154 | case GetByIdWithThis: |
3155 | clobberWorld(); |
3156 | makeHeapTopForNode(node); |
3157 | break; |
3158 | |
3159 | case GetArrayLength: { |
3160 | JSArrayBufferView* view = m_graph.tryGetFoldableView( |
3161 | forNode(node->child1()).m_value, node->arrayMode()); |
3162 | if (view) { |
3163 | setConstant(node, jsNumber(view->length())); |
3164 | break; |
3165 | } |
3166 | setNonCellTypeForNode(node, SpecInt32Only); |
3167 | break; |
3168 | } |
3169 | |
3170 | case GetVectorLength: { |
3171 | setNonCellTypeForNode(node, SpecInt32Only); |
3172 | break; |
3173 | } |
3174 | |
3175 | case DeleteById: |
3176 | case DeleteByVal: { |
3177 | // FIXME: This could decide if the delete will be successful based on the set of structures that |
3178 | // we get from our base value. https://bugs.webkit.org/show_bug.cgi?id=156611 |
3179 | clobberWorld(); |
3180 | setNonCellTypeForNode(node, SpecBoolean); |
3181 | break; |
3182 | } |
3183 | |
3184 | case CheckStructure: { |
3185 | AbstractValue& value = forNode(node->child1()); |
3186 | |
3187 | const RegisteredStructureSet& set = node->structureSet(); |
3188 | |
3189 | // It's interesting that we could have proven that the object has a larger structure set |
3190 | // that includes the set we're testing. In that case we could make the structure check |
3191 | // more efficient. We currently don't. |
3192 | |
3193 | if (value.m_structure.isSubsetOf(set)) |
3194 | m_state.setShouldTryConstantFolding(true); |
3195 | |
3196 | SpeculatedType admittedTypes = SpecNone; |
3197 | switch (node->child1().useKind()) { |
3198 | case CellUse: |
3199 | case KnownCellUse: |
3200 | admittedTypes = SpecNone; |
3201 | break; |
3202 | case CellOrOtherUse: |
3203 | admittedTypes = SpecOther; |
3204 | break; |
3205 | default: |
3206 | DFG_CRASH(m_graph, node, "Bad use kind" ); |
3207 | break; |
3208 | } |
3209 | |
3210 | filter(value, set, admittedTypes); |
3211 | break; |
3212 | } |
3213 | |
3214 | case CheckStructureOrEmpty: { |
3215 | AbstractValue& value = forNode(node->child1()); |
3216 | |
3217 | bool mayBeEmpty = value.m_type & SpecEmpty; |
3218 | if (!mayBeEmpty) |
3219 | m_state.setShouldTryConstantFolding(true); |
3220 | |
3221 | SpeculatedType admittedTypes = mayBeEmpty ? SpecEmpty : SpecNone; |
3222 | filter(value, node->structureSet(), admittedTypes); |
3223 | break; |
3224 | } |
3225 | |
3226 | case CheckStructureImmediate: { |
3227 | // FIXME: This currently can only reason about one structure at a time. |
3228 | // https://bugs.webkit.org/show_bug.cgi?id=136988 |
3229 | |
3230 | AbstractValue& value = forNode(node->child1()); |
3231 | const RegisteredStructureSet& set = node->structureSet(); |
3232 | |
3233 | if (value.value()) { |
3234 | if (Structure* structure = jsDynamicCast<Structure*>(m_vm, value.value())) { |
3235 | if (set.contains(m_graph.registerStructure(structure))) { |
3236 | m_state.setShouldTryConstantFolding(true); |
3237 | break; |
3238 | } |
3239 | } |
3240 | m_state.setIsValid(false); |
3241 | break; |
3242 | } |
3243 | |
3244 | if (m_phiChildren) { |
3245 | bool allGood = true; |
3246 | m_phiChildren->forAllTransitiveIncomingValues( |
3247 | node, |
3248 | [&] (Node* incoming) { |
3249 | if (Structure* structure = incoming->dynamicCastConstant<Structure*>(m_vm)) { |
3250 | if (set.contains(m_graph.registerStructure(structure))) |
3251 | return; |
3252 | } |
3253 | allGood = false; |
3254 | }); |
3255 | if (allGood) { |
3256 | m_state.setShouldTryConstantFolding(true); |
3257 | break; |
3258 | } |
3259 | } |
3260 | |
3261 | if (RegisteredStructure structure = set.onlyStructure()) { |
3262 | filterByValue(node->child1(), *m_graph.freeze(structure.get())); |
3263 | break; |
3264 | } |
3265 | |
3266 | // Aw shucks, we can't do anything! |
3267 | break; |
3268 | } |
3269 | |
3270 | case PutStructure: |
3271 | if (!forNode(node->child1()).m_structure.isClear()) { |
3272 | if (forNode(node->child1()).m_structure.onlyStructure() == node->transition()->next) { |
3273 | didFoldClobberStructures(); |
3274 | m_state.setShouldTryConstantFolding(true); |
3275 | } else { |
3276 | observeTransition( |
3277 | clobberLimit, node->transition()->previous, node->transition()->next); |
3278 | forNode(node->child1()).changeStructure(m_graph, node->transition()->next); |
3279 | } |
3280 | } else { |
3281 | // We're going to exit before we get here, but for the sake of validation, we've folded our write to StructureID. |
3282 | didFoldClobberStructures(); |
3283 | } |
3284 | break; |
3285 | case GetButterfly: |
3286 | case AllocatePropertyStorage: |
3287 | case ReallocatePropertyStorage: |
3288 | case NukeStructureAndSetButterfly: |
3289 | // FIXME: We don't model the fact that the structureID is nuked, simply because currently |
3290 | // nobody would currently benefit from having that information. But it's a bug nonetheless. |
3291 | if (node->op() == NukeStructureAndSetButterfly) |
3292 | didFoldClobberStructures(); |
3293 | clearForNode(node); // The result is not a JS value. |
3294 | break; |
3295 | case CheckSubClass: { |
3296 | JSValue constant = forNode(node->child1()).value(); |
3297 | if (constant) { |
3298 | if (constant.isCell() && constant.asCell()->inherits(m_vm, node->classInfo())) { |
3299 | m_state.setShouldTryConstantFolding(true); |
3300 | ASSERT(constant); |
3301 | break; |
3302 | } |
3303 | } |
3304 | |
3305 | AbstractValue& value = forNode(node->child1()); |
3306 | |
3307 | if (value.m_structure.isSubClassOf(node->classInfo())) |
3308 | m_state.setShouldTryConstantFolding(true); |
3309 | |
3310 | filterClassInfo(value, node->classInfo()); |
3311 | break; |
3312 | } |
3313 | case CallDOMGetter: { |
3314 | CallDOMGetterData* callDOMGetterData = node->callDOMGetterData(); |
3315 | DOMJIT::CallDOMGetterSnippet* snippet = callDOMGetterData->snippet; |
3316 | if (!snippet || snippet->effect.writes) |
3317 | clobberWorld(); |
3318 | if (callDOMGetterData->domJIT) |
3319 | setTypeForNode(node, callDOMGetterData->domJIT->resultType()); |
3320 | else |
3321 | makeBytecodeTopForNode(node); |
3322 | break; |
3323 | } |
3324 | case CallDOM: { |
3325 | const DOMJIT::Signature* signature = node->signature(); |
3326 | if (signature->effect.writes) |
3327 | clobberWorld(); |
3328 | setTypeForNode(node, signature->result); |
3329 | break; |
3330 | } |
3331 | case CheckArray: { |
3332 | if (node->arrayMode().alreadyChecked(m_graph, node, forNode(node->child1()))) { |
3333 | m_state.setShouldTryConstantFolding(true); |
3334 | break; |
3335 | } |
3336 | switch (node->arrayMode().type()) { |
3337 | case Array::String: |
3338 | filter(node->child1(), SpecString); |
3339 | break; |
3340 | case Array::Int32: |
3341 | case Array::Double: |
3342 | case Array::Contiguous: |
3343 | case Array::Undecided: |
3344 | case Array::ArrayStorage: |
3345 | case Array::SlowPutArrayStorage: |
3346 | break; |
3347 | case Array::DirectArguments: |
3348 | filter(node->child1(), SpecDirectArguments); |
3349 | break; |
3350 | case Array::ScopedArguments: |
3351 | filter(node->child1(), SpecScopedArguments); |
3352 | break; |
3353 | case Array::Int8Array: |
3354 | filter(node->child1(), SpecInt8Array); |
3355 | break; |
3356 | case Array::Int16Array: |
3357 | filter(node->child1(), SpecInt16Array); |
3358 | break; |
3359 | case Array::Int32Array: |
3360 | filter(node->child1(), SpecInt32Array); |
3361 | break; |
3362 | case Array::Uint8Array: |
3363 | filter(node->child1(), SpecUint8Array); |
3364 | break; |
3365 | case Array::Uint8ClampedArray: |
3366 | filter(node->child1(), SpecUint8ClampedArray); |
3367 | break; |
3368 | case Array::Uint16Array: |
3369 | filter(node->child1(), SpecUint16Array); |
3370 | break; |
3371 | case Array::Uint32Array: |
3372 | filter(node->child1(), SpecUint32Array); |
3373 | break; |
3374 | case Array::Float32Array: |
3375 | filter(node->child1(), SpecFloat32Array); |
3376 | break; |
3377 | case Array::Float64Array: |
3378 | filter(node->child1(), SpecFloat64Array); |
3379 | break; |
3380 | case Array::AnyTypedArray: |
3381 | filter(node->child1(), SpecTypedArrayView); |
3382 | break; |
3383 | default: |
3384 | RELEASE_ASSERT_NOT_REACHED(); |
3385 | break; |
3386 | } |
3387 | filterArrayModes(node->child1(), node->arrayMode().arrayModesThatPassFiltering()); |
3388 | break; |
3389 | } |
3390 | case Arrayify: { |
3391 | if (node->arrayMode().alreadyChecked(m_graph, node, forNode(node->child1()))) { |
3392 | didFoldClobberStructures(); |
3393 | m_state.setShouldTryConstantFolding(true); |
3394 | break; |
3395 | } |
3396 | ASSERT(node->arrayMode().conversion() == Array::Convert); |
3397 | clobberStructures(); |
3398 | filterArrayModes(node->child1(), node->arrayMode().arrayModesThatPassFiltering()); |
3399 | break; |
3400 | } |
3401 | case ArrayifyToStructure: { |
3402 | AbstractValue& value = forNode(node->child1()); |
3403 | if (value.m_structure.isSubsetOf(RegisteredStructureSet(node->structure()))) |
3404 | m_state.setShouldTryConstantFolding(true); |
3405 | clobberStructures(); |
3406 | |
3407 | // We have a bunch of options of how to express the abstract set at this point. Let set S |
3408 | // be the set of structures that the value had before clobbering and assume that all of |
3409 | // them are watchable. The new value should be the least expressible upper bound of the |
3410 | // intersection of "values that currently have structure = node->structure()" and "values |
3411 | // that have structure in S plus any structure transition-reachable from S". Assume that |
3412 | // node->structure() is not in S but it is transition-reachable from S. Then we would |
3413 | // like to say that the result is "values that have structure = node->structure() until |
3414 | // we invalidate", but there is no way to express this using the AbstractValue syntax. So |
3415 | // we must choose between: |
3416 | // |
3417 | // 1) "values that currently have structure = node->structure()". This is a valid |
3418 | // superset of the value that we really want, and it's specific enough to satisfy the |
3419 | // preconditions of the array access that this is guarding. It's also specific enough |
3420 | // to allow relevant optimizations in the case that we didn't have a contradiction |
3421 | // like in this example. Notice that in the abscence of any contradiction, this result |
3422 | // is precise rather than being a conservative LUB. |
3423 | // |
3424 | // 2) "values that currently hava structure in S plus any structure transition-reachable |
3425 | // from S". This is also a valid superset of the value that we really want, but it's |
3426 | // not specific enough to satisfy the preconditions of the array access that this is |
3427 | // guarding - so playing such shenanigans would preclude us from having assertions on |
3428 | // the typing preconditions of any array accesses. This would also not be a desirable |
3429 | // answer in the absence of a contradiction. |
3430 | // |
3431 | // Note that it's tempting to simply say that the resulting value is BOTTOM because of |
3432 | // the contradiction. That would be wrong, since we haven't hit an invalidation point, |
3433 | // yet. |
3434 | forNode(node->child1()).set(m_graph, node->structure()); |
3435 | break; |
3436 | } |
3437 | case GetIndexedPropertyStorage: { |
3438 | JSArrayBufferView* view = m_graph.tryGetFoldableView( |
3439 | forNode(node->child1()).m_value, node->arrayMode()); |
3440 | if (view) |
3441 | m_state.setShouldTryConstantFolding(true); |
3442 | clearForNode(node); |
3443 | break; |
3444 | } |
3445 | case ConstantStoragePointer: { |
3446 | clearForNode(node); |
3447 | break; |
3448 | } |
3449 | |
3450 | case GetTypedArrayByteOffset: { |
3451 | JSArrayBufferView* view = m_graph.tryGetFoldableView(forNode(node->child1()).m_value); |
3452 | if (view) { |
3453 | Optional<unsigned> byteOffset = view->byteOffsetConcurrently(); |
3454 | if (byteOffset) { |
3455 | setConstant(node, jsNumber(*byteOffset)); |
3456 | break; |
3457 | } |
3458 | } |
3459 | setNonCellTypeForNode(node, SpecInt32Only); |
3460 | break; |
3461 | } |
3462 | |
3463 | case GetPrototypeOf: { |
3464 | AbstractValue& value = forNode(node->child1()); |
3465 | if ((value.m_type && !(value.m_type & ~SpecObject)) && value.m_structure.isFinite()) { |
3466 | bool canFold = !value.m_structure.isClear(); |
3467 | JSValue prototype; |
3468 | value.m_structure.forEach([&] (RegisteredStructure structure) { |
3469 | auto getPrototypeMethod = structure->classInfo()->methodTable.getPrototype; |
3470 | MethodTable::GetPrototypeFunctionPtr defaultGetPrototype = JSObject::getPrototype; |
3471 | if (getPrototypeMethod != defaultGetPrototype) { |
3472 | canFold = false; |
3473 | return; |
3474 | } |
3475 | |
3476 | if (structure->hasPolyProto()) { |
3477 | canFold = false; |
3478 | return; |
3479 | } |
3480 | if (!prototype) |
3481 | prototype = structure->storedPrototype(); |
3482 | else if (prototype != structure->storedPrototype()) |
3483 | canFold = false; |
3484 | }); |
3485 | |
3486 | if (prototype && canFold) { |
3487 | switch (node->child1().useKind()) { |
3488 | case ArrayUse: |
3489 | case FunctionUse: |
3490 | case FinalObjectUse: |
3491 | break; |
3492 | default: |
3493 | didFoldClobberWorld(); |
3494 | break; |
3495 | } |
3496 | setConstant(node, *m_graph.freeze(prototype)); |
3497 | break; |
3498 | } |
3499 | } |
3500 | |
3501 | switch (node->child1().useKind()) { |
3502 | case ArrayUse: |
3503 | case FunctionUse: |
3504 | case FinalObjectUse: |
3505 | break; |
3506 | default: |
3507 | clobberWorld(); |
3508 | break; |
3509 | } |
3510 | setTypeForNode(node, SpecObject | SpecOther); |
3511 | break; |
3512 | } |
3513 | |
3514 | case GetByOffset: { |
3515 | StorageAccessData& data = node->storageAccessData(); |
3516 | |
3517 | // FIXME: The part of this that handles inferred property types relies on AI knowing the structure |
3518 | // right now. That's probably not optimal. In some cases, we may perform an optimization (usually |
3519 | // by something other than AI, maybe by CSE for example) that obscures AI's view of the structure |
3520 | // at the point where GetByOffset runs. Currently, when that happens, we'll have to rely entirely |
3521 | // on the type that ByteCodeParser was able to prove. |
3522 | AbstractValue value = m_graph.inferredValueForProperty( |
3523 | forNode(node->child2()), data.offset, m_state.structureClobberState()); |
3524 | |
3525 | // If we decide that there does not exist any value that this can return, then it's probably |
3526 | // because the compilation was already invalidated. |
3527 | if (value.isClear()) |
3528 | m_state.setIsValid(false); |
3529 | |
3530 | setForNode(node, value); |
3531 | if (value.m_value) |
3532 | m_state.setShouldTryConstantFolding(true); |
3533 | break; |
3534 | } |
3535 | |
3536 | case GetGetterSetterByOffset: { |
3537 | StorageAccessData& data = node->storageAccessData(); |
3538 | AbstractValue base = forNode(node->child2()); |
3539 | JSValue result = m_graph.tryGetConstantProperty(base, data.offset); |
3540 | if (result && jsDynamicCast<GetterSetter*>(m_vm, result)) { |
3541 | setConstant(node, *m_graph.freeze(result)); |
3542 | break; |
3543 | } |
3544 | |
3545 | setForNode(node, m_vm.getterSetterStructure.get()); |
3546 | break; |
3547 | } |
3548 | |
3549 | case MultiGetByOffset: { |
3550 | // This code will filter the base value in a manner that is possibly different (either more |
3551 | // or less precise) than the way it would be filtered if this was strength-reduced to a |
3552 | // CheckStructure. This is fine. It's legal for different passes over the code to prove |
3553 | // different things about the code, so long as all of them are sound. That even includes |
3554 | // one guy proving that code should never execute (due to a contradiction) and another guy |
3555 | // not finding that contradiction. If someone ever proved that there would be a |
3556 | // contradiction then there must always be a contradiction even if subsequent passes don't |
3557 | // realize it. This is the case here. |
3558 | |
3559 | // Ordinarily you have to be careful with calling setShouldTryConstantFolding() |
3560 | // because of the effect on compile times, but this node is FTL-only. |
3561 | m_state.setShouldTryConstantFolding(true); |
3562 | |
3563 | AbstractValue base = forNode(node->child1()); |
3564 | RegisteredStructureSet baseSet; |
3565 | AbstractValue result; |
3566 | for (const MultiGetByOffsetCase& getCase : node->multiGetByOffsetData().cases) { |
3567 | RegisteredStructureSet set = getCase.set(); |
3568 | set.filter(base); |
3569 | if (set.isEmpty()) |
3570 | continue; |
3571 | baseSet.merge(set); |
3572 | |
3573 | switch (getCase.method().kind()) { |
3574 | case GetByOffsetMethod::Constant: { |
3575 | AbstractValue thisResult; |
3576 | thisResult.set( |
3577 | m_graph, |
3578 | *getCase.method().constant(), |
3579 | m_state.structureClobberState()); |
3580 | result.merge(thisResult); |
3581 | break; |
3582 | } |
3583 | |
3584 | default: { |
3585 | result.makeHeapTop(); |
3586 | break; |
3587 | } } |
3588 | } |
3589 | |
3590 | if (forNode(node->child1()).changeStructure(m_graph, baseSet) == Contradiction) |
3591 | m_state.setIsValid(false); |
3592 | |
3593 | setForNode(node, result); |
3594 | break; |
3595 | } |
3596 | |
3597 | case PutByOffset: { |
3598 | break; |
3599 | } |
3600 | |
3601 | case MultiPutByOffset: { |
3602 | RegisteredStructureSet newSet; |
3603 | TransitionVector transitions; |
3604 | |
3605 | // Ordinarily you have to be careful with calling setShouldTryConstantFolding() |
3606 | // because of the effect on compile times, but this node is FTL-only. |
3607 | m_state.setShouldTryConstantFolding(true); |
3608 | |
3609 | AbstractValue base = forNode(node->child1()); |
3610 | AbstractValue originalValue = forNode(node->child2()); |
3611 | AbstractValue resultingValue; |
3612 | |
3613 | if (node->multiPutByOffsetData().writesStructures()) |
3614 | didFoldClobberStructures(); |
3615 | |
3616 | for (unsigned i = node->multiPutByOffsetData().variants.size(); i--;) { |
3617 | const PutByIdVariant& variant = node->multiPutByOffsetData().variants[i]; |
3618 | RegisteredStructureSet thisSet = *m_graph.addStructureSet(variant.oldStructure()); |
3619 | thisSet.filter(base); |
3620 | if (thisSet.isEmpty()) |
3621 | continue; |
3622 | |
3623 | AbstractValue thisValue = originalValue; |
3624 | resultingValue.merge(thisValue); |
3625 | |
3626 | if (variant.kind() == PutByIdVariant::Transition) { |
3627 | RegisteredStructure newStructure = m_graph.registerStructure(variant.newStructure()); |
3628 | if (thisSet.onlyStructure() != newStructure) { |
3629 | transitions.append( |
3630 | Transition(m_graph.registerStructure(variant.oldStructureForTransition()), newStructure)); |
3631 | } // else this is really a replace. |
3632 | newSet.add(newStructure); |
3633 | } else { |
3634 | ASSERT(variant.kind() == PutByIdVariant::Replace); |
3635 | newSet.merge(thisSet); |
3636 | } |
3637 | } |
3638 | |
3639 | // We need to order AI executing these effects in the same order as they're executed |
3640 | // at runtime. This is critical when you have JS code like `o.f = o;`. We first |
3641 | // filter types on o, then transition o. Not the other way around. If we got |
3642 | // this ordering wrong, we could end up with the wrong type representing o. |
3643 | setForNode(node->child2(), resultingValue); |
3644 | if (!!originalValue && !resultingValue) |
3645 | m_state.setIsValid(false); |
3646 | |
3647 | observeTransitions(clobberLimit, transitions); |
3648 | if (forNode(node->child1()).changeStructure(m_graph, newSet) == Contradiction) |
3649 | m_state.setIsValid(false); |
3650 | break; |
3651 | } |
3652 | |
3653 | case GetExecutable: { |
3654 | JSValue value = forNode(node->child1()).value(); |
3655 | if (value) { |
3656 | JSFunction* function = jsDynamicCast<JSFunction*>(m_vm, value); |
3657 | if (function) { |
3658 | setConstant(node, *m_graph.freeze(function->executable())); |
3659 | break; |
3660 | } |
3661 | } |
3662 | setTypeForNode(node, SpecCellOther); |
3663 | break; |
3664 | } |
3665 | |
3666 | case CheckCell: { |
3667 | JSValue value = forNode(node->child1()).value(); |
3668 | if (value == node->cellOperand()->value()) { |
3669 | m_state.setShouldTryConstantFolding(true); |
3670 | ASSERT(value); |
3671 | break; |
3672 | } |
3673 | filterByValue(node->child1(), *node->cellOperand()); |
3674 | break; |
3675 | } |
3676 | |
3677 | case AssertNotEmpty: |
3678 | case CheckNotEmpty: { |
3679 | AbstractValue& value = forNode(node->child1()); |
3680 | if (!(value.m_type & SpecEmpty)) { |
3681 | m_state.setShouldTryConstantFolding(true); |
3682 | break; |
3683 | } |
3684 | |
3685 | filter(value, ~SpecEmpty); |
3686 | break; |
3687 | } |
3688 | |
3689 | case CheckIdent: { |
3690 | AbstractValue& value = forNode(node->child1()); |
3691 | UniquedStringImpl* uid = node->uidOperand(); |
3692 | |
3693 | JSValue childConstant = value.value(); |
3694 | if (childConstant) { |
3695 | if (childConstant.isString()) { |
3696 | if (asString(childConstant)->tryGetValueImpl() == uid) { |
3697 | m_state.setShouldTryConstantFolding(true); |
3698 | break; |
3699 | } |
3700 | } else if (childConstant.isSymbol()) { |
3701 | if (&jsCast<Symbol*>(childConstant)->privateName().uid() == uid) { |
3702 | m_state.setShouldTryConstantFolding(true); |
3703 | break; |
3704 | } |
3705 | } |
3706 | } |
3707 | |
3708 | if (node->child1().useKind() == StringIdentUse) |
3709 | filter(value, SpecStringIdent); |
3710 | else |
3711 | filter(value, SpecSymbol); |
3712 | break; |
3713 | } |
3714 | |
3715 | case CheckInBounds: { |
3716 | JSValue left = forNode(node->child1()).value(); |
3717 | JSValue right = forNode(node->child2()).value(); |
3718 | if (left && right && left.isInt32() && right.isInt32() && static_cast<uint32_t>(left.asInt32()) < static_cast<uint32_t>(right.asInt32())) |
3719 | m_state.setShouldTryConstantFolding(true); |
3720 | |
3721 | // We claim we result in Int32. It's not really important what our result is (though we |
3722 | // don't want to claim we may result in the empty value), other nodes with data flow edges |
3723 | // to us just do that to maintain the invariant that they can't be hoisted higher than us. |
3724 | // So we just arbitrarily pick Int32. In some ways, StorageResult may be the more correct |
3725 | // thing to do here. We pick NodeResultJS because it makes converting this to an identity |
3726 | // easier. |
3727 | setNonCellTypeForNode(node, SpecInt32Only); |
3728 | break; |
3729 | } |
3730 | |
3731 | case PutById: |
3732 | case PutByIdFlush: |
3733 | case PutByIdDirect: { |
3734 | AbstractValue& value = forNode(node->child1()); |
3735 | if (value.m_structure.isFinite()) { |
3736 | PutByIdStatus status = PutByIdStatus::computeFor( |
3737 | m_graph.globalObjectFor(node->origin.semantic), |
3738 | value.m_structure.toStructureSet(), |
3739 | m_graph.identifiers()[node->identifierNumber()], |
3740 | node->op() == PutByIdDirect); |
3741 | |
3742 | if (status.isSimple()) { |
3743 | RegisteredStructureSet newSet; |
3744 | TransitionVector transitions; |
3745 | |
3746 | for (unsigned i = status.numVariants(); i--;) { |
3747 | const PutByIdVariant& variant = status[i]; |
3748 | if (variant.kind() == PutByIdVariant::Transition) { |
3749 | RegisteredStructure newStructure = m_graph.registerStructure(variant.newStructure()); |
3750 | transitions.append( |
3751 | Transition( |
3752 | m_graph.registerStructure(variant.oldStructureForTransition()), newStructure)); |
3753 | newSet.add(newStructure); |
3754 | } else { |
3755 | ASSERT(variant.kind() == PutByIdVariant::Replace); |
3756 | newSet.merge(*m_graph.addStructureSet(variant.oldStructure())); |
3757 | } |
3758 | } |
3759 | |
3760 | if (status.numVariants() == 1 || m_graph.m_plan.isFTL()) |
3761 | m_state.setShouldTryConstantFolding(true); |
3762 | |
3763 | didFoldClobberWorld(); |
3764 | observeTransitions(clobberLimit, transitions); |
3765 | if (forNode(node->child1()).changeStructure(m_graph, newSet) == Contradiction) |
3766 | m_state.setIsValid(false); |
3767 | break; |
3768 | } |
3769 | } |
3770 | |
3771 | clobberWorld(); |
3772 | break; |
3773 | } |
3774 | |
3775 | case PutByValWithThis: |
3776 | case PutByIdWithThis: |
3777 | clobberWorld(); |
3778 | break; |
3779 | |
3780 | case PutGetterById: |
3781 | case PutSetterById: |
3782 | case PutGetterSetterById: |
3783 | case PutGetterByVal: |
3784 | case PutSetterByVal: { |
3785 | clobberWorld(); |
3786 | break; |
3787 | } |
3788 | |
3789 | case DefineDataProperty: |
3790 | case DefineAccessorProperty: |
3791 | clobberWorld(); |
3792 | break; |
3793 | |
3794 | case InById: { |
3795 | // FIXME: We can determine when the property definitely exists based on abstract |
3796 | // value information. |
3797 | clobberWorld(); |
3798 | filter(node->child1(), SpecObject); |
3799 | setNonCellTypeForNode(node, SpecBoolean); |
3800 | break; |
3801 | } |
3802 | |
3803 | case InByVal: { |
3804 | AbstractValue& property = forNode(node->child2()); |
3805 | if (JSValue constant = property.value()) { |
3806 | if (constant.isString()) { |
3807 | JSString* string = asString(constant); |
3808 | const StringImpl* impl = string->tryGetValueImpl(); |
3809 | if (impl && impl->isAtom()) |
3810 | m_state.setShouldTryConstantFolding(true); |
3811 | } |
3812 | } |
3813 | |
3814 | // FIXME: We can determine when the property definitely exists based on abstract |
3815 | // value information. |
3816 | clobberWorld(); |
3817 | filter(node->child1(), SpecObject); |
3818 | setNonCellTypeForNode(node, SpecBoolean); |
3819 | break; |
3820 | } |
3821 | |
3822 | case HasOwnProperty: { |
3823 | clobberWorld(); |
3824 | setNonCellTypeForNode(node, SpecBoolean); |
3825 | break; |
3826 | } |
3827 | |
3828 | case GetEnumerableLength: { |
3829 | setNonCellTypeForNode(node, SpecInt32Only); |
3830 | break; |
3831 | } |
3832 | case HasGenericProperty: { |
3833 | setNonCellTypeForNode(node, SpecBoolean); |
3834 | clobberWorld(); |
3835 | break; |
3836 | } |
3837 | case HasStructureProperty: { |
3838 | setNonCellTypeForNode(node, SpecBoolean); |
3839 | clobberWorld(); |
3840 | break; |
3841 | } |
3842 | case HasIndexedProperty: { |
3843 | ArrayMode mode = node->arrayMode(); |
3844 | switch (mode.type()) { |
3845 | case Array::Int32: |
3846 | case Array::Double: |
3847 | case Array::Contiguous: |
3848 | case Array::ArrayStorage: { |
3849 | break; |
3850 | } |
3851 | default: { |
3852 | clobberWorld(); |
3853 | break; |
3854 | } |
3855 | } |
3856 | setNonCellTypeForNode(node, SpecBoolean); |
3857 | break; |
3858 | } |
3859 | case GetDirectPname: { |
3860 | clobberWorld(); |
3861 | makeHeapTopForNode(node); |
3862 | break; |
3863 | } |
3864 | case GetPropertyEnumerator: { |
3865 | setTypeForNode(node, SpecCell); |
3866 | clobberWorld(); |
3867 | break; |
3868 | } |
3869 | case GetEnumeratorStructurePname: { |
3870 | setTypeForNode(node, SpecString | SpecOther); |
3871 | break; |
3872 | } |
3873 | case GetEnumeratorGenericPname: { |
3874 | setTypeForNode(node, SpecString | SpecOther); |
3875 | break; |
3876 | } |
3877 | case ToIndexString: { |
3878 | setTypeForNode(node, SpecString); |
3879 | break; |
3880 | } |
3881 | |
3882 | case GetGlobalVar: |
3883 | makeHeapTopForNode(node); |
3884 | break; |
3885 | |
3886 | case GetGlobalLexicalVariable: |
3887 | makeBytecodeTopForNode(node); |
3888 | break; |
3889 | |
3890 | case GetDynamicVar: |
3891 | clobberWorld(); |
3892 | makeBytecodeTopForNode(node); |
3893 | break; |
3894 | |
3895 | case PutDynamicVar: |
3896 | clobberWorld(); |
3897 | break; |
3898 | |
3899 | case ResolveScope: |
3900 | clobberWorld(); |
3901 | setTypeForNode(node, SpecObject); |
3902 | break; |
3903 | |
3904 | case ResolveScopeForHoistingFuncDeclInEval: |
3905 | clobberWorld(); |
3906 | makeBytecodeTopForNode(node); |
3907 | break; |
3908 | |
3909 | case PutGlobalVariable: |
3910 | case NotifyWrite: |
3911 | break; |
3912 | |
3913 | case OverridesHasInstance: |
3914 | setNonCellTypeForNode(node, SpecBoolean); |
3915 | break; |
3916 | |
3917 | case InstanceOf: |
3918 | clobberWorld(); |
3919 | setNonCellTypeForNode(node, SpecBoolean); |
3920 | break; |
3921 | |
3922 | case InstanceOfCustom: |
3923 | clobberWorld(); |
3924 | setNonCellTypeForNode(node, SpecBoolean); |
3925 | break; |
3926 | |
3927 | case MatchStructure: { |
3928 | AbstractValue base = forNode(node->child1()); |
3929 | RegisteredStructureSet baseSet; |
3930 | |
3931 | BooleanLattice result = BooleanLattice::Bottom; |
3932 | for (MatchStructureVariant& variant : node->matchStructureData().variants) { |
3933 | RegisteredStructure structure = variant.structure; |
3934 | if (!base.contains(structure)) { |
3935 | m_state.setShouldTryConstantFolding(true); |
3936 | continue; |
3937 | } |
3938 | |
3939 | baseSet.add(structure); |
3940 | result = leastUpperBoundOfBooleanLattices( |
3941 | result, variant.result ? BooleanLattice::True : BooleanLattice::False); |
3942 | } |
3943 | |
3944 | if (forNode(node->child1()).changeStructure(m_graph, baseSet) == Contradiction) |
3945 | m_state.setIsValid(false); |
3946 | |
3947 | switch (result) { |
3948 | case BooleanLattice::False: |
3949 | setConstant(node, jsBoolean(false)); |
3950 | break; |
3951 | case BooleanLattice::True: |
3952 | setConstant(node, jsBoolean(true)); |
3953 | break; |
3954 | default: |
3955 | setNonCellTypeForNode(node, SpecBoolean); |
3956 | break; |
3957 | } |
3958 | break; |
3959 | } |
3960 | |
3961 | case Phi: |
3962 | RELEASE_ASSERT(m_graph.m_form == SSA); |
3963 | setForNode(node, forNode(NodeFlowProjection(node, NodeFlowProjection::Shadow))); |
3964 | // The state of this node would have already been decided, but it may have become a |
3965 | // constant, in which case we'd like to know. |
3966 | if (forNode(node).m_value) |
3967 | m_state.setShouldTryConstantFolding(true); |
3968 | break; |
3969 | |
3970 | case Upsilon: { |
3971 | NodeFlowProjection shadow(node->phi(), NodeFlowProjection::Shadow); |
3972 | if (shadow.isStillValid()) { |
3973 | m_state.createValueForNode(shadow); |
3974 | setForNode(shadow, forNode(node->child1())); |
3975 | } |
3976 | break; |
3977 | } |
3978 | |
3979 | case Flush: |
3980 | case PhantomLocal: |
3981 | break; |
3982 | |
3983 | case Call: |
3984 | case TailCallInlinedCaller: |
3985 | case Construct: |
3986 | case CallVarargs: |
3987 | case CallForwardVarargs: |
3988 | case TailCallVarargsInlinedCaller: |
3989 | case ConstructVarargs: |
3990 | case ConstructForwardVarargs: |
3991 | case TailCallForwardVarargsInlinedCaller: |
3992 | case CallEval: |
3993 | case DirectCall: |
3994 | case DirectConstruct: |
3995 | case DirectTailCallInlinedCaller: |
3996 | clobberWorld(); |
3997 | makeHeapTopForNode(node); |
3998 | break; |
3999 | |
4000 | case ForceOSRExit: |
4001 | case CheckBadCell: |
4002 | m_state.setIsValid(false); |
4003 | break; |
4004 | |
4005 | case InvalidationPoint: |
4006 | m_state.setStructureClobberState(StructuresAreWatched); |
4007 | m_state.observeInvalidationPoint(); |
4008 | break; |
4009 | |
4010 | case CPUIntrinsic: |
4011 | if (node->intrinsic() == CPURdtscIntrinsic) |
4012 | setNonCellTypeForNode(node, SpecInt32Only); |
4013 | else |
4014 | setNonCellTypeForNode(node, SpecOther); |
4015 | break; |
4016 | |
4017 | case CheckTraps: |
4018 | case LogShadowChickenPrologue: |
4019 | case LogShadowChickenTail: |
4020 | case ProfileType: |
4021 | case ProfileControlFlow: |
4022 | case Phantom: |
4023 | case CountExecution: |
4024 | case CheckTierUpInLoop: |
4025 | case CheckTierUpAtReturn: |
4026 | case SuperSamplerBegin: |
4027 | case SuperSamplerEnd: |
4028 | case CheckTierUpAndOSREnter: |
4029 | case LoopHint: |
4030 | case ZombieHint: |
4031 | case ExitOK: |
4032 | case FilterCallLinkStatus: |
4033 | case FilterGetByStatus: |
4034 | case FilterPutByIdStatus: |
4035 | case FilterInByIdStatus: |
4036 | case ClearCatchLocals: |
4037 | break; |
4038 | |
4039 | case CheckTypeInfoFlags: { |
4040 | const AbstractValue& abstractValue = forNode(node->child1()); |
4041 | unsigned bits = node->typeInfoOperand(); |
4042 | ASSERT(bits); |
4043 | if (bits == ImplementsDefaultHasInstance) { |
4044 | if (abstractValue.m_type == SpecFunctionWithDefaultHasInstance) { |
4045 | m_state.setShouldTryConstantFolding(true); |
4046 | break; |
4047 | } |
4048 | } |
4049 | |
4050 | if (JSValue value = abstractValue.value()) { |
4051 | if (value.isCell()) { |
4052 | // This works because if we see a cell here, we know it's fully constructed |
4053 | // and we can read its inline type info flags. These flags don't change over the |
4054 | // object's lifetime. |
4055 | if ((value.asCell()->inlineTypeFlags() & bits) == bits) { |
4056 | m_state.setShouldTryConstantFolding(true); |
4057 | break; |
4058 | } |
4059 | } |
4060 | } |
4061 | |
4062 | if (abstractValue.m_structure.isFinite()) { |
4063 | bool ok = true; |
4064 | abstractValue.m_structure.forEach([&] (RegisteredStructure structure) { |
4065 | ok &= (structure->typeInfo().inlineTypeFlags() & bits) == bits; |
4066 | }); |
4067 | if (ok) { |
4068 | m_state.setShouldTryConstantFolding(true); |
4069 | break; |
4070 | } |
4071 | } |
4072 | |
4073 | break; |
4074 | } |
4075 | |
4076 | case ParseInt: { |
4077 | AbstractValue value = forNode(node->child1()); |
4078 | if (value.m_type && !(value.m_type & ~SpecInt32Only)) { |
4079 | JSValue radix; |
4080 | if (!node->child2()) |
4081 | radix = jsNumber(0); |
4082 | else |
4083 | radix = forNode(node->child2()).m_value; |
4084 | |
4085 | if (radix.isNumber() |
4086 | && (radix.asNumber() == 0 || radix.asNumber() == 10)) { |
4087 | m_state.setShouldTryConstantFolding(true); |
4088 | if (node->child1().useKind() == UntypedUse) |
4089 | didFoldClobberWorld(); |
4090 | setNonCellTypeForNode(node, SpecInt32Only); |
4091 | break; |
4092 | } |
4093 | } |
4094 | |
4095 | if (node->child1().useKind() == UntypedUse) |
4096 | clobberWorld(); |
4097 | setNonCellTypeForNode(node, SpecBytecodeNumber); |
4098 | break; |
4099 | } |
4100 | |
4101 | case CreateRest: |
4102 | if (!m_graph.isWatchingHavingABadTimeWatchpoint(node)) { |
4103 | // This means we're already having a bad time. |
4104 | clobberWorld(); |
4105 | setTypeForNode(node, SpecArray); |
4106 | break; |
4107 | } |
4108 | setForNode(node, |
4109 | m_graph.globalObjectFor(node->origin.semantic)->restParameterStructure()); |
4110 | break; |
4111 | |
4112 | case CheckVarargs: |
4113 | case Check: { |
4114 | // Simplify out checks that don't actually do checking. |
4115 | m_graph.doToChildren(node, [&] (Edge edge) { |
4116 | if (!edge) |
4117 | return; |
4118 | if (edge.isProved() || edge.willNotHaveCheck()) |
4119 | m_state.setShouldTryConstantFolding(true); |
4120 | }); |
4121 | break; |
4122 | } |
4123 | |
4124 | case SetFunctionName: { |
4125 | clobberWorld(); |
4126 | break; |
4127 | } |
4128 | |
4129 | case StoreBarrier: |
4130 | case FencedStoreBarrier: { |
4131 | filter(node->child1(), SpecCell); |
4132 | break; |
4133 | } |
4134 | |
4135 | case DataViewGetInt: { |
4136 | DataViewData data = node->dataViewData(); |
4137 | if (data.byteSize < 4) |
4138 | setNonCellTypeForNode(node, SpecInt32Only); |
4139 | else { |
4140 | ASSERT(data.byteSize == 4); |
4141 | if (data.isSigned) |
4142 | setNonCellTypeForNode(node, SpecInt32Only); |
4143 | else |
4144 | setNonCellTypeForNode(node, SpecInt52Any); |
4145 | } |
4146 | break; |
4147 | } |
4148 | |
4149 | case DataViewGetFloat: { |
4150 | setNonCellTypeForNode(node, SpecFullDouble); |
4151 | break; |
4152 | } |
4153 | |
4154 | case DateGetInt32OrNaN: { |
4155 | setNonCellTypeForNode(node, SpecInt32Only | SpecDoublePureNaN); |
4156 | break; |
4157 | } |
4158 | |
4159 | case DateGetTime: { |
4160 | setNonCellTypeForNode(node, SpecFullDouble); |
4161 | break; |
4162 | } |
4163 | |
4164 | case DataViewSet: { |
4165 | break; |
4166 | } |
4167 | |
4168 | case Unreachable: |
4169 | // It may be that during a previous run of AI we proved that something was unreachable, but |
4170 | // during this run of AI we forget that it's unreachable. AI's proofs don't have to get |
4171 | // monotonically stronger over time. So, we don't assert that AI doesn't reach the |
4172 | // Unreachable. We have no choice but to take our past proof at face value. Otherwise we'll |
4173 | // crash whenever AI fails to be as powerful on run K as it was on run K-1. |
4174 | m_state.setIsValid(false); |
4175 | break; |
4176 | |
4177 | case LastNodeType: |
4178 | case ArithIMul: |
4179 | case FiatInt52: |
4180 | DFG_CRASH(m_graph, node, "Unexpected node type" ); |
4181 | break; |
4182 | } |
4183 | |
4184 | return m_state.isValid(); |
4185 | } |
4186 | |
4187 | template<typename AbstractStateType> |
4188 | void AbstractInterpreter<AbstractStateType>::filterICStatus(Node* node) |
4189 | { |
4190 | switch (node->op()) { |
4191 | case FilterCallLinkStatus: |
4192 | if (JSValue value = forNode(node->child1()).m_value) |
4193 | node->callLinkStatus()->filter(m_vm, value); |
4194 | break; |
4195 | |
4196 | case FilterGetByStatus: { |
4197 | AbstractValue& value = forNode(node->child1()); |
4198 | if (value.m_structure.isFinite()) |
4199 | node->getByStatus()->filter(value.m_structure.toStructureSet()); |
4200 | break; |
4201 | } |
4202 | |
4203 | case FilterInByIdStatus: { |
4204 | AbstractValue& value = forNode(node->child1()); |
4205 | if (value.m_structure.isFinite()) |
4206 | node->inByIdStatus()->filter(value.m_structure.toStructureSet()); |
4207 | break; |
4208 | } |
4209 | |
4210 | case FilterPutByIdStatus: { |
4211 | AbstractValue& value = forNode(node->child1()); |
4212 | if (value.m_structure.isFinite()) |
4213 | node->putByIdStatus()->filter(value.m_structure.toStructureSet()); |
4214 | break; |
4215 | } |
4216 | |
4217 | default: |
4218 | RELEASE_ASSERT_NOT_REACHED(); |
4219 | break; |
4220 | } |
4221 | } |
4222 | |
4223 | template<typename AbstractStateType> |
4224 | bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned indexInBlock) |
4225 | { |
4226 | return executeEffects(indexInBlock, m_state.block()->at(indexInBlock)); |
4227 | } |
4228 | |
4229 | template<typename AbstractStateType> |
4230 | bool AbstractInterpreter<AbstractStateType>::execute(unsigned indexInBlock) |
4231 | { |
4232 | Node* node = m_state.block()->at(indexInBlock); |
4233 | |
4234 | startExecuting(); |
4235 | executeEdges(node); |
4236 | return executeEffects(indexInBlock, node); |
4237 | } |
4238 | |
4239 | template<typename AbstractStateType> |
4240 | bool AbstractInterpreter<AbstractStateType>::execute(Node* node) |
4241 | { |
4242 | startExecuting(); |
4243 | executeEdges(node); |
4244 | return executeEffects(UINT_MAX, node); |
4245 | } |
4246 | |
4247 | template<typename AbstractStateType> |
4248 | void AbstractInterpreter<AbstractStateType>::clobberWorld() |
4249 | { |
4250 | clobberStructures(); |
4251 | } |
4252 | |
4253 | template<typename AbstractStateType> |
4254 | void AbstractInterpreter<AbstractStateType>::didFoldClobberWorld() |
4255 | { |
4256 | didFoldClobberStructures(); |
4257 | } |
4258 | |
4259 | template<typename AbstractStateType> |
4260 | template<typename Functor> |
4261 | void AbstractInterpreter<AbstractStateType>::forAllValues( |
4262 | unsigned clobberLimit, Functor& functor) |
4263 | { |
4264 | if (clobberLimit >= m_state.block()->size()) |
4265 | clobberLimit = m_state.block()->size(); |
4266 | else |
4267 | clobberLimit++; |
4268 | ASSERT(clobberLimit <= m_state.block()->size()); |
4269 | for (size_t i = clobberLimit; i--;) { |
4270 | NodeFlowProjection::forEach( |
4271 | m_state.block()->at(i), |
4272 | [&] (NodeFlowProjection nodeProjection) { |
4273 | functor(forNode(nodeProjection)); |
4274 | }); |
4275 | } |
4276 | if (m_graph.m_form == SSA) { |
4277 | for (NodeFlowProjection node : m_state.block()->ssa->liveAtHead) { |
4278 | if (node.isStillValid()) |
4279 | functor(forNode(node)); |
4280 | } |
4281 | } |
4282 | for (size_t i = m_state.numberOfArguments(); i--;) |
4283 | functor(m_state.argument(i)); |
4284 | for (size_t i = m_state.numberOfLocals(); i--;) |
4285 | functor(m_state.local(i)); |
4286 | } |
4287 | |
4288 | template<typename AbstractStateType> |
4289 | void AbstractInterpreter<AbstractStateType>::clobberStructures() |
4290 | { |
4291 | m_state.clobberStructures(); |
4292 | m_state.mergeClobberState(AbstractInterpreterClobberState::ClobberedStructures); |
4293 | m_state.setStructureClobberState(StructuresAreClobbered); |
4294 | } |
4295 | |
4296 | template<typename AbstractStateType> |
4297 | void AbstractInterpreter<AbstractStateType>::didFoldClobberStructures() |
4298 | { |
4299 | m_state.mergeClobberState(AbstractInterpreterClobberState::FoldedClobber); |
4300 | } |
4301 | |
4302 | template<typename AbstractStateType> |
4303 | void AbstractInterpreter<AbstractStateType>::observeTransition( |
4304 | unsigned clobberLimit, RegisteredStructure from, RegisteredStructure to) |
4305 | { |
4306 | // Stop performing precise structure transition tracking. |
4307 | // Precise structure transition tracking shows quadratic complexity for # of nodes in a basic block. |
4308 | // If it is too large, we conservatively clobber all the structures. |
4309 | if (m_state.block()->size() > Options::maxDFGNodesInBasicBlockForPreciseAnalysis()) { |
4310 | clobberStructures(); |
4311 | return; |
4312 | } |
4313 | |
4314 | AbstractValue::TransitionObserver transitionObserver(from, to); |
4315 | forAllValues(clobberLimit, transitionObserver); |
4316 | |
4317 | ASSERT(!from->dfgShouldWatch()); // We don't need to claim to be in a clobbered state because 'from' was never watchable (during the time we were compiling), hence no constants ever introduced into the DFG IR that ever had a watchable structure would ever have the same structure as from. |
4318 | |
4319 | m_state.mergeClobberState(AbstractInterpreterClobberState::ObservedTransitions); |
4320 | } |
4321 | |
4322 | template<typename AbstractStateType> |
4323 | void AbstractInterpreter<AbstractStateType>::observeTransitions( |
4324 | unsigned clobberLimit, const TransitionVector& vector) |
4325 | { |
4326 | if (vector.isEmpty()) |
4327 | return; |
4328 | |
4329 | // Stop performing precise structure transition tracking. |
4330 | // Precise structure transition tracking shows quadratic complexity for # of nodes in a basic block. |
4331 | // If it is too large, we conservatively clobber all the structures. |
4332 | if (m_state.block()->size() > Options::maxDFGNodesInBasicBlockForPreciseAnalysis()) { |
4333 | clobberStructures(); |
4334 | return; |
4335 | } |
4336 | |
4337 | AbstractValue::TransitionsObserver transitionsObserver(vector); |
4338 | forAllValues(clobberLimit, transitionsObserver); |
4339 | |
4340 | if (!ASSERT_DISABLED) { |
4341 | // We don't need to claim to be in a clobbered state because none of the Transition::previous structures are watchable. |
4342 | for (unsigned i = vector.size(); i--;) |
4343 | ASSERT(!vector[i].previous->dfgShouldWatch()); |
4344 | } |
4345 | |
4346 | m_state.mergeClobberState(AbstractInterpreterClobberState::ObservedTransitions); |
4347 | } |
4348 | |
4349 | template<typename AbstractStateType> |
4350 | void AbstractInterpreter<AbstractStateType>::dump(PrintStream& out) const |
4351 | { |
4352 | const_cast<AbstractInterpreter<AbstractStateType>*>(this)->dump(out); |
4353 | } |
4354 | |
4355 | template<typename AbstractStateType> |
4356 | void AbstractInterpreter<AbstractStateType>::dump(PrintStream& out) |
4357 | { |
4358 | CommaPrinter comma(" " ); |
4359 | HashSet<NodeFlowProjection> seen; |
4360 | if (m_graph.m_form == SSA) { |
4361 | for (NodeFlowProjection node : m_state.block()->ssa->liveAtHead) { |
4362 | seen.add(node); |
4363 | AbstractValue& value = forNode(node); |
4364 | if (value.isClear()) |
4365 | continue; |
4366 | out.print(comma, node, ":" , value); |
4367 | } |
4368 | } |
4369 | for (size_t i = 0; i < m_state.block()->size(); ++i) { |
4370 | NodeFlowProjection::forEach( |
4371 | m_state.block()->at(i), [&] (NodeFlowProjection nodeProjection) { |
4372 | seen.add(nodeProjection); |
4373 | AbstractValue& value = forNode(nodeProjection); |
4374 | if (value.isClear()) |
4375 | return; |
4376 | out.print(comma, nodeProjection, ":" , value); |
4377 | }); |
4378 | } |
4379 | if (m_graph.m_form == SSA) { |
4380 | for (NodeFlowProjection node : m_state.block()->ssa->liveAtTail) { |
4381 | if (seen.contains(node)) |
4382 | continue; |
4383 | AbstractValue& value = forNode(node); |
4384 | if (value.isClear()) |
4385 | continue; |
4386 | out.print(comma, node, ":" , value); |
4387 | } |
4388 | } |
4389 | } |
4390 | |
4391 | template<typename AbstractStateType> |
4392 | FiltrationResult AbstractInterpreter<AbstractStateType>::filter( |
4393 | AbstractValue& value, const RegisteredStructureSet& set, SpeculatedType admittedTypes) |
4394 | { |
4395 | if (value.filter(m_graph, set, admittedTypes) == FiltrationOK) |
4396 | return FiltrationOK; |
4397 | m_state.setIsValid(false); |
4398 | return Contradiction; |
4399 | } |
4400 | |
4401 | template<typename AbstractStateType> |
4402 | FiltrationResult AbstractInterpreter<AbstractStateType>::filterArrayModes( |
4403 | AbstractValue& value, ArrayModes arrayModes) |
4404 | { |
4405 | if (value.filterArrayModes(arrayModes) == FiltrationOK) |
4406 | return FiltrationOK; |
4407 | m_state.setIsValid(false); |
4408 | return Contradiction; |
4409 | } |
4410 | |
4411 | template<typename AbstractStateType> |
4412 | FiltrationResult AbstractInterpreter<AbstractStateType>::filter( |
4413 | AbstractValue& value, SpeculatedType type) |
4414 | { |
4415 | if (value.filter(type) == FiltrationOK) |
4416 | return FiltrationOK; |
4417 | m_state.setIsValid(false); |
4418 | return Contradiction; |
4419 | } |
4420 | |
4421 | template<typename AbstractStateType> |
4422 | FiltrationResult AbstractInterpreter<AbstractStateType>::filterByValue( |
4423 | AbstractValue& abstractValue, FrozenValue concreteValue) |
4424 | { |
4425 | if (abstractValue.filterByValue(concreteValue) == FiltrationOK) |
4426 | return FiltrationOK; |
4427 | m_state.setIsValid(false); |
4428 | return Contradiction; |
4429 | } |
4430 | |
4431 | template<typename AbstractStateType> |
4432 | FiltrationResult AbstractInterpreter<AbstractStateType>::filterClassInfo( |
4433 | AbstractValue& value, const ClassInfo* classInfo) |
4434 | { |
4435 | if (value.filterClassInfo(m_graph, classInfo) == FiltrationOK) |
4436 | return FiltrationOK; |
4437 | m_state.setIsValid(false); |
4438 | return Contradiction; |
4439 | } |
4440 | |
4441 | template<typename AbstractStateType> |
4442 | void AbstractInterpreter<AbstractStateType>::executeDoubleUnaryOpEffects(Node* node, double(*equivalentFunction)(double)) |
4443 | { |
4444 | JSValue child = forNode(node->child1()).value(); |
4445 | if (Optional<double> number = child.toNumberFromPrimitive()) { |
4446 | if (node->child1().useKind() != DoubleRepUse) |
4447 | didFoldClobberWorld(); |
4448 | setConstant(node, jsDoubleNumber(equivalentFunction(*number))); |
4449 | return; |
4450 | } |
4451 | SpeculatedType type; |
4452 | if (node->child1().useKind() == DoubleRepUse) |
4453 | type = typeOfDoubleUnaryOp(forNode(node->child1()).m_type); |
4454 | else { |
4455 | clobberWorld(); |
4456 | type = SpecBytecodeNumber; |
4457 | } |
4458 | setNonCellTypeForNode(node, type); |
4459 | } |
4460 | |
4461 | } } // namespace JSC::DFG |
4462 | |
4463 | #endif // ENABLE(DFG_JIT) |
4464 | |