1 | /*
|
---|
2 | * Copyright (C) 2015-2021 Apple Inc. All rights reserved.
|
---|
3 | *
|
---|
4 | * Redistribution and use in source and binary forms, with or without
|
---|
5 | * modification, are permitted provided that the following conditions
|
---|
6 | * are met:
|
---|
7 | * 1. Redistributions of source code must retain the above copyright
|
---|
8 | * notice, this list of conditions and the following disclaimer.
|
---|
9 | * 2. Redistributions in binary form must reproduce the above copyright
|
---|
10 | * notice, this list of conditions and the following disclaimer in the
|
---|
11 | * documentation and/or other materials provided with the distribution.
|
---|
12 | *
|
---|
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
|
---|
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
---|
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
---|
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
|
---|
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
---|
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
---|
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
---|
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
---|
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
---|
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
---|
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
---|
24 | */
|
---|
25 |
|
---|
26 | #include "config.h"
|
---|
27 | #include "DFGIntegerRangeOptimizationPhase.h"
|
---|
28 |
|
---|
29 | #if ENABLE(DFG_JIT)
|
---|
30 |
|
---|
31 | #include "DFGBlockMapInlines.h"
|
---|
32 | #include "DFGBlockSet.h"
|
---|
33 | #include "DFGGraph.h"
|
---|
34 | #include "DFGInsertionSet.h"
|
---|
35 | #include "DFGNodeFlowProjection.h"
|
---|
36 | #include "DFGPhase.h"
|
---|
37 | #include "JSCJSValueInlines.h"
|
---|
38 |
|
---|
39 | namespace JSC { namespace DFG {
|
---|
40 |
|
---|
41 | namespace {
|
---|
42 |
|
---|
43 | namespace DFGIntegerRangeOptimizationPhaseInternal {
|
---|
44 | static constexpr bool verbose = false;
|
---|
45 | }
|
---|
46 | const unsigned giveUpThreshold = 50;
|
---|
47 |
|
---|
48 | int64_t clampedSumImpl() { return 0; }
|
---|
49 |
|
---|
50 | template<typename... Args>
|
---|
51 | int64_t clampedSumImpl(int left, Args... args)
|
---|
52 | {
|
---|
53 | return static_cast<int64_t>(left) + clampedSumImpl(args...);
|
---|
54 | }
|
---|
55 |
|
---|
56 | template<typename... Args>
|
---|
57 | int clampedSum(Args... args)
|
---|
58 | {
|
---|
59 | int64_t result = clampedSumImpl(args...);
|
---|
60 | return static_cast<int>(std::min(
|
---|
61 | static_cast<int64_t>(std::numeric_limits<int>::max()),
|
---|
62 | std::max(
|
---|
63 | static_cast<int64_t>(std::numeric_limits<int>::min()),
|
---|
64 | result)));
|
---|
65 | }
|
---|
66 |
|
---|
67 | bool isGeneralOffset(int offset)
|
---|
68 | {
|
---|
69 | return offset >= -1 && offset <= 1;
|
---|
70 | }
|
---|
71 |
|
---|
72 | class Relationship {
|
---|
73 | public:
|
---|
74 | enum Kind {
|
---|
75 | LessThan,
|
---|
76 | Equal,
|
---|
77 | NotEqual,
|
---|
78 | GreaterThan
|
---|
79 | };
|
---|
80 |
|
---|
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 |
|
---|
98 | static constexpr unsigned minVagueness = 0;
|
---|
99 | static constexpr unsigned maxVagueness = 2;
|
---|
100 |
|
---|
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 |
|
---|
125 | Relationship(NodeFlowProjection left, NodeFlowProjection right, Kind kind, int offset = 0)
|
---|
126 | : 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 |
|
---|
136 | static Relationship safeCreate(NodeFlowProjection left, NodeFlowProjection right, Kind kind, int offset = 0)
|
---|
137 | {
|
---|
138 | if (!left.isStillValid() || !right.isStillValid() || left == right)
|
---|
139 | return Relationship();
|
---|
140 | return Relationship(left, right, kind, offset);
|
---|
141 | }
|
---|
142 |
|
---|
143 | explicit operator bool() const { return !!m_left; }
|
---|
144 |
|
---|
145 | NodeFlowProjection left() const { return m_left; }
|
---|
146 | NodeFlowProjection right() const { return m_right; }
|
---|
147 | Kind kind() const { return m_kind; }
|
---|
148 | int offset() const { return m_offset; }
|
---|
149 |
|
---|
150 | unsigned vagueness() const { return vagueness(kind()); }
|
---|
151 |
|
---|
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 | }
|
---|
222 |
|
---|
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 |
|
---|
231 | 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 | }
|
---|
242 | return false;
|
---|
243 | }
|
---|
244 |
|
---|
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.
|
---|
271 | Relationship forNode(NodeFlowProjection node) const
|
---|
272 | {
|
---|
273 | if (m_left == node)
|
---|
274 | return *this;
|
---|
275 | if (m_right == node)
|
---|
276 | return flipped();
|
---|
277 | return Relationship();
|
---|
278 | }
|
---|
279 |
|
---|
280 | void setLeft(NodeFlowProjection left)
|
---|
281 | {
|
---|
282 | RELEASE_ASSERT(left != m_right);
|
---|
283 | m_left = left;
|
---|
284 | }
|
---|
285 | void setRight(NodeFlowProjection right)
|
---|
286 | {
|
---|
287 | RELEASE_ASSERT(right != m_left);
|
---|
288 | m_right = right;
|
---|
289 | }
|
---|
290 | bool addToOffset(int offset)
|
---|
291 | {
|
---|
292 | if (sumOverflows<int>(m_offset, offset))
|
---|
293 | return false;
|
---|
294 | m_offset += offset;
|
---|
295 | return true;
|
---|
296 | }
|
---|
297 |
|
---|
298 | // 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
|
---|
301 | // 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 | //
|
---|
307 | // If *this and other are identical, we just pass it to the functor.
|
---|
308 | //
|
---|
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
|
---|
338 | // 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.
|
---|
344 | //
|
---|
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.
|
---|
355 | //
|
---|
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
|
---|
373 | {
|
---|
374 | // Handle the super obvious case first.
|
---|
375 | 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));
|
---|
389 |
|
---|
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)
|
---|
415 | return;
|
---|
416 |
|
---|
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)
|
---|
423 | return;
|
---|
424 | }
|
---|
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);
|
---|
435 | if (!result)
|
---|
436 | return;
|
---|
437 |
|
---|
438 | if (needFlip)
|
---|
439 | result = result.flipped();
|
---|
440 |
|
---|
441 | functor(result);
|
---|
442 | }
|
---|
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 | }
|
---|
544 |
|
---|
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:
|
---|
571 | if (differenceOverflows<int>(otherEffectiveRight, thisRight))
|
---|
572 | return *this;
|
---|
573 |
|
---|
574 | // 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 | }
|
---|
593 |
|
---|
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 |
|
---|
664 | private:
|
---|
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.
|
---|
730 | if (isGeneralOffset(bestOffset))
|
---|
731 | 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 |
|
---|
752 | if (sumOverflows<int32_t>(other.m_offset, 1))
|
---|
753 | return Relationship();
|
---|
754 |
|
---|
755 | int bestOffset = std::max(m_offset, other.m_offset + 1);
|
---|
756 |
|
---|
757 | // We have something like @a < @b + 2. We can't do it.
|
---|
758 | if (isGeneralOffset(bestOffset))
|
---|
759 | 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 |
|
---|
786 | ASSERT(isGeneralOffset(lessThan.offset()));
|
---|
787 | }
|
---|
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 |
|
---|
794 | ASSERT(isGeneralOffset(greaterThan.offset()));
|
---|
795 | }
|
---|
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 | }
|
---|
806 |
|
---|
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 |
|
---|
820 | // Here are some of the arrangements we can merge usefully assuming @c < @d:
|
---|
821 | //
|
---|
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 | }
|
---|
1007 |
|
---|
1008 | NodeFlowProjection m_left;
|
---|
1009 | NodeFlowProjection m_right;
|
---|
1010 | Kind m_kind;
|
---|
1011 | int m_offset; // This offset can be arbitrarily large.
|
---|
1012 | };
|
---|
1013 |
|
---|
1014 | typedef HashMap<NodeFlowProjection, Vector<Relationship>> RelationshipMap;
|
---|
1015 |
|
---|
1016 | class IntegerRangeOptimizationPhase : public Phase {
|
---|
1017 | public:
|
---|
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.
|
---|
1031 | 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 | }
|
---|
1041 |
|
---|
1042 | if (DFGIntegerRangeOptimizationPhaseInternal::verbose) {
|
---|
1043 | 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
|
---|
1064 | // all such lists it is an intersection. Merging relationships over different variables
|
---|
1065 | // 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
|
---|
1106 | // small set of "general" relationships. There are 12 such relationships, plus TOP. See
|
---|
1107 | // the comment above Relationship::merge() for details.
|
---|
1108 | bool changed = true;
|
---|
1109 | while (changed) {
|
---|
1110 | ++m_iterations;
|
---|
1111 | if (m_iterations >= giveUpThreshold) {
|
---|
1112 | // This case is not necessarily wrong but it can be a sign that this phase
|
---|
1113 | // 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
|
---|
1116 | // to the smallest values that converges.
|
---|
1117 |
|
---|
1118 | // Do not risk holding the thread for too long since this phase is really slow.
|
---|
1119 | return false;
|
---|
1120 | }
|
---|
1121 |
|
---|
1122 | 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 |
|
---|
1131 | for (auto* node : *block) {
|
---|
1132 | if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
|
---|
1133 | 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 {
|
---|
1160 | // FIXME: Handle CompareBelow and CompareBelowEq.
|
---|
1161 | 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 |
|
---|
1218 | if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
|
---|
1219 | dataLog("Dealing with true:\n");
|
---|
1220 | setRelationship(forTrue, relationshipForTrue);
|
---|
1221 | if (Relationship relationshipForFalse = relationshipForTrue.inverse()) {
|
---|
1222 | if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
|
---|
1223 | 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);
|
---|
1246 | if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
|
---|
1247 | 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()) {
|
---|
1253 | 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 | }
|
---|
1275 | bool absIsUnchecked = !shouldCheckOverflow(node->arithMode());
|
---|
1276 | if (maxValue < 0 || (absIsUnchecked && maxValue <= 0)) {
|
---|
1277 | node->convertToArithNegate();
|
---|
1278 | if (absIsUnchecked || minValue > std::numeric_limits<int>::min())
|
---|
1279 | 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 | }
|
---|
1291 | 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 | }
|
---|
1309 |
|
---|
1310 | if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
|
---|
1311 | dataLog(" minValue = ", minValue, ", maxValue = ", maxValue, "\n");
|
---|
1312 |
|
---|
1313 | if (sumOverflows<int>(minValue, node->child2()->asInt32()) ||
|
---|
1314 | sumOverflows<int>(maxValue, node->child2()->asInt32()))
|
---|
1315 | break;
|
---|
1316 |
|
---|
1317 | if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
|
---|
1318 | dataLog(" It's in bounds.\n");
|
---|
1319 |
|
---|
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 |
|
---|
1337 | if (relationship.right() == node->child2().node()) {
|
---|
1338 | 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 | }
|
---|
1347 |
|
---|
1348 | if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
|
---|
1349 | dataLogLn("CheckInBounds ", node, " has: ", nonNegative, " ", lessThanLength);
|
---|
1350 |
|
---|
1351 | if (nonNegative && lessThanLength) {
|
---|
1352 | executeNode(block->at(nodeIndex));
|
---|
1353 | if (UNLIKELY(Options::validateBoundsCheckElimination()) && node->op() == CheckInBounds)
|
---|
1354 | m_insertionSet.insertNode(nodeIndex, SpecNone, AssertInBounds, node->origin, node->child1(), node->child2());
|
---|
1355 | // We just need to make sure we are a value-producing node.
|
---|
1356 | node->convertToIdentityOn(node->child1().node());
|
---|
1357 | changed = true;
|
---|
1358 | }
|
---|
1359 | break;
|
---|
1360 | }
|
---|
1361 |
|
---|
1362 | case EnumeratorGetByVal:
|
---|
1363 | case GetByVal: {
|
---|
1364 | if (node->arrayMode().type() != Array::Undecided)
|
---|
1365 | break;
|
---|
1366 |
|
---|
1367 | auto iter = m_relationships.find(m_graph.varArgChild(node, 1).node());
|
---|
1368 | 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 |
|
---|
1384 | default:
|
---|
1385 | break;
|
---|
1386 | }
|
---|
1387 |
|
---|
1388 | executeNode(block->at(nodeIndex));
|
---|
1389 | }
|
---|
1390 | }
|
---|
1391 |
|
---|
1392 | return changed;
|
---|
1393 | }
|
---|
1394 |
|
---|
1395 | private:
|
---|
1396 | void executeNode(Node* node)
|
---|
1397 | {
|
---|
1398 | switch (node->op()) {
|
---|
1399 | // FIXME: Teach this about EnumeratorNextExtractIndex.
|
---|
1400 | 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 | }
|
---|
1405 |
|
---|
1406 | case ArithAbs: {
|
---|
1407 | if (node->child1().useKind() != Int32Use)
|
---|
1408 | break;
|
---|
1409 |
|
---|
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));
|
---|
1428 | break;
|
---|
1429 | }
|
---|
1430 |
|
---|
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 |
|
---|
1534 | case GetArrayLength:
|
---|
1535 | case GetVectorLength: {
|
---|
1536 | setRelationship(Relationship(node, m_zero, Relationship::GreaterThan, -1));
|
---|
1537 | break;
|
---|
1538 | }
|
---|
1539 |
|
---|
1540 | case Upsilon: {
|
---|
1541 | 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);
|
---|
1547 | break;
|
---|
1548 | }
|
---|
1549 |
|
---|
1550 | case Phi: {
|
---|
1551 | setEquivalence(
|
---|
1552 | NodeFlowProjection(node, NodeFlowProjection::Shadow),
|
---|
1553 | node);
|
---|
1554 | break;
|
---|
1555 | }
|
---|
1556 |
|
---|
1557 | default:
|
---|
1558 | break;
|
---|
1559 | }
|
---|
1560 | }
|
---|
1561 |
|
---|
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 | }
|
---|
1577 |
|
---|
1578 | 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 |
|
---|
1598 | 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 |
|
---|
1616 | if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
|
---|
1617 | dataLogLn(" Setting: ", relationship, " (ttl = ", timeToLive, ")");
|
---|
1618 |
|
---|
1619 | auto result = relationshipMap.add(
|
---|
1620 | relationship.left(), Vector<Relationship>());
|
---|
1621 | Vector<Relationship>& relationships = result.iterator->value;
|
---|
1622 |
|
---|
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);
|
---|
1670 | if (DFGIntegerRangeOptimizationPhaseInternal::verbose && newRelationship != relationship)
|
---|
1671 | 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);
|
---|
1684 | if (DFGIntegerRangeOptimizationPhaseInternal::verbose && newRelationship != otherRelationship)
|
---|
1685 | dataLog(" Refined ", otherRelationship, " to: ", newRelationship, "\n");
|
---|
1686 | otherRelationship = newRelationship;
|
---|
1687 | }
|
---|
1688 | }
|
---|
1689 | }
|
---|
1690 | }
|
---|
1691 |
|
---|
1692 | 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;
|
---|
1699 | if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
|
---|
1700 | dataLogLn(" filtered: ", filtered);
|
---|
1701 | found = true;
|
---|
1702 | }
|
---|
1703 | }
|
---|
1704 |
|
---|
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.
|
---|
1707 |
|
---|
1708 | if (timeToLive && otherRelationship.kind() == Relationship::Equal) {
|
---|
1709 | if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
|
---|
1710 | dataLog(" Considering (lhs): ", otherRelationship, "\n");
|
---|
1711 |
|
---|
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 | }
|
---|
1733 |
|
---|
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 | }
|
---|
1758 |
|
---|
1759 | 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 | {
|
---|
1770 | if (DFGIntegerRangeOptimizationPhaseInternal::verbose) {
|
---|
1771 | 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.
|
---|
1778 | auto isLive = [&] (NodeFlowProjection node) {
|
---|
1779 | 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())) {
|
---|
1792 | if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
|
---|
1793 | 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.
|
---|
1810 | Vector<NodeFlowProjection> toRemove;
|
---|
1811 | 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 | }
|
---|
1819 |
|
---|
1820 | Vector<Relationship> constantRelationshipsAtHead;
|
---|
1821 | for (Relationship& relationshipAtHead : entry.value) {
|
---|
1822 | if (relationshipAtHead.right()->isInt32Constant())
|
---|
1823 | constantRelationshipsAtHead.append(relationshipAtHead);
|
---|
1824 | }
|
---|
1825 |
|
---|
1826 | Vector<Relationship> mergedRelationships;
|
---|
1827 | for (Relationship targetRelationship : entry.value) {
|
---|
1828 | for (Relationship sourceRelationship : iter->value) {
|
---|
1829 | if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
|
---|
1830 | dataLog(" Merging ", targetRelationship, " and ", sourceRelationship, ":\n");
|
---|
1831 | targetRelationship.merge(
|
---|
1832 | sourceRelationship,
|
---|
1833 | [&] (Relationship newRelationship) {
|
---|
1834 | if (DFGIntegerRangeOptimizationPhaseInternal::verbose)
|
---|
1835 | dataLog(" Got ", newRelationship, "\n");
|
---|
1836 |
|
---|
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 | }
|
---|
1854 |
|
---|
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 | }
|
---|
1886 | }
|
---|
1887 |
|
---|
1888 | if (!found)
|
---|
1889 | mergedRelationships.append(newRelationship);
|
---|
1890 | });
|
---|
1891 | }
|
---|
1892 | }
|
---|
1893 | std::sort(mergedRelationships.begin(), mergedRelationships.end());
|
---|
1894 | if (entry.value == mergedRelationships)
|
---|
1895 | continue;
|
---|
1896 |
|
---|
1897 | entry.value = mergedRelationships;
|
---|
1898 | changed = true;
|
---|
1899 | }
|
---|
1900 | for (NodeFlowProjection node : toRemove)
|
---|
1901 | 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;
|
---|
1925 |
|
---|
1926 | unsigned m_iterations { 0 };
|
---|
1927 | };
|
---|
1928 |
|
---|
1929 | } // anonymous namespace
|
---|
1930 |
|
---|
1931 | bool performIntegerRangeOptimization(Graph& graph)
|
---|
1932 | {
|
---|
1933 | return runPhase<IntegerRangeOptimizationPhase>(graph);
|
---|
1934 | }
|
---|
1935 |
|
---|
1936 | } } // namespace JSC::DFG
|
---|
1937 |
|
---|
1938 | #endif // ENABLE(DFG_JIT)
|
---|
1939 |
|
---|