Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8315066: Add unsigned bounds and known bits to TypeInt/Long #17508

Open
wants to merge 60 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
3a87d66
implement unsigned bounds and known bits
merykitty Jan 20, 2024
61f4d21
refactor
merykitty Jan 20, 2024
d11497e
fix template parameter
merykitty Jan 20, 2024
12f268a
add unit tests
merykitty Jan 20, 2024
6b417f9
fix tests, add verify
merykitty Jan 20, 2024
756d615
add comments, group arguments to reduce C-style reference passing arg…
merykitty Jan 22, 2024
1faa48b
fix release build
merykitty Jan 22, 2024
ffb0abd
Merge branch 'master' into unsignedbounds
merykitty Mar 15, 2024
6e2e6c5
add comments
merykitty Mar 15, 2024
ec4ca3e
Merge branch 'master' into unsignedbounds
merykitty Aug 14, 2024
d5ad9f1
fix compile errors
merykitty Aug 14, 2024
2c3807b
address reviews
merykitty Sep 3, 2024
ae47385
move static_asserts
merykitty Sep 3, 2024
2bf545f
add more comments, group KnownBits
merykitty Sep 4, 2024
4f4a6be
fix build
merykitty Sep 4, 2024
8d14f8e
fix build
merykitty Sep 4, 2024
f164821
more explanation
merykitty Sep 5, 2024
5990628
rename tests
merykitty Sep 5, 2024
123e055
make should return the correct type
merykitty Sep 5, 2024
089c566
add trivial test cases
merykitty Sep 5, 2024
2e3955d
fix builds
merykitty Sep 5, 2024
e8ab32a
Merge branch 'master' into unsignedbounds
merykitty Sep 8, 2024
9b70213
change (~v & ones) == 0 to (v & ones) == ones
merykitty Sep 8, 2024
81f4e15
add doc to TypeInt, rename parameters, remove unused methods
merykitty Sep 10, 2024
a77e8f4
remove leftover code
merykitty Sep 10, 2024
2564378
refine comments
merykitty Sep 12, 2024
8a5370a
add comments, refactor functions to helper class
merykitty Sep 18, 2024
644bced
address reviews
merykitty Sep 19, 2024
f2d3f3b
formality
merykitty Sep 19, 2024
c440a72
comment adjust_lo empty case
merykitty Sep 20, 2024
4858e12
address reviews
merykitty Sep 20, 2024
7f3316f
Merge branch 'master' into unsignedbounds
merykitty Oct 20, 2024
41082f6
Merge branch 'master' into unsignedbounds
merykitty Nov 13, 2024
468834f
further reviews
merykitty Nov 13, 2024
c2d7d36
whitespace
merykitty Nov 13, 2024
dcc9030
build failures
merykitty Nov 13, 2024
7164653
build failure
merykitty Nov 13, 2024
8c0ac2f
Merge branch 'master' into unsignedbounds
merykitty Dec 6, 2024
cf1de62
move try_cast to Type
merykitty Dec 6, 2024
85acf6e
Merge branch 'master' into unsignedbounds
merykitty Dec 15, 2024
d033833
Merge branch 'master' into unsignedbounds
merykitty Jan 4, 2025
4d33014
copyright
merykitty Jan 4, 2025
539a032
Merge branch 'master' into unsignedbounds
merykitty Jan 22, 2025
c4d46c8
remove precompiled.hpp
merykitty Jan 22, 2025
98aaa03
Merge branch 'master' into unsignedbounds
merykitty Jan 27, 2025
ac1ddfc
Emmanuel's review
merykitty Jan 27, 2025
f85eff5
make con
merykitty Jan 28, 2025
01dc22d
exhaustive tests
kittyoracle Jan 29, 2025
c33576a
assignment operator
kittyoracle Jan 29, 2025
cf56091
refine first_violation
merykitty Jan 30, 2025
a849917
clean up intn_t
merykitty Jan 30, 2025
1d34a54
include
merykitty Jan 30, 2025
ee07d29
number lemmas
merykitty Jan 30, 2025
d87e036
harden SimpleCanonicalResult
merykitty Jan 30, 2025
0f347a5
Merge branch 'master' into unsignedbounds
merykitty Feb 12, 2025
3cd2586
Merge branch 'master' into unsignedbounds
merykitty Feb 13, 2025
5586036
refine comments
merykitty Feb 14, 2025
7b26016
Merge branch 'master' into unsignedbounds
merykitty Mar 3, 2025
727216f
reviews
merykitty Mar 3, 2025
9ca8023
Merge branch 'master' into unsignedbounds
merykitty Mar 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 28 additions & 29 deletions src/hotspot/share/opto/rangeinference.cpp
Original file line number Diff line number Diff line change
@@ -29,13 +29,12 @@

