RFR: 8315066: Add unsigned bounds and known bits to TypeInt/Long [v56]

Emanuel Peter epeter at openjdk.org
Fri May 2 08:17:08 UTC 2025


On Fri, 2 May 2025 00:50:31 GMT, Quan Anh Mai <qamai at openjdk.org> wrote:

>> Hi,
>> 
>> This patch adds unsigned bounds and known bits constraints to TypeInt and TypeLong. This opens more transformation opportunities in an elegant manner as well as helps avoid some ad-hoc rules in Hotspot.
>> 
>> In general, a `TypeInt/Long` represents a set of values `x` that satisfies: `x s>= lo && x s<= hi && x u>= ulo && x u<= uhi && (x & zeros) == 0 && (x & ones) == ones`. These constraints are not independent, e.g. an int that lies in [0, 3] in signed domain must also lie in [0, 3] in unsigned domain and have all bits but the last 2 being unset. As a result, we must canonicalize the constraints (tighten the constraints so that they are optimal) before constructing a `TypeInt/Long` instance.
>> 
>> This is extracted from #15440 , node value transformations are left for later PRs. I have also added unit tests to verify the soundness of constraint normalization.
>> 
>> Please kindly review, thanks a lot.
>> 
>> Testing
>> 
>> - [x] GHA
>> - [x] Linux x64, tier 1-4
>
> Quan Anh Mai has updated the pull request incrementally with one additional commit since the last revision:
> 
>   Emanuel's reviews

Comments for `type.hpp`

src/hotspot/share/opto/type.hpp line 293:

> 291: 
> 292:   template <typename TypeClass>
> 293:   const TypeClass* try_cast() const;

This is a way to get to the `isa_...` via templated types, right?

I wonder if it might be better to name it `isa_???`, or even just `isa`, so that it is clearer that it is about the `isa` query.

Currently, it is not very clear what it does, until you look at the implementation. That's a bit unfortunate.

src/hotspot/share/opto/type.hpp line 636:

> 634:  * of a TypeInt iff:
> 635:  *
> 636:  * v >= _lo && v <= _hi && juint(v) >= _ulo && juint(v) <= _uhi && _bits.is_satisfied_by(v)

Suggestion:

 * A TypeInt represents a set of non-empty jint values. A jint v is an element
 * of a TypeInt iff:
 *   _lo  <=       v  <= _hi  &&
 *   _ulo <= juint(v) <= _uhi &&
 *   _bits.is_satisfied_by(v)

Would be equivalent, but more readable, right?

src/hotspot/share/opto/type.hpp line 645:

> 643:  *
> 644:  * Then, t1 and t2 both represent the set {3, 5}. We can also see that the
> 645:  * constraints of t2 are tightest possible. I.e there exists no TypeInt t3

Suggestion:

 * constraints of t2 are the tightest possible. I.e there exists no TypeInt t3

src/hotspot/share/opto/type.hpp line 649:

> 647:  *
> 648:  * t3._lo > t2._lo || t3._hi < t2._hi || t3._ulo > t2._ulo || t3._uhi < t2._uhi ||
> 649:  *     (t3._bits._zeros &~ t2._bis._zeros) != 0 || (t3._bits._ones &~ t2._bits._ones) != 0

Suggestion:

 * 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

src/hotspot/share/opto/type.hpp line 654:

> 652:  * t3._bits._zeros and t2._bits._zeros is not empty, which means that the
> 653:  * bits in t3._bits._zeros is not a subset of those in t2._bits._zeros, the
> 654:  * same applies to _bits._ones

Then you can refer to the `condition 5)` and `condition 6)` and the reader can find them quicker, and does not have to worry if you are counting 0 or 1 based ;)

src/hotspot/share/opto/type.hpp line 657:

> 655:  *
> 656:  * As a result, every TypeInt is canonicalized to its tightest form upon
> 657:  * construction. This makes it easier to reason about them in optimizations.

As a result of what? I think this could be better:
Suggestion:

 * To simplify reasoning about the types in optimizations, we canonicalize every
 * TypeInt to its tightest form, already at construction.

