RFR: 8336759: C2: int counted loop with long limit not recognized as counted loop

Christian Hagedorn chagedorn at openjdk.org
Tue Dec 3 09:40:44 UTC 2024


On Fri, 29 Nov 2024 01:08:04 GMT, Kangcheng Xu <kxu at openjdk.org> wrote:

> This patch implements [JDK-8336759](https://bugs.openjdk.org/browse/JDK-8336759) that recognizes int counted loops with long limits.
> 
> Currently, patterns like `for ( int i =...; i < long_limit; ...)` where int `i` is implicitly promoted to long (i.e., `(long) i < long_limit`) is not recognized as (int) counted loop. This patch speculatively and optimistically converts long limits to ints and deoptimize if the limit is outside int range, allowing more optimization opportunities. 
> 
> In other words, it transforms 
> 
> 
> for (int i = 0; (long) i < long_limit; i++) {...}
> 
> 
> to 
> 
> 
> if (int_min <= long_limit && long_limit <= int_max ) {
>     for (int i = 0;  i < (int) long_limit; i++) {...}
> } else {
>     trap: loop_limit_check
> }
> 
> 
> This could benefit calls to APIs like `long MemorySegment#byteSize()` when iterating over a long limit.

Nice enhancement! Couple of mostly minor comments.

src/hotspot/share/opto/loopnode.cpp line 1625:

> 1623: // counted loop but deoptimize if the limit is out of int range.
> 1624: // This is common pattern with "for (int i =...; i < long_limit; ...)" where int "i" is implicitly promoted to long,
> 1625: // i.e., "(long) i < long_limit", and therefore making it not an int counted loop without this transformation.

Maybe add here the visualization from the PR which makes it easier to grasp, something like:

In summary, we transform

  for (int i = 0; (long) i < long_limit; i++) {...}

to

  if (int_min <= long_limit && long_limit <= int_max ) {
    for (int i = 0;  i < (int) long_limit; i++) {...}
  } else {
    trap: loop_limit_check
  }

src/hotspot/share/opto/loopnode.cpp line 1626:

> 1624: // This is common pattern with "for (int i =...; i < long_limit; ...)" where int "i" is implicitly promoted to long,
> 1625: // i.e., "(long) i < long_limit", and therefore making it not an int counted loop without this transformation.
> 1626: bool PhaseIdealLoop::is_counted_loop_with_speculative_long_limit(Node* x, IdealLoopTree*&loop, BasicType iv_bt) {

I suggest to rename `x` to `loop_head` (could also be done in `do_is_counted_loop()`):
Suggestion:

bool PhaseIdealLoop::is_counted_loop_with_speculative_long_limit(Node* loop_head, IdealLoopTree*&loop, BasicType iv_bt) {

src/hotspot/share/opto/loopnode.cpp line 1638:

> 1636:   Node* init_control = x->in(LoopNode::EntryControl);
> 1637: 
> 1638:   // Make sure there is a parse predicate for us to insert the loop limit check.

I usually write the predicate names capitalized to better highlight that these are well-defined names:
Suggestion:

  // Make sure there is a Loop Limit Check Parse Predicate for us to insert the Loop Limit Check Predicate above it.

src/hotspot/share/opto/loopnode.cpp line 1642:

> 1640:   const PredicateBlock* loop_limit_check_predicate_block = predicates.loop_limit_check_predicate_block();
> 1641:   if (!loop_limit_check_predicate_block->has_parse_predicate()) {
> 1642:     return false;

I suggest to also add here a trace message, similar to what we already do in `is_counted_loop()`:
https://github.com/openjdk/jdk/blob/c330b90b9f43f80c322153585fa78704358f0224/src/hotspot/share/opto/loopnode.cpp#L2013-L2023

src/hotspot/share/opto/loopnode.cpp line 1671:

> 1669:       cmp->in(1) == incr
> 1670:           ? new CmpINode(new_incr, new_limit) : new CmpINode(new_limit, new_incr),
> 1671:       cmp);

This is a little hard to read. How about:
Suggestion:

  Node* new_cmp = cmp->in(1) == incr ? new CmpINode(new_incr, new_limit) : new CmpINode(new_limit, new_incr);
  _igvn.register_new_node_with_optimizer(new_cmp, cmp);

src/hotspot/share/opto/loopnode.cpp line 1684:

> 1682:     _igvn.rehash_node_delayed(bol);
> 1683:     bol->replace_edge(new_cmp, cmp, &_igvn);
> 1684: 

New line can be removed
Suggestion:

src/hotspot/share/opto/loopnode.cpp line 1696:

> 1694:   Node* bol_limit = new BoolNode(cmp_limit, BoolTest::eq);
> 1695:   insert_loop_limit_check_predicate(init_control->as_IfTrue(), cmp_limit, bol_limit);
> 1696: 

New line can be removed
Suggestion:

src/hotspot/share/opto/loopnode.cpp line 1713:

> 1711:   Node* limit = nullptr;
> 1712:   Node* cmp = loop_exit_test(back_control, loop, incr, limit, bt, cl_prob);
> 1713: 

Not sure if that new line was intended
Suggestion:

src/hotspot/share/opto/loopnode.hpp line 1229:

> 1227:   bool is_counted_loop_with_speculative_long_limit(Node* x, IdealLoopTree*& loop, BasicType iv_bt);
> 1228:  private:
> 1229:   bool do_is_counted_loop(Node* x, IdealLoopTree*& loop, BasicType iv_bt);

Was like that before but I think `is_counted_loop()` is a bit misleading, suggesting it's a query but it's actually doing the conversion work. Since you now change these methods anyway, what do you think about the following naming suggestions?
- public `try_convert_to_counted_loop()`
  - calls: private `convert_to_counted_loop()`
  - call: private `convert_to_counted_loop_with_speculative_long_limit()`

test/hotspot/jtreg/compiler/loopopts/TestIntCountedLoopLongLimit.java line 41:

> 39:  */
> 40: public class TestIntCountedLoopLongLimit {
> 41:     private static final Random RNG = jdk.test.lib.Utils.getRandomInstance();

I suggest to rather use an `import` instead of a fully qualified name:
Suggestion:

import jdk.test.lib.Asserts;
import jdk.test.lib.Utils;

import java.util.Random;

/**
 * @test
 * @bug 8336759
 * @summary test long limits in int counted loops are speculatively converted to int for counted loop
 *         optimizations
 * @library /test/lib /
 * @requires vm.compiler2.enabled
 * @run driver compiler.loopopts.TestIntCountedLoopLongLimit
 */
public class TestIntCountedLoopLongLimit {
    private static final Random RNG = Utils.getRandomInstance();

test/hotspot/jtreg/compiler/loopopts/TestIntCountedLoopLongLimit.java line 88:

> 86:             "testCountedLoopWithSwappedComparisonOperand" })
> 87:     public static void runTestSimpleCountedLoops(RunInfo info) {
> 88:         long limit = RNG.nextLong(0, 1024 * 1024); // Choice a small number to avoid tests taking too long

Suggestion:

        long limit = RNG.nextLong(0, 1024 * 1024); // Choose a small number to avoid tests taking too long

test/hotspot/jtreg/compiler/loopopts/TestIntCountedLoopLongLimit.java line 127:

> 125: 
> 126:     // Use a larger stride to avoid tests taking too long
> 127:     private static final int LARGE_STRIDE = Integer.MAX_VALUE / 1024;

Can you move this to the top of the class to the other static final field?

test/hotspot/jtreg/compiler/loopopts/TestIntCountedLoopLongLimit.java line 179:

> 177:     }
> 178: 
> 179:     private static volatile long SOME_LONG = 42;

Same here, can you move this to the top of the class?

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

PR Review: https://git.openjdk.org/jdk/pull/22449#pullrequestreview-2474951014
PR Review Comment: https://git.openjdk.org/jdk/pull/22449#discussion_r1867278568
PR Review Comment: https://git.openjdk.org/jdk/pull/22449#discussion_r1867318157
PR Review Comment: https://git.openjdk.org/jdk/pull/22449#discussion_r1867324663
PR Review Comment: https://git.openjdk.org/jdk/pull/22449#discussion_r1867330528
PR Review Comment: https://git.openjdk.org/jdk/pull/22449#discussion_r1867342583
PR Review Comment: https://git.openjdk.org/jdk/pull/22449#discussion_r1867306191
PR Review Comment: https://git.openjdk.org/jdk/pull/22449#discussion_r1867305838
PR Review Comment: https://git.openjdk.org/jdk/pull/22449#discussion_r1867305458
PR Review Comment: https://git.openjdk.org/jdk/pull/22449#discussion_r1867304899
PR Review Comment: https://git.openjdk.org/jdk/pull/22449#discussion_r1867349691
PR Review Comment: https://git.openjdk.org/jdk/pull/22449#discussion_r1867351852
PR Review Comment: https://git.openjdk.org/jdk/pull/22449#discussion_r1867353100
PR Review Comment: https://git.openjdk.org/jdk/pull/22449#discussion_r1867353558


More information about the hotspot-compiler-dev mailing list