Skip to content

Conversation

@marc-chevalier
Copy link
Member

@marc-chevalier marc-chevalier commented Feb 21, 2025

This collapses double shift lefts by constants in a single constant: (x << con1) << con2 => x << (con1 + con2). Care must be taken in the case con1 + con2 is bigger than the number of bits in the integer type. In this case, we must simplify to 0.

Moreover, the simplification logic of the sign extension trick had to be improved. For instance, we use (x << 16) >> 16 to convert a 32 bits into a 16 bits integer, with sign extension. When storing this into a 16-bit field, this can be simplified into simple x. But in the case where x is itself a left-shift expression, say y << 3, this PR makes the IR looks like (y << 19) >> 16 instead of the old ((y << 3) << 16) >> 16. The former logic didn't handle the case where the left and the right shift have different magnitude. In this PR, I generalize this simplification to cases where the left shift has a larger magnitude than the right shift. This improvement was needed not to miss vectorization opportunities: without the simplification, we have a left shift and a right shift instead of a single left shift, which confuses the type inference.

This also works for multiplications by powers of 2 since they are already translated into shifts.

Thanks,
Marc


Progress

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

Issue

  • JDK-8347459: C2: missing transformation for chain of shifts/multiplications by constants (Enhancement - P4)

Reviewers

Reviewing

Using git

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

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

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 23728

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

Using diff file

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

Using Webrev

Link to Webrev Comment

@bridgekeeper
Copy link

bridgekeeper bot commented Feb 21, 2025

👋 Welcome back marc-chevalier! 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 Feb 21, 2025

@marc-chevalier This change now passes all automated pre-integration checks.

ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details.

After integration, the commit message for the final commit will be:

8347459: C2: missing transformation for chain of shifts/multiplications by constants

Reviewed-by: dfenacci, epeter

You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed.

At the time when this comment was updated there had been 20 new commits pushed to the master branch:

  • c94bc74: 8352595: Regression of JDK-8314999 in IR matching
  • 48fac66: 8347406: [REDO] C1/C2 don't handle allocation failure properly during initialization (RuntimeStub::new_runtime_stub fatal crash)
  • 99c8a6e: 8350463: AArch64: Add vector rearrange support for small lane count vectors
  • b2da0d3: 8352289: [macos] Review skipped tests in tools/jpackage/macosx/SigningPackage*
  • ba658a7: 8349522: AArch64: Add backend implementation for new unsigned and saturating vector operations
  • 5625b43: 8350429: runtime/NMT/CheckForProperDetailStackTrace.java should only run for debug JVM
  • 2c60fc5: 8352176: Automate setting up environment for mac signing tests
  • 6e6a39d: 8347321: [ubsan] CGGlyphImages.m:553:30: runtime error: nan is outside the range of representable values of type 'unsigned long'
  • b84b292: 8352615: [Test] RISC-V: TestVectorizationMultiInvar.java fails on riscv64 without rvv support
  • a54445f: 8350609: Cleanup unknown unwind opcode (0xB) for windows
  • ... and 10 more: https://git.openjdk.org/jdk/compare/e23e0f85ef0f959a68adda0cff9e721ba2173ffc...master

As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details.

As you do not have Committer status in this project an existing Committer must agree to sponsor your change. Possible candidates are the reviewers of this PR (@jaskarth, @dafedafe, @eme64) but any other Committer may sponsor as well.

➡️ To flag this PR as ready for integration with the above commit message, type /integrate in a new comment. (Afterwards, your sponsor types /sponsor in a new comment to perform the integration).

@openjdk openjdk bot added the rfr Pull request is ready for review label Feb 21, 2025
@openjdk
Copy link

openjdk bot commented Feb 21, 2025

@marc-chevalier 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.

@openjdk openjdk bot added the hotspot-compiler hotspot-compiler-dev@openjdk.org label Feb 21, 2025
@mlbridge
Copy link

mlbridge bot commented Feb 21, 2025

Copy link
Member

@jaskarth jaskarth left a comment

Choose a reason for hiding this comment

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

This is a nice improvement! I'm glad to see that the limitation of Store/Shift folding requiring both shifts to have the same constant is being fixed. I've left some comments here.

I also noticed that in LShiftINode::Ideal, there is a transform that mentions not breaking i2s and i2b patterns. It might be worth looking separately to see if this condition can be relaxed because of the change to StoreNode::Ideal_sign_extended_input.

@dean-long
Copy link
Member

Care must be taken in the case con1 + con2 is bigger than the number of bits in the integer type. In this case, we must simplify to 0.

So 1 << 33 and 1 << 30 << 3 are still treated differently, according to the JVM spec?

@marc-chevalier
Copy link
Member Author

@dean-long Yes! 1 << 33 was already, and is still, transformed into 1 << 1, while 1 << 30 << 3 is NOT transformed into 1 << 33 but directly into 0. The second part is exhibited by this test:

    @Test
    @IR(failOn = {IRNode.LSHIFT})
    // Checks (x << 31) << 1 => 0
    public int testDoubleShift3(int x) {
        return (x << 31) << 1;
    }

(and a few similar other). I didn't add a test for the simple 1 << 33 since my code doesn't kick in unless there are 2 shifts, so nothing should have changed here. I can add such a test if you think it's needed.

@dean-long
Copy link
Member

I didn't add a test for the simple 1 << 33 since my code doesn't kick in unless there are 2 shifts, so nothing should have changed here. I can add such a test if you think it's needed.

I don't know if it's needed (we might have that covered in other tests), but adding now seems like a good idea for completeness.

Copy link
Contributor

@dafedafe dafedafe left a comment

Choose a reason for hiding this comment

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

Cool improvement @marc-chevalier! Thanks a lot!

