blob: 764accc5b972c7456222517c14046390fa059b68 [file] [log] [blame]
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001/*
msaboff@apple.com7d038492021-10-20 21:45:13 +00002 * Copyright (C) 2015-2021 Apple Inc. All rights reserved.
fpizlo@apple.comee3920c2015-06-17 05:31:30 +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"
27#include "DFGIntegerRangeOptimizationPhase.h"
28
29#if ENABLE(DFG_JIT)
30
31#include "DFGBlockMapInlines.h"
fpizlo@apple.com92dbc1f2015-11-02 00:48:03 +000032#include "DFGBlockSet.h"
fpizlo@apple.comee3920c2015-06-17 05:31:30 +000033#include "DFGGraph.h"
34#include "DFGInsertionSet.h"
fpizlo@apple.com8ff2aef2016-11-04 05:28:35 +000035#include "DFGNodeFlowProjection.h"
fpizlo@apple.comee3920c2015-06-17 05:31:30 +000036#include "DFGPhase.h"
ross.kirsling@sony.come257a3b2020-05-19 23:56:00 +000037#include "JSCJSValueInlines.h"
fpizlo@apple.comee3920c2015-06-17 05:31:30 +000038
39namespace JSC { namespace DFG {
40
41namespace {
42
keith_miller@apple.com504d5852017-09-13 01:31:07 +000043namespace DFGIntegerRangeOptimizationPhaseInternal {
mark.lam@apple.comd27553f2019-09-18 00:36:19 +000044static constexpr bool verbose = false;
keith_miller@apple.com504d5852017-09-13 01:31:07 +000045}
commit-queue@webkit.org07a90bb2016-03-24 09:02:55 +000046const unsigned giveUpThreshold = 50;
fpizlo@apple.comee3920c2015-06-17 05:31:30 +000047
48int64_t clampedSumImpl() { return 0; }
49
50template<typename... Args>
51int64_t clampedSumImpl(int left, Args... args)
52{
53 return static_cast<int64_t>(left) + clampedSumImpl(args...);
54}
55
56template<typename... Args>
57int clampedSum(Args... args)
58{
59 int64_t result = clampedSumImpl(args...);
achristensen@apple.com477be962015-06-17 07:07:42 +000060 return static_cast<int>(std::min(
fpizlo@apple.comee3920c2015-06-17 05:31:30 +000061 static_cast<int64_t>(std::numeric_limits<int>::max()),
62 std::max(
63 static_cast<int64_t>(std::numeric_limits<int>::min()),
achristensen@apple.com477be962015-06-17 07:07:42 +000064 result)));
fpizlo@apple.comee3920c2015-06-17 05:31:30 +000065}
66
fpizlo@apple.com12227fb2015-08-21 00:26:41 +000067bool isGeneralOffset(int offset)
68{
69 return offset >= -1 && offset <= 1;
70}
71
fpizlo@apple.comee3920c2015-06-17 05:31:30 +000072class Relationship {
73public:
74 enum Kind {
75 LessThan,
76 Equal,
77 NotEqual,
78 GreaterThan
79 };
fpizlo@apple.com12227fb2015-08-21 00:26:41 +000080
81 // Some relationships provide more information than others. When a relationship provides more
82 // information, it is less vague.
83 static unsigned vagueness(Kind kind)
84 {
85 switch (kind) {
86 case Equal:
87 return 0;
88 case LessThan:
89 case GreaterThan:
90 return 1;
91 case NotEqual:
92 return 2;
93 }
94 RELEASE_ASSERT_NOT_REACHED();
95 return 0;
96 }
97
mark.lam@apple.comd27553f2019-09-18 00:36:19 +000098 static constexpr unsigned minVagueness = 0;
99 static constexpr unsigned maxVagueness = 2;
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000100
101 static Kind flipped(Kind kind)
102 {
103 switch (kind) {
104 case LessThan:
105 return GreaterThan;
106 case Equal:
107 return Equal;
108 case NotEqual:
109 return NotEqual;
110 case GreaterThan:
111 return LessThan;
112 }
113 RELEASE_ASSERT_NOT_REACHED();
114 return kind;
115 }
116
117 Relationship()
118 : m_left(nullptr)
119 , m_right(nullptr)
120 , m_kind(Equal)
121 , m_offset(0)
122 {
123 }
124
fpizlo@apple.com8ff2aef2016-11-04 05:28:35 +0000125 Relationship(NodeFlowProjection left, NodeFlowProjection right, Kind kind, int offset = 0)
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000126 : m_left(left)
127 , m_right(right)
128 , m_kind(kind)
129 , m_offset(offset)
130 {
131 RELEASE_ASSERT(m_left);
132 RELEASE_ASSERT(m_right);
133 RELEASE_ASSERT(m_left != m_right);
134 }
135
fpizlo@apple.com8ff2aef2016-11-04 05:28:35 +0000136 static Relationship safeCreate(NodeFlowProjection left, NodeFlowProjection right, Kind kind, int offset = 0)
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000137 {
fpizlo@apple.com8ff2aef2016-11-04 05:28:35 +0000138 if (!left.isStillValid() || !right.isStillValid() || left == right)
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000139 return Relationship();
140 return Relationship(left, right, kind, offset);
141 }
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000142
fpizlo@apple.com8ff2aef2016-11-04 05:28:35 +0000143 explicit operator bool() const { return !!m_left; }
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000144
fpizlo@apple.com8ff2aef2016-11-04 05:28:35 +0000145 NodeFlowProjection left() const { return m_left; }
146 NodeFlowProjection right() const { return m_right; }
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000147 Kind kind() const { return m_kind; }
148 int offset() const { return m_offset; }
fpizlo@apple.com12227fb2015-08-21 00:26:41 +0000149
150 unsigned vagueness() const { return vagueness(kind()); }
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000151
152 Relationship flipped() const
153 {
154 if (!*this)
155 return Relationship();
156
157 // This should return Relationship() if -m_offset overflows. For example:
158 //
159 // @a > @b - 2**31
160 //
161 // If we flip it we get:
162 //
163 // @b < @a + 2**31
164 //
165 // Except that the sign gets flipped since it's INT_MIN:
166 //
167 // @b < @a - 2**31
168 //
169 // And that makes no sense. To see how little sense it makes, consider:
170 //
171 // @a > @zero - 2**31
172 //
173 // We would flip it to mean:
174 //
175 // @zero < @a - 2**31
176 //
177 // Which is absurd.
178
179 if (m_offset == std::numeric_limits<int>::min())
180 return Relationship();
181
182 return Relationship(m_right, m_left, flipped(m_kind), -m_offset);
183 }
184
185 Relationship inverse() const
186 {
187 if (!*this)
188 return *this;
189
190 switch (m_kind) {
191 case Equal:
192 return Relationship(m_left, m_right, NotEqual, m_offset);
193 case NotEqual:
194 return Relationship(m_left, m_right, Equal, m_offset);
195 case LessThan:
196 if (sumOverflows<int>(m_offset, -1))
197 return Relationship();
198 return Relationship(m_left, m_right, GreaterThan, m_offset - 1);
199 case GreaterThan:
200 if (sumOverflows<int>(m_offset, 1))
201 return Relationship();
202 return Relationship(m_left, m_right, LessThan, m_offset + 1);
203 }
204
205 RELEASE_ASSERT_NOT_REACHED();
206 }
207
208 bool isCanonical() const { return m_left < m_right; }
209
210 Relationship canonical() const
211 {
212 if (isCanonical())
213 return *this;
214 return flipped();
215 }
216
217 bool sameNodesAs(const Relationship& other) const
218 {
219 return m_left == other.m_left
220 && m_right == other.m_right;
221 }
commit-queue@webkit.org07a90bb2016-03-24 09:02:55 +0000222
223 bool isEquivalentTo(const Relationship& other) const
224 {
225 if (m_left != other.m_left || m_kind != other.m_kind)
226 return false;
227
228 if (*this == other)
229 return true;
230
msaboff@apple.com7d038492021-10-20 21:45:13 +0000231 if (m_right->isInt32Constant() && other.m_right->isInt32Constant()) {
232 int thisRight = m_right->asInt32();
233 int otherRight = other.m_right->asInt32();
234
235 if (sumOverflows<int>(thisRight, m_offset))
236 return false;
237 if (sumOverflows<int>(otherRight, other.m_offset))
238 return false;
239
240 return (thisRight + m_offset) == (otherRight + other.m_offset);
241 }
commit-queue@webkit.org07a90bb2016-03-24 09:02:55 +0000242 return false;
243 }
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000244
245 bool operator==(const Relationship& other) const
246 {
247 return sameNodesAs(other)
248 && m_kind == other.m_kind
249 && m_offset == other.m_offset;
250 }
251
252 bool operator!=(const Relationship& other) const
253 {
254 return !(*this == other);
255 }
256
257 bool operator<(const Relationship& other) const
258 {
259 if (m_left != other.m_left)
260 return m_left < other.m_left;
261 if (m_right != other.m_right)
262 return m_right < other.m_right;
263 if (m_kind != other.m_kind)
264 return m_kind < other.m_kind;
265 return m_offset < other.m_offset;
266 }
267
268 // If possible, returns a form of this relationship where the given node is the left
269 // side. Returns a null relationship if this relationship cannot say anything about this
270 // node.
fpizlo@apple.com8ff2aef2016-11-04 05:28:35 +0000271 Relationship forNode(NodeFlowProjection node) const
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000272 {
273 if (m_left == node)
274 return *this;
275 if (m_right == node)
276 return flipped();
277 return Relationship();
278 }
279
fpizlo@apple.com8ff2aef2016-11-04 05:28:35 +0000280 void setLeft(NodeFlowProjection left)
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000281 {
282 RELEASE_ASSERT(left != m_right);
283 m_left = left;
284 }
justin_michaud@apple.com43813ff2019-08-16 21:33:28 +0000285 void setRight(NodeFlowProjection right)
286 {
287 RELEASE_ASSERT(right != m_left);
288 m_right = right;
289 }
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000290 bool addToOffset(int offset)
291 {
292 if (sumOverflows<int>(m_offset, offset))
293 return false;
294 m_offset += offset;
295 return true;
296 }
297
fpizlo@apple.com12227fb2015-08-21 00:26:41 +0000298 // Attempts to create relationships that summarize the union of this relationship and
299 // the other relationship. Relationships are returned by calling the functor with the newly
300 // created relationships. No relationships are created to indicate TOP. This is used
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000301 // for merging the current relationship-at-head for some pair of nodes and a new
302 // relationship-at-head being proposed by a predecessor. We wish to create a new
303 // relationship that is true whenever either of them are true, which ensuring that we don't
304 // do this forever. Anytime we create a relationship that is not equal to either of the
305 // previous ones, we will cause the analysis fixpoint to reexecute.
306 //
fpizlo@apple.com12227fb2015-08-21 00:26:41 +0000307 // If *this and other are identical, we just pass it to the functor.
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000308 //
309 // If they are different, we pick from a finite set of "general" relationships:
310 //
311 // Eq: this == other + C, where C is -1, 0, or 1.
312 // Lt: this < other + C, where C is -1, 0, or 1.
313 // Gt: this > other + C, where C is -1, 0, or 1.
314 // Ne: this != other + C, where C is -1, 0, or 1.
315 // TOP: the null relationship.
316 //
317 // Constraining C to -1,0,1 is necessary to ensure that the set of general relationships is
318 // finite. This finite set of relationships forms a pretty simple lattice where a
319 // relA->relB means "relB is more general than relA". For example, this<other+1 is more
320 // general than this==other. I'll leave it as an exercise for the reader to see that a
321 // graph between the 13 general relationships is indeed a lattice. The fact that the set of
322 // general relationships is a finite lattice ensures monotonicity of the fixpoint, since
323 // any merge over not-identical relationships returns a relationship that is closer to the
324 // TOP relationship than either of the original relationships. Here's how convergence is
325 // achieved for any pair of relationships over the same nodes:
326 //
327 // - If they are identical, then returning *this means that we won't be responsible for
328 // causing another fixpoint iteration. Once all merges reach this point, we're done.
329 //
330 // - If they are different, then we pick the most constraining of the 13 general
331 // relationships that is true if either *this or other are true. This means that if the
332 // relationships are not identical, the merged relationship will be closer to TOP than
333 // either of the originals. Returning a different relationship means that we will be
334 // responsible for the fixpoint to reloop, but we can only do this at most 13 times since
335 // that's how "deep" the general relationship lattice is.
336 //
337 // Note that C being constrained to -1,0,1 also ensures that we never have to return a
fpizlo@apple.com12227fb2015-08-21 00:26:41 +0000338 // combination of Lt and Gt, as in for example this<other+C && this>other-D. The only possible
339 // values of C and D where this would work are -1 and 1, but in that case we just say
340 // this==other. That said, the logic for merging two == relationships, like this==other+C ||
341 // this==other+D is to attempt to create these two relationships: this>other+min(C,D)-1 &&
342 // this<other+max(C,D)+1. But only one of these relationships will belong to the set of general
343 // relationships.
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000344 //
345 // Here's an example of this in action:
346 //
347 // for (var i = a; ; ++i) { }
348 //
349 // Without C being constrained to -1,0,1, we could end up looping forever: first we'd say
350 // that i==a, then we might say that i<a+2, then i<a+3, then i<a+4, etc. We won't do this
351 // because i<a+2 is not a valid general relationship: so when we merge i==a from the first
352 // iteration and i==a+1 from the second iteration, we create i>a-1 and i<a+2 but then
353 // realize that only i>a-1 is a valid general relationship. This gives us exactly what we
354 // want: a statement that i>=a.
fpizlo@apple.com12227fb2015-08-21 00:26:41 +0000355 //
356 // However, this may return a pair of relationships when merging relationships involving
357 // constants. For example, if given:
358 //
359 // @x == @c
360 // @x == @d
361 //
362 // where @c and @d are constants, then this may pass two relationships to the functor:
363 //
364 // @x > min(@c, @d) - 1
365 // @x < max(@c, @d) + 1
366 //
367 // This still allows for convergence, because just as when merging relationships over
368 // variables, this always picks from a set of general relationships. Hence although this may
369 // produce two relationships as a result of the merge, the total number of relationships that
370 // can be present at head of block is limited by O(graph.size^2).
371 template<typename Functor>
372 void merge(const Relationship& other, const Functor& functor) const
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000373 {
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000374 // Handle the super obvious case first.
fpizlo@apple.com12227fb2015-08-21 00:26:41 +0000375 if (*this == other) {
376 functor(*this);
377 return;
378 }
379
380 if (m_left != other.m_left)
381 return;
382
383 if (m_right != other.m_right) {
384 mergeConstantsImpl(other, functor);
385 return;
386 }
387
388 ASSERT(sameNodesAs(other));
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000389
390 // This does some interesting permutations to reduce the amount of duplicate code. For
391 // example:
392 //
393 // initially: @a != @b, @a > @b
394 // @b != @a, @b < @a
395 // @b < @a, @b != @a
396 // finally: @b != a, @b < @a
397 //
398 // Another example:
399 //
400 // initially: @a < @b, @a != @b
401 // finally: @a != @b, @a < @b
402
403 Relationship a = *this;
404 Relationship b = other;
405 bool needFlip = false;
406
407 // Get rid of GreaterThan.
408 if (a.m_kind == GreaterThan || b.m_kind == GreaterThan) {
409 a = a.flipped();
410 b = b.flipped();
411
412 // In rare cases, we might not be able to flip. Just give up on life in those
413 // cases.
414 if (!a || !b)
fpizlo@apple.com12227fb2015-08-21 00:26:41 +0000415 return;
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000416
417 needFlip = true;
418
419 // If we still have GreaterThan, then it means that we started with @a < @b and
420 // @a > @b. That's pretty much always a tautology; we don't attempt to do smart
421 // things for that case for now.
422 if (a.m_kind == GreaterThan || b.m_kind == GreaterThan)
fpizlo@apple.com12227fb2015-08-21 00:26:41 +0000423 return;
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000424 }
425
426 // Make sure that if we have a LessThan, then it's first.
427 if (b.m_kind == LessThan)
428 std::swap(a, b);
429
430 // Make sure that if we have a NotEqual, then it's first.
431 if (b.m_kind == NotEqual)
432 std::swap(a, b);
433
434 Relationship result = a.mergeImpl(b);
fpizlo@apple.com12227fb2015-08-21 00:26:41 +0000435 if (!result)
436 return;
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000437
438 if (needFlip)
fpizlo@apple.com12227fb2015-08-21 00:26:41 +0000439 result = result.flipped();
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000440
fpizlo@apple.com12227fb2015-08-21 00:26:41 +0000441 functor(result);
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000442 }
443
444 // Attempts to construct one Relationship that adequately summarizes the intersection of
445 // this and other. Returns a null relationship if the filtration should be expressed as two
446 // different relationships. Returning null is always safe because relationship lists in
447 // this phase always imply intersection. So, you could soundly skip calling this method and
448 // just put both relationships into the list. But, that could lead the fixpoint to diverge.
449 // Hence this will attempt to combine the two relationships into one as a convergence hack.
450 // In some cases, it will do something conservative. It's always safe for this to return
451 // *this, or to return other. It'll do that sometimes, mainly to accelerate convergence for
452 // things that we don't think are important enough to slow down the analysis.
453 Relationship filter(const Relationship& other) const
454 {
455 // We are only interested in merging relationships over the same nodes.
456 ASSERT(sameNodesAs(other));
457
458 if (*this == other)
459 return *this;
460
461 // From here we can assume that the two relationships are not identical. Usually we use
462 // this to assume that we have different offsets anytime that everything but the offset
463 // is identical.
464
465 // We want equality to take precedent over everything else, and we don't want multiple
466 // independent claims of equality. That would just be a contradiction. When it does
467 // happen, we will be conservative in the sense that we will pick one.
468 if (m_kind == Equal)
469 return *this;
470 if (other.m_kind == Equal)
471 return other;
472
473 // Useful helper for flipping.
474 auto filterFlipped = [&] () -> Relationship {
475 // If we cannot flip, then just conservatively return *this.
476 Relationship a = flipped();
477 Relationship b = other.flipped();
478 if (!a || !b)
479 return *this;
480 Relationship result = a.filter(b);
481 if (!result)
482 return Relationship();
483 result = result.flipped();
484 if (!result)
485 return *this;
486 return result;
487 };
488
489 if (m_kind == NotEqual) {
490 if (other.m_kind == NotEqual) {
491 // We could do something smarter here. We could even keep both NotEqual's. We
492 // would need to make sure that we correctly collapsed them when merging. But
493 // for now, we just pick one of them and hope for the best.
494 return *this;
495 }
496
497 if (other.m_kind == GreaterThan) {
498 // Implement this in terms of NotEqual.filter(LessThan).
499 return filterFlipped();
500 }
501
502 ASSERT(other.m_kind == LessThan);
503 // We have two claims:
504 // @a != @b + C
505 // @a < @b + D
506 //
507 // If C >= D, then the NotEqual is redundant.
508 // If C < D - 1, then we could keep both, but for now we just keep the LessThan.
509 // If C == D - 1, then the LessThan can be turned into:
510 //
511 // @a < @b + C
512 //
513 // Note that C == this.m_offset, D == other.m_offset.
514
515 if (m_offset == other.m_offset - 1)
516 return Relationship(m_left, m_right, LessThan, m_offset);
517
518 return other;
519 }
520
521 if (other.m_kind == NotEqual)
522 return other.filter(*this);
523
524 if (m_kind == LessThan) {
525 if (other.m_kind == LessThan) {
526 return Relationship(
527 m_left, m_right, LessThan, std::min(m_offset, other.m_offset));
528 }
529
530 ASSERT(other.m_kind == GreaterThan);
531 if (sumOverflows<int>(m_offset, -1))
532 return Relationship();
533 if (sumOverflows<int>(other.m_offset, 1))
534 return Relationship();
535 if (m_offset - 1 == other.m_offset + 1)
536 return Relationship(m_left, m_right, Equal, m_offset - 1);
537
538 return Relationship();
539 }
540
541 ASSERT(m_kind == GreaterThan);
542 return filterFlipped();
543 }
fpizlo@apple.com12227fb2015-08-21 00:26:41 +0000544
545 // Come up with a relationship that is the best description of this && other, provided that left() is
546 // the same and right() is a constant. Also requires that this is at least as vague as other. It may
547 // return this or it may return something else, but whatever it returns, it will have the same nodes as
548 // this. This is not automatically done by filter() because it currently only makes sense to call this
549 // during a very particular part of setOneSide().
550 Relationship filterConstant(const Relationship& other) const
551 {
552 ASSERT(m_left == other.m_left);
553 ASSERT(m_right->isInt32Constant());
554 ASSERT(other.m_right->isInt32Constant());
555 ASSERT(vagueness() >= other.vagueness());
556
557 if (vagueness() == other.vagueness())
558 return *this;
559
560 int thisRight = m_right->asInt32();
561 int otherRight = other.m_right->asInt32();
562
563 // Ignore funny business.
564 if (sumOverflows<int>(otherRight, other.m_offset))
565 return *this;
566
567 int otherEffectiveRight = otherRight + other.m_offset;
568
569 switch (other.m_kind) {
570 case Equal:
msaboff@apple.com4c8b0ae2021-10-21 17:54:48 +0000571 if (differenceOverflows<int>(otherEffectiveRight, thisRight))
572 return *this;
573
fpizlo@apple.com12227fb2015-08-21 00:26:41 +0000574 // Return a version of *this that is Equal to other's constant.
575 return Relationship(m_left, m_right, Equal, otherEffectiveRight - thisRight);
576
577 case LessThan:
578 case GreaterThan:
579 ASSERT(m_kind == NotEqual);
580 // We could do smart things here. But we don't currently have an example of when it would be
581 // valuable. Note that you have to be careful. We could refine NotEqual to LessThan, but only
582 // if the LessThan subsumes the NotEqual.
583 return *this;
584
585 case NotEqual:
586 RELEASE_ASSERT_NOT_REACHED();
587 return Relationship();
588 }
589
590 RELEASE_ASSERT_NOT_REACHED();
591 return Relationship();
592 }
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000593
594 int minValueOfLeft() const
595 {
596 if (m_left->isInt32Constant())
597 return m_left->asInt32();
598
599 if (m_kind == LessThan || m_kind == NotEqual)
600 return std::numeric_limits<int>::min();
601
602 int minRightValue = std::numeric_limits<int>::min();
603 if (m_right->isInt32Constant())
604 minRightValue = m_right->asInt32();
605
606 if (m_kind == GreaterThan)
607 return clampedSum(minRightValue, m_offset, 1);
608 ASSERT(m_kind == Equal);
609 return clampedSum(minRightValue, m_offset);
610 }
611
612 int maxValueOfLeft() const
613 {
614 if (m_left->isInt32Constant())
615 return m_left->asInt32();
616
617 if (m_kind == GreaterThan || m_kind == NotEqual)
618 return std::numeric_limits<int>::max();
619
620 int maxRightValue = std::numeric_limits<int>::max();
621 if (m_right->isInt32Constant())
622 maxRightValue = m_right->asInt32();
623
624 if (m_kind == LessThan)
625 return clampedSum(maxRightValue, m_offset, -1);
626 ASSERT(m_kind == Equal);
627 return clampedSum(maxRightValue, m_offset);
628 }
629
630 void dump(PrintStream& out) const
631 {
632 // This prints out the relationship without any whitespace, like @x<@y+42. This
633 // optimizes for the clarity of a list of relationships. It's easier to read something
634 // like [@1<@2+3, @4==@5-6] than it would be if there was whitespace inside the
635 // relationships.
636
637 if (!*this) {
638 out.print("null");
639 return;
640 }
641
642 out.print(m_left);
643 switch (m_kind) {
644 case LessThan:
645 out.print("<");
646 break;
647 case Equal:
648 out.print("==");
649 break;
650 case NotEqual:
651 out.print("!=");
652 break;
653 case GreaterThan:
654 out.print(">");
655 break;
656 }
657 out.print(m_right);
658 if (m_offset > 0)
659 out.print("+", m_offset);
660 else if (m_offset < 0)
661 out.print("-", -static_cast<int64_t>(m_offset));
662 }
663
664private:
665 Relationship mergeImpl(const Relationship& other) const
666 {
667 ASSERT(sameNodesAs(other));
668 ASSERT(m_kind != GreaterThan);
669 ASSERT(other.m_kind != GreaterThan);
670 ASSERT(*this != other);
671
672 // The purpose of this method is to guarantee that:
673 //
674 // - We avoid having more than one Relationship over the same two nodes. Therefore, if
675 // the merge could be expressed as two Relationships, we prefer to instead pick the
676 // less precise single Relationship form even if that means TOP.
677 //
678 // - If the difference between two Relationships is just the m_offset, then we create a
679 // Relationship that has an offset of -1, 0, or 1. This is an essential convergence
680 // hack. We need -1 and 1 to support <= and >=.
681
682 // From here we can assume that the two relationships are not identical. Usually we use
683 // this to assume that we have different offsets anytime that everything but the offset
684 // is identical.
685
686 if (m_kind == NotEqual) {
687 if (other.m_kind == NotEqual)
688 return Relationship(); // Different offsets, so tautology.
689
690 if (other.m_kind == Equal) {
691 if (m_offset != other.m_offset) {
692 // Saying that you might be B when you've already said that you're anything
693 // but A, where A and B are different, is a tautology. You could just say
694 // that you're anything but A. Adding "(a == b + 1)" to "(a != b + 5)" has
695 // no value.
696 return *this;
697 }
698 // Otherwise, same offsets: we're saying that you're either A or you're not
699 // equal to A.
700
701 return Relationship();
702 }
703
704 RELEASE_ASSERT(other.m_kind == LessThan);
705 // We have these claims, and we're merging them:
706 // @a != @b + C
707 // @a < @b + D
708 //
709 // If we have C == D, then the merge is clearly just the NotEqual.
710 // If we have C < D, then the merge is a tautology.
711 // If we have C > D, then we could keep both claims, but we are cheap, so we
712 // don't. We just use the NotEqual.
713
714 if (m_offset < other.m_offset)
715 return Relationship();
716
717 return *this;
718 }
719
720 if (m_kind == LessThan) {
721 if (other.m_kind == LessThan) {
722 // Figure out what offset to select to merge them. The appropriate offsets are
723 // -1, 0, or 1.
724
725 // First figure out what offset we'd like to use.
726 int bestOffset = std::max(m_offset, other.m_offset);
727
728 // We have something like @a < @b + 2. We can't represent this under the
729 // -1,0,1 rule.
fpizlo@apple.com12227fb2015-08-21 00:26:41 +0000730 if (isGeneralOffset(bestOffset))
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000731 return Relationship(m_left, m_right, LessThan, std::max(bestOffset, -1));
732
733 return Relationship();
734 }
735
736 // The only thing left is Equal. We would have eliminated the GreaterThan's, and
737 // if we merge LessThan and NotEqual, the NotEqual always comes first.
738 RELEASE_ASSERT(other.m_kind == Equal);
739
740 // This is the really interesting case. We have:
741 //
742 // @a < @b + C
743 //
744 // and:
745 //
746 // @a == @b + D
747 //
748 // Therefore we'd like to return:
749 //
750 // @a < @b + max(C, D + 1)
751
sbarati@apple.com3d0f0d12022-03-22 03:54:43 +0000752 if (sumOverflows<int32_t>(other.m_offset, 1))
753 return Relationship();
754
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000755 int bestOffset = std::max(m_offset, other.m_offset + 1);
756
757 // We have something like @a < @b + 2. We can't do it.
fpizlo@apple.com12227fb2015-08-21 00:26:41 +0000758 if (isGeneralOffset(bestOffset))
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000759 return Relationship(m_left, m_right, LessThan, std::max(bestOffset, -1));
760
761 return Relationship();
762 }
763
764 // The only thing left is Equal, since we would have gotten rid of the GreaterThan's.
765 RELEASE_ASSERT(m_kind == Equal);
766
767 // We would never see NotEqual, because those always come first. We would never
768 // see GreaterThan, because we would have eliminated those. We would never see
769 // LessThan, because those always come first.
770
771 RELEASE_ASSERT(other.m_kind == Equal);
772 // We have @a == @b + C and @a == @b + D, where C != D. Turn this into some
773 // inequality that involves a constant that is -1,0,1. Note that we will never have
774 // lessThan and greaterThan because the constants are constrained to -1,0,1. The only
775 // way for both of them to be valid is a<b+1 and a>b-1, but then we would have said
776 // a==b.
777
778 Relationship lessThan;
779 Relationship greaterThan;
780
781 int lessThanEqOffset = std::max(m_offset, other.m_offset);
782 if (lessThanEqOffset >= -2 && lessThanEqOffset <= 0) {
783 lessThan = Relationship(
784 m_left, other.m_right, LessThan, lessThanEqOffset + 1);
785
fpizlo@apple.com12227fb2015-08-21 00:26:41 +0000786 ASSERT(isGeneralOffset(lessThan.offset()));
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000787 }
788
789 int greaterThanEqOffset = std::min(m_offset, other.m_offset);
790 if (greaterThanEqOffset >= 0 && greaterThanEqOffset <= 2) {
791 greaterThan = Relationship(
792 m_left, other.m_right, GreaterThan, greaterThanEqOffset - 1);
793
fpizlo@apple.com12227fb2015-08-21 00:26:41 +0000794 ASSERT(isGeneralOffset(greaterThan.offset()));
fpizlo@apple.comee3920c2015-06-17 05:31:30 +0000795 }
796
797 if (lessThan) {
798 // Both relationships cannot be valid; see above.
799 RELEASE_ASSERT(!greaterThan);
800
801 return lessThan;
802 }
803
804 return greaterThan;
805 }
fpizlo@apple.com12227fb2015-08-21 00:26:41 +0000806
807 template<typename Functor>
808 void mergeConstantsImpl(const Relationship& other, const Functor& functor) const
809 {
810 ASSERT(m_left == other.m_left);
811
812 // Only deal with constant right.
813 if (!m_right->isInt32Constant() || !other.m_right->isInt32Constant())
814 return;
815
816 // What follows is a fairly conservative merge. We could tune this phase to come up with
817 // all possible inequalities between variables and constants, but we focus mainly on cheap
818 // cases for now.
819
rmorisset@apple.comab64b6c2017-10-09 16:49:13 +0000820 // Here are some of the arrangements we can merge usefully assuming @c < @d:
fpizlo@apple.com12227fb2015-08-21 00:26:41 +0000821 //
822 // @x == @c || @x == @d => @x >= c && @x <= @d
823 // @x >= @c || @x <= @d => TOP
824 // @x == @c || @x != @d => @x != @d
825
826 int thisRight = m_right->asInt32();
827 int otherRight = other.m_right->asInt32();
828
829 // Ignore funny business.
830 if (sumOverflows<int>(thisRight, m_offset))
831 return;
832 if (sumOverflows<int>(otherRight, other.m_offset))
833 return;
834
835 int thisEffectiveRight = thisRight + m_offset;
836 int otherEffectiveRight = otherRight + other.m_offset;
837
838 auto makeUpper = [&] (int64_t upper) {
839 if (upper <= thisRight) {
840 // We want m_right + offset to be equal to upper. Hence we want offset to cancel
841 // with m_right. But there's more to it, since we want +1 to turn the LessThan into
842 // a LessThanOrEqual, and we want to make sure we don't end up with non-general
843 // offsets.
844 int offset = static_cast<int>(std::max(
845 static_cast<int64_t>(1) + upper - static_cast<int64_t>(thisRight),
846 static_cast<int64_t>(-1)));
847 functor(Relationship(m_left, m_right, LessThan, offset));
848 }
849 if (upper <= otherRight) {
850 int offset = static_cast<int>(std::max(
851 static_cast<int64_t>(1) + upper - static_cast<int64_t>(otherRight),
852 static_cast<int64_t>(-1)));
853 functor(Relationship(m_left, other.m_right, LessThan, offset));
854 }
855 };
856 auto makeLower = [&] (int64_t lower) {
857 if (lower >= thisRight) {
858 // We want m_right + offset to be equal to lower. Hence we want offset to cancel with
859 // m_right. But there's more to it, since we want -1 to turn the GreaterThan into a
860 // GreaterThanOrEqual, and we want to make sure we don't end up with non-general
861 // offsets.
862 int offset = static_cast<int>(std::min(
863 static_cast<int64_t>(-1) + lower - static_cast<int64_t>(thisRight),
864 static_cast<int64_t>(1)));
865 functor(Relationship(m_left, m_right, GreaterThan, offset));
866 }
867 if (lower >= otherRight) {
868 int offset = static_cast<int>(std::min(
869 static_cast<int64_t>(-1) + lower - static_cast<int64_t>(otherRight),
870 static_cast<int64_t>(1)));
871 functor(Relationship(m_left, other.m_right, GreaterThan, offset));
872 }
873 };
874
875 switch (m_kind) {
876 case Equal: {
877 switch (other.m_kind) {
878 case Equal: {
879 if (thisEffectiveRight == otherEffectiveRight) {
880 // This probably won't arise often. We can keep whichever relationship is general.
881 if (isGeneralOffset(m_offset))
882 functor(*this);
883 if (isGeneralOffset(other.m_offset))
884 functor(other);
885 return;
886 }
887
888 // What follows is the only case where a merge will create more rules than what it
889 // started with. This is fine for convergence because the LessThan/GreaterThan
890 // rules that this creates are general (i.e. have small offsets) and they never
891 // spawn more rules upon subsequent merging.
892
893 makeUpper(std::max(thisEffectiveRight, otherEffectiveRight));
894 makeLower(std::min(thisEffectiveRight, otherEffectiveRight));
895 return;
896 }
897
898 case LessThan: {
899 // Either the LessThan condition subsumes the equality, or the LessThan condition
900 // and equality merge together to create a looser LessThan condition.
901
902 // This is @x == thisEffectiveRight
903 // Other is: @x < otherEffectiveRight
904
905 // We want to create @x <= upper. Figure out the value of upper.
906 makeUpper(std::max(
907 static_cast<int64_t>(thisEffectiveRight),
908 static_cast<int64_t>(otherEffectiveRight) - 1));
909 return;
910 }
911
912 case GreaterThan: {
913 // Opposite of the LessThan case, above.
914
915 // This is: @x == thisEffectiveRight
916 // Other is: @x > otherEffectiveRight
917
918 makeLower(std::min(
919 static_cast<int64_t>(thisEffectiveRight),
920 static_cast<int64_t>(otherEffectiveRight) + 1));
921 return;
922 }
923
924 case NotEqual: {
925 // We keep the NotEqual so long as it doesn't contradict our Equal.
926 if (otherEffectiveRight == thisEffectiveRight)
927 return;
928
929 // But, we only keep the NotEqual if it is general. This simplifies reasoning about
930 // convergence: merging never introduces a new rule unless that rule is general.
931 if (!isGeneralOffset(other.m_offset))
932 return;
933
934 functor(other);
935 return;
936 } }
937
938 RELEASE_ASSERT_NOT_REACHED();
939 return;
940 }
941
942 case LessThan: {
943 switch (other.m_kind) {
944 case Equal: {
945 other.mergeConstantsImpl(*this, functor);
946 return;
947 }
948
949 case LessThan: {
950 makeUpper(std::max(
951 static_cast<int64_t>(thisEffectiveRight) - 1,
952 static_cast<int64_t>(otherEffectiveRight) - 1));
953 return;
954 }
955
956 case GreaterThan: {
957 // We have a claim that @x > @c || @x < @d. If @d > @c, this is the tautology. If
958 // @d <= @c, it's sort of uninteresting. Just ignore this.
959 return;
960 }
961
962 case NotEqual: {
963 // We have a claim that @x < @c || @x != @d. This isn't interesting.
964 return;
965 } }
966
967 RELEASE_ASSERT_NOT_REACHED();
968 return;
969 }
970
971 case GreaterThan: {
972 switch (other.m_kind) {
973 case Equal: {
974 other.mergeConstantsImpl(*this, functor);
975 return;
976 }
977
978 case LessThan: {
979 // Not interesting, see above.
980 return;
981 }
982
983 case GreaterThan: {
984 makeLower(std::min(
985 static_cast<int64_t>(thisEffectiveRight) + 1,
986 static_cast<int64_t>(otherEffectiveRight) + 1));
987 return;
988 }
989
990 case NotEqual: {
991 // Not interesting, see above.
992 return;
993 } }
994
995 RELEASE_ASSERT_NOT_REACHED();
996 return;
997 }
998
999 case NotEqual: {
1000 if (other.m_kind == Equal)
1001 other.mergeConstantsImpl(*this, functor);
1002 return;
1003 } }
1004
1005 RELEASE_ASSERT_NOT_REACHED();
1006 }
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001007
fpizlo@apple.com8ff2aef2016-11-04 05:28:35 +00001008 NodeFlowProjection m_left;
1009 NodeFlowProjection m_right;
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001010 Kind m_kind;
1011 int m_offset; // This offset can be arbitrarily large.
1012};
1013
fpizlo@apple.com8ff2aef2016-11-04 05:28:35 +00001014typedef HashMap<NodeFlowProjection, Vector<Relationship>> RelationshipMap;
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001015
1016class IntegerRangeOptimizationPhase : public Phase {
1017public:
1018 IntegerRangeOptimizationPhase(Graph& graph)
1019 : Phase(graph, "integer range optimization")
1020 , m_zero(nullptr)
1021 , m_relationshipsAtHead(graph)
1022 , m_insertionSet(graph)
1023 {
1024 }
1025
1026 bool run()
1027 {
1028 ASSERT(m_graph.m_form == SSA);
1029
1030 // Before we do anything, make sure that we have a zero constant at the top.
justin_michaud@apple.com4bb63e52019-07-23 17:24:13 +00001031 for (Node* node : *m_graph.block(0)) {
1032 if (node->isInt32Constant() && !node->asInt32()) {
1033 m_zero = node;
1034 break;
1035 }
1036 }
1037 if (!m_zero) {
1038 m_zero = m_insertionSet.insertConstant(0, m_graph.block(0)->at(0)->origin, jsNumber(0));
1039 m_insertionSet.execute(m_graph.block(0));
1040 }
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001041
keith_miller@apple.com504d5852017-09-13 01:31:07 +00001042 if (DFGIntegerRangeOptimizationPhaseInternal::verbose) {
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001043 dataLog("Graph before integer range optimization:\n");
1044 m_graph.dump();
1045 }
1046
1047 // This performs a fixpoint over the blocks in reverse post-order. Logically, we
1048 // maintain a list of relationships at each point in the program. The list should be
1049 // read as an intersection. For example if we have {rel1, rel2, ..., relN}, you should
1050 // read this as:
1051 //
1052 // TOP && rel1 && rel2 && ... && relN
1053 //
1054 // This allows us to express things like:
1055 //
1056 // @a > @b - 42 && @a < @b + 25
1057 //
1058 // But not things like:
1059 //
1060 // @a < @b - 42 || @a > @b + 25
1061 //
1062 // We merge two lists by merging each relationship in one list with each relationship
1063 // in the other list. Merging two relationships will yield a relationship list; as with
mark.lam@apple.com5ee9cd42019-01-08 20:09:58 +00001064 // all such lists it is an intersection. Merging relationships over different variables
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001065 // always yields the empty list (i.e. TOP). This merge style is sound because if we
1066 // have:
1067 //
1068 // (A && B && C) || (D && E && F)
1069 //
1070 // Then a valid merge is just one that will return true if A, B, C are all true, or
1071 // that will return true if D, E, F are all true. Our merge style essentially does:
1072 //
1073 // (A || D) && (A || E) && (A || F) && (B || D) && (B || E) && (B || F) &&
1074 // (C || D) && (C || E) && (C || F)
1075 //
1076 // If A && B && C is true, then this returns true. If D && E && F is true, this also
1077 // returns true.
1078 //
1079 // While this appears at first like a kind of expression explosion, in practice it
1080 // isn't. The code that handles this knows that the merge of two relationships over
1081 // different variables is TOP (i.e. the empty list). For example if A above is @a < @b
1082 // and B above is @c > @d, where @a, @b, @c, and @d are different nodes, the merge will
1083 // yield nothing. In fact, the merge algorithm will skip such merges entirely because
1084 // the relationship lists are actually keyed by node.
1085 //
1086 // Note that it's always safe to drop any of relationship from the relationship list.
1087 // This merely increases the likelihood of the "expression" yielding true, i.e. being
1088 // closer to TOP. Optimizations are only performed if we can establish that the
1089 // expression implied by the relationship list is false for all of those cases where
1090 // some check would have failed.
1091 //
1092 // There is no notion of BOTTOM because we treat blocks that haven't had their
1093 // state-at-head set as a special case: we just transfer all live relationships to such
1094 // a block. After the head of a block is set, we perform the merging as above. In all
1095 // other places where we would ordinarily need BOTTOM, we approximate it by having some
1096 // non-BOTTOM relationship.
1097
1098 BlockList postOrder = m_graph.blocksInPostOrder();
1099
1100 // This loop analyzes the IR to give us m_relationshipsAtHead for each block. This
1101 // may reexecute blocks many times, but it is guaranteed to converge. The state of
1102 // the relationshipsAtHead over any pair of nodes converge monotonically towards the
1103 // TOP relationship (i.e. no relationships in the relationship list). The merge rule
1104 // when between the current relationshipsAtHead and the relationships being propagated
1105 // from a predecessor ensures monotonicity by converting disagreements into one of a
mark.lam@apple.com5ee9cd42019-01-08 20:09:58 +00001106 // small set of "general" relationships. There are 12 such relationships, plus TOP. See
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001107 // the comment above Relationship::merge() for details.
1108 bool changed = true;
1109 while (changed) {
commit-queue@webkit.org07a90bb2016-03-24 09:02:55 +00001110 ++m_iterations;
1111 if (m_iterations >= giveUpThreshold) {
1112 // This case is not necessarily wrong but it can be a sign that this phase
msaboff@apple.com04f9b052019-01-03 17:58:32 +00001113 // does not converge. The value giveUpThreshold was chosen emperically based on
1114 // current tests and real world JS.
1115 // If you hit this case for a legitimate reason, update the giveUpThreshold
commit-queue@webkit.org07a90bb2016-03-24 09:02:55 +00001116 // to the smallest values that converges.
commit-queue@webkit.org07a90bb2016-03-24 09:02:55 +00001117
msaboff@apple.com04f9b052019-01-03 17:58:32 +00001118 // Do not risk holding the thread for too long since this phase is really slow.
commit-queue@webkit.org07a90bb2016-03-24 09:02:55 +00001119 return false;
1120 }
1121
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001122 changed = false;
1123 for (unsigned postOrderIndex = postOrder.size(); postOrderIndex--;) {
1124 BasicBlock* block = postOrder[postOrderIndex];
1125 DFG_ASSERT(
1126 m_graph, nullptr,
1127 block == m_graph.block(0) || m_seenBlocks.contains(block));
1128
1129 m_relationships = m_relationshipsAtHead[block];
1130
annulen@yandex.ru23120122016-12-20 18:26:10 +00001131 for (auto* node : *block) {
keith_miller@apple.com504d5852017-09-13 01:31:07 +00001132 if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001133 dataLog("Analysis: at ", node, ": ", listDump(sortedRelationships()), "\n");
1134 executeNode(node);
1135 }
1136
1137 // Now comes perhaps the most important piece of cleverness: if we Branch, and
1138 // the predicate involves some relation over integers, we propagate different
1139 // information to the taken and notTaken paths. This handles:
1140 // - Branch on int32.
1141 // - Branch on LogicalNot on int32.
1142 // - Branch on compare on int32's.
1143 // - Branch on LogicalNot of compare on int32's.
1144 Node* terminal = block->terminal();
1145 bool alreadyMerged = false;
1146 if (terminal->op() == Branch) {
1147 Relationship relationshipForTrue;
1148 BranchData* branchData = terminal->branchData();
1149
1150 bool invert = false;
1151 if (terminal->child1()->op() == LogicalNot) {
1152 terminal = terminal->child1().node();
1153 invert = true;
1154 }
1155
1156 if (terminal->child1().useKind() == Int32Use) {
1157 relationshipForTrue = Relationship::safeCreate(
1158 terminal->child1().node(), m_zero, Relationship::NotEqual, 0);
1159 } else {
utatane.tea@gmail.coma1264de2017-10-14 15:35:48 +00001160 // FIXME: Handle CompareBelow and CompareBelowEq.
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001161 Node* compare = terminal->child1().node();
1162 switch (compare->op()) {
1163 case CompareEq:
1164 case CompareStrictEq:
1165 case CompareLess:
1166 case CompareLessEq:
1167 case CompareGreater:
1168 case CompareGreaterEq: {
1169 if (!compare->isBinaryUseKind(Int32Use))
1170 break;
1171
1172 switch (compare->op()) {
1173 case CompareEq:
1174 case CompareStrictEq:
1175 relationshipForTrue = Relationship::safeCreate(
1176 compare->child1().node(), compare->child2().node(),
1177 Relationship::Equal, 0);
1178 break;
1179 case CompareLess:
1180 relationshipForTrue = Relationship::safeCreate(
1181 compare->child1().node(), compare->child2().node(),
1182 Relationship::LessThan, 0);
1183 break;
1184 case CompareLessEq:
1185 relationshipForTrue = Relationship::safeCreate(
1186 compare->child1().node(), compare->child2().node(),
1187 Relationship::LessThan, 1);
1188 break;
1189 case CompareGreater:
1190 relationshipForTrue = Relationship::safeCreate(
1191 compare->child1().node(), compare->child2().node(),
1192 Relationship::GreaterThan, 0);
1193 break;
1194 case CompareGreaterEq:
1195 relationshipForTrue = Relationship::safeCreate(
1196 compare->child1().node(), compare->child2().node(),
1197 Relationship::GreaterThan, -1);
1198 break;
1199 default:
1200 DFG_CRASH(m_graph, compare, "Invalid comparison node type");
1201 break;
1202 }
1203 break;
1204 }
1205
1206 default:
1207 break;
1208 }
1209 }
1210
1211 if (invert)
1212 relationshipForTrue = relationshipForTrue.inverse();
1213
1214 if (relationshipForTrue) {
1215 RelationshipMap forTrue = m_relationships;
1216 RelationshipMap forFalse = m_relationships;
1217
keith_miller@apple.com504d5852017-09-13 01:31:07 +00001218 if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001219 dataLog("Dealing with true:\n");
1220 setRelationship(forTrue, relationshipForTrue);
1221 if (Relationship relationshipForFalse = relationshipForTrue.inverse()) {
keith_miller@apple.com504d5852017-09-13 01:31:07 +00001222 if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001223 dataLog("Dealing with false:\n");
1224 setRelationship(forFalse, relationshipForFalse);
1225 }
1226
1227 changed |= mergeTo(forTrue, branchData->taken.block);
1228 changed |= mergeTo(forFalse, branchData->notTaken.block);
1229 alreadyMerged = true;
1230 }
1231 }
1232
1233 if (!alreadyMerged) {
1234 for (BasicBlock* successor : block->successors())
1235 changed |= mergeTo(m_relationships, successor);
1236 }
1237 }
1238 }
1239
1240 // Now we transform the code based on the results computed in the previous loop.
1241 changed = false;
1242 for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
1243 m_relationships = m_relationshipsAtHead[block];
1244 for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
1245 Node* node = block->at(nodeIndex);
keith_miller@apple.com504d5852017-09-13 01:31:07 +00001246 if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001247 dataLog("Transformation: at ", node, ": ", listDump(sortedRelationships()), "\n");
1248
1249 // This ends up being pretty awkward to write because we need to decide if we
1250 // optimize by using the relationships before the operation, but we need to
1251 // call executeNode() before we optimize.
1252 switch (node->op()) {
commit-queue@webkit.orgf4256ed2016-02-17 23:35:11 +00001253 case ArithAbs: {
1254 if (node->child1().useKind() != Int32Use)
1255 break;
1256
1257 auto iter = m_relationships.find(node->child1().node());
1258 if (iter == m_relationships.end())
1259 break;
1260
1261 int minValue = std::numeric_limits<int>::min();
1262 int maxValue = std::numeric_limits<int>::max();
1263 for (Relationship relationship : iter->value) {
1264 minValue = std::max(minValue, relationship.minValueOfLeft());
1265 maxValue = std::min(maxValue, relationship.maxValueOfLeft());
1266 }
1267
1268 executeNode(block->at(nodeIndex));
1269
1270 if (minValue >= 0) {
1271 node->convertToIdentityOn(node->child1().node());
1272 changed = true;
1273 break;
1274 }
commit-queue@webkit.orgf59f0ed2016-10-15 02:19:16 +00001275 bool absIsUnchecked = !shouldCheckOverflow(node->arithMode());
1276 if (maxValue < 0 || (absIsUnchecked && maxValue <= 0)) {
commit-queue@webkit.orgf4256ed2016-02-17 23:35:11 +00001277 node->convertToArithNegate();
commit-queue@webkit.orgf59f0ed2016-10-15 02:19:16 +00001278 if (absIsUnchecked || minValue > std::numeric_limits<int>::min())
commit-queue@webkit.orgf4256ed2016-02-17 23:35:11 +00001279 node->setArithMode(Arith::Unchecked);
1280 changed = true;
1281 break;
1282 }
1283 if (minValue > std::numeric_limits<int>::min()) {
1284 node->setArithMode(Arith::Unchecked);
1285 changed = true;
1286 break;
1287 }
1288
1289 break;
1290 }
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001291 case ArithAdd: {
1292 if (!node->isBinaryUseKind(Int32Use))
1293 break;
1294 if (node->arithMode() != Arith::CheckOverflow)
1295 break;
1296 if (!node->child2()->isInt32Constant())
1297 break;
1298
1299 auto iter = m_relationships.find(node->child1().node());
1300 if (iter == m_relationships.end())
1301 break;
1302
1303 int minValue = std::numeric_limits<int>::min();
1304 int maxValue = std::numeric_limits<int>::max();
1305 for (Relationship relationship : iter->value) {
1306 minValue = std::max(minValue, relationship.minValueOfLeft());
1307 maxValue = std::min(maxValue, relationship.maxValueOfLeft());
1308 }
fpizlo@apple.com11aaea92016-01-26 08:17:31 +00001309
keith_miller@apple.com504d5852017-09-13 01:31:07 +00001310 if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
fpizlo@apple.com11aaea92016-01-26 08:17:31 +00001311 dataLog(" minValue = ", minValue, ", maxValue = ", maxValue, "\n");
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001312
1313 if (sumOverflows<int>(minValue, node->child2()->asInt32()) ||
1314 sumOverflows<int>(maxValue, node->child2()->asInt32()))
1315 break;
fpizlo@apple.com11aaea92016-01-26 08:17:31 +00001316
keith_miller@apple.com504d5852017-09-13 01:31:07 +00001317 if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
fpizlo@apple.com11aaea92016-01-26 08:17:31 +00001318 dataLog(" It's in bounds.\n");
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001319
1320 executeNode(block->at(nodeIndex));
1321 node->setArithMode(Arith::Unchecked);
1322 changed = true;
1323 break;
1324 }
1325
1326 case CheckInBounds: {
1327 auto iter = m_relationships.find(node->child1().node());
1328 if (iter == m_relationships.end())
1329 break;
1330
1331 bool nonNegative = false;
1332 bool lessThanLength = false;
1333 for (Relationship relationship : iter->value) {
1334 if (relationship.minValueOfLeft() >= 0)
1335 nonNegative = true;
1336
fpizlo@apple.com8ff2aef2016-11-04 05:28:35 +00001337 if (relationship.right() == node->child2().node()) {
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001338 if (relationship.kind() == Relationship::Equal
1339 && relationship.offset() < 0)
1340 lessThanLength = true;
1341
1342 if (relationship.kind() == Relationship::LessThan
1343 && relationship.offset() <= 0)
1344 lessThanLength = true;
1345 }
1346 }
justin_michaud@apple.com43813ff2019-08-16 21:33:28 +00001347
1348 if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
1349 dataLogLn("CheckInBounds ", node, " has: ", nonNegative, " ", lessThanLength);
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001350
1351 if (nonNegative && lessThanLength) {
1352 executeNode(block->at(nodeIndex));
rmorisset@apple.com01494a42021-10-17 04:00:16 +00001353 if (UNLIKELY(Options::validateBoundsCheckElimination()) && node->op() == CheckInBounds)
mark.lam@apple.combf384ad2020-09-29 04:54:36 +00001354 m_insertionSet.insertNode(nodeIndex, SpecNone, AssertInBounds, node->origin, node->child1(), node->child2());
justin_michaud@apple.com4bb63e52019-07-23 17:24:13 +00001355 // We just need to make sure we are a value-producing node.
1356 node->convertToIdentityOn(node->child1().node());
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001357 changed = true;
1358 }
1359 break;
1360 }
benjamin@webkit.org5c3fb3a2015-08-14 03:54:32 +00001361
keith_miller@apple.com84bca962021-08-07 21:38:59 +00001362 case EnumeratorGetByVal:
benjamin@webkit.org5c3fb3a2015-08-14 03:54:32 +00001363 case GetByVal: {
1364 if (node->arrayMode().type() != Array::Undecided)
1365 break;
1366
sbarati@apple.come2cdd872018-02-13 01:12:28 +00001367 auto iter = m_relationships.find(m_graph.varArgChild(node, 1).node());
benjamin@webkit.org5c3fb3a2015-08-14 03:54:32 +00001368 if (iter == m_relationships.end())
1369 break;
1370
1371 int minValue = std::numeric_limits<int>::min();
1372 for (Relationship relationship : iter->value)
1373 minValue = std::max(minValue, relationship.minValueOfLeft());
1374
1375 if (minValue < 0)
1376 break;
1377
1378 executeNode(block->at(nodeIndex));
1379 m_graph.convertToConstant(node, jsUndefined());
1380 changed = true;
1381 break;
1382 }
1383
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001384 default:
1385 break;
1386 }
1387
1388 executeNode(block->at(nodeIndex));
1389 }
1390 }
1391
1392 return changed;
1393 }
1394
1395private:
1396 void executeNode(Node* node)
1397 {
1398 switch (node->op()) {
keith_miller@apple.com84bca962021-08-07 21:38:59 +00001399 // FIXME: Teach this about EnumeratorNextExtractIndex.
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001400 case CheckInBounds: {
1401 setRelationship(Relationship::safeCreate(node->child1().node(), node->child2().node(), Relationship::LessThan));
1402 setRelationship(Relationship::safeCreate(node->child1().node(), m_zero, Relationship::GreaterThan, -1));
1403 break;
1404 }
commit-queue@webkit.orgf4256ed2016-02-17 23:35:11 +00001405
1406 case ArithAbs: {
1407 if (node->child1().useKind() != Int32Use)
1408 break;
ysuzuki@apple.comd55676f2021-10-21 00:00:20 +00001409
1410 // If ArithAbs cares about overflow, then INT32_MIN input will cause OSR exit.
1411 // Thus we can safely say `x >= 0`.
1412 if (shouldCheckOverflow(node->arithMode())) {
1413 setRelationship(Relationship(node, m_zero, Relationship::GreaterThan, -1));
1414 break;
1415 }
1416
1417 // If ArithAbs does not care about overflow, it can return INT32_MIN if the input is INT32_MIN.
1418 // If minValue is not INT32_MIN, we can still say it is `x >= 0`.
1419 int minValue = std::numeric_limits<int>::min();
1420 auto iter = m_relationships.find(node->child1().node());
1421 if (iter != m_relationships.end()) {
1422 for (Relationship relationship : iter->value)
1423 minValue = std::max(minValue, relationship.minValueOfLeft());
1424 }
1425
1426 if (minValue > std::numeric_limits<int>::min())
1427 setRelationship(Relationship(node, m_zero, Relationship::GreaterThan, -1));
commit-queue@webkit.orgf4256ed2016-02-17 23:35:11 +00001428 break;
1429 }
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001430
1431 case ArithAdd: {
1432 // We're only interested in int32 additions and we currently only know how to
1433 // handle the non-wrapping ones.
1434 if (!node->isBinaryUseKind(Int32Use))
1435 break;
1436
1437 // FIXME: We could handle the unchecked arithmetic case. We just do it don't right
1438 // now.
1439 if (node->arithMode() != Arith::CheckOverflow)
1440 break;
1441
1442 // Handle add: @value + constant.
1443 if (!node->child2()->isInt32Constant())
1444 break;
1445
1446 int offset = node->child2()->asInt32();
1447
1448 // We add a relationship for @add == @value + constant, and then we copy the
1449 // relationships for @value. This gives us a one-deep view of @value's existing
1450 // relationships, which matches the one-deep search in setRelationship().
1451
1452 setRelationship(
1453 Relationship(node, node->child1().node(), Relationship::Equal, offset));
1454
1455 auto iter = m_relationships.find(node->child1().node());
1456 if (iter != m_relationships.end()) {
1457 Vector<Relationship> toAdd;
1458 for (Relationship relationship : iter->value) {
1459 // We have:
1460 // add: ArithAdd(@x, C)
1461 // @x op @y + D
1462 //
1463 // The following certainly holds:
1464 // @x == @add - C
1465 //
1466 // Which allows us to substitute:
1467 // @add - C op @y + D
1468 //
1469 // And then carry the C over:
1470 // @add op @y + D + C
1471
1472 Relationship newRelationship = relationship;
1473 ASSERT(newRelationship.left() == node->child1().node());
1474
1475 if (newRelationship.right() == node)
1476 continue;
1477 newRelationship.setLeft(node);
1478 if (newRelationship.addToOffset(offset))
1479 toAdd.append(newRelationship);
1480 }
1481 for (Relationship relationship : toAdd)
1482 setRelationship(relationship, 0);
1483 }
1484
1485 // Now we want to establish that both the input and the output of the addition are
1486 // within a particular range of integers.
1487
1488 if (offset > 0) {
1489 // If we have "add: @value + 1" then we know that @value <= max - 1, i.e. that
1490 // @value < max.
1491 if (!sumOverflows<int>(std::numeric_limits<int>::max(), -offset, 1)) {
1492 setRelationship(
1493 Relationship::safeCreate(
1494 node->child1().node(), m_zero, Relationship::LessThan,
1495 std::numeric_limits<int>::max() - offset + 1),
1496 0);
1497 }
1498
1499 // If we have "add: @value + 1" then we know that @add >= min + 1, i.e. that
1500 // @add > min.
1501 if (!sumOverflows<int>(std::numeric_limits<int>::min(), offset, -1)) {
1502 setRelationship(
1503 Relationship(
1504 node, m_zero, Relationship::GreaterThan,
1505 std::numeric_limits<int>::min() + offset - 1),
1506 0);
1507 }
1508 }
1509
1510 if (offset < 0 && offset != std::numeric_limits<int>::min()) {
1511 // If we have "add: @value - 1" then we know that @value >= min + 1, i.e. that
1512 // @value > min.
1513 if (!sumOverflows<int>(std::numeric_limits<int>::min(), offset, -1)) {
1514 setRelationship(
1515 Relationship::safeCreate(
1516 node->child1().node(), m_zero, Relationship::GreaterThan,
1517 std::numeric_limits<int>::min() + offset - 1),
1518 0);
1519 }
1520
1521 // If we have "add: @value + 1" then we know that @add <= max - 1, i.e. that
1522 // @add < max.
1523 if (!sumOverflows<int>(std::numeric_limits<int>::max(), -offset, 1)) {
1524 setRelationship(
1525 Relationship(
1526 node, m_zero, Relationship::LessThan,
1527 std::numeric_limits<int>::max() - offset + 1),
1528 0);
1529 }
1530 }
1531 break;
1532 }
1533
utatane.tea@gmail.com95eee7b2017-05-22 06:24:44 +00001534 case GetArrayLength:
1535 case GetVectorLength: {
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001536 setRelationship(Relationship(node, m_zero, Relationship::GreaterThan, -1));
1537 break;
1538 }
1539
1540 case Upsilon: {
rmorisset@apple.comde25bf82020-10-28 17:32:26 +00001541 auto shadowNode = NodeFlowProjection(node->phi(), NodeFlowProjection::Shadow);
1542 // We must first remove all relationships involving the shadow node, because setEquivalence does not overwrite them.
1543 // Overwriting is only required here because the shadowNodes are not in SSA form (can be written to by several Upsilons).
1544 // Another way to think of it, is that we are maintaining the invariant that relationshipMaps are pruned by liveness.
1545 kill(shadowNode);
1546 setEquivalence(node->child1().node(), shadowNode);
fpizlo@apple.com8ff2aef2016-11-04 05:28:35 +00001547 break;
1548 }
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001549
fpizlo@apple.com8ff2aef2016-11-04 05:28:35 +00001550 case Phi: {
1551 setEquivalence(
1552 NodeFlowProjection(node, NodeFlowProjection::Shadow),
1553 node);
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001554 break;
1555 }
1556
1557 default:
1558 break;
1559 }
1560 }
rmorisset@apple.comde25bf82020-10-28 17:32:26 +00001561
1562 void kill(NodeFlowProjection node)
1563 {
1564 m_relationships.remove(node);
1565
1566 for (auto& relationships : m_relationships.values()) {
1567 unsigned i = 0, j = 0;
1568 while (i < relationships.size()) {
1569 const Relationship& rel = relationships[i++];
1570 ASSERT(rel.left() != node);
1571 if (rel.right() != node)
1572 relationships[j++] = rel;
1573 }
1574 relationships.shrink(j);
1575 }
1576 }
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001577
fpizlo@apple.com8ff2aef2016-11-04 05:28:35 +00001578 void setEquivalence(NodeFlowProjection oldNode, NodeFlowProjection newNode)
1579 {
1580 setRelationship(Relationship::safeCreate(oldNode, newNode, Relationship::Equal, 0));
1581
1582 auto iter = m_relationships.find(oldNode);
1583 if (iter != m_relationships.end()) {
1584 Vector<Relationship> toAdd;
1585 for (Relationship relationship : iter->value) {
1586 Relationship newRelationship = relationship;
1587 // Avoid creating any kind of self-relationship.
1588 if (newNode.node() == newRelationship.right().node())
1589 continue;
1590 newRelationship.setLeft(newNode);
1591 toAdd.append(newRelationship);
1592 }
1593 for (Relationship relationship : toAdd)
1594 setRelationship(relationship);
1595 }
1596 }
1597
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001598 void setRelationship(Relationship relationship, unsigned timeToLive = 1)
1599 {
1600 setRelationship(m_relationships, relationship, timeToLive);
1601 }
1602
1603 void setRelationship(
1604 RelationshipMap& relationshipMap, Relationship relationship, unsigned timeToLive = 1)
1605 {
1606 setOneSide(relationshipMap, relationship, timeToLive);
1607 setOneSide(relationshipMap, relationship.flipped(), timeToLive);
1608 }
1609
1610 void setOneSide(
1611 RelationshipMap& relationshipMap, Relationship relationship, unsigned timeToLive = 1)
1612 {
1613 if (!relationship)
1614 return;
1615
keith_miller@apple.com504d5852017-09-13 01:31:07 +00001616 if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
rmorisset@apple.comde25bf82020-10-28 17:32:26 +00001617 dataLogLn(" Setting: ", relationship, " (ttl = ", timeToLive, ")");
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001618
1619 auto result = relationshipMap.add(
1620 relationship.left(), Vector<Relationship>());
1621 Vector<Relationship>& relationships = result.iterator->value;
fpizlo@apple.com12227fb2015-08-21 00:26:41 +00001622
1623 if (relationship.right()->isInt32Constant()) {
1624 // We want to do some work to refine relationships over constants. This is necessary because
1625 // when we introduce a constant into the IR, we don't automatically create relationships
1626 // between that constant and the other constants. That means that when we do introduce
1627 // relationships between a non-constant and a constant, we need to check the other
1628 // relationships between that non-constant and other constants to see if we can make some
1629 // refinements. Possible constant statement filtrations:
1630 //
1631 // - @x == @c and @x != @d, where @c > @d:
1632 // @x == @c and @x > @d
1633 //
1634 // but actually we are more aggressive:
1635 //
1636 // - @x == @c and @x op @d where @c == @d + k
1637 // @x == @c and @x == @d + k
1638 //
1639 // And this is also possible:
1640 //
1641 // - @x > @c and @x != @d where @c == @d + k and k >= 0
1642 //
1643 // @x > @c and @x > @d + k
1644 //
1645 // So, here's what we should do depending on the kind of relationship we're introducing:
1646 //
1647 // Equal constant: Find all LessThan, NotEqual, and GreaterThan constant operations and refine
1648 // them to be Equal constant. Don't worry about contradictions.
1649 //
1650 // LessThan, GreaterThan constant: See if there is any Equal constant, and if so, refine to
1651 // that. Otherwise, find all NotEqual constant operations and refine them to be LessThan or
1652 // GreaterThan constant if possible.
1653 //
1654 // NotEqual constant: See if there is any Equal constant, and if so, refine to that. Otherwise,
1655 // see if there is any LessThan or GreaterThan constant operation, and if so, attempt to
1656 // refine to that.
1657 //
1658 // Seems that the key thing is to have a filterConstant() operation that returns a refined
1659 // version of *this based on other. The code here accomplishes this by using the vagueness
1660 // index (Relationship::vagueness()) to first find less vague relationships and refine this one
1661 // using them, and then find more vague relationships and refine those to this.
1662
1663 if (relationship.vagueness() != Relationship::minVagueness) {
1664 // We're not minimally vague (maximally specific), so try to refine ourselves based on what
1665 // we already know.
1666 for (Relationship& otherRelationship : relationships) {
1667 if (otherRelationship.vagueness() < relationship.vagueness()
1668 && otherRelationship.right()->isInt32Constant()) {
1669 Relationship newRelationship = relationship.filterConstant(otherRelationship);
keith_miller@apple.com504d5852017-09-13 01:31:07 +00001670 if (DFGIntegerRangeOptimizationPhaseInternal::verbose && newRelationship != relationship)
fpizlo@apple.com12227fb2015-08-21 00:26:41 +00001671 dataLog(" Refined to: ", newRelationship, " based on ", otherRelationship, "\n");
1672 relationship = newRelationship;
1673 }
1674 }
1675 }
1676
1677 if (relationship.vagueness() != Relationship::maxVagueness) {
1678 // We're not maximally value (minimally specific), so try to refine other relationships
1679 // based on this one.
1680 for (Relationship& otherRelationship : relationships) {
1681 if (otherRelationship.vagueness() > relationship.vagueness()
1682 && otherRelationship.right()->isInt32Constant()) {
1683 Relationship newRelationship = otherRelationship.filterConstant(relationship);
keith_miller@apple.com504d5852017-09-13 01:31:07 +00001684 if (DFGIntegerRangeOptimizationPhaseInternal::verbose && newRelationship != otherRelationship)
fpizlo@apple.com12227fb2015-08-21 00:26:41 +00001685 dataLog(" Refined ", otherRelationship, " to: ", newRelationship, "\n");
1686 otherRelationship = newRelationship;
1687 }
1688 }
1689 }
1690 }
1691
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001692 Vector<Relationship> toAdd;
1693 bool found = false;
1694 for (Relationship& otherRelationship : relationships) {
1695 if (otherRelationship.sameNodesAs(relationship)) {
1696 if (Relationship filtered = otherRelationship.filter(relationship)) {
1697 ASSERT(filtered.left() == relationship.left());
1698 otherRelationship = filtered;
rmorisset@apple.comde25bf82020-10-28 17:32:26 +00001699 if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
1700 dataLogLn(" filtered: ", filtered);
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001701 found = true;
1702 }
1703 }
fpizlo@apple.com12227fb2015-08-21 00:26:41 +00001704
1705 // FIXME: Also add filtration over statements about constants. For example, if we have
1706 // @x == @c and @x != @d, where @d > @c, then we want to turn @x != @d into @x < @d.
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001707
1708 if (timeToLive && otherRelationship.kind() == Relationship::Equal) {
keith_miller@apple.com504d5852017-09-13 01:31:07 +00001709 if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
justin_michaud@apple.com43813ff2019-08-16 21:33:28 +00001710 dataLog(" Considering (lhs): ", otherRelationship, "\n");
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001711
1712 // We have:
1713 // @a op @b + C
1714 // @a == @c + D
1715 //
1716 // This implies:
1717 // @c + D op @b + C
1718 // @c op @b + C - D
1719 //
1720 // Where: @a == relationship.left(), @b == relationship.right(),
1721 // @a == otherRelationship.left(), @c == otherRelationship.right().
1722
1723 if (otherRelationship.offset() != std::numeric_limits<int>::min()) {
1724 Relationship newRelationship = relationship;
1725 if (newRelationship.right() != otherRelationship.right()) {
1726 newRelationship.setLeft(otherRelationship.right());
1727 if (newRelationship.addToOffset(-otherRelationship.offset()))
1728 toAdd.append(newRelationship);
1729 }
1730 }
1731 }
1732 }
justin_michaud@apple.com43813ff2019-08-16 21:33:28 +00001733
1734 if (timeToLive && relationship.kind() != Relationship::Equal) {
1735 for (Relationship& possibleEquality : relationshipMap.get(relationship.right())) {
1736 if (possibleEquality.kind() != Relationship::Equal
1737 || possibleEquality.offset() == std::numeric_limits<int>::min()
1738 || possibleEquality.right() == relationship.left())
1739 continue;
1740 if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
1741 dataLog(" Considering (rhs): ", possibleEquality, "\n");
1742
1743 // We have:
1744 // @a op @b + C
1745 // @b == @c + D
1746 //
1747 // This implies:
1748 // @a op @c + (C + D)
1749 //
1750 // Where: @a == relationship.left(), @b == relationship.right()
1751
1752 Relationship newRelationship = relationship;
1753 newRelationship.setRight(possibleEquality.right());
1754 if (newRelationship.addToOffset(possibleEquality.offset()))
1755 toAdd.append(newRelationship);
1756 }
1757 }
rmorisset@apple.comde25bf82020-10-28 17:32:26 +00001758
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001759 if (!found)
1760 relationships.append(relationship);
1761
1762 for (Relationship anotherRelationship : toAdd) {
1763 ASSERT(timeToLive);
1764 setOneSide(relationshipMap, anotherRelationship, timeToLive - 1);
1765 }
1766 }
1767
1768 bool mergeTo(RelationshipMap& relationshipMap, BasicBlock* target)
1769 {
keith_miller@apple.com504d5852017-09-13 01:31:07 +00001770 if (DFGIntegerRangeOptimizationPhaseInternal::verbose) {
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001771 dataLog("Merging to ", pointerDump(target), ":\n");
1772 dataLog(" Incoming: ", listDump(sortedRelationships(relationshipMap)), "\n");
1773 dataLog(" At head: ", listDump(sortedRelationships(m_relationshipsAtHead[target])), "\n");
1774 }
1775
1776 if (m_seenBlocks.add(target)) {
1777 // This is a new block. We copy subject to liveness pruning.
fpizlo@apple.com8ff2aef2016-11-04 05:28:35 +00001778 auto isLive = [&] (NodeFlowProjection node) {
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001779 if (node == m_zero)
1780 return true;
1781 return target->ssa->liveAtHead.contains(node);
1782 };
1783
1784 for (auto& entry : relationshipMap) {
1785 if (!isLive(entry.key))
1786 continue;
1787
1788 Vector<Relationship> values;
1789 for (Relationship relationship : entry.value) {
1790 ASSERT(relationship.left() == entry.key);
1791 if (isLive(relationship.right())) {
keith_miller@apple.com504d5852017-09-13 01:31:07 +00001792 if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001793 dataLog(" Propagating ", relationship, "\n");
1794 values.append(relationship);
1795 }
1796 }
1797
1798 std::sort(values.begin(), values.end());
1799 m_relationshipsAtHead[target].add(entry.key, values);
1800 }
1801 return true;
1802 }
1803
1804 // Merge by intersecting. We have no notion of BOTTOM, so we use the omission of
1805 // relationships for a pair of nodes to mean TOP. The reason why we don't need BOTTOM
1806 // is (1) we just overapproximate contradictions and (2) a value never having been
1807 // assigned would only happen if we have not processed the node's predecessor. We
1808 // shouldn't process blocks until we have processed the block's predecessor because we
1809 // are using reverse postorder.
fpizlo@apple.com8ff2aef2016-11-04 05:28:35 +00001810 Vector<NodeFlowProjection> toRemove;
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001811 bool changed = false;
1812 for (auto& entry : m_relationshipsAtHead[target]) {
1813 auto iter = relationshipMap.find(entry.key);
1814 if (iter == relationshipMap.end()) {
1815 toRemove.append(entry.key);
1816 changed = true;
1817 continue;
1818 }
commit-queue@webkit.org07a90bb2016-03-24 09:02:55 +00001819
1820 Vector<Relationship> constantRelationshipsAtHead;
1821 for (Relationship& relationshipAtHead : entry.value) {
1822 if (relationshipAtHead.right()->isInt32Constant())
1823 constantRelationshipsAtHead.append(relationshipAtHead);
1824 }
1825
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001826 Vector<Relationship> mergedRelationships;
1827 for (Relationship targetRelationship : entry.value) {
1828 for (Relationship sourceRelationship : iter->value) {
keith_miller@apple.com504d5852017-09-13 01:31:07 +00001829 if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001830 dataLog(" Merging ", targetRelationship, " and ", sourceRelationship, ":\n");
fpizlo@apple.com12227fb2015-08-21 00:26:41 +00001831 targetRelationship.merge(
1832 sourceRelationship,
1833 [&] (Relationship newRelationship) {
keith_miller@apple.com504d5852017-09-13 01:31:07 +00001834 if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
fpizlo@apple.com12227fb2015-08-21 00:26:41 +00001835 dataLog(" Got ", newRelationship, "\n");
commit-queue@webkit.org07a90bb2016-03-24 09:02:55 +00001836
1837 if (newRelationship.right()->isInt32Constant()) {
1838 // We can produce a relationship with a constant equivalent to
1839 // an existing relationship yet of a different form. For example:
1840 //
1841 // @a == @b(42) + 0
1842 // @a == @c(41) + 1
1843 //
1844 // We do not want to perpetually switch between those two forms,
1845 // so we always prefer the one already at head.
1846
1847 for (Relationship& existingRelationshipAtHead : constantRelationshipsAtHead) {
1848 if (existingRelationshipAtHead.isEquivalentTo(newRelationship)) {
1849 newRelationship = existingRelationshipAtHead;
1850 break;
1851 }
1852 }
1853 }
fpizlo@apple.com12227fb2015-08-21 00:26:41 +00001854
1855 // We need to filter() to avoid exponential explosion of identical
1856 // relationships. We do this here to avoid making setOneSide() do
1857 // more work, since we expect setOneSide() will be called more
1858 // frequently. Here's an example. At some point someone might start
1859 // with two relationships like @a > @b - C and @a < @b + D. Then
1860 // someone does a setRelationship() passing something that turns
1861 // both of these into @a == @b. Now we have @a == @b duplicated.
1862 // Let's say that this duplicate @a == @b ends up at the head of a
1863 // loop. If we didn't have this rule, then the loop would propagate
1864 // duplicate @a == @b's onto the existing duplicate @a == @b's.
1865 // There would be four pairs of @a == @b, each of which would
1866 // create a new @a == @b. Now we'd have four of these duplicates
1867 // and the next time around we'd have 8, then 16, etc. We avoid
1868 // this here by doing this filtration. That might be a bit of
1869 // overkill, since it's probably just the identical duplicate
1870 // relationship case we want' to avoid. But, I'll keep this until
1871 // we have evidence that this is a performance problem. Remember -
1872 // we are already dealing with a list that is pruned down to
1873 // relationships with identical left operand. It shouldn't be a
1874 // large list.
1875 bool found = false;
1876 for (Relationship& existingRelationship : mergedRelationships) {
1877 if (existingRelationship.sameNodesAs(newRelationship)) {
1878 Relationship filtered =
1879 existingRelationship.filter(newRelationship);
1880 if (filtered) {
1881 existingRelationship = filtered;
1882 found = true;
1883 break;
1884 }
1885 }
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001886 }
fpizlo@apple.com12227fb2015-08-21 00:26:41 +00001887
1888 if (!found)
1889 mergedRelationships.append(newRelationship);
1890 });
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001891 }
1892 }
1893 std::sort(mergedRelationships.begin(), mergedRelationships.end());
1894 if (entry.value == mergedRelationships)
1895 continue;
1896
1897 entry.value = mergedRelationships;
1898 changed = true;
1899 }
fpizlo@apple.com8ff2aef2016-11-04 05:28:35 +00001900 for (NodeFlowProjection node : toRemove)
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001901 m_relationshipsAtHead[target].remove(node);
1902
1903 return changed;
1904 }
1905
1906 Vector<Relationship> sortedRelationships(const RelationshipMap& relationships)
1907 {
1908 Vector<Relationship> result;
1909 for (auto& entry : relationships)
1910 result.appendVector(entry.value);
1911 std::sort(result.begin(), result.end());
1912 return result;
1913 }
1914
1915 Vector<Relationship> sortedRelationships()
1916 {
1917 return sortedRelationships(m_relationships);
1918 }
1919
1920 Node* m_zero;
1921 RelationshipMap m_relationships;
1922 BlockSet m_seenBlocks;
1923 BlockMap<RelationshipMap> m_relationshipsAtHead;
1924 InsertionSet m_insertionSet;
commit-queue@webkit.org07a90bb2016-03-24 09:02:55 +00001925
1926 unsigned m_iterations { 0 };
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001927};
1928
1929} // anonymous namespace
1930
1931bool performIntegerRangeOptimization(Graph& graph)
1932{
fpizlo@apple.comee3920c2015-06-17 05:31:30 +00001933 return runPhase<IntegerRangeOptimizationPhase>(graph);
1934}
1935
1936} } // namespace JSC::DFG
1937
1938#endif // ENABLE(DFG_JIT)
1939