5c1be68e986633c86e08c910a9999a3559ddb48a
[WebKit-https.git] / Source / JavaScriptCore / dfg / DFGStrengthReductionPhase.cpp
1 /*
2  * Copyright (C) 2013-2016 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 "DFGStrengthReductionPhase.h"
28
29 #if ENABLE(DFG_JIT)
30
31 #include "DFGAbstractHeap.h"
32 #include "DFGClobberize.h"
33 #include "DFGGraph.h"
34 #include "DFGInsertionSet.h"
35 #include "DFGPhase.h"
36 #include "DFGPredictionPropagationPhase.h"
37 #include "DFGVariableAccessDataDump.h"
38 #include "JSCInlines.h"
39 #include "MathCommon.h"
40 #include "RegExpConstructor.h"
41 #include "StringPrototype.h"
42 #include <cstdlib>
43 #include <wtf/text/StringBuilder.h>
44
45 namespace JSC { namespace DFG {
46
47 class StrengthReductionPhase : public Phase {
48     static const bool verbose = false;
49     
50 public:
51     StrengthReductionPhase(Graph& graph)
52         : Phase(graph, "strength reduction")
53         , m_insertionSet(graph)
54     {
55     }
56     
57     bool run()
58     {
59         ASSERT(m_graph.m_fixpointState == FixpointNotConverged);
60         
61         m_changed = false;
62         
63         for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
64             m_block = m_graph.block(blockIndex);
65             if (!m_block)
66                 continue;
67             for (m_nodeIndex = 0; m_nodeIndex < m_block->size(); ++m_nodeIndex) {
68                 m_node = m_block->at(m_nodeIndex);
69                 handleNode();
70             }
71             m_insertionSet.execute(m_block);
72         }
73         
74         return m_changed;
75     }
76
77 private:
78     void handleNode()
79     {
80         switch (m_node->op()) {
81         case BitOr:
82             handleCommutativity();
83
84             if (m_node->child1().useKind() != UntypedUse && m_node->child2()->isInt32Constant() && !m_node->child2()->asInt32()) {
85                 convertToIdentityOverChild1();
86                 break;
87             }
88             break;
89             
90         case BitXor:
91         case BitAnd:
92             handleCommutativity();
93             break;
94             
95         case BitLShift:
96         case BitRShift:
97         case BitURShift:
98             if (m_node->child1().useKind() != UntypedUse && m_node->child2()->isInt32Constant() && !(m_node->child2()->asInt32() & 0x1f)) {
99                 convertToIdentityOverChild1();
100                 break;
101             }
102             break;
103             
104         case UInt32ToNumber:
105             if (m_node->child1()->op() == BitURShift
106                 && m_node->child1()->child2()->isInt32Constant()
107                 && (m_node->child1()->child2()->asInt32() & 0x1f)
108                 && m_node->arithMode() != Arith::DoOverflow) {
109                 m_node->convertToIdentity();
110                 m_changed = true;
111                 break;
112             }
113             break;
114             
115         case ArithAdd:
116             handleCommutativity();
117             
118             if (m_node->child2()->isInt32Constant() && !m_node->child2()->asInt32()) {
119                 convertToIdentityOverChild1();
120                 break;
121             }
122             break;
123             
124         case ArithMul: {
125             handleCommutativity();
126             Edge& child2 = m_node->child2();
127             if (child2->isNumberConstant() && child2->asNumber() == 2) {
128                 switch (m_node->binaryUseKind()) {
129                 case DoubleRepUse:
130                     // It is always valuable to get rid of a double multiplication by 2.
131                     // We won't have half-register dependencies issues on x86 and we won't have to load the constants.
132                     m_node->setOp(ArithAdd);
133                     child2.setNode(m_node->child1().node());
134                     m_changed = true;
135                     break;
136 #if USE(JSVALUE64)
137                 case Int52RepUse:
138 #endif
139                 case Int32Use:
140                     // For integers, we can only convert compatible modes.
141                     // ArithAdd does handle do negative zero check for example.
142                     if (m_node->arithMode() == Arith::CheckOverflow || m_node->arithMode() == Arith::Unchecked) {
143                         m_node->setOp(ArithAdd);
144                         child2.setNode(m_node->child1().node());
145                         m_changed = true;
146                     }
147                     break;
148                 default:
149                     break;
150                 }
151             }
152             break;
153         }
154         case ArithSub:
155             if (m_node->child2()->isInt32Constant()
156                 && m_node->isBinaryUseKind(Int32Use)) {
157                 int32_t value = m_node->child2()->asInt32();
158                 if (value != INT32_MIN) {
159                     m_node->setOp(ArithAdd);
160                     m_node->child2().setNode(
161                         m_insertionSet.insertConstant(
162                             m_nodeIndex, m_node->origin, jsNumber(-value)));
163                     m_changed = true;
164                     break;
165                 }
166             }
167             break;
168
169         case ArithPow:
170             if (m_node->child2()->isNumberConstant()) {
171                 double yOperandValue = m_node->child2()->asNumber();
172                 if (yOperandValue == 1) {
173                     convertToIdentityOverChild1();
174                     m_changed = true;
175                 } else if (yOperandValue == 2) {
176                     m_node->setOp(ArithMul);
177                     m_node->child2() = m_node->child1();
178                     m_changed = true;
179                 }
180             }
181             break;
182
183         case ArithMod:
184             // On Integers
185             // In: ArithMod(ArithMod(x, const1), const2)
186             // Out: Identity(ArithMod(x, const1))
187             //     if const1 <= const2.
188             if (m_node->binaryUseKind() == Int32Use
189                 && m_node->child2()->isInt32Constant()
190                 && m_node->child1()->op() == ArithMod
191                 && m_node->child1()->binaryUseKind() == Int32Use
192                 && m_node->child1()->child2()->isInt32Constant()
193                 && std::abs(m_node->child1()->child2()->asInt32()) <= std::abs(m_node->child2()->asInt32())) {
194                     convertToIdentityOverChild1();
195             }
196             break;
197
198         case ArithDiv:
199             // Transform
200             //    ArithDiv(x, constant)
201             // Into
202             //    ArithMul(x, 1 / constant)
203             // if the operation has the same result.
204             if (m_node->isBinaryUseKind(DoubleRepUse)
205                 && m_node->child2()->isNumberConstant()) {
206
207                 if (std::optional<double> reciprocal = safeReciprocalForDivByConst(m_node->child2()->asNumber())) {
208                     Node* reciprocalNode = m_insertionSet.insertConstant(m_nodeIndex, m_node->origin, jsDoubleNumber(*reciprocal), DoubleConstant);
209                     m_node->setOp(ArithMul);
210                     m_node->child2() = Edge(reciprocalNode, DoubleRepUse);
211                     m_changed = true;
212                     break;
213                 }
214             }
215             break;
216
217         case ValueRep:
218         case Int52Rep: {
219             // This short-circuits circuitous conversions, like ValueRep(Int52Rep(value)).
220             
221             // The only speculation that we would do beyond validating that we have a type that
222             // can be represented a certain way is an Int32 check that would appear on Int52Rep
223             // nodes. For now, if we see this and the final type we want is an Int52, we use it
224             // as an excuse not to fold. The only thing we would need is a Int52RepInt32Use kind.
225             bool hadInt32Check = false;
226             if (m_node->op() == Int52Rep) {
227                 if (m_node->child1().useKind() != Int32Use)
228                     break;
229                 hadInt32Check = true;
230             }
231             for (Node* node = m_node->child1().node(); ; node = node->child1().node()) {
232                 if (canonicalResultRepresentation(node->result()) ==
233                     canonicalResultRepresentation(m_node->result())) {
234                     m_insertionSet.insertCheck(m_graph, m_nodeIndex, m_node);
235                     if (hadInt32Check) {
236                         // FIXME: Consider adding Int52RepInt32Use or even DoubleRepInt32Use,
237                         // which would be super weird. The latter would only arise in some
238                         // seriously circuitous conversions.
239                         if (canonicalResultRepresentation(node->result()) != NodeResultJS)
240                             break;
241                         
242                         m_insertionSet.insertCheck(
243                             m_nodeIndex, m_node->origin, Edge(node, Int32Use));
244                     }
245                     m_node->child1() = node->defaultEdge();
246                     m_node->convertToIdentity();
247                     m_changed = true;
248                     break;
249                 }
250                 
251                 switch (node->op()) {
252                 case Int52Rep:
253                     if (node->child1().useKind() != Int32Use)
254                         break;
255                     hadInt32Check = true;
256                     continue;
257                     
258                 case ValueRep:
259                     continue;
260                     
261                 default:
262                     break;
263                 }
264                 break;
265             }
266             break;
267         }
268             
269         case Flush: {
270             ASSERT(m_graph.m_form != SSA);
271
272             if (m_graph.willCatchExceptionInMachineFrame(m_node->origin.semantic)) {
273                 // FIXME: We should be able to relax this:
274                 // https://bugs.webkit.org/show_bug.cgi?id=150824
275                 break;
276             }
277             
278             Node* setLocal = nullptr;
279             VirtualRegister local = m_node->local();
280             
281             for (unsigned i = m_nodeIndex; i--;) {
282                 Node* node = m_block->at(i);
283
284                 if (node->op() == SetLocal && node->local() == local) {
285                     setLocal = node;
286                     break;
287                 }
288
289                 if (accessesOverlap(m_graph, node, AbstractHeap(Stack, local)))
290                     break;
291
292             }
293             
294             if (!setLocal)
295                 break;
296             
297             // The Flush should become a PhantomLocal at this point. This means that we want the
298             // local's value during OSR, but we don't care if the value is stored to the stack. CPS
299             // rethreading can canonicalize PhantomLocals for us.
300             m_node->convertFlushToPhantomLocal();
301             m_graph.dethread();
302             m_changed = true;
303             break;
304         }
305
306         // FIXME: we should probably do this in constant folding but this currently relies on an OSR exit rule.
307         // https://bugs.webkit.org/show_bug.cgi?id=154832
308         case OverridesHasInstance: {
309             if (!m_node->child2().node()->isCellConstant())
310                 break;
311
312             if (m_node->child2().node()->asCell() != m_graph.globalObjectFor(m_node->origin.semantic)->functionProtoHasInstanceSymbolFunction()) {
313                 m_graph.convertToConstant(m_node, jsBoolean(true));
314                 m_changed = true;
315
316             } else if (!m_graph.hasExitSite(m_node->origin.semantic, BadTypeInfoFlags)) {
317                 // We optimistically assume that we will not see a function that has a custom instanceof operation as they should be rare.
318                 m_insertionSet.insertNode(m_nodeIndex, SpecNone, CheckTypeInfoFlags, m_node->origin, OpInfo(ImplementsDefaultHasInstance), Edge(m_node->child1().node(), CellUse));
319                 m_graph.convertToConstant(m_node, jsBoolean(false));
320                 m_changed = true;
321             }
322
323             break;
324         }
325
326         // FIXME: We have a lot of string constant-folding rules here. It would be great to
327         // move these to the abstract interpreter once AbstractValue can support LazyJSValue.
328         // https://bugs.webkit.org/show_bug.cgi?id=155204
329
330         case ValueAdd: {
331             if (m_node->child1()->isConstant()
332                 && m_node->child2()->isConstant()
333                 && (!!m_node->child1()->tryGetString(m_graph) || !!m_node->child2()->tryGetString(m_graph))) {
334                 auto tryGetConstantString = [&] (Node* node) -> String {
335                     String string = node->tryGetString(m_graph);
336                     if (!string.isEmpty())
337                         return string;
338                     JSValue value = node->constant()->value();
339                     if (!value)
340                         return String();
341                     if (value.isInt32())
342                         return String::number(value.asInt32());
343                     if (value.isNumber())
344                         return String::numberToStringECMAScript(value.asNumber());
345                     if (value.isBoolean())
346                         return value.asBoolean() ? ASCIILiteral("true") : ASCIILiteral("false");
347                     if (value.isNull())
348                         return ASCIILiteral("null");
349                     if (value.isUndefined())
350                         return ASCIILiteral("undefined");
351                     return String();
352                 };
353
354                 String leftString = tryGetConstantString(m_node->child1().node());
355                 if (!leftString)
356                     break;
357                 String rightString = tryGetConstantString(m_node->child2().node());
358                 if (!rightString)
359                     break;
360
361                 StringBuilder builder;
362                 builder.append(leftString);
363                 builder.append(rightString);
364                 m_node->convertToLazyJSConstant(
365                     m_graph, LazyJSValue::newString(m_graph, builder.toString()));
366                 m_changed = true;
367             }
368             break;
369         }
370
371         case MakeRope:
372         case StrCat: {
373             String leftString = m_node->child1()->tryGetString(m_graph);
374             if (!leftString)
375                 break;
376             String rightString = m_node->child2()->tryGetString(m_graph);
377             if (!rightString)
378                 break;
379             String extraString;
380             if (m_node->child3()) {
381                 extraString = m_node->child3()->tryGetString(m_graph);
382                 if (!extraString)
383                     break;
384             }
385
386             StringBuilder builder;
387             builder.append(leftString);
388             builder.append(rightString);
389             if (!!extraString)
390                 builder.append(extraString);
391
392             m_node->convertToLazyJSConstant(
393                 m_graph, LazyJSValue::newString(m_graph, builder.toString()));
394             m_changed = true;
395             break;
396         }
397
398         case ToString:
399         case CallStringConstructor: {
400             Edge& child1 = m_node->child1();
401             switch (child1.useKind()) {
402             case Int32Use:
403             case Int52RepUse:
404             case DoubleRepUse: {
405                 if (child1->hasConstant()) {
406                     JSValue value = child1->constant()->value();
407                     if (value) {
408                         String result;
409                         if (value.isInt32())
410                             result = String::number(value.asInt32());
411                         else if (value.isNumber())
412                             result = String::numberToStringECMAScript(value.asNumber());
413
414                         if (!result.isNull()) {
415                             m_node->convertToLazyJSConstant(m_graph, LazyJSValue::newString(m_graph, result));
416                             m_changed = true;
417                         }
418                     }
419                 }
420                 break;
421             }
422
423             default:
424                 break;
425             }
426             break;
427         }
428
429         case NumberToStringWithValidRadixConstant: {
430             Edge& child1 = m_node->child1();
431             if (child1->hasConstant()) {
432                 JSValue value = child1->constant()->value();
433                 if (value && value.isNumber()) {
434                     String result = toStringWithRadix(value.asNumber(), m_node->validRadixConstant());
435                     m_node->convertToLazyJSConstant(m_graph, LazyJSValue::newString(m_graph, result));
436                     m_changed = true;
437                 }
438             }
439             break;
440         }
441
442         case GetArrayLength: {
443             if (m_node->arrayMode().type() == Array::Generic
444                 || m_node->arrayMode().type() == Array::String) {
445                 String string = m_node->child1()->tryGetString(m_graph);
446                 if (!!string) {
447                     m_graph.convertToConstant(m_node, jsNumber(string.length()));
448                     m_changed = true;
449                     break;
450                 }
451             }
452             break;
453         }
454
455         case GetGlobalObject: {
456             if (JSObject* object = m_node->child1()->dynamicCastConstant<JSObject*>(vm())) {
457                 m_graph.convertToConstant(m_node, object->globalObject());
458                 m_changed = true;
459                 break;
460             }
461             break;
462         }
463
464         case RegExpExec:
465         case RegExpTest:
466         case RegExpMatchFast:
467         case RegExpExecNonGlobalOrSticky: {
468             JSGlobalObject* globalObject = m_node->child1()->dynamicCastConstant<JSGlobalObject*>(vm());
469             if (!globalObject) {
470                 if (verbose)
471                     dataLog("Giving up because no global object.\n");
472                 break;
473             }
474
475             if (globalObject->isHavingABadTime()) {
476                 if (verbose)
477                     dataLog("Giving up because bad time.\n");
478                 break;
479             }
480
481             Node* regExpObjectNode = nullptr;
482             RegExp* regExp = nullptr;
483             if (m_node->op() == RegExpExec || m_node->op() == RegExpTest || m_node->op() == RegExpMatchFast) {
484                 regExpObjectNode = m_node->child2().node();
485                 if (RegExpObject* regExpObject = regExpObjectNode->dynamicCastConstant<RegExpObject*>(vm()))
486                     regExp = regExpObject->regExp();
487                 else if (regExpObjectNode->op() == NewRegexp)
488                     regExp = regExpObjectNode->castOperand<RegExp*>();
489                 else {
490                     if (verbose)
491                         dataLog("Giving up because the regexp is unknown.\n");
492                     break;
493                 }
494             } else
495                 regExp = m_node->castOperand<RegExp*>();
496
497             if (m_node->op() == RegExpMatchFast) {
498                 if (regExp->global()) {
499                     if (regExp->sticky())
500                         break;
501                     if (m_node->child3().useKind() != StringUse)
502                         break;
503                     NodeOrigin origin = m_node->origin;
504                     m_insertionSet.insertNode(
505                         m_nodeIndex, SpecNone, Check, origin, m_node->children.justChecks());
506                     m_insertionSet.insertNode(
507                         m_nodeIndex, SpecNone, SetRegExpObjectLastIndex, origin,
508                         OpInfo(false),
509                         Edge(regExpObjectNode, RegExpObjectUse),
510                         m_insertionSet.insertConstantForUse(
511                             m_nodeIndex, origin, jsNumber(0), UntypedUse));
512                     origin = origin.withInvalidExit();
513                     m_node->convertToRegExpMatchFastGlobal(m_graph.freeze(regExp));
514                     m_node->origin = origin;
515                     m_changed = true;
516                     break;
517                 }
518
519                 m_node->setOp(RegExpExec);
520                 m_changed = true;
521                 // Continue performing strength reduction onto RegExpExec node.
522             }
523
524             ASSERT(m_node->op() != RegExpMatchFast);
525
526             auto foldToConstant = [&] {
527                 Node* stringNode = nullptr;
528                 if (m_node->op() == RegExpExecNonGlobalOrSticky)
529                     stringNode = m_node->child2().node();
530                 else
531                     stringNode = m_node->child3().node();
532
533                 // NOTE: This mostly already protects us from having the compiler execute a regexp
534                 // operation on a ginormous string by preventing us from getting our hands on ginormous
535                 // strings in the first place.
536                 String string = stringNode->tryGetString(m_graph);
537                 if (!string) {
538                     if (verbose)
539                         dataLog("Giving up because the string is unknown.\n");
540                     return false;
541                 }
542
543                 FrozenValue* regExpFrozenValue = m_graph.freeze(regExp);
544
545                 // Refuse to do things with regular expressions that have a ginormous number of
546                 // subpatterns.
547                 unsigned ginormousNumberOfSubPatterns = 1000;
548                 if (regExp->numSubpatterns() > ginormousNumberOfSubPatterns) {
549                     if (verbose)
550                         dataLog("Giving up because of pattern limit.\n");
551                     return false;
552                 }
553
554                 if ((m_node->op() == RegExpExec || m_node->op() == RegExpExecNonGlobalOrSticky) && regExp->hasNamedCaptures()) {
555                     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=176464
556                     // Implement strength reduction optimization for named capture groups.
557                     if (verbose)
558                         dataLog("Giving up because of named capture groups.\n");
559                     return false;
560                 }
561
562                 unsigned lastIndex;
563                 if (regExp->globalOrSticky()) {
564                     // This will only work if we can prove what the value of lastIndex is. To do this
565                     // safely, we need to execute the insertion set so that we see any previous strength
566                     // reductions. This is needed for soundness since otherwise the effectfulness of any
567                     // previous strength reductions would be invisible to us.
568                     ASSERT(regExpObjectNode);
569                     executeInsertionSet();
570                     lastIndex = UINT_MAX;
571                     for (unsigned otherNodeIndex = m_nodeIndex; otherNodeIndex--;) {
572                         Node* otherNode = m_block->at(otherNodeIndex);
573                         if (otherNode == regExpObjectNode) {
574                             lastIndex = 0;
575                             break;
576                         }
577                         if (otherNode->op() == SetRegExpObjectLastIndex
578                             && otherNode->child1() == regExpObjectNode
579                             && otherNode->child2()->isInt32Constant()
580                             && otherNode->child2()->asInt32() >= 0) {
581                             lastIndex = static_cast<unsigned>(otherNode->child2()->asInt32());
582                             break;
583                         }
584                         if (writesOverlap(m_graph, otherNode, RegExpObject_lastIndex))
585                             break;
586                     }
587                     if (lastIndex == UINT_MAX) {
588                         if (verbose)
589                             dataLog("Giving up because the last index is not known.\n");
590                         return false;
591                     }
592                 } else
593                     lastIndex = 0;
594
595                 m_graph.watchpoints().addLazily(globalObject->havingABadTimeWatchpoint());
596
597                 Structure* structure;
598                 if ((m_node->op() == RegExpExec || m_node->op() == RegExpExecNonGlobalOrSticky) && regExp->hasNamedCaptures())
599                     structure = globalObject->regExpMatchesArrayWithGroupsStructure();
600                 else
601                     structure = globalObject->regExpMatchesArrayStructure();
602
603                 if (structure->indexingType() != ArrayWithContiguous) {
604                     // This is further protection against a race with haveABadTime.
605                     if (verbose)
606                         dataLog("Giving up because the structure has the wrong indexing type.\n");
607                     return false;
608                 }
609                 m_graph.registerStructure(structure);
610
611                 RegExpConstructor* constructor = globalObject->regExpConstructor();
612                 FrozenValue* constructorFrozenValue = m_graph.freeze(constructor);
613
614                 MatchResult result;
615                 Vector<int> ovector;
616                 // We have to call the kind of match function that the main thread would have called.
617                 // Otherwise, we might not have the desired Yarr code compiled, and the match will fail.
618                 if (m_node->op() == RegExpExec || m_node->op() == RegExpExecNonGlobalOrSticky) {
619                     int position;
620                     if (!regExp->matchConcurrently(vm(), string, lastIndex, position, ovector)) {
621                         if (verbose)
622                             dataLog("Giving up because match failed.\n");
623                         return false;
624                     }
625                     result.start = position;
626                     result.end = ovector[1];
627                 } else {
628                     if (!regExp->matchConcurrently(vm(), string, lastIndex, result)) {
629                         if (verbose)
630                             dataLog("Giving up because match failed.\n");
631                         return false;
632                     }
633                 }
634
635                 // We've constant-folded the regexp. Now we're committed to replacing RegExpExec/Test.
636
637                 m_changed = true;
638
639                 NodeOrigin origin = m_node->origin;
640
641                 m_insertionSet.insertNode(
642                     m_nodeIndex, SpecNone, Check, origin, m_node->children.justChecks());
643
644                 if (m_node->op() == RegExpExec || m_node->op() == RegExpExecNonGlobalOrSticky) {
645                     if (result) {
646                         RegisteredStructureSet* structureSet = m_graph.addStructureSet(structure);
647
648                         // Create an array modeling the JS array that we will try to allocate. This is
649                         // basically createRegExpMatchesArray but over C++ strings instead of JSStrings.
650                         Vector<String> resultArray;
651                         resultArray.append(string.substring(result.start, result.end - result.start));
652                         for (unsigned i = 1; i <= regExp->numSubpatterns(); ++i) {
653                             int start = ovector[2 * i];
654                             if (start >= 0)
655                                 resultArray.append(string.substring(start, ovector[2 * i + 1] - start));
656                             else
657                                 resultArray.append(String());
658                         }
659
660                         unsigned publicLength = resultArray.size();
661                         unsigned vectorLength =
662                             Butterfly::optimalContiguousVectorLength(structure, publicLength);
663
664                         UniquedStringImpl* indexUID = vm().propertyNames->index.impl();
665                         UniquedStringImpl* inputUID = vm().propertyNames->input.impl();
666                         unsigned indexIndex = m_graph.identifiers().ensure(indexUID);
667                         unsigned inputIndex = m_graph.identifiers().ensure(inputUID);
668
669                         unsigned firstChild = m_graph.m_varArgChildren.size();
670                         m_graph.m_varArgChildren.append(
671                             m_insertionSet.insertConstantForUse(
672                                 m_nodeIndex, origin, structure, KnownCellUse));
673                         ObjectMaterializationData* data = m_graph.m_objectMaterializationData.add();
674
675                         m_graph.m_varArgChildren.append(
676                             m_insertionSet.insertConstantForUse(
677                                 m_nodeIndex, origin, jsNumber(publicLength), KnownInt32Use));
678                         data->m_properties.append(PublicLengthPLoc);
679
680                         m_graph.m_varArgChildren.append(
681                             m_insertionSet.insertConstantForUse(
682                                 m_nodeIndex, origin, jsNumber(vectorLength), KnownInt32Use));
683                         data->m_properties.append(VectorLengthPLoc);
684
685                         m_graph.m_varArgChildren.append(
686                             m_insertionSet.insertConstantForUse(
687                                 m_nodeIndex, origin, jsNumber(result.start), UntypedUse));
688                         data->m_properties.append(
689                             PromotedLocationDescriptor(NamedPropertyPLoc, indexIndex));
690
691                         m_graph.m_varArgChildren.append(Edge(stringNode, UntypedUse));
692                         data->m_properties.append(
693                             PromotedLocationDescriptor(NamedPropertyPLoc, inputIndex));
694
695                         auto materializeString = [&] (const String& string) -> Node* {
696                             if (string.isNull())
697                                 return nullptr;
698                             if (string.isEmpty()) {
699                                 return m_insertionSet.insertConstant(
700                                     m_nodeIndex, origin, vm().smallStrings.emptyString());
701                             }
702                             LazyJSValue value = LazyJSValue::newString(m_graph, string);
703                             return m_insertionSet.insertNode(
704                                 m_nodeIndex, SpecNone, LazyJSConstant, origin,
705                                 OpInfo(m_graph.m_lazyJSValues.add(value)));
706                         };
707
708                         for (unsigned i = 0; i < resultArray.size(); ++i) {
709                             if (Node* node = materializeString(resultArray[i])) {
710                                 m_graph.m_varArgChildren.append(Edge(node, UntypedUse));
711                                 data->m_properties.append(
712                                     PromotedLocationDescriptor(IndexedPropertyPLoc, i));
713                             }
714                         }
715
716                         Node* resultNode = m_insertionSet.insertNode(
717                             m_nodeIndex, SpecArray, Node::VarArg, MaterializeNewObject, origin,
718                             OpInfo(structureSet), OpInfo(data), firstChild,
719                             m_graph.m_varArgChildren.size() - firstChild);
720
721                         m_node->convertToIdentityOn(resultNode);
722                     } else
723                         m_graph.convertToConstant(m_node, jsNull());
724                 } else
725                     m_graph.convertToConstant(m_node, jsBoolean(!!result));
726
727                 // Whether it's Exec or Test, we need to tell the constructor and RegExpObject what's up.
728                 // Because SetRegExpObjectLastIndex may exit and it clobbers exit state, we do that
729                 // first.
730
731                 if (regExp->globalOrSticky()) {
732                     ASSERT(regExpObjectNode);
733                     m_insertionSet.insertNode(
734                         m_nodeIndex, SpecNone, SetRegExpObjectLastIndex, origin,
735                         OpInfo(false),
736                         Edge(regExpObjectNode, RegExpObjectUse),
737                         m_insertionSet.insertConstantForUse(
738                             m_nodeIndex, origin, jsNumber(result ? result.end : 0), UntypedUse));
739
740                     origin = origin.withInvalidExit();
741                 }
742
743                 if (result) {
744                     unsigned firstChild = m_graph.m_varArgChildren.size();
745                     m_graph.m_varArgChildren.append(
746                         m_insertionSet.insertConstantForUse(
747                             m_nodeIndex, origin, constructorFrozenValue, KnownCellUse));
748                     m_graph.m_varArgChildren.append(
749                         m_insertionSet.insertConstantForUse(
750                             m_nodeIndex, origin, regExpFrozenValue, KnownCellUse));
751                     m_graph.m_varArgChildren.append(Edge(stringNode, KnownCellUse));
752                     m_graph.m_varArgChildren.append(
753                         m_insertionSet.insertConstantForUse(
754                             m_nodeIndex, origin, jsNumber(result.start), KnownInt32Use));
755                     m_graph.m_varArgChildren.append(
756                         m_insertionSet.insertConstantForUse(
757                             m_nodeIndex, origin, jsNumber(result.end), KnownInt32Use));
758                     m_insertionSet.insertNode(
759                         m_nodeIndex, SpecNone, Node::VarArg, RecordRegExpCachedResult, origin,
760                         OpInfo(), OpInfo(), firstChild, m_graph.m_varArgChildren.size() - firstChild);
761
762                     origin = origin.withInvalidExit();
763                 }
764
765                 m_node->origin = origin;
766                 return true;
767             };
768
769             auto convertToStatic = [&] {
770                 if (m_node->op() != RegExpExec)
771                     return false;
772                 if (regExp->globalOrSticky())
773                     return false;
774                 if (m_node->child3().useKind() != StringUse)
775                     return false;
776                 NodeOrigin origin = m_node->origin;
777                 m_insertionSet.insertNode(
778                     m_nodeIndex, SpecNone, Check, origin, m_node->children.justChecks());
779                 m_node->convertToRegExpExecNonGlobalOrSticky(m_graph.freeze(regExp));
780                 m_changed = true;
781                 return true;
782             };
783
784             if (foldToConstant())
785                 break;
786
787             if (convertToStatic())
788                 break;
789
790             break;
791         }
792
793         case StringReplace:
794         case StringReplaceRegExp: {
795             Node* stringNode = m_node->child1().node();
796             String string = stringNode->tryGetString(m_graph);
797             if (!string)
798                 break;
799             
800             Node* regExpObjectNode = m_node->child2().node();
801             RegExp* regExp;
802             if (RegExpObject* regExpObject = regExpObjectNode->dynamicCastConstant<RegExpObject*>(vm()))
803                 regExp = regExpObject->regExp();
804             else if (regExpObjectNode->op() == NewRegexp)
805                 regExp = regExpObjectNode->castOperand<RegExp*>();
806             else {
807                 if (verbose)
808                     dataLog("Giving up because the regexp is unknown.\n");
809                 break;
810             }
811
812             String replace = m_node->child3()->tryGetString(m_graph);
813             if (!replace)
814                 break;
815
816             StringBuilder builder;
817
818             unsigned lastIndex = 0;
819             unsigned startPosition = 0;
820             bool ok = true;
821             do {
822                 MatchResult result;
823                 Vector<int> ovector;
824                 // Model which version of match() is called by the main thread.
825                 if (replace.isEmpty() && regExp->global()) {
826                     if (!regExp->matchConcurrently(vm(), string, startPosition, result)) {
827                         ok = false;
828                         break;
829                     }
830                 } else {
831                     int position;
832                     if (!regExp->matchConcurrently(vm(), string, startPosition, position, ovector)) {
833                         ok = false;
834                         break;
835                     }
836                     
837                     result.start = position;
838                     result.end = ovector[1];
839                 }
840                 
841                 if (!result)
842                     break;
843
844                 unsigned replLen = replace.length();
845                 if (lastIndex < result.start || replLen) {
846                     builder.append(string, lastIndex, result.start - lastIndex);
847                     if (replLen)
848                         builder.append(substituteBackreferences(replace, string, ovector.data(), regExp));
849                 }
850
851                 lastIndex = result.end;
852                 startPosition = lastIndex;
853
854                 // special case of empty match
855                 if (result.empty()) {
856                     startPosition++;
857                     if (startPosition > string.length())
858                         break;
859                 }
860             } while (regExp->global());
861             if (!ok)
862                 break;
863
864             // We are committed at this point.
865             m_changed = true;
866
867             NodeOrigin origin = m_node->origin;
868
869             if (regExp->global()) {
870                 m_insertionSet.insertNode(
871                     m_nodeIndex, SpecNone, SetRegExpObjectLastIndex, origin,
872                     OpInfo(false),
873                     Edge(regExpObjectNode, RegExpObjectUse),
874                     m_insertionSet.insertConstantForUse(
875                         m_nodeIndex, origin, jsNumber(0), UntypedUse));
876                 
877                 origin = origin.withInvalidExit();
878             }
879
880             if (!lastIndex && builder.isEmpty())
881                 m_node->convertToIdentityOn(stringNode);
882             else {
883                 if (lastIndex < string.length())
884                     builder.append(string, lastIndex, string.length() - lastIndex);
885                 
886                 m_node->convertToLazyJSConstant(
887                     m_graph, LazyJSValue::newString(m_graph, builder.toString()));
888             }
889
890             m_node->origin = origin;
891             break;
892         }
893             
894         case Call:
895         case Construct:
896         case TailCallInlinedCaller:
897         case TailCall: {
898             ExecutableBase* executable = nullptr;
899             Edge callee = m_graph.varArgChild(m_node, 0);
900             if (JSFunction* function = callee->dynamicCastConstant<JSFunction*>(vm()))
901                 executable = function->executable();
902             else if (callee->isFunctionAllocation())
903                 executable = callee->castOperand<FunctionExecutable*>();
904             
905             if (!executable)
906                 break;
907             
908             if (FunctionExecutable* functionExecutable = jsDynamicCast<FunctionExecutable*>(vm(), executable)) {
909                 // We need to update m_parameterSlots before we get to the backend, but we don't
910                 // want to do too much of this.
911                 unsigned numAllocatedArgs =
912                     static_cast<unsigned>(functionExecutable->parameterCount()) + 1;
913                 
914                 if (numAllocatedArgs <= Options::maximumDirectCallStackSize()) {
915                     m_graph.m_parameterSlots = std::max(
916                         m_graph.m_parameterSlots,
917                         Graph::parameterSlotsForArgCount(numAllocatedArgs));
918                 }
919             }
920             
921             m_node->convertToDirectCall(m_graph.freeze(executable));
922             m_changed = true;
923             break;
924         }
925
926         default:
927             break;
928         }
929     }
930             
931     void convertToIdentityOverChild(unsigned childIndex)
932     {
933         ASSERT(!(m_node->flags() & NodeHasVarArgs));
934         m_insertionSet.insertCheck(m_graph, m_nodeIndex, m_node);
935         m_node->children.removeEdge(childIndex ^ 1);
936         m_node->convertToIdentity();
937         m_changed = true;
938     }
939     
940     void convertToIdentityOverChild1()
941     {
942         convertToIdentityOverChild(0);
943     }
944     
945     void convertToIdentityOverChild2()
946     {
947         convertToIdentityOverChild(1);
948     }
949     
950     void handleCommutativity()
951     {
952         // It's definitely not sound to swap the lhs and rhs when we may be performing effectful
953         // calls on the lhs/rhs for valueOf.
954         if (m_node->child1().useKind() == UntypedUse || m_node->child2().useKind() == UntypedUse)
955             return;
956
957         // If the right side is a constant then there is nothing left to do.
958         if (m_node->child2()->hasConstant())
959             return;
960         
961         // This case ensures that optimizations that look for x + const don't also have
962         // to look for const + x.
963         if (m_node->child1()->hasConstant() && !m_node->child1()->asJSValue().isCell()) {
964             std::swap(m_node->child1(), m_node->child2());
965             m_changed = true;
966             return;
967         }
968         
969         // This case ensures that CSE is commutativity-aware.
970         if (m_node->child1().node() > m_node->child2().node()) {
971             std::swap(m_node->child1(), m_node->child2());
972             m_changed = true;
973             return;
974         }
975     }
976
977     void executeInsertionSet()
978     {
979         m_nodeIndex += m_insertionSet.execute(m_block);
980     }
981     
982     InsertionSet m_insertionSet;
983     BasicBlock* m_block;
984     unsigned m_nodeIndex;
985     Node* m_node;
986     bool m_changed;
987 };
988     
989 bool performStrengthReduction(Graph& graph)
990 {
991     return runPhase<StrengthReductionPhase>(graph);
992 }
993
994 } } // namespace JSC::DFG
995
996 #endif // ENABLE(DFG_JIT)
997