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

Emanuel Peter epeter at openjdk.org
Thu May 1 16:01:20 UTC 2025


On Thu, 1 May 2025 10:48:42 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

Made it down through `type.cpp`.
Will have to do a pass through the rest later. And then go through the whole thing again.

I feel like we are making good progress on this now :)

src/hotspot/share/opto/rangeinference.cpp line 694:

> 692:     return i1;
> 693:   }
> 694:   const CT* i2 = t2->try_cast<CT>();

And what is the significance of the `dual` state of i1 and `i2`? Should their `dual` state be the same?
Can we add some assert here?

src/hotspot/share/opto/rangeinference.hpp line 185:

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

Suggestion:

  static const Type* int_type_widen(const CT* new_type, const CT* old_type, const CT* limit_type);

  template <class CT>
  static const Type* int_type_narrow(const CT* new_type, const CT* old_type, const CT* limit_type);

For consistency with its definition.

src/hotspot/share/opto/type.cpp line 1806:

> 1804: bool TypeInt::contains(jint i) const {
> 1805:   juint u = i;
> 1806:   return i >= _lo && i <= _hi && u >= _ulo && u <= _uhi && _bits.is_satisfied_by(u);

Suggestion:

  return _lo  <= i && i <= _hi  &&
         _ulo <= u && u <= _uhi &&
         _bits.is_satisfied_by(u);

Optional, for readability.

src/hotspot/share/opto/type.cpp line 1809:

> 1807: }
> 1808: 
> 1809: bool TypeInt::contains(const TypeInt* t) const {

Is dual allowed here, or could we assert?

src/hotspot/share/opto/type.cpp line 1848:

> 1846:     // The widen bits must be allowed to run freely through the graph.
> 1847:     return (new TypeInt(TypeIntPrototype<jint, juint>{{ft->_lo, ft->_hi}, {ft->_ulo, ft->_uhi}, ft->_bits},
> 1848:                         this->_widen, false))->hashcons();

Why not use `TypeInt::make`?

src/hotspot/share/opto/type.cpp line 1930:

> 1928: bool TypeLong::contains(jlong i) const {
> 1929:   julong u = i;
> 1930:   return i >= _lo && i <= _hi && u >= _ulo && u <= _uhi && _bits.is_satisfied_by(u);

Suggestion:

  return _lo  <= i && i <= _hi  &&
         _ulo <= u && u <= _uhi &&
         _bits.is_satisfied_by(u);

For readability.

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

PR Review: https://git.openjdk.org/jdk/pull/17508#pullrequestreview-2810044167
PR Review Comment: https://git.openjdk.org/jdk/pull/17508#discussion_r2070423848
PR Review Comment: https://git.openjdk.org/jdk/pull/17508#discussion_r2070413425
PR Review Comment: https://git.openjdk.org/jdk/pull/17508#discussion_r2070420492
PR Review Comment: https://git.openjdk.org/jdk/pull/17508#discussion_r2070430474
PR Review Comment: https://git.openjdk.org/jdk/pull/17508#discussion_r2070432896
PR Review Comment: https://git.openjdk.org/jdk/pull/17508#discussion_r2070437258


More information about the hotspot-compiler-dev mailing list