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

8353290: C2: Refactor PhaseIdealLoop::is_counted_loop() #24458

Open
wants to merge 16 commits into
base: master
Choose a base branch
from

Conversation

tabjy
Copy link
Member

@tabjy tabjy commented Apr 4, 2025

This PR refactors PhaseIdealLoop::is_counted_loop() into (mostly) CountedLoopConverter::is_counted_loop() and CountedLoopConverter::convert() to decouple the detection and conversion code. This enables us to try different loop configurations easily and finally convert once a counted loop is found.

A nested PhaseIdealLoop::CountedLoopConverter class is created to handle the context, but I'm not if this is the best name or place for it. Please let me know what you think.

Blocks JDK-8336759.


Progress

  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue
  • Change must be properly reviewed (2 reviews required, with at least 1 Reviewer, 1 Author)

Issue

  • JDK-8353290: C2: Refactor PhaseIdealLoop::is_counted_loop() (Enhancement - P4)

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/24458/head:pull/24458
$ git checkout pull/24458

Update a local copy of the PR:
$ git checkout pull/24458
$ git pull https://git.openjdk.org/jdk.git pull/24458/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 24458

View PR using the GUI difftool:
$ git pr show -t 24458

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/24458.diff

Using Webrev

Link to Webrev Comment

@bridgekeeper
Copy link

bridgekeeper bot commented Apr 4, 2025

👋 Welcome back kxu! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk
Copy link

openjdk bot commented Apr 4, 2025

❗ This change is not yet ready to be integrated.
See the Progress checklist in the description for automated requirements.

@openjdk
Copy link

openjdk bot commented Apr 4, 2025

@tabjy The following label will be automatically applied to this pull request:

  • hotspot-compiler

When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command.

@tabjy
Copy link
Member Author

tabjy commented Apr 7, 2025

/reviewers 2

C2 changes

@openjdk
Copy link

openjdk bot commented Apr 7, 2025

@tabjy
The total number of required reviews for this PR (including the jcheck configuration and the last /reviewers command) is now set to 2 (with at least 1 Reviewer, 1 Author).

@tabjy tabjy marked this pull request as ready for review April 9, 2025 15:49
@openjdk openjdk bot added the rfr Pull request is ready for review label Apr 9, 2025
@mlbridge
Copy link

mlbridge bot commented Apr 9, 2025

Webrevs

Copy link
Member

@chhagedorn chhagedorn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good idea to refactor this, thanks for tackling it! Some initial general comments, I will have a closer look at your patch later again.

