-
Notifications
You must be signed in to change notification settings - Fork 5.6k
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
8328998: Encoding support for Intel APX extended general-purpose registers #18476
Conversation
👋 Welcome back steveatgh! A progress list of the required criteria for merging this PR into |
@steveatgh 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:
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 247 new commits pushed to the
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 (@vnkozlov, @sviswa7, @jatin-bhateja, @eme64) but any other Committer may sponsor as well. ➡️ To flag this PR as ready for integration with the above commit message, type |
@steveatgh The following label will be automatically applied to this pull request:
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. |
Hi @steveatgh , |
- updates for 32-bit build
- make emit_opcode_prefix_and_encoding functions active for 32-bit and 64-bit builds
Webrevs
|
How can we be confident that the encoding is correct? Would it be possible to write tests for this? Maybe one that disassembles it and compares the result to a 3rd party disassembler offline or in-process hsdis? |
Also: what if the |
And I agree with @dean-long : you need to have some test for this. At least some test should have the flag enabled. Something that stresses the registers, and verifies the results could be an idea. Also: I suspect you would want to put this flag into the IR-framework whitelist, since it has no effect on the IR, right? |
Can the APX features be simulated, maybe even with SDE? Now you made the flag EXPERIMENTAL and by default false. What is the roadmap with this? It is generally not great to have default false flags, because the code underneath will just slowly rot and become broken. Is there a plan to eventually make it default true? What stops us from doing that already now? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have few comments.
assert(((base_enc & 0x7) != 4), "illegal addressing mode"); | ||
if (disp == 0 && no_relocation && ((base_enc & 0x7) != 5)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We loost information with this change. Can it be done as is_r13_encoding(base_enc)
and is_r12_enxoding(base_enc)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the comment. The "& 0x7" style was suggested to me by @sviswa7 as a efficient way to check for r12, r20, r28 in the assert, and for r13, r21, r29 in the if statement. I originally was comparing against each new APX register encoding. The style in the PR is concise but it can be done either way. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it - it is for few registers check now. What is common between rsp, r12, r20, r28 registers (except encoding)?
R12 is used for heap base in compressed oops and RSP is RSP. What are r20 and r28? Why they can't be used in this addressing mode?
Please add comments for all lines where you replaced checks for r*->encoding()
to say for which registers you do a check and why.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason registers 12, 20, and 28 are asserted out of that else at line 668 is they are handled earlier in an else around line 646.
} else if ((base_enc & 0x7) == 4) { // [rsp + disp]
I added a comment to this effect and have also added comments in the 3 other places in the function where the replacement was done, indicating the registers involved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good.
InstructionAttr *attributes); | ||
|
||
int vex_prefix_and_encode(int dst_enc, int nds_enc, int src_enc, | ||
VexSimdPrefix pre, VexOpcode opc, | ||
InstructionAttr *attributes); | ||
InstructionAttr *attributes, bool src_is_gpr = false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I saw a lot of usage of this method with src_is_gpr
is true
. What is actual most common case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @vnkozlov. I believe false is the most common case. If I counted right, I see 400+ calls total and 37 calls with src_is_gpr set to true.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good. Thank you for checking.
|
||
void simd_prefix(XMMRegister xreg, XMMRegister nds, Address adr, VexSimdPrefix pre, | ||
VexOpcode opc, InstructionAttr *attributes); | ||
|
||
int simd_prefix_and_encode(XMMRegister dst, XMMRegister nds, XMMRegister src, VexSimdPrefix pre, | ||
VexOpcode opc, InstructionAttr *attributes); | ||
VexOpcode opc, InstructionAttr *attributes, bool src_is_gpr = false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same question as for vex_prefix_and_encode
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @vnkozlov. I believe false is the most common case here too. Again, if my counts are close, I see ~180 calls total and 12 calls with src_is_gpr = true.
src/hotspot/cpu/x86/globals_x86.hpp
Outdated
|
||
"mitigations for the Intel JCC erratum") \ | ||
\ | ||
product(bool, UseAPX, false, EXPERIMENTAL, \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spacing to \
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@steveatgh, do you plan to add code to vm_version_x86.*
to check for presence of AVX and setting this flag accordingly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @vnkozlov.
Spacing fixed locally.
Regarding the UseAPX flag, yes, a subsequent PR (see JDK-8329030) will tie the logic of the flag in with querying the hardware features.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay.
Thank you @dean-long for the comment. I agree, tests are needed. Up to this point we have not had a separate formal tool to test encoding of x86. I did a lot of manual testing by adding loops that used r0-r31in different addressing patterns. I put these in a stub file that would be compiled by hotspot but not executed. I manually compared the disassembly of that against the output of similar assembly included in a small C program and run on the SDE. This worked pretty well for debugging but the manual aspect of it makes it error-prone and it takes a lot of time, too much time if iterating an implementation. Subsequent pull requests will add encoding support for additional APX instructions (e.g. those using New Data Destination). Maybe one of these PRs can include a tool for testing instruction encoding for APX features. What do you think? |
Thank you @eme64 for the comments. The functionality of the UseAPX flag is, as you point out, incomplete in this pull request. A subsequent PR (see JDK-8329030) will tie the logic of the flag in with a query of the hardware features. It was added in this PR thinking it could be useful for testing or debugging the encoding functionality. |
void Assembler::prefix(Address adr, Register reg, bool byteinst) { | ||
void Assembler::prefix_rex2(Address adr, bool is_map1) { | ||
int bits = is_map1 ? REX2BIT_M0 : 0; | ||
bits |= get_base_prefix_bits(adr.base()->encoding()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bits |= get_base_prefix_bits(adr.base()->encoding()); | |
bits |= get_base_prefix_bits(adr.base()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per section 3.7.5 of Intel SDM (Index ∗ Scale) + Displacement is a valid addressing mode. Thus we should set the bits corresponding to extended base register encoding only if its a valid register.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @jatin-bhateja. I've made the change.
|
||
void Assembler::prefix_rex2(Address adr, Register reg, bool byteinst, bool is_map1) { | ||
int bits = is_map1 ? REX2BIT_M0 : 0; | ||
bits |= get_base_prefix_bits(adr.base()->encoding()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also needs fix to:
bits |= get_base_prefix_bits(adr.base());
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @sviswa7, made the change.
@@ -635,7 +644,8 @@ void Assembler::emit_operand_helper(int reg_enc, int base_enc, int index_enc, | |||
scale, index_enc, base_enc); | |||
emit_data(disp, rspec, disp32_operand); | |||
} | |||
} else if (base_enc == rsp->encoding() LP64_ONLY(|| base_enc == r12->encoding())) { | |||
} else if ((base_enc & 0x7) == 4) { | |||
// rbp | r12 | r20 | r28 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment should be:
// rsp | r12 | r20 | r28
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, done.
assert(base_enc != rsp->encoding() LP64_ONLY(&& base_enc != r12->encoding()), "illegal addressing mode"); | ||
if (disp == 0 && no_relocation && | ||
base_enc != rbp->encoding() LP64_ONLY(&& base_enc != r13->encoding())) { | ||
// !(rbp | r12 | r20 | r28) were handled above |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comment should be:
// (rsp | r12 | r20 | r28) were handled above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, done.
@@ -3550,28 +3617,28 @@ void Assembler::movq(Register dst, XMMRegister src) { | |||
NOT_LP64(assert(VM_Version::supports_sse2(), "")); | |||
InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); | |||
// swap src/dst to get correct prefix | |||
int encode = simd_prefix_and_encode(src, xnoreg, as_XMMRegister(dst->encoding()), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); | |||
int encode = simd_prefix_and_encode(src, xnoreg, as_XMMRegister(dst->encoding()), VEX_SIMD_66, VEX_OPCODE_0F, &attributes, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here the last argument to simd_prefix_and_encode shouldn't be true as src is not gpr?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because of the swap of src and dst args in this case, I think src is actually a gpr here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you are correct. Thanks for the clarification.
if (adr.index_needs_rex2()) { | ||
assert(false, "prefix(Register dst, Address adr) does not support handling of an X"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this could be written as:
assert(!adr.index_needs_rex2(), "prefix(Register dst, Address adr) does not support handling of an X");
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice. Thank you, done.
emit_int24(get_prefixq(src, dst), | ||
0x0F, | ||
(unsigned char)0xBE); | ||
int prefix = get_prefixq(src, dst, true /* page1 */); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are not consistent in the comment is_map1, M0, page1 all refer to the same thing. Also some places there is no comment that the true is for is_map1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you. Naming is now consistent with APX spec naming (is_map1 for the bool argument and M0 for the bit name). I added inline comment /* is_map1 */ for calls that pass the boolean argument.
@@ -14179,4 +14476,4 @@ void InstructionAttr::set_address_attributes(int tuple_type, int input_size_in_b | |||
_tuple_type = tuple_type; | |||
_input_size_in_bits = input_size_in_bits; | |||
} | |||
} | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New line missing at the end of file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, done.
int encode = prefixq_and_encode(dst->encoding(), src->encoding()); | ||
emit_int24(0x0F, (unsigned char)0xB8, (0xC0 | encode)); | ||
int encode = prefixq_and_encode(dst->encoding(), src->encoding(), true); | ||
emit_opcode_prefix_and_encoding((unsigned char)0xB8, 0xC0, encode); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
void Assembler::popcntq(Register dst, Address src) also need to be handled for rex2 generation. get_prefixq() will return a 16 bit entity and so call to emit_int32 directly is not correct.
emit_int32((unsigned char)0xF3,
get_prefixq(src, dst),
0x0F,
(unsigned char)0xB8);
Likewise void Assembler::cvttsd2siq(Register dst, Address src) also needs to be updated to handle extended gprs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, missed these. Updated popcntq(Register dst, Address src) and cvttsd2siq(Register dst, Address src) for egpr support.
} else { | ||
emit_int24((prefix & 0xFF00) >> 8, prefix & 0x00FF, b1); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need a check for UseAPX > 0 here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sviswa7, sorry can you clarify what check is needed here. Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I understand now. Have added an assert to require UseAPX if prefix is WREX2.
@steveatgh I just saw that we from Oracle did not run any tests for this. Can you please hold off for a day or two until we have the testing completed? I'm sure you did exhaustive testing - but still I'd like to make sure it runs fine on all the x64 machines we have. |
@steveatgh I think it could make sense to add a simple "hello world" JTREG test that enables the |
@@ -1603,83 +1665,91 @@ void Assembler::andl(Register dst, Register src) { | |||
|
|||
void Assembler::andnl(Register dst, Register src1, Register src2) { | |||
assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); | |||
InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); | |||
int encode = vex_prefix_and_encode(dst->encoding(), src1->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); | |||
assert(!needs_eevex(dst, src1, src2) || UseAPX, "extended gpr use requires UseAPX and UseAVX > 2"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technical detail: UseAPX and UseAVX > 2
sounds wrong. Did you mean to say "or"? Because UseAPX is only enabled when UseAVX >= 3
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, see comment below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the confusion comes from the "or" in the code, and the "and" in the assert description.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. I removed the "and UseAVX > 2" here. Thank you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assert is repeated a lot. Instead of it I think UseAPX
assert check should be done in needs_rex2()
and needs_eevex()
when they return true
.
int8_t w = 0x01; | ||
Prefix p = Prefix_EMPTY; | ||
if (needs_eevex(crc, adr.base(), adr.index())) { | ||
assert(UseAPX, "extended gpr use requires UseAPX and UseAVX > 2"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe here the "and" makes sense, but not sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, "and" is technically correct. APX includes instructions that require AVX 3 (evex) encoding for extended gpr use, together with +UseAPX.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to say "and UseAVX >2" because you switch off UseAPX
in vm_version_x86.cpp
when UseAVX < 3
.
// APX support code currently requires UseAPX > 2. This may change in the future. | ||
if (UseAPX && (UseAVX < 3)) { | ||
if (!FLAG_IS_DEFAULT(UseAPX)) { | ||
warning("UseAPX is only available when UseAVX > 2"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warning("UseAPX is only available when UseAVX > 2"); | |
warning("UseAPX is only available when UseAVX > 2. Disabling UseAPX."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would tell the user what we are doing, just like with the UseAVX flag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, done.
Wait. Does this mean that if I enable the |
We will not start APX encoding instructions with this PR. APX encoding only comes into play when extended GPRs are used and the register allocator hasn't been extended to allocate extended GPRs yet. That will come in follow-up patches. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Few comments.
@@ -1002,6 +1002,13 @@ void VM_Version::get_processor_features() { | |||
} | |||
} | |||
|
|||
if (UseAPX && (UseAVX < 3)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it enough to have AVX512F present for APX? What about Knight CPUs which have limited AVX512 features?
@@ -1002,6 +1002,13 @@ void VM_Version::get_processor_features() { | |||
} | |||
} | |||
|
|||
if (UseAPX && (UseAVX < 3)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should add code which checks CPUID features bit to set UseAPX
. Or set it to false
unconditionally in this PR regardless UseAVX value with comment "APX is not supported on this CPU". Otherwise someone will switch it on command line on avx512 machine.
Or we should push #18562 first. Which I prefer.
int8_t w = 0x01; | ||
Prefix p = Prefix_EMPTY; | ||
if (needs_eevex(crc, adr.base(), adr.index())) { | ||
assert(UseAPX, "extended gpr use requires UseAPX and UseAVX > 2"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to say "and UseAVX >2" because you switch off UseAPX
in vm_version_x86.cpp
when UseAVX < 3
.
@@ -1603,83 +1665,91 @@ void Assembler::andl(Register dst, Register src) { | |||
|
|||
void Assembler::andnl(Register dst, Register src1, Register src2) { | |||
assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); | |||
InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); | |||
int encode = vex_prefix_and_encode(dst->encoding(), src1->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); | |||
assert(!needs_eevex(dst, src1, src2) || UseAPX, "extended gpr use requires UseAPX and UseAVX > 2"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assert is repeated a lot. Instead of it I think UseAPX
assert check should be done in needs_rex2()
and needs_eevex()
when they return true
.
Thanks @vnkozlov for the comments.
|
void Assembler::stmxcsr( Address dst) { | ||
if (UseAVX > 0 ) { | ||
void Assembler::stmxcsr(Address dst) { | ||
if (UseAVX > 0 && !UseAPX ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New && !UseAPX
check is strange here. If UseAPX
is true
we will execute } else {
part of code which was executed only for SSE (UseAVX == 0) before. Is this intentional? This needs comment explaining why we do that if it is intentional.
I see in other place you have adr.base_needs_rex2() || adr.index_needs_rex2()
check. Do we need it here too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The !UseAPX test was added because if UseAPX is enabled we want to support extended register use via rex2 encoding in the else clause. The existing vex encoding remains when UseAPX is not enabled. There is a needs_rex2 check of address registers in the call to ::vex_prefix, asserting if UseAPX not enabled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a comment on this to the stmxcsr/ldmxcsr functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay
Add instruction encoding support for Intel APX extended general-purpose registers:
Intel Advanced Performance Extensions (APX) doubles the number of general-purpose registers, from 16 to 32. For more information about APX, see https://www.intel.com/content/www/us/en/developer/articles/technical/advanced-performance-extensions-apx.html.
By specification, instruction encoding remains unchanged for instructions using only the lower 16 GPRs. For cases where one or more instruction operands reference extended GPRs (Egprs), encoding targets either REX2, an extension of REX encoding, or an extended version of EVEX encoding. These new encoding schemes extend or modify existing instruction prefixes only when Egprs are used.
Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/18476/head:pull/18476
$ git checkout pull/18476
Update a local copy of the PR:
$ git checkout pull/18476
$ git pull https://git.openjdk.org/jdk.git pull/18476/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 18476
View PR using the GUI difftool:
$ git pr show -t 18476
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/18476.diff
Webrev
Link to Webrev Comment