[DFG] AI should convert CreateThis to NewObject if the prototype object is proved
[WebKit-https.git] / Source / JavaScriptCore / dfg / DFGConstantFoldingPhase.cpp
1 /*
2  * Copyright (C) 2012-2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "DFGConstantFoldingPhase.h"
28
29 #if ENABLE(DFG_JIT)
30
31 #include "DFGAbstractInterpreterInlines.h"
32 #include "DFGArgumentsUtilities.h"
33 #include "DFGBasicBlockInlines.h"
34 #include "DFGGraph.h"
35 #include "DFGInPlaceAbstractState.h"
36 #include "DFGInferredTypeCheck.h"
37 #include "DFGInsertionSet.h"
38 #include "DFGPhase.h"
39 #include "GetByIdStatus.h"
40 #include "JSCInlines.h"
41 #include "PutByIdStatus.h"
42
43 namespace JSC { namespace DFG {
44
45 class ConstantFoldingPhase : public Phase {
46 public:
47     ConstantFoldingPhase(Graph& graph)
48         : Phase(graph, "constant folding")
49         , m_state(graph)
50         , m_interpreter(graph, m_state)
51         , m_insertionSet(graph)
52     {
53     }
54     
55     bool run()
56     {
57         bool changed = false;
58
59         for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
60             if (block->cfaFoundConstants)
61                 changed |= foldConstants(block);
62         }
63         
64         if (changed && m_graph.m_form == SSA) {
65             // It's now possible that we have Upsilons pointed at JSConstants. Fix that.
66             for (BasicBlock* block : m_graph.blocksInNaturalOrder())
67                 fixUpsilons(block);
68         }
69
70         if (m_graph.m_form == SSA) {
71             // It's now possible to simplify basic blocks by placing an Unreachable terminator right
72             // after anything that invalidates AI.
73             bool didClipBlock = false;
74             Vector<Node*> nodesToDelete;
75             for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
76                 m_state.beginBasicBlock(block);
77                 for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
78                     if (block->at(nodeIndex)->isTerminal()) {
79                         // It's possible that we have something after the terminal. It could be a
80                         // no-op Check node, for example. We don't want the logic below to turn that
81                         // node into Unreachable, since then we'd have two terminators.
82                         break;
83                     }
84                     if (!m_state.isValid()) {
85                         NodeOrigin origin = block->at(nodeIndex)->origin;
86                         for (unsigned killIndex = nodeIndex; killIndex < block->size(); ++killIndex)
87                             nodesToDelete.append(block->at(killIndex));
88                         block->resize(nodeIndex);
89                         block->appendNode(m_graph, SpecNone, Unreachable, origin);
90                         didClipBlock = true;
91                         break;
92                     }
93                     m_interpreter.execute(nodeIndex);
94                 }
95                 m_state.reset();
96             }
97
98             if (didClipBlock) {
99                 changed = true;
100
101                 m_graph.invalidateNodeLiveness();
102
103                 for (Node* node : nodesToDelete)
104                     m_graph.deleteNode(node);
105
106                 m_graph.invalidateCFG();
107                 m_graph.resetReachability();
108                 m_graph.killUnreachableBlocks();
109             }
110         }
111          
112         return changed;
113     }
114
115 private:
116     bool foldConstants(BasicBlock* block)
117     {
118         bool changed = false;
119         m_state.beginBasicBlock(block);
120         for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
121             if (!m_state.isValid())
122                 break;
123             
124             Node* node = block->at(indexInBlock);
125
126             bool alreadyHandled = false;
127             bool eliminated = false;
128                     
129             switch (node->op()) {
130             case BooleanToNumber: {
131                 if (node->child1().useKind() == UntypedUse
132                     && !m_interpreter.needsTypeCheck(node->child1(), SpecBoolean))
133                     node->child1().setUseKind(BooleanUse);
134                 break;
135             }
136
137             case CompareEq: {
138                 // FIXME: We should add back the broken folding phase here for comparisions where we prove at least one side has type SpecOther.
139                 // See: https://bugs.webkit.org/show_bug.cgi?id=174844
140                 break;
141             }
142
143             case CompareStrictEq: {
144                 if (node->isBinaryUseKind(UntypedUse)) {
145                     JSValue child1Constant = m_state.forNode(node->child1().node()).value();
146                     JSValue child2Constant = m_state.forNode(node->child2().node()).value();
147
148                     // FIXME: Revisit this condition when introducing BigInt to JSC.
149                     auto isNonStringCellConstant = [] (JSValue value) {
150                         return value && value.isCell() && !value.isString();
151                     };
152
153                     if (isNonStringCellConstant(child1Constant)) {
154                         node->convertToCompareEqPtr(m_graph.freezeStrong(child1Constant.asCell()), node->child2());
155                         changed = true;
156                     } else if (isNonStringCellConstant(child2Constant)) {
157                         node->convertToCompareEqPtr(m_graph.freezeStrong(child2Constant.asCell()), node->child1());
158                         changed = true;
159                     }
160                 }
161                 break;
162             }
163
164             case CheckStructureOrEmpty: {
165                 const AbstractValue& value = m_state.forNode(node->child1());
166                 if (value.m_type & SpecEmpty)
167                     break;
168                 node->convertCheckStructureOrEmptyToCheckStructure();
169                 changed = true;
170                 FALLTHROUGH;
171             }
172             case CheckStructure:
173             case ArrayifyToStructure: {
174                 AbstractValue& value = m_state.forNode(node->child1());
175                 RegisteredStructureSet set;
176                 if (node->op() == ArrayifyToStructure)
177                     set = node->structure();
178                 else {
179                     set = node->structureSet();
180                     if ((SpecCellCheck & SpecEmpty) && node->child1().useKind() == CellUse && m_state.forNode(node->child1()).m_type & SpecEmpty) {
181                         m_insertionSet.insertNode(
182                             indexInBlock, SpecNone, AssertNotEmpty, node->origin, Edge(node->child1().node(), UntypedUse));
183                     }
184                 }
185                 if (value.m_structure.isSubsetOf(set)) {
186                     m_interpreter.execute(indexInBlock); // Catch the fact that we may filter on cell.
187                     node->remove(m_graph);
188                     eliminated = true;
189                     break;
190                 }
191                 break;
192             }
193
194             case CheckSubClass: {
195                 JSValue constant = m_state.forNode(node->child1()).value();
196                 if (constant) {
197                     if (constant.isCell() && constant.asCell()->inherits(m_graph.m_vm, node->classInfo())) {
198                         m_interpreter.execute(indexInBlock);
199                         node->remove(m_graph);
200                         eliminated = true;
201                         break;
202                     }
203                 }
204
205                 AbstractValue& value = m_state.forNode(node->child1());
206
207                 if (value.m_structure.isSubClassOf(node->classInfo())) {
208                     m_interpreter.execute(indexInBlock);
209                     node->remove(m_graph);
210                     eliminated = true;
211                     break;
212                 }
213                 break;
214             }
215                 
216             case GetIndexedPropertyStorage: {
217                 JSArrayBufferView* view = m_graph.tryGetFoldableView(
218                     m_state.forNode(node->child1()).m_value, node->arrayMode());
219                 if (!view)
220                     break;
221                 
222                 if (view->mode() == FastTypedArray) {
223                     // FIXME: It would be awesome to be able to fold the property storage for
224                     // these GC-allocated typed arrays. For now it doesn't matter because the
225                     // most common use-cases for constant typed arrays involve large arrays with
226                     // aliased buffer views.
227                     // https://bugs.webkit.org/show_bug.cgi?id=125425
228                     break;
229                 }
230                 
231                 m_interpreter.execute(indexInBlock);
232                 eliminated = true;
233                 
234                 m_insertionSet.insertCheck(indexInBlock, node->origin, node->children);
235                 node->convertToConstantStoragePointer(view->vector());
236                 break;
237             }
238                 
239             case CheckStructureImmediate: {
240                 AbstractValue& value = m_state.forNode(node->child1());
241                 const RegisteredStructureSet& set = node->structureSet();
242                 
243                 if (value.value()) {
244                     if (Structure* structure = jsDynamicCast<Structure*>(m_graph.m_vm, value.value())) {
245                         if (set.contains(m_graph.registerStructure(structure))) {
246                             m_interpreter.execute(indexInBlock);
247                             node->remove(m_graph);
248                             eliminated = true;
249                             break;
250                         }
251                     }
252                 }
253                 
254                 if (PhiChildren* phiChildren = m_interpreter.phiChildren()) {
255                     bool allGood = true;
256                     phiChildren->forAllTransitiveIncomingValues(
257                         node,
258                         [&] (Node* incoming) {
259                             if (Structure* structure = incoming->dynamicCastConstant<Structure*>(m_graph.m_vm)) {
260                                 if (set.contains(m_graph.registerStructure(structure)))
261                                     return;
262                             }
263                             allGood = false;
264                         });
265                     if (allGood) {
266                         m_interpreter.execute(indexInBlock);
267                         node->remove(m_graph);
268                         eliminated = true;
269                         break;
270                     }
271                 }
272                 break;
273             }
274                 
275             case CheckArray:
276             case Arrayify: {
277                 if (!node->arrayMode().alreadyChecked(m_graph, node, m_state.forNode(node->child1())))
278                     break;
279                 node->remove(m_graph);
280                 eliminated = true;
281                 break;
282             }
283                 
284             case PutStructure: {
285                 if (m_state.forNode(node->child1()).m_structure.onlyStructure() != node->transition()->next)
286                     break;
287                 
288                 node->remove(m_graph);
289                 eliminated = true;
290                 break;
291             }
292                 
293             case CheckCell: {
294                 if (m_state.forNode(node->child1()).value() != node->cellOperand()->value())
295                     break;
296                 node->remove(m_graph);
297                 eliminated = true;
298                 break;
299             }
300
301             case AssertNotEmpty:
302             case CheckNotEmpty: {
303                 if (m_state.forNode(node->child1()).m_type & SpecEmpty)
304                     break;
305                 node->remove(m_graph);
306                 eliminated = true;
307                 break;
308             }
309
310             case CheckStringIdent: {
311                 UniquedStringImpl* uid = node->uidOperand();
312                 const UniquedStringImpl* constantUid = nullptr;
313
314                 JSValue childConstant = m_state.forNode(node->child1()).value();
315                 if (childConstant) {
316                     if (childConstant.isString()) {
317                         if (const auto* impl = asString(childConstant)->tryGetValueImpl()) {
318                             // Edge filtering requires that a value here should be StringIdent.
319                             // However, a constant value propagated in DFG is not filtered.
320                             // So here, we check the propagated value is actually an atomic string.
321                             // And if it's not, we just ignore.
322                             if (impl->isAtomic())
323                                 constantUid = static_cast<const UniquedStringImpl*>(impl);
324                         }
325                     }
326                 }
327
328                 if (constantUid == uid) {
329                     node->remove(m_graph);
330                     eliminated = true;
331                 }
332                 break;
333             }
334
335             case CheckInBounds: {
336                 JSValue left = m_state.forNode(node->child1()).value();
337                 JSValue right = m_state.forNode(node->child2()).value();
338                 if (left && right && left.isInt32() && right.isInt32()
339                     && static_cast<uint32_t>(left.asInt32()) < static_cast<uint32_t>(right.asInt32())) {
340                     node->remove(m_graph);
341                     eliminated = true;
342                     break;
343                 }
344                 
345                 break;
346             }
347                 
348             case GetMyArgumentByVal:
349             case GetMyArgumentByValOutOfBounds: {
350                 JSValue indexValue = m_state.forNode(node->child2()).value();
351                 if (!indexValue || !indexValue.isUInt32())
352                     break;
353
354                 Checked<unsigned, RecordOverflow> checkedIndex = indexValue.asUInt32();
355                 checkedIndex += node->numberOfArgumentsToSkip();
356                 if (checkedIndex.hasOverflowed())
357                     break;
358                 
359                 unsigned index = checkedIndex.unsafeGet();
360                 Node* arguments = node->child1().node();
361                 InlineCallFrame* inlineCallFrame = arguments->origin.semantic.inlineCallFrame;
362                 
363                 // Don't try to do anything if the index is known to be outside our static bounds. Note
364                 // that our static bounds are usually strictly larger than the dynamic bounds. The
365                 // exception is something like this, assuming foo() is not inlined:
366                 //
367                 // function foo() { return arguments[5]; }
368                 //
369                 // Here the static bound on number of arguments is 0, and we're accessing index 5. We
370                 // will not strength-reduce this to GetStack because GetStack is otherwise assumed by the
371                 // compiler to access those variables that are statically accounted for; for example if
372                 // we emitted a GetStack on arg6 we would have out-of-bounds access crashes anywhere that
373                 // uses an Operands<> map. There is not much cost to continuing to use a
374                 // GetMyArgumentByVal in such statically-out-of-bounds accesses; we just lose CFA unless
375                 // GCSE removes the access entirely.
376                 if (inlineCallFrame) {
377                     if (index >= inlineCallFrame->argumentCountIncludingThis - 1)
378                         break;
379                 } else {
380                     if (index >= m_state.variables().numberOfArguments() - 1)
381                         break;
382                 }
383                 
384                 m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
385                 
386                 StackAccessData* data;
387                 if (inlineCallFrame) {
388                     data = m_graph.m_stackAccessData.add(
389                         VirtualRegister(
390                             inlineCallFrame->stackOffset +
391                             CallFrame::argumentOffset(index)),
392                         FlushedJSValue);
393                 } else {
394                     data = m_graph.m_stackAccessData.add(
395                         virtualRegisterForArgument(index + 1), FlushedJSValue);
396                 }
397                 
398                 if (inlineCallFrame && !inlineCallFrame->isVarargs() && index < inlineCallFrame->argumentCountIncludingThis - 1) {
399                     node->convertToGetStack(data);
400                     eliminated = true;
401                     break;
402                 }
403                 
404                 if (node->op() == GetMyArgumentByValOutOfBounds)
405                     break;
406                 
407                 Node* length = emitCodeToGetArgumentsArrayLength(
408                     m_insertionSet, arguments, indexInBlock, node->origin);
409                 m_insertionSet.insertNode(
410                     indexInBlock, SpecNone, CheckInBounds, node->origin,
411                     node->child2(), Edge(length, Int32Use));
412                 node->convertToGetStack(data);
413                 eliminated = true;
414                 break;
415             }
416                 
417             case MultiGetByOffset: {
418                 Edge baseEdge = node->child1();
419                 Node* base = baseEdge.node();
420                 MultiGetByOffsetData& data = node->multiGetByOffsetData();
421
422                 // First prune the variants, then check if the MultiGetByOffset can be
423                 // strength-reduced to a GetByOffset.
424                 
425                 AbstractValue baseValue = m_state.forNode(base);
426                 
427                 m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
428                 alreadyHandled = true; // Don't allow the default constant folder to do things to this.
429                 
430                 for (unsigned i = 0; i < data.cases.size(); ++i) {
431                     MultiGetByOffsetCase& getCase = data.cases[i];
432                     getCase.set().filter(baseValue);
433                     if (getCase.set().isEmpty()) {
434                         data.cases[i--] = data.cases.last();
435                         data.cases.removeLast();
436                         changed = true;
437                     }
438                 }
439                 
440                 if (data.cases.size() != 1)
441                     break;
442                 
443                 emitGetByOffset(indexInBlock, node, baseValue, data.cases[0], data.identifierNumber);
444                 changed = true;
445                 break;
446             }
447                 
448             case MultiPutByOffset: {
449                 Edge baseEdge = node->child1();
450                 Node* base = baseEdge.node();
451                 MultiPutByOffsetData& data = node->multiPutByOffsetData();
452                 
453                 AbstractValue baseValue = m_state.forNode(base);
454
455                 m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
456                 alreadyHandled = true; // Don't allow the default constant folder to do things to this.
457                 
458
459                 for (unsigned i = 0; i < data.variants.size(); ++i) {
460                     PutByIdVariant& variant = data.variants[i];
461                     variant.oldStructure().genericFilter([&] (Structure* structure) -> bool {
462                         return baseValue.contains(m_graph.registerStructure(structure));
463                     });
464                     
465                     if (variant.oldStructure().isEmpty()) {
466                         data.variants[i--] = data.variants.last();
467                         data.variants.removeLast();
468                         changed = true;
469                         continue;
470                     }
471                     
472                     if (variant.kind() == PutByIdVariant::Transition
473                         && variant.oldStructure().onlyStructure() == variant.newStructure()) {
474                         variant = PutByIdVariant::replace(
475                             variant.oldStructure(),
476                             variant.offset(),
477                             variant.requiredType());
478                         changed = true;
479                     }
480                 }
481
482                 if (data.variants.size() != 1)
483                     break;
484                 
485                 emitPutByOffset(
486                     indexInBlock, node, baseValue, data.variants[0], data.identifierNumber);
487                 changed = true;
488                 break;
489             }
490         
491             case GetById:
492             case GetByIdFlush: {
493                 Edge childEdge = node->child1();
494                 Node* child = childEdge.node();
495                 unsigned identifierNumber = node->identifierNumber();
496                 
497                 AbstractValue baseValue = m_state.forNode(child);
498
499                 m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
500                 alreadyHandled = true; // Don't allow the default constant folder to do things to this.
501
502                 if (!baseValue.m_structure.isFinite()
503                     || (node->child1().useKind() == UntypedUse || (baseValue.m_type & ~SpecCell)))
504                     break;
505                 
506                 GetByIdStatus status = GetByIdStatus::computeFor(
507                     baseValue.m_structure.toStructureSet(), m_graph.identifiers()[identifierNumber]);
508                 if (!status.isSimple())
509                     break;
510                 
511                 for (unsigned i = status.numVariants(); i--;) {
512                     if (!status[i].conditionSet().isEmpty()) {
513                         // FIXME: We could handle prototype cases.
514                         // https://bugs.webkit.org/show_bug.cgi?id=110386
515                         break;
516                     }
517                 }
518                 
519                 if (status.numVariants() == 1) {
520                     emitGetByOffset(indexInBlock, node, baseValue, status[0], identifierNumber);
521                     changed = true;
522                     break;
523                 }
524                 
525                 if (!isFTL(m_graph.m_plan.mode))
526                     break;
527                 
528                 MultiGetByOffsetData* data = m_graph.m_multiGetByOffsetData.add();
529                 for (const GetByIdVariant& variant : status.variants()) {
530                     data->cases.append(
531                         MultiGetByOffsetCase(
532                             *m_graph.addStructureSet(variant.structureSet()),
533                             GetByOffsetMethod::load(variant.offset())));
534                 }
535                 data->identifierNumber = identifierNumber;
536                 node->convertToMultiGetByOffset(data);
537                 changed = true;
538                 break;
539             }
540                 
541             case PutById:
542             case PutByIdDirect:
543             case PutByIdFlush: {
544                 NodeOrigin origin = node->origin;
545                 Edge childEdge = node->child1();
546                 Node* child = childEdge.node();
547                 unsigned identifierNumber = node->identifierNumber();
548                 
549                 ASSERT(childEdge.useKind() == CellUse);
550                 
551                 AbstractValue baseValue = m_state.forNode(child);
552                 AbstractValue valueValue = m_state.forNode(node->child2());
553
554                 m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
555                 alreadyHandled = true; // Don't allow the default constant folder to do things to this.
556
557                 if (!baseValue.m_structure.isFinite())
558                     break;
559                 
560                 PutByIdStatus status = PutByIdStatus::computeFor(
561                     m_graph.globalObjectFor(origin.semantic),
562                     baseValue.m_structure.toStructureSet(),
563                     m_graph.identifiers()[identifierNumber],
564                     node->op() == PutByIdDirect);
565                 
566                 if (!status.isSimple())
567                     break;
568
569                 ASSERT(status.numVariants());
570                 
571                 if (status.numVariants() > 1 && !isFTL(m_graph.m_plan.mode))
572                     break;
573                 
574                 changed = true;
575
576                 bool allGood = true;
577                 for (const PutByIdVariant& variant : status.variants()) {
578                     if (!allGood)
579                         break;
580                     for (const ObjectPropertyCondition& condition : variant.conditionSet()) {
581                         if (m_graph.watchCondition(condition))
582                             continue;
583
584                         Structure* structure = condition.object()->structure();
585                         if (!condition.structureEnsuresValidity(structure)) {
586                             allGood = false;
587                             break;
588                         }
589
590                         m_insertionSet.insertNode(
591                             indexInBlock, SpecNone, CheckStructure, node->origin,
592                             OpInfo(m_graph.addStructureSet(structure)),
593                             m_insertionSet.insertConstantForUse(
594                                 indexInBlock, node->origin, condition.object(), KnownCellUse));
595                     }
596                 }
597
598                 if (!allGood)
599                     break;
600                 
601                 if (status.numVariants() == 1) {
602                     emitPutByOffset(indexInBlock, node, baseValue, status[0], identifierNumber);
603                     break;
604                 }
605                 
606                 ASSERT(isFTL(m_graph.m_plan.mode));
607
608                 MultiPutByOffsetData* data = m_graph.m_multiPutByOffsetData.add();
609                 data->variants = status.variants();
610                 data->identifierNumber = identifierNumber;
611                 node->convertToMultiPutByOffset(data);
612                 break;
613             }
614
615             case ToPrimitive: {
616                 if (m_state.forNode(node->child1()).m_type & ~(SpecFullNumber | SpecBoolean | SpecString | SpecSymbol))
617                     break;
618                 
619                 node->convertToIdentity();
620                 changed = true;
621                 break;
622             }
623
624             case ToThis: {
625                 ToThisResult result = isToThisAnIdentity(m_graph.m_vm, m_graph.executableFor(node->origin.semantic)->isStrictMode(), m_state.forNode(node->child1()));
626                 if (result == ToThisResult::Identity) {
627                     node->convertToIdentity();
628                     changed = true;
629                     break;
630                 }
631                 if (result == ToThisResult::GlobalThis) {
632                     node->convertToGetGlobalThis();
633                     changed = true;
634                     break;
635                 }
636                 break;
637             }
638
639             case CreateThis: {
640                 if (JSValue base = m_state.forNode(node->child1()).m_value) {
641                     if (auto* function = jsDynamicCast<JSFunction*>(m_graph.m_vm, base)) {
642                         if (FunctionRareData* rareData = function->rareData()) {
643                             if (Structure* structure = rareData->objectAllocationStructure()) {
644                                 // FIXME: we should be able to allocate a poly proto object here:
645                                 // https://bugs.webkit.org/show_bug.cgi?id=177517
646                                 if (structure->hasMonoProto()) {
647                                     m_graph.freeze(rareData);
648                                     m_graph.watchpoints().addLazily(rareData->allocationProfileWatchpointSet());
649                                     node->convertToNewObject(m_graph.registerStructure(structure));
650                                     changed = true;
651                                     break;
652                                 }
653                             }
654                         }
655                     }
656                 }
657                 break;
658             }
659
660             case ToNumber: {
661                 if (m_state.forNode(node->child1()).m_type & ~SpecBytecodeNumber)
662                     break;
663
664                 node->convertToIdentity();
665                 changed = true;
666                 break;
667             }
668
669             case NormalizeMapKey: {
670                 SpeculatedType typeMaybeNormalized = (SpecFullNumber & ~SpecInt32Only);
671                 if (m_state.forNode(node->child1()).m_type & typeMaybeNormalized)
672                     break;
673
674                 node->convertToIdentity();
675                 changed = true;
676                 break;
677             }
678
679             case ParseInt: {
680                 AbstractValue& value = m_state.forNode(node->child1());
681                 if (!value.m_type || (value.m_type & ~SpecInt32Only))
682                     break;
683
684                 JSValue radix;
685                 if (!node->child2())
686                     radix = jsNumber(0);
687                 else
688                     radix = m_state.forNode(node->child2()).m_value;
689
690                 if (!radix.isNumber())
691                     break;
692
693                 if (radix.asNumber() == 0 || radix.asNumber() == 10) {
694                     node->child2() = Edge();
695                     node->convertToIdentity();
696                     changed = true;
697                 }
698
699                 break;
700             }
701
702             case NumberToStringWithRadix: {
703                 JSValue radixValue = m_state.forNode(node->child2()).m_value;
704                 if (radixValue && radixValue.isInt32()) {
705                     int32_t radix = radixValue.asInt32();
706                     if (2 <= radix && radix <= 36) {
707                         if (radix == 10) {
708                             node->setOpAndDefaultFlags(ToString);
709                             node->child2() = Edge();
710                         } else
711                             node->convertToNumberToStringWithValidRadixConstant(radix);
712                         changed = true;
713                         break;
714                     }
715                 }
716                 break;
717             }
718
719             case Check: {
720                 alreadyHandled = true;
721                 m_interpreter.execute(indexInBlock);
722                 for (unsigned i = 0; i < AdjacencyList::Size; ++i) {
723                     Edge edge = node->children.child(i);
724                     if (!edge)
725                         break;
726                     if (edge.isProved() || edge.willNotHaveCheck()) {
727                         node->children.removeEdge(i--);
728                         changed = true;
729                     }
730                 }
731                 break;
732             }
733
734             case CheckVarargs: {
735                 alreadyHandled = true;
736                 m_interpreter.execute(indexInBlock);
737                 unsigned targetIndex = 0;
738                 for (unsigned i = 0; i < node->numChildren(); ++i) {
739                     Edge& edge = m_graph.varArgChild(node, i);
740                     if (!edge)
741                         continue;
742                     if (edge.isProved() || edge.willNotHaveCheck()) {
743                         edge = Edge();
744                         changed = true;
745                         continue;
746                     }
747                     Edge& dst = m_graph.varArgChild(node, targetIndex++);
748                     std::swap(dst, edge);
749                 }
750                 node->children.setNumChildren(targetIndex);
751                 break;
752             }
753
754             case MakeRope: {
755                 for (unsigned i = 0; i < AdjacencyList::Size; ++i) {
756                     Edge& edge = node->children.child(i);
757                     if (!edge)
758                         break;
759                     JSValue childConstant = m_state.forNode(edge).value();
760                     if (!childConstant)
761                         continue;
762                     if (!childConstant.isString())
763                         continue;
764                     if (asString(childConstant)->length())
765                         continue;
766
767                     // Don't allow the MakeRope to have zero children.
768                     if (!i && !node->child2())
769                         break;
770
771                     node->children.removeEdge(i--);
772                     changed = true;
773                 }
774
775                 if (!node->child2()) {
776                     ASSERT(!node->child3());
777                     node->convertToIdentity();
778                     changed = true;
779                 }
780                 break;
781             }
782                 
783             default:
784                 break;
785             }
786             
787             if (eliminated) {
788                 changed = true;
789                 continue;
790             }
791                 
792             if (alreadyHandled)
793                 continue;
794             
795             m_interpreter.execute(indexInBlock);
796             if (!m_state.isValid()) {
797                 // If we invalidated then we shouldn't attempt to constant-fold. Here's an
798                 // example:
799                 //
800                 //     c: JSConstant(4.2)
801                 //     x: ValueToInt32(Check:Int32:@const)
802                 //
803                 // It would be correct for an analysis to assume that execution cannot
804                 // proceed past @x. Therefore, constant-folding @x could be rather bad. But,
805                 // the CFA may report that it found a constant even though it also reported
806                 // that everything has been invalidated. This will only happen in a couple of
807                 // the constant folding cases; most of them are also separately defensive
808                 // about such things.
809                 break;
810             }
811             if (!node->shouldGenerate() || m_state.didClobber() || node->hasConstant())
812                 continue;
813             
814             // Interesting fact: this freezing that we do right here may turn an fragile value into
815             // a weak value. See DFGValueStrength.h.
816             FrozenValue* value = m_graph.freeze(m_state.forNode(node).value());
817             if (!*value)
818                 continue;
819             
820             if (node->op() == GetLocal) {
821                 // Need to preserve bytecode liveness in ThreadedCPS form. This wouldn't be necessary
822                 // if it wasn't for https://bugs.webkit.org/show_bug.cgi?id=144086.
823                 m_insertionSet.insertNode(
824                     indexInBlock, SpecNone, PhantomLocal, node->origin,
825                     OpInfo(node->variableAccessData()));
826                 m_graph.dethread();
827             } else
828                 m_insertionSet.insertCheck(m_graph, indexInBlock, node);
829             m_graph.convertToConstant(node, value);
830             
831             changed = true;
832         }
833         m_state.reset();
834         m_insertionSet.execute(block);
835         
836         return changed;
837     }
838     
839     void emitGetByOffset(unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const MultiGetByOffsetCase& getCase, unsigned identifierNumber)
840     {
841         // When we get to here we have already emitted all of the requisite checks for everything.
842         // So, we just need to emit what the method object tells us to emit.
843         
844         addBaseCheck(indexInBlock, node, baseValue, getCase.set());
845
846         GetByOffsetMethod method = getCase.method();
847         
848         switch (method.kind()) {
849         case GetByOffsetMethod::Invalid:
850             RELEASE_ASSERT_NOT_REACHED();
851             return;
852             
853         case GetByOffsetMethod::Constant:
854             m_graph.convertToConstant(node, method.constant());
855             return;
856             
857         case GetByOffsetMethod::Load:
858             emitGetByOffset(indexInBlock, node, node->child1(), identifierNumber, method.offset());
859             return;
860             
861         case GetByOffsetMethod::LoadFromPrototype: {
862             Node* child = m_insertionSet.insertConstant(
863                 indexInBlock, node->origin, method.prototype());
864             emitGetByOffset(
865                 indexInBlock, node, Edge(child, KnownCellUse), identifierNumber, method.offset());
866             return;
867         } }
868         
869         RELEASE_ASSERT_NOT_REACHED();
870     }
871     
872     void emitGetByOffset(unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const GetByIdVariant& variant, unsigned identifierNumber)
873     {
874         Edge childEdge = node->child1();
875
876         addBaseCheck(indexInBlock, node, baseValue, variant.structureSet());
877         
878         // We aren't set up to handle prototype stuff.
879         DFG_ASSERT(m_graph, node, variant.conditionSet().isEmpty());
880
881         if (JSValue value = m_graph.tryGetConstantProperty(baseValue.m_value, *m_graph.addStructureSet(variant.structureSet()), variant.offset())) {
882             m_graph.convertToConstant(node, m_graph.freeze(value));
883             return;
884         }
885         
886         emitGetByOffset(indexInBlock, node, childEdge, identifierNumber, variant.offset());
887     }
888     
889     void emitGetByOffset(
890         unsigned indexInBlock, Node* node, Edge childEdge, unsigned identifierNumber,
891         PropertyOffset offset, const InferredType::Descriptor& inferredType = InferredType::Top)
892     {
893         childEdge.setUseKind(KnownCellUse);
894         
895         Edge propertyStorage;
896         
897         if (isInlineOffset(offset))
898             propertyStorage = childEdge;
899         else {
900             propertyStorage = Edge(m_insertionSet.insertNode(
901                 indexInBlock, SpecNone, GetButterfly, node->origin, childEdge));
902         }
903         
904         StorageAccessData& data = *m_graph.m_storageAccessData.add();
905         data.offset = offset;
906         data.identifierNumber = identifierNumber;
907         data.inferredType = inferredType;
908         
909         node->convertToGetByOffset(data, propertyStorage, childEdge);
910     }
911
912     void emitPutByOffset(unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const PutByIdVariant& variant, unsigned identifierNumber)
913     {
914         NodeOrigin origin = node->origin;
915         Edge childEdge = node->child1();
916
917         addBaseCheck(indexInBlock, node, baseValue, variant.oldStructure());
918         insertInferredTypeCheck(
919             m_insertionSet, indexInBlock, origin, node->child2().node(), variant.requiredType());
920
921         node->child1().setUseKind(KnownCellUse);
922         childEdge.setUseKind(KnownCellUse);
923
924         Transition* transition = 0;
925         if (variant.kind() == PutByIdVariant::Transition) {
926             transition = m_graph.m_transitions.add(
927                 m_graph.registerStructure(variant.oldStructureForTransition()), m_graph.registerStructure(variant.newStructure()));
928         }
929
930         Edge propertyStorage;
931
932         DFG_ASSERT(m_graph, node, origin.exitOK);
933         bool canExit = true;
934         bool didAllocateStorage = false;
935
936         if (isInlineOffset(variant.offset()))
937             propertyStorage = childEdge;
938         else if (!variant.reallocatesStorage()) {
939             propertyStorage = Edge(m_insertionSet.insertNode(
940                 indexInBlock, SpecNone, GetButterfly, origin, childEdge));
941         } else if (!variant.oldStructureForTransition()->outOfLineCapacity()) {
942             ASSERT(variant.newStructure()->outOfLineCapacity());
943             ASSERT(!isInlineOffset(variant.offset()));
944             Node* allocatePropertyStorage = m_insertionSet.insertNode(
945                 indexInBlock, SpecNone, AllocatePropertyStorage,
946                 origin, OpInfo(transition), childEdge);
947             propertyStorage = Edge(allocatePropertyStorage);
948             didAllocateStorage = true;
949         } else {
950             ASSERT(variant.oldStructureForTransition()->outOfLineCapacity());
951             ASSERT(variant.newStructure()->outOfLineCapacity() > variant.oldStructureForTransition()->outOfLineCapacity());
952             ASSERT(!isInlineOffset(variant.offset()));
953
954             Node* reallocatePropertyStorage = m_insertionSet.insertNode(
955                 indexInBlock, SpecNone, ReallocatePropertyStorage, origin,
956                 OpInfo(transition), childEdge,
957                 Edge(m_insertionSet.insertNode(
958                     indexInBlock, SpecNone, GetButterfly, origin, childEdge)));
959             propertyStorage = Edge(reallocatePropertyStorage);
960             didAllocateStorage = true;
961         }
962
963         StorageAccessData& data = *m_graph.m_storageAccessData.add();
964         data.offset = variant.offset();
965         data.identifierNumber = identifierNumber;
966         
967         node->convertToPutByOffset(data, propertyStorage, childEdge);
968         node->origin.exitOK = canExit;
969
970         if (variant.kind() == PutByIdVariant::Transition) {
971             if (didAllocateStorage) {
972                 m_insertionSet.insertNode(
973                     indexInBlock + 1, SpecNone, NukeStructureAndSetButterfly,
974                     origin.withInvalidExit(), childEdge, propertyStorage);
975             }
976             
977             // FIXME: PutStructure goes last until we fix either
978             // https://bugs.webkit.org/show_bug.cgi?id=142921 or
979             // https://bugs.webkit.org/show_bug.cgi?id=142924.
980             m_insertionSet.insertNode(
981                 indexInBlock + 1, SpecNone, PutStructure, origin.withInvalidExit(), OpInfo(transition),
982                 childEdge);
983         }
984     }
985     
986     void addBaseCheck(
987         unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const StructureSet& set)
988     {
989         addBaseCheck(indexInBlock, node, baseValue, *m_graph.addStructureSet(set));
990     }
991
992     void addBaseCheck(
993         unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const RegisteredStructureSet& set)
994     {
995         if (!baseValue.m_structure.isSubsetOf(set)) {
996             // Arises when we prune MultiGetByOffset. We could have a
997             // MultiGetByOffset with a single variant that checks for structure S,
998             // and the input has structures S and T, for example.
999             ASSERT(node->child1());
1000             m_insertionSet.insertNode(
1001                 indexInBlock, SpecNone, CheckStructure, node->origin,
1002                 OpInfo(m_graph.addStructureSet(set.toStructureSet())), node->child1());
1003             return;
1004         }
1005         
1006         if (baseValue.m_type & ~SpecCell)
1007             m_insertionSet.insertCheck(indexInBlock, node->origin, node->child1());
1008     }
1009     
1010     void addStructureTransitionCheck(NodeOrigin origin, unsigned indexInBlock, JSCell* cell, Structure* structure)
1011     {
1012         {
1013             StructureRegistrationResult result;
1014             m_graph.registerStructure(cell->structure(), result);
1015             if (result == StructureRegisteredAndWatched)
1016                 return;
1017         }
1018         
1019         m_graph.registerStructure(structure);
1020
1021         Node* weakConstant = m_insertionSet.insertNode(
1022             indexInBlock, speculationFromValue(cell), JSConstant, origin,
1023             OpInfo(m_graph.freeze(cell)));
1024         
1025         m_insertionSet.insertNode(
1026             indexInBlock, SpecNone, CheckStructure, origin,
1027             OpInfo(m_graph.addStructureSet(structure)), Edge(weakConstant, CellUse));
1028     }
1029     
1030     void fixUpsilons(BasicBlock* block)
1031     {
1032         for (unsigned nodeIndex = block->size(); nodeIndex--;) {
1033             Node* node = block->at(nodeIndex);
1034             if (node->op() != Upsilon)
1035                 continue;
1036             switch (node->phi()->op()) {
1037             case Phi:
1038                 break;
1039             case JSConstant:
1040             case DoubleConstant:
1041             case Int52Constant:
1042                 node->remove(m_graph);
1043                 break;
1044             default:
1045                 DFG_CRASH(m_graph, node, "Bad Upsilon phi() pointer");
1046                 break;
1047             }
1048         }
1049     }
1050     
1051     InPlaceAbstractState m_state;
1052     AbstractInterpreter<InPlaceAbstractState> m_interpreter;
1053     InsertionSet m_insertionSet;
1054 };
1055
1056 bool performConstantFolding(Graph& graph)
1057 {
1058     return runPhase<ConstantFoldingPhase>(graph);
1059 }
1060
1061 } } // namespace JSC::DFG
1062
1063 #endif // ENABLE(DFG_JIT)
1064
1065