diff --git a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp index 6b3058d94f6ff..b835950dfdbca 100644 --- a/src/hotspot/share/opto/castnode.cpp +++ b/src/hotspot/share/opto/castnode.cpp @@ -498,7 +498,11 @@ Node* ConstraintCastNode::make_cast_for_type(Node* c, Node* in, const Type* type Node* ConstraintCastNode::optimize_integer_cast(PhaseGVN* phase, BasicType bt) { PhaseIterGVN *igvn = phase->is_IterGVN(); - const TypeInteger* this_type = this->type()->is_integer(bt); + const TypeInteger* this_type = this->type()->isa_integer(bt); + if (this_type == nullptr) { + return nullptr; + } + Node* z = in(1); const TypeInteger* rx = nullptr; const TypeInteger* ry = nullptr; diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index a5023408cdfe3..fb42ba4c9af9f 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -4521,7 +4521,9 @@ Node* Compile::conv_I2X_index(PhaseGVN* phase, Node* idx, const TypeInt* sizetyp // number. (The prior range check has ensured this.) // This assertion is used by ConvI2LNode::Ideal. int index_max = max_jint - 1; // array size is max_jint, index is one less - if (sizetype != nullptr) index_max = sizetype->_hi - 1; + if (sizetype != nullptr && sizetype->_hi > 0) { + index_max = sizetype->_hi - 1; + } const TypeInt* iidxtype = TypeInt::make(0, index_max, Type::WidenMax); idx = constrained_convI2L(phase, idx, iidxtype, ctrl); #endif diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 9b6d8db05b0e6..678b05a85178c 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -928,8 +928,8 @@ class Compile : public Phase { bool copy_node_notes_to(Node* dest, Node* source); // Workhorse function to sort out the blocked Node_Notes array: - inline Node_Notes* locate_node_notes(GrowableArray* arr, - int idx, bool can_grow = false); + Node_Notes* locate_node_notes(GrowableArray* arr, + int idx, bool can_grow = false); void grow_node_notes(GrowableArray* arr, int grow_by); diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 20feca26ede55..828dff29b55f8 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -3853,7 +3853,9 @@ Node* GraphKit::new_array(Node* klass_node, // array klass (maybe variable) if (tilen != nullptr && tilen->_lo < 0) { // Add a manual constraint to a positive range. Cf. array_element_address. jint size_max = fast_size_limit; - if (size_max > tilen->_hi) size_max = tilen->_hi; + if (size_max > tilen->_hi && tilen->_hi >= 0) { + size_max = tilen->_hi; + } const TypeInt* tlcon = TypeInt::make(0, size_max, Type::WidenMin); // Only do a narrow I2L conversion if the range check passed. diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp index 8d810e4202fab..fb048264071ab 100644 --- a/src/hotspot/share/opto/ifnode.cpp +++ b/src/hotspot/share/opto/ifnode.cpp @@ -1046,8 +1046,7 @@ bool IfNode::fold_compares_helper(ProjNode* proj, ProjNode* success, ProjNode* f if (failtype != nullptr) { const TypeInt* type2 = filtered_int_type(igvn, n, fail); if (type2 != nullptr) { - failtype = failtype->join(type2)->is_int(); - if (failtype->empty()) { + if (failtype->filter(type2) == Type::TOP) { // previous if determines the result of this if so // replace Bool with constant igvn->replace_input_of(this, 1, igvn->intcon(success->_con)); diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp new file mode 100644 index 0000000000000..40b9da4bde510 --- /dev/null +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -0,0 +1,1081 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "opto/rangeinference.hpp" +#include "opto/type.hpp" +#include "utilities/intn_t.hpp" +#include "utilities/tuple.hpp" + +// If the cardinality of a TypeInt is below this threshold, use min widen, see +// TypeIntPrototype::normalize_widen +constexpr juint SMALL_TYPEINT_THRESHOLD = 3; + +// This represents the result of an iterative calculation +template +class AdjustResult { +public: + bool _progress; // whether there is progress compared to the last iteration + bool _present; // whether the result is empty, typically due to the calculation arriving at contradiction + T _result; + + bool empty() const { + return !_present; + } + + static AdjustResult make_empty() { + return {true, false, {}}; + } +}; + +// This is the result of canonicalizing a simple interval (see TypeInt at +// type.hpp) +template +class SimpleCanonicalResult { + static_assert(U(-1) > U(0), "bit info should be unsigned"); +public: + const bool _present; // whether this is an empty set + const RangeInt _bounds; // The bounds must be in the same half of the integer domain (see TypeInt) + const KnownBits _bits; + + SimpleCanonicalResult(bool present, const RangeInt& bounds, const KnownBits& bits) + : _present(present), _bounds(bounds), _bits(bits) { + if (!present) { + return; + } + // Do some verification + assert(bits.is_satisfied_by(bounds._lo) && bits.is_satisfied_by(bounds._hi), "must be canonical"); + // 0b1000... + constexpr U mid_point = (std::numeric_limits::max() >> 1) + U(1); + assert((bounds._lo < mid_point) == (bounds._hi < mid_point), "must be a simple interval, see Lemma 4"); + } + + bool empty() const { + return !_present; + } + + static SimpleCanonicalResult make_empty() { + return SimpleCanonicalResult(false, {}, {}); + } +}; + +// Find the minimum value that is not less than lo and satisfies bits. If there +// does not exist one such number, the calculation will return a value < lo. +// +// Formally, this function tries to find the minimum value that is not less +// than lo and satisfies bits, assuming such value exists. The cases where such +// value does not exists automatically follows. +// +// If there exists a value not less than lo and satisfies bits, then this +// function will always find one such value. The converse is also true, that is +// if this function finds a value not less than lo and satisfies bits, then it +// must trivially be the case that there exists one such value. As a result, +// the negation of those statements are also equivalent, there does not exists +// a value not less than lo and satisfies bits if and only if this function +// does not return one such value. +// +// In practice, since the algorithm always ensures that the returned value +// satisfies bits, we only need to check if it is not less than lo. +// +// Here, we view a number in binary as a bit string. As a result, the first +// bit refers to the highest bit (the MSB), the last bit refers to the lowest +// bit (the LSB), a bit comes before (being higher than) another if it is more +// significant, and a bit comes after (being lower than) another if it is less +// significant. For a value n with w bits, we denote n[0] the first (highest) +// bit of n, n[1] the second bit, ..., n[w - 1] the last (lowest) bit of n. +template +static U adjust_lo(U lo, const KnownBits& bits) { + // Violation of lo with respects to bits + // E.g: lo = 1100 + // zeros = 0100 + // ones = 1001 + // zero_violation = 0100, i.e the second bit should be zero, but it is 1 in + // lo. Similarly, one_violation = 0001, i.e the last bit should be one, but + // it is 0 in lo. These make lo not satisfy the bit constraints, which + // results in us having to find the smallest value that satisfies bits. + U zero_violation = lo & bits._zeros; + U one_violation = ~lo & bits._ones; + if (zero_violation == one_violation) { + // This means lo does not violate bits, it is the result + assert(zero_violation == U(0), ""); + return lo; + } + + /* + 1. Intuition: + Call r the lowest value not smaller than lo that satisfies bits, consider the + first bit in r that is different from the corresponding bit in lo: + - Since r is larger than lo the bit must be 0 in lo and 1 in r + - Since r must satisify bits the bit must be 0 in zeros + - Since r should be the smallest value, this bit should be the lowest one + possible + + E.g: 1 2 3 4 5 6 + lo = 1 0 0 1 1 0 + x = 1 0 1 0 1 0 + y = 0 1 1 1 1 1 + x would be larger than lo since the first different bit is the 3rd one, + while y is smaller than lo because the first different bit is the 1st bit. + Next, consider: + x1 = 1 0 1 0 1 0 + x2 = 1 0 0 1 1 1 + Both x1 and x2 are larger than lo, but x1 > x2 since its first different + bit from lo is the 3rd one, while with x2 it is the 7th one. As a result, + if both x1 and x2 satisfy bits, x2 would be closer to our true result. + + 2. Formality: + + Call r the smallest value not smaller than lo that satisfies bits. Since lo + does not satisfy bits, lo < r (2.1) + + Call i the largest bit index such that: + + - lo[x] satisfies bits for 0 <= x < i (2.2) + - zeros[i] = 0 (2.3) + - lo[i] = 0 (2.4) + + Consider v: + + - v[x] = lo[x], for 0 <= x < i (2.5) + - v[i] = 1 (2.6) + - v[x] = ones[x], for x > i (2.7) + + We will prove that v == r. + + a. Firstly, we prove that r <= v: + + a.1. lo < v, since: + lo[x] == v[x], for 0 <= x < i (according to 2.5) + lo[i] < v[i] (according to 2.4 and 2.6, lo[i] == 0 < v[i] == 1) + bits at x > i have lower significance, and are thus irrelevant + + a.2. v satisfies bits, because: + v[x] satisfies bits for 0 <= x < i (according to 2.2 and 2.5) + v[i] satisfies bits: + According to 2.3 and 2.6, zeros[i] == 0 and v[i] == 1, v[i] does not violate + bits, which means v[i] satisfies bits + v[x] satisfies bits for x > i: + Assume bits is not contradictory, we cannot have: + ones[x] == 1, v[x] == 0 (according to 2.7, v[x] == ones[x]) + zeros[x] == 1, v[x] == 1 (according to 2.7, ones[x] == v[x] == 1, which means + bits is contradictory) + + From a.1 and a.2, v > lo and v satisfies bits. Which means r <= v since r is the + smallest such value. + + b. Secondly, from r <= v, we prove that r == v. Suppose the contradiction r < v: + + Since r < v, there must be a bit position j that: + + r[j] == 0 (2.b.1) + v[j] == 1 (2.b.2) + r[x] == v[x], for x < j (2.b.3) + + b.1. If j < i + This means that: + r[j] == 0 (according to 2.b.1) + lo[j] == 1 (according to 2.b.2 and 2.5, lo[j] == v[j] == 1 because j < i) + r[x] == lo[x], for x < j (according to 2.b.3 and 2.5, lo[x] == v[x] == r[x] with x < j < i) + bits at x > j have lower significance, and are thus irrelevant + + Which leads to r < lo, which contradicts that lo < r (acording to 2.1) + + b.2. If j == i + Since r > lo (according to 2.1), there must exist a bit index k such that: + + r[k] == 1 (2.b.2.1) + lo[k] == 0 (2.b.2.2) + r[x] == lo[x], for x < k (2.b.2.3) + + Then, since we have: + r[x] == v[x], for x < i (according to 2.b.3) + v[x] == lo[x], for x < i (according to 2.5) + r[i] == 0 (according to 2.b.1 because i == j) + lo[i] == 0 (according to 2.4) + + this leads to: r[x] == lo[x], for x <= i + while r[k] == 1 != lo[k] == 0, we can conclude that k > i + + However, since: + lo[x] satisfies bits for 0 <= x < k: + According to 2.b.2.3, lo[x] == r[x] and r satisfies bits + zeros[k] == 0 (according to 2.b.2.1, r[k] == 1 and r satisfies bits) + lo[k] == 0 (according to 2.b.2.2) + + This contradicts the assumption that i is the largest bit index satisfying such conditions. + + b.3. If j > i + ones[j] == v[j] (according to 2.7 since j > i) + v[j] == 1 (according to 2.b.2) + r[j] == 0 (according to 2.b.1) + + This means that r[j] == 0 and ones[j] == 1, this contradicts the assumption that r + satisfies bits. + + All cases lead to contradictions, which mean r < v is incorrect, which means that + r == v, which means the value v having the above form is the lowest value not smaller + than lo that satisfies bits. + + 3. Conclusion + Our objective now is to find the largest value i that satisfies: + - lo[x] satisfies bits for 0 <= x < i (3.1) + - zeros[i] = 0 (3.2) + - lo[i] = 0 (3.3) + */ + + // The algorithm depends on whether the first violation violates zeros or + // ones. If it violates zeros, we have the bit being 1 in zero_violation and + // 0 in one_violation. Since all higher bits are 0 in zero_violation and + // one_violation, we have zero_violation > one_violation. Similarly, if the + // first violation violates ones, we have zero_violation < one_violation. + if (zero_violation < one_violation) { + // This means that the first bit that does not satisfy the bit requirement + // is a 0 that should be a 1. Obviously, since the bit at that position in + // ones is 1, the same bit in zeros is 0. + // + // From section 3 above, we know i is the largest bit index such that: + // - lo[x] satisfies bits for 0 <= x < i (3.1) + // - zeros[i] = 0 (3.2) + // - lo[i] = 0 (3.3) + // + // For the given i, we know that lo satisfies all bits before i, hence (3.1) + // holds. Further, lo[i] = 0 (3.3), and we have a one violation at i, hence + // zero[i] = 0 (3.2). Any smaller i would not be the largest possible such + // index. Any larger i would violate (3.1), since lo[i] does not satisfy bits. + // As a result, the first violation is the bit i we are looking for. + // + // E.g: 1 2 3 4 5 6 7 8 + // lo = 1 1 0 0 0 1 1 0 + // zeros = 0 0 1 0 0 1 0 0 + // ones = 0 1 0 1 0 0 1 0 + // 1-vio = 0 0 0 1 0 0 0 0 + // 0-vio = 0 0 0 0 0 1 0 0 + // Since the result must have the 4th bit set, it must be at least: + // 1 1 0 1 0 0 0 0 + // This value must satisfy zeros, because all bits before the 4th bit have + // already satisfied zeros, and all bits after the 4th bit are all 0 now. + // Just OR this value with ones to obtain the final result. + + // first_violation is the position of the violation counting from the + // highest bit down (0-based), since i == 4, first_violation == 3 + juint first_violation = count_leading_zeros(one_violation); + // 1 0 0 0 0 0 0 0 + constexpr U highest_bit = (std::numeric_limits::max() >> 1) + U(1); + // This is the bit at which we want to change the bit 0 in lo to a 1, and + // all bits after to zero. This is similar to an operation that aligns lo + // up to the next multiple of this modulo. + // 0 0 0 1 0 0 0 0 + U alignment = highest_bit >> first_violation; + // This is the first value which have the violated bit being 1, which means + // that the result should not be smaller than this. + // This is a standard operation to align a value up to the next multiple of + // a certain power of 2. Since alignment is a power of 2, -alignment is a + // value having all the bits being 1 upto the location of the bit in + // alignment (in the example, -alignment = 11110000). As a result, + // lo & -alignment set all bits after the bit in alignment to 0, which is + // equivalent to rounding lo down to a multiple of alignment. To round lo + // up to the next multiple of alignment, we add alignment to the rounded + // down value. + // Note that this computation cannot overflow as the bit in lo that is at + // the same position as the only bit 1 in alignment must be 0. As a result, + // this operation just set that bit to 1 and set all the bits after to 0. + // We now have: + // - new_lo[x] = lo[x], for 0 <= x < i (2.5) + // - new_lo[i] = 1 (2.6) + // - new_lo[x] = 0, for x > i (not yet 2.7) + // 1 1 0 1 0 0 0 0 + U new_lo = (lo & -alignment) + alignment; + // Note that there exists no value x not larger than i such that + // new_lo[x] == 0 and ones[x] == 1. This is because all bits of lo before i + // should satisfy bits, and new_lo[i] == 1. As a result, doing + // new_lo |= bits.ones will give us a value such that: + // - new_lo[x] = lo[x], for 0 <= x < i (2.5) + // - new_lo[i] = 1 (2.6) + // - new_lo[x] = ones[x], for x > i (2.7) + // This is the result we are looking for. + // 1 1 0 1 0 0 1 0 + new_lo |= bits._ones; + // Note that in this case, new_lo is always a valid answer. That is, it is + // a value not less than lo and satisfies bits. + assert(lo < new_lo, "the result must be valid"); + return new_lo; + } else { + assert(zero_violation > one_violation, "remaining case"); + // This means that the first bit that does not satisfy the bit requirement + // is a 1 that should be a 0. + // + // From section 3 above, we know i is the largest bit index such that: + // - lo[x] satisfies bits for 0 <= x < i (3.1) + // - zeros[i] = 0 (3.2) + // - lo[i] = 0 (3.3) + // + // We know that lo satisfies all bits before first_violation, hence (3.1) + // holds. However, first_violation is not the value i we are looking for + // because lo[first_violation] == 1. We can also see that any larger value + // of i would violate (3.1) since lo[first_violation] does not satisfy + // bits. As a result, we should find the last index x upto first_violation + // such that lo[x] == zeros[x] == 0. That value of x would be the value of + // i we are looking for. + // + // E.g: 1 2 3 4 5 6 7 8 + // lo = 1 0 0 0 1 1 1 0 + // zeros = 0 0 0 1 0 1 0 0 + // ones = 1 0 0 0 0 0 1 1 + // 1-vio = 0 0 0 0 0 0 0 1 + // 0-vio = 0 0 0 0 0 1 0 0 + // The first violation is the 6th bit, which should be 0. We want to flip + // it to 0. However, since we must obtain a value larger than lo, we must + // find an earlier bit that can be flipped from 0 to 1. The 5th cannot be + // the bit we are looking for, because it is already 1, the 4th bit also + // cannot be, because it must be 0. As a result, the last bit we can flip, + // which is the first different bit between the result and lo must be the + // 3rd bit. As a result, the result must not be smaller than: + // 1 0 1 0 0 0 0 0 + // This one satisfies zeros so we can use the logic in the previous case, + // just OR with ones to obtain the final result, which is: + // 1 0 1 0 0 0 1 1 + + juint first_violation = count_leading_zeros(zero_violation); + // This masks out all bits after the first violation + // 1 1 1 1 1 0 0 0 + U find_mask = ~(std::numeric_limits::max() >> first_violation); + // We want to find the last index x upto first_violation such that + // lo[x] == zeros[x] == 0. + // We start with all bits where lo[x] == zeros[x] == 0: + // 0 1 1 0 0 0 0 1 + U neither = ~(lo | bits._zeros); + // Now let us find all the bit indices x upto first_violation such that + // lo[x] == zeros[x] == 0. The last one of these bits must be at index i. + // 0 1 1 0 0 0 0 0 + U neither_upto_first_violation = neither & find_mask; + // We now want to select the last one of these candidates, which is exactly + // the last index x upto first_violation such that lo[x] == zeros[x] == 0. + // This would be the value i we are looking for. + // Similar to the other case, we want to obtain the value with only the bit + // i set, this is equivalent to extracting the last set bit of + // neither_upto_first_violation, do it directly without going through i. + // The formula x & (-x) will give us the last set bit of an integer x + // (please see the x86 instruction blsi). + // In our example, i == 2 + // 0 0 1 0 0 0 0 0 + U alignment = neither_upto_first_violation & (-neither_upto_first_violation); + // Set the bit of lo at i and unset all the bits after, this is the + // smallest value that satisfies bits._zeros. Similar to the above case, + // this is similar to aligning lo up to the next multiple of alignment. + // Also similar to the above case, this computation cannot overflow. + // We now have: + // - new_lo[x] = lo[x], for 0 <= x < i (2.5) + // - new_lo[i] = 1 (2.6) + // - new_lo[x] = 0, for x > i (not yet 2.7) + // 1 0 1 0 0 0 0 0 + U new_lo = (lo & -alignment) + alignment; + // Note that there exists no value x not larger than i such that + // new_lo[x] == 0 and ones[x] == 1. This is because all bits of lo before i + // should satisfy bits, and new_lo[i] == 1. As a result, doing + // new_lo |= bits.ones will give us a value such that: + // - new_lo[x] = lo[x], for 0 <= x < i (2.5) + // - new_lo[i] = 1 (2.6) + // - new_lo[x] = ones[x], for x > i (2.7) + // This is the result we are looking for. + // 1 0 1 0 0 0 1 1 + new_lo |= bits._ones; + // Note that formally, this function assumes that there exists a value not + // smaller than lo and satisfies bits. This implies the existence of the + // index i satisfies (3.1-3.3), which means that + // neither_upto_first_violation != 0. The converse is + // also true, if neither_upto_first_violation != 0, then an index i + // satisfies (3.1-3.3) exists, which implies the existence of a value not + // smaller than lo and satisfies bits. As a result, the negation of those + // statements are equivalent. neither_upto_first_violation == 0 if and only + // if there does not exists a value not smaller than lo and satisfies bits. + // In this case, alignment == 0 and new_lo == bits._ones. We know that, if + // the assumption of this function holds, we return a value satisfying + // bits, and if the assumption of this function does not hold, the returned + // value would be bits._ones, which also satisfies bits. As a result, this + // function always returns a value satisfying bits, regardless whether if + // the assumption of this function holds. In conclusion, the caller only + // needs to check lo <= new_lo to find the cases where there exists no + // value not smaller than lo and satisfies bits (see the overview of the + // function). + assert(lo < new_lo || new_lo == bits._ones, "invalid result must be bits._ones"); + return new_lo; + } +} + +// Try to tighten the bound constraints from the known bit information. I.e, we +// find the smallest value not smaller than lo, as well as the largest value +// not larger than hi both of which satisfy bits +// E.g: lo = 0010, hi = 1001 +// zeros = 0011 +// ones = 0000 +// -> 4-aligned +// +// 0 1 2 3 4 5 6 7 8 9 10 +// 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 +// bits: ok . . . ok . . . ok . . +// bounds: lo hi +// adjust: --------> lo hi <--- +template +static AdjustResult> +adjust_unsigned_bounds_from_bits(const RangeInt& bounds, const KnownBits& bits) { + U new_lo = adjust_lo(bounds._lo, bits); + if (new_lo < bounds._lo) { + // This means we wrapped around, which means no value not less than lo + // satisfies bits + return AdjustResult>::make_empty(); + } + + // We need to find the largest value not larger than hi that satisfies bits + // One possible method is to do similar to adjust_lo, just with the other + // direction + // However, we can observe that if v satisfies {bits._zeros, bits._ones}, + // then ~v would satisfy {bits._ones, bits._zeros}. Combine with the fact + // that bitwise-not is a strictly decreasing function, if new_hi is the + // largest value not larger than hi that satisfies {bits._zeros, bits._ones}, + // then ~new_hi is the smallest value not smaller than ~hi that satisfies + // {bits._ones, bits._zeros}. + // + // Proof: + // Calling h the smallest value not smaller than ~hi that satisfies + // {bits._ones, bits._zeros}. + // + // 1. Since h satisfies {bits._ones, bits._zeros}, ~h satisfies + // {bits._zeros, bits._ones}. Assume the contradiction ~h does not satisfy + // {bits._zeros, bits._ones}, There can be 2 cases: + // 1.1. There is a bit in ~h that is 0 where the corresponding bit in ones + // is 1. This implies the corresponding bit in h is 1. But this is + // contradictory since h satisfies {bits._ones, bits._zeros}. + // 1.2. There is a bit in ~h that is 1 where the corresponding bit in zeros + // is 1. Similarly, this leads to contradiction because h needs to + // satisfy {bits._ones, bits._zeros}. + // + // 2. Assume there is a value k that is larger than ~h such that k is not + // larger than hi, i.e. ~h < k <= hi, and k satisfies {bits._zeros, bits._ones}. + // As a result, ~k would satisfy {bits._ones, bits._zeros}. And since bitwise-not + // is a strictly decreasing function, given ~h < k <= hi, we have h > ~k >= ~hi. + // This contradicts the assumption that h is the smallest value not smaller than + // ~hi and satisfies {bits._ones, bits._zeros}. + // + // As a result, ~h is the largest value not larger than hi that satisfies + // bits (QED). + U h = adjust_lo(~bounds._hi, {bits._ones, bits._zeros}); + if (h < ~bounds._hi) { + return AdjustResult>::make_empty(); + } + + U new_hi = ~h; + bool progress = (new_lo != bounds._lo) || (new_hi != bounds._hi); + bool present = new_lo <= new_hi; + return {progress, present, {new_lo, new_hi}}; +} + +// Try to tighten the known bit constraints from the bound information by +// extracting the common prefix of lo and hi and combining with the current +// bit constraints +// E.g: lo = 010011 +// hi = 010100, +// then all values in [lo, hi] would be +// 010*** +template +static AdjustResult> +adjust_bits_from_unsigned_bounds(const KnownBits& bits, const RangeInt& bounds) { + // Find the mask to filter the common prefix, all values between bounds._lo + // and bounds._hi should share this common prefix in terms of bits + U mismatch = bounds._lo ^ bounds._hi; + // Find the first mismatch, all bits before it are the same in bounds._lo and + // bounds._hi + U match_mask = mismatch == U(0) ? std::numeric_limits::max() + : ~(std::numeric_limits::max() >> count_leading_zeros(mismatch)); + // match_mask & bounds._lo is the common prefix, extract zeros and ones from + // it + U common_prefix_zeros = match_mask & ~bounds._lo; + assert(common_prefix_zeros == (match_mask & ~bounds._hi), ""); + U new_zeros = bits._zeros | common_prefix_zeros; + + U common_prefix_ones = match_mask & bounds._lo; + assert(common_prefix_ones == (match_mask & bounds._hi), ""); + U new_ones = bits._ones | common_prefix_ones; + + bool progress = (new_zeros != bits._zeros) || (new_ones != bits._ones); + bool present = ((new_zeros & new_ones) == U(0)); + return {progress, present, {new_zeros, new_ones}}; +} + +// Try to tighten both the bounds and the bits at the same time. +// Iteratively tighten one using the other until no progress is made. +// This function converges because at each iteration, some bits that are unknown +// are made known. As there are at most 64 bits, the number of iterations should +// not be larger than 64. +// This function is called simple because it deals with a simple intervals (see +// TypeInt at type.hpp). +template +static SimpleCanonicalResult +canonicalize_constraints_simple(const RangeInt& bounds, const KnownBits& bits) { + assert((bounds._lo ^ bounds._hi) < (std::numeric_limits::max() >> 1) + U(1), "bounds must be a simple interval"); + + AdjustResult> canonicalized_bits = adjust_bits_from_unsigned_bounds(bits, bounds); + if (canonicalized_bits.empty()) { + return SimpleCanonicalResult::make_empty(); + } + AdjustResult> canonicalized_bounds{true, true, bounds}; + // Since bits are derived from bounds in the previous iteration and vice + // versa, if one does not show progress, the other will also not show + // progress, so we terminate early + while (true) { + canonicalized_bounds = adjust_unsigned_bounds_from_bits(canonicalized_bounds._result, canonicalized_bits._result); + if (!canonicalized_bounds._progress || canonicalized_bounds.empty()) { + return SimpleCanonicalResult(canonicalized_bounds._present, canonicalized_bounds._result, canonicalized_bits._result); + } + canonicalized_bits = adjust_bits_from_unsigned_bounds(canonicalized_bits._result, canonicalized_bounds._result); + if (!canonicalized_bits._progress || canonicalized_bits.empty()) { + return SimpleCanonicalResult(canonicalized_bits._present, canonicalized_bounds._result, canonicalized_bits._result); + } + } +} + +// Tighten all constraints of a TypeIntPrototype to its canonical form. +// i.e the result represents the same set as the input, each bound belongs to +// the set and for each bit position that is not constrained, there exists 2 +// values with the bit value at that position being set and unset, respectively, +// such that both belong to the set represented by the constraints. +template +typename TypeIntPrototype::CanonicalizedTypeIntPrototype +TypeIntPrototype::canonicalize_constraints() const { + RangeInt srange = _srange; + RangeInt urange = _urange; + // Trivial contradictions + if (srange._lo > srange._hi || + urange._lo > urange._hi || + (_bits._zeros & _bits._ones) != U(0)) { + return CanonicalizedTypeIntPrototype::make_empty(); + } + + // We try to make [srange._lo, S(urange._hi)] and + // [S(urange._lo), srange._hi] be both simple intervals (as defined in + // TypeInt at type.hpp) + if (S(urange._lo) > S(urange._hi)) { + // This means that S(urange._lo) >= 0 and S(urange._hi) < 0 because here we + // know that U(urange._lo) <= U(urange._hi) + if (S(urange._hi) < srange._lo) { + // This means that there should be no element in the interval + // [min_S, S(urange._hi)], tighten urange._hi to max_S + // Signed: + // min_S----uhi---------lo---------0--------ulo==========hi----max_S + // Unsigned: + // 0--------ulo==========hi----max_S min_S-----uhi---------lo--------- + urange._hi = U(std::numeric_limits::max()); + } else if (S(urange._lo) > srange._hi) { + // This means that there should be no element in the interval + // [S(urange._lo), max_S], tighten urange._lo to min_S + // Signed: + // min_S----lo=========uhi---------0--------hi----------ulo----max_S + // Unsigned: + // 0--------hi----------ulo----max_S min_S----lo=========uhi--------- + urange._lo = U(std::numeric_limits::min()); + } + } + + // Now [srange._lo, S(urange._hi)] and [S(urange._lo), srange._hi] are both + // simple intervals (as defined in TypeInt at type.hpp), we process them + // separately and combine the results + if (S(urange._lo) <= S(urange._hi)) { + // The 2 simple intervals should be tightened to the same result + urange._lo = U(MAX2(S(urange._lo), srange._lo)); + urange._hi = U(MIN2(S(urange._hi), srange._hi)); + if (urange._lo > urange._hi || S(urange._lo) > S(urange._hi)) { + return CanonicalizedTypeIntPrototype::make_empty(); + } + + auto type = canonicalize_constraints_simple(urange, _bits); + return {type._present, {{S(type._bounds._lo), S(type._bounds._hi)}, + type._bounds, type._bits}}; + } + + // The 2 simple intervals can be tightened into 2 separate results + auto neg_type = canonicalize_constraints_simple({U(srange._lo), urange._hi}, _bits); + auto pos_type = canonicalize_constraints_simple({urange._lo, U(srange._hi)}, _bits); + + if (neg_type.empty() && pos_type.empty()) { + return CanonicalizedTypeIntPrototype::make_empty(); + } else if (neg_type.empty()) { + return {true, {{S(pos_type._bounds._lo), S(pos_type._bounds._hi)}, + pos_type._bounds, pos_type._bits}}; + } else if (pos_type.empty()) { + return {true, {{S(neg_type._bounds._lo), S(neg_type._bounds._hi)}, + neg_type._bounds, neg_type._bits}}; + } else { + return {true, {{S(neg_type._bounds._lo), S(pos_type._bounds._hi)}, + {pos_type._bounds._lo, neg_type._bounds._hi}, + {neg_type._bits._zeros & pos_type._bits._zeros, neg_type._bits._ones & pos_type._bits._ones}}}; + } +} + +template +int TypeIntPrototype::normalize_widen(int widen) const { + // Certain normalizations keep us sane when comparing types. + // The 'SMALL_TYPEINT_THRESHOLD' covers constants and also CC and its relatives. + if (TypeIntHelper::cardinality_from_bounds(_srange, _urange) <= U(SMALL_TYPEINT_THRESHOLD)) { + return Type::WidenMin; + } + if (_srange._lo == std::numeric_limits::min() && _srange._hi == std::numeric_limits::max() && + _urange._lo == std::numeric_limits::min() && _urange._hi == std::numeric_limits::max() && + _bits._zeros == U(0) && _bits._ones == U(0)) { + // bottom type + return Type::WidenMax; + } + return widen; +} + +#ifdef ASSERT +template +bool TypeIntPrototype::contains(S v) const { + U u(v); + return v >= _srange._lo && v <= _srange._hi && + u >= _urange._lo && u <= _urange._hi && + _bits.is_satisfied_by(u); +} + +// Verify that this set representation is canonical +template +void TypeIntPrototype::verify_constraints() const { + // Assert that the bounds cannot be further tightened + assert(contains(_srange._lo) && contains(_srange._hi) && + contains(S(_urange._lo)) && contains(S(_urange._hi)), ""); + + // Assert that the bits cannot be further tightened + if (U(_srange._lo) == _urange._lo) { + assert(!adjust_bits_from_unsigned_bounds(_bits, _urange)._progress, ""); + } else { + RangeInt neg_range{U(_srange._lo), _urange._hi}; + auto neg_bits = adjust_bits_from_unsigned_bounds(_bits, neg_range); + assert(neg_bits._present, ""); + assert(!adjust_unsigned_bounds_from_bits(neg_range, neg_bits._result)._progress, ""); + + RangeInt pos_range{_urange._lo, U(_srange._hi)}; + auto pos_bits = adjust_bits_from_unsigned_bounds(_bits, pos_range); + assert(pos_bits._present, ""); + assert(!adjust_unsigned_bounds_from_bits(pos_range, pos_bits._result)._progress, ""); + + assert((neg_bits._result._zeros & pos_bits._result._zeros) == _bits._zeros && + (neg_bits._result._ones & pos_bits._result._ones) == _bits._ones, ""); + } +} +#endif // ASSERT + +template class TypeIntPrototype; +template class TypeIntPrototype; +template class TypeIntPrototype, uintn_t<1>>; +template class TypeIntPrototype, uintn_t<2>>; +template class TypeIntPrototype, uintn_t<3>>; +template class TypeIntPrototype, uintn_t<4>>; + +// Compute the meet of 2 types. When dual is true, the subset relation in CT is +// reversed. This means that the result of 2 CTs would be the intersection of +// them if dual is true, and be the union of them if dual is false. The subset +// relation in the Type hierarchy is still the same, however. E.g. the result +// of 1 CT and Type::BOTTOM would always be Type::BOTTOM, and the result of 1 +// CT and Type::TOP would always be the CT instance itself. +template +const Type* TypeIntHelper::int_type_xmeet(const CT* i1, const Type* t2) { + // Perform a fast test for common case; meeting the same types together. + if (i1 == t2 || t2 == Type::TOP) { + return i1; + } + const CT* i2 = t2->try_cast(); + if (i2 != nullptr) { + assert(i1->_is_dual == i2->_is_dual, "must have the same duality"); + using S = std::remove_const_t; + using U = std::remove_const_t; + + if (!i1->_is_dual) { + // meet (a.k.a union) + return CT::make_or_top(TypeIntPrototype{{MIN2(i1->_lo, i2->_lo), MAX2(i1->_hi, i2->_hi)}, + {MIN2(i1->_ulo, i2->_ulo), MAX2(i1->_uhi, i2->_uhi)}, + {i1->_bits._zeros & i2->_bits._zeros, i1->_bits._ones & i2->_bits._ones}}, + MAX2(i1->_widen, i2->_widen), false); + } else { + // join (a.k.a intersection) + return CT::make_or_top(TypeIntPrototype{{MAX2(i1->_lo, i2->_lo), MIN2(i1->_hi, i2->_hi)}, + {MAX2(i1->_ulo, i2->_ulo), MIN2(i1->_uhi, i2->_uhi)}, + {i1->_bits._zeros | i2->_bits._zeros, i1->_bits._ones | i2->_bits._ones}}, + MIN2(i1->_widen, i2->_widen), true); + } + } + + assert(t2->base() != i1->base(), ""); + switch (t2->base()) { // Switch on original type + case Type::AnyPtr: // Mixing with oops happens when javac + case Type::RawPtr: // reuses local variables + case Type::OopPtr: + case Type::InstPtr: + case Type::AryPtr: + case Type::MetadataPtr: + case Type::KlassPtr: + case Type::InstKlassPtr: + case Type::AryKlassPtr: + case Type::NarrowOop: + case Type::NarrowKlass: + case Type::Int: + case Type::Long: + case Type::HalfFloatTop: + case Type::HalfFloatCon: + case Type::HalfFloatBot: + case Type::FloatTop: + case Type::FloatCon: + case Type::FloatBot: + case Type::DoubleTop: + case Type::DoubleCon: + case Type::DoubleBot: + case Type::Bottom: // Ye Olde Default + return Type::BOTTOM; + default: // All else is a mistake + i1->typerr(t2); + return nullptr; + } +} +template const Type* TypeIntHelper::int_type_xmeet(const TypeInt* i1, const Type* t2); +template const Type* TypeIntHelper::int_type_xmeet(const TypeLong* i1, const Type* t2); + +// Called in PhiNode::Value during CCP, monotically widen the value set, do so rigorously +// first, after WidenMax attempts, if the type has still not converged we speed up the +// convergence by abandoning the bounds +template +const Type* TypeIntHelper::int_type_widen(const CT* new_type, const CT* old_type, const CT* limit_type) { + using S = std::remove_const_t; + using U = std::remove_const_t; + + if (old_type == nullptr) { + return new_type; + } + + // If new guy is equal to old guy, no widening + if (int_type_is_equal(new_type, old_type)) { + return old_type; + } + + // If old guy contains new, then we probably widened too far & dropped to + // bottom. Return the wider fellow. + if (int_type_is_subset(old_type, new_type)) { + return old_type; + } + + // Neither contains each other, weird? + if (!int_type_is_subset(new_type, old_type)) { + return CT::TYPE_DOMAIN; + } + + // If old guy was a constant, do not bother + if (old_type->singleton()) { + return new_type; + } + + // If new guy contains old, then we widened + // If new guy is already wider than old, no widening + if (new_type->_widen > old_type->_widen) { + return new_type; + } + + if (new_type->_widen < Type::WidenMax) { + // Returned widened new guy + TypeIntPrototype prototype{{new_type->_lo, new_type->_hi}, {new_type->_ulo, new_type->_uhi}, new_type->_bits}; + return CT::make_or_top(prototype, new_type->_widen + 1); + } + + // Speed up the convergence by abandoning the bounds, there are only a couple of bits so + // they converge fast + S min = std::numeric_limits::min(); + S max = std::numeric_limits::max(); + U umin = std::numeric_limits::min(); + U umax = std::numeric_limits::max(); + U zeros = new_type->_bits._zeros; + U ones = new_type->_bits._ones; + if (limit_type != nullptr) { + min = limit_type->_lo; + max = limit_type->_hi; + umin = limit_type->_ulo; + umax = limit_type->_uhi; + zeros |= limit_type->_bits._zeros; + ones |= limit_type->_bits._ones; + } + TypeIntPrototype prototype{{min, max}, {umin, umax}, {zeros, ones}}; + return CT::make_or_top(prototype, Type::WidenMax); +} +template const Type* TypeIntHelper::int_type_widen(const TypeInt* new_type, const TypeInt* old_type, const TypeInt* limit_type); +template const Type* TypeIntHelper::int_type_widen(const TypeLong* new_type, const TypeLong* old_type, const TypeLong* limit_type); + +// Called by PhiNode::Value during GVN, monotonically narrow the value set, only +// narrow if the bits change or if the bounds are tightened enough to avoid +// slow convergence +template +const Type* TypeIntHelper::int_type_narrow(const CT* new_type, const CT* old_type) { + using S = decltype(CT::_lo); + using U = decltype(CT::_ulo); + + if (new_type->singleton() || old_type == nullptr) { + return new_type; + } + + // If new guy is equal to old guy, no narrowing + if (int_type_is_equal(new_type, old_type)) { + return old_type; + } + + // If old guy was maximum range, allow the narrowing + if (int_type_is_equal(old_type, CT::TYPE_DOMAIN)) { + return new_type; + } + + // Doesn't narrow; pretty weird + if (!int_type_is_subset(old_type, new_type)) { + return new_type; + } + + // Bits change + if (old_type->_bits._zeros != new_type->_bits._zeros || old_type->_bits._ones != new_type->_bits._ones) { + return new_type; + } + + // Only narrow if the range shrinks a lot + U oc = cardinality_from_bounds(RangeInt{old_type->_lo, old_type->_hi}, + RangeInt{old_type->_ulo, old_type->_uhi}); + U nc = cardinality_from_bounds(RangeInt{new_type->_lo, new_type->_hi}, + RangeInt{new_type->_ulo, new_type->_uhi}); + return (nc > (oc >> 1) + (SMALL_TYPEINT_THRESHOLD * 2)) ? old_type : new_type; +} +template const Type* TypeIntHelper::int_type_narrow(const TypeInt* new_type, const TypeInt* old_type); +template const Type* TypeIntHelper::int_type_narrow(const TypeLong* new_type, const TypeLong* old_type); + + +#ifndef PRODUCT +template +static const char* int_name_near(T origin, const char* xname, char* buf, size_t buf_size, T n) { + if (n < origin) { + if (n <= origin - 10000) { + return nullptr; + } + os::snprintf_checked(buf, buf_size, "%s-" INT32_FORMAT, xname, jint(origin - n)); + } else if (n > origin) { + if (n >= origin + 10000) { + return nullptr; + } + os::snprintf_checked(buf, buf_size, "%s+" INT32_FORMAT, xname, jint(n - origin)); + } else { + return xname; + } + return buf; +} + +const char* TypeIntHelper::intname(char* buf, size_t buf_size, jint n) { + const char* str = int_name_near(max_jint, "maxint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = int_name_near(min_jint, "minint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + os::snprintf_checked(buf, buf_size, INT32_FORMAT, n); + return buf; +} + +const char* TypeIntHelper::uintname(char* buf, size_t buf_size, juint n) { + const char* str = int_name_near(max_juint, "maxuint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = int_name_near(max_jint, "maxint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + os::snprintf_checked(buf, buf_size, UINT32_FORMAT"u", n); + return buf; +} + +const char* TypeIntHelper::longname(char* buf, size_t buf_size, jlong n) { + const char* str = int_name_near(max_jlong, "maxlong", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = int_name_near(min_jlong, "minlong", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = int_name_near(max_juint, "maxuint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = int_name_near(max_jint, "maxint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = int_name_near(min_jint, "minint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + os::snprintf_checked(buf, buf_size, JLONG_FORMAT, n); + return buf; +} + +const char* TypeIntHelper::ulongname(char* buf, size_t buf_size, julong n) { + const char* str = int_name_near(max_julong, "maxulong", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = int_name_near(max_jlong, "maxlong", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = int_name_near(max_juint, "maxuint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = int_name_near(max_jint, "maxint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + os::snprintf_checked(buf, buf_size, JULONG_FORMAT"u", n); + return buf; +} + +template +const char* TypeIntHelper::bitname(char* buf, size_t buf_size, U zeros, U ones) { + constexpr juint W = sizeof(U) * 8; + + if (buf_size < W + 1) { + return "#####"; + } + + for (juint i = 0; i < W; i++) { + U mask = U(1) << (W - 1 - i); + if ((zeros & mask) != 0) { + buf[i] = '0'; + } else if ((ones & mask) != 0) { + buf[i] = '1'; + } else { + buf[i] = '*'; + } + } + buf[W] = 0; + return buf; +} +template const char* TypeIntHelper::bitname(char* buf, size_t buf_size, juint zeros, juint ones); +template const char* TypeIntHelper::bitname(char* buf, size_t buf_size, julong zeros, julong ones); + +void TypeIntHelper::int_type_dump(const TypeInt* t, outputStream* st, bool verbose) { + char buf1[40], buf2[40], buf3[40], buf4[40], buf5[40]; + if (int_type_is_equal(t, TypeInt::INT)) { + st->print("int"); + } else if (t->is_con()) { + st->print("int:%s", intname(buf1, sizeof(buf1), t->get_con())); + } else if (int_type_is_equal(t, TypeInt::BOOL)) { + st->print("bool"); + } else if (int_type_is_equal(t, TypeInt::BYTE)) { + st->print("byte"); + } else if (int_type_is_equal(t, TypeInt::CHAR)) { + st->print("char"); + } else if (int_type_is_equal(t, TypeInt::SHORT)) { + st->print("short"); + } else { + if (verbose) { + st->print("int:%s..%s, %s..%s, %s", + intname(buf1, sizeof(buf1), t->_lo), intname(buf2, sizeof(buf2), t->_hi), + uintname(buf3, sizeof(buf3), t->_ulo), uintname(buf4, sizeof(buf4), t->_uhi), + bitname(buf5, sizeof(buf5), t->_bits._zeros, t->_bits._ones)); + } else { + if (t->_lo >= 0) { + if (t->_hi == max_jint) { + st->print("int:>=%s", intname(buf1, sizeof(buf1), t->_lo)); + } else { + st->print("int:%s..%s", intname(buf1, sizeof(buf1), t->_lo), intname(buf2, sizeof(buf2), t->_hi)); + } + } else if (t->_hi < 0) { + if (t->_lo == min_jint) { + st->print("int:<=%s", intname(buf1, sizeof(buf1), t->_hi)); + } else { + st->print("int:%s..%s", intname(buf1, sizeof(buf1), t->_lo), intname(buf2, sizeof(buf2), t->_hi)); + } + } else { + st->print("int:%s..%s, %s..%s", + intname(buf1, sizeof(buf1), t->_lo), intname(buf2, sizeof(buf2), t->_hi), + uintname(buf3, sizeof(buf3), t->_ulo), uintname(buf4, sizeof(buf4), t->_uhi)); + } + + } + } + + if (t->_widen > 0 && t != TypeInt::INT) { + st->print(", widen: %d", t->_widen); + } +} + +void TypeIntHelper::int_type_dump(const TypeLong* t, outputStream* st, bool verbose) { + char buf1[80], buf2[80], buf3[80], buf4[80], buf5[80]; + if (int_type_is_equal(t, TypeLong::LONG)) { + st->print("long"); + } else if (t->is_con()) { + st->print("long:%s", longname(buf1, sizeof(buf1), t->get_con())); + } else { + if (verbose) { + st->print("long:%s..%s, %s..%s, bits:%s", + longname(buf1, sizeof(buf1), t->_lo), longname(buf2,sizeof(buf2), t-> _hi), + ulongname(buf3, sizeof(buf3), t->_ulo), ulongname(buf4, sizeof(buf4), t->_uhi), + bitname(buf5, sizeof(buf5), t->_bits._zeros, t->_bits._ones)); + } else { + if (t->_lo >= 0) { + if (t->_hi == max_jint) { + st->print("long:>=%s", longname(buf1, sizeof(buf1), t->_lo)); + } else { + st->print("long:%s..%s", longname(buf1, sizeof(buf1), t->_lo), longname(buf2, sizeof(buf2), t->_hi)); + } + } else if (t->_hi < 0) { + if (t->_lo == min_jint) { + st->print("long:<=%s", longname(buf1, sizeof(buf1), t->_hi)); + } else { + st->print("long:%s..%s", longname(buf1, sizeof(buf1), t->_lo), longname(buf2, sizeof(buf2), t->_hi)); + } + } else { + st->print("long:%s..%s, %s..%s", + longname(buf1, sizeof(buf1), t->_lo), longname(buf2,sizeof(buf2), t-> _hi), + ulongname(buf3, sizeof(buf3), t->_ulo), ulongname(buf4, sizeof(buf4), t->_uhi)); + } + } + } + + if (t->_widen > 0 && t != TypeLong::LONG) { + st->print(", widen: %d", t->_widen); + } +} +#endif // PRODUCT diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp new file mode 100644 index 0000000000000..84891d5e31188 --- /dev/null +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_OPTO_RANGEINFERENCE_HPP +#define SHARE_OPTO_RANGEINFERENCE_HPP + +#include "utilities/globalDefinitions.hpp" +#include + +class outputStream; +class Type; +class TypeInt; +class TypeLong; + +// A simple range in the signed or unsigned domain +template +class RangeInt { +public: + T _lo; + T _hi; +}; + +/** + * Bits that are known to be 0 or 1. A value v satisfies this constraint iff + * (v & zeros) == 0 && (v & ones) == ones. I.e, any bit that is 1 in zeros must + * be 0 in v, and any bit that is 1 in ones must be 1 in v. + * + * I.e, for each bit position from 0 to sizeof(U) - 1, the corresponding bits + * of zeros, ones and the allowed bit in v must follow: + * + * zeros ones allowed bits + * 0 0 0 or 1 + * 1 0 0 + * 0 1 1 + * 1 1 none (impossible state) + * + * E.g: + * zeros: 00110100 + * ones: 10000010 + * Then: 10001010 would satisfy the bit constraints + * while: 10011000 would not since the bit at the 4th position violates + * zeros and the bit at the 7th position violates ones + * + * A KnownBits is sane if there is no position at which a bit must be both set + * and unset at the same time. That is (zeros & ones) == 0. + */ +template +class KnownBits { + static_assert(U(-1) > U(0), "bit info should be unsigned"); + +public: + U _zeros; + U _ones; + + bool is_satisfied_by(U v) const { + return (v & _zeros) == U(0) && (v & _ones) == _ones; + } +}; + +// All the information needed to construct a TypeInt/TypeLong, the constraints +// here may be arbitrary and need to be canonicalized to construct a +// TypeInt/TypeLong +template +class TypeIntPrototype { +public: + static_assert(S(-1) < S(0), ""); + static_assert(U(-1) > U(0), ""); + static_assert(sizeof(S) == sizeof(U), ""); + + RangeInt _srange; + RangeInt _urange; + KnownBits _bits; + +private: + friend class TypeInt; + friend class TypeLong; + + template + friend void test_canonicalize_constraints_exhaustive(); + + template + friend void test_canonicalize_constraints_simple(); + + template + friend void test_canonicalize_constraints_random(); + + // A canonicalized version of a TypeIntPrototype, if the prototype represents + // an empty type, _present is false, otherwise, _data is canonical + class CanonicalizedTypeIntPrototype { + public: + bool _present; // whether this is an empty set + TypeIntPrototype _data; + + bool empty() const { + return !_present; + } + + static CanonicalizedTypeIntPrototype make_empty() { + return {false, {}}; + } + }; + + CanonicalizedTypeIntPrototype canonicalize_constraints() const; + int normalize_widen(int w) const; +#ifdef ASSERT + bool contains(S v) const; + void verify_constraints() const; +#endif // ASSERT +}; + +// Various helper functions for TypeInt/TypeLong operations +class TypeIntHelper { +public: + // Calculate the cardinality of a TypeInt/TypeLong ignoring the bits + // constraints, the return value is the cardinality minus 1 to not overflow + // with the bottom type + template + static U cardinality_from_bounds(const RangeInt& srange, const RangeInt& urange) { + static_assert(S(-1) < S(0), ""); + static_assert(U(-1) > U(0), ""); + static_assert(sizeof(S) == sizeof(U), ""); + + if (U(srange._lo) == urange._lo) { + // srange is the same as urange + assert(U(srange._hi) == urange._hi, ""); + // The cardinality is (hi - lo + 1), we return the result minus 1 + return urange._hi - urange._lo; + } + + // srange intersects with urange in 2 intervals [srange._lo, urange._hi] + // and [urange._lo, srange._hi] + // The cardinality is (uhi - lo + 1) + (hi - ulo + 1), we return the result + // minus 1 + return (urange._hi - U(srange._lo)) + (U(srange._hi) - urange._lo) + U(1); + } + + template + static const Type* int_type_xmeet(const CT* i1, const Type* t2); + + template + static bool int_type_is_equal(const CT* t1, const CT* t2) { + return t1->_lo == t2->_lo && t1->_hi == t2->_hi && + t1->_ulo == t2->_ulo && t1->_uhi == t2->_uhi && + t1->_bits._zeros == t2->_bits._zeros && t1->_bits._ones == t2->_bits._ones; + } + + template + static bool int_type_is_subset(const CT* super, const CT* sub) { + return super->_lo <= sub->_lo && super->_hi >= sub->_hi && + super->_ulo <= sub->_ulo && super->_uhi >= sub->_uhi && + // All bits that are known in super must also be known to be the same + // value in sub, &~ (and not) is the same as a set subtraction on bit + // sets + (super->_bits._zeros &~ sub->_bits._zeros) == 0 && (super->_bits._ones &~ sub->_bits._ones) == 0; + } + + template + static const Type* int_type_widen(const CT* new_type, const CT* old_type, const CT* limit_type); + + template + static const Type* int_type_narrow(const CT* new_type, const CT* old_type); + +#ifndef PRODUCT + static const char* intname(char* buf, size_t buf_size, jint n); + static const char* uintname(char* buf, size_t buf_size, juint n); + static const char* longname(char* buf, size_t buf_size, jlong n); + static const char* ulongname(char* buf, size_t buf_size, julong n); + + template + static const char* bitname(char* buf, size_t buf_size, U zeros, U ones); + + static void int_type_dump(const TypeInt* t, outputStream* st, bool verbose); + static void int_type_dump(const TypeLong* t, outputStream* st, bool verbose); +#endif // PRODUCT +}; + +#endif // SHARE_OPTO_RANGEINFERENCE_HPP diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 163f94ee95933..050297db15328 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -40,6 +40,7 @@ #include "opto/matcher.hpp" #include "opto/node.hpp" #include "opto/opcodes.hpp" +#include "opto/rangeinference.hpp" #include "opto/runtime.hpp" #include "opto/type.hpp" #include "utilities/checkedCast.hpp" @@ -428,7 +429,6 @@ int Type::uhash( const Type *const t ) { return (int)t->hash(); } -#define SMALLINT ((juint)3) // a value too insignificant to consider widening #define POSITIVE_INFINITE_F 0x7f800000 // hex representation for IEEE 754 single precision positive infinite #define POSITIVE_INFINITE_D 0x7ff0000000000000 // hex representation for IEEE 754 double precision positive infinite @@ -487,44 +487,47 @@ void Type::Initialize_shared(Compile* current) { TypeInt::MAX = TypeInt::make(max_jint); // Int MAX TypeInt::MIN = TypeInt::make(min_jint); // Int MIN - TypeInt::MINUS_1 = TypeInt::make(-1); // -1 - TypeInt::ZERO = TypeInt::make( 0); // 0 - TypeInt::ONE = TypeInt::make( 1); // 1 - TypeInt::BOOL = TypeInt::make(0,1, WidenMin); // 0 or 1, FALSE or TRUE. - TypeInt::CC = TypeInt::make(-1, 1, WidenMin); // -1, 0 or 1, condition codes - TypeInt::CC_LT = TypeInt::make(-1,-1, WidenMin); // == TypeInt::MINUS_1 - TypeInt::CC_GT = TypeInt::make( 1, 1, WidenMin); // == TypeInt::ONE - TypeInt::CC_EQ = TypeInt::make( 0, 0, WidenMin); // == TypeInt::ZERO - TypeInt::CC_LE = TypeInt::make(-1, 0, WidenMin); - TypeInt::CC_GE = TypeInt::make( 0, 1, WidenMin); // == TypeInt::BOOL - TypeInt::BYTE = TypeInt::make(-128,127, WidenMin); // Bytes - TypeInt::UBYTE = TypeInt::make(0, 255, WidenMin); // Unsigned Bytes - TypeInt::CHAR = TypeInt::make(0,65535, WidenMin); // Java chars - TypeInt::SHORT = TypeInt::make(-32768,32767, WidenMin); // Java shorts - TypeInt::POS = TypeInt::make(0,max_jint, WidenMin); // Non-neg values - TypeInt::POS1 = TypeInt::make(1,max_jint, WidenMin); // Positive values - TypeInt::INT = TypeInt::make(min_jint,max_jint, WidenMax); // 32-bit integers - TypeInt::SYMINT = TypeInt::make(-max_jint,max_jint,WidenMin); // symmetric range - TypeInt::TYPE_DOMAIN = TypeInt::INT; + TypeInt::MINUS_1 = TypeInt::make(-1); // -1 + TypeInt::ZERO = TypeInt::make( 0); // 0 + TypeInt::ONE = TypeInt::make( 1); // 1 + TypeInt::BOOL = TypeInt::make( 0, 1, WidenMin); // 0 or 1, FALSE or TRUE. + TypeInt::CC = TypeInt::make(-1, 1, WidenMin); // -1, 0 or 1, condition codes + TypeInt::CC_LT = TypeInt::make(-1,-1, WidenMin); // == TypeInt::MINUS_1 + TypeInt::CC_GT = TypeInt::make( 1, 1, WidenMin); // == TypeInt::ONE + TypeInt::CC_EQ = TypeInt::make( 0, 0, WidenMin); // == TypeInt::ZERO + TypeInt::CC_NE = TypeInt::make_or_top(TypeIntPrototype{{-1, 1}, {1, max_juint}, {0, 1}}, WidenMin)->is_int(); + TypeInt::CC_LE = TypeInt::make(-1, 0, WidenMin); + TypeInt::CC_GE = TypeInt::make( 0, 1, WidenMin); // == TypeInt::BOOL + TypeInt::BYTE = TypeInt::make(-128, 127, WidenMin); // Bytes + TypeInt::UBYTE = TypeInt::make(0, 255, WidenMin); // Unsigned Bytes + TypeInt::CHAR = TypeInt::make(0, 65535, WidenMin); // Java chars + TypeInt::SHORT = TypeInt::make(-32768, 32767, WidenMin); // Java shorts + TypeInt::NON_ZERO = TypeInt::make_or_top(TypeIntPrototype{{min_jint, max_jint}, {1, max_juint}, {0, 0}}, WidenMin)->is_int(); + TypeInt::POS = TypeInt::make(0, max_jint, WidenMin); // Non-neg values + TypeInt::POS1 = TypeInt::make(1, max_jint, WidenMin); // Positive values + TypeInt::INT = TypeInt::make(min_jint, max_jint, WidenMax); // 32-bit integers + TypeInt::SYMINT = TypeInt::make(-max_jint, max_jint, WidenMin); // symmetric range + TypeInt::TYPE_DOMAIN = TypeInt::INT; // CmpL is overloaded both as the bytecode computation returning - // a trinary (-1,0,+1) integer result AND as an efficient long + // a trinary (-1, 0, +1) integer result AND as an efficient long // compare returning optimizer ideal-type flags. - assert( TypeInt::CC_LT == TypeInt::MINUS_1, "types must match for CmpL to work" ); - assert( TypeInt::CC_GT == TypeInt::ONE, "types must match for CmpL to work" ); - assert( TypeInt::CC_EQ == TypeInt::ZERO, "types must match for CmpL to work" ); - assert( TypeInt::CC_GE == TypeInt::BOOL, "types must match for CmpL to work" ); - assert( (juint)(TypeInt::CC->_hi - TypeInt::CC->_lo) <= SMALLINT, "CC is truly small"); - - TypeLong::MAX = TypeLong::make(max_jlong); // Long MAX - TypeLong::MIN = TypeLong::make(min_jlong); // Long MIN - TypeLong::MINUS_1 = TypeLong::make(-1); // -1 - TypeLong::ZERO = TypeLong::make( 0); // 0 - TypeLong::ONE = TypeLong::make( 1); // 1 - TypeLong::POS = TypeLong::make(0,max_jlong, WidenMin); // Non-neg values - TypeLong::LONG = TypeLong::make(min_jlong,max_jlong,WidenMax); // 64-bit integers - TypeLong::INT = TypeLong::make((jlong)min_jint,(jlong)max_jint,WidenMin); - TypeLong::UINT = TypeLong::make(0,(jlong)max_juint,WidenMin); - TypeLong::TYPE_DOMAIN = TypeLong::LONG; + assert(TypeInt::CC_LT == TypeInt::MINUS_1, "types must match for CmpL to work" ); + assert(TypeInt::CC_GT == TypeInt::ONE, "types must match for CmpL to work" ); + assert(TypeInt::CC_EQ == TypeInt::ZERO, "types must match for CmpL to work" ); + assert(TypeInt::CC_GE == TypeInt::BOOL, "types must match for CmpL to work" ); + + TypeLong::MAX = TypeLong::make(max_jlong); // Long MAX + TypeLong::MIN = TypeLong::make(min_jlong); // Long MIN + TypeLong::MINUS_1 = TypeLong::make(-1); // -1 + TypeLong::ZERO = TypeLong::make( 0); // 0 + TypeLong::ONE = TypeLong::make( 1); // 1 + TypeLong::NON_ZERO = TypeLong::make_or_top(TypeIntPrototype{{min_jlong, max_jlong}, {1, max_julong}, {0, 0}}, WidenMin)->is_long(); + TypeLong::POS = TypeLong::make(0, max_jlong, WidenMin); // Non-neg values + TypeLong::NEG = TypeLong::make(min_jlong, -1, WidenMin); + TypeLong::LONG = TypeLong::make(min_jlong, max_jlong, WidenMax); // 64-bit integers + TypeLong::INT = TypeLong::make((jlong)min_jint, (jlong)max_jint,WidenMin); + TypeLong::UINT = TypeLong::make(0, (jlong)max_juint, WidenMin); + TypeLong::TYPE_DOMAIN = TypeLong::LONG; const Type **fboth =(const Type**)shared_type_arena->AmallocWords(2*sizeof(Type*)); fboth[0] = Type::CONTROL; @@ -1745,218 +1748,124 @@ const TypeInteger* TypeInteger::minus_1(BasicType bt) { //============================================================================= // Convenience common pre-built types. -const TypeInt *TypeInt::MAX; // INT_MAX -const TypeInt *TypeInt::MIN; // INT_MIN -const TypeInt *TypeInt::MINUS_1;// -1 -const TypeInt *TypeInt::ZERO; // 0 -const TypeInt *TypeInt::ONE; // 1 -const TypeInt *TypeInt::BOOL; // 0 or 1, FALSE or TRUE. -const TypeInt *TypeInt::CC; // -1,0 or 1, condition codes -const TypeInt *TypeInt::CC_LT; // [-1] == MINUS_1 -const TypeInt *TypeInt::CC_GT; // [1] == ONE -const TypeInt *TypeInt::CC_EQ; // [0] == ZERO -const TypeInt *TypeInt::CC_LE; // [-1,0] -const TypeInt *TypeInt::CC_GE; // [0,1] == BOOL (!) -const TypeInt *TypeInt::BYTE; // Bytes, -128 to 127 -const TypeInt *TypeInt::UBYTE; // Unsigned Bytes, 0 to 255 -const TypeInt *TypeInt::CHAR; // Java chars, 0-65535 -const TypeInt *TypeInt::SHORT; // Java shorts, -32768-32767 -const TypeInt *TypeInt::POS; // Positive 32-bit integers or zero -const TypeInt *TypeInt::POS1; // Positive 32-bit integers -const TypeInt *TypeInt::INT; // 32-bit integers -const TypeInt *TypeInt::SYMINT; // symmetric range [-max_jint..max_jint] -const TypeInt *TypeInt::TYPE_DOMAIN; // alias for TypeInt::INT - -//------------------------------TypeInt---------------------------------------- -TypeInt::TypeInt( jint lo, jint hi, int w ) : TypeInteger(Int, w), _lo(lo), _hi(hi) { -} +const TypeInt* TypeInt::MAX; // INT_MAX +const TypeInt* TypeInt::MIN; // INT_MIN +const TypeInt* TypeInt::MINUS_1;// -1 +const TypeInt* TypeInt::ZERO; // 0 +const TypeInt* TypeInt::ONE; // 1 +const TypeInt* TypeInt::BOOL; // 0 or 1, FALSE or TRUE. +const TypeInt* TypeInt::CC; // -1,0 or 1, condition codes +const TypeInt* TypeInt::CC_LT; // [-1] == MINUS_1 +const TypeInt* TypeInt::CC_GT; // [1] == ONE +const TypeInt* TypeInt::CC_EQ; // [0] == ZERO +const TypeInt* TypeInt::CC_NE; +const TypeInt* TypeInt::CC_LE; // [-1,0] +const TypeInt* TypeInt::CC_GE; // [0,1] == BOOL (!) +const TypeInt* TypeInt::BYTE; // Bytes, -128 to 127 +const TypeInt* TypeInt::UBYTE; // Unsigned Bytes, 0 to 255 +const TypeInt* TypeInt::CHAR; // Java chars, 0-65535 +const TypeInt* TypeInt::SHORT; // Java shorts, -32768-32767 +const TypeInt* TypeInt::NON_ZERO; +const TypeInt* TypeInt::POS; // Positive 32-bit integers or zero +const TypeInt* TypeInt::POS1; // Positive 32-bit integers +const TypeInt* TypeInt::INT; // 32-bit integers +const TypeInt* TypeInt::SYMINT; // symmetric range [-max_jint..max_jint] +const TypeInt* TypeInt::TYPE_DOMAIN; // alias for TypeInt::INT -//------------------------------make------------------------------------------- -const TypeInt *TypeInt::make( jint lo ) { - return (TypeInt*)(new TypeInt(lo,lo,WidenMin))->hashcons(); +TypeInt::TypeInt(const TypeIntPrototype& t, int widen, bool dual) + : TypeInteger(Int, t.normalize_widen(widen), dual), _lo(t._srange._lo), _hi(t._srange._hi), + _ulo(t._urange._lo), _uhi(t._urange._hi), _bits(t._bits) { + DEBUG_ONLY(t.verify_constraints()); } -static int normalize_int_widen( jint lo, jint hi, int w ) { - // Certain normalizations keep us sane when comparing types. - // The 'SMALLINT' covers constants and also CC and its relatives. - if (lo <= hi) { - if (((juint)hi - lo) <= SMALLINT) w = Type::WidenMin; - if (((juint)hi - lo) >= max_juint) w = Type::WidenMax; // TypeInt::INT - } else { - if (((juint)lo - hi) <= SMALLINT) w = Type::WidenMin; - if (((juint)lo - hi) >= max_juint) w = Type::WidenMin; // dual TypeInt::INT +const Type* TypeInt::make_or_top(const TypeIntPrototype& t, int widen, bool dual) { + auto canonicalized_t = t.canonicalize_constraints(); + if (canonicalized_t.empty()) { + return dual ? Type::BOTTOM : Type::TOP; } - return w; + return (new TypeInt(canonicalized_t._data, widen, dual))->hashcons()->is_int(); } -const TypeInt *TypeInt::make( jint lo, jint hi, int w ) { - w = normalize_int_widen(lo, hi, w); - return (TypeInt*)(new TypeInt(lo,hi,w))->hashcons(); +const TypeInt* TypeInt::make(jint con) { + juint ucon = con; + return (new TypeInt(TypeIntPrototype{{con, con}, {ucon, ucon}, {~ucon, ucon}}, + WidenMin, false))->hashcons()->is_int(); } -//------------------------------meet------------------------------------------- -// Compute the MEET of two types. It returns a new Type representation object -// with reference count equal to the number of Types pointing at it. -// Caller should wrap a Types around it. -const Type *TypeInt::xmeet( const Type *t ) const { - // Perform a fast test for common case; meeting the same types together. - if( this == t ) return this; // Meeting same type? - - // Currently "this->_base" is a TypeInt - switch (t->base()) { // Switch on original type - case AnyPtr: // Mixing with oops happens when javac - case RawPtr: // reuses local variables - case OopPtr: - case InstPtr: - case AryPtr: - case MetadataPtr: - case KlassPtr: - case InstKlassPtr: - case AryKlassPtr: - case NarrowOop: - case NarrowKlass: - case Long: - case HalfFloatTop: - case HalfFloatCon: - case HalfFloatBot: - case FloatTop: - case FloatCon: - case FloatBot: - case DoubleTop: - case DoubleCon: - case DoubleBot: - case Bottom: // Ye Olde Default - return Type::BOTTOM; - default: // All else is a mistake - typerr(t); - case Top: // No change - return this; - case Int: // Int vs Int? - break; - } - - // Expand covered set - const TypeInt *r = t->is_int(); - return make( MIN2(_lo,r->_lo), MAX2(_hi,r->_hi), MAX2(_widen,r->_widen) ); +const TypeInt* TypeInt::make(jint lo, jint hi, int widen) { + assert(lo <= hi, "must be legal bounds"); + return make_or_top(TypeIntPrototype{{lo, hi}, {0, max_juint}, {0, 0}}, widen)->is_int(); } -//------------------------------xdual------------------------------------------ -// Dual: reverse hi & lo; flip widen -const Type *TypeInt::xdual() const { - int w = normalize_int_widen(_hi,_lo, WidenMax-_widen); - return new TypeInt(_hi,_lo,w); -} - -//------------------------------widen------------------------------------------ -// Only happens for optimistic top-down optimizations. -const Type *TypeInt::widen( const Type *old, const Type* limit ) const { - // Coming from TOP or such; no widening - if( old->base() != Int ) return this; - const TypeInt *ot = old->is_int(); - - // If new guy is equal to old guy, no widening - if( _lo == ot->_lo && _hi == ot->_hi ) - return old; - - // If new guy contains old, then we widened - if( _lo <= ot->_lo && _hi >= ot->_hi ) { - // New contains old - // If new guy is already wider than old, no widening - if( _widen > ot->_widen ) return this; - // If old guy was a constant, do not bother - if (ot->_lo == ot->_hi) return this; - // Now widen new guy. - // Check for widening too far - if (_widen == WidenMax) { - int max = max_jint; - int min = min_jint; - if (limit->isa_int()) { - max = limit->is_int()->_hi; - min = limit->is_int()->_lo; - } - if (min < _lo && _hi < max) { - // If neither endpoint is extremal yet, push out the endpoint - // which is closer to its respective limit. - if (_lo >= 0 || // easy common case - ((juint)_lo - min) >= ((juint)max - _hi)) { - // Try to widen to an unsigned range type of 31 bits: - return make(_lo, max, WidenMax); - } else { - return make(min, _hi, WidenMax); - } - } - return TypeInt::INT; - } - // Returned widened new guy - return make(_lo,_hi,_widen+1); - } - - // If old guy contains new, then we probably widened too far & dropped to - // bottom. Return the wider fellow. - if ( ot->_lo <= _lo && ot->_hi >= _hi ) - return old; +const Type* TypeInt::make_or_top(const TypeIntPrototype& t, int widen) { + return make_or_top(t, widen, false); +} - //fatal("Integer value range is not subset"); - //return this; - return TypeInt::INT; +bool TypeInt::contains(jint i) const { + assert(!_is_dual, "dual types should only be used for join calculation"); + juint u = i; + return i >= _lo && i <= _hi && + u >= _ulo && u <= _uhi && + _bits.is_satisfied_by(u); } -//------------------------------narrow--------------------------------------- -// Only happens for pessimistic optimizations. -const Type *TypeInt::narrow( const Type *old ) const { - if (_lo >= _hi) return this; // already narrow enough - if (old == nullptr) return this; - const TypeInt* ot = old->isa_int(); - if (ot == nullptr) return this; - jint olo = ot->_lo; - jint ohi = ot->_hi; +bool TypeInt::contains(const TypeInt* t) const { + assert(!_is_dual && !t->_is_dual, "dual types should only be used for join calculation"); + return TypeIntHelper::int_type_is_subset(this, t); +} - // If new guy is equal to old guy, no narrowing - if (_lo == olo && _hi == ohi) return old; +const Type* TypeInt::xmeet(const Type* t) const { + return TypeIntHelper::int_type_xmeet(this, t); +} - // If old guy was maximum range, allow the narrowing - if (olo == min_jint && ohi == max_jint) return this; +const Type* TypeInt::xdual() const { + return new TypeInt(TypeIntPrototype{{_lo, _hi}, {_ulo, _uhi}, _bits}, + _widen, !_is_dual); +} - if (_lo < olo || _hi > ohi) - return this; // doesn't narrow; pretty weird +const Type* TypeInt::widen(const Type* old, const Type* limit) const { + assert(!_is_dual, "dual types should only be used for join calculation"); + return TypeIntHelper::int_type_widen(this, old->isa_int(), limit->isa_int()); +} - // The new type narrows the old type, so look for a "death march". - // See comments on PhaseTransform::saturate. - juint nrange = (juint)_hi - _lo; - juint orange = (juint)ohi - olo; - if (nrange < max_juint - 1 && nrange > (orange >> 1) + (SMALLINT*2)) { - // Use the new type only if the range shrinks a lot. - // We do not want the optimizer computing 2^31 point by point. - return old; +const Type* TypeInt::narrow(const Type* old) const { + assert(!_is_dual, "dual types should only be used for join calculation"); + if (old == nullptr) { + return this; } - return this; + return TypeIntHelper::int_type_narrow(this, old->isa_int()); } //-----------------------------filter------------------------------------------ -const Type *TypeInt::filter_helper(const Type *kills, bool include_speculative) const { +const Type* TypeInt::filter_helper(const Type* kills, bool include_speculative) const { + assert(!_is_dual, "dual types should only be used for join calculation"); const TypeInt* ft = join_helper(kills, include_speculative)->isa_int(); - if (ft == nullptr || ft->empty()) + if (ft == nullptr) { return Type::TOP; // Canonical empty value + } + assert(!ft->_is_dual, "dual types should only be used for join calculation"); if (ft->_widen < this->_widen) { // Do not allow the value of kill->_widen to affect the outcome. // The widen bits must be allowed to run freely through the graph. - ft = TypeInt::make(ft->_lo, ft->_hi, this->_widen); + return (new TypeInt(TypeIntPrototype{{ft->_lo, ft->_hi}, {ft->_ulo, ft->_uhi}, ft->_bits}, + this->_widen, false))->hashcons(); } return ft; } //------------------------------eq--------------------------------------------- // Structural equality check for Type representations -bool TypeInt::eq( const Type *t ) const { - const TypeInt *r = t->is_int(); // Handy access - return r->_lo == _lo && r->_hi == _hi && r->_widen == _widen; +bool TypeInt::eq(const Type* t) const { + const TypeInt* r = t->is_int(); + return TypeIntHelper::int_type_is_equal(this, r) && _widen == r->_widen && _is_dual == r->_is_dual; } //------------------------------hash------------------------------------------- // Type-specific hashing function. uint TypeInt::hash(void) const { - return (uint)_lo + (uint)_hi + (uint)_widen + (uint)Type::Int; + return (uint)_lo + (uint)_hi + (uint)_ulo + (uint)_uhi + + (uint)_bits._zeros + (uint)_bits._ones + (uint)_widen + (uint)_is_dual + (uint)Type::Int; } //------------------------------is_finite-------------------------------------- @@ -1965,267 +1874,126 @@ bool TypeInt::is_finite() const { return true; } -//------------------------------dump2------------------------------------------ -// Dump TypeInt -#ifndef PRODUCT -static const char* intname(char* buf, size_t buf_size, jint n) { - if (n == min_jint) - return "min"; - else if (n < min_jint + 10000) - os::snprintf_checked(buf, buf_size, "min+" INT32_FORMAT, n - min_jint); - else if (n == max_jint) - return "max"; - else if (n > max_jint - 10000) - os::snprintf_checked(buf, buf_size, "max-" INT32_FORMAT, max_jint - n); - else - os::snprintf_checked(buf, buf_size, INT32_FORMAT, n); - return buf; -} - -void TypeInt::dump2( Dict &d, uint depth, outputStream *st ) const { - char buf[40], buf2[40]; - if (_lo == min_jint && _hi == max_jint) - st->print("int"); - else if (is_con()) - st->print("int:%s", intname(buf, sizeof(buf), get_con())); - else if (_lo == BOOL->_lo && _hi == BOOL->_hi) - st->print("bool"); - else if (_lo == BYTE->_lo && _hi == BYTE->_hi) - st->print("byte"); - else if (_lo == CHAR->_lo && _hi == CHAR->_hi) - st->print("char"); - else if (_lo == SHORT->_lo && _hi == SHORT->_hi) - st->print("short"); - else if (_hi == max_jint) - st->print("int:>=%s", intname(buf, sizeof(buf), _lo)); - else if (_lo == min_jint) - st->print("int:<=%s", intname(buf, sizeof(buf), _hi)); - else - st->print("int:%s..%s", intname(buf, sizeof(buf), _lo), intname(buf2, sizeof(buf2), _hi)); - - if (_widen != 0 && this != TypeInt::INT) - st->print(":%.*s", _widen, "wwww"); -} -#endif - //------------------------------singleton-------------------------------------- // TRUE if Type is a singleton type, FALSE otherwise. Singletons are simple // constants. bool TypeInt::singleton(void) const { - return _lo >= _hi; + return _lo == _hi; } bool TypeInt::empty(void) const { - return _lo > _hi; + return false; } //============================================================================= // Convenience common pre-built types. -const TypeLong *TypeLong::MAX; -const TypeLong *TypeLong::MIN; -const TypeLong *TypeLong::MINUS_1;// -1 -const TypeLong *TypeLong::ZERO; // 0 -const TypeLong *TypeLong::ONE; // 1 -const TypeLong *TypeLong::POS; // >=0 -const TypeLong *TypeLong::LONG; // 64-bit integers -const TypeLong *TypeLong::INT; // 32-bit subrange -const TypeLong *TypeLong::UINT; // 32-bit unsigned subrange -const TypeLong *TypeLong::TYPE_DOMAIN; // alias for TypeLong::LONG - -//------------------------------TypeLong--------------------------------------- -TypeLong::TypeLong(jlong lo, jlong hi, int w) : TypeInteger(Long, w), _lo(lo), _hi(hi) { -} +const TypeLong* TypeLong::MAX; +const TypeLong* TypeLong::MIN; +const TypeLong* TypeLong::MINUS_1;// -1 +const TypeLong* TypeLong::ZERO; // 0 +const TypeLong* TypeLong::ONE; // 1 +const TypeLong* TypeLong::NON_ZERO; +const TypeLong* TypeLong::POS; // >=0 +const TypeLong* TypeLong::NEG; +const TypeLong* TypeLong::LONG; // 64-bit integers +const TypeLong* TypeLong::INT; // 32-bit subrange +const TypeLong* TypeLong::UINT; // 32-bit unsigned subrange +const TypeLong* TypeLong::TYPE_DOMAIN; // alias for TypeLong::LONG -//------------------------------make------------------------------------------- -const TypeLong *TypeLong::make( jlong lo ) { - return (TypeLong*)(new TypeLong(lo,lo,WidenMin))->hashcons(); +TypeLong::TypeLong(const TypeIntPrototype& t, int widen, bool dual) + : TypeInteger(Long, t.normalize_widen(widen), dual), _lo(t._srange._lo), _hi(t._srange._hi), + _ulo(t._urange._lo), _uhi(t._urange._hi), _bits(t._bits) { + DEBUG_ONLY(t.verify_constraints()); } -static int normalize_long_widen( jlong lo, jlong hi, int w ) { - // Certain normalizations keep us sane when comparing types. - // The 'SMALLINT' covers constants. - if (lo <= hi) { - if (((julong)hi - lo) <= SMALLINT) w = Type::WidenMin; - if (((julong)hi - lo) >= max_julong) w = Type::WidenMax; // TypeLong::LONG - } else { - if (((julong)lo - hi) <= SMALLINT) w = Type::WidenMin; - if (((julong)lo - hi) >= max_julong) w = Type::WidenMin; // dual TypeLong::LONG +const Type* TypeLong::make_or_top(const TypeIntPrototype& t, int widen, bool dual) { + auto canonicalized_t = t.canonicalize_constraints(); + if (canonicalized_t.empty()) { + return dual ? Type::BOTTOM : Type::TOP; } - return w; + return (new TypeLong(canonicalized_t._data, widen, dual))->hashcons()->is_long(); } -const TypeLong *TypeLong::make( jlong lo, jlong hi, int w ) { - w = normalize_long_widen(lo, hi, w); - return (TypeLong*)(new TypeLong(lo,hi,w))->hashcons(); +const TypeLong* TypeLong::make(jlong con) { + julong ucon = con; + return (new TypeLong(TypeIntPrototype{{con, con}, {ucon, ucon}, {~ucon, ucon}}, + WidenMin, false))->hashcons()->is_long(); } - -//------------------------------meet------------------------------------------- -// Compute the MEET of two types. It returns a new Type representation object -// with reference count equal to the number of Types pointing at it. -// Caller should wrap a Types around it. -const Type *TypeLong::xmeet( const Type *t ) const { - // Perform a fast test for common case; meeting the same types together. - if( this == t ) return this; // Meeting same type? - - // Currently "this->_base" is a TypeLong - switch (t->base()) { // Switch on original type - case AnyPtr: // Mixing with oops happens when javac - case RawPtr: // reuses local variables - case OopPtr: - case InstPtr: - case AryPtr: - case MetadataPtr: - case KlassPtr: - case InstKlassPtr: - case AryKlassPtr: - case NarrowOop: - case NarrowKlass: - case Int: - case HalfFloatTop: - case HalfFloatCon: - case HalfFloatBot: - case FloatTop: - case FloatCon: - case FloatBot: - case DoubleTop: - case DoubleCon: - case DoubleBot: - case Bottom: // Ye Olde Default - return Type::BOTTOM; - default: // All else is a mistake - typerr(t); - case Top: // No change - return this; - case Long: // Long vs Long? - break; - } - - // Expand covered set - const TypeLong *r = t->is_long(); // Turn into a TypeLong - return make( MIN2(_lo,r->_lo), MAX2(_hi,r->_hi), MAX2(_widen,r->_widen) ); +const TypeLong* TypeLong::make(jlong lo, jlong hi, int widen) { + assert(lo <= hi, "must be legal bounds"); + return make_or_top(TypeIntPrototype{{lo, hi}, {0, max_julong}, {0, 0}}, widen)->is_long(); } -//------------------------------xdual------------------------------------------ -// Dual: reverse hi & lo; flip widen -const Type *TypeLong::xdual() const { - int w = normalize_long_widen(_hi,_lo, WidenMax-_widen); - return new TypeLong(_hi,_lo,w); -} - -//------------------------------widen------------------------------------------ -// Only happens for optimistic top-down optimizations. -const Type *TypeLong::widen( const Type *old, const Type* limit ) const { - // Coming from TOP or such; no widening - if( old->base() != Long ) return this; - const TypeLong *ot = old->is_long(); - - // If new guy is equal to old guy, no widening - if( _lo == ot->_lo && _hi == ot->_hi ) - return old; - - // If new guy contains old, then we widened - if( _lo <= ot->_lo && _hi >= ot->_hi ) { - // New contains old - // If new guy is already wider than old, no widening - if( _widen > ot->_widen ) return this; - // If old guy was a constant, do not bother - if (ot->_lo == ot->_hi) return this; - // Now widen new guy. - // Check for widening too far - if (_widen == WidenMax) { - jlong max = max_jlong; - jlong min = min_jlong; - if (limit->isa_long()) { - max = limit->is_long()->_hi; - min = limit->is_long()->_lo; - } - if (min < _lo && _hi < max) { - // If neither endpoint is extremal yet, push out the endpoint - // which is closer to its respective limit. - if (_lo >= 0 || // easy common case - ((julong)_lo - min) >= ((julong)max - _hi)) { - // Try to widen to an unsigned range type of 32/63 bits: - if (max >= max_juint && _hi < max_juint) - return make(_lo, max_juint, WidenMax); - else - return make(_lo, max, WidenMax); - } else { - return make(min, _hi, WidenMax); - } - } - return TypeLong::LONG; - } - // Returned widened new guy - return make(_lo,_hi,_widen+1); - } - - // If old guy contains new, then we probably widened too far & dropped to - // bottom. Return the wider fellow. - if ( ot->_lo <= _lo && ot->_hi >= _hi ) - return old; +const Type* TypeLong::make_or_top(const TypeIntPrototype& t, int widen) { + return make_or_top(t, widen, false); +} - // fatal("Long value range is not subset"); - // return this; - return TypeLong::LONG; +bool TypeLong::contains(jlong i) const { + assert(!_is_dual, "dual types should only be used for join calculation"); + julong u = i; + return i >= _lo && i <= _hi && + u >= _ulo && u <= _uhi && + _bits.is_satisfied_by(u); } -//------------------------------narrow---------------------------------------- -// Only happens for pessimistic optimizations. -const Type *TypeLong::narrow( const Type *old ) const { - if (_lo >= _hi) return this; // already narrow enough - if (old == nullptr) return this; - const TypeLong* ot = old->isa_long(); - if (ot == nullptr) return this; - jlong olo = ot->_lo; - jlong ohi = ot->_hi; +bool TypeLong::contains(const TypeLong* t) const { + assert(!_is_dual && !t->_is_dual, "dual types should only be used for join calculation"); + return TypeIntHelper::int_type_is_subset(this, t); +} - // If new guy is equal to old guy, no narrowing - if (_lo == olo && _hi == ohi) return old; +const Type* TypeLong::xmeet(const Type* t) const { + return TypeIntHelper::int_type_xmeet(this, t); +} - // If old guy was maximum range, allow the narrowing - if (olo == min_jlong && ohi == max_jlong) return this; +const Type* TypeLong::xdual() const { + return new TypeLong(TypeIntPrototype{{_lo, _hi}, {_ulo, _uhi}, _bits}, + _widen, !_is_dual); +} - if (_lo < olo || _hi > ohi) - return this; // doesn't narrow; pretty weird +const Type* TypeLong::widen(const Type* old, const Type* limit) const { + assert(!_is_dual, "dual types should only be used for join calculation"); + return TypeIntHelper::int_type_widen(this, old->isa_long(), limit->isa_long()); +} - // The new type narrows the old type, so look for a "death march". - // See comments on PhaseTransform::saturate. - julong nrange = (julong)_hi - _lo; - julong orange = (julong)ohi - olo; - if (nrange < max_julong - 1 && nrange > (orange >> 1) + (SMALLINT*2)) { - // Use the new type only if the range shrinks a lot. - // We do not want the optimizer computing 2^31 point by point. - return old; +const Type* TypeLong::narrow(const Type* old) const { + assert(!_is_dual, "dual types should only be used for join calculation"); + if (old == nullptr) { + return this; } - return this; + return TypeIntHelper::int_type_narrow(this, old->isa_long()); } //-----------------------------filter------------------------------------------ -const Type *TypeLong::filter_helper(const Type *kills, bool include_speculative) const { +const Type* TypeLong::filter_helper(const Type* kills, bool include_speculative) const { + assert(!_is_dual, "dual types should only be used for join calculation"); const TypeLong* ft = join_helper(kills, include_speculative)->isa_long(); - if (ft == nullptr || ft->empty()) + if (ft == nullptr) { return Type::TOP; // Canonical empty value + } + assert(!ft->_is_dual, "dual types should only be used for join calculation"); if (ft->_widen < this->_widen) { // Do not allow the value of kill->_widen to affect the outcome. // The widen bits must be allowed to run freely through the graph. - ft = TypeLong::make(ft->_lo, ft->_hi, this->_widen); + return (new TypeLong(TypeIntPrototype{{ft->_lo, ft->_hi}, {ft->_ulo, ft->_uhi}, ft->_bits}, + this->_widen, false))->hashcons(); } return ft; } //------------------------------eq--------------------------------------------- // Structural equality check for Type representations -bool TypeLong::eq( const Type *t ) const { - const TypeLong *r = t->is_long(); // Handy access - return r->_lo == _lo && r->_hi == _hi && r->_widen == _widen; +bool TypeLong::eq(const Type* t) const { + const TypeLong* r = t->is_long(); + return TypeIntHelper::int_type_is_equal(this, r) && _widen == r->_widen && _is_dual == r->_is_dual; } //------------------------------hash------------------------------------------- // Type-specific hashing function. uint TypeLong::hash(void) const { - return (uint)_lo + (uint)_hi + (uint)_widen + (uint)Type::Long; + return (uint)_lo + (uint)_hi + (uint)_ulo + (uint)_uhi + + (uint)_bits._zeros + (uint)_bits._ones + (uint)_widen + (uint)_is_dual + (uint)Type::Long; } //------------------------------is_finite-------------------------------------- @@ -2234,72 +2002,36 @@ bool TypeLong::is_finite() const { return true; } -//------------------------------dump2------------------------------------------ -// Dump TypeLong -#ifndef PRODUCT -static const char* longnamenear(jlong x, const char* xname, char* buf, size_t buf_size, jlong n) { - if (n > x) { - if (n >= x + 10000) return nullptr; - os::snprintf_checked(buf, buf_size, "%s+" JLONG_FORMAT, xname, n - x); - } else if (n < x) { - if (n <= x - 10000) return nullptr; - os::snprintf_checked(buf, buf_size, "%s-" JLONG_FORMAT, xname, x - n); - } else { - return xname; - } - return buf; -} - -static const char* longname(char* buf, size_t buf_size, jlong n) { - const char* str; - if (n == min_jlong) - return "min"; - else if (n < min_jlong + 10000) - os::snprintf_checked(buf, buf_size, "min+" JLONG_FORMAT, n - min_jlong); - else if (n == max_jlong) - return "max"; - else if (n > max_jlong - 10000) - os::snprintf_checked(buf, buf_size, "max-" JLONG_FORMAT, max_jlong - n); - else if ((str = longnamenear(max_juint, "maxuint", buf, buf_size, n)) != nullptr) - return str; - else if ((str = longnamenear(max_jint, "maxint", buf, buf_size, n)) != nullptr) - return str; - else if ((str = longnamenear(min_jint, "minint", buf, buf_size, n)) != nullptr) - return str; - else - os::snprintf_checked(buf, buf_size, JLONG_FORMAT, n); - return buf; -} - -void TypeLong::dump2( Dict &d, uint depth, outputStream *st ) const { - char buf[80], buf2[80]; - if (_lo == min_jlong && _hi == max_jlong) - st->print("long"); - else if (is_con()) - st->print("long:%s", longname(buf, sizeof(buf), get_con())); - else if (_hi == max_jlong) - st->print("long:>=%s", longname(buf, sizeof(buf), _lo)); - else if (_lo == min_jlong) - st->print("long:<=%s", longname(buf, sizeof(buf), _hi)); - else - st->print("long:%s..%s", longname(buf, sizeof(buf), _lo), longname(buf2,sizeof(buf2), _hi)); - - if (_widen != 0 && this != TypeLong::LONG) - st->print(":%.*s", _widen, "wwww"); -} -#endif - //------------------------------singleton-------------------------------------- // TRUE if Type is a singleton type, FALSE otherwise. Singletons are simple // constants bool TypeLong::singleton(void) const { - return _lo >= _hi; + return _lo == _hi; } bool TypeLong::empty(void) const { - return _lo > _hi; + return false; +} + +//------------------------------dump2------------------------------------------ +#ifndef PRODUCT +void TypeInt::dump2(Dict& d, uint depth, outputStream* st) const { + TypeIntHelper::int_type_dump(this, st, false); +} + +void TypeInt::dump_verbose() const { + TypeIntHelper::int_type_dump(this, tty, true); } +void TypeLong::dump2(Dict& d, uint depth, outputStream* st) const { + TypeIntHelper::int_type_dump(this, st, false); +} + +void TypeLong::dump_verbose() const { + TypeIntHelper::int_type_dump(this, tty, true); +} +#endif + //============================================================================= // Convenience common pre-built types. const TypeTuple *TypeTuple::IFBOTH; // Return both arms of IF as reachable @@ -2551,10 +2283,15 @@ const Type *TypeAry::xmeet( const Type *t ) const { typerr(t); case Array: { // Meeting 2 arrays? - const TypeAry *a = t->is_ary(); + const TypeAry* a = t->is_ary(); + const Type* size = _size->xmeet(a->_size); + const TypeInt* isize = size->isa_int(); + if (isize == nullptr) { + assert(size == Type::TOP || size == Type::BOTTOM, ""); + return size; + } return TypeAry::make(_elem->meet_speculative(a->_elem), - _size->xmeet(a->_size)->is_int(), - _stable && a->_stable); + isize, _stable && a->_stable); } case Top: break; @@ -4984,10 +4721,12 @@ const TypeInt* TypeAryPtr::narrow_size_type(const TypeInt* size) const { chg = true; } // Negative length arrays will produce weird intermediate dead fast-path code - if (lo > hi) + if (lo > hi) { return TypeInt::ZERO; - if (!chg) + } + if (!chg) { return size; + } return TypeInt::make(lo, hi, Type::WidenMin); } @@ -5152,7 +4891,12 @@ const Type *TypeAryPtr::xmeet_helper(const Type *t) const { case AryPtr: { // Meeting 2 references? const TypeAryPtr *tap = t->is_aryptr(); int off = meet_offset(tap->offset()); - const TypeAry *tary = _ary->meet_speculative(tap->_ary)->is_ary(); + const Type* tm = _ary->meet_speculative(tap->_ary); + const TypeAry* tary = tm->isa_ary(); + if (tary == nullptr) { + assert(tm == Type::TOP || tm == Type::BOTTOM, ""); + return tm; + } PTR ptr = meet_ptr(tap->ptr()); int instance_id = meet_instance_id(tap->instance_id()); const TypePtr* speculative = xmeet_speculative(tap); diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 9c40e1ab3cdd7..f8f523563a02e 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -26,6 +26,8 @@ #define SHARE_OPTO_TYPE_HPP #include "opto/adlcVMDeps.hpp" +#include "opto/compile.hpp" +#include "opto/rangeinference.hpp" #include "runtime/handles.hpp" // Portions of code courtesy of Clifford Click @@ -73,6 +75,9 @@ class TypeAryKlassPtr; class TypeMetadataPtr; class VerifyMeet; +template +class TypeIntPrototype; + //------------------------------Type------------------------------------------- // Basic Type object, represents a set of primitive Values. // Types are hash-cons'd into a private class dictionary, so only one of each @@ -284,6 +289,10 @@ class Type { virtual float getf() const; double getd() const; + // This has the same semantics as std::dynamic_cast(this) + template + const TypeClass* try_cast() const; + const TypeInt *is_int() const; const TypeInt *isa_int() const; // Returns null if not an Int const TypeInteger* is_integer(BasicType bt) const; @@ -594,7 +603,12 @@ class TypeD : public Type { class TypeInteger : public Type { protected: - TypeInteger(TYPES t, int w) : Type(t), _widen(w) {} + TypeInteger(TYPES t, int w, bool dual) : Type(t), _is_dual(dual), _widen(w) {} + + // Denote that a set is a dual set. + // Dual sets are only used to compute the join of 2 sets, and not used + // outside. + const bool _is_dual; public: const short _widen; // Limit on times we widen this sucker @@ -614,82 +628,240 @@ class TypeInteger : public Type { static const TypeInteger* minus_1(BasicType type); }; +/** + * Definition: + * + * A TypeInt represents a set of non-empty jint values. A jint v is an element + * of a TypeInt iff: + * + * v >= _lo && v <= _hi && + * juint(v) >= _ulo && juint(v) <= _uhi && + * _bits.is_satisfied_by(v) + * + * Multiple sets of parameters can represent the same set. + * E.g: consider 2 TypeInt t1, t2 + * + * t1._lo = 2, t1._hi = 7, t1._ulo = 0, t1._uhi = 5, t1._bits._zeros = 0x00000000, t1._bits._ones = 0x1 + * t2._lo = 3, t2._hi = 5, t2._ulo = 3, t2._uhi = 5, t2._bits._zeros = 0xFFFFFFF8, t2._bits._ones = 0x1 + * + * Then, t1 and t2 both represent the set {3, 5}. We can also see that the + * constraints of t2 are the tightest possible. I.e there exists no TypeInt t3 + * which also represents {3, 5} such that any of these would be true: + * + * 1) t3._lo > t2._lo + * 2) t3._hi < t2._hi + * 3) t3._ulo > t2._ulo + * 4) t3._uhi < t2._uhi + * 5) (t3._bits._zeros &~ t2._bis._zeros) != 0 + * 6) (t3._bits._ones &~ t2._bits._ones) != 0 + * + * The 5-th condition mean that the subtraction of the bitsets represented by + * t3._bits._zeros and t2._bits._zeros is not empty, which means that the + * bits in t3._bits._zeros is not a subset of those in t2._bits._zeros, the + * same applies to _bits._ones + * + * To simplify reasoning about the types in optimizations, we canonicalize + * every TypeInt to its tightest form, already at construction. E.g a TypeInt + * t with t._lo < 0 will definitely contain negative values. It also makes it + * trivial to determine if a TypeInt instance is a subset of another. + * + * Lemmas: + * + * 1. Since every TypeInt instance is non-empty and canonicalized, all the + * bounds must also be elements of such TypeInt. Or else, we can tighten the + * bounds by narrowing it by one, which contradicts the assumption of the + * TypeInt being canonical. + * + * 2. + * 2.1. _lo <= jint(_ulo) + * 2.2. _lo <= _hi + * 2.3. _lo <= jint(_uhi) + * 2.4. _ulo <= juint(_lo) + * 2.5. _ulo <= juint(_hi) + * 2.6. _ulo <= _uhi + * 2.7. _hi >= _lo + * 2.8. _hi >= jint(_ulo) + * 2.9. _hi >= jint(_uhi) + * 2.10. _uhi >= juint(_lo) + * 2.11. _uhi >= _ulo + * 2.12. _uhi >= juint(_hi) + * + * Proof of lemma 2: + * + * 2.1. _lo <= jint(_ulo): + * According the lemma 1, _ulo is an element of the TypeInt, so in the + * signed domain, it must not be less than the smallest element of that + * TypeInt, which is _lo. Which means that _lo <= _ulo in the signed + * domain, or in a more programmatical way, _lo <= jint(_ulo). + * 2.2. _lo <= _hi: + * According the lemma 1, _hi is an element of the TypeInt, so in the + * signed domain, it must not be less than the smallest element of that + * TypeInt, which is _lo. Which means that _lo <= _hi. + * + * The other inequalities can be proved in a similar manner. + * + * 3. Given 2 jint values x, y where either both >= 0 or both < 0. Then: + * + * x <= y iff juint(x) <= juint(y) + * I.e. x <= y in the signed domain iff x <= y in the unsigned domain + * + * 4. Either _lo == jint(_ulo) and _hi == jint(_uhi), or each element of a + * TypeInt lies in either interval [_lo, jint(_uhi)] or [jint(_ulo), _hi] + * (note that these intervals are disjoint in this case). + * + * Proof of lemma 4: + * + * For a TypeInt t, there are 3 possible cases: + * + * a. t._lo >= 0, we have: + * + * 0 <= t_lo <= jint(t._ulo) (lemma 2.1) + * juint(t._lo) <= juint(jint(t._ulo)) (lemma 3) + * == t._ulo (juint(jint(v)) == v with juint v) + * <= juint(t._lo) (lemma 2.4) + * + * Which means that t._lo == jint(t._ulo). + * + * Furthermore, + * + * 0 <= t._lo <= t._hi (lemma 2.2) + * 0 <= t._lo <= jint(t._uhi) (lemma 2.3) + * t._hi >= jint(t._uhi) (lemma 2.9) + * + * juint(t._hi) >= juint(jint(t._uhi)) (lemma 3) + * == t._uhi (juint(jint(v)) == v with juint v) + * >= juint(t._hi) (lemma 2.12) + * + * Which means that t._hi == jint(t._uhi). + * In this case, t._lo == jint(t._ulo) and t._hi == jint(t._uhi) + * + * b. t._hi < 0. Similarly, we can conclude that: + * t._lo == jint(t._ulo) and t._hi == jint(t._uhi) + * + * c. t._lo < 0, t._hi >= 0. + * + * Since t._ulo <= juint(t._hi) (lemma 2.5), we must have jint(t._ulo) >= 0 + * because all negative values is larger than all non-negative values in the + * unsigned domain. + * + * Since t._uhi >= juint(t._lo) (lemma 2.10), we must have jint(t._uhi) < 0 + * similar to the reasoning above. + * + * In this case, each element of t belongs to either [t._lo, jint(t._uhi)] or + * [jint(t._ulo), t._hi]. + * + * Below is an illustration of the TypeInt in this case, the intervals that + * the elements can be in are marked using the = symbol. Note how the + * negative range in the signed domain wrap around in the unsigned domain. + * + * Signed: + * -----lo=========uhi---------0--------ulo==========hi----- + * Unsigned: + * 0--------ulo==========hi----------lo=========uhi--------- + * + * This property is useful for our analysis of TypeInt values. Additionally, + * it can be seen that _lo and jint(_uhi) are both < 0 or both >= 0, and the + * same applies to jint(_ulo) and _hi. + * + * We call [_lo, jint(_uhi)] and [jint(_ulo), _hi] "simple intervals". Then, + * a TypeInt consists of 2 simple intervals, each of which has its bounds + * being both >= 0 or both < 0. If both simple intervals lie in the same half + * of the integer domain, they must be the same (i.e _lo == jint(_ulo) and + * _hi == jint(_uhi)). Otherwise, [_lo, jint(_uhi)] must lie in the negative + * half and [jint(_ulo), _hi] must lie in the non-negative half of the signed + * domain (equivalently, [_lo, jint(_uhi)] must lie in the upper half and + * [jint(_ulo), _hi] must lie in the lower half of the unsigned domain). + */ +class TypeInt : public TypeInteger { +private: + TypeInt(const TypeIntPrototype& t, int w, bool dual); + static const Type* make_or_top(const TypeIntPrototype& t, int widen, bool dual); + friend class TypeIntHelper; -//------------------------------TypeInt---------------------------------------- -// Class of integer ranges, the set of integers between a lower bound and an -// upper bound, inclusive. -class TypeInt : public TypeInteger { - TypeInt( jint lo, jint hi, int w ); protected: - virtual const Type *filter_helper(const Type *kills, bool include_speculative) const; + virtual const Type* filter_helper(const Type* kills, bool include_speculative) const; public: typedef jint NativeType; - virtual bool eq( const Type *t ) const; + virtual bool eq(const Type* t) const; virtual uint hash() const; // Type specific hashing virtual bool singleton(void) const; // TRUE if type is a singleton virtual bool empty(void) const; // TRUE if type is vacuous - const jint _lo, _hi; // Lower bound, upper bound + // A value is in the set represented by this TypeInt if it satisfies all + // the below constraints, see contains(jint) + const jint _lo, _hi; // Lower bound, upper bound in the signed domain + const juint _ulo, _uhi; // Lower bound, upper bound in the unsigned domain + const KnownBits _bits; - static const TypeInt *make(jint lo); + static const TypeInt* make(jint con); // must always specify w - static const TypeInt *make(jint lo, jint hi, int w); + static const TypeInt* make(jint lo, jint hi, int widen); + static const Type* make_or_top(const TypeIntPrototype& t, int widen); // Check for single integer - bool is_con() const { return _lo==_hi; } + bool is_con() const { return _lo == _hi; } bool is_con(jint i) const { return is_con() && _lo == i; } - jint get_con() const { assert(is_con(), "" ); return _lo; } + jint get_con() const { assert(is_con(), ""); return _lo; } + // Check if a jint/TypeInt is a subset of this TypeInt (i.e. all elements of the + // argument are also elements of this type) + bool contains(jint i) const; + bool contains(const TypeInt* t) const; - virtual bool is_finite() const; // Has a finite value + virtual bool is_finite() const; // Has a finite value - virtual const Type *xmeet( const Type *t ) const; - virtual const Type *xdual() const; // Compute dual right now. - virtual const Type *widen( const Type *t, const Type* limit_type ) const; - virtual const Type *narrow( const Type *t ) const; + virtual const Type* xmeet(const Type* t) const; + virtual const Type* xdual() const; // Compute dual right now. + virtual const Type* widen(const Type* t, const Type* limit_type) const; + virtual const Type* narrow(const Type* t) const; virtual jlong hi_as_long() const { return _hi; } virtual jlong lo_as_long() const { return _lo; } // Do not kill _widen bits. // Convenience common pre-built types. - static const TypeInt *MAX; - static const TypeInt *MIN; - static const TypeInt *MINUS_1; - static const TypeInt *ZERO; - static const TypeInt *ONE; - static const TypeInt *BOOL; - static const TypeInt *CC; - static const TypeInt *CC_LT; // [-1] == MINUS_1 - static const TypeInt *CC_GT; // [1] == ONE - static const TypeInt *CC_EQ; // [0] == ZERO - static const TypeInt *CC_LE; // [-1,0] - static const TypeInt *CC_GE; // [0,1] == BOOL (!) - static const TypeInt *BYTE; - static const TypeInt *UBYTE; - static const TypeInt *CHAR; - static const TypeInt *SHORT; - static const TypeInt *POS; - static const TypeInt *POS1; - static const TypeInt *INT; - static const TypeInt *SYMINT; // symmetric range [-max_jint..max_jint] - static const TypeInt *TYPE_DOMAIN; // alias for TypeInt::INT - - static const TypeInt *as_self(const Type *t) { return t->is_int(); } + static const TypeInt* MAX; + static const TypeInt* MIN; + static const TypeInt* MINUS_1; + static const TypeInt* ZERO; + static const TypeInt* ONE; + static const TypeInt* BOOL; + static const TypeInt* CC; + static const TypeInt* CC_LT; // [-1] == MINUS_1 + static const TypeInt* CC_GT; // [1] == ONE + static const TypeInt* CC_EQ; // [0] == ZERO + static const TypeInt* CC_NE; // [-1, 1] + static const TypeInt* CC_LE; // [-1,0] + static const TypeInt* CC_GE; // [0,1] == BOOL (!) + static const TypeInt* BYTE; + static const TypeInt* UBYTE; + static const TypeInt* CHAR; + static const TypeInt* SHORT; + static const TypeInt* NON_ZERO; + static const TypeInt* POS; + static const TypeInt* POS1; + static const TypeInt* INT; + static const TypeInt* SYMINT; // symmetric range [-max_jint..max_jint] + static const TypeInt* TYPE_DOMAIN; // alias for TypeInt::INT + + static const TypeInt* as_self(const Type* t) { return t->is_int(); } #ifndef PRODUCT - virtual void dump2( Dict &d, uint depth, outputStream *st ) const; + virtual void dump2(Dict& d, uint depth, outputStream* st) const; + void dump_verbose() const; #endif }; - -//------------------------------TypeLong--------------------------------------- -// Class of long integer ranges, the set of integers between a lower bound and -// an upper bound, inclusive. +// Similar to TypeInt class TypeLong : public TypeInteger { - TypeLong( jlong lo, jlong hi, int w ); +private: + TypeLong(const TypeIntPrototype& t, int w, bool dual); + static const Type* make_or_top(const TypeIntPrototype& t, int widen, bool dual); + + friend class TypeIntHelper; + protected: // Do not kill _widen bits. - virtual const Type *filter_helper(const Type *kills, bool include_speculative) const; + virtual const Type* filter_helper(const Type* kills, bool include_speculative) const; public: typedef jlong NativeType; virtual bool eq( const Type *t ) const; @@ -697,16 +869,25 @@ class TypeLong : public TypeInteger { virtual bool singleton(void) const; // TRUE if type is a singleton virtual bool empty(void) const; // TRUE if type is vacuous public: - const jlong _lo, _hi; // Lower bound, upper bound + // A value is in the set represented by this TypeLong if it satisfies all + // the below constraints, see contains(jlong) + const jlong _lo, _hi; // Lower bound, upper bound in the signed domain + const julong _ulo, _uhi; // Lower bound, upper bound in the unsigned domain + const KnownBits _bits; - static const TypeLong *make(jlong lo); + static const TypeLong* make(jlong con); // must always specify w - static const TypeLong *make(jlong lo, jlong hi, int w); + static const TypeLong* make(jlong lo, jlong hi, int widen); + static const Type* make_or_top(const TypeIntPrototype& t, int widen); // Check for single integer - bool is_con() const { return _lo==_hi; } + bool is_con() const { return _lo == _hi; } bool is_con(jlong i) const { return is_con() && _lo == i; } jlong get_con() const { assert(is_con(), "" ); return _lo; } + // Check if a jlong/TypeLong is a subset of this TypeLong (i.e. all elements of the + // argument are also elements of this type) + bool contains(jlong i) const; + bool contains(const TypeLong* t) const; // Check for positive 32-bit value. int is_positive_int() const { return _lo >= 0 && _hi <= (jlong)max_jint; } @@ -716,27 +897,30 @@ class TypeLong : public TypeInteger { virtual jlong hi_as_long() const { return _hi; } virtual jlong lo_as_long() const { return _lo; } - virtual const Type *xmeet( const Type *t ) const; - virtual const Type *xdual() const; // Compute dual right now. - virtual const Type *widen( const Type *t, const Type* limit_type ) const; - virtual const Type *narrow( const Type *t ) const; + virtual const Type* xmeet(const Type* t) const; + virtual const Type* xdual() const; // Compute dual right now. + virtual const Type* widen(const Type* t, const Type* limit_type) const; + virtual const Type* narrow(const Type* t) const; // Convenience common pre-built types. - static const TypeLong *MAX; - static const TypeLong *MIN; - static const TypeLong *MINUS_1; - static const TypeLong *ZERO; - static const TypeLong *ONE; - static const TypeLong *POS; - static const TypeLong *LONG; - static const TypeLong *INT; // 32-bit subrange [min_jint..max_jint] - static const TypeLong *UINT; // 32-bit unsigned [0..max_juint] - static const TypeLong *TYPE_DOMAIN; // alias for TypeLong::LONG + static const TypeLong* MAX; + static const TypeLong* MIN; + static const TypeLong* MINUS_1; + static const TypeLong* ZERO; + static const TypeLong* ONE; + static const TypeLong* NON_ZERO; + static const TypeLong* POS; + static const TypeLong* NEG; + static const TypeLong* LONG; + static const TypeLong* INT; // 32-bit subrange [min_jint..max_jint] + static const TypeLong* UINT; // 32-bit unsigned [0..max_juint] + static const TypeLong* TYPE_DOMAIN; // alias for TypeLong::LONG // static convenience methods. - static const TypeLong *as_self(const Type *t) { return t->is_long(); } + static const TypeLong* as_self(const Type* t) { return t->is_long(); } #ifndef PRODUCT - virtual void dump2( Dict &d, uint, outputStream *st ) const;// Specialized per-Type dumping + virtual void dump2(Dict& d, uint, outputStream* st) const;// Specialized per-Type dumping + void dump_verbose() const; #endif }; @@ -2246,6 +2430,16 @@ inline const TypeLong* Type::cast() const { return is_long(); } +template <> +inline const TypeInt* Type::try_cast() const { + return isa_int(); +} + +template <> +inline const TypeLong* Type::try_cast() const { + return isa_long(); +} + // =============================================================== // Things that need to be 64-bits in the 64-bit build but // 32-bits in the 32-bit build. Done this way to get full diff --git a/src/hotspot/share/utilities/count_leading_zeros.hpp b/src/hotspot/share/utilities/count_leading_zeros.hpp index d6cbed9a355e9..122507743d351 100644 --- a/src/hotspot/share/utilities/count_leading_zeros.hpp +++ b/src/hotspot/share/utilities/count_leading_zeros.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ #ifndef SHARE_UTILITIES_COUNT_LEADING_ZEROS_HPP #define SHARE_UTILITIES_COUNT_LEADING_ZEROS_HPP +#include "metaprogramming/enableIf.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" @@ -36,7 +37,8 @@ // We implement and support variants for 8, 16, 32 and 64 bit integral types. template struct CountLeadingZerosImpl; -template unsigned count_leading_zeros(T v) { +template ::value)> +unsigned count_leading_zeros(T v) { assert(v != 0, "precondition"); return CountLeadingZerosImpl::doit(v); } diff --git a/src/hotspot/share/utilities/intn_t.hpp b/src/hotspot/share/utilities/intn_t.hpp new file mode 100644 index 0000000000000..093db367e1743 --- /dev/null +++ b/src/hotspot/share/utilities/intn_t.hpp @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_UTILITIES_INTN_T_HPP +#define SHARE_UTILITIES_INTN_T_HPP + +#include "utilities/count_leading_zeros.hpp" + +#include + +template +class uintn_t; + +// This class represents a signed integer type with the width of exactly nbits +// bits. Conceptually, nbits == 8 gives a type equivalent to int8_t, +// nbits == 16 gives a type equivalent to int16_t, and so on. This class may be +// used to verify the correctness of an algorithm that is supposed to be +// applicable to all fixed-width integral types. With small nbits, it makes it +// possible to perform an exhaustive test that exercises the algorithm with all +// possible input values. +// Implementation-wise, this class currently only supports 0 < nbits <= 8. Also +// note that this class is implemented so that overflows in alrithmetic +// operations are well-defined and wrap-around. +template +class intn_t { + static_assert(0 < nbits && nbits <= 8, "should not be larger than char"); + +private: + // Only the lowest nbits bits are important, operations should act as if it + // sign extends the lowest nbits to an int, performs the calculation on ints, + // then truncates the result to nbits. In practice, we do not need to + // truncate the result, as the lowest nbits will be sign extended in the next + // operations. We can also sign extends the operands sparingly, for example, + // addition or subtraction do not need this sign extension, and we can add or + // subtract the value of _v directly. This is because the lowest nbits bits + // of a sum or a difference only depends on the lowest nbits bits of the + // operands. + uint _v; + + constexpr static uint _mask = (1 << nbits) - 1; + + friend class uintn_t; + +public: + explicit constexpr intn_t(int v) : _v(v) {} + constexpr intn_t() : _v(0) {} + constexpr intn_t(const intn_t&) = default; + constexpr intn_t& operator=(const intn_t&) = default; + explicit constexpr intn_t(uintn_t v); + + // Sign extension + explicit constexpr operator int() const { + int shift = 32 - nbits; + return int(_v << shift) >> shift; + } + + constexpr static int min = std::numeric_limits::max() << (nbits - 1); + constexpr static int max = (1 << (nbits - 1)) - 1; + static_assert(min < max, ""); + + constexpr bool operator==(intn_t o) const { return (_v & _mask) == (o._v & _mask); } + constexpr bool operator<(intn_t o) const { return int(*this) < int(o); } + constexpr bool operator>(intn_t o) const { return int(*this) > int(o); } + constexpr bool operator<=(intn_t o) const { return int(*this) <= int(o); } + constexpr bool operator>=(intn_t o) const { return int(*this) >= int(o); } +}; + +template +unsigned count_leading_zeros(uintn_t); + +// The unsigned version of intn_t +template +class uintn_t { + static_assert(0 < nbits && nbits <= 8, "should not be larger than char"); + +private: + // Similar to intn_t, the difference is that the operation should act + // as if it zero extends the lowest nbits bits of the operands. + uint _v; + + constexpr static uint _mask = (1 << nbits) - 1; + + friend class intn_t; + + friend unsigned count_leading_zeros(uintn_t); + +public: + explicit constexpr uintn_t(int v) : _v(v) {} + constexpr uintn_t() : _v(0) {} + constexpr uintn_t(const uintn_t&) = default; + constexpr uintn_t& operator=(const uintn_t&) = default; + explicit constexpr uintn_t(intn_t v) : _v(v._v) {} + + // Zero extension + explicit constexpr operator uint() const { return _v & _mask; } + + constexpr static int min = 0; + constexpr static int max = _mask; + static_assert(min < max, ""); + + constexpr bool operator==(uintn_t o) const { return (_v & _mask) == (o._v & _mask); } + constexpr bool operator!=(uintn_t o) const { return (_v & _mask) != (o._v & _mask); } + constexpr bool operator<(uintn_t o) const { return (_v & _mask) < (o._v & _mask); } + constexpr bool operator>(uintn_t o) const { return (_v & _mask) > (o._v & _mask); } + constexpr bool operator<=(uintn_t o) const { return (_v & _mask) <= (o._v & _mask); } + constexpr bool operator>=(uintn_t o) const { return (_v & _mask) >= (o._v & _mask); } + constexpr uintn_t operator+(uintn_t o) const { return uintn_t(_v + o._v); } + constexpr uintn_t operator-(uintn_t o) const { return uintn_t(_v - o._v); } + constexpr uintn_t operator&(uintn_t o) const { return uintn_t(_v & o._v); } + constexpr uintn_t operator|(uintn_t o) const { return uintn_t(_v | o._v); } + constexpr uintn_t operator^(uintn_t o) const { return uintn_t(_v ^ o._v); } + constexpr uintn_t operator>>(unsigned int s) const { return uintn_t((_v & _mask) >> s); } + constexpr uintn_t operator<<(unsigned int s) const { return uintn_t(_v << s); } + constexpr uintn_t operator~() const { return uintn_t(~_v); } + constexpr uintn_t operator-() const { return uintn_t(-_v); } + constexpr uintn_t& operator|=(uintn_t o) { _v |= o._v; return *this; } +}; + +template +constexpr intn_t::intn_t(uintn_t v) : _v(v._v) {} + +namespace std { + +template +class numeric_limits> { +public: + constexpr static intn_t min() { return intn_t(intn_t::min); } + constexpr static intn_t max() { return intn_t(intn_t::max); } +}; + +template +class numeric_limits> { +public: + constexpr static uintn_t min() { return uintn_t(uintn_t::min); } + constexpr static uintn_t max() { return uintn_t(uintn_t::max); } +}; + +} // namespace std + +template +inline unsigned count_leading_zeros(uintn_t v) { + return count_leading_zeros(v._v & uintn_t::_mask) - (32 - nbits); +} + +#endif // SHARE_UTILITIES_INTN_T_HPP diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp new file mode 100644 index 0000000000000..1a62941a48620 --- /dev/null +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "opto/rangeinference.hpp" +#include "opto/type.hpp" +#include "runtime/os.hpp" +#include "utilities/intn_t.hpp" +#include "unittest.hpp" + +template +static U uniform_random(); + +template <> +juint uniform_random() { + return os::random(); +} + +template <> +julong uniform_random() { + return (julong(os::random()) << 32) | julong(juint(os::random())); +} + +static void test_canonicalize_constraints_trivial() { + ASSERT_FALSE(TypeInt::NON_ZERO->contains(0)); + ASSERT_TRUE(TypeInt::NON_ZERO->contains(1)); + ASSERT_TRUE(TypeInt::NON_ZERO->contains(-1)); + ASSERT_TRUE(TypeInt::CC_NE->contains(-1)); + ASSERT_TRUE(TypeInt::CC_NE->contains(1)); + ASSERT_FALSE(TypeInt::CC_NE->contains(0)); + ASSERT_FALSE(TypeInt::CC_NE->contains(-2)); + ASSERT_FALSE(TypeInt::CC_NE->contains(2)); + ASSERT_FALSE(TypeLong::NON_ZERO->contains(jlong(0))); + ASSERT_TRUE(TypeLong::NON_ZERO->contains(jlong(1))); + ASSERT_TRUE(TypeLong::NON_ZERO->contains(jlong(-1))); +} + +template +static void test_canonicalize_constraints_exhaustive() { + { + TypeIntPrototype t{{S(0), S(0)}, {U(0), U(0)}, {U(-1), U(0)}}; + auto new_t = t.canonicalize_constraints(); + ASSERT_TRUE(new_t._present); + DEBUG_ONLY(ASSERT_TRUE(t.contains(S(0)))); + DEBUG_ONLY(ASSERT_FALSE(t.contains(S(1)))); + } + { + TypeIntPrototype t{{S(0), S(0)}, {U(1), U(1)}, {U(-1), U(0)}}; + auto new_t = t.canonicalize_constraints(); + ASSERT_FALSE(new_t._present); + DEBUG_ONLY(ASSERT_FALSE(t.contains(S(0)))); + DEBUG_ONLY(ASSERT_FALSE(t.contains(S(1)))); + } + { + TypeIntPrototype t{{S(S::min), S(S::max)}, {U(U::min), U(U::max)}, {U(0), U(0)}}; + auto new_t = t.canonicalize_constraints(); + ASSERT_TRUE(new_t._present); + for (int v = S::min; v <= S::max; v++) { + DEBUG_ONLY(ASSERT_TRUE(t.contains(S(v)))); + } + } + for (int lo = S::min; lo <= S::max; lo++) { + for (int hi = lo; hi <= S::max; hi++) { + for (int ulo = U::min; ulo <= U::max; ulo++) { + for (int uhi = ulo; uhi <= U::max; uhi++) { + for (int zeros = U::min; zeros <= U::max; zeros++) { + for (int ones = U::min; ones <= U::max; ones++) { + TypeIntPrototype t{{S(lo), S(hi)}, {U(ulo), U(uhi)}, {U(zeros), U(ones)}}; + auto new_t = t.canonicalize_constraints(); + if (new_t._present) { + DEBUG_ONLY(new_t._data.verify_constraints()); + } + for (int v = S::min; v <= S::max; v++) { + if (!new_t._present) { + DEBUG_ONLY(ASSERT_FALSE(t.contains(S(v)))); + } else { + DEBUG_ONLY(ASSERT_EQ(t.contains(S(v)), new_t._data.contains(S(v)))); + } + } + } + } + } + } + } + } +} + +template +static void test_canonicalize_constraints_simple() { + constexpr int parameters = 1000; + for (int i = 0; i < parameters; i++) { + S a = uniform_random(); + S b = uniform_random(); + + { + S lo = MIN2(a, b); + S hi = MAX2(a, b); + TypeIntPrototype t{{lo, hi}, {std::numeric_limits::min(), std::numeric_limits::max()}, + {0, 0}}; + auto new_t = t.canonicalize_constraints(); + ASSERT_TRUE(new_t._present); + DEBUG_ONLY(new_t._data.verify_constraints()); + ASSERT_EQ(lo, new_t._data._srange._lo); + ASSERT_EQ(hi, new_t._data._srange._hi); + if (U(lo) <= U(hi)) { + ASSERT_EQ(U(lo), new_t._data._urange._lo); + ASSERT_EQ(U(hi), new_t._data._urange._hi); + } else { + ASSERT_EQ(std::numeric_limits::min(), new_t._data._urange._lo); + ASSERT_EQ(std::numeric_limits::max(), new_t._data._urange._hi); + } + } + + { + U ulo = MIN2(a, b); + U uhi = MAX2(a, b); + TypeIntPrototype t{{std::numeric_limits::min(), std::numeric_limits::max()}, + {ulo, uhi}, {0, 0}}; + auto new_t = t.canonicalize_constraints(); + ASSERT_TRUE(new_t._present); + DEBUG_ONLY(new_t._data.verify_constraints()); + ASSERT_EQ(ulo, new_t._data._urange._lo); + ASSERT_EQ(uhi, new_t._data._urange._hi); + if (S(ulo) <= S(uhi)) { + ASSERT_EQ(S(ulo), new_t._data._srange._lo); + ASSERT_EQ(S(uhi), new_t._data._srange._hi); + } else { + ASSERT_EQ(std::numeric_limits::min(), new_t._data._srange._lo); + ASSERT_EQ(std::numeric_limits::max(), new_t._data._srange._hi); + } + } + + { + U intersection = a & b; + U zeros = a ^ intersection; + U ones = b ^ intersection; + TypeIntPrototype t{{std::numeric_limits::min(), std::numeric_limits::max()}, + {std::numeric_limits::min(), std::numeric_limits::max()}, {zeros, ones}}; + auto new_t = t.canonicalize_constraints(); + ASSERT_TRUE(new_t._present); + DEBUG_ONLY(new_t._data.verify_constraints()); + ASSERT_EQ(zeros, new_t._data._bits._zeros); + ASSERT_EQ(ones, new_t._data._bits._ones); + ASSERT_EQ(ones, new_t._data._urange._lo); + ASSERT_EQ(~zeros, new_t._data._urange._hi); + } + } +} + +template +static void test_canonicalize_constraints_random() { + constexpr int samples = 1000; + constexpr int parameters = 1000; + for (int i = 0; i < parameters; i++) { + S s1 = uniform_random(); + S s2 = uniform_random(); + S lo = MIN2(s1, s2); + S hi = MAX2(s1, s2); + U u1 = uniform_random(); + U u2 = uniform_random(); + U ulo = MIN2(u1, u2); + U uhi = MAX2(u1, u2); + U b1 = uniform_random(); + U b2 = uniform_random(); + U intersection = b1 & b2; + U zeros = b1 ^ intersection; + U ones = b2 ^ intersection; + TypeIntPrototype t{{lo, hi}, {ulo, uhi}, {zeros, ones}}; + auto new_t = t.canonicalize_constraints(); + if (new_t._present) { + DEBUG_ONLY(new_t._data.verify_constraints()); + } + for (int j = 0; j < samples; j++) { + S v = uniform_random(); + if (!new_t._present) { + DEBUG_ONLY(ASSERT_FALSE(t.contains(v))); + } else { + DEBUG_ONLY(ASSERT_EQ(t.contains(v), new_t._data.contains(v))); + } + } + } +} + +TEST_VM(opto, canonicalize_constraints) { + test_canonicalize_constraints_trivial(); + test_canonicalize_constraints_exhaustive, uintn_t<1>>(); + test_canonicalize_constraints_exhaustive, uintn_t<2>>(); + test_canonicalize_constraints_exhaustive, uintn_t<3>>(); + test_canonicalize_constraints_exhaustive, uintn_t<4>>(); + test_canonicalize_constraints_simple(); + test_canonicalize_constraints_simple(); + test_canonicalize_constraints_random(); + test_canonicalize_constraints_random(); +} diff --git a/test/hotspot/gtest/utilities/test_intn_t.cpp b/test/hotspot/gtest/utilities/test_intn_t.cpp new file mode 100644 index 0000000000000..89958e0757e0e --- /dev/null +++ b/test/hotspot/gtest/utilities/test_intn_t.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "utilities/intn_t.hpp" +#include "unittest.hpp" + +// Sanity tests for off-by-one errors +static_assert(intn_t<1>::min == -1 && intn_t<1>::max == 0, ""); +static_assert(intn_t<2>::min == -2 && intn_t<2>::max == 1, ""); +static_assert(intn_t<3>::min == -4 && intn_t<3>::max == 3, ""); +static_assert(uintn_t<1>::max == 1, ""); +static_assert(uintn_t<2>::max == 3, ""); +static_assert(uintn_t<3>::max == 7, ""); + +template +static void test_intn_t() { + static_assert(std::numeric_limits>::min() <= intn_t(-1) && + intn_t(-1) < intn_t(0) && + intn_t(0) <= std::numeric_limits>::max(), "basic sanity"); + constexpr int period = intn_t::max - intn_t::min + 1; + for (int i = std::numeric_limits::min(); i < std::numeric_limits::max(); i++) { + ASSERT_EQ(intn_t(i), intn_t(i + period)); + ASSERT_EQ(int(intn_t(i)), int(intn_t(i + period))); + } + for (int i = intn_t::min; i <= intn_t::max; i++) { + ASSERT_EQ(i, int(intn_t(i))); + if (i > intn_t::min) { + ASSERT_TRUE(intn_t(i - 1) < intn_t(i)); + } else { + ASSERT_TRUE(intn_t(i - 1) > intn_t(i)); + } + if (i < intn_t::max) { + ASSERT_TRUE(intn_t(i) < intn_t(i + 1)); + } else { + ASSERT_TRUE(intn_t(i) > intn_t(i + 1)); + } + } +} + +TEST(utilities, intn_t) { + test_intn_t<1>(); + test_intn_t<2>(); + test_intn_t<3>(); + test_intn_t<4>(); + test_intn_t<5>(); + test_intn_t<6>(); + test_intn_t<7>(); + test_intn_t<8>(); +}