constexpr juint SMALLINT = 3; // a value too insignificant to consider widening

// This represents the result of an iterative calculation
template <class T>
class AdjustResult {
public:
// Denote whether there is a change in _data compared to the previous
// iteration
bool _progress;
bool _present; // whether the constraints are contradictory
bool _progress; // whether there is progress compared to the last iteration
bool _present; // whether the calculation arrives at contradiction
T _data;

static AdjustResult<T> make_empty() {
@@ -277,15 +276,15 @@ canonicalize_constraints_simple(const RangeInt<U>& bounds, const KnownBits<U>& b
// 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 <class S, class U>
CanonicalizedTypeIntPrototype<S, U>
typename TypeIntPrototype<S, U>::CanonicalizedTypeIntPrototype
TypeIntPrototype<S, U>::canonicalize_constraints() const {
RangeInt<S> srange = _srange;
RangeInt<U> urange = _urange;
// Trivial contradictions
if (srange._lo > srange._hi ||
urange._lo > urange._hi ||
(_bits._zeros & _bits._ones) != 0) {
return CanonicalizedTypeIntPrototype<S, U>::make_empty();
return CanonicalizedTypeIntPrototype::make_empty();
}

// Trivially canonicalize the bounds so that srange._lo and urange._hi are
@@ -309,7 +308,7 @@ TypeIntPrototype<S, U>::canonicalize_constraints() const {
urange._lo = MAX2<S>(urange._lo, srange._lo);
urange._hi = MIN2<S>(urange._hi, srange._hi);
if (urange._lo > urange._hi) {
return CanonicalizedTypeIntPrototype<S, U>::make_empty();
return CanonicalizedTypeIntPrototype::make_empty();
}

auto type = canonicalize_constraints_simple(urange, _bits);
@@ -325,7 +324,7 @@ TypeIntPrototype<S, U>::canonicalize_constraints() const {
auto pos_type = canonicalize_constraints_simple({urange._lo, U(srange._hi)}, _bits);

if (!neg_type._present && !pos_type._present) {
return CanonicalizedTypeIntPrototype<S, U>::make_empty();
return CanonicalizedTypeIntPrototype::make_empty();
} else if (!neg_type._present) {
return {true, {{S(pos_type._bounds._lo), S(pos_type._bounds._hi)},
pos_type._bounds, pos_type._bits}};
@@ -343,7 +342,7 @@ template <class S, class U>
int TypeIntPrototype<S, U>::normalize_widen(int w) const {
// Certain normalizations keep us sane when comparing types.
// The 'SMALLINT' covers constants and also CC and its relatives.
if (cardinality_from_bounds(_srange, _urange) <= SMALLINT) {
if (TypeIntHelper::cardinality_from_bounds(_srange, _urange) <= SMALLINT) {
return Type::WidenMin;
}
if (_srange._lo == std::numeric_limits<S>::min() && _srange._hi == std::numeric_limits<S>::max() &&
@@ -395,7 +394,7 @@ template class TypeIntPrototype<jlong, julong>;
// Compute the meet of 2 types, when dual is true, we are actually computing the
// join.
template <class CT, class S, class U>
const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(const TypeIntPrototype<S, U>&, int, bool), bool dual) {
const Type* TypeIntHelper::int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(const TypeIntPrototype<S, U>&, int, bool), bool dual) {
// Perform a fast test for common case; meeting the same types together.
if (i1 == t2 || t2 == Type::TOP) {
return i1;
@@ -444,16 +443,16 @@ const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(con
return nullptr;
}
}
template const Type* int_type_xmeet(const TypeInt* i1, const Type* t2,
const Type* (*make)(const TypeIntPrototype<jint, juint>&, int, bool), bool dual);
template const Type* int_type_xmeet(const TypeLong* i1, const Type* t2,
const Type* (*make)(const TypeIntPrototype<jlong, julong>&, int, bool), bool dual);
template const Type* TypeIntHelper::int_type_xmeet(const TypeInt* i1, const Type* t2,
const Type* (*make)(const TypeIntPrototype<jint, juint>&, int, bool), bool dual);
template const Type* TypeIntHelper::int_type_xmeet(const TypeLong* i1, const Type* t2,
const Type* (*make)(const TypeIntPrototype<jlong, julong>&, int, bool), bool dual);

// 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 <class CT>
const Type* int_type_widen(const CT* new_type, const CT* old_type, const CT* limit_type) {
const Type* TypeIntHelper::int_type_widen(const CT* new_type, const CT* old_type, const CT* limit_type) {
using S = std::remove_const_t<decltype(CT::_lo)>;
using U = std::remove_const_t<decltype(CT::_ulo)>;

@@ -513,14 +512,14 @@ const Type* int_type_widen(const CT* new_type, const CT* old_type, const CT* lim
TypeIntPrototype<S, U> prototype{{min, max}, {umin, umax}, {zeros, ones}};
return CT::try_make(prototype, Type::WidenMax);
}
template const Type* int_type_widen(const TypeInt* new_type, const TypeInt* old_type, const TypeInt* limit_type);
template const Type* int_type_widen(const TypeLong* new_type, const TypeLong* old_type, const TypeLong* limit_type);
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 <class CT>
const Type* int_type_narrow(const CT* new_type, const CT* old_type) {
const Type* TypeIntHelper::int_type_narrow(const CT* new_type, const CT* old_type) {
using S = decltype(CT::_lo);
using U = decltype(CT::_ulo);

@@ -555,8 +554,8 @@ const Type* int_type_narrow(const CT* new_type, const CT* old_type) {
RangeInt<U>{new_type->_ulo, new_type->_uhi});
return (nc > (oc >> 1) + (SMALLINT * 2)) ? old_type : new_type;
}
template const Type* int_type_narrow(const TypeInt* new_type, const TypeInt* old_type);
template const Type* int_type_narrow(const TypeLong* new_type, const TypeLong* old_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
@@ -578,7 +577,7 @@ static const char* int_name_near(T origin, const char* xname, char* buf, size_t
return buf;
}

const char* intname(char* buf, size_t buf_size, jint n) {
const char* TypeIntHelper::intname(char* buf, size_t buf_size, jint n) {
const char* str = int_name_near<jint>(max_jint, "maxint", buf, buf_size, n);
if (str != nullptr) {
return str;
@@ -593,7 +592,7 @@ const char* intname(char* buf, size_t buf_size, jint n) {
return buf;
}

const char* uintname(char* buf, size_t buf_size, juint n) {
const char* TypeIntHelper::uintname(char* buf, size_t buf_size, juint n) {
const char* str = int_name_near<juint>(max_juint, "maxuint", buf, buf_size, n);
if (str != nullptr) {
return str;
@@ -608,7 +607,7 @@ const char* uintname(char* buf, size_t buf_size, juint n) {
return buf;
}

const char* longname(char* buf, size_t buf_size, jlong n) {
const char* TypeIntHelper::longname(char* buf, size_t buf_size, jlong n) {
const char* str = int_name_near<jlong>(max_jlong, "maxlong", buf, buf_size, n);
if (str != nullptr) {
return str;
@@ -638,7 +637,7 @@ const char* longname(char* buf, size_t buf_size, jlong n) {
return buf;
}

const char* ulongname(char* buf, size_t buf_size, julong n) {
const char* TypeIntHelper::ulongname(char* buf, size_t buf_size, julong n) {
const char* str = int_name_near<julong>(max_julong, "maxulong", buf, buf_size, n);
if (str != nullptr) {
return str;
@@ -664,7 +663,7 @@ const char* ulongname(char* buf, size_t buf_size, julong n) {
}

template <class U>
const char* bitname(char* buf, size_t buf_size, U zeros, U ones) {
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) {
@@ -684,10 +683,10 @@ const char* bitname(char* buf, size_t buf_size, U zeros, U ones) {
buf[W] = 0;
return buf;
}
template const char* bitname(char* buf, size_t buf_size, juint zeros, juint ones);
template const char* bitname(char* buf, size_t buf_size, julong zeros, julong ones);
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 int_type_dump(const TypeInt* t, outputStream* st, bool verbose) {
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_equal(t, TypeInt::INT)) {
st->print("int");
@@ -734,7 +733,7 @@ void int_type_dump(const TypeInt* t, outputStream* st, bool verbose) {
}
}

void int_type_dump(const TypeLong* t, outputStream* st, bool verbose) {
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_equal(t, TypeLong::LONG)) {
st->print("long");
130 changes: 80 additions & 50 deletions src/hotspot/share/opto/rangeinference.hpp
Original file line number Diff line number Diff line change
@@ -33,6 +33,7 @@ class Type;
class TypeInt;
class TypeLong;

// A simple range in the signed or unsigned domain
template <class T>
class RangeInt {
public:
@@ -42,8 +43,17 @@ class RangeInt {

/**
* Bits that are known to be 0 or 1. A value v satisfies this constraint iff
* (v & zeros) == 0 && (v & ones) == ones. I.e, all bits that is set in zeros
* must be unset in v, and all bits that is set in ones must be set in v.
* (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
@@ -68,20 +78,9 @@ class KnownBits {
}
};

template <class S, class U>
class TypeIntPrototype;

template <class S, class U>
class CanonicalizedTypeIntPrototype {
public:
bool _present;
TypeIntPrototype<S, U> _data;

static CanonicalizedTypeIntPrototype<S, U> make_empty() {
return {false, {}};
}
};

// 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 S, class U>
class TypeIntPrototype {
public:
@@ -93,57 +92,88 @@ class TypeIntPrototype {
RangeInt<U> _urange;
KnownBits<U> _bits;

CanonicalizedTypeIntPrototype<S, U> canonicalize_constraints() const;
private:
friend class TypeInt;
friend class TypeLong;

template <class T1, class T2>
friend void test_canonicalize_constraints_simple();

template <class T1, class T2>
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<S, U> _data;

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
};

// The result is tuned down by one since we do not have empty type
// and this is not required to be accurate
template <class S, class U>
U cardinality_from_bounds(const RangeInt<S>& srange, const RangeInt<U>& urange) {
if (U(srange._lo) == urange._lo) {
return urange._hi - urange._lo;
// Various helper functions for TypeInt/TypeLong operations
class TypeIntHelper {
public:
// Calculate the cardinality of a TypeInt/TypeLong ignoring the bits
// constraints, the result is tuned down by 1 to ensure the bottom type is
// correctly calculated
template <class S, class U>
static U cardinality_from_bounds(const RangeInt<S>& srange, const RangeInt<U>& urange) {
static_assert(std::is_signed<S>::value, "");
static_assert(std::is_unsigned<U>::value, "");
static_assert(sizeof(S) == sizeof(U), "");

if (U(srange._lo) == urange._lo) {
return urange._hi - urange._lo;
}

return (urange._hi - U(srange._lo)) + (U(srange._hi) - urange._lo) + 1;
}

return (urange._hi - U(srange._lo)) + (U(srange._hi) - urange._lo) + 1;
}
template <class CT, class S, class U>
static const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(const TypeIntPrototype<S, U>&, int, bool), bool dual);

template <class CT, class S, class U>
const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(const TypeIntPrototype<S, U>&, int, bool), bool dual);

template <class CT>
bool int_type_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 <class CT>
static bool int_type_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 <class CT>
bool int_type_subset(const CT* super, const CT* sub) {
return super->_lo <= sub->_lo && super->_hi >= sub->_hi && super->_ulo <= sub->_ulo && super->_uhi >= sub->_uhi &&
(super->_bits._zeros &~ sub->_bits._zeros) == 0 && (super->_bits._ones &~ sub->_bits._ones) == 0;
}
template <class CT>
static bool int_type_subset(const CT* super, const CT* sub) {
return super->_lo <= sub->_lo && super->_hi >= sub->_hi && super->_ulo <= sub->_ulo && super->_uhi >= sub->_uhi &&
(super->_bits._zeros &~ sub->_bits._zeros) == 0 && (super->_bits._ones &~ sub->_bits._ones) == 0;
}

template <class CT>
const Type* int_type_widen(const CT* nt, const CT* ot, const CT* lt);
template <class CT>
static const Type* int_type_widen(const CT* nt, const CT* ot, const CT* lt);

template <class CT>
const Type* int_type_narrow(const CT* nt, const CT* ot);
template <class CT>
static const Type* int_type_narrow(const CT* nt, const CT* ot);

#ifndef PRODUCT
const char* intname(char* buf, size_t buf_size, jint n);
const char* uintname(char* buf, size_t buf_size, juint n);
const char* longname(char* buf, size_t buf_size, jlong n);
const char* ulongname(char* buf, size_t buf_size, julong n);
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 <class U>
const char* bitname(char* buf, size_t buf_size, U zeros, U ones);
template <class U>
static const char* bitname(char* buf, size_t buf_size, U zeros, U ones);

void int_type_dump(const TypeInt* t, outputStream* st, bool verbose);
void int_type_dump(const TypeLong* t, outputStream* st, bool verbose);
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
Loading