summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/dfg/DFGIntegerRangeOptimizationPhase.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2016-04-10 09:28:39 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2016-04-10 09:28:39 +0000
commit32761a6cee1d0dee366b885b7b9c777e67885688 (patch)
treed6bec92bebfb216f4126356e55518842c2f476a1 /Source/JavaScriptCore/dfg/DFGIntegerRangeOptimizationPhase.cpp
parenta4e969f4965059196ca948db781e52f7cfebf19e (diff)
downloadWebKitGtk-tarball-32761a6cee1d0dee366b885b7b9c777e67885688.tar.gz
webkitgtk-2.4.11webkitgtk-2.4.11
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGIntegerRangeOptimizationPhase.cpp')
-rw-r--r--Source/JavaScriptCore/dfg/DFGIntegerRangeOptimizationPhase.cpp1780
1 files changed, 0 insertions, 1780 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGIntegerRangeOptimizationPhase.cpp b/Source/JavaScriptCore/dfg/DFGIntegerRangeOptimizationPhase.cpp
deleted file mode 100644
index 02aa134d8..000000000
--- a/Source/JavaScriptCore/dfg/DFGIntegerRangeOptimizationPhase.cpp
+++ /dev/null
@@ -1,1780 +0,0 @@
-/*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "DFGIntegerRangeOptimizationPhase.h"
-
-#if ENABLE(DFG_JIT)
-
-#include "DFGBlockMapInlines.h"
-#include "DFGBlockSet.h"
-#include "DFGGraph.h"
-#include "DFGInsertionSet.h"
-#include "DFGPhase.h"
-#include "DFGPredictionPropagationPhase.h"
-#include "DFGVariableAccessDataDump.h"
-#include "JSCInlines.h"
-
-namespace JSC { namespace DFG {
-
-namespace {
-
-const bool verbose = false;
-
-int64_t clampedSumImpl() { return 0; }
-
-template<typename... Args>
-int64_t clampedSumImpl(int left, Args... args)
-{
- return static_cast<int64_t>(left) + clampedSumImpl(args...);
-}
-
-template<typename... Args>
-int clampedSum(Args... args)
-{
- int64_t result = clampedSumImpl(args...);
- return static_cast<int>(std::min(
- static_cast<int64_t>(std::numeric_limits<int>::max()),
- std::max(
- static_cast<int64_t>(std::numeric_limits<int>::min()),
- result)));
-}
-
-bool isGeneralOffset(int offset)
-{
- return offset >= -1 && offset <= 1;
-}
-
-class Relationship {
-public:
- enum Kind {
- LessThan,
- Equal,
- NotEqual,
- GreaterThan
- };
-
- // Some relationships provide more information than others. When a relationship provides more
- // information, it is less vague.
- static unsigned vagueness(Kind kind)
- {
- switch (kind) {
- case Equal:
- return 0;
- case LessThan:
- case GreaterThan:
- return 1;
- case NotEqual:
- return 2;
- }
- RELEASE_ASSERT_NOT_REACHED();
- return 0;
- }
-
- static const unsigned minVagueness = 0;
- static const unsigned maxVagueness = 2;
-
- static Kind flipped(Kind kind)
- {
- switch (kind) {
- case LessThan:
- return GreaterThan;
- case Equal:
- return Equal;
- case NotEqual:
- return NotEqual;
- case GreaterThan:
- return LessThan;
- }
- RELEASE_ASSERT_NOT_REACHED();
- return kind;
- }
-
- Relationship()
- : m_left(nullptr)
- , m_right(nullptr)
- , m_kind(Equal)
- , m_offset(0)
- {
- }
-
- Relationship(Node* left, Node* right, Kind kind, int offset = 0)
- : m_left(left)
- , m_right(right)
- , m_kind(kind)
- , m_offset(offset)
- {
- RELEASE_ASSERT(m_left);
- RELEASE_ASSERT(m_right);
- RELEASE_ASSERT(m_left != m_right);
- }
-
- static Relationship safeCreate(Node* left, Node* right, Kind kind, int offset = 0)
- {
- if (!left || !right || left == right)
- return Relationship();
- return Relationship(left, right, kind, offset);
- }
-
- explicit operator bool() const { return m_left; }
-
- Node* left() const { return m_left; }
- Node* right() const { return m_right; }
- Kind kind() const { return m_kind; }
- int offset() const { return m_offset; }
-
- unsigned vagueness() const { return vagueness(kind()); }
-
- Relationship flipped() const
- {
- if (!*this)
- return Relationship();
-
- // This should return Relationship() if -m_offset overflows. For example:
- //
- // @a > @b - 2**31
- //
- // If we flip it we get:
- //
- // @b < @a + 2**31
- //
- // Except that the sign gets flipped since it's INT_MIN:
- //
- // @b < @a - 2**31
- //
- // And that makes no sense. To see how little sense it makes, consider:
- //
- // @a > @zero - 2**31
- //
- // We would flip it to mean:
- //
- // @zero < @a - 2**31
- //
- // Which is absurd.
-
- if (m_offset == std::numeric_limits<int>::min())
- return Relationship();
-
- return Relationship(m_right, m_left, flipped(m_kind), -m_offset);
- }
-
- Relationship inverse() const
- {
- if (!*this)
- return *this;
-
- switch (m_kind) {
- case Equal:
- return Relationship(m_left, m_right, NotEqual, m_offset);
- case NotEqual:
- return Relationship(m_left, m_right, Equal, m_offset);
- case LessThan:
- if (sumOverflows<int>(m_offset, -1))
- return Relationship();
- return Relationship(m_left, m_right, GreaterThan, m_offset - 1);
- case GreaterThan:
- if (sumOverflows<int>(m_offset, 1))
- return Relationship();
- return Relationship(m_left, m_right, LessThan, m_offset + 1);
- }
-
- RELEASE_ASSERT_NOT_REACHED();
- }
-
- bool isCanonical() const { return m_left < m_right; }
-
- Relationship canonical() const
- {
- if (isCanonical())
- return *this;
- return flipped();
- }
-
- bool sameNodesAs(const Relationship& other) const
- {
- return m_left == other.m_left
- && m_right == other.m_right;
- }
-
- bool operator==(const Relationship& other) const
- {
- return sameNodesAs(other)
- && m_kind == other.m_kind
- && m_offset == other.m_offset;
- }
-
- bool operator!=(const Relationship& other) const
- {
- return !(*this == other);
- }
-
- bool operator<(const Relationship& other) const
- {
- if (m_left != other.m_left)
- return m_left < other.m_left;
- if (m_right != other.m_right)
- return m_right < other.m_right;
- if (m_kind != other.m_kind)
- return m_kind < other.m_kind;
- return m_offset < other.m_offset;
- }
-
- // If possible, returns a form of this relationship where the given node is the left
- // side. Returns a null relationship if this relationship cannot say anything about this
- // node.
- Relationship forNode(Node* node) const
- {
- if (m_left == node)
- return *this;
- if (m_right == node)
- return flipped();
- return Relationship();
- }
-
- void setLeft(Node* left)
- {
- RELEASE_ASSERT(left != m_right);
- m_left = left;
- }
- bool addToOffset(int offset)
- {
- if (sumOverflows<int>(m_offset, offset))
- return false;
- m_offset += offset;
- return true;
- }
-
- // Attempts to create relationships that summarize the union of this relationship and
- // the other relationship. Relationships are returned by calling the functor with the newly
- // created relationships. No relationships are created to indicate TOP. This is used
- // for merging the current relationship-at-head for some pair of nodes and a new
- // relationship-at-head being proposed by a predecessor. We wish to create a new
- // relationship that is true whenever either of them are true, which ensuring that we don't
- // do this forever. Anytime we create a relationship that is not equal to either of the
- // previous ones, we will cause the analysis fixpoint to reexecute.
- //
- // If *this and other are identical, we just pass it to the functor.
- //
- // If they are different, we pick from a finite set of "general" relationships:
- //
- // Eq: this == other + C, where C is -1, 0, or 1.
- // Lt: this < other + C, where C is -1, 0, or 1.
- // Gt: this > other + C, where C is -1, 0, or 1.
- // Ne: this != other + C, where C is -1, 0, or 1.
- // TOP: the null relationship.
- //
- // Constraining C to -1,0,1 is necessary to ensure that the set of general relationships is
- // finite. This finite set of relationships forms a pretty simple lattice where a
- // relA->relB means "relB is more general than relA". For example, this<other+1 is more
- // general than this==other. I'll leave it as an exercise for the reader to see that a
- // graph between the 13 general relationships is indeed a lattice. The fact that the set of
- // general relationships is a finite lattice ensures monotonicity of the fixpoint, since
- // any merge over not-identical relationships returns a relationship that is closer to the
- // TOP relationship than either of the original relationships. Here's how convergence is
- // achieved for any pair of relationships over the same nodes:
- //
- // - If they are identical, then returning *this means that we won't be responsible for
- // causing another fixpoint iteration. Once all merges reach this point, we're done.
- //
- // - If they are different, then we pick the most constraining of the 13 general
- // relationships that is true if either *this or other are true. This means that if the
- // relationships are not identical, the merged relationship will be closer to TOP than
- // either of the originals. Returning a different relationship means that we will be
- // responsible for the fixpoint to reloop, but we can only do this at most 13 times since
- // that's how "deep" the general relationship lattice is.
- //
- // Note that C being constrained to -1,0,1 also ensures that we never have to return a
- // combination of Lt and Gt, as in for example this<other+C && this>other-D. The only possible
- // values of C and D where this would work are -1 and 1, but in that case we just say
- // this==other. That said, the logic for merging two == relationships, like this==other+C ||
- // this==other+D is to attempt to create these two relationships: this>other+min(C,D)-1 &&
- // this<other+max(C,D)+1. But only one of these relationships will belong to the set of general
- // relationships.
- //
- // Here's an example of this in action:
- //
- // for (var i = a; ; ++i) { }
- //
- // Without C being constrained to -1,0,1, we could end up looping forever: first we'd say
- // 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
- // because i<a+2 is not a valid general relationship: so when we merge i==a from the first
- // iteration and i==a+1 from the second iteration, we create i>a-1 and i<a+2 but then
- // realize that only i>a-1 is a valid general relationship. This gives us exactly what we
- // want: a statement that i>=a.
- //
- // However, this may return a pair of relationships when merging relationships involving
- // constants. For example, if given:
- //
- // @x == @c
- // @x == @d
- //
- // where @c and @d are constants, then this may pass two relationships to the functor:
- //
- // @x > min(@c, @d) - 1
- // @x < max(@c, @d) + 1
- //
- // This still allows for convergence, because just as when merging relationships over
- // variables, this always picks from a set of general relationships. Hence although this may
- // produce two relationships as a result of the merge, the total number of relationships that
- // can be present at head of block is limited by O(graph.size^2).
- template<typename Functor>
- void merge(const Relationship& other, const Functor& functor) const
- {
- // Handle the super obvious case first.
- if (*this == other) {
- functor(*this);
- return;
- }
-
- if (m_left != other.m_left)
- return;
-
- if (m_right != other.m_right) {
- mergeConstantsImpl(other, functor);
- return;
- }
-
- ASSERT(sameNodesAs(other));
-
- // This does some interesting permutations to reduce the amount of duplicate code. For
- // example:
- //
- // initially: @a != @b, @a > @b
- // @b != @a, @b < @a
- // @b < @a, @b != @a
- // finally: @b != a, @b < @a
- //
- // Another example:
- //
- // initially: @a < @b, @a != @b
- // finally: @a != @b, @a < @b
-
- Relationship a = *this;
- Relationship b = other;
- bool needFlip = false;
-
- // Get rid of GreaterThan.
- if (a.m_kind == GreaterThan || b.m_kind == GreaterThan) {
- a = a.flipped();
- b = b.flipped();
-
- // In rare cases, we might not be able to flip. Just give up on life in those
- // cases.
- if (!a || !b)
- return;
-
- needFlip = true;
-
- // If we still have GreaterThan, then it means that we started with @a < @b and
- // @a > @b. That's pretty much always a tautology; we don't attempt to do smart
- // things for that case for now.
- if (a.m_kind == GreaterThan || b.m_kind == GreaterThan)
- return;
- }
-
- // Make sure that if we have a LessThan, then it's first.
- if (b.m_kind == LessThan)
- std::swap(a, b);
-
- // Make sure that if we have a NotEqual, then it's first.
- if (b.m_kind == NotEqual)
- std::swap(a, b);
-
- Relationship result = a.mergeImpl(b);
- if (!result)
- return;
-
- if (needFlip)
- result = result.flipped();
-
- functor(result);
- }
-
- // Attempts to construct one Relationship that adequately summarizes the intersection of
- // this and other. Returns a null relationship if the filtration should be expressed as two
- // different relationships. Returning null is always safe because relationship lists in
- // this phase always imply intersection. So, you could soundly skip calling this method and
- // just put both relationships into the list. But, that could lead the fixpoint to diverge.
- // Hence this will attempt to combine the two relationships into one as a convergence hack.
- // In some cases, it will do something conservative. It's always safe for this to return
- // *this, or to return other. It'll do that sometimes, mainly to accelerate convergence for
- // things that we don't think are important enough to slow down the analysis.
- Relationship filter(const Relationship& other) const
- {
- // We are only interested in merging relationships over the same nodes.
- ASSERT(sameNodesAs(other));
-
- if (*this == other)
- return *this;
-
- // From here we can assume that the two relationships are not identical. Usually we use
- // this to assume that we have different offsets anytime that everything but the offset
- // is identical.
-
- // We want equality to take precedent over everything else, and we don't want multiple
- // independent claims of equality. That would just be a contradiction. When it does
- // happen, we will be conservative in the sense that we will pick one.
- if (m_kind == Equal)
- return *this;
- if (other.m_kind == Equal)
- return other;
-
- // Useful helper for flipping.
- auto filterFlipped = [&] () -> Relationship {
- // If we cannot flip, then just conservatively return *this.
- Relationship a = flipped();
- Relationship b = other.flipped();
- if (!a || !b)
- return *this;
- Relationship result = a.filter(b);
- if (!result)
- return Relationship();
- result = result.flipped();
- if (!result)
- return *this;
- return result;
- };
-
- if (m_kind == NotEqual) {
- if (other.m_kind == NotEqual) {
- // We could do something smarter here. We could even keep both NotEqual's. We
- // would need to make sure that we correctly collapsed them when merging. But
- // for now, we just pick one of them and hope for the best.
- return *this;
- }
-
- if (other.m_kind == GreaterThan) {
- // Implement this in terms of NotEqual.filter(LessThan).
- return filterFlipped();
- }
-
- ASSERT(other.m_kind == LessThan);
- // We have two claims:
- // @a != @b + C
- // @a < @b + D
- //
- // If C >= D, then the NotEqual is redundant.
- // If C < D - 1, then we could keep both, but for now we just keep the LessThan.
- // If C == D - 1, then the LessThan can be turned into:
- //
- // @a < @b + C
- //
- // Note that C == this.m_offset, D == other.m_offset.
-
- if (m_offset == other.m_offset - 1)
- return Relationship(m_left, m_right, LessThan, m_offset);
-
- return other;
- }
-
- if (other.m_kind == NotEqual)
- return other.filter(*this);
-
- if (m_kind == LessThan) {
- if (other.m_kind == LessThan) {
- return Relationship(
- m_left, m_right, LessThan, std::min(m_offset, other.m_offset));
- }
-
- ASSERT(other.m_kind == GreaterThan);
- if (sumOverflows<int>(m_offset, -1))
- return Relationship();
- if (sumOverflows<int>(other.m_offset, 1))
- return Relationship();
- if (m_offset - 1 == other.m_offset + 1)
- return Relationship(m_left, m_right, Equal, m_offset - 1);
-
- return Relationship();
- }
-
- ASSERT(m_kind == GreaterThan);
- return filterFlipped();
- }
-
- // Come up with a relationship that is the best description of this && other, provided that left() is
- // the same and right() is a constant. Also requires that this is at least as vague as other. It may
- // return this or it may return something else, but whatever it returns, it will have the same nodes as
- // this. This is not automatically done by filter() because it currently only makes sense to call this
- // during a very particular part of setOneSide().
- Relationship filterConstant(const Relationship& other) const
- {
- ASSERT(m_left == other.m_left);
- ASSERT(m_right->isInt32Constant());
- ASSERT(other.m_right->isInt32Constant());
- ASSERT(vagueness() >= other.vagueness());
-
- if (vagueness() == other.vagueness())
- return *this;
-
- int thisRight = m_right->asInt32();
- int otherRight = other.m_right->asInt32();
-
- // Ignore funny business.
- if (sumOverflows<int>(otherRight, other.m_offset))
- return *this;
-
- int otherEffectiveRight = otherRight + other.m_offset;
-
- switch (other.m_kind) {
- case Equal:
- // Return a version of *this that is Equal to other's constant.
- return Relationship(m_left, m_right, Equal, otherEffectiveRight - thisRight);
-
- case LessThan:
- case GreaterThan:
- ASSERT(m_kind == NotEqual);
- // We could do smart things here. But we don't currently have an example of when it would be
- // valuable. Note that you have to be careful. We could refine NotEqual to LessThan, but only
- // if the LessThan subsumes the NotEqual.
- return *this;
-
- case NotEqual:
- RELEASE_ASSERT_NOT_REACHED();
- return Relationship();
- }
-
- RELEASE_ASSERT_NOT_REACHED();
- return Relationship();
- }
-
- int minValueOfLeft() const
- {
- if (m_left->isInt32Constant())
- return m_left->asInt32();
-
- if (m_kind == LessThan || m_kind == NotEqual)
- return std::numeric_limits<int>::min();
-
- int minRightValue = std::numeric_limits<int>::min();
- if (m_right->isInt32Constant())
- minRightValue = m_right->asInt32();
-
- if (m_kind == GreaterThan)
- return clampedSum(minRightValue, m_offset, 1);
- ASSERT(m_kind == Equal);
- return clampedSum(minRightValue, m_offset);
- }
-
- int maxValueOfLeft() const
- {
- if (m_left->isInt32Constant())
- return m_left->asInt32();
-
- if (m_kind == GreaterThan || m_kind == NotEqual)
- return std::numeric_limits<int>::max();
-
- int maxRightValue = std::numeric_limits<int>::max();
- if (m_right->isInt32Constant())
- maxRightValue = m_right->asInt32();
-
- if (m_kind == LessThan)
- return clampedSum(maxRightValue, m_offset, -1);
- ASSERT(m_kind == Equal);
- return clampedSum(maxRightValue, m_offset);
- }
-
- void dump(PrintStream& out) const
- {
- // This prints out the relationship without any whitespace, like @x<@y+42. This
- // optimizes for the clarity of a list of relationships. It's easier to read something
- // like [@1<@2+3, @4==@5-6] than it would be if there was whitespace inside the
- // relationships.
-
- if (!*this) {
- out.print("null");
- return;
- }
-
- out.print(m_left);
- switch (m_kind) {
- case LessThan:
- out.print("<");
- break;
- case Equal:
- out.print("==");
- break;
- case NotEqual:
- out.print("!=");
- break;
- case GreaterThan:
- out.print(">");
- break;
- }
- out.print(m_right);
- if (m_offset > 0)
- out.print("+", m_offset);
- else if (m_offset < 0)
- out.print("-", -static_cast<int64_t>(m_offset));
- }
-
-private:
- Relationship mergeImpl(const Relationship& other) const
- {
- ASSERT(sameNodesAs(other));
- ASSERT(m_kind != GreaterThan);
- ASSERT(other.m_kind != GreaterThan);
- ASSERT(*this != other);
-
- // The purpose of this method is to guarantee that:
- //
- // - We avoid having more than one Relationship over the same two nodes. Therefore, if
- // the merge could be expressed as two Relationships, we prefer to instead pick the
- // less precise single Relationship form even if that means TOP.
- //
- // - If the difference between two Relationships is just the m_offset, then we create a
- // Relationship that has an offset of -1, 0, or 1. This is an essential convergence
- // hack. We need -1 and 1 to support <= and >=.
-
- // From here we can assume that the two relationships are not identical. Usually we use
- // this to assume that we have different offsets anytime that everything but the offset
- // is identical.
-
- if (m_kind == NotEqual) {
- if (other.m_kind == NotEqual)
- return Relationship(); // Different offsets, so tautology.
-
- if (other.m_kind == Equal) {
- if (m_offset != other.m_offset) {
- // Saying that you might be B when you've already said that you're anything
- // but A, where A and B are different, is a tautology. You could just say
- // that you're anything but A. Adding "(a == b + 1)" to "(a != b + 5)" has
- // no value.
- return *this;
- }
- // Otherwise, same offsets: we're saying that you're either A or you're not
- // equal to A.
-
- return Relationship();
- }
-
- RELEASE_ASSERT(other.m_kind == LessThan);
- // We have these claims, and we're merging them:
- // @a != @b + C
- // @a < @b + D
- //
- // If we have C == D, then the merge is clearly just the NotEqual.
- // If we have C < D, then the merge is a tautology.
- // If we have C > D, then we could keep both claims, but we are cheap, so we
- // don't. We just use the NotEqual.
-
- if (m_offset < other.m_offset)
- return Relationship();
-
- return *this;
- }
-
- if (m_kind == LessThan) {
- if (other.m_kind == LessThan) {
- // Figure out what offset to select to merge them. The appropriate offsets are
- // -1, 0, or 1.
-
- // First figure out what offset we'd like to use.
- int bestOffset = std::max(m_offset, other.m_offset);
-
- // We have something like @a < @b + 2. We can't represent this under the
- // -1,0,1 rule.
- if (isGeneralOffset(bestOffset))
- return Relationship(m_left, m_right, LessThan, std::max(bestOffset, -1));
-
- return Relationship();
- }
-
- // The only thing left is Equal. We would have eliminated the GreaterThan's, and
- // if we merge LessThan and NotEqual, the NotEqual always comes first.
- RELEASE_ASSERT(other.m_kind == Equal);
-
- // This is the really interesting case. We have:
- //
- // @a < @b + C
- //
- // and:
- //
- // @a == @b + D
- //
- // Therefore we'd like to return:
- //
- // @a < @b + max(C, D + 1)
-
- int bestOffset = std::max(m_offset, other.m_offset + 1);
-
- // We have something like @a < @b + 2. We can't do it.
- if (isGeneralOffset(bestOffset))
- return Relationship(m_left, m_right, LessThan, std::max(bestOffset, -1));
-
- return Relationship();
- }
-
- // The only thing left is Equal, since we would have gotten rid of the GreaterThan's.
- RELEASE_ASSERT(m_kind == Equal);
-
- // We would never see NotEqual, because those always come first. We would never
- // see GreaterThan, because we would have eliminated those. We would never see
- // LessThan, because those always come first.
-
- RELEASE_ASSERT(other.m_kind == Equal);
- // We have @a == @b + C and @a == @b + D, where C != D. Turn this into some
- // inequality that involves a constant that is -1,0,1. Note that we will never have
- // lessThan and greaterThan because the constants are constrained to -1,0,1. The only
- // way for both of them to be valid is a<b+1 and a>b-1, but then we would have said
- // a==b.
-
- Relationship lessThan;
- Relationship greaterThan;
-
- int lessThanEqOffset = std::max(m_offset, other.m_offset);
- if (lessThanEqOffset >= -2 && lessThanEqOffset <= 0) {
- lessThan = Relationship(
- m_left, other.m_right, LessThan, lessThanEqOffset + 1);
-
- ASSERT(isGeneralOffset(lessThan.offset()));
- }
-
- int greaterThanEqOffset = std::min(m_offset, other.m_offset);
- if (greaterThanEqOffset >= 0 && greaterThanEqOffset <= 2) {
- greaterThan = Relationship(
- m_left, other.m_right, GreaterThan, greaterThanEqOffset - 1);
-
- ASSERT(isGeneralOffset(greaterThan.offset()));
- }
-
- if (lessThan) {
- // Both relationships cannot be valid; see above.
- RELEASE_ASSERT(!greaterThan);
-
- return lessThan;
- }
-
- return greaterThan;
- }
-
- template<typename Functor>
- void mergeConstantsImpl(const Relationship& other, const Functor& functor) const
- {
- ASSERT(m_left == other.m_left);
-
- // Only deal with constant right.
- if (!m_right->isInt32Constant() || !other.m_right->isInt32Constant())
- return;
-
- // What follows is a fairly conservative merge. We could tune this phase to come up with
- // all possible inequalities between variables and constants, but we focus mainly on cheap
- // cases for now.
-
- // Here are some of the the arrangements we can merge usefully assuming @c < @d:
- //
- // @x == @c || @x == @d => @x >= c && @x <= @d
- // @x >= @c || @x <= @d => TOP
- // @x == @c || @x != @d => @x != @d
-
- int thisRight = m_right->asInt32();
- int otherRight = other.m_right->asInt32();
-
- // Ignore funny business.
- if (sumOverflows<int>(thisRight, m_offset))
- return;
- if (sumOverflows<int>(otherRight, other.m_offset))
- return;
-
- int thisEffectiveRight = thisRight + m_offset;
- int otherEffectiveRight = otherRight + other.m_offset;
-
- auto makeUpper = [&] (int64_t upper) {
- if (upper <= thisRight) {
- // We want m_right + offset to be equal to upper. Hence we want offset to cancel
- // with m_right. But there's more to it, since we want +1 to turn the LessThan into
- // a LessThanOrEqual, and we want to make sure we don't end up with non-general
- // offsets.
- int offset = static_cast<int>(std::max(
- static_cast<int64_t>(1) + upper - static_cast<int64_t>(thisRight),
- static_cast<int64_t>(-1)));
- functor(Relationship(m_left, m_right, LessThan, offset));
- }
- if (upper <= otherRight) {
- int offset = static_cast<int>(std::max(
- static_cast<int64_t>(1) + upper - static_cast<int64_t>(otherRight),
- static_cast<int64_t>(-1)));
- functor(Relationship(m_left, other.m_right, LessThan, offset));
- }
- };
- auto makeLower = [&] (int64_t lower) {
- if (lower >= thisRight) {
- // We want m_right + offset to be equal to lower. Hence we want offset to cancel with
- // m_right. But there's more to it, since we want -1 to turn the GreaterThan into a
- // GreaterThanOrEqual, and we want to make sure we don't end up with non-general
- // offsets.
- int offset = static_cast<int>(std::min(
- static_cast<int64_t>(-1) + lower - static_cast<int64_t>(thisRight),
- static_cast<int64_t>(1)));
- functor(Relationship(m_left, m_right, GreaterThan, offset));
- }
- if (lower >= otherRight) {
- int offset = static_cast<int>(std::min(
- static_cast<int64_t>(-1) + lower - static_cast<int64_t>(otherRight),
- static_cast<int64_t>(1)));
- functor(Relationship(m_left, other.m_right, GreaterThan, offset));
- }
- };
-
- switch (m_kind) {
- case Equal: {
- switch (other.m_kind) {
- case Equal: {
- if (thisEffectiveRight == otherEffectiveRight) {
- // This probably won't arise often. We can keep whichever relationship is general.
- if (isGeneralOffset(m_offset))
- functor(*this);
- if (isGeneralOffset(other.m_offset))
- functor(other);
- return;
- }
-
- // What follows is the only case where a merge will create more rules than what it
- // started with. This is fine for convergence because the LessThan/GreaterThan
- // rules that this creates are general (i.e. have small offsets) and they never
- // spawn more rules upon subsequent merging.
-
- makeUpper(std::max(thisEffectiveRight, otherEffectiveRight));
- makeLower(std::min(thisEffectiveRight, otherEffectiveRight));
- return;
- }
-
- case LessThan: {
- // Either the LessThan condition subsumes the equality, or the LessThan condition
- // and equality merge together to create a looser LessThan condition.
-
- // This is @x == thisEffectiveRight
- // Other is: @x < otherEffectiveRight
-
- // We want to create @x <= upper. Figure out the value of upper.
- makeUpper(std::max(
- static_cast<int64_t>(thisEffectiveRight),
- static_cast<int64_t>(otherEffectiveRight) - 1));
- return;
- }
-
- case GreaterThan: {
- // Opposite of the LessThan case, above.
-
- // This is: @x == thisEffectiveRight
- // Other is: @x > otherEffectiveRight
-
- makeLower(std::min(
- static_cast<int64_t>(thisEffectiveRight),
- static_cast<int64_t>(otherEffectiveRight) + 1));
- return;
- }
-
- case NotEqual: {
- // We keep the NotEqual so long as it doesn't contradict our Equal.
- if (otherEffectiveRight == thisEffectiveRight)
- return;
-
- // But, we only keep the NotEqual if it is general. This simplifies reasoning about
- // convergence: merging never introduces a new rule unless that rule is general.
- if (!isGeneralOffset(other.m_offset))
- return;
-
- functor(other);
- return;
- } }
-
- RELEASE_ASSERT_NOT_REACHED();
- return;
- }
-
- case LessThan: {
- switch (other.m_kind) {
- case Equal: {
- other.mergeConstantsImpl(*this, functor);
- return;
- }
-
- case LessThan: {
- makeUpper(std::max(
- static_cast<int64_t>(thisEffectiveRight) - 1,
- static_cast<int64_t>(otherEffectiveRight) - 1));
- return;
- }
-
- case GreaterThan: {
- // We have a claim that @x > @c || @x < @d. If @d > @c, this is the tautology. If
- // @d <= @c, it's sort of uninteresting. Just ignore this.
- return;
- }
-
- case NotEqual: {
- // We have a claim that @x < @c || @x != @d. This isn't interesting.
- return;
- } }
-
- RELEASE_ASSERT_NOT_REACHED();
- return;
- }
-
- case GreaterThan: {
- switch (other.m_kind) {
- case Equal: {
- other.mergeConstantsImpl(*this, functor);
- return;
- }
-
- case LessThan: {
- // Not interesting, see above.
- return;
- }
-
- case GreaterThan: {
- makeLower(std::min(
- static_cast<int64_t>(thisEffectiveRight) + 1,
- static_cast<int64_t>(otherEffectiveRight) + 1));
- return;
- }
-
- case NotEqual: {
- // Not interesting, see above.
- return;
- } }
-
- RELEASE_ASSERT_NOT_REACHED();
- return;
- }
-
- case NotEqual: {
- if (other.m_kind == Equal)
- other.mergeConstantsImpl(*this, functor);
- return;
- } }
-
- RELEASE_ASSERT_NOT_REACHED();
- }
-
- Node* m_left;
- Node* m_right;
- Kind m_kind;
- int m_offset; // This offset can be arbitrarily large.
-};
-
-typedef HashMap<Node*, Vector<Relationship>> RelationshipMap;
-
-class IntegerRangeOptimizationPhase : public Phase {
-public:
- IntegerRangeOptimizationPhase(Graph& graph)
- : Phase(graph, "integer range optimization")
- , m_zero(nullptr)
- , m_relationshipsAtHead(graph)
- , m_insertionSet(graph)
- {
- }
-
- bool run()
- {
- ASSERT(m_graph.m_form == SSA);
-
- // Before we do anything, make sure that we have a zero constant at the top.
- for (Node* node : *m_graph.block(0)) {
- if (node->isInt32Constant() && !node->asInt32()) {
- m_zero = node;
- break;
- }
- }
- if (!m_zero) {
- m_zero = m_insertionSet.insertConstant(0, m_graph.block(0)->at(0)->origin, jsNumber(0));
- m_insertionSet.execute(m_graph.block(0));
- }
-
- if (verbose) {
- dataLog("Graph before integer range optimization:\n");
- m_graph.dump();
- }
-
- // This performs a fixpoint over the blocks in reverse post-order. Logically, we
- // maintain a list of relationships at each point in the program. The list should be
- // read as an intersection. For example if we have {rel1, rel2, ..., relN}, you should
- // read this as:
- //
- // TOP && rel1 && rel2 && ... && relN
- //
- // This allows us to express things like:
- //
- // @a > @b - 42 && @a < @b + 25
- //
- // But not things like:
- //
- // @a < @b - 42 || @a > @b + 25
- //
- // We merge two lists by merging each relationship in one list with each relationship
- // in the other list. Merging two relationships will yield a relationship list; as with
- // all such lists it is an intersction. Merging relationships over different variables
- // always yields the empty list (i.e. TOP). This merge style is sound because if we
- // have:
- //
- // (A && B && C) || (D && E && F)
- //
- // Then a valid merge is just one that will return true if A, B, C are all true, or
- // that will return true if D, E, F are all true. Our merge style essentially does:
- //
- // (A || D) && (A || E) && (A || F) && (B || D) && (B || E) && (B || F) &&
- // (C || D) && (C || E) && (C || F)
- //
- // If A && B && C is true, then this returns true. If D && E && F is true, this also
- // returns true.
- //
- // While this appears at first like a kind of expression explosion, in practice it
- // isn't. The code that handles this knows that the merge of two relationships over
- // different variables is TOP (i.e. the empty list). For example if A above is @a < @b
- // and B above is @c > @d, where @a, @b, @c, and @d are different nodes, the merge will
- // yield nothing. In fact, the merge algorithm will skip such merges entirely because
- // the relationship lists are actually keyed by node.
- //
- // Note that it's always safe to drop any of relationship from the relationship list.
- // This merely increases the likelihood of the "expression" yielding true, i.e. being
- // closer to TOP. Optimizations are only performed if we can establish that the
- // expression implied by the relationship list is false for all of those cases where
- // some check would have failed.
- //
- // There is no notion of BOTTOM because we treat blocks that haven't had their
- // state-at-head set as a special case: we just transfer all live relationships to such
- // a block. After the head of a block is set, we perform the merging as above. In all
- // other places where we would ordinarily need BOTTOM, we approximate it by having some
- // non-BOTTOM relationship.
-
- BlockList postOrder = m_graph.blocksInPostOrder();
-
- // This loop analyzes the IR to give us m_relationshipsAtHead for each block. This
- // may reexecute blocks many times, but it is guaranteed to converge. The state of
- // the relationshipsAtHead over any pair of nodes converge monotonically towards the
- // TOP relationship (i.e. no relationships in the relationship list). The merge rule
- // when between the current relationshipsAtHead and the relationships being propagated
- // from a predecessor ensures monotonicity by converting disagreements into one of a
- // small set of "general" relationships. There are 12 such relationshis, plus TOP. See
- // the comment above Relationship::merge() for details.
- bool changed = true;
- while (changed) {
- changed = false;
- for (unsigned postOrderIndex = postOrder.size(); postOrderIndex--;) {
- BasicBlock* block = postOrder[postOrderIndex];
- DFG_ASSERT(
- m_graph, nullptr,
- block == m_graph.block(0) || m_seenBlocks.contains(block));
-
- m_relationships = m_relationshipsAtHead[block];
-
- for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
- Node* node = block->at(nodeIndex);
- if (verbose)
- dataLog("Analysis: at ", node, ": ", listDump(sortedRelationships()), "\n");
- executeNode(node);
- }
-
- // Now comes perhaps the most important piece of cleverness: if we Branch, and
- // the predicate involves some relation over integers, we propagate different
- // information to the taken and notTaken paths. This handles:
- // - Branch on int32.
- // - Branch on LogicalNot on int32.
- // - Branch on compare on int32's.
- // - Branch on LogicalNot of compare on int32's.
- Node* terminal = block->terminal();
- bool alreadyMerged = false;
- if (terminal->op() == Branch) {
- Relationship relationshipForTrue;
- BranchData* branchData = terminal->branchData();
-
- bool invert = false;
- if (terminal->child1()->op() == LogicalNot) {
- terminal = terminal->child1().node();
- invert = true;
- }
-
- if (terminal->child1().useKind() == Int32Use) {
- relationshipForTrue = Relationship::safeCreate(
- terminal->child1().node(), m_zero, Relationship::NotEqual, 0);
- } else {
- Node* compare = terminal->child1().node();
- switch (compare->op()) {
- case CompareEq:
- case CompareStrictEq:
- case CompareLess:
- case CompareLessEq:
- case CompareGreater:
- case CompareGreaterEq: {
- if (!compare->isBinaryUseKind(Int32Use))
- break;
-
- switch (compare->op()) {
- case CompareEq:
- case CompareStrictEq:
- relationshipForTrue = Relationship::safeCreate(
- compare->child1().node(), compare->child2().node(),
- Relationship::Equal, 0);
- break;
- case CompareLess:
- relationshipForTrue = Relationship::safeCreate(
- compare->child1().node(), compare->child2().node(),
- Relationship::LessThan, 0);
- break;
- case CompareLessEq:
- relationshipForTrue = Relationship::safeCreate(
- compare->child1().node(), compare->child2().node(),
- Relationship::LessThan, 1);
- break;
- case CompareGreater:
- relationshipForTrue = Relationship::safeCreate(
- compare->child1().node(), compare->child2().node(),
- Relationship::GreaterThan, 0);
- break;
- case CompareGreaterEq:
- relationshipForTrue = Relationship::safeCreate(
- compare->child1().node(), compare->child2().node(),
- Relationship::GreaterThan, -1);
- break;
- default:
- DFG_CRASH(m_graph, compare, "Invalid comparison node type");
- break;
- }
- break;
- }
-
- default:
- break;
- }
- }
-
- if (invert)
- relationshipForTrue = relationshipForTrue.inverse();
-
- if (relationshipForTrue) {
- RelationshipMap forTrue = m_relationships;
- RelationshipMap forFalse = m_relationships;
-
- if (verbose)
- dataLog("Dealing with true:\n");
- setRelationship(forTrue, relationshipForTrue);
- if (Relationship relationshipForFalse = relationshipForTrue.inverse()) {
- if (verbose)
- dataLog("Dealing with false:\n");
- setRelationship(forFalse, relationshipForFalse);
- }
-
- changed |= mergeTo(forTrue, branchData->taken.block);
- changed |= mergeTo(forFalse, branchData->notTaken.block);
- alreadyMerged = true;
- }
- }
-
- if (!alreadyMerged) {
- for (BasicBlock* successor : block->successors())
- changed |= mergeTo(m_relationships, successor);
- }
- }
- }
-
- // Now we transform the code based on the results computed in the previous loop.
- changed = false;
- for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
- m_relationships = m_relationshipsAtHead[block];
- for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
- Node* node = block->at(nodeIndex);
- if (verbose)
- dataLog("Transformation: at ", node, ": ", listDump(sortedRelationships()), "\n");
-
- // This ends up being pretty awkward to write because we need to decide if we
- // optimize by using the relationships before the operation, but we need to
- // call executeNode() before we optimize.
- switch (node->op()) {
- case ArithAbs: {
- if (node->child1().useKind() != Int32Use)
- break;
-
- auto iter = m_relationships.find(node->child1().node());
- if (iter == m_relationships.end())
- break;
-
- int minValue = std::numeric_limits<int>::min();
- int maxValue = std::numeric_limits<int>::max();
- for (Relationship relationship : iter->value) {
- minValue = std::max(minValue, relationship.minValueOfLeft());
- maxValue = std::min(maxValue, relationship.maxValueOfLeft());
- }
-
- executeNode(block->at(nodeIndex));
-
- if (minValue >= 0) {
- node->convertToIdentityOn(node->child1().node());
- changed = true;
- break;
- }
- if (maxValue <= 0) {
- node->convertToArithNegate();
- if (minValue > std::numeric_limits<int>::min())
- node->setArithMode(Arith::Unchecked);
- changed = true;
- break;
- }
- if (minValue > std::numeric_limits<int>::min()) {
- node->setArithMode(Arith::Unchecked);
- changed = true;
- break;
- }
-
- break;
- }
- case ArithAdd: {
- if (!node->isBinaryUseKind(Int32Use))
- break;
- if (node->arithMode() != Arith::CheckOverflow)
- break;
- if (!node->child2()->isInt32Constant())
- break;
-
- auto iter = m_relationships.find(node->child1().node());
- if (iter == m_relationships.end())
- break;
-
- int minValue = std::numeric_limits<int>::min();
- int maxValue = std::numeric_limits<int>::max();
- for (Relationship relationship : iter->value) {
- minValue = std::max(minValue, relationship.minValueOfLeft());
- maxValue = std::min(maxValue, relationship.maxValueOfLeft());
- }
-
- if (verbose)
- dataLog(" minValue = ", minValue, ", maxValue = ", maxValue, "\n");
-
- if (sumOverflows<int>(minValue, node->child2()->asInt32()) ||
- sumOverflows<int>(maxValue, node->child2()->asInt32()))
- break;
-
- if (verbose)
- dataLog(" It's in bounds.\n");
-
- executeNode(block->at(nodeIndex));
- node->setArithMode(Arith::Unchecked);
- changed = true;
- break;
- }
-
- case CheckInBounds: {
- auto iter = m_relationships.find(node->child1().node());
- if (iter == m_relationships.end())
- break;
-
- bool nonNegative = false;
- bool lessThanLength = false;
- for (Relationship relationship : iter->value) {
- if (relationship.minValueOfLeft() >= 0)
- nonNegative = true;
-
- if (relationship.right() == node->child2()) {
- if (relationship.kind() == Relationship::Equal
- && relationship.offset() < 0)
- lessThanLength = true;
-
- if (relationship.kind() == Relationship::LessThan
- && relationship.offset() <= 0)
- lessThanLength = true;
- }
- }
-
- if (nonNegative && lessThanLength) {
- executeNode(block->at(nodeIndex));
- node->remove();
- changed = true;
- }
- break;
- }
-
- case GetByVal: {
- if (node->arrayMode().type() != Array::Undecided)
- break;
-
- auto iter = m_relationships.find(node->child2().node());
- if (iter == m_relationships.end())
- break;
-
- int minValue = std::numeric_limits<int>::min();
- for (Relationship relationship : iter->value)
- minValue = std::max(minValue, relationship.minValueOfLeft());
-
- if (minValue < 0)
- break;
-
- executeNode(block->at(nodeIndex));
- m_graph.convertToConstant(node, jsUndefined());
- changed = true;
- break;
- }
-
- default:
- break;
- }
-
- executeNode(block->at(nodeIndex));
- }
- }
-
- return changed;
- }
-
-private:
- void executeNode(Node* node)
- {
- switch (node->op()) {
- case CheckInBounds: {
- setRelationship(Relationship::safeCreate(node->child1().node(), node->child2().node(), Relationship::LessThan));
- setRelationship(Relationship::safeCreate(node->child1().node(), m_zero, Relationship::GreaterThan, -1));
- break;
- }
-
- case ArithAbs: {
- if (node->child1().useKind() != Int32Use)
- break;
- setRelationship(Relationship(node, m_zero, Relationship::GreaterThan, -1));
- break;
- }
-
- case ArithAdd: {
- // We're only interested in int32 additions and we currently only know how to
- // handle the non-wrapping ones.
- if (!node->isBinaryUseKind(Int32Use))
- break;
-
- // FIXME: We could handle the unchecked arithmetic case. We just do it don't right
- // now.
- if (node->arithMode() != Arith::CheckOverflow)
- break;
-
- // Handle add: @value + constant.
- if (!node->child2()->isInt32Constant())
- break;
-
- int offset = node->child2()->asInt32();
-
- // We add a relationship for @add == @value + constant, and then we copy the
- // relationships for @value. This gives us a one-deep view of @value's existing
- // relationships, which matches the one-deep search in setRelationship().
-
- setRelationship(
- Relationship(node, node->child1().node(), Relationship::Equal, offset));
-
- auto iter = m_relationships.find(node->child1().node());
- if (iter != m_relationships.end()) {
- Vector<Relationship> toAdd;
- for (Relationship relationship : iter->value) {
- // We have:
- // add: ArithAdd(@x, C)
- // @x op @y + D
- //
- // The following certainly holds:
- // @x == @add - C
- //
- // Which allows us to substitute:
- // @add - C op @y + D
- //
- // And then carry the C over:
- // @add op @y + D + C
-
- Relationship newRelationship = relationship;
- ASSERT(newRelationship.left() == node->child1().node());
-
- if (newRelationship.right() == node)
- continue;
- newRelationship.setLeft(node);
- if (newRelationship.addToOffset(offset))
- toAdd.append(newRelationship);
- }
- for (Relationship relationship : toAdd)
- setRelationship(relationship, 0);
- }
-
- // Now we want to establish that both the input and the output of the addition are
- // within a particular range of integers.
-
- if (offset > 0) {
- // If we have "add: @value + 1" then we know that @value <= max - 1, i.e. that
- // @value < max.
- if (!sumOverflows<int>(std::numeric_limits<int>::max(), -offset, 1)) {
- setRelationship(
- Relationship::safeCreate(
- node->child1().node(), m_zero, Relationship::LessThan,
- std::numeric_limits<int>::max() - offset + 1),
- 0);
- }
-
- // If we have "add: @value + 1" then we know that @add >= min + 1, i.e. that
- // @add > min.
- if (!sumOverflows<int>(std::numeric_limits<int>::min(), offset, -1)) {
- setRelationship(
- Relationship(
- node, m_zero, Relationship::GreaterThan,
- std::numeric_limits<int>::min() + offset - 1),
- 0);
- }
- }
-
- if (offset < 0 && offset != std::numeric_limits<int>::min()) {
- // If we have "add: @value - 1" then we know that @value >= min + 1, i.e. that
- // @value > min.
- if (!sumOverflows<int>(std::numeric_limits<int>::min(), offset, -1)) {
- setRelationship(
- Relationship::safeCreate(
- node->child1().node(), m_zero, Relationship::GreaterThan,
- std::numeric_limits<int>::min() + offset - 1),
- 0);
- }
-
- // If we have "add: @value + 1" then we know that @add <= max - 1, i.e. that
- // @add < max.
- if (!sumOverflows<int>(std::numeric_limits<int>::max(), -offset, 1)) {
- setRelationship(
- Relationship(
- node, m_zero, Relationship::LessThan,
- std::numeric_limits<int>::max() - offset + 1),
- 0);
- }
- }
- break;
- }
-
- case GetArrayLength: {
- setRelationship(Relationship(node, m_zero, Relationship::GreaterThan, -1));
- break;
- }
-
- case Upsilon: {
- setRelationship(
- Relationship::safeCreate(
- node->child1().node(), node->phi(), Relationship::Equal, 0));
-
- auto iter = m_relationships.find(node->child1().node());
- if (iter != m_relationships.end()) {
- Vector<Relationship> toAdd;
- for (Relationship relationship : iter->value) {
- Relationship newRelationship = relationship;
- if (node->phi() == newRelationship.right())
- continue;
- newRelationship.setLeft(node->phi());
- toAdd.append(newRelationship);
- }
- for (Relationship relationship : toAdd)
- setRelationship(relationship);
- }
- break;
- }
-
- default:
- break;
- }
- }
-
- void setRelationship(Relationship relationship, unsigned timeToLive = 1)
- {
- setRelationship(m_relationships, relationship, timeToLive);
- }
-
- void setRelationship(
- RelationshipMap& relationshipMap, Relationship relationship, unsigned timeToLive = 1)
- {
- setOneSide(relationshipMap, relationship, timeToLive);
- setOneSide(relationshipMap, relationship.flipped(), timeToLive);
- }
-
- void setOneSide(
- RelationshipMap& relationshipMap, Relationship relationship, unsigned timeToLive = 1)
- {
- if (!relationship)
- return;
-
- if (verbose)
- dataLog(" Setting: ", relationship, " (ttl = ", timeToLive, ")\n");
-
- auto result = relationshipMap.add(
- relationship.left(), Vector<Relationship>());
- Vector<Relationship>& relationships = result.iterator->value;
-
- if (relationship.right()->isInt32Constant()) {
- // We want to do some work to refine relationships over constants. This is necessary because
- // when we introduce a constant into the IR, we don't automatically create relationships
- // between that constant and the other constants. That means that when we do introduce
- // relationships between a non-constant and a constant, we need to check the other
- // relationships between that non-constant and other constants to see if we can make some
- // refinements. Possible constant statement filtrations:
- //
- // - @x == @c and @x != @d, where @c > @d:
- // @x == @c and @x > @d
- //
- // but actually we are more aggressive:
- //
- // - @x == @c and @x op @d where @c == @d + k
- // @x == @c and @x == @d + k
- //
- // And this is also possible:
- //
- // - @x > @c and @x != @d where @c == @d + k and k >= 0
- //
- // @x > @c and @x > @d + k
- //
- // So, here's what we should do depending on the kind of relationship we're introducing:
- //
- // Equal constant: Find all LessThan, NotEqual, and GreaterThan constant operations and refine
- // them to be Equal constant. Don't worry about contradictions.
- //
- // LessThan, GreaterThan constant: See if there is any Equal constant, and if so, refine to
- // that. Otherwise, find all NotEqual constant operations and refine them to be LessThan or
- // GreaterThan constant if possible.
- //
- // NotEqual constant: See if there is any Equal constant, and if so, refine to that. Otherwise,
- // see if there is any LessThan or GreaterThan constant operation, and if so, attempt to
- // refine to that.
- //
- // Seems that the key thing is to have a filterConstant() operation that returns a refined
- // version of *this based on other. The code here accomplishes this by using the vagueness
- // index (Relationship::vagueness()) to first find less vague relationships and refine this one
- // using them, and then find more vague relationships and refine those to this.
-
- if (relationship.vagueness() != Relationship::minVagueness) {
- // We're not minimally vague (maximally specific), so try to refine ourselves based on what
- // we already know.
- for (Relationship& otherRelationship : relationships) {
- if (otherRelationship.vagueness() < relationship.vagueness()
- && otherRelationship.right()->isInt32Constant()) {
- Relationship newRelationship = relationship.filterConstant(otherRelationship);
- if (verbose && newRelationship != relationship)
- dataLog(" Refined to: ", newRelationship, " based on ", otherRelationship, "\n");
- relationship = newRelationship;
- }
- }
- }
-
- if (relationship.vagueness() != Relationship::maxVagueness) {
- // We're not maximally value (minimally specific), so try to refine other relationships
- // based on this one.
- for (Relationship& otherRelationship : relationships) {
- if (otherRelationship.vagueness() > relationship.vagueness()
- && otherRelationship.right()->isInt32Constant()) {
- Relationship newRelationship = otherRelationship.filterConstant(relationship);
- if (verbose && newRelationship != otherRelationship)
- dataLog(" Refined ", otherRelationship, " to: ", newRelationship, "\n");
- otherRelationship = newRelationship;
- }
- }
- }
- }
-
- Vector<Relationship> toAdd;
- bool found = false;
- for (Relationship& otherRelationship : relationships) {
- if (otherRelationship.sameNodesAs(relationship)) {
- if (Relationship filtered = otherRelationship.filter(relationship)) {
- ASSERT(filtered.left() == relationship.left());
- otherRelationship = filtered;
- found = true;
- }
- }
-
- // FIXME: Also add filtration over statements about constants. For example, if we have
- // @x == @c and @x != @d, where @d > @c, then we want to turn @x != @d into @x < @d.
-
- if (timeToLive && otherRelationship.kind() == Relationship::Equal) {
- if (verbose)
- dataLog(" Considering: ", otherRelationship, "\n");
-
- // We have:
- // @a op @b + C
- // @a == @c + D
- //
- // This implies:
- // @c + D op @b + C
- // @c op @b + C - D
- //
- // Where: @a == relationship.left(), @b == relationship.right(),
- // @a == otherRelationship.left(), @c == otherRelationship.right().
-
- if (otherRelationship.offset() != std::numeric_limits<int>::min()) {
- Relationship newRelationship = relationship;
- if (newRelationship.right() != otherRelationship.right()) {
- newRelationship.setLeft(otherRelationship.right());
- if (newRelationship.addToOffset(-otherRelationship.offset()))
- toAdd.append(newRelationship);
- }
- }
- }
- }
-
- if (!found)
- relationships.append(relationship);
-
- for (Relationship anotherRelationship : toAdd) {
- ASSERT(timeToLive);
- setOneSide(relationshipMap, anotherRelationship, timeToLive - 1);
- }
- }
-
- bool mergeTo(RelationshipMap& relationshipMap, BasicBlock* target)
- {
- if (verbose) {
- dataLog("Merging to ", pointerDump(target), ":\n");
- dataLog(" Incoming: ", listDump(sortedRelationships(relationshipMap)), "\n");
- dataLog(" At head: ", listDump(sortedRelationships(m_relationshipsAtHead[target])), "\n");
- }
-
- if (m_seenBlocks.add(target)) {
- // This is a new block. We copy subject to liveness pruning.
- auto isLive = [&] (Node* node) {
- if (node == m_zero)
- return true;
- return target->ssa->liveAtHead.contains(node);
- };
-
- for (auto& entry : relationshipMap) {
- if (!isLive(entry.key))
- continue;
-
- Vector<Relationship> values;
- for (Relationship relationship : entry.value) {
- ASSERT(relationship.left() == entry.key);
- if (isLive(relationship.right())) {
- if (verbose)
- dataLog(" Propagating ", relationship, "\n");
- values.append(relationship);
- }
- }
-
- std::sort(values.begin(), values.end());
- m_relationshipsAtHead[target].add(entry.key, values);
- }
- return true;
- }
-
- // Merge by intersecting. We have no notion of BOTTOM, so we use the omission of
- // relationships for a pair of nodes to mean TOP. The reason why we don't need BOTTOM
- // is (1) we just overapproximate contradictions and (2) a value never having been
- // assigned would only happen if we have not processed the node's predecessor. We
- // shouldn't process blocks until we have processed the block's predecessor because we
- // are using reverse postorder.
- Vector<Node*> toRemove;
- bool changed = false;
- for (auto& entry : m_relationshipsAtHead[target]) {
- auto iter = relationshipMap.find(entry.key);
- if (iter == relationshipMap.end()) {
- toRemove.append(entry.key);
- changed = true;
- continue;
- }
-
- Vector<Relationship> mergedRelationships;
- for (Relationship targetRelationship : entry.value) {
- for (Relationship sourceRelationship : iter->value) {
- if (verbose)
- dataLog(" Merging ", targetRelationship, " and ", sourceRelationship, ":\n");
- targetRelationship.merge(
- sourceRelationship,
- [&] (Relationship newRelationship) {
- if (verbose)
- dataLog(" Got ", newRelationship, "\n");
-
- // We need to filter() to avoid exponential explosion of identical
- // relationships. We do this here to avoid making setOneSide() do
- // more work, since we expect setOneSide() will be called more
- // frequently. Here's an example. At some point someone might start
- // with two relationships like @a > @b - C and @a < @b + D. Then
- // someone does a setRelationship() passing something that turns
- // both of these into @a == @b. Now we have @a == @b duplicated.
- // Let's say that this duplicate @a == @b ends up at the head of a
- // loop. If we didn't have this rule, then the loop would propagate
- // duplicate @a == @b's onto the existing duplicate @a == @b's.
- // There would be four pairs of @a == @b, each of which would
- // create a new @a == @b. Now we'd have four of these duplicates
- // and the next time around we'd have 8, then 16, etc. We avoid
- // this here by doing this filtration. That might be a bit of
- // overkill, since it's probably just the identical duplicate
- // relationship case we want' to avoid. But, I'll keep this until
- // we have evidence that this is a performance problem. Remember -
- // we are already dealing with a list that is pruned down to
- // relationships with identical left operand. It shouldn't be a
- // large list.
- bool found = false;
- for (Relationship& existingRelationship : mergedRelationships) {
- if (existingRelationship.sameNodesAs(newRelationship)) {
- Relationship filtered =
- existingRelationship.filter(newRelationship);
- if (filtered) {
- existingRelationship = filtered;
- found = true;
- break;
- }
- }
- }
-
- if (!found)
- mergedRelationships.append(newRelationship);
- });
- }
- }
- std::sort(mergedRelationships.begin(), mergedRelationships.end());
- if (entry.value == mergedRelationships)
- continue;
-
- entry.value = mergedRelationships;
- changed = true;
- }
- for (Node* node : toRemove)
- m_relationshipsAtHead[target].remove(node);
-
- return changed;
- }
-
- Vector<Relationship> sortedRelationships(const RelationshipMap& relationships)
- {
- Vector<Relationship> result;
- for (auto& entry : relationships)
- result.appendVector(entry.value);
- std::sort(result.begin(), result.end());
- return result;
- }
-
- Vector<Relationship> sortedRelationships()
- {
- return sortedRelationships(m_relationships);
- }
-
- Node* m_zero;
- RelationshipMap m_relationships;
- BlockSet m_seenBlocks;
- BlockMap<RelationshipMap> m_relationshipsAtHead;
- InsertionSet m_insertionSet;
-};
-
-} // anonymous namespace
-
-bool performIntegerRangeOptimization(Graph& graph)
-{
- SamplingRegion samplingRegion("DFG Integer Range Optimization Phase");
- return runPhase<IntegerRangeOptimizationPhase>(graph);
-}
-
-} } // namespace JSC::DFG
-
-#endif // ENABLE(DFG_JIT)
-