blob: 65afb4ae895ad487bf5fabecdfeb5978ecec004d [file] [log] [blame]
fpizlo@apple.comce995b22013-12-08 19:01:17 +00001/*
fpizlo@apple.com280ef002016-04-05 22:13:16 +00002 * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
fpizlo@apple.comce995b22013-12-08 19:01:17 +00003 *
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"
ossy@webkit.orgbeb0de42014-02-17 19:00:03 +000027#include "DFGStrengthReductionPhase.h"
fpizlo@apple.comce995b22013-12-08 19:01:17 +000028
29#if ENABLE(DFG_JIT)
30
fpizlo@apple.com2c4a7e92014-08-06 05:27:46 +000031#include "DFGAbstractHeap.h"
32#include "DFGClobberize.h"
fpizlo@apple.com0bef2a12014-02-10 19:26:29 +000033#include "DFGGraph.h"
fpizlo@apple.comce995b22013-12-08 19:01:17 +000034#include "DFGInsertionSet.h"
35#include "DFGPhase.h"
36#include "DFGPredictionPropagationPhase.h"
fpizlo@apple.com0bef2a12014-02-10 19:26:29 +000037#include "DFGVariableAccessDataDump.h"
fpizlo@apple.comfb7eff22014-02-11 01:45:50 +000038#include "JSCInlines.h"
commit-queue@webkit.orgbf72a9b2016-04-22 04:46:53 +000039#include "MathCommon.h"
fpizlo@apple.com280ef002016-04-05 22:13:16 +000040#include "RegExpConstructor.h"
41#include "StringPrototype.h"
benjamin@webkit.org18c346c2016-03-02 07:53:20 +000042#include <cstdlib>
fpizlo@apple.combc16ddb2016-09-06 01:02:22 +000043#include <wtf/text/StringBuilder.h>
fpizlo@apple.comce995b22013-12-08 19:01:17 +000044
45namespace JSC { namespace DFG {
46
47class StrengthReductionPhase : public Phase {
fpizlo@apple.com280ef002016-04-05 22:13:16 +000048 static const bool verbose = false;
49
fpizlo@apple.comce995b22013-12-08 19:01:17 +000050public:
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
77private:
78 void handleNode()
79 {
80 switch (m_node->op()) {
81 case BitOr:
fpizlo@apple.com4c96a842014-02-13 22:46:51 +000082 handleCommutativity();
83
mark.lam@apple.comc0008652015-12-15 21:19:31 +000084 if (m_node->child1().useKind() != UntypedUse && m_node->child2()->isInt32Constant() && !m_node->child2()->asInt32()) {
fpizlo@apple.comb41e6822014-07-25 20:55:17 +000085 convertToIdentityOverChild1();
86 break;
fpizlo@apple.com9089acb2013-12-14 06:33:42 +000087 }
88 break;
89
fpizlo@apple.com4c96a842014-02-13 22:46:51 +000090 case BitXor:
91 case BitAnd:
92 handleCommutativity();
93 break;
94
fpizlo@apple.com9089acb2013-12-14 06:33:42 +000095 case BitLShift:
96 case BitRShift:
97 case BitURShift:
mark.lam@apple.comc0008652015-12-15 21:19:31 +000098 if (m_node->child1().useKind() != UntypedUse && m_node->child2()->isInt32Constant() && !(m_node->child2()->asInt32() & 0x1f)) {
fpizlo@apple.comb41e6822014-07-25 20:55:17 +000099 convertToIdentityOverChild1();
100 break;
fpizlo@apple.com9089acb2013-12-14 06:33:42 +0000101 }
102 break;
103
104 case UInt32ToNumber:
105 if (m_node->child1()->op() == BitURShift
fpizlo@apple.comb41e6822014-07-25 20:55:17 +0000106 && 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;
fpizlo@apple.comce995b22013-12-08 19:01:17 +0000112 }
113 break;
114
fpizlo@apple.com4c96a842014-02-13 22:46:51 +0000115 case ArithAdd:
116 handleCommutativity();
117
fpizlo@apple.comb41e6822014-07-25 20:55:17 +0000118 if (m_node->child2()->isInt32Constant() && !m_node->child2()->asInt32()) {
119 convertToIdentityOverChild1();
120 break;
fpizlo@apple.com4c96a842014-02-13 22:46:51 +0000121 }
122 break;
123
benjamin@webkit.org0037c2d2015-08-16 08:13:50 +0000124 case ArithMul: {
fpizlo@apple.com4c96a842014-02-13 22:46:51 +0000125 handleCommutativity();
benjamin@webkit.org0037c2d2015-08-16 08:13:50 +0000126 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 }
fpizlo@apple.com4c96a842014-02-13 22:46:51 +0000152 break;
benjamin@webkit.org0037c2d2015-08-16 08:13:50 +0000153 }
fpizlo@apple.com4c96a842014-02-13 22:46:51 +0000154 case ArithSub:
fpizlo@apple.comb41e6822014-07-25 20:55:17 +0000155 if (m_node->child2()->isInt32Constant()
fpizlo@apple.com4c96a842014-02-13 22:46:51 +0000156 && m_node->isBinaryUseKind(Int32Use)) {
fpizlo@apple.comb41e6822014-07-25 20:55:17 +0000157 int32_t value = m_node->child2()->asInt32();
fpizlo@apple.com4c96a842014-02-13 22:46:51 +0000158 if (-value != value) {
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;
benjamin@webkit.org903025b2015-02-14 04:20:21 +0000168
169 case ArithPow:
170 if (m_node->child2()->isNumberConstant()) {
171 double yOperandValue = m_node->child2()->asNumber();
172 if (yOperandValue == 1) {
173 convertToIdentityOverChild1();
benjamin@webkit.org88d9c892016-04-28 20:50:08 +0000174 m_changed = true;
175 } else if (yOperandValue == 2) {
176 m_node->setOp(ArithMul);
177 m_node->child2() = m_node->child1();
benjamin@webkit.org903025b2015-02-14 04:20:21 +0000178 m_changed = true;
179 }
180 }
181 break;
182
benjamin@webkit.org18c346c2016-03-02 07:53:20 +0000183 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
commit-queue@webkit.orgbf72a9b2016-04-22 04:46:53 +0000198 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
utatane.tea@gmail.com43926962016-11-27 06:08:16 +0000207 if (std::optional<double> reciprocal = safeReciprocalForDivByConst(m_node->child2()->asNumber())) {
commit-queue@webkit.orgbf72a9b2016-04-22 04:46:53 +0000208 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
fpizlo@apple.com7b33e0c2014-04-15 20:26:16 +0000217 case ValueRep:
sbarati@apple.comd8df2b72017-06-22 23:34:05 +0000218 case Int52Rep: {
219 // This short-circuits circuitous conversions, like ValueRep(Int52Rep(value)).
fpizlo@apple.com7b33e0c2014-04-15 20:26:16 +0000220
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) {
fpizlo@apple.comf2999932014-07-15 00:41:39 +0000227 if (m_node->child1().useKind() != Int32Use)
228 break;
fpizlo@apple.com7b33e0c2014-04-15 20:26:16 +0000229 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())) {
fpizlo@apple.com163291d2015-04-28 19:27:23 +0000234 m_insertionSet.insertCheck(m_nodeIndex, m_node);
fpizlo@apple.com7b33e0c2014-04-15 20:26:16 +0000235 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
fpizlo@apple.com163291d2015-04-28 19:27:23 +0000242 m_insertionSet.insertCheck(
243 m_nodeIndex, m_node->origin, Edge(node, Int32Use));
fpizlo@apple.com7b33e0c2014-04-15 20:26:16 +0000244 }
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:
fpizlo@apple.comf2999932014-07-15 00:41:39 +0000253 if (node->child1().useKind() != Int32Use)
254 break;
fpizlo@apple.com7b33e0c2014-04-15 20:26:16 +0000255 hadInt32Check = true;
256 continue;
257
fpizlo@apple.com7b33e0c2014-04-15 20:26:16 +0000258 case ValueRep:
259 continue;
260
261 default:
262 break;
263 }
264 break;
265 }
266 break;
267 }
268
fpizlo@apple.com2c4a7e92014-08-06 05:27:46 +0000269 case Flush: {
270 ASSERT(m_graph.m_form != SSA);
sbarati@apple.com60a3be72017-08-25 18:26:15 +0000271
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 }
fpizlo@apple.com2c4a7e92014-08-06 05:27:46 +0000277
278 Node* setLocal = nullptr;
279 VirtualRegister local = m_node->local();
280
fpizlo@apple.comda834ae2015-03-26 04:28:43 +0000281 for (unsigned i = m_nodeIndex; i--;) {
282 Node* node = m_block->at(i);
sbarati@apple.com60a3be72017-08-25 18:26:15 +0000283
fpizlo@apple.comda834ae2015-03-26 04:28:43 +0000284 if (node->op() == SetLocal && node->local() == local) {
285 setLocal = node;
286 break;
fpizlo@apple.com2c4a7e92014-08-06 05:27:46 +0000287 }
sbarati@apple.com60a3be72017-08-25 18:26:15 +0000288
fpizlo@apple.comda834ae2015-03-26 04:28:43 +0000289 if (accessesOverlap(m_graph, node, AbstractHeap(Stack, local)))
290 break;
sbarati@apple.com60a3be72017-08-25 18:26:15 +0000291
fpizlo@apple.com2c4a7e92014-08-06 05:27:46 +0000292 }
293
294 if (!setLocal)
295 break;
296
fpizlo@apple.com1337b9e2015-02-02 18:15:44 +0000297 // 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();
fpizlo@apple.com2c4a7e92014-08-06 05:27:46 +0000301 m_graph.dethread();
302 m_changed = true;
303 break;
304 }
keith_miller@apple.com53f4eec2016-02-29 22:45:16 +0000305
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 }
fpizlo@apple.com024424c2016-03-09 05:16:47 +0000325
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
sbarati@apple.com6e923952016-10-11 07:28:08 +0000330 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
fpizlo@apple.com024424c2016-03-09 05:16:47 +0000371 case MakeRope:
fpizlo@apple.com024424c2016-03-09 05:16:47 +0000372 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
utatane.tea@gmail.comfe5e95d2017-03-21 11:31:43 +0000398 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
fpizlo@apple.com024424c2016-03-09 05:16:47 +0000429 case GetArrayLength: {
430 if (m_node->arrayMode().type() == Array::Generic
431 || m_node->arrayMode().type() == Array::String) {
432 String string = m_node->child1()->tryGetString(m_graph);
433 if (!!string) {
434 m_graph.convertToConstant(m_node, jsNumber(string.length()));
435 m_changed = true;
fpizlo@apple.com280ef002016-04-05 22:13:16 +0000436 break;
fpizlo@apple.com024424c2016-03-09 05:16:47 +0000437 }
438 }
439 break;
440 }
441
fpizlo@apple.com280ef002016-04-05 22:13:16 +0000442 case GetGlobalObject: {
keith_miller@apple.com45da7602017-01-27 01:47:52 +0000443 if (JSObject* object = m_node->child1()->dynamicCastConstant<JSObject*>(vm())) {
fpizlo@apple.com280ef002016-04-05 22:13:16 +0000444 m_graph.convertToConstant(m_node, object->globalObject());
445 m_changed = true;
446 break;
447 }
448 break;
449 }
450
451 case RegExpExec:
452 case RegExpTest: {
keith_miller@apple.com45da7602017-01-27 01:47:52 +0000453 JSGlobalObject* globalObject = m_node->child1()->dynamicCastConstant<JSGlobalObject*>(vm());
fpizlo@apple.com280ef002016-04-05 22:13:16 +0000454 if (!globalObject) {
455 if (verbose)
456 dataLog("Giving up because no global object.\n");
457 break;
458 }
459
460 if (globalObject->isHavingABadTime()) {
461 if (verbose)
462 dataLog("Giving up because bad time.\n");
463 break;
464 }
465
466 Node* regExpObjectNode = m_node->child2().node();
467 RegExp* regExp;
keith_miller@apple.com45da7602017-01-27 01:47:52 +0000468 if (RegExpObject* regExpObject = regExpObjectNode->dynamicCastConstant<RegExpObject*>(vm()))
fpizlo@apple.com280ef002016-04-05 22:13:16 +0000469 regExp = regExpObject->regExp();
470 else if (regExpObjectNode->op() == NewRegexp)
commit-queue@webkit.orgdb1a6422016-08-25 07:04:00 +0000471 regExp = regExpObjectNode->castOperand<RegExp*>();
fpizlo@apple.com280ef002016-04-05 22:13:16 +0000472 else {
473 if (verbose)
474 dataLog("Giving up because the regexp is unknown.\n");
475 break;
476 }
477
478 Node* stringNode = m_node->child3().node();
479
480 // NOTE: This mostly already protects us from having the compiler execute a regexp
481 // operation on a ginormous string by preventing us from getting our hands on ginormous
482 // strings in the first place.
483 String string = m_node->child3()->tryGetString(m_graph);
484 if (!string) {
485 if (verbose)
486 dataLog("Giving up because the string is unknown.\n");
487 break;
488 }
489
490 FrozenValue* regExpFrozenValue = m_graph.freeze(regExp);
491
492 // Refuse to do things with regular expressions that have a ginormous number of
493 // subpatterns.
494 unsigned ginormousNumberOfSubPatterns = 1000;
495 if (regExp->numSubpatterns() > ginormousNumberOfSubPatterns) {
496 if (verbose)
497 dataLog("Giving up because of pattern limit.\n");
498 break;
499 }
fpizlo@apple.combc16ddb2016-09-06 01:02:22 +0000500
fpizlo@apple.com280ef002016-04-05 22:13:16 +0000501 unsigned lastIndex;
502 if (regExp->globalOrSticky()) {
503 // This will only work if we can prove what the value of lastIndex is. To do this
504 // safely, we need to execute the insertion set so that we see any previous strength
505 // reductions. This is needed for soundness since otherwise the effectfulness of any
506 // previous strength reductions would be invisible to us.
507 executeInsertionSet();
508 lastIndex = UINT_MAX;
509 for (unsigned otherNodeIndex = m_nodeIndex; otherNodeIndex--;) {
510 Node* otherNode = m_block->at(otherNodeIndex);
511 if (otherNode == regExpObjectNode) {
512 lastIndex = 0;
513 break;
514 }
515 if (otherNode->op() == SetRegExpObjectLastIndex
516 && otherNode->child1() == regExpObjectNode
517 && otherNode->child2()->isInt32Constant()
518 && otherNode->child2()->asInt32() >= 0) {
519 lastIndex = static_cast<unsigned>(otherNode->child2()->asInt32());
520 break;
521 }
522 if (writesOverlap(m_graph, otherNode, RegExpObject_lastIndex))
523 break;
524 }
525 if (lastIndex == UINT_MAX) {
526 if (verbose)
527 dataLog("Giving up because the last index is not known.\n");
528 break;
529 }
530 } else
531 lastIndex = 0;
532
533 m_graph.watchpoints().addLazily(globalObject->havingABadTimeWatchpoint());
534
535 Structure* structure = globalObject->regExpMatchesArrayStructure();
536 if (structure->indexingType() != ArrayWithContiguous) {
537 // This is further protection against a race with haveABadTime.
538 if (verbose)
539 dataLog("Giving up because the structure has the wrong indexing type.\n");
540 break;
541 }
542 m_graph.registerStructure(structure);
543
544 RegExpConstructor* constructor = globalObject->regExpConstructor();
545 FrozenValue* constructorFrozenValue = m_graph.freeze(constructor);
546
547 MatchResult result;
fpizlo@apple.combc16ddb2016-09-06 01:02:22 +0000548 Vector<int> ovector;
fpizlo@apple.com280ef002016-04-05 22:13:16 +0000549 // We have to call the kind of match function that the main thread would have called.
550 // Otherwise, we might not have the desired Yarr code compiled, and the match will fail.
551 if (m_node->op() == RegExpExec) {
552 int position;
553 if (!regExp->matchConcurrently(vm(), string, lastIndex, position, ovector)) {
554 if (verbose)
555 dataLog("Giving up because match failed.\n");
556 break;
557 }
558 result.start = position;
559 result.end = ovector[1];
560 } else {
561 if (!regExp->matchConcurrently(vm(), string, lastIndex, result)) {
562 if (verbose)
563 dataLog("Giving up because match failed.\n");
564 break;
565 }
566 }
567
568 // We've constant-folded the regexp. Now we're committed to replacing RegExpExec/Test.
569
570 m_changed = true;
571
572 NodeOrigin origin = m_node->origin;
573
574 m_insertionSet.insertNode(
575 m_nodeIndex, SpecNone, Check, origin, m_node->children.justChecks());
576
577 if (m_node->op() == RegExpExec) {
578 if (result) {
sbarati@apple.com239d20b2017-01-26 23:50:58 +0000579 RegisteredStructureSet* structureSet = m_graph.addStructureSet(structure);
fpizlo@apple.com280ef002016-04-05 22:13:16 +0000580
581 // Create an array modeling the JS array that we will try to allocate. This is
582 // basically createRegExpMatchesArray but over C++ strings instead of JSStrings.
583 Vector<String> resultArray;
584 resultArray.append(string.substring(result.start, result.end - result.start));
585 for (unsigned i = 1; i <= regExp->numSubpatterns(); ++i) {
586 int start = ovector[2 * i];
587 if (start >= 0)
588 resultArray.append(string.substring(start, ovector[2 * i + 1] - start));
589 else
590 resultArray.append(String());
591 }
592
593 unsigned publicLength = resultArray.size();
fpizlo@apple.combc16ddb2016-09-06 01:02:22 +0000594 unsigned vectorLength =
595 Butterfly::optimalContiguousVectorLength(structure, publicLength);
fpizlo@apple.com280ef002016-04-05 22:13:16 +0000596
597 UniquedStringImpl* indexUID = vm().propertyNames->index.impl();
598 UniquedStringImpl* inputUID = vm().propertyNames->input.impl();
599 unsigned indexIndex = m_graph.identifiers().ensure(indexUID);
600 unsigned inputIndex = m_graph.identifiers().ensure(inputUID);
601
602 unsigned firstChild = m_graph.m_varArgChildren.size();
603 m_graph.m_varArgChildren.append(
604 m_insertionSet.insertConstantForUse(
605 m_nodeIndex, origin, structure, KnownCellUse));
606 ObjectMaterializationData* data = m_graph.m_objectMaterializationData.add();
607
608 m_graph.m_varArgChildren.append(
609 m_insertionSet.insertConstantForUse(
610 m_nodeIndex, origin, jsNumber(publicLength), KnownInt32Use));
611 data->m_properties.append(PublicLengthPLoc);
612
613 m_graph.m_varArgChildren.append(
614 m_insertionSet.insertConstantForUse(
615 m_nodeIndex, origin, jsNumber(vectorLength), KnownInt32Use));
616 data->m_properties.append(VectorLengthPLoc);
617
618 m_graph.m_varArgChildren.append(
619 m_insertionSet.insertConstantForUse(
620 m_nodeIndex, origin, jsNumber(result.start), UntypedUse));
621 data->m_properties.append(
622 PromotedLocationDescriptor(NamedPropertyPLoc, indexIndex));
623
624 m_graph.m_varArgChildren.append(Edge(stringNode, UntypedUse));
625 data->m_properties.append(
626 PromotedLocationDescriptor(NamedPropertyPLoc, inputIndex));
627
628 auto materializeString = [&] (const String& string) -> Node* {
629 if (string.isNull())
630 return nullptr;
631 if (string.isEmpty()) {
632 return m_insertionSet.insertConstant(
633 m_nodeIndex, origin, vm().smallStrings.emptyString());
634 }
635 LazyJSValue value = LazyJSValue::newString(m_graph, string);
636 return m_insertionSet.insertNode(
637 m_nodeIndex, SpecNone, LazyJSConstant, origin,
638 OpInfo(m_graph.m_lazyJSValues.add(value)));
639 };
640
641 for (unsigned i = 0; i < resultArray.size(); ++i) {
642 if (Node* node = materializeString(resultArray[i])) {
643 m_graph.m_varArgChildren.append(Edge(node, UntypedUse));
644 data->m_properties.append(
645 PromotedLocationDescriptor(IndexedPropertyPLoc, i));
646 }
647 }
648
649 Node* resultNode = m_insertionSet.insertNode(
650 m_nodeIndex, SpecArray, Node::VarArg, MaterializeNewObject, origin,
651 OpInfo(structureSet), OpInfo(data), firstChild,
652 m_graph.m_varArgChildren.size() - firstChild);
653
654 m_node->convertToIdentityOn(resultNode);
655 } else
656 m_graph.convertToConstant(m_node, jsNull());
657 } else
658 m_graph.convertToConstant(m_node, jsBoolean(!!result));
659
660 // Whether it's Exec or Test, we need to tell the constructor and RegExpObject what's up.
661 // Because SetRegExpObjectLastIndex may exit and it clobbers exit state, we do that
662 // first.
663
664 if (regExp->globalOrSticky()) {
665 m_insertionSet.insertNode(
666 m_nodeIndex, SpecNone, SetRegExpObjectLastIndex, origin,
667 Edge(regExpObjectNode, RegExpObjectUse),
668 m_insertionSet.insertConstantForUse(
669 m_nodeIndex, origin, jsNumber(result ? result.end : 0), UntypedUse));
670
671 origin = origin.withInvalidExit();
672 }
673
674 if (result) {
675 unsigned firstChild = m_graph.m_varArgChildren.size();
676 m_graph.m_varArgChildren.append(
677 m_insertionSet.insertConstantForUse(
678 m_nodeIndex, origin, constructorFrozenValue, KnownCellUse));
679 m_graph.m_varArgChildren.append(
680 m_insertionSet.insertConstantForUse(
681 m_nodeIndex, origin, regExpFrozenValue, KnownCellUse));
682 m_graph.m_varArgChildren.append(Edge(stringNode, KnownCellUse));
683 m_graph.m_varArgChildren.append(
684 m_insertionSet.insertConstantForUse(
685 m_nodeIndex, origin, jsNumber(result.start), KnownInt32Use));
686 m_graph.m_varArgChildren.append(
687 m_insertionSet.insertConstantForUse(
688 m_nodeIndex, origin, jsNumber(result.end), KnownInt32Use));
689 m_insertionSet.insertNode(
690 m_nodeIndex, SpecNone, Node::VarArg, RecordRegExpCachedResult, origin,
691 OpInfo(), OpInfo(), firstChild, m_graph.m_varArgChildren.size() - firstChild);
692
693 origin = origin.withInvalidExit();
694 }
695
696 m_node->origin = origin;
697 break;
698 }
699
msaboff@apple.com69940442016-04-27 01:28:03 +0000700 case StringReplace:
701 case StringReplaceRegExp: {
fpizlo@apple.com280ef002016-04-05 22:13:16 +0000702 Node* stringNode = m_node->child1().node();
703 String string = stringNode->tryGetString(m_graph);
704 if (!string)
705 break;
706
707 Node* regExpObjectNode = m_node->child2().node();
708 RegExp* regExp;
keith_miller@apple.com45da7602017-01-27 01:47:52 +0000709 if (RegExpObject* regExpObject = regExpObjectNode->dynamicCastConstant<RegExpObject*>(vm()))
fpizlo@apple.com280ef002016-04-05 22:13:16 +0000710 regExp = regExpObject->regExp();
711 else if (regExpObjectNode->op() == NewRegexp)
commit-queue@webkit.orgdb1a6422016-08-25 07:04:00 +0000712 regExp = regExpObjectNode->castOperand<RegExp*>();
fpizlo@apple.com280ef002016-04-05 22:13:16 +0000713 else {
714 if (verbose)
715 dataLog("Giving up because the regexp is unknown.\n");
716 break;
717 }
718
719 String replace = m_node->child3()->tryGetString(m_graph);
sbarati@apple.com1b6609c2016-05-22 19:13:23 +0000720 if (!replace)
fpizlo@apple.com280ef002016-04-05 22:13:16 +0000721 break;
722
723 StringBuilder builder;
724
725 unsigned lastIndex = 0;
726 unsigned startPosition = 0;
727 bool ok = true;
728 do {
729 MatchResult result;
fpizlo@apple.combc16ddb2016-09-06 01:02:22 +0000730 Vector<int> ovector;
fpizlo@apple.com280ef002016-04-05 22:13:16 +0000731 // Model which version of match() is called by the main thread.
732 if (replace.isEmpty() && regExp->global()) {
733 if (!regExp->matchConcurrently(vm(), string, startPosition, result)) {
734 ok = false;
735 break;
736 }
737 } else {
738 int position;
739 if (!regExp->matchConcurrently(vm(), string, startPosition, position, ovector)) {
740 ok = false;
741 break;
742 }
743
744 result.start = position;
745 result.end = ovector[1];
746 }
747
748 if (!result)
749 break;
750
751 unsigned replLen = replace.length();
752 if (lastIndex < result.start || replLen) {
753 builder.append(string, lastIndex, result.start - lastIndex);
754 if (replLen)
755 builder.append(substituteBackreferences(replace, string, ovector.data(), regExp));
756 }
757
758 lastIndex = result.end;
759 startPosition = lastIndex;
760
761 // special case of empty match
762 if (result.empty()) {
763 startPosition++;
764 if (startPosition > string.length())
765 break;
766 }
767 } while (regExp->global());
768 if (!ok)
769 break;
770
771 // We are committed at this point.
772 m_changed = true;
773
774 NodeOrigin origin = m_node->origin;
775
776 if (regExp->global()) {
777 m_insertionSet.insertNode(
778 m_nodeIndex, SpecNone, SetRegExpObjectLastIndex, origin,
779 Edge(regExpObjectNode, RegExpObjectUse),
780 m_insertionSet.insertConstantForUse(
781 m_nodeIndex, origin, jsNumber(0), UntypedUse));
782
783 origin = origin.withInvalidExit();
784 }
785
786 if (!lastIndex && builder.isEmpty())
787 m_node->convertToIdentityOn(stringNode);
788 else {
789 if (lastIndex < string.length())
790 builder.append(string, lastIndex, string.length() - lastIndex);
791
792 m_node->convertToLazyJSConstant(
793 m_graph, LazyJSValue::newString(m_graph, builder.toString()));
794 }
795
796 m_node->origin = origin;
797 break;
798 }
fpizlo@apple.comf0b30cb2016-10-18 18:30:05 +0000799
800 case Call:
801 case Construct:
802 case TailCallInlinedCaller:
803 case TailCall: {
804 ExecutableBase* executable = nullptr;
805 Edge callee = m_graph.varArgChild(m_node, 0);
keith_miller@apple.com45da7602017-01-27 01:47:52 +0000806 if (JSFunction* function = callee->dynamicCastConstant<JSFunction*>(vm()))
fpizlo@apple.comf0b30cb2016-10-18 18:30:05 +0000807 executable = function->executable();
808 else if (callee->isFunctionAllocation())
809 executable = callee->castOperand<FunctionExecutable*>();
810
811 if (!executable)
812 break;
813
keith_miller@apple.com45da7602017-01-27 01:47:52 +0000814 if (FunctionExecutable* functionExecutable = jsDynamicCast<FunctionExecutable*>(vm(), executable)) {
fpizlo@apple.comf0b30cb2016-10-18 18:30:05 +0000815 // We need to update m_parameterSlots before we get to the backend, but we don't
816 // want to do too much of this.
817 unsigned numAllocatedArgs =
818 static_cast<unsigned>(functionExecutable->parameterCount()) + 1;
819
820 if (numAllocatedArgs <= Options::maximumDirectCallStackSize()) {
821 m_graph.m_parameterSlots = std::max(
822 m_graph.m_parameterSlots,
823 Graph::parameterSlotsForArgCount(numAllocatedArgs));
824 }
825 }
826
827 m_node->convertToDirectCall(m_graph.freeze(executable));
828 m_changed = true;
829 break;
830 }
fpizlo@apple.com280ef002016-04-05 22:13:16 +0000831
fpizlo@apple.comce995b22013-12-08 19:01:17 +0000832 default:
833 break;
834 }
835 }
fpizlo@apple.com9089acb2013-12-14 06:33:42 +0000836
837 void convertToIdentityOverChild(unsigned childIndex)
838 {
fpizlo@apple.com163291d2015-04-28 19:27:23 +0000839 m_insertionSet.insertCheck(m_nodeIndex, m_node);
fpizlo@apple.com9089acb2013-12-14 06:33:42 +0000840 m_node->children.removeEdge(childIndex ^ 1);
841 m_node->convertToIdentity();
842 m_changed = true;
843 }
844
845 void convertToIdentityOverChild1()
846 {
847 convertToIdentityOverChild(0);
848 }
849
850 void convertToIdentityOverChild2()
851 {
852 convertToIdentityOverChild(1);
853 }
fpizlo@apple.comce995b22013-12-08 19:01:17 +0000854
fpizlo@apple.com4c96a842014-02-13 22:46:51 +0000855 void handleCommutativity()
856 {
sbarati@apple.combe40c4f2016-08-06 00:46:50 +0000857 // It's definitely not sound to swap the lhs and rhs when we may be performing effectful
858 // calls on the lhs/rhs for valueOf.
859 if (m_node->child1().useKind() == UntypedUse || m_node->child2().useKind() == UntypedUse)
860 return;
861
fpizlo@apple.com4c96a842014-02-13 22:46:51 +0000862 // If the right side is a constant then there is nothing left to do.
863 if (m_node->child2()->hasConstant())
864 return;
865
866 // This case ensures that optimizations that look for x + const don't also have
867 // to look for const + x.
sbarati@apple.combe40c4f2016-08-06 00:46:50 +0000868 if (m_node->child1()->hasConstant() && !m_node->child1()->asJSValue().isCell()) {
fpizlo@apple.com4c96a842014-02-13 22:46:51 +0000869 std::swap(m_node->child1(), m_node->child2());
870 m_changed = true;
871 return;
872 }
873
874 // This case ensures that CSE is commutativity-aware.
875 if (m_node->child1().node() > m_node->child2().node()) {
876 std::swap(m_node->child1(), m_node->child2());
877 m_changed = true;
878 return;
879 }
880 }
fpizlo@apple.com280ef002016-04-05 22:13:16 +0000881
882 void executeInsertionSet()
883 {
884 m_nodeIndex += m_insertionSet.execute(m_block);
885 }
fpizlo@apple.com4c96a842014-02-13 22:46:51 +0000886
fpizlo@apple.comce995b22013-12-08 19:01:17 +0000887 InsertionSet m_insertionSet;
888 BasicBlock* m_block;
889 unsigned m_nodeIndex;
890 Node* m_node;
891 bool m_changed;
892};
893
894bool performStrengthReduction(Graph& graph)
895{
fpizlo@apple.comce995b22013-12-08 19:01:17 +0000896 return runPhase<StrengthReductionPhase>(graph);
897}
898
899} } // namespace JSC::DFG
900
901#endif // ENABLE(DFG_JIT)
902