Copy link
Contributor

@dafedafe dafedafe left a comment

Choose a reason for hiding this comment

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

I just left one more small comment. Otherwise it looks good to me. Thanks @marc-chevalier!

Copy link
Contributor

@eme64 eme64 left a comment

Choose a reason for hiding this comment

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

@marc-chevalier This is good work, thanks for working on this :)
I have a first batch of comments and suggestions.

set_req_X(MemNode::ValueIn, shl->in(1), phase);
return this;
// If (conIL > conIR) we are inventing 0 lower bits, and throwing
// away upper bits, but we are not introducing garbage bits by above.
Copy link
Contributor

Choose a reason for hiding this comment

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

What do you mean with by above?

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 mean from the upper bits, from the left if written like decimal numbers. Rephrasing that.

Comment on lines 3526 to 3527
// This case happens when the store source was itself a left shift, that gets merged
// into the inner left shift of the sign-extension.
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, above you were talking about a left and a right shift. But now you seem to be talking about some "source" left shift and an "inner" left shift. It's a bit confusing. Are you talking about this case?
(StoreB ... (RShiftI _ (LShiftI _ (LShiftI _ valIn conIL1 ) conIL2 ) conIR) )
Where the two left shifts are already combined by Ideal earlier?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes indeed. You seem confused about the term "source". I'm happy to change it, but I can't see a better word: a store (or move, or assignment) has a source and a destination, that are often resp. the left-hand side and the right-hand side of the expression/statement/instruction. I'm rephrasing that.

// +------------------------+---------+
// 31 8 7 0
// v[0..7] is meaningful, but v[8..31] is not. In this case, num_rejected_bits == 24
// If we do the shift left then right by 24 bits, we get:
Copy link
Contributor

Choose a reason for hiding this comment

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

It could be nice if you explicitly denoted the 3 cases, maybe even using some indentation for emphasis:

Case 1: conIL == conIR
Case 2: conIL > conIR
Case 3: conIL < conIR

Copy link
Contributor

Choose a reason for hiding this comment

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

It could also be nice to say what you are trying to show / prove here.

// +------------------+---------+-----+
// | sign bit | v[0..5] | 0 |
// +------------------+---------+-----+
// 31 8 7 2 1 0
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you make a statement if this is ok or not?

// | sign bit | v[0..5] | 0 |
// +------------------+---------+-----+
// 31 10 9 4 3 0
//
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we also have a case where conIL < conIR? What happens then?

@marc-chevalier
Copy link
Member Author

Fixed all the comments. You can do the next iteration.

Copy link
Contributor

@eme64 eme64 left a comment

Choose a reason for hiding this comment

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

@marc-chevalier Nice work. Thanks for all the updates, and extra time spent on the proof!

@openjdk openjdk bot added the ready Pull request is ready to be integrated label Mar 21, 2025
Copy link
Contributor

@dafedafe dafedafe left a comment

Choose a reason for hiding this comment

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

Thanks for reworking on the explanation etc. @marc-chevalier!
Still looks ok to me.

@marc-chevalier
Copy link
Member Author

/integrate

I've merged master in it, ran more tests. Seems all good.
Thanks for the many reviews!

@openjdk openjdk bot added the sponsor Pull request is ready to be sponsored label Mar 25, 2025
@openjdk
Copy link

openjdk bot commented Mar 25, 2025

@marc-chevalier
Your change (at version cd0b0c0) is now ready to be sponsored by a Committer.

@TobiHartmann
Copy link
Member

/sponsor

1 similar comment
@eme64
Copy link
Contributor

eme64 commented Mar 25, 2025

/sponsor

@openjdk
Copy link

openjdk bot commented Mar 25, 2025

Going to push as commit bdcac98.
Since your change was applied there have been 27 commits pushed to the master branch:

  • 3d3b782: 8352607: RISC-V: use cmove in min/max when Zicond is supported
  • 9f582e5: 8320997: RISC-V: C2 ReverseV
  • 6879c44: 8351405: G1: Collection set early pruning causes suboptimal region selection
  • aee4d69: 8348829: Remove ObjectMonitor perf counters
  • f9bcef4: 8351627: C2 AArch64 ROR/ROL: assert((1 << ((T>>1)+3)) > shift) failed: Invalid Shift value
  • 17dc30c: 8352414: JFR: JavaMonitorDeflateEvent crashes when deflated monitor object is dead
  • 6bc4803: 8351277: Remove pipewire from AIX build
  • c94bc74: 8352595: Regression of JDK-8314999 in IR matching
  • 48fac66: 8347406: [REDO] C1/C2 don't handle allocation failure properly during initialization (RuntimeStub::new_runtime_stub fatal crash)
  • 99c8a6e: 8350463: AArch64: Add vector rearrange support for small lane count vectors
  • ... and 17 more: https://git.openjdk.org/jdk/compare/e23e0f85ef0f959a68adda0cff9e721ba2173ffc...master

Your commit was automatically rebased without conflicts.

@openjdk openjdk bot added the integrated Pull request has been integrated label Mar 25, 2025
@openjdk openjdk bot closed this Mar 25, 2025
@openjdk openjdk bot removed ready Pull request is ready to be integrated rfr Pull request is ready for review sponsor Pull request is ready to be sponsored labels Mar 25, 2025
@openjdk
Copy link

openjdk bot commented Mar 25, 2025

@TobiHartmann @marc-chevalier Pushed as commit bdcac98.

💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored.

@openjdk
Copy link

openjdk bot commented Mar 25, 2025

@eme64 The command sponsor can only be used in open pull requests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

hotspot-compiler hotspot-compiler-dev@openjdk.org integrated Pull request has been integrated

Development

Successfully merging this pull request may close these issues.

6 participants