src/hotspot/share/opto/type.hpp line 702:

> 700:  *
> 701:  *   Proof of lemma 3:
> 702:  *   Lemma 3.1: For 2 jint value x, y such that they are both >= 0 or both < 0.

Suggestion:

 *   Lemma 3.1: Given 2 jint values x, y where either both >= 0 or both < 0.

src/hotspot/share/opto/type.hpp line 708:

> 706:  *   I.e. x <= y in the signed domain iff x <= y in the unsigned domain
> 707:  *
> 708:  *   Then, we have:

The two `Then` are confusing me a little. The assumption is only the first sentence, not `x <= y iff juint(x) <= juint(y)`, right?

Hmm, ah you have an additional Lemma 3.1 here. But it is a little unclear where that starts and ends. Maybe that can be fixed with some indentation?

src/hotspot/share/opto/type.hpp line 714:

> 712:  *   a. t._lo >= 0, we have:
> 713:  *
> 714:  *     0 <= t_lo <= jint(t._ulo)           (lemma 2.1)

Suggestion:

 *   a. 0 <= t._lo, we have:
 *
 *     0 <= t._lo <= jint(t._ulo)           (lemma 2.1)

src/hotspot/share/opto/type.hpp line 735:

> 733:  *   b. t._hi < 0. Similarly, t._lo == jint(t._ulo) and t._hi == jint(t._uhi)
> 734:  *
> 735:  *   c. t._lo < 0, t._hi >= 0.

Suggestion:

 *   c. t._lo < 0, 0 <= t._hi.

I like ordering numbers according to their value :)

src/hotspot/share/opto/type.hpp line 745:

> 743:  *
> 744:  *     In this case, all elements of t belongs to either [t._lo, jint(t._uhi)] or
> 745:  *     [jint(t._ulo), t._hi].

When you say "all elements belong to x or y", one might misunderstand that they all are in one range and the other is empty.
Suggestion:

 *     In this case, each element of t belongs to either [t._lo, jint(t._uhi)] or
 *     [jint(t._ulo), t._hi].

Here we look at them individually, and so it sounds a little less misleading to me. But that may just be my brain that sees it like this 😅

src/hotspot/share/opto/type.hpp line 772:

> 770: private:
> 771:   TypeInt(const TypeIntPrototype<jint, juint>& t, int w, bool dual);
> 772:   static const Type* try_make(const TypeIntPrototype<jint, juint>& t, int widen, bool dual);

Just an idea, very optional.
`try_make` does not say what it does when it fails.
Exception? `nullptr`? `TOP`?
You you could rename it to `try_make_else_null` or `make_or_null`. Something like that.

-------------

PR Review: https://git.openjdk.org/jdk/pull/17508#pullrequestreview-2811316183
PR Review Comment: https://git.openjdk.org/jdk/pull/17508#discussion_r2071192824
PR Review Comment: https://git.openjdk.org/jdk/pull/17508#discussion_r2071196300
PR Review Comment: https://git.openjdk.org/jdk/pull/17508#discussion_r2071197526
PR Review Comment: https://git.openjdk.org/jdk/pull/17508#discussion_r2071201252
PR Review Comment: https://git.openjdk.org/jdk/pull/17508#discussion_r2071202049
PR Review Comment: https://git.openjdk.org/jdk/pull/17508#discussion_r2071205778
PR Review Comment: https://git.openjdk.org/jdk/pull/17508#discussion_r2071210512
PR Review Comment: https://git.openjdk.org/jdk/pull/17508#discussion_r2071215527
PR Review Comment: https://git.openjdk.org/jdk/pull/17508#discussion_r2071222242
PR Review Comment: https://git.openjdk.org/jdk/pull/17508#discussion_r2071229350
PR Review Comment: https://git.openjdk.org/jdk/pull/17508#discussion_r2071232432
PR Review Comment: https://git.openjdk.org/jdk/pull/17508#discussion_r2071242488


More information about the hotspot-compiler-dev mailing list