@@ -310,7 +310,7 @@ IdealLoopTree* PhaseIdealLoop::insert_outer_loop(IdealLoopTree* loop, LoopNode*
// clone the outer loop as well as the inner, unrolling needs to only
// clone the inner loop etc. No optimizations need to change the outer
// strip mined loop as it is only a skeleton.
IdealLoopTree* PhaseIdealLoop::create_outer_strip_mined_loop(BoolNode *test, Node *cmp, Node *init_control,
IdealLoopTree* PhaseIdealLoop::create_outer_strip_mined_loop(Node *init_control,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally, for the touched code, you can also fix the * placement to be at the type.

Suggested change
IdealLoopTree* PhaseIdealLoop::create_outer_strip_mined_loop(Node *init_control,
IdealLoopTree* PhaseIdealLoop::create_outer_strip_mined_loop(Node* init_control,

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I tried to fix them as I go but missed this one.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the x -> head change, there are some more opportunities :-)

@@ -364,7 +364,7 @@ void PhaseIdealLoop::insert_loop_limit_check_predicate(ParsePredicateSuccessProj
#endif
}

Node* PhaseIdealLoop::loop_exit_control(Node* x, IdealLoopTree* loop) {
Node* PhaseIdealLoop::loop_exit_control(const Node* x, const IdealLoopTree* loop) const {
Copy link
Member

@chhagedorn chhagedorn Apr 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are quite some places where we use x to denote a loop_head. Maybe you can also fix that with this patch.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer a simple head for function named .*loop.*. Updated all relevant functions I touched.

Node* phi_incr = nullptr;
incr = loop_iv_incr(incr, x, loop, phi_incr);
assert(incr != nullptr && incr->Opcode() == Op_Add(bt), "no incr");
LoopExitTest exit_test = loop_exit_test(back_control, loop);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just an idea: Would it make sense to have a single LoopStructure (or another name) class that contains all the information stored now with separate structs LoopExitTest, LoopIvIncr and LoopIvStride? Then we could offer query methods for validation like is_valid(), is_stride_valid() etc., or check for existence like has_stride(), or access specific nodes like stride(), incr() etc.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me see what I can do! :)

IdealLoopTree* loop, float cl_prob, float le_fcnt,
Node*& entry_control, Node*& iffalse);

class CountedLoopConverter {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably quite a subjective matter, but what about just naming it CountedLoop? Then you have counted_loop.convert().

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree: I don't want to confuse it with CountedLoopNode or making it looks like extending from IdealLoopTree. Besides it wouldn't not necessarily guarantee a counted loop until confirmed by is_counted_loop()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I had in mind was something like that (I'm also fine with CountedLoopConverter:

CountedLoop counted_loop(...);
counted_loop.convert();
return counted_loop.is_valid();

But maybe you can explain in more detail what the follow-up work will be and how you use this class again later?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About the placement of this class. You could probably also move it out of the already huge PhaseIdealLoop class to make it a non-inner class, if there is not some strong coupling that you absolutely need.

IdealLoopTree* loop, float cl_prob, float le_fcnt,
Node*& entry_control, Node*& iffalse);

class CountedLoopConverter {
PhaseIdealLoop* _phase;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to add const whenever you can. For example, here you will probably not change the _phase pointer anymore:

Suggested change
PhaseIdealLoop* _phase;
PhaseIdealLoop* const _phase;

Copy link
Member Author

@tabjy tabjy Apr 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my initial thought, too; however, PhaseIdealLoop::insert_loop_limit_check_predicate(), ::lazy_replace(), ::set_subtree_ctrl() and many other mutations make this impossible.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might be confusing it with having:

const PhaseIdealLoop* _phase

where we cannot call any non-const methods. That's indeed not possible. But what I mean was to make the pointer const:

PhaseIdealLoop* const _phase;

such that you cannot do _phase = xyz later. You would probably not do that anyway but it's an easy addition and safety for all fields not being reassigned again. It also helps to see which fields are going to be updated as part of the mutable state and which fields are not.

Comment on lines 1327 to 1331
Node* _phi_incr;
Node* _stride;
bool _includes_limit;
BoolTest::mask _mask;
Node* _increment;
Copy link
Member

@chhagedorn chhagedorn Apr 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you have (_phi_)incr and increment. I suggest to make the naming consistent. I personally prefer full names whenever we can if it's not a universal abbreviation like cmp or iv. But what we count as well-known abbreviation is debatable and also depends on personal taste.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated s/_phi_incr/_phi_increment

float _cl_prob;
Node* _sfpt;
jlong _final_correction;
Node* _trunc1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trunc1 is a little hard to understand. Can we find a better name?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Droped Node* _trunc1 in favour of TypeInteger* _increment_truncation_type. Added a note in should_stress_long_counted_loop().

return false;
}

bool PhaseIdealLoop::CountedLoopConverter::is_counted_loop() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_counted_loop() is still huge. When trying to refactor it anyway, it might be a good idea to further split the code up into separate methods for all the different checks. That would increase the readability since we have a lot of different checks.

IdealLoopTree* loop, float cl_prob, float le_fcnt,
Node*& entry_control, Node*& iffalse);

class CountedLoopConverter {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About the placement of this class. You could probably also move it out of the already huge PhaseIdealLoop class to make it a non-inner class, if there is not some strong coupling that you absolutely need.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
hotspot-compiler [email protected] rfr Pull request is ready for review
Development

Successfully merging this pull request may close these issues.

2 participants