Compare commits

...

68 Commits

Author SHA1 Message Date
Daniel Fuchs
d4984d5e5a merge latest changes from master branch 2025-06-12 17:32:28 +01:00
jeremy
8d33ea7395 8354646: java.awt.TextField allows to identify the spaces in a password when double clicked at the starting and end of the text
Reviewed-by: aivanov, kizune, tr, psadhukhan
2025-06-12 16:11:35 +00:00
David Briemann
3c53057fa6 8359232: [PPC64] C2: Clean up ppc.ad: add instr sizes, remove comments
Reviewed-by: mdoerr
2025-06-12 15:27:44 +00:00
Roland Westrelin
1fcede053c 8358334: C2/Shenandoah: incorrect execution with Unsafe
Reviewed-by: wkemper, shade
2025-06-12 15:02:38 +00:00
Afshin Zafari
fae9c7a3f0 8351661: NMT: VMATree should support separate call-stacks for reserve and commit operations
Reviewed-by: gziemski, jsjolen
2025-06-12 14:46:39 +00:00
Emanuel Peter
dd68829017 8347273: C2: VerifyIterativeGVN for Ideal and Identity
Reviewed-by: chagedorn, mhaessig
2025-06-12 14:19:08 +00:00
Emanuel Peter
b85fe02be5 8358600: Template-Framework Library: Template for TestFramework test class
Reviewed-by: chagedorn, mhaessig
2025-06-12 14:12:14 +00:00
Radim Vansa
e18277b470 8352075: Perf regression accessing fields
Reviewed-by: coleenp, iklam, jsjolen
2025-06-12 12:29:15 +00:00
Rohitash Kumar
e5ce5c57c8 8357959: (bf) ByteBuffer.allocateDirect initialization can result in large TTSP spikes
Reviewed-by: shade, alanb
2025-06-12 12:23:42 +00:00
kabutz
91fdd72c97 8355726: LinkedBlockingDeque fixes and improvements
Reviewed-by: vklang, dl
2025-06-12 11:44:04 +00:00
Marc Chevalier
b6ec93b038 8359121: C2: Region added by vectorizedMismatch intrinsic can survive as a dead node after IGVN
Reviewed-by: thartmann, chagedorn
2025-06-12 11:40:31 +00:00
Anjian Wen
65e63b6ab4 8359218: RISC-V: Only enable CRC32 intrinsic when AvoidUnalignedAccess == false
Reviewed-by: fyang, fjiang
2025-06-12 10:44:47 +00:00
Johannes Bechberger
3f0fef2c9c 8359135: New test TestCPUTimeSampleThrottling fails intermittently
Reviewed-by: mdoerr
2025-06-12 08:54:21 +00:00
SendaoYan
3e0ef832cc 8359083: Test jdkCheckHtml.java should report SkippedException rather than report fails when miss tidy
Reviewed-by: hannesw
2025-06-12 08:18:00 +00:00
SendaoYan
7b7136b4ec 8359181: Error messages generated by configure --help after 8301197
Reviewed-by: erikj, ihse
2025-06-12 08:10:27 +00:00
SendaoYan
5886ef728f 8359182: Use @requires instead of SkippedException for MaxPath.java
Reviewed-by: bpb, bchristi
2025-06-12 07:51:29 +00:00
Matthias Baesken
d7aa349820 8357826: Avoid running some jtreg tests when asan is configured
Reviewed-by: sspitsyn, amitkumar, lmesnik, syan, lucy, cjplummer
2025-06-12 07:08:39 +00:00
Ioi Lam
3b32f6a8ec 8344556: [Graal] compiler/intrinsics/bmi/* fail when AOTCache cannot be loaded
Reviewed-by: dnsimon, kvn
2025-06-12 00:41:39 +00:00
Aleksei Efimov
c4bcb39577 http3: update H3InsertionsLimitTest to start after receival of client settings 2025-06-12 00:34:52 +01:00
Serguei Spitsyn
8f73357004 8358815: Exception event spec has stale reference to catch_klass parameter
Reviewed-by: cjplummer, alanb
2025-06-11 18:51:54 +00:00
Calvin Cheung
429158218b 8357382: runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java#aot fails with Xcomp and C1
Reviewed-by: iklam, kvn
2025-06-11 18:10:34 +00:00
Mohamed Issa
ef4cbec6fb 8358556: Assert when running with -XX:-UseLibmIntrinsic
Reviewed-by: sviswanathan, kvn
2025-06-11 17:47:03 +00:00
Naoto Sato
e9216efefc 8358734: Remove JavaTimeSupplementary resource bundles
Reviewed-by: jlu, joehw, iris
2025-06-11 16:04:26 +00:00
Alan Bateman
e5196fc24d 8358764: (sc) SocketChannel.close when thread blocked in read causes connection to be reset (win)
Reviewed-by: jpai, vyazici
2025-06-11 14:09:45 +00:00
Jatin Bhateja
c98dffa186 8357982: Fix several failing BMI tests with -XX:+UseAPX
Reviewed-by: epeter, sviswanathan
2025-06-11 13:48:56 +00:00
Matthias Baesken
7d7fc69355 8357570: [macOS] os::Bsd::available_memory() might return too low values
Reviewed-by: clanger, mdoerr, lucy
2025-06-11 13:32:57 +00:00
Anton Artemov
42ab8fcfb9 8265754: Move suspend/resume API from HandshakeState
Reviewed-by: coleenp, dholmes, pchilanomate
2025-06-11 12:42:57 +00:00
Daniel Fuchs
4cee27b5ec merge latest changes from master branch 2025-06-11 13:01:35 +01:00
Benoît Maillard
bf7d40d048 8356751: IGV: clean up redundant field _should_send_method
Co-authored-by: Manuel Hässig <mhaessig@openjdk.org>
Reviewed-by: mhaessig, thartmann, dfenacci
2025-06-11 11:08:38 +00:00
Khalid Boulanouare
5ae32c4c86 8352149: Test java/awt/Frame/MultiScreenTest.java fails: Window list is empty
Reviewed-by: aivanov, abhiscxk
2025-06-11 10:25:28 +00:00
Martin Doerr
56ce70c5df 8359165: AIX build broken after 8358799
Reviewed-by: kbarrett, jkern
2025-06-11 08:28:48 +00:00
Martin Doerr
abc76c6b5b 8359126: [AIX] new test TestImplicitNullChecks.java fails
Reviewed-by: rcastanedalo, dbriemann
2025-06-11 08:28:31 +00:00
Rajan Halade
9586817cea 8359170: Add 2 TLS and 2 CS Sectigo roots
Reviewed-by: mullan
2025-06-10 21:59:29 +00:00
Albert Mingkun Yang
38b877e941 8358294: Remove unnecessary GenAlignment
Reviewed-by: iwalulya, tschatzl
2025-06-10 20:10:19 +00:00
Alex Menkov
8f487d26c0 8358577: Test serviceability/jvmti/thread/GetCurrentContendedMonitor/contmon01/contmon01.java failed: unexpexcted monitor object
Reviewed-by: cjplummer, syan, sspitsyn
2025-06-10 19:05:08 +00:00
Calvin Cheung
500a3a2d0a 8358799: Refactor os::jvm_path()
Reviewed-by: dholmes, jsjolen
2025-06-10 16:20:33 +00:00
Roland Westrelin
a2f99fd88b 8354383: C2: enable sinking of Type nodes out of loop
Reviewed-by: chagedorn, thartmann
2025-06-10 14:19:19 +00:00
Daniel Fuchs
0582bd290d 8357639: DigestEchoClient fails intermittently due to: java.io.IOException: Data received while in pool
Reviewed-by: djelinski
2025-06-10 11:01:50 +00:00
Varada M
3ff83ec49e 8358159: Empty mode/padding in cipher transformations
Reviewed-by: amitkumar, valeriep
2025-06-10 08:17:52 +00:00
Benoît Maillard
7c9c8ba363 8356780: PhaseMacroExpand::_has_locks is unused
Reviewed-by: mhaessig, chagedorn, kvn, mchevalier
2025-06-10 07:27:10 +00:00
Aleksey Shipilev
ca7b885873 8358749: Fix input checks in Vector API intrinsics
Co-authored-by: Vladimir Ivanov <vlivanov@openjdk.org>
Reviewed-by: vlivanov, sviswanathan
2025-06-10 06:15:13 +00:00
Matthias Bläsing
92be7821f5 8353950: Clipboard interaction on Windows is unstable
8332271: Reading data from the clipboard from multiple threads crashes the JVM

Reviewed-by: abhiscxk, dnguyen
2025-06-10 00:21:18 +00:00
David Holmes
bcf860703d 8355792: Remove expired flags in JDK 26
Reviewed-by: coleenp, kvn
2025-06-09 22:25:20 +00:00
Ioi Lam
d186dacdb7 8357591: Re-enable CDS test cases for jvmci after JDK-8345826
Reviewed-by: dholmes, kvn
2025-06-09 21:54:55 +00:00
David Holmes
ef45c8154c 8346237: Obsolete the UseOprofile flag
Reviewed-by: coleenp, kvn
2025-06-09 20:59:30 +00:00
Justin Lu
cd9b1bc820 8358426: Improve lazy computation in Locale
Reviewed-by: naoto, liach
2025-06-09 20:49:33 +00:00
Naoto Sato
fcb68ea22d 8358626: Emit UTF-8 CLDR resources
Reviewed-by: erikj, vyazici
2025-06-09 19:03:21 +00:00
Coleen Phillimore
eb256deb80 8358326: Use oopFactory array allocation
Reviewed-by: fparain, stefank
2025-06-09 18:33:00 +00:00
Magnus Ihse Bursie
156187accc 8356978: Convert unicode sequences in Java source code to UTF-8
Co-authored-by: Alexey Ivanov <aivanov@openjdk.org>
Reviewed-by: naoto, prr, joehw
2025-06-09 17:58:49 +00:00
kieran-farrell
a377773fa7 8358617: java/net/HttpURLConnection/HttpURLConnectionExpectContinueTest.java fails with 403 due to system proxies
Reviewed-by: dfuchs
2025-06-09 17:39:39 +00:00
Jiangli Zhou
cae1fd3385 8357632: CDS test failures on static JDK
Reviewed-by: ccheung, dholmes
2025-06-09 16:08:18 +00:00
Phil Race
eb8ee8bdc7 8358731: Remove jdk.internal.access.JavaAWTAccess.java
Reviewed-by: dfuchs, serb
2025-06-09 16:01:18 +00:00
Alexander Zvegintsev
2103dc15cb 8358452: JNI exception pending in Java_sun_awt_screencast_ScreencastHelper_remoteDesktopKeyImpl of screencast_pipewire.c:1214 (ID: 51119)
Reviewed-by: psadhukhan, serb, aivanov, avu
2025-06-09 13:35:01 +00:00
Joel Sikström
1c72b350e4 8357053: ZGC: Improved utility for ZPageAge
Co-authored-by: Axel Boldt-Christmas <aboldtch@openjdk.org>
Reviewed-by: sjohanss, stefank
2025-06-09 09:03:12 +00:00
Jaikiran Pai
ac6499c558 quic: separate out the idle termination timer and the STREAM_DATA_BLOCKED timer 2025-06-09 14:21:47 +05:30
Per Minborg
52338c94f6 8358520: Improve lazy computation in BreakIteratorResourceBundle and related classes
Reviewed-by: naoto, jlu
2025-06-09 07:00:51 +00:00
Roberto Castañeda Lozano
91f12600d2 8345067: C2: enable implicit null checks for ZGC reads
Reviewed-by: aboldtch, kvn, epeter
2025-06-09 06:23:17 +00:00
Daniel Skantz
6c616c71ec 8357822: C2: Multiple string optimization tests are no longer testing string concatenation optimizations
Reviewed-by: rcastanedalo, epeter
2025-06-09 06:11:05 +00:00
Kim Barrett
e94ad551c6 8342639: Global operator new in adlc has wrong exception spec
Reviewed-by: kvn, mdoerr
2025-06-07 20:34:34 +00:00
Rajan Halade
d735255919 8345414: Google CAInterop test failures
Reviewed-by: weijun
Backport-of: 8e9ba788ae04a9a617a393709bf2c51a0c157206
2025-06-06 21:35:21 +00:00
Stuart Marks
d024f58e61 8358809: Improve link to stdin.encoding from java.lang.IO
Reviewed-by: naoto
2025-06-06 20:07:43 +00:00
Alexandre Iline
026975a1aa 8358721: Update JCov for class file version 70
Reviewed-by: iris, alanb, erikj
2025-06-06 15:05:43 +00:00
Vicente Romero
8adb052b46 8341778: Some javac tests ignore the result of JavacTask::call
Reviewed-by: shade
2025-06-06 14:11:27 +00:00
Hamlin Li
9658cecde3 8358685: [TEST] AOTLoggingTag.java failed with missing log message
Reviewed-by: iklam, shade
2025-06-06 13:59:17 +00:00
Jaikiran Pai
75bd7fb4de quic: simplify idle timeout management 2025-06-06 19:22:18 +05:30
Jaikiran Pai
2140d5433d http3: rely on the sole isOpen() method instead of isOpen() and isClosed() 2025-06-06 19:22:18 +05:30
Jaikiran Pai
7c1f31d7b3 quic: do not let h3 idle (in pool) timeout to influence the quic transport idle timeout 2025-06-06 19:22:17 +05:30
Fernando Guallini
b2e7cda6a0 8358171: Additional code coverage for PEM API
Reviewed-by: ascarpino
2025-06-06 09:53:25 +00:00
308 changed files with 9055 additions and 4552 deletions

View File

@ -1,6 +1,6 @@
#!/bin/bash
#
# Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@ -366,7 +366,7 @@ EOT
# Print additional help, e.g. a list of toolchains and JVM features.
# This must be done by the autoconf script.
( CONFIGURE_PRINT_ADDITIONAL_HELP=true . $generated_script PRINTF=printf )
( CONFIGURE_PRINT_ADDITIONAL_HELP=true . $generated_script PRINTF=printf ECHO=echo )
cat <<EOT

View File

@ -1192,8 +1192,8 @@ var getJibProfilesDependencies = function (input, common) {
server: "jpg",
product: "jcov",
version: "3.0",
build_number: "1",
file: "bundles/jcov-3.0+1.zip",
build_number: "3",
file: "bundles/jcov-3.0+3.zip",
environment_name: "JCOV_HOME",
},

View File

@ -46,6 +46,8 @@ CLDR_GEN_DONE := $(GENSRC_DIR)/_cldr-gensrc.marker
TZ_DATA_DIR := $(MODULE_SRC)/share/data/tzdata
ZONENAME_TEMPLATE := $(MODULE_SRC)/share/classes/java/time/format/ZoneName.java.template
# The `-utf8` option is used even for US English, as some names
# may contain non-ASCII characters, such as “Türkiye”.
$(CLDR_GEN_DONE): $(wildcard $(CLDR_DATA_DIR)/dtd/*.dtd) \
$(wildcard $(CLDR_DATA_DIR)/main/en*.xml) \
$(wildcard $(CLDR_DATA_DIR)/supplemental/*.xml) \
@ -61,7 +63,8 @@ $(CLDR_GEN_DONE): $(wildcard $(CLDR_DATA_DIR)/dtd/*.dtd) \
-basemodule \
-year $(COPYRIGHT_YEAR) \
-zntempfile $(ZONENAME_TEMPLATE) \
-tzdatadir $(TZ_DATA_DIR))
-tzdatadir $(TZ_DATA_DIR) \
-utf8)
$(TOUCH) $@
TARGETS += $(CLDR_GEN_DONE)

View File

@ -45,7 +45,8 @@ $(CLDR_GEN_DONE): $(wildcard $(CLDR_DATA_DIR)/dtd/*.dtd) \
-baselocales "en-US" \
-year $(COPYRIGHT_YEAR) \
-o $(GENSRC_DIR) \
-tzdatadir $(TZ_DATA_DIR))
-tzdatadir $(TZ_DATA_DIR) \
-utf8)
$(TOUCH) $@
TARGETS += $(CLDR_GEN_DONE)

View File

@ -187,22 +187,18 @@ public class HelloWorld {
new Run("none", "Hello from Cupertino")
}),
new Paragraph("title", new Run[] {
new Run("none", "\u53F0\u5317\u554F\u5019\u60A8\u0021")
new Run("none", "台北問候您!")
}),
new Paragraph("title", new Run[] {
new Run("none", "\u0391\u03B8\u03B7\u03BD\u03B1\u03B9\u0020" // Greek
+ "\u03B1\u03C3\u03C0\u03B1\u03B6\u03BF\u03BD"
+ "\u03C4\u03B1\u03B9\u0020\u03C5\u03BC\u03B1"
+ "\u03C2\u0021")
new Run("none", "Αθηναι ασπαζονται υμας!") // Greek
}),
new Paragraph("title", new Run[] {
new Run("none", "\u6771\u4eac\u304b\u3089\u4eca\u65e5\u306f")
new Run("none", "東京から今日は")
}),
new Paragraph("title", new Run[] {
new Run("none", "\u05e9\u05dc\u05d5\u05dd \u05de\u05d9\u05e8\u05d5"
+ "\u05e9\u05dc\u05d9\u05dd")
new Run("none", "שלום מירושלים")
}),
new Paragraph("title", new Run[] {
new Run("none", "\u0633\u0644\u0627\u0645")
new Run("none", "سلام")
}), };
}

View File

@ -3921,6 +3921,10 @@ ins_attrib ins_alignment(4); // Required alignment attribute (must
// compute_padding() function must be
// provided for the instruction
// Whether this node is expanded during code emission into a sequence of
// instructions and the first instruction can perform an implicit null check.
ins_attrib ins_is_late_expanded_null_check_candidate(false);
//----------OPERANDS-----------------------------------------------------------
// Operand definitions must precede instruction definitions for correct parsing
// in the ADLC because operands constitute user defined types which are used in

View File

@ -106,6 +106,13 @@ instruct zLoadP(iRegPNoSp dst, memory8 mem, rFlagsReg cr)
match(Set dst (LoadP mem));
predicate(UseZGC && !needs_acquiring_load(n) && n->as_Load()->barrier_data() != 0);
effect(TEMP dst, KILL cr);
// The main load is a candidate to implement implicit null checks, as long as
// legitimize_address() does not require a preceding lea instruction to
// materialize the memory operand. The absence of a preceding lea instruction
// is guaranteed for immLoffset8 memory operands, because these do not lead to
// out-of-range offsets (see definition of immLoffset8). Fortunately,
// immLoffset8 memory operands are the most common ones in practice.
ins_is_late_expanded_null_check_candidate(opnd_array(1)->opcode() == INDOFFL8);
ins_cost(4 * INSN_COST);
@ -117,7 +124,11 @@ instruct zLoadP(iRegPNoSp dst, memory8 mem, rFlagsReg cr)
// Fix up any out-of-range offsets.
assert_different_registers(rscratch2, as_Register($mem$$base));
assert_different_registers(rscratch2, $dst$$Register);
ref_addr = __ legitimize_address(ref_addr, 8, rscratch2);
int size = 8;
assert(!this->is_late_expanded_null_check_candidate() ||
!MacroAssembler::legitimize_address_requires_lea(ref_addr, size),
"an instruction that can be used for implicit null checking should emit the candidate memory access first");
ref_addr = __ legitimize_address(ref_addr, size, rscratch2);
}
__ ldr($dst$$Register, ref_addr);
z_load_barrier(masm, this, ref_addr, $dst$$Register, rscratch1);

View File

@ -129,16 +129,21 @@ class MacroAssembler: public Assembler {
a.lea(this, r);
}
// Whether materializing the given address for a LDR/STR requires an
// additional lea instruction.
static bool legitimize_address_requires_lea(const Address &a, int size) {
return a.getMode() == Address::base_plus_offset &&
!Address::offset_ok_for_immed(a.offset(), exact_log2(size));
}
/* Sometimes we get misaligned loads and stores, usually from Unsafe
accesses, and these can exceed the offset range. */
Address legitimize_address(const Address &a, int size, Register scratch) {
if (a.getMode() == Address::base_plus_offset) {
if (! Address::offset_ok_for_immed(a.offset(), exact_log2(size))) {
block_comment("legitimize_address {");
lea(scratch, a);
block_comment("} legitimize_address");
return Address(scratch);
}
if (legitimize_address_requires_lea(a, size)) {
block_comment("legitimize_address {");
lea(scratch, a);
block_comment("} legitimize_address");
return Address(scratch);
}
return a;
}

View File

@ -141,6 +141,7 @@ instruct zLoadP(iRegPdst dst, memoryAlg4 mem, flagsRegCR0 cr0)
%{
match(Set dst (LoadP mem));
effect(TEMP_DEF dst, KILL cr0);
ins_is_late_expanded_null_check_candidate(true);
ins_cost(MEMORY_REF_COST);
predicate((UseZGC && n->as_Load()->barrier_data() != 0)
@ -160,6 +161,7 @@ instruct zLoadP_acq(iRegPdst dst, memoryAlg4 mem, flagsRegCR0 cr0)
%{
match(Set dst (LoadP mem));
effect(TEMP_DEF dst, KILL cr0);
ins_is_late_expanded_null_check_candidate(true);
ins_cost(3 * MEMORY_REF_COST);
// Predicate on instruction order is implicitly present due to the predicate of the cheaper zLoadP operation

View File

@ -4036,6 +4036,10 @@ ins_attrib ins_field_cbuf_insts_offset(-1);
ins_attrib ins_field_load_ic_hi_node(0);
ins_attrib ins_field_load_ic_node(0);
// Whether this node is expanded during code emission into a sequence of
// instructions and the first instruction can perform an implicit null check.
ins_attrib ins_is_late_expanded_null_check_candidate(false);
//----------OPERANDS-----------------------------------------------------------
// Operand definitions must precede instruction definitions for correct
// parsing in the ADLC because operands constitute user defined types
@ -6683,6 +6687,7 @@ instruct reinterpretL(iRegLdst dst) %{
match(Set dst (VectorReinterpret dst));
ins_cost(0);
format %{ "reinterpret $dst" %}
size(0);
ins_encode( /*empty*/ );
ins_pipe(pipe_class_empty);
%}
@ -6691,6 +6696,7 @@ instruct reinterpretX(vecX dst) %{
match(Set dst (VectorReinterpret dst));
ins_cost(0);
format %{ "reinterpret $dst" %}
size(0);
ins_encode( /*empty*/ );
ins_pipe(pipe_class_empty);
%}
@ -6810,7 +6816,6 @@ instruct cond_set_0_oop(iRegNdst dst, flagsRegSrc crx, iRegPsrc src1) %{
format %{ "CMOVE $dst, $crx eq, 0, $src1 \t// encode: preserve 0" %}
size(4);
ins_encode %{
// This is a Power7 instruction for which no machine description exists.
__ isel_0($dst$$Register, $crx$$CondRegister, Assembler::equal, $src1$$Register);
%}
ins_pipe(pipe_class_default);
@ -6942,7 +6947,6 @@ instruct cond_set_0_ptr(iRegPdst dst, flagsRegSrc crx, iRegPsrc src1) %{
format %{ "CMOVE $dst, $crx eq, 0, $src1 \t// decode: preserve 0" %}
size(4);
ins_encode %{
// This is a Power7 instruction for which no machine description exists.
__ isel_0($dst$$Register, $crx$$CondRegister, Assembler::equal, $src1$$Register);
%}
ins_pipe(pipe_class_default);
@ -7419,8 +7423,6 @@ instruct cmovI_reg_isel(cmpOp cmp, flagsRegSrc crx, iRegIdst dst, iRegIsrc src)
format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %}
size(4);
ins_encode %{
// This is a Power7 instruction for which no machine description
// exists. Anyways, the scheduler should be off on Power7.
int cc = $cmp$$cmpcode;
__ isel($dst$$Register, $crx$$CondRegister,
(Assembler::Condition)(cc & 3), /*invert*/((~cc) & 8), $src$$Register);
@ -7436,8 +7438,6 @@ instruct cmovL_reg_isel(cmpOp cmp, flagsRegSrc crx, iRegLdst dst, iRegLsrc src)
format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %}
size(4);
ins_encode %{
// This is a Power7 instruction for which no machine description
// exists. Anyways, the scheduler should be off on Power7.
int cc = $cmp$$cmpcode;
__ isel($dst$$Register, $crx$$CondRegister,
(Assembler::Condition)(cc & 3), /*invert*/((~cc) & 8), $src$$Register);
@ -7453,8 +7453,6 @@ instruct cmovN_reg_isel(cmpOp cmp, flagsRegSrc crx, iRegNdst dst, iRegNsrc src)
format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %}
size(4);
ins_encode %{
// This is a Power7 instruction for which no machine description
// exists. Anyways, the scheduler should be off on Power7.
int cc = $cmp$$cmpcode;
__ isel($dst$$Register, $crx$$CondRegister,
(Assembler::Condition)(cc & 3), /*invert*/((~cc) & 8), $src$$Register);
@ -7470,8 +7468,6 @@ instruct cmovP_reg_isel(cmpOp cmp, flagsRegSrc crx, iRegPdst dst, iRegPsrc src)
format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %}
size(4);
ins_encode %{
// This is a Power7 instruction for which no machine description
// exists. Anyways, the scheduler should be off on Power7.
int cc = $cmp$$cmpcode;
__ isel($dst$$Register, $crx$$CondRegister,
(Assembler::Condition)(cc & 3), /*invert*/((~cc) & 8), $src$$Register);
@ -9917,13 +9913,6 @@ instruct andcL_reg_reg(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{
// of java.lang.Float etc., e.g.
// int floatToIntBits(float value)
// float intBitsToFloat(int bits)
//
// Notes on the implementation on ppc64:
// For Power7 and earlier, the rules are limited to those which move between a
// register and a stack-location, because we always have to go through memory
// when moving between a float register and an integer register.
// This restriction is removed in Power8 with the introduction of the mtfprd
// and mffprd instructions.
instruct moveL2D_reg(regD dst, iRegLsrc src) %{
match(Set dst (MoveL2D src));
@ -12430,6 +12419,7 @@ instruct minI_reg_reg_isel(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsRegC
effect(KILL cr0);
ins_cost(DEFAULT_COST*2);
size(8);
ins_encode %{
__ cmpw(CR0, $src1$$Register, $src2$$Register);
__ isel($dst$$Register, CR0, Assembler::less, /*invert*/false, $src1$$Register, $src2$$Register);
@ -12443,6 +12433,7 @@ instruct maxI_reg_reg_isel(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsRegC
effect(KILL cr0);
ins_cost(DEFAULT_COST*2);
size(8);
ins_encode %{
__ cmpw(CR0, $src1$$Register, $src2$$Register);
__ isel($dst$$Register, CR0, Assembler::greater, /*invert*/false, $src1$$Register, $src2$$Register);
@ -12452,7 +12443,6 @@ instruct maxI_reg_reg_isel(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsRegC
//---------- Population Count Instructions ------------------------------------
// Popcnt for Power7.
instruct popCountI(iRegIdst dst, iRegIsrc src) %{
match(Set dst (PopCountI src));
predicate(UsePopCountInstruction);
@ -12466,7 +12456,6 @@ instruct popCountI(iRegIdst dst, iRegIsrc src) %{
ins_pipe(pipe_class_default);
%}
// Popcnt for Power7.
instruct popCountL(iRegIdst dst, iRegLsrc src) %{
predicate(UsePopCountInstruction);
match(Set dst (PopCountL src));
@ -13295,6 +13284,7 @@ instruct repl2F_immF0(iRegLdst dst, immF_0 zero) %{
Matcher::vector_element_basic_type(n) == T_FLOAT);
format %{ "LI $dst, #0 \t// replicate2F" %}
size(4);
ins_encode %{
__ li($dst$$Register, 0x0);
%}
@ -13923,6 +13913,7 @@ instruct overflowAddL_reg_reg(flagsRegCR0 cr0, iRegLsrc op1, iRegLsrc op2) %{
match(Set cr0 (OverflowAddL op1 op2));
format %{ "add_ $op1, $op2\t# overflow check long" %}
size(12);
ins_encode %{
__ li(R0, 0);
__ mtxer(R0); // clear XER.SO
@ -13935,6 +13926,7 @@ instruct overflowSubL_reg_reg(flagsRegCR0 cr0, iRegLsrc op1, iRegLsrc op2) %{
match(Set cr0 (OverflowSubL op1 op2));
format %{ "subfo_ R0, $op2, $op1\t# overflow check long" %}
size(12);
ins_encode %{
__ li(R0, 0);
__ mtxer(R0); // clear XER.SO
@ -13947,6 +13939,7 @@ instruct overflowNegL_reg(flagsRegCR0 cr0, immL_0 zero, iRegLsrc op2) %{
match(Set cr0 (OverflowSubL zero op2));
format %{ "nego_ R0, $op2\t# overflow check long" %}
size(12);
ins_encode %{
__ li(R0, 0);
__ mtxer(R0); // clear XER.SO
@ -13959,6 +13952,7 @@ instruct overflowMulL_reg_reg(flagsRegCR0 cr0, iRegLsrc op1, iRegLsrc op2) %{
match(Set cr0 (OverflowMulL op1 op2));
format %{ "mulldo_ R0, $op1, $op2\t# overflow check long" %}
size(12);
ins_encode %{
__ li(R0, 0);
__ mtxer(R0); // clear XER.SO
@ -13997,6 +13991,7 @@ instruct repl4F_immF0(vecX dst, immF_0 zero) %{
Matcher::vector_element_basic_type(n) == T_FLOAT);
format %{ "XXLXOR $dst, $zero \t// replicate4F" %}
size(4);
ins_encode %{
__ xxlxor($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
%}

View File

@ -96,6 +96,7 @@ instruct zLoadP(iRegPNoSp dst, memory mem, iRegPNoSp tmp, rFlagsReg cr)
match(Set dst (LoadP mem));
predicate(UseZGC && n->as_Load()->barrier_data() != 0);
effect(TEMP dst, TEMP tmp, KILL cr);
ins_is_late_expanded_null_check_candidate(true);
ins_cost(4 * DEFAULT_COST);

View File

@ -2619,6 +2619,10 @@ ins_attrib ins_alignment(4); // Required alignment attribute (must
// compute_padding() function must be
// provided for the instruction
// Whether this node is expanded during code emission into a sequence of
// instructions and the first instruction can perform an implicit null check.
ins_attrib ins_is_late_expanded_null_check_candidate(false);
//----------OPERANDS-----------------------------------------------------------
// Operand definitions must precede instruction definitions for correct parsing
// in the ADLC because operands constitute user defined types which are used in

View File

@ -203,15 +203,15 @@ void VM_Version::common_initialize() {
}
}
// Misc Intrinsics could depend on RVV
// Misc Intrinsics that could depend on RVV.
if (UseZba || UseRVV) {
if (!AvoidUnalignedAccesses && (UseZba || UseRVV)) {
if (FLAG_IS_DEFAULT(UseCRC32Intrinsics)) {
FLAG_SET_DEFAULT(UseCRC32Intrinsics, true);
}
} else {
if (!FLAG_IS_DEFAULT(UseCRC32Intrinsics)) {
warning("CRC32 intrinsic requires Zba or RVV instructions (not available on this CPU)");
warning("CRC32 intrinsic are not available on this CPU.");
}
FLAG_SET_DEFAULT(UseCRC32Intrinsics, false);
}

View File

@ -118,6 +118,10 @@ instruct zLoadP(rRegP dst, memory mem, rFlagsReg cr)
predicate(UseZGC && n->as_Load()->barrier_data() != 0);
match(Set dst (LoadP mem));
effect(TEMP dst, KILL cr);
// The main load is a candidate to implement implicit null checks. The
// barrier's slow path includes an identical reload, which does not need to be
// registered in the exception table because it is dominated by the main one.
ins_is_late_expanded_null_check_candidate(true);
ins_cost(125);

View File

@ -465,13 +465,19 @@ address TemplateInterpreterGenerator::generate_math_entry(AbstractInterpreter::M
__ call_VM_leaf0(CAST_FROM_FN_PTR(address, SharedRuntime::dtan));
}
} else if (kind == Interpreter::java_lang_math_tanh) {
assert(StubRoutines::dtanh() != nullptr, "not initialized");
if (StubRoutines::dtanh() != nullptr) {
__ movdbl(xmm0, Address(rsp, wordSize));
__ call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::dtanh())));
} else {
return nullptr; // Fallback to default implementation
}
} else if (kind == Interpreter::java_lang_math_cbrt) {
assert(StubRoutines::dcbrt() != nullptr, "not initialized");
__ movdbl(xmm0, Address(rsp, wordSize));
__ call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::dcbrt())));
if (StubRoutines::dcbrt() != nullptr) {
__ movdbl(xmm0, Address(rsp, wordSize));
__ call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::dcbrt())));
} else {
return nullptr; // Fallback to default implementation
}
} else if (kind == Interpreter::java_lang_math_abs) {
assert(StubRoutines::x86::double_sign_mask() != nullptr, "not initialized");
__ movdbl(xmm0, Address(rsp, wordSize));

View File

@ -2055,6 +2055,10 @@ ins_attrib ins_alignment(1); // Required alignment attribute (must
// compute_padding() function must be
// provided for the instruction
// Whether this node is expanded during code emission into a sequence of
// instructions and the first instruction can perform an implicit null check.
ins_attrib ins_is_late_expanded_null_check_candidate(false);
//----------OPERANDS-----------------------------------------------------------
// Operand definitions must precede instruction definitions for correct parsing
// in the ADLC because operands constitute user defined types which are used in
@ -10527,7 +10531,8 @@ instruct xorI_rReg_im1_ndd(rRegI dst, rRegI src, immI_M1 imm)
// Xor Register with Immediate
instruct xorI_rReg_imm(rRegI dst, immI src, rFlagsReg cr)
%{
predicate(!UseAPX);
// Strict predicate check to make selection of xorI_rReg_im1 cost agnostic if immI src is -1.
predicate(!UseAPX && n->in(2)->bottom_type()->is_int()->get_con() != -1);
match(Set dst (XorI dst src));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
@ -10541,7 +10546,8 @@ instruct xorI_rReg_imm(rRegI dst, immI src, rFlagsReg cr)
instruct xorI_rReg_rReg_imm_ndd(rRegI dst, rRegI src1, immI src2, rFlagsReg cr)
%{
predicate(UseAPX);
// Strict predicate check to make selection of xorI_rReg_im1_ndd cost agnostic if immI src2 is -1.
predicate(UseAPX && n->in(2)->bottom_type()->is_int()->get_con() != -1);
match(Set dst (XorI src1 src2));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
@ -10559,6 +10565,7 @@ instruct xorI_rReg_mem_imm_ndd(rRegI dst, memory src1, immI src2, rFlagsReg cr)
predicate(UseAPX);
match(Set dst (XorI (LoadI src1) src2));
effect(KILL cr);
ins_cost(150);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
format %{ "exorl $dst, $src1, $src2\t# int ndd" %}
@ -11201,7 +11208,8 @@ instruct xorL_rReg_im1_ndd(rRegL dst,rRegL src, immL_M1 imm)
// Xor Register with Immediate
instruct xorL_rReg_imm(rRegL dst, immL32 src, rFlagsReg cr)
%{
predicate(!UseAPX);
// Strict predicate check to make selection of xorL_rReg_im1 cost agnostic if immL32 src is -1.
predicate(!UseAPX && n->in(2)->bottom_type()->is_long()->get_con() != -1L);
match(Set dst (XorL dst src));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
@ -11215,7 +11223,8 @@ instruct xorL_rReg_imm(rRegL dst, immL32 src, rFlagsReg cr)
instruct xorL_rReg_rReg_imm(rRegL dst, rRegL src1, immL32 src2, rFlagsReg cr)
%{
predicate(UseAPX);
// Strict predicate check to make selection of xorL_rReg_im1_ndd cost agnostic if immL32 src2 is -1.
predicate(UseAPX && n->in(2)->bottom_type()->is_long()->get_con() != -1L);
match(Set dst (XorL src1 src2));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
@ -11234,6 +11243,7 @@ instruct xorL_rReg_mem_imm(rRegL dst, memory src1, immL32 src2, rFlagsReg cr)
match(Set dst (XorL (LoadL src1) src2));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(150);
format %{ "exorq $dst, $src1, $src2\t# long ndd" %}
ins_encode %{

View File

@ -1261,69 +1261,6 @@ void os::pd_print_cpu_info(outputStream* st, char* buf, size_t buflen) {
// Nothing to do beyond of what os::print_cpu_info() does.
}
static char saved_jvm_path[MAXPATHLEN] = {0};
// Find the full path to the current module, libjvm.so.
void os::jvm_path(char *buf, jint buflen) {
// Error checking.
if (buflen < MAXPATHLEN) {
assert(false, "must use a large-enough buffer");
buf[0] = '\0';
return;
}
// Lazy resolve the path to current module.
if (saved_jvm_path[0] != 0) {
strcpy(buf, saved_jvm_path);
return;
}
Dl_info dlinfo;
int ret = dladdr(CAST_FROM_FN_PTR(void *, os::jvm_path), &dlinfo);
assert(ret != 0, "cannot locate libjvm");
char* rp = os::realpath((char *)dlinfo.dli_fname, buf, buflen);
assert(rp != nullptr, "error in realpath(): maybe the 'path' argument is too long?");
// If executing unit tests we require JAVA_HOME to point to the real JDK.
if (Arguments::executing_unit_tests()) {
// Look for JAVA_HOME in the environment.
char* java_home_var = ::getenv("JAVA_HOME");
if (java_home_var != nullptr && java_home_var[0] != 0) {
// Check the current module name "libjvm.so".
const char* p = strrchr(buf, '/');
if (p == nullptr) {
return;
}
assert(strstr(p, "/libjvm") == p, "invalid library name");
stringStream ss(buf, buflen);
rp = os::realpath(java_home_var, buf, buflen);
if (rp == nullptr) {
return;
}
assert((int)strlen(buf) < buflen, "Ran out of buffer room");
ss.print("%s/lib", buf);
if (0 == access(buf, F_OK)) {
// Use current module name "libjvm.so"
ss.print("/%s/libjvm%s", Abstract_VM_Version::vm_variant(), JNI_LIB_SUFFIX);
assert(strcmp(buf + strlen(buf) - strlen(JNI_LIB_SUFFIX), JNI_LIB_SUFFIX) == 0,
"buf has been truncated");
} else {
// Go back to path of .so
rp = os::realpath((char *)dlinfo.dli_fname, buf, buflen);
if (rp == nullptr) {
return;
}
}
}
}
strncpy(saved_jvm_path, buf, sizeof(saved_jvm_path));
saved_jvm_path[sizeof(saved_jvm_path) - 1] = '\0';
}
////////////////////////////////////////////////////////////////////////////////
// Virtual Memory

View File

@ -154,7 +154,8 @@ julong os::Bsd::available_memory() {
assert(kerr == KERN_SUCCESS,
"host_statistics64 failed - check mach_host_self() and count");
if (kerr == KERN_SUCCESS) {
available = vmstat.free_count * os::vm_page_size();
// free_count is just a lowerbound, other page categories can be freed too and make memory available
available = (vmstat.free_count + vmstat.inactive_count + vmstat.purgeable_count) * os::vm_page_size();
}
#endif
return available;
@ -1482,83 +1483,6 @@ void os::print_memory_info(outputStream* st) {
st->cr();
}
static char saved_jvm_path[MAXPATHLEN] = {0};
// Find the full path to the current module, libjvm
void os::jvm_path(char *buf, jint buflen) {
// Error checking.
if (buflen < MAXPATHLEN) {
assert(false, "must use a large-enough buffer");
buf[0] = '\0';
return;
}
// Lazy resolve the path to current module.
if (saved_jvm_path[0] != 0) {
strcpy(buf, saved_jvm_path);
return;
}
char dli_fname[MAXPATHLEN];
dli_fname[0] = '\0';
bool ret = dll_address_to_library_name(
CAST_FROM_FN_PTR(address, os::jvm_path),
dli_fname, sizeof(dli_fname), nullptr);
assert(ret, "cannot locate libjvm");
char *rp = nullptr;
if (ret && dli_fname[0] != '\0') {
rp = os::realpath(dli_fname, buf, buflen);
}
if (rp == nullptr) {
return;
}
// If executing unit tests we require JAVA_HOME to point to the real JDK.
if (Arguments::executing_unit_tests()) {
// Look for JAVA_HOME in the environment.
char* java_home_var = ::getenv("JAVA_HOME");
if (java_home_var != nullptr && java_home_var[0] != 0) {
// Check the current module name "libjvm"
const char* p = strrchr(buf, '/');
assert(strstr(p, "/libjvm") == p, "invalid library name");
stringStream ss(buf, buflen);
rp = os::realpath(java_home_var, buf, buflen);
if (rp == nullptr) {
return;
}
assert((int)strlen(buf) < buflen, "Ran out of buffer space");
// Add the appropriate library and JVM variant subdirs
ss.print("%s/lib/%s", buf, Abstract_VM_Version::vm_variant());
if (0 != access(buf, F_OK)) {
ss.reset();
ss.print("%s/lib", buf);
}
// If the path exists within JAVA_HOME, add the JVM library name
// to complete the path to JVM being overridden. Otherwise fallback
// to the path to the current library.
if (0 == access(buf, F_OK)) {
// Use current module name "libjvm"
ss.print("/libjvm%s", JNI_LIB_SUFFIX);
assert(strcmp(buf + strlen(buf) - strlen(JNI_LIB_SUFFIX), JNI_LIB_SUFFIX) == 0,
"buf has been truncated");
} else {
// Fall back to path of current library
rp = os::realpath(dli_fname, buf, buflen);
if (rp == nullptr) {
return;
}
}
}
}
strncpy(saved_jvm_path, buf, MAXPATHLEN);
saved_jvm_path[MAXPATHLEN - 1] = '\0';
}
////////////////////////////////////////////////////////////////////////////////
// Virtual Memory

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -35,9 +35,6 @@
range, \
constraint) \
\
product(bool, UseOprofile, false, \
"(Deprecated) enable support for Oprofile profiler") \
\
product(bool, UseTransparentHugePages, false, \
"Use MADV_HUGEPAGE for large pages") \
\

View File

@ -2746,118 +2746,9 @@ void os::get_summary_cpu_info(char* cpuinfo, size_t length) {
#endif
}
static char saved_jvm_path[MAXPATHLEN] = {0};
// Find the full path to the current module, libjvm.so
void os::jvm_path(char *buf, jint buflen) {
// Error checking.
if (buflen < MAXPATHLEN) {
assert(false, "must use a large-enough buffer");
buf[0] = '\0';
return;
}
// Lazy resolve the path to current module.
if (saved_jvm_path[0] != 0) {
strcpy(buf, saved_jvm_path);
return;
}
char dli_fname[MAXPATHLEN];
dli_fname[0] = '\0';
bool ret = dll_address_to_library_name(
CAST_FROM_FN_PTR(address, os::jvm_path),
dli_fname, sizeof(dli_fname), nullptr);
assert(ret, "cannot locate libjvm");
char *rp = nullptr;
if (ret && dli_fname[0] != '\0') {
rp = os::realpath(dli_fname, buf, buflen);
}
if (rp == nullptr) {
return;
}
// If executing unit tests we require JAVA_HOME to point to the real JDK.
if (Arguments::executing_unit_tests()) {
// Look for JAVA_HOME in the environment.
char* java_home_var = ::getenv("JAVA_HOME");
if (java_home_var != nullptr && java_home_var[0] != 0) {
// Check the current module name "libjvm.so".
const char* p = strrchr(buf, '/');
if (p == nullptr) {
return;
}
assert(strstr(p, "/libjvm") == p, "invalid library name");
stringStream ss(buf, buflen);
rp = os::realpath(java_home_var, buf, buflen);
if (rp == nullptr) {
return;
}
assert((int)strlen(buf) < buflen, "Ran out of buffer room");
ss.print("%s/lib", buf);
if (0 == access(buf, F_OK)) {
// Use current module name "libjvm.so"
ss.print("/%s/libjvm%s", Abstract_VM_Version::vm_variant(), JNI_LIB_SUFFIX);
assert(strcmp(buf + strlen(buf) - strlen(JNI_LIB_SUFFIX), JNI_LIB_SUFFIX) == 0,
"buf has been truncated");
} else {
// Go back to path of .so
rp = os::realpath(dli_fname, buf, buflen);
if (rp == nullptr) {
return;
}
}
}
}
strncpy(saved_jvm_path, buf, MAXPATHLEN);
saved_jvm_path[MAXPATHLEN - 1] = '\0';
}
////////////////////////////////////////////////////////////////////////////////
// Virtual Memory
// Rationale behind this function:
// current (Mon Apr 25 20:12:18 MSD 2005) oprofile drops samples without executable
// mapping for address (see lookup_dcookie() in the kernel module), thus we cannot get
// samples for JITted code. Here we create private executable mapping over the code cache
// and then we can use standard (well, almost, as mapping can change) way to provide
// info for the reporting script by storing timestamp and location of symbol
void linux_wrap_code(char* base, size_t size) {
static volatile jint cnt = 0;
static_assert(sizeof(off_t) == 8, "Expected Large File Support in this file");
if (!UseOprofile) {
return;
}
char buf[PATH_MAX+1];
int num = Atomic::add(&cnt, 1);
snprintf(buf, sizeof(buf), "%s/hs-vm-%d-%d",
os::get_temp_directory(), os::current_process_id(), num);
unlink(buf);
int fd = ::open(buf, O_CREAT | O_RDWR, S_IRWXU);
if (fd != -1) {
off_t rv = ::lseek(fd, size-2, SEEK_SET);
if (rv != (off_t)-1) {
if (::write(fd, "", 1) == 1) {
mmap(base, size,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_PRIVATE|MAP_FIXED|MAP_NORESERVE, fd, 0);
}
}
::close(fd);
unlink(buf);
}
}
static bool recoverable_mmap_error(int err) {
// See if the error is one we can let the caller handle. This
// list of errno values comes from JBS-6843484. I can't find a

View File

@ -59,6 +59,7 @@
#ifdef AIX
#include "loadlib_aix.hpp"
#include "os_aix.hpp"
#include "porting_aix.hpp"
#endif
#ifdef LINUX
#include "os_linux.hpp"
@ -1060,6 +1061,95 @@ bool os::same_files(const char* file1, const char* file2) {
return is_same;
}
static char saved_jvm_path[MAXPATHLEN] = {0};
// Find the full path to the current module, libjvm.so
void os::jvm_path(char *buf, jint buflen) {
// Error checking.
if (buflen < MAXPATHLEN) {
assert(false, "must use a large-enough buffer");
buf[0] = '\0';
return;
}
// Lazy resolve the path to current module.
if (saved_jvm_path[0] != 0) {
strcpy(buf, saved_jvm_path);
return;
}
const char* fname;
#ifdef AIX
Dl_info dlinfo;
int ret = dladdr(CAST_FROM_FN_PTR(void *, os::jvm_path), &dlinfo);
assert(ret != 0, "cannot locate libjvm");
if (ret == 0) {
return;
}
fname = dlinfo.dli_fname;
#else
char dli_fname[MAXPATHLEN];
dli_fname[0] = '\0';
bool ret = dll_address_to_library_name(
CAST_FROM_FN_PTR(address, os::jvm_path),
dli_fname, sizeof(dli_fname), nullptr);
assert(ret, "cannot locate libjvm");
if (!ret) {
return;
}
fname = dli_fname;
#endif // AIX
char* rp = nullptr;
if (fname[0] != '\0') {
rp = os::realpath(fname, buf, buflen);
}
if (rp == nullptr) {
return;
}
// If executing unit tests we require JAVA_HOME to point to the real JDK.
if (Arguments::executing_unit_tests()) {
// Look for JAVA_HOME in the environment.
char* java_home_var = ::getenv("JAVA_HOME");
if (java_home_var != nullptr && java_home_var[0] != 0) {
// Check the current module name "libjvm.so".
const char* p = strrchr(buf, '/');
if (p == nullptr) {
return;
}
assert(strstr(p, "/libjvm") == p, "invalid library name");
stringStream ss(buf, buflen);
rp = os::realpath(java_home_var, buf, buflen);
if (rp == nullptr) {
return;
}
assert((int)strlen(buf) < buflen, "Ran out of buffer room");
ss.print("%s/lib", buf);
// If the path exists within JAVA_HOME, add the VM variant directory and JVM
// library name to complete the path to JVM being overridden. Otherwise fallback
// to the path to the current library.
if (0 == access(buf, F_OK)) {
// Use current module name "libjvm.so"
ss.print("/%s/libjvm%s", Abstract_VM_Version::vm_variant(), JNI_LIB_SUFFIX);
assert(strcmp(buf + strlen(buf) - strlen(JNI_LIB_SUFFIX), JNI_LIB_SUFFIX) == 0,
"buf has been truncated");
} else {
// Go back to path of .so
rp = os::realpath(fname, buf, buflen);
if (rp == nullptr) {
return;
}
}
}
}
strncpy(saved_jvm_path, buf, MAXPATHLEN);
saved_jvm_path[MAXPATHLEN - 1] = '\0';
}
// Called when creating the thread. The minimum stack sizes have already been calculated
size_t os::Posix::get_initial_stack_size(ThreadType thr_type, size_t req_stack_size) {
size_t stack_size;

View File

@ -481,7 +481,3 @@ int get_legal_text(FileBuff &fbuf, char **legal_text)
*legal_text = legal_start;
return (int) (legal_end - legal_start);
}
void *operator new( size_t size, int, const char *, int ) throw() {
return ::operator new( size );
}

View File

@ -1626,6 +1626,8 @@ void ArchDesc::declareClasses(FILE *fp) {
while (attr != nullptr) {
if (strcmp (attr->_ident, "ins_is_TrapBasedCheckNode") == 0) {
fprintf(fp, " virtual bool is_TrapBasedCheckNode() const { return %s; }\n", attr->_val);
} else if (strcmp (attr->_ident, "ins_is_late_expanded_null_check_candidate") == 0) {
fprintf(fp, " virtual bool is_late_expanded_null_check_candidate() const { return %s; }\n", attr->_val);
} else if (strcmp (attr->_ident, "ins_cost") != 0 &&
strncmp(attr->_ident, "ins_field_", 10) != 0 &&
// Must match function in node.hpp: return type bool, no prefix "ins_".

View File

@ -110,12 +110,24 @@ const char* CDSConfig::default_archive_path() {
// before CDSConfig::ergo_initialize() is called.
assert(_cds_ergo_initialize_started, "sanity");
if (_default_archive_path == nullptr) {
char jvm_path[JVM_MAXPATHLEN];
os::jvm_path(jvm_path, sizeof(jvm_path));
char *end = strrchr(jvm_path, *os::file_separator());
if (end != nullptr) *end = '\0';
stringStream tmp;
tmp.print("%s%sclasses", jvm_path, os::file_separator());
if (is_vm_statically_linked()) {
// It's easier to form the path using JAVA_HOME as os::jvm_path
// gives the path to the launcher executable on static JDK.
const char* subdir = WINDOWS_ONLY("bin") NOT_WINDOWS("lib");
tmp.print("%s%s%s%s%s%sclasses",
Arguments::get_java_home(), os::file_separator(),
subdir, os::file_separator(),
Abstract_VM_Version::vm_variant(), os::file_separator());
} else {
// Assume .jsa is in the same directory where libjvm resides on
// non-static JDK.
char jvm_path[JVM_MAXPATHLEN];
os::jvm_path(jvm_path, sizeof(jvm_path));
char *end = strrchr(jvm_path, *os::file_separator());
if (end != nullptr) *end = '\0';
tmp.print("%s%sclasses", jvm_path, os::file_separator());
}
#ifdef _LP64
if (!UseCompressedOops) {
tmp.print_raw("_nocoops");

View File

@ -3740,6 +3740,7 @@ void ClassFileParser::apply_parsed_class_metadata(
_cp->set_pool_holder(this_klass);
this_klass->set_constants(_cp);
this_klass->set_fieldinfo_stream(_fieldinfo_stream);
this_klass->set_fieldinfo_search_table(_fieldinfo_search_table);
this_klass->set_fields_status(_fields_status);
this_klass->set_methods(_methods);
this_klass->set_inner_classes(_inner_classes);
@ -3749,6 +3750,8 @@ void ClassFileParser::apply_parsed_class_metadata(
this_klass->set_permitted_subclasses(_permitted_subclasses);
this_klass->set_record_components(_record_components);
DEBUG_ONLY(FieldInfoStream::validate_search_table(_cp, _fieldinfo_stream, _fieldinfo_search_table));
// Delay the setting of _local_interfaces and _transitive_interfaces until after
// initialize_supers() in fill_instance_klass(). It is because the _local_interfaces could
// be shared with _transitive_interfaces and _transitive_interfaces may be shared with
@ -5056,6 +5059,7 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik,
// note that is not safe to use the fields in the parser from this point on
assert(nullptr == _cp, "invariant");
assert(nullptr == _fieldinfo_stream, "invariant");
assert(nullptr == _fieldinfo_search_table, "invariant");
assert(nullptr == _fields_status, "invariant");
assert(nullptr == _methods, "invariant");
assert(nullptr == _inner_classes, "invariant");
@ -5276,6 +5280,7 @@ ClassFileParser::ClassFileParser(ClassFileStream* stream,
_super_klass(),
_cp(nullptr),
_fieldinfo_stream(nullptr),
_fieldinfo_search_table(nullptr),
_fields_status(nullptr),
_methods(nullptr),
_inner_classes(nullptr),
@ -5352,6 +5357,7 @@ void ClassFileParser::clear_class_metadata() {
// deallocated if classfile parsing returns an error.
_cp = nullptr;
_fieldinfo_stream = nullptr;
_fieldinfo_search_table = nullptr;
_fields_status = nullptr;
_methods = nullptr;
_inner_classes = nullptr;
@ -5374,6 +5380,7 @@ ClassFileParser::~ClassFileParser() {
if (_fieldinfo_stream != nullptr) {
MetadataFactory::free_array<u1>(_loader_data, _fieldinfo_stream);
}
MetadataFactory::free_array<u1>(_loader_data, _fieldinfo_search_table);
if (_fields_status != nullptr) {
MetadataFactory::free_array<FieldStatus>(_loader_data, _fields_status);
@ -5774,6 +5781,7 @@ void ClassFileParser::post_process_parsed_stream(const ClassFileStream* const st
_fieldinfo_stream =
FieldInfoStream::create_FieldInfoStream(_temp_field_info, _java_fields_count,
injected_fields_count, loader_data(), CHECK);
_fieldinfo_search_table = FieldInfoStream::create_search_table(_cp, _fieldinfo_stream, _loader_data, CHECK);
_fields_status =
MetadataFactory::new_array<FieldStatus>(_loader_data, _temp_field_info->length(),
FieldStatus(0), CHECK);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -123,6 +123,7 @@ class ClassFileParser {
const InstanceKlass* _super_klass;
ConstantPool* _cp;
Array<u1>* _fieldinfo_stream;
Array<u1>* _fieldinfo_search_table;
Array<FieldStatus>* _fields_status;
Array<Method*>* _methods;
Array<u2>* _inner_classes;

View File

@ -301,7 +301,7 @@ void FieldLayout::reconstruct_layout(const InstanceKlass* ik, bool& has_instance
BasicType last_type;
int last_offset = -1;
while (ik != nullptr) {
for (AllFieldStream fs(ik->fieldinfo_stream(), ik->constants()); !fs.done(); fs.next()) {
for (AllFieldStream fs(ik); !fs.done(); fs.next()) {
BasicType type = Signature::basic_type(fs.signature());
// distinction between static and non-static fields is missing
if (fs.access_flags().is_static()) continue;
@ -461,7 +461,7 @@ void FieldLayout::print(outputStream* output, bool is_static, const InstanceKlas
bool found = false;
const InstanceKlass* ik = super;
while (!found && ik != nullptr) {
for (AllFieldStream fs(ik->fieldinfo_stream(), ik->constants()); !fs.done(); fs.next()) {
for (AllFieldStream fs(ik); !fs.done(); fs.next()) {
if (fs.offset() == b->offset()) {
output->print_cr(" @%d \"%s\" %s %d/%d %s",
b->offset(),

View File

@ -967,6 +967,13 @@ void java_lang_Class::fixup_mirror(Klass* k, TRAPS) {
Array<u1>* new_fis = FieldInfoStream::create_FieldInfoStream(fields, java_fields, injected_fields, k->class_loader_data(), CHECK);
ik->set_fieldinfo_stream(new_fis);
MetadataFactory::free_array<u1>(k->class_loader_data(), old_stream);
Array<u1>* old_table = ik->fieldinfo_search_table();
Array<u1>* search_table = FieldInfoStream::create_search_table(ik->constants(), new_fis, k->class_loader_data(), CHECK);
ik->set_fieldinfo_search_table(search_table);
MetadataFactory::free_array<u1>(k->class_loader_data(), old_table);
DEBUG_ONLY(FieldInfoStream::validate_search_table(ik->constants(), new_fis, search_table));
}
}

View File

@ -289,8 +289,6 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) {
case vmIntrinsics::_dsin:
case vmIntrinsics::_dcos:
case vmIntrinsics::_dtan:
case vmIntrinsics::_dtanh:
case vmIntrinsics::_dcbrt:
case vmIntrinsics::_dlog:
case vmIntrinsics::_dexp:
case vmIntrinsics::_dpow:
@ -316,6 +314,13 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) {
case vmIntrinsics::_fmaF:
if (!InlineMathNatives || !UseFMA) return true;
break;
case vmIntrinsics::_dtanh:
case vmIntrinsics::_dcbrt:
if (!InlineMathNatives || !InlineIntrinsics) return true;
#if defined(AMD64) && (defined(COMPILER1) || defined(COMPILER2))
if (!UseLibmIntrinsic) return true;
#endif
break;
case vmIntrinsics::_floatToFloat16:
case vmIntrinsics::_float16ToFloat:
if (!InlineIntrinsics) return true;

View File

@ -98,15 +98,15 @@ void ParallelArguments::initialize() {
FullGCForwarding::initialize_flags(heap_reserved_size_bytes());
}
// The alignment used for boundary between young gen and old gen
static size_t default_gen_alignment() {
// The alignment used for spaces in young gen and old gen
static size_t default_space_alignment() {
return 64 * K * HeapWordSize;
}
void ParallelArguments::initialize_alignments() {
// Initialize card size before initializing alignments
CardTable::initialize_card_size();
SpaceAlignment = GenAlignment = default_gen_alignment();
SpaceAlignment = default_space_alignment();
HeapAlignment = compute_heap_alignment();
}
@ -123,9 +123,8 @@ void ParallelArguments::initialize_heap_flags_and_sizes() {
// Can a page size be something else than a power of two?
assert(is_power_of_2((intptr_t)page_sz), "must be a power of 2");
size_t new_alignment = align_up(page_sz, GenAlignment);
if (new_alignment != GenAlignment) {
GenAlignment = new_alignment;
size_t new_alignment = align_up(page_sz, SpaceAlignment);
if (new_alignment != SpaceAlignment) {
SpaceAlignment = new_alignment;
// Redo everything from the start
initialize_heap_flags_and_sizes_one_pass();

View File

@ -29,10 +29,8 @@
void ParallelInitLogger::print_heap() {
log_info_p(gc, init)("Alignments:"
" Space " EXACTFMT ","
" Generation " EXACTFMT ","
" Heap " EXACTFMT,
EXACTFMTARGS(SpaceAlignment),
EXACTFMTARGS(GenAlignment),
EXACTFMTARGS(HeapAlignment));
GCInitLogger::print_heap();
}

View File

@ -69,8 +69,8 @@ jint ParallelScavengeHeap::initialize() {
initialize_reserved_region(heap_rs);
// Layout the reserved space for the generations.
ReservedSpace old_rs = heap_rs.first_part(MaxOldSize, GenAlignment);
ReservedSpace young_rs = heap_rs.last_part(MaxOldSize, GenAlignment);
ReservedSpace old_rs = heap_rs.first_part(MaxOldSize, SpaceAlignment);
ReservedSpace young_rs = heap_rs.last_part(MaxOldSize, SpaceAlignment);
assert(young_rs.size() == MaxNewSize, "Didn't reserve all of the heap");
PSCardTable* card_table = new PSCardTable(_reserved);
@ -107,7 +107,7 @@ jint ParallelScavengeHeap::initialize() {
new PSAdaptiveSizePolicy(eden_capacity,
initial_promo_size,
young_gen()->to_space()->capacity_in_bytes(),
GenAlignment,
SpaceAlignment,
max_gc_pause_sec,
GCTimeRatio
);

View File

@ -41,7 +41,7 @@ PSOldGen::PSOldGen(ReservedSpace rs, size_t initial_size, size_t min_size,
_min_gen_size(min_size),
_max_gen_size(max_size)
{
initialize(rs, initial_size, GenAlignment);
initialize(rs, initial_size, SpaceAlignment);
}
void PSOldGen::initialize(ReservedSpace rs, size_t initial_size, size_t alignment) {

View File

@ -47,7 +47,7 @@ PSYoungGen::PSYoungGen(ReservedSpace rs, size_t initial_size, size_t min_size, s
_from_counters(nullptr),
_to_counters(nullptr)
{
initialize(rs, initial_size, GenAlignment);
initialize(rs, initial_size, SpaceAlignment);
}
void PSYoungGen::initialize_virtual_space(ReservedSpace rs,
@ -746,7 +746,7 @@ size_t PSYoungGen::available_to_live() {
}
size_t delta_in_bytes = unused_committed + delta_in_survivor;
delta_in_bytes = align_down(delta_in_bytes, GenAlignment);
delta_in_bytes = align_down(delta_in_bytes, SpaceAlignment);
return delta_in_bytes;
}

View File

@ -188,8 +188,8 @@ jint SerialHeap::initialize() {
initialize_reserved_region(heap_rs);
ReservedSpace young_rs = heap_rs.first_part(MaxNewSize, GenAlignment);
ReservedSpace old_rs = heap_rs.last_part(MaxNewSize, GenAlignment);
ReservedSpace young_rs = heap_rs.first_part(MaxNewSize, SpaceAlignment);
ReservedSpace old_rs = heap_rs.last_part(MaxNewSize, SpaceAlignment);
_rem_set = new CardTableRS(_reserved);
_rem_set->initialize(young_rs.base(), old_rs.base());

View File

@ -35,7 +35,7 @@ extern size_t SpaceAlignment;
class GCArguments {
protected:
// Initialize HeapAlignment, SpaceAlignment, and extra alignments (E.g. GenAlignment)
// Initialize HeapAlignment, SpaceAlignment
virtual void initialize_alignments() = 0;
virtual void initialize_heap_flags_and_sizes();
virtual void initialize_size_info();

View File

@ -42,17 +42,15 @@ size_t MaxOldSize = 0;
// See more in JDK-8346005
size_t OldSize = ScaleForWordSize(4*M);
size_t GenAlignment = 0;
size_t GenArguments::conservative_max_heap_alignment() { return (size_t)Generation::GenGrain; }
static size_t young_gen_size_lower_bound() {
// The young generation must be aligned and have room for eden + two survivors
return align_up(3 * SpaceAlignment, GenAlignment);
return 3 * SpaceAlignment;
}
static size_t old_gen_size_lower_bound() {
return align_up(SpaceAlignment, GenAlignment);
return SpaceAlignment;
}
size_t GenArguments::scale_by_NewRatio_aligned(size_t base_size, size_t alignment) {
@ -69,23 +67,20 @@ static size_t bound_minus_alignment(size_t desired_size,
void GenArguments::initialize_alignments() {
// Initialize card size before initializing alignments
CardTable::initialize_card_size();
SpaceAlignment = GenAlignment = (size_t)Generation::GenGrain;
SpaceAlignment = (size_t)Generation::GenGrain;
HeapAlignment = compute_heap_alignment();
}
void GenArguments::initialize_heap_flags_and_sizes() {
GCArguments::initialize_heap_flags_and_sizes();
assert(GenAlignment != 0, "Generation alignment not set up properly");
assert(HeapAlignment >= GenAlignment,
"HeapAlignment: %zu less than GenAlignment: %zu",
HeapAlignment, GenAlignment);
assert(GenAlignment % SpaceAlignment == 0,
"GenAlignment: %zu not aligned by SpaceAlignment: %zu",
GenAlignment, SpaceAlignment);
assert(HeapAlignment % GenAlignment == 0,
"HeapAlignment: %zu not aligned by GenAlignment: %zu",
HeapAlignment, GenAlignment);
assert(SpaceAlignment != 0, "Generation alignment not set up properly");
assert(HeapAlignment >= SpaceAlignment,
"HeapAlignment: %zu less than SpaceAlignment: %zu",
HeapAlignment, SpaceAlignment);
assert(HeapAlignment % SpaceAlignment == 0,
"HeapAlignment: %zu not aligned by SpaceAlignment: %zu",
HeapAlignment, SpaceAlignment);
// All generational heaps have a young gen; handle those flags here
@ -106,7 +101,7 @@ void GenArguments::initialize_heap_flags_and_sizes() {
// Make sure NewSize allows an old generation to fit even if set on the command line
if (FLAG_IS_CMDLINE(NewSize) && NewSize >= InitialHeapSize) {
size_t revised_new_size = bound_minus_alignment(NewSize, InitialHeapSize, GenAlignment);
size_t revised_new_size = bound_minus_alignment(NewSize, InitialHeapSize, SpaceAlignment);
log_warning(gc, ergo)("NewSize (%zuk) is equal to or greater than initial heap size (%zuk). A new "
"NewSize of %zuk will be used to accomodate an old generation.",
NewSize/K, InitialHeapSize/K, revised_new_size/K);
@ -115,8 +110,8 @@ void GenArguments::initialize_heap_flags_and_sizes() {
// Now take the actual NewSize into account. We will silently increase NewSize
// if the user specified a smaller or unaligned value.
size_t bounded_new_size = bound_minus_alignment(NewSize, MaxHeapSize, GenAlignment);
bounded_new_size = MAX2(smallest_new_size, align_down(bounded_new_size, GenAlignment));
size_t bounded_new_size = bound_minus_alignment(NewSize, MaxHeapSize, SpaceAlignment);
bounded_new_size = MAX2(smallest_new_size, align_down(bounded_new_size, SpaceAlignment));
if (bounded_new_size != NewSize) {
FLAG_SET_ERGO(NewSize, bounded_new_size);
}
@ -125,7 +120,7 @@ void GenArguments::initialize_heap_flags_and_sizes() {
if (!FLAG_IS_DEFAULT(MaxNewSize)) {
if (MaxNewSize >= MaxHeapSize) {
// Make sure there is room for an old generation
size_t smaller_max_new_size = MaxHeapSize - GenAlignment;
size_t smaller_max_new_size = MaxHeapSize - SpaceAlignment;
if (FLAG_IS_CMDLINE(MaxNewSize)) {
log_warning(gc, ergo)("MaxNewSize (%zuk) is equal to or greater than the entire "
"heap (%zuk). A new max generation size of %zuk will be used.",
@ -137,8 +132,8 @@ void GenArguments::initialize_heap_flags_and_sizes() {
}
} else if (MaxNewSize < NewSize) {
FLAG_SET_ERGO(MaxNewSize, NewSize);
} else if (!is_aligned(MaxNewSize, GenAlignment)) {
FLAG_SET_ERGO(MaxNewSize, align_down(MaxNewSize, GenAlignment));
} else if (!is_aligned(MaxNewSize, SpaceAlignment)) {
FLAG_SET_ERGO(MaxNewSize, align_down(MaxNewSize, SpaceAlignment));
}
}
@ -166,13 +161,13 @@ void GenArguments::initialize_heap_flags_and_sizes() {
// exceed it. Adjust New/OldSize as necessary.
size_t calculated_size = NewSize + OldSize;
double shrink_factor = (double) MaxHeapSize / calculated_size;
size_t smaller_new_size = align_down((size_t)(NewSize * shrink_factor), GenAlignment);
size_t smaller_new_size = align_down((size_t)(NewSize * shrink_factor), SpaceAlignment);
FLAG_SET_ERGO(NewSize, MAX2(young_gen_size_lower_bound(), smaller_new_size));
// OldSize is already aligned because above we aligned MaxHeapSize to
// HeapAlignment, and we just made sure that NewSize is aligned to
// GenAlignment. In initialize_flags() we verified that HeapAlignment
// is a multiple of GenAlignment.
// SpaceAlignment. In initialize_flags() we verified that HeapAlignment
// is a multiple of SpaceAlignment.
OldSize = MaxHeapSize - NewSize;
} else {
FLAG_SET_ERGO(MaxHeapSize, align_up(NewSize + OldSize, HeapAlignment));
@ -200,7 +195,7 @@ void GenArguments::initialize_size_info() {
// Determine maximum size of the young generation.
if (FLAG_IS_DEFAULT(MaxNewSize)) {
max_young_size = scale_by_NewRatio_aligned(MaxHeapSize, GenAlignment);
max_young_size = scale_by_NewRatio_aligned(MaxHeapSize, SpaceAlignment);
// Bound the maximum size by NewSize below (since it historically
// would have been NewSize and because the NewRatio calculation could
// yield a size that is too small) and bound it by MaxNewSize above.
@ -229,18 +224,18 @@ void GenArguments::initialize_size_info() {
// If NewSize is set on the command line, we should use it as
// the initial size, but make sure it is within the heap bounds.
initial_young_size =
MIN2(max_young_size, bound_minus_alignment(NewSize, InitialHeapSize, GenAlignment));
MinNewSize = bound_minus_alignment(initial_young_size, MinHeapSize, GenAlignment);
MIN2(max_young_size, bound_minus_alignment(NewSize, InitialHeapSize, SpaceAlignment));
MinNewSize = bound_minus_alignment(initial_young_size, MinHeapSize, SpaceAlignment);
} else {
// For the case where NewSize is not set on the command line, use
// NewRatio to size the initial generation size. Use the current
// NewSize as the floor, because if NewRatio is overly large, the resulting
// size can be too small.
initial_young_size =
clamp(scale_by_NewRatio_aligned(InitialHeapSize, GenAlignment), NewSize, max_young_size);
clamp(scale_by_NewRatio_aligned(InitialHeapSize, SpaceAlignment), NewSize, max_young_size);
// Derive MinNewSize from MinHeapSize
MinNewSize = MIN2(scale_by_NewRatio_aligned(MinHeapSize, GenAlignment), initial_young_size);
MinNewSize = MIN2(scale_by_NewRatio_aligned(MinHeapSize, SpaceAlignment), initial_young_size);
}
}
@ -252,7 +247,7 @@ void GenArguments::initialize_size_info() {
// The maximum old size can be determined from the maximum young
// and maximum heap size since no explicit flags exist
// for setting the old generation maximum.
MaxOldSize = MAX2(MaxHeapSize - max_young_size, GenAlignment);
MaxOldSize = MAX2(MaxHeapSize - max_young_size, SpaceAlignment);
MinOldSize = MIN3(MaxOldSize,
InitialHeapSize - initial_young_size,
MinHeapSize - MinNewSize);
@ -315,10 +310,10 @@ void GenArguments::assert_flags() {
assert(NewSize >= MinNewSize, "Ergonomics decided on a too small young gen size");
assert(NewSize <= MaxNewSize, "Ergonomics decided on incompatible initial and maximum young gen sizes");
assert(FLAG_IS_DEFAULT(MaxNewSize) || MaxNewSize < MaxHeapSize, "Ergonomics decided on incompatible maximum young gen and heap sizes");
assert(NewSize % GenAlignment == 0, "NewSize alignment");
assert(FLAG_IS_DEFAULT(MaxNewSize) || MaxNewSize % GenAlignment == 0, "MaxNewSize alignment");
assert(NewSize % SpaceAlignment == 0, "NewSize alignment");
assert(FLAG_IS_DEFAULT(MaxNewSize) || MaxNewSize % SpaceAlignment == 0, "MaxNewSize alignment");
assert(OldSize + NewSize <= MaxHeapSize, "Ergonomics decided on incompatible generation and heap sizes");
assert(OldSize % GenAlignment == 0, "OldSize alignment");
assert(OldSize % SpaceAlignment == 0, "OldSize alignment");
}
void GenArguments::assert_size_info() {
@ -327,19 +322,19 @@ void GenArguments::assert_size_info() {
assert(MaxNewSize < MaxHeapSize, "Ergonomics decided on incompatible maximum young and heap sizes");
assert(MinNewSize <= NewSize, "Ergonomics decided on incompatible minimum and initial young gen sizes");
assert(NewSize <= MaxNewSize, "Ergonomics decided on incompatible initial and maximum young gen sizes");
assert(MinNewSize % GenAlignment == 0, "_min_young_size alignment");
assert(NewSize % GenAlignment == 0, "_initial_young_size alignment");
assert(MaxNewSize % GenAlignment == 0, "MaxNewSize alignment");
assert(MinNewSize <= bound_minus_alignment(MinNewSize, MinHeapSize, GenAlignment),
assert(MinNewSize % SpaceAlignment == 0, "_min_young_size alignment");
assert(NewSize % SpaceAlignment == 0, "_initial_young_size alignment");
assert(MaxNewSize % SpaceAlignment == 0, "MaxNewSize alignment");
assert(MinNewSize <= bound_minus_alignment(MinNewSize, MinHeapSize, SpaceAlignment),
"Ergonomics made minimum young generation larger than minimum heap");
assert(NewSize <= bound_minus_alignment(NewSize, InitialHeapSize, GenAlignment),
assert(NewSize <= bound_minus_alignment(NewSize, InitialHeapSize, SpaceAlignment),
"Ergonomics made initial young generation larger than initial heap");
assert(MaxNewSize <= bound_minus_alignment(MaxNewSize, MaxHeapSize, GenAlignment),
assert(MaxNewSize <= bound_minus_alignment(MaxNewSize, MaxHeapSize, SpaceAlignment),
"Ergonomics made maximum young generation lager than maximum heap");
assert(MinOldSize <= OldSize, "Ergonomics decided on incompatible minimum and initial old gen sizes");
assert(OldSize <= MaxOldSize, "Ergonomics decided on incompatible initial and maximum old gen sizes");
assert(MaxOldSize % GenAlignment == 0, "MaxOldSize alignment");
assert(OldSize % GenAlignment == 0, "OldSize alignment");
assert(MaxOldSize % SpaceAlignment == 0, "MaxOldSize alignment");
assert(OldSize % SpaceAlignment == 0, "OldSize alignment");
assert(MaxHeapSize <= (MaxNewSize + MaxOldSize), "Total maximum heap sizes must be sum of generation maximum sizes");
assert(MinNewSize + MinOldSize <= MinHeapSize, "Minimum generation sizes exceed minimum heap size");
assert(NewSize + OldSize == InitialHeapSize, "Initial generation sizes should match initial heap size");

View File

@ -35,8 +35,6 @@ extern size_t MaxOldSize;
extern size_t OldSize;
extern size_t GenAlignment;
class GenArguments : public GCArguments {
friend class TestGenCollectorPolicy; // Testing
private:

View File

@ -625,6 +625,34 @@ void ShenandoahBarrierC2Support::verify(RootNode* root) {
}
#endif
bool ShenandoahBarrierC2Support::is_anti_dependent_load_at_control(PhaseIdealLoop* phase, Node* maybe_load, Node* store,
Node* control) {
return maybe_load->is_Load() && phase->C->can_alias(store->adr_type(), phase->C->get_alias_index(maybe_load->adr_type())) &&
phase->ctrl_or_self(maybe_load) == control;
}
void ShenandoahBarrierC2Support::maybe_push_anti_dependent_loads(PhaseIdealLoop* phase, Node* maybe_store, Node* control, Unique_Node_List &wq) {
if (!maybe_store->is_Store() && !maybe_store->is_LoadStore()) {
return;
}
Node* mem = maybe_store->in(MemNode::Memory);
for (DUIterator_Fast imax, i = mem->fast_outs(imax); i < imax; i++) {
Node* u = mem->fast_out(i);
if (is_anti_dependent_load_at_control(phase, u, maybe_store, control)) {
wq.push(u);
}
}
}
void ShenandoahBarrierC2Support::push_data_inputs_at_control(PhaseIdealLoop* phase, Node* n, Node* ctrl, Unique_Node_List &wq) {
for (uint i = 0; i < n->req(); i++) {
Node* in = n->in(i);
if (in != nullptr && phase->has_ctrl(in) && phase->get_ctrl(in) == ctrl) {
wq.push(in);
}
}
}
bool ShenandoahBarrierC2Support::is_dominator_same_ctrl(Node* c, Node* d, Node* n, PhaseIdealLoop* phase) {
// That both nodes have the same control is not sufficient to prove
// domination, verify that there's no path from d to n
@ -639,22 +667,9 @@ bool ShenandoahBarrierC2Support::is_dominator_same_ctrl(Node* c, Node* d, Node*
if (m->is_Phi() && m->in(0)->is_Loop()) {
assert(phase->ctrl_or_self(m->in(LoopNode::EntryControl)) != c, "following loop entry should lead to new control");
} else {
if (m->is_Store() || m->is_LoadStore()) {
// Take anti-dependencies into account
Node* mem = m->in(MemNode::Memory);
for (DUIterator_Fast imax, i = mem->fast_outs(imax); i < imax; i++) {
Node* u = mem->fast_out(i);
if (u->is_Load() && phase->C->can_alias(m->adr_type(), phase->C->get_alias_index(u->adr_type())) &&
phase->ctrl_or_self(u) == c) {
wq.push(u);
}
}
}
for (uint i = 0; i < m->req(); i++) {
if (m->in(i) != nullptr && phase->ctrl_or_self(m->in(i)) == c) {
wq.push(m->in(i));
}
}
// Take anti-dependencies into account
maybe_push_anti_dependent_loads(phase, m, c, wq);
push_data_inputs_at_control(phase, m, c, wq);
}
}
return true;
@ -1006,7 +1021,20 @@ void ShenandoahBarrierC2Support::call_lrb_stub(Node*& ctrl, Node*& val, Node* lo
phase->register_new_node(val, ctrl);
}
void ShenandoahBarrierC2Support::fix_ctrl(Node* barrier, Node* region, const MemoryGraphFixer& fixer, Unique_Node_List& uses, Unique_Node_List& uses_to_ignore, uint last, PhaseIdealLoop* phase) {
void ShenandoahBarrierC2Support::collect_nodes_above_barrier(Unique_Node_List &nodes_above_barrier, PhaseIdealLoop* phase, Node* ctrl, Node* init_raw_mem) {
nodes_above_barrier.clear();
if (phase->has_ctrl(init_raw_mem) && phase->get_ctrl(init_raw_mem) == ctrl && !init_raw_mem->is_Phi()) {
nodes_above_barrier.push(init_raw_mem);
}
for (uint next = 0; next < nodes_above_barrier.size(); next++) {
Node* n = nodes_above_barrier.at(next);
// Take anti-dependencies into account
maybe_push_anti_dependent_loads(phase, n, ctrl, nodes_above_barrier);
push_data_inputs_at_control(phase, n, ctrl, nodes_above_barrier);
}
}
void ShenandoahBarrierC2Support::fix_ctrl(Node* barrier, Node* region, const MemoryGraphFixer& fixer, Unique_Node_List& uses, Unique_Node_List& nodes_above_barrier, uint last, PhaseIdealLoop* phase) {
Node* ctrl = phase->get_ctrl(barrier);
Node* init_raw_mem = fixer.find_mem(ctrl, barrier);
@ -1017,30 +1045,17 @@ void ShenandoahBarrierC2Support::fix_ctrl(Node* barrier, Node* region, const Mem
// control will be after the expanded barrier. The raw memory (if
// its memory is control dependent on the barrier's input control)
// must stay above the barrier.
uses_to_ignore.clear();
if (phase->has_ctrl(init_raw_mem) && phase->get_ctrl(init_raw_mem) == ctrl && !init_raw_mem->is_Phi()) {
uses_to_ignore.push(init_raw_mem);
}
for (uint next = 0; next < uses_to_ignore.size(); next++) {
Node *n = uses_to_ignore.at(next);
for (uint i = 0; i < n->req(); i++) {
Node* in = n->in(i);
if (in != nullptr && phase->has_ctrl(in) && phase->get_ctrl(in) == ctrl) {
uses_to_ignore.push(in);
}
}
}
collect_nodes_above_barrier(nodes_above_barrier, phase, ctrl, init_raw_mem);
for (DUIterator_Fast imax, i = ctrl->fast_outs(imax); i < imax; i++) {
Node* u = ctrl->fast_out(i);
if (u->_idx < last &&
u != barrier &&
!u->depends_only_on_test() && // preserve dependency on test
!uses_to_ignore.member(u) &&
!nodes_above_barrier.member(u) &&
(u->in(0) != ctrl || (!u->is_Region() && !u->is_Phi())) &&
(ctrl->Opcode() != Op_CatchProj || u->Opcode() != Op_CreateEx)) {
Node* old_c = phase->ctrl_or_self(u);
Node* c = old_c;
if (c != ctrl ||
if (old_c != ctrl ||
is_dominator_same_ctrl(old_c, barrier, u, phase) ||
ShenandoahBarrierSetC2::is_shenandoah_state_load(u)) {
phase->igvn().rehash_node_delayed(u);
@ -1315,7 +1330,7 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) {
// Expand load-reference-barriers
MemoryGraphFixer fixer(Compile::AliasIdxRaw, true, phase);
Unique_Node_List uses_to_ignore;
Unique_Node_List nodes_above_barriers;
for (int i = state->load_reference_barriers_count() - 1; i >= 0; i--) {
ShenandoahLoadReferenceBarrierNode* lrb = state->load_reference_barrier(i);
uint last = phase->C->unique();
@ -1410,7 +1425,7 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) {
Node* out_val = val_phi;
phase->register_new_node(val_phi, region);
fix_ctrl(lrb, region, fixer, uses, uses_to_ignore, last, phase);
fix_ctrl(lrb, region, fixer, uses, nodes_above_barriers, last, phase);
ctrl = orig_ctrl;

View File

@ -62,8 +62,12 @@ private:
PhaseIdealLoop* phase, int flags);
static void call_lrb_stub(Node*& ctrl, Node*& val, Node* load_addr,
DecoratorSet decorators, PhaseIdealLoop* phase);
static void collect_nodes_above_barrier(Unique_Node_List &nodes_above_barrier, PhaseIdealLoop* phase, Node* ctrl,
Node* init_raw_mem);
static void test_in_cset(Node*& ctrl, Node*& not_cset_ctrl, Node* val, Node* raw_mem, PhaseIdealLoop* phase);
static void fix_ctrl(Node* barrier, Node* region, const MemoryGraphFixer& fixer, Unique_Node_List& uses, Unique_Node_List& uses_to_ignore, uint last, PhaseIdealLoop* phase);
static void fix_ctrl(Node* barrier, Node* region, const MemoryGraphFixer& fixer, Unique_Node_List& uses, Unique_Node_List& nodes_above_barrier, uint last, PhaseIdealLoop* phase);
static Node* get_load_addr(PhaseIdealLoop* phase, VectorSet& visited, Node* lrb);
public:
@ -76,6 +80,11 @@ public:
static bool expand(Compile* C, PhaseIterGVN& igvn);
static void pin_and_expand(PhaseIdealLoop* phase);
static void push_data_inputs_at_control(PhaseIdealLoop* phase, Node* n, Node* ctrl,
Unique_Node_List &wq);
static bool is_anti_dependent_load_at_control(PhaseIdealLoop* phase, Node* maybe_load, Node* store, Node* control);
static void maybe_push_anti_dependent_loads(PhaseIdealLoop* phase, Node* maybe_store, Node* control, Unique_Node_List &wq);
#ifdef ASSERT
static void verify(RootNode* root);
#endif

View File

@ -23,6 +23,7 @@
#include "gc/z/zAllocator.hpp"
#include "gc/z/zObjectAllocator.hpp"
#include "gc/z/zPageAge.inline.hpp"
ZAllocatorEden* ZAllocator::_eden;
ZAllocatorForRelocation* ZAllocator::_relocation[ZAllocator::_relocation_allocators];
@ -47,7 +48,7 @@ ZPageAge ZAllocatorForRelocation::install() {
for (uint i = 0; i < ZAllocator::_relocation_allocators; ++i) {
if (_relocation[i] == nullptr) {
_relocation[i] = this;
return static_cast<ZPageAge>(i + 1);
return to_zpageage(i + 1);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -35,7 +35,7 @@ class ZPage;
class ZAllocator {
public:
static constexpr uint _relocation_allocators = static_cast<uint>(ZPageAge::old);
static constexpr uint _relocation_allocators = ZPageAgeCount - 1;
protected:
ZObjectAllocator _object_allocator;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -28,13 +28,14 @@
#include "gc/z/zAddress.inline.hpp"
#include "gc/z/zHeap.hpp"
#include "gc/z/zPageAge.inline.hpp"
inline ZAllocatorEden* ZAllocator::eden() {
return _eden;
}
inline ZAllocatorForRelocation* ZAllocator::relocation(ZPageAge page_age) {
return _relocation[static_cast<uint>(page_age) - 1];
return _relocation[untype(page_age - 1)];
}
inline ZAllocatorForRelocation* ZAllocator::old() {

View File

@ -41,6 +41,7 @@
#include "gc/z/zHeap.inline.hpp"
#include "gc/z/zJNICritical.hpp"
#include "gc/z/zMark.inline.hpp"
#include "gc/z/zPageAge.inline.hpp"
#include "gc/z/zPageAllocator.hpp"
#include "gc/z/zRelocationSet.inline.hpp"
#include "gc/z/zRelocationSetSelector.inline.hpp"
@ -699,11 +700,10 @@ uint ZGenerationYoung::compute_tenuring_threshold(ZRelocationSetSelectorStats st
uint last_populated_age = 0;
size_t last_populated_live = 0;
for (uint i = 0; i <= ZPageAgeMax; ++i) {
const ZPageAge age = static_cast<ZPageAge>(i);
for (ZPageAge age : ZPageAgeRange()) {
const size_t young_live = stats.small(age).live() + stats.medium(age).live() + stats.large(age).live();
if (young_live > 0) {
last_populated_age = i;
last_populated_age = untype(age);
last_populated_live = young_live;
if (young_live_last > 0) {
young_life_expectancy_sum += double(young_live) / double(young_live_last);
@ -842,8 +842,8 @@ void ZGenerationYoung::mark_start() {
// Retire allocating pages
ZAllocator::eden()->retire_pages();
for (ZPageAge i = ZPageAge::survivor1; i <= ZPageAge::survivor14; i = static_cast<ZPageAge>(static_cast<uint>(i) + 1)) {
ZAllocator::relocation(i)->retire_pages();
for (ZPageAge age : ZPageAgeRangeSurvivor) {
ZAllocator::relocation(age)->retire_pages();
}
// Reset allocated/reclaimed/used statistics

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -24,6 +24,7 @@
#ifndef SHARE_GC_Z_ZPAGEAGE_HPP
#define SHARE_GC_Z_ZPAGEAGE_HPP
#include "utilities/enumIterator.hpp"
#include "utilities/globalDefinitions.hpp"
enum class ZPageAge : uint8_t {
@ -45,6 +46,19 @@ enum class ZPageAge : uint8_t {
old
};
constexpr uint ZPageAgeMax = static_cast<uint>(ZPageAge::old);
constexpr uint ZPageAgeCount = static_cast<uint>(ZPageAge::old) + 1;
constexpr ZPageAge ZPageAgeLastPlusOne = static_cast<ZPageAge>(ZPageAgeCount);
ENUMERATOR_RANGE(ZPageAge,
ZPageAge::eden,
ZPageAge::old);
using ZPageAgeRange = EnumRange<ZPageAge>;
constexpr ZPageAgeRange ZPageAgeRangeEden = ZPageAgeRange::create<ZPageAge::eden, ZPageAge::survivor1>();
constexpr ZPageAgeRange ZPageAgeRangeYoung = ZPageAgeRange::create<ZPageAge::eden, ZPageAge::old>();
constexpr ZPageAgeRange ZPageAgeRangeSurvivor = ZPageAgeRange::create<ZPageAge::survivor1, ZPageAge::old>();
constexpr ZPageAgeRange ZPageAgeRangeRelocation = ZPageAgeRange::create<ZPageAge::survivor1, ZPageAgeLastPlusOne>();
constexpr ZPageAgeRange ZPageAgeRangeOld = ZPageAgeRange::create<ZPageAge::old, ZPageAgeLastPlusOne>();
#endif // SHARE_GC_Z_ZPAGEAGE_HPP

View File

@ -1,12 +1,10 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@ -23,13 +21,32 @@
* questions.
*/
package sun.text.resources;
#ifndef SHARE_GC_Z_ZPAGEAGE_INLINE_HPP
#define SHARE_GC_Z_ZPAGEAGE_INLINE_HPP
import java.util.spi.ResourceBundleProvider;
#include "gc/z/zPageAge.hpp"
/**
* An interface for the internal locale data provider for which {@code ResourceBundle}
* searches.
*/
public interface JavaTimeSupplementaryProvider extends ResourceBundleProvider {
#include "utilities/checkedCast.hpp"
#include <type_traits>
inline uint untype(ZPageAge age) {
return static_cast<uint>(age);
}
inline ZPageAge to_zpageage(uint age) {
assert(age < ZPageAgeCount, "Invalid age");
return static_cast<ZPageAge>(age);
}
inline ZPageAge operator+(ZPageAge age, size_t size) {
const auto size_value = checked_cast<std::underlying_type_t<ZPageAge>>(size);
return to_zpageage(untype(age) + size_value);
}
inline ZPageAge operator-(ZPageAge age, size_t size) {
const auto size_value = checked_cast<std::underlying_type_t<ZPageAge>>(size);
return to_zpageage(untype(age) - size_value);
}
#endif // SHARE_GC_Z_ZPAGEAGE_INLINE_HPP

View File

@ -488,11 +488,11 @@ public:
}
ZPage* shared(ZPageAge age) {
return _shared[static_cast<uint>(age) - 1];
return _shared[untype(age - 1)];
}
void set_shared(ZPageAge age, ZPage* page) {
_shared[static_cast<uint>(age) - 1] = page;
_shared[untype(age - 1)] = page;
}
ZPage* alloc_and_retire_target_page(ZForwarding* forwarding, ZPage* target) {
@ -570,11 +570,11 @@ private:
ZPage* target(ZPageAge age) {
return _target[static_cast<uint>(age) - 1];
return _target[untype(age - 1)];
}
void set_target(ZPageAge age, ZPage* page) {
_target[static_cast<uint>(age) - 1] = page;
_target[untype(age - 1)] = page;
}
size_t object_alignment() const {
@ -1232,12 +1232,12 @@ ZPageAge ZRelocate::compute_to_age(ZPageAge from_age) {
return ZPageAge::old;
}
const uint age = static_cast<uint>(from_age);
const uint age = untype(from_age);
if (age >= ZGeneration::young()->tenuring_threshold()) {
return ZPageAge::old;
}
return static_cast<ZPageAge>(age + 1);
return to_zpageage(age + 1);
}
class ZFlipAgePagesTask : public ZTask {

View File

@ -25,6 +25,7 @@
#include "gc/z/zArray.inline.hpp"
#include "gc/z/zForwarding.inline.hpp"
#include "gc/z/zPage.inline.hpp"
#include "gc/z/zPageAge.inline.hpp"
#include "gc/z/zRelocationSetSelector.inline.hpp"
#include "jfr/jfrEvents.hpp"
#include "logging/log.hpp"
@ -117,8 +118,8 @@ void ZRelocationSetSelectorGroup::select_inner() {
const int npages = _live_pages.length();
int selected_from = 0;
int selected_to = 0;
size_t npages_selected[ZPageAgeMax + 1] = { 0 };
size_t selected_live_bytes[ZPageAgeMax + 1] = { 0 };
size_t npages_selected[ZPageAgeCount] = { 0 };
size_t selected_live_bytes[ZPageAgeCount] = { 0 };
size_t selected_forwarding_entries = 0;
size_t from_live_bytes = 0;
@ -149,8 +150,8 @@ void ZRelocationSetSelectorGroup::select_inner() {
if (diff_reclaimable > _fragmentation_limit) {
selected_from = from;
selected_to = to;
selected_live_bytes[static_cast<uint>(page->age())] += page_live_bytes;
npages_selected[static_cast<uint>(page->age())] += 1;
selected_live_bytes[untype(page->age())] += page_live_bytes;
npages_selected[untype(page->age())] += 1;
selected_forwarding_entries = from_forwarding_entries;
}
@ -172,7 +173,7 @@ void ZRelocationSetSelectorGroup::select_inner() {
_forwarding_entries = selected_forwarding_entries;
// Update statistics
for (uint i = 0; i <= ZPageAgeMax; ++i) {
for (uint i = 0; i < ZPageAgeCount; ++i) {
_stats[i]._relocate = selected_live_bytes[i];
_stats[i]._npages_selected = npages_selected[i];
}
@ -200,7 +201,7 @@ void ZRelocationSetSelectorGroup::select() {
}
ZRelocationSetSelectorGroupStats s{};
for (uint i = 0; i <= ZPageAgeMax; ++i) {
for (uint i = 0; i < ZPageAgeCount; ++i) {
s._npages_candidates += _stats[i].npages_candidates();
s._total += _stats[i].total();
s._empty += _stats[i].empty();
@ -239,8 +240,8 @@ void ZRelocationSetSelector::select() {
ZRelocationSetSelectorStats ZRelocationSetSelector::stats() const {
ZRelocationSetSelectorStats stats;
for (uint i = 0; i <= ZPageAgeMax; ++i) {
const ZPageAge age = static_cast<ZPageAge>(i);
for (ZPageAge age : ZPageAgeRange()) {
const uint i = untype(age);
stats._small[i] = _small.stats(age);
stats._medium[i] = _medium.stats(age);
stats._large[i] = _large.stats(age);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -62,9 +62,9 @@ class ZRelocationSetSelectorStats {
friend class ZRelocationSetSelector;
private:
ZRelocationSetSelectorGroupStats _small[ZPageAgeMax + 1];
ZRelocationSetSelectorGroupStats _medium[ZPageAgeMax + 1];
ZRelocationSetSelectorGroupStats _large[ZPageAgeMax + 1];
ZRelocationSetSelectorGroupStats _small[ZPageAgeCount];
ZRelocationSetSelectorGroupStats _medium[ZPageAgeCount];
ZRelocationSetSelectorGroupStats _large[ZPageAgeCount];
size_t _has_relocatable_pages;
@ -90,7 +90,7 @@ private:
ZArray<ZPage*> _live_pages;
ZArray<ZPage*> _not_selected_pages;
size_t _forwarding_entries;
ZRelocationSetSelectorGroupStats _stats[ZPageAgeMax + 1];
ZRelocationSetSelectorGroupStats _stats[ZPageAgeCount];
bool is_disabled();
bool is_selectable();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -29,6 +29,7 @@
#include "gc/z/zArray.inline.hpp"
#include "gc/z/zGlobals.hpp"
#include "gc/z/zPage.inline.hpp"
#include "gc/z/zPageAge.inline.hpp"
#include "utilities/powerOfTwo.hpp"
inline size_t ZRelocationSetSelectorGroupStats::npages_candidates() const {
@ -60,15 +61,15 @@ inline bool ZRelocationSetSelectorStats::has_relocatable_pages() const {
}
inline const ZRelocationSetSelectorGroupStats& ZRelocationSetSelectorStats::small(ZPageAge age) const {
return _small[static_cast<uint>(age)];
return _small[untype(age)];
}
inline const ZRelocationSetSelectorGroupStats& ZRelocationSetSelectorStats::medium(ZPageAge age) const {
return _medium[static_cast<uint>(age)];
return _medium[untype(age)];
}
inline const ZRelocationSetSelectorGroupStats& ZRelocationSetSelectorStats::large(ZPageAge age) const {
return _large[static_cast<uint>(age)];
return _large[untype(age)];
}
inline bool ZRelocationSetSelectorGroup::pre_filter_page(const ZPage* page, size_t live_bytes) const {
@ -113,7 +114,7 @@ inline void ZRelocationSetSelectorGroup::register_live_page(ZPage* page) {
}
const size_t size = page->size();
const uint age = static_cast<uint>(page->age());
const uint age = untype(page->age());
_stats[age]._npages_candidates++;
_stats[age]._total += size;
_stats[age]._live += live;
@ -122,7 +123,7 @@ inline void ZRelocationSetSelectorGroup::register_live_page(ZPage* page) {
inline void ZRelocationSetSelectorGroup::register_empty_page(ZPage* page) {
const size_t size = page->size();
const uint age = static_cast<uint>(page->age());
const uint age = untype(page->age());
_stats[age]._npages_candidates++;
_stats[age]._total += size;
_stats[age]._empty += size;
@ -141,7 +142,7 @@ inline size_t ZRelocationSetSelectorGroup::forwarding_entries() const {
}
inline const ZRelocationSetSelectorGroupStats& ZRelocationSetSelectorGroup::stats(ZPageAge age) const {
return _stats[static_cast<uint>(age)];
return _stats[untype(age)];
}
inline void ZRelocationSetSelector::register_live_page(ZPage* page) {
@ -188,8 +189,7 @@ inline void ZRelocationSetSelector::clear_empty_pages() {
inline size_t ZRelocationSetSelector::total() const {
size_t sum = 0;
for (uint i = 0; i <= ZPageAgeMax; ++i) {
const ZPageAge age = static_cast<ZPageAge>(i);
for (ZPageAge age : ZPageAgeRange()) {
sum += _small.stats(age).total() + _medium.stats(age).total() + _large.stats(age).total();
}
return sum;
@ -197,8 +197,7 @@ inline size_t ZRelocationSetSelector::total() const {
inline size_t ZRelocationSetSelector::empty() const {
size_t sum = 0;
for (uint i = 0; i <= ZPageAgeMax; ++i) {
const ZPageAge age = static_cast<ZPageAge>(i);
for (ZPageAge age : ZPageAgeRange()) {
sum += _small.stats(age).empty() + _medium.stats(age).empty() + _large.stats(age).empty();
}
return sum;
@ -206,8 +205,7 @@ inline size_t ZRelocationSetSelector::empty() const {
inline size_t ZRelocationSetSelector::relocate() const {
size_t sum = 0;
for (uint i = 0; i <= ZPageAgeMax; ++i) {
const ZPageAge age = static_cast<ZPageAge>(i);
for (ZPageAge age : ZPageAgeRange()) {
sum += _small.stats(age).relocate() + _medium.stats(age).relocate() + _large.stats(age).relocate();
}
return sum;

View File

@ -30,6 +30,7 @@
#include "gc/z/zGeneration.inline.hpp"
#include "gc/z/zGlobals.hpp"
#include "gc/z/zNMethodTable.hpp"
#include "gc/z/zPageAge.inline.hpp"
#include "gc/z/zPageAllocator.inline.hpp"
#include "gc/z/zRelocationSetSelector.inline.hpp"
#include "gc/z/zStat.hpp"
@ -1499,9 +1500,7 @@ void ZStatRelocation::print_page_summary() {
summary.relocate += stats.relocate();
};
for (uint i = 0; i <= ZPageAgeMax; ++i) {
const ZPageAge age = static_cast<ZPageAge>(i);
for (ZPageAge age : ZPageAgeRange()) {
account_page_size(small_summary, _selector_stats.small(age));
account_page_size(medium_summary, _selector_stats.medium(age));
account_page_size(large_summary, _selector_stats.large(age));
@ -1557,13 +1556,13 @@ void ZStatRelocation::print_age_table() {
.center("Large")
.end());
size_t live[ZPageAgeMax + 1] = {};
size_t total[ZPageAgeMax + 1] = {};
size_t live[ZPageAgeCount] = {};
size_t total[ZPageAgeCount] = {};
uint oldest_none_empty_age = 0;
for (uint i = 0; i <= ZPageAgeMax; ++i) {
ZPageAge age = static_cast<ZPageAge>(i);
for (ZPageAge age : ZPageAgeRange()) {
uint i = untype(age);
auto summarize_pages = [&](const ZRelocationSetSelectorGroupStats& stats) {
live[i] += stats.live();
total[i] += stats.total();
@ -1579,7 +1578,7 @@ void ZStatRelocation::print_age_table() {
}
for (uint i = 0; i <= oldest_none_empty_age; ++i) {
ZPageAge age = static_cast<ZPageAge>(i);
ZPageAge age = to_zpageage(i);
FormatBuffer<> age_str("");
if (age == ZPageAge::eden) {
@ -1791,8 +1790,7 @@ void ZStatHeap::at_select_relocation_set(const ZRelocationSetSelectorStats& stat
ZLocker<ZLock> locker(&_stat_lock);
size_t live = 0;
for (uint i = 0; i <= ZPageAgeMax; ++i) {
const ZPageAge age = static_cast<ZPageAge>(i);
for (ZPageAge age : ZPageAgeRange()) {
live += stats.small(age).live() + stats.medium(age).live() + stats.large(age).live();
}
_at_mark_end.live = live;

View File

@ -113,7 +113,7 @@
\
product(int, ZTenuringThreshold, -1, DIAGNOSTIC, \
"Young generation tenuring threshold, -1 for dynamic computation")\
range(-1, static_cast<int>(ZPageAgeMax)) \
range(-1, static_cast<int>(ZPageAgeCount) - 1) \
\
develop(bool, ZVerifyOops, false, \
"Verify accessed oops") \

View File

@ -1463,8 +1463,7 @@ JVMCIPrimitiveArray JVMCIEnv::new_byteArray(int length, JVMCI_TRAPS) {
JVMCIObjectArray JVMCIEnv::new_byte_array_array(int length, JVMCI_TRAPS) {
JavaThread* THREAD = JavaThread::current(); // For exception macros.
if (is_hotspot()) {
Klass* byteArrayArrayKlass = TypeArrayKlass::cast(Universe::byteArrayKlass())->array_klass(CHECK_(JVMCIObject()));
objArrayOop result = ObjArrayKlass::cast(byteArrayArrayKlass) ->allocate(length, CHECK_(JVMCIObject()));
objArrayOop result = oopFactory::new_objArray(Universe::byteArrayKlass(), length, CHECK_(JVMCIObject()));
return wrap(result);
} else {
JNIAccessMark jni(this, THREAD);

View File

@ -216,6 +216,7 @@ class outputStream;
LOG_TAG(valuebasedclasses) \
LOG_TAG(verification) \
LOG_TAG(verify) \
LOG_TAG(vmatree) \
LOG_TAG(vmmutex) \
LOG_TAG(vmoperation) \
LOG_TAG(vmthread) \

View File

@ -189,14 +189,6 @@ static size_t align_to_page_size(size_t size) {
}
void CodeHeap::on_code_mapping(char* base, size_t size) {
#ifdef LINUX
extern void linux_wrap_code(char* base, size_t size);
linux_wrap_code(base, size);
#endif
}
bool CodeHeap::reserve(ReservedSpace rs, size_t committed_size, size_t segment_size) {
assert(rs.size() >= committed_size, "reserved < committed");
assert(is_aligned(committed_size, rs.page_size()), "must be page aligned");
@ -213,7 +205,6 @@ bool CodeHeap::reserve(ReservedSpace rs, size_t committed_size, size_t segment_s
return false;
}
on_code_mapping(_memory.low(), _memory.committed_size());
_number_of_committed_segments = size_to_segments(_memory.committed_size());
_number_of_reserved_segments = size_to_segments(_memory.reserved_size());
assert(_number_of_reserved_segments >= _number_of_committed_segments, "just checking");
@ -250,7 +241,6 @@ bool CodeHeap::expand_by(size_t size) {
}
char* base = _memory.low() + _memory.committed_size();
if (!_memory.expand_by(dm)) return false;
on_code_mapping(base, dm);
size_t i = _number_of_committed_segments;
_number_of_committed_segments = size_to_segments(_memory.committed_size());
assert(_number_of_reserved_segments == size_to_segments(_memory.reserved_size()), "number of reserved segments should not change");

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -146,9 +146,6 @@ class CodeHeap : public CHeapObj<mtCode> {
void* next_used(HeapBlock* b) const;
HeapBlock* block_start(void* p) const;
// to perform additional actions on creation of executable code
void on_code_mapping(char* base, size_t size);
public:
CodeHeap(const char* name, const CodeBlobType code_blob_type);

View File

@ -40,41 +40,41 @@
#include "utilities/utf8.hpp"
typeArrayOop oopFactory::new_boolArray(int length, TRAPS) {
return Universe::boolArrayKlass()->allocate(length, THREAD);
return Universe::boolArrayKlass()->allocate_instance(length, THREAD);
}
typeArrayOop oopFactory::new_charArray(int length, TRAPS) {
return Universe::charArrayKlass()->allocate(length, THREAD);
return Universe::charArrayKlass()->allocate_instance(length, THREAD);
}
typeArrayOop oopFactory::new_floatArray(int length, TRAPS) {
return Universe::floatArrayKlass()->allocate(length, THREAD);
return Universe::floatArrayKlass()->allocate_instance(length, THREAD);
}
typeArrayOop oopFactory::new_doubleArray(int length, TRAPS) {
return Universe::doubleArrayKlass()->allocate(length, THREAD);
return Universe::doubleArrayKlass()->allocate_instance(length, THREAD);
}
typeArrayOop oopFactory::new_byteArray(int length, TRAPS) {
return Universe::byteArrayKlass()->allocate(length, THREAD);
return Universe::byteArrayKlass()->allocate_instance(length, THREAD);
}
typeArrayOop oopFactory::new_shortArray(int length, TRAPS) {
return Universe::shortArrayKlass()->allocate(length, THREAD);
return Universe::shortArrayKlass()->allocate_instance(length, THREAD);
}
typeArrayOop oopFactory::new_intArray(int length, TRAPS) {
return Universe::intArrayKlass()->allocate(length, THREAD);
return Universe::intArrayKlass()->allocate_instance(length, THREAD);
}
typeArrayOop oopFactory::new_longArray(int length, TRAPS) {
return Universe::longArrayKlass()->allocate(length, THREAD);
return Universe::longArrayKlass()->allocate_instance(length, THREAD);
}
// create java.lang.Object[]
objArrayOop oopFactory::new_objectArray(int length, TRAPS) {
assert(Universe::objectArrayKlass() != nullptr, "Too early?");
return Universe::objectArrayKlass()->allocate(length, THREAD);
return Universe::objectArrayKlass()->allocate_instance(length, THREAD);
}
typeArrayOop oopFactory::new_charArray(const char* utf8_str, TRAPS) {
@ -88,7 +88,7 @@ typeArrayOop oopFactory::new_charArray(const char* utf8_str, TRAPS) {
typeArrayOop oopFactory::new_typeArray(BasicType type, int length, TRAPS) {
TypeArrayKlass* klass = Universe::typeArrayKlass(type);
return klass->allocate(length, THREAD);
return klass->allocate_instance(length, THREAD);
}
// Create a Java array that points to Symbol.

View File

@ -91,7 +91,7 @@ void MemoryFileTracker::print_report_on(const MemoryFile* file, outputStream* st
NMTUtil::tag_to_name(prev->val().out.mem_tag()));
{
StreamIndentor si(stream, 4);
_stack_storage.get(prev->val().out.stack()).print_on(stream);
_stack_storage.get(prev->val().out.reserved_stack()).print_on(stream);
}
stream->cr();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -43,11 +43,8 @@
class NativeCallStackStorage : public CHeapObjBase {
public:
using StackIndex = int;
private:
constexpr static const StackIndex invalid = std::numeric_limits<StackIndex>::max() - 1;
public:
static bool equals(const StackIndex a, const StackIndex b) {
return a == b;
}

View File

@ -28,21 +28,228 @@
#include "utilities/globalDefinitions.hpp"
#include "utilities/growableArray.hpp"
const VMATree::RegionData VMATree::empty_regiondata{NativeCallStackStorage::StackIndex{}, mtNone};
// Semantics
// This tree is used to store and track the state of virtual memory regions.
// The nodes in the tree are key-value pairs where the key is the memory address and the value is the State of the memory regions.
// The State of a region describes whether the region is released, reserved or committed, which MemTag it has and where in
// Hotspot (using call-stacks) it is reserved or committed.
// Each node holds the State of the regions to its left and right. Each memory region is described by two
// memory addresses for its start and end.
// For example, to describe the region that starts at memory address 0xA000 with size 0x1000, there will be two nodes
// with the keys 0xA000 (node A) and 0xB000 (node B) in the tree. The value of the key-value pairs of node A and
// node B describe the region's State, using right of A and left of B (<--left--A--right-->.....<--left--B--right-->...).
//
// Virtual memory can be reserved, committed, uncommitted and released. For each operation a request
// (<from-address, to-address, operation, tag, call-stack, which-tag-to-use >) is sent to the tree to handle.
//
// The expected changes are described here for each operation:
//
// ### Reserve a region
// When a region is reserved, all the overlapping regions in the tree should:
// - be marked as Reserved
// - take MemTag of the operation
// - store call-stack of the request to the reserve call-stack
// - clear commit call-stack
//
// ### Commit a region
// When a region is committed, all the overlapping regions in the tree should:
// - be marked as Committed
// - take MemTag of the operation or MemTag of the existing region, depends on which-tag-to-use in the request
// - if the region is in Released state
// - mark the region as both Reserved and Committed
// - store the call-stack of the request to the reserve call-stack
// - store the call-stack of the request to the commit call-stack
//
// ### Uncommit a region
// When a region is uncommitted, all the overlapping regions in the tree should:
// - be ignored if the region is in Released state
// - be marked as Reserved
// - not change the MemTag
// - not change the reserve call-stack
// - clear commit call-stack
//
// ### Release a region
// When a region is released, all the overlapping regions in the tree should:
// - be marked as Released
// - set the MemTag to mtNone
// - clear both reserve and commit call-stack
//
// --- Accounting
// After each operation, the tree should be able to report how much memory is reserved or committed per MemTag.
// So for each region that changes to a new State, the report should contain (separately for each tag) the amount
// of reserve and commit that are changed (increased or decreased) due to the operation.
const VMATree::RegionData VMATree::empty_regiondata{NativeCallStackStorage::invalid, mtNone};
const char* VMATree::statetype_strings[3] = {
"reserved", "committed", "released",
"released", "reserved", "committed"
};
VMATree::SummaryDiff VMATree::register_mapping(position A, position B, StateType state,
VMATree::SIndex VMATree::get_new_reserve_callstack(const SIndex es, const StateType ex, const RequestInfo& req) const {
const SIndex ES = NativeCallStackStorage::invalid; // Empty Stack
const SIndex rq = req.callstack;
const int op = req.op_to_index();
const Operation oper = req.op();
assert(op >= 0 && op < 4, "should be");
assert(op >= 0 && op < 4, "should be");
// existing state
SIndex result[4][3] = {// Rl Rs C
{ES, ES, ES}, // op == Release
{rq, rq, rq}, // op == Reserve
{es, es, es}, // op == Commit
{es, es, es} // op == Uncommit
};
// When committing a Released region, the reserve-call-stack of the region should also be as what is in the request
if (oper == Operation::Commit && ex == StateType::Released) {
return rq;
} else {
return result[op][state_to_index(ex)];
}
}
VMATree::SIndex VMATree::get_new_commit_callstack(const SIndex es, const StateType ex, const RequestInfo& req) const {
const SIndex ES = NativeCallStackStorage::invalid; // Empty Stack
const SIndex rq = req.callstack;
const int op_index = req.op_to_index();
const Operation op = req.op();
assert(op_index >= 0 && op_index < 4, "should be");
// existing state
SIndex result[4][3] = {// Rl Rs C
{ES, ES, ES}, // op == Release
{ES, ES, ES}, // op == Reserve
{rq, rq, rq}, // op == Commit
{ES, ES, ES} // op == Uncommit
};
return result[op_index][state_to_index(ex)];
}
VMATree::StateType VMATree::get_new_state(const StateType ex, const RequestInfo& req) const {
const StateType Rl = StateType::Released;
const StateType Rs = StateType::Reserved;
const StateType C = StateType::Committed;
const int op = req.op_to_index();
assert(op >= 0 && op < 4, "should be");
// existing state
StateType result[4][3] = {// Rl Rs C
{Rl, Rl, Rl}, // op == Release
{Rs, Rs, Rs}, // op == Reserve
{ C, C, C}, // op == Commit
{Rl, Rs, Rs} // op == Uncommit
};
return result[op][state_to_index(ex)];
}
MemTag VMATree::get_new_tag(const MemTag ex, const RequestInfo& req) const {
switch(req.op()) {
case Operation::Release:
return mtNone;
case Operation::Reserve:
return req.tag;
case Operation::Commit:
return req.use_tag_inplace ? ex : req.tag;
case Operation::Uncommit:
return ex;
default:
break;
}
return mtNone;
}
void VMATree::compute_summary_diff(const SingleDiff::delta region_size,
const MemTag current_tag,
const StateType& ex,
const RequestInfo& req,
const MemTag operation_tag,
SummaryDiff& diff) const {
const StateType Rl = StateType::Released;
const StateType Rs = StateType::Reserved;
const StateType C = StateType::Committed;
const int op = req.op_to_index();
const Operation oper = req.op();
assert(op >= 0 && op < 4, "should be");
SingleDiff::delta a = region_size;
// A region with size `a` has a state as <column> and an operation is requested as in <row>
// The region has tag `current_tag` and the operation has tag `operation_tag`.
// For each state, we decide how much to be added/subtracted from current_tag to operation_tag. Two tables for reserve and commit.
// Each pair of <x,y> in the table means add `x` to current_tag and add `y` to operation_tag. There are 3 pairs in each row for 3 states.
// For example, `reserve[1][4,5]` says `-a,a` means:
// - we are reserving with operation_tag a region which is already commited with current_tag
// - since we are reserving, then `a` will be added to operation_tag. (`y` is `a`)
// - since we uncommitting (by reserving) then `a` is to be subtracted from current_tag. (`x` is `-a`).
// - amount of uncommitted size is in table `commit[1][4,5]` which is `-a,0` that means subtract `a` from current_tag.
// existing state
SingleDiff::delta reserve[4][3*2] = {// Rl Rs C
{0,0, -a,0, -a,0 }, // op == Release
{0,a, -a,a, -a,a }, // op == Reserve
{0,a, -a,a, -a,a }, // op == Commit
{0,0, 0,0, 0,0 } // op == Uncommit
};
SingleDiff::delta commit[4][3*2] = {// Rl Rs C
{0,0, 0,0, -a,0 }, // op == Release
{0,0, 0,0, -a,0 }, // op == Reserve
{0,a, 0,a, -a,a }, // op == Commit
{0,0, 0,0, -a,0 } // op == Uncommit
};
SingleDiff& from_rescom = diff.tag[NMTUtil::tag_to_index(current_tag)];
SingleDiff& to_rescom = diff.tag[NMTUtil::tag_to_index(operation_tag)];
int st = state_to_index(ex);
from_rescom.reserve += reserve[op][st * 2 ];
to_rescom.reserve += reserve[op][st * 2 + 1];
from_rescom.commit += commit[op][st * 2 ];
to_rescom.commit += commit[op][st * 2 + 1];
}
// update the region state between n1 and n2. Since n1 and n2 are pointers, any update of them will be visible from tree.
// If n1 is noop, it can be removed because its left region (n1->val().in) is already decided and its right state (n1->val().out) is decided here.
// The state of right of n2 (n2->val().out) cannot be decided here yet.
void VMATree::update_region(TreapNode* n1, TreapNode* n2, const RequestInfo& req, SummaryDiff& diff) {
assert(n1 != nullptr,"sanity");
assert(n2 != nullptr,"sanity");
//.........n1......n2......
// ^------^
// |
IntervalState exSt = n1->val().out; // existing state info
StateType existing_state = exSt.type();
MemTag existing_tag = exSt.mem_tag();
SIndex existing_reserve_callstack = exSt.reserved_stack();
SIndex existing_commit_callstack = exSt.committed_stack();
StateType new_state = get_new_state(existing_state, req);
MemTag new_tag = get_new_tag(n1->val().out.mem_tag(), req);
SIndex new_reserve_callstack = get_new_reserve_callstack(existing_reserve_callstack, existing_state, req);
SIndex new_commit_callstack = get_new_commit_callstack(existing_commit_callstack, existing_state, req);
// n1........n2
// out-->
n1->val().out.set_tag(new_tag);
n1->val().out.set_type(new_state);
n1->val().out.set_reserve_stack(new_reserve_callstack);
n1->val().out.set_commit_stack(new_commit_callstack);
// n1........n2
// <--in
n2->val().in.set_tag(new_tag);
n2->val().in.set_type(new_state);
n2->val().in.set_reserve_stack(new_reserve_callstack);
n2->val().in.set_commit_stack(new_commit_callstack);
SingleDiff::delta region_size = n2->key() - n1->key();
compute_summary_diff(region_size, existing_tag, existing_state, req, new_tag, diff);
}
VMATree::SummaryDiff VMATree::register_mapping(position _A, position _B, StateType state,
const RegionData& metadata, bool use_tag_inplace) {
assert(!use_tag_inplace || metadata.mem_tag == mtNone,
"If using use_tag_inplace, then the supplied tag should be mtNone, was instead: %s", NMTUtil::tag_to_name(metadata.mem_tag));
if (A == B) {
// A 0-sized mapping isn't worth recording.
if (_A == _B) {
return SummaryDiff();
}
assert(_A < _B, "should be");
SummaryDiff diff;
RequestInfo req{_A, _B, state, metadata.mem_tag, metadata.stack_idx, use_tag_inplace};
IntervalChange stA{
IntervalState{StateType::Released, empty_regiondata},
IntervalState{ state, metadata}
@ -51,176 +258,400 @@ VMATree::SummaryDiff VMATree::register_mapping(position A, position B, StateType
IntervalState{ state, metadata},
IntervalState{StateType::Released, empty_regiondata}
};
stA.out.set_commit_stack(NativeCallStackStorage::invalid);
stB.in.set_commit_stack(NativeCallStackStorage::invalid);
VMATreap::Range rA = _tree.find_enclosing_range(_A);
VMATreap::Range rB = _tree.find_enclosing_range(_B);
// First handle A.
// Find closest node that is LEQ A
bool LEQ_A_found = false;
AddressState LEQ_A;
TreapNode* leqA_n = _tree.closest_leq(A);
if (leqA_n == nullptr) {
assert(!use_tag_inplace, "Cannot use the tag inplace if no pre-existing tag exists. From: " PTR_FORMAT " To: " PTR_FORMAT, A, B);
if (use_tag_inplace) {
log_debug(nmt)("Cannot use the tag inplace if no pre-existing tag exists. From: " PTR_FORMAT " To: " PTR_FORMAT, A, B);
// nodes: .....X.......Y...Z......W........U
// request: A------------------B
// X,Y = enclosing_nodes(A)
// W,U = enclosing_nodes(B)
// The cases are whether or not X and Y exists and X == A. (A == Y doesn't happen since it is searched by 'lt' predicate)
// The cases are whether or not W and U exists and W == B. (B == U doesn't happen since it is searched by 'lt' predicate)
// We update regions in 3 sections: 1) X..A..Y, 2) Y....W, 3) W..B..U
// Y: is the closest node greater than A, but less than B
// W: is the closest node less than B, but greater than A
// The regions in [Y,W) are updated in a loop. We update X..A..Y before the loop and W..B..U after the loop.
// The table below summarizes the overlap cases. The overlapping case depends on whether X, Y, W and U exist or not,
// and if they exist whether they are the same or not.
// In the notations here, when there is not dot ('.') between two nodes it meaans that they are the same. For example,
// ...XA....Y.... means X == A.
// row 0: .........A..................B.....
// row 1: .........A...YW.............B..... // it is impossible, since it means only one node exists in the tree.
// row 2: .........A...Y..........W...B.....
// row 3: .........A...Y.............WB.....
// row 4: .....X...A..................B.....
// row 5: .....X...A...YW.............B.....
// row 6: .....X...A...Y..........W...B.....
// row 7: .....X...A...Y.............WB.....
// row 8: ........XA..................B.....
// row 9: ........XA...YW.............B.....
// row 10: ........XA...Y..........W...B.....
// row 11: ........XA...Y.............WB.....
// row 12: .........A..................B....U
// row 13: .........A...YW.............B....U
// row 14: .........A...Y..........W...B....U
// row 15: .........A...Y.............WB....U
// row 16: .....X...A..................B....U
// row 17: .....X...A...YW.............B....U
// row 18: .....X...A...Y..........W...B....U
// row 19: .....X...A...Y.............WB....U
// row 20: ........XA..................B....U
// row 21: ........XA...YW.............B....U
// row 22: ........XA...Y..........W...B....U
// row 23: ........XA...Y.............WB....U
// We intentionally did not summarize/compress the cases to keep them as separate.
// This expanded way of describing the cases helps us to understand/analyze/verify/debug/maintain
// the corresponding code more easily.
// Mapping of table to row, row to switch-case should be consistent. If one changes, the others have
// to be updated accordingly. The sequence of dependecies is: table -> row no -> switch(row)-case -> code.
// Meaning that whenever any of one item in this sequence is changed, the rest of the consequent items to
// be checked/changed.
TreapNode* X = rA.start;
TreapNode* Y = rA.end;
TreapNode* W = rB.start;
TreapNode* U = rB.end;
TreapNode nA{_A, stA, 0}; // the node that represents A
TreapNode nB{_B, stB, 0}; // the node that represents B
TreapNode* A = &nA;
TreapNode* B = &nB;
auto upsert_if= [&](TreapNode* node) {
if (!node->val().is_noop()) {
_tree.upsert(node->key(), node->val());
}
// No match. We add the A node directly, unless it would have no effect.
if (!stA.is_noop()) {
_tree.upsert(A, stA);
};
// update region between n1 and n2
auto update = [&](TreapNode* n1, TreapNode* n2) {
update_region(n1, n2, req, diff);
};
auto remove_if = [&](TreapNode* node) -> bool{
if (node->val().is_noop()) {
_tree.remove(node->key());
return true;
}
} else {
LEQ_A_found = true;
LEQ_A = AddressState{leqA_n->key(), leqA_n->val()};
StateType leqA_state = leqA_n->val().out.type();
StateType new_state = stA.out.type();
// If we specify use_tag_inplace then the new region takes over the current tag instead of the tag in metadata.
// This is important because the VirtualMemoryTracker API doesn't require supplying the tag for some operations.
if (use_tag_inplace) {
assert(leqA_n->val().out.type() != StateType::Released, "Should not use inplace the tag of a released region");
MemTag tag = leqA_n->val().out.mem_tag();
stA.out.set_tag(tag);
stB.in.set_tag(tag);
}
// Unless we know better, let B's outgoing state be the outgoing state of the node at or preceding A.
// Consider the case where the found node is the start of a region enclosing [A,B)
stB.out = out_state(leqA_n);
// Direct address match.
if (leqA_n->key() == A) {
// Take over in state from old address.
stA.in = in_state(leqA_n);
// We may now be able to merge two regions:
// If the node's old state matches the new, it becomes a noop. That happens, for example,
// when expanding a committed area: commit [x1, A); ... commit [A, x3)
// and the result should be a larger area, [x1, x3). In that case, the middle node (A and le_n)
// is not needed anymore. So we just remove the old node.
stB.in = stA.out;
if (stA.is_noop()) {
// invalidates leqA_n
_tree.remove(leqA_n->key());
} else {
// If the state is not matching then we have different operations, such as:
// reserve [x1, A); ... commit [A, x2); or
// reserve [x1, A), mem_tag1; ... reserve [A, x2), mem_tag2; or
// reserve [A, x1), mem_tag1; ... reserve [A, x2), mem_tag2;
// then we re-use the existing out node, overwriting its old metadata.
leqA_n->val() = stA;
return false;
};
GrowableArrayCHeap<position, mtNMT> to_be_removed;
// update regions in [Y,W)
auto update_loop = [&]() {
TreapNode* prev = nullptr;
_tree.visit_range_in_order(_A + 1, _B + 1, [&](TreapNode* curr) {
if (prev != nullptr) {
update_region(prev, curr, req, diff);
// during visit, structure of the tree should not be changed
// keep the keys to be removed, and remove them later
if (prev->val().is_noop()) {
to_be_removed.push(prev->key());
}
}
} else {
// The address must be smaller.
assert(A > leqA_n->key(), "must be");
prev = curr;
});
};
// update region of [A,T)
auto update_A = [&](TreapNode* T) {
A->val().out = A->val().in;
update(A, T);
};
bool X_exists = X != nullptr;
bool Y_exists = Y != nullptr && Y->key() <= _B;
bool W_exists = W != nullptr && W->key() > _A;
bool U_exists = U != nullptr;
bool X_eq_A = X_exists && X->key() == _A;
bool W_eq_B = W_exists && W->key() == _B;
bool Y_eq_W = Y_exists && W_exists && W->key() == Y->key();
int row = -1;
#ifdef ASSERT
auto print_case = [&]() {
log_trace(vmatree)(" req: %4d---%4d", (int)_A, (int)_B);
log_trace(vmatree)(" row: %2d", row);
log_trace(vmatree)(" X: %4ld", X_exists ? (long)X->key() : -1);
log_trace(vmatree)(" Y: %4ld", Y_exists ? (long)Y->key() : -1);
log_trace(vmatree)(" W: %4ld", W_exists ? (long)W->key() : -1);
log_trace(vmatree)(" U: %4ld", U_exists ? (long)U->key() : -1);
};
#endif
// Order of the nodes if they exist are as: X <= A < Y <= W <= B < U
// A---------------------------B
// X Y YW WB U
// XA Y YW WB U
if (!X_exists && !Y_exists && !U_exists) { row = 0; }
if (!X_exists && Y_exists && Y_eq_W && !W_eq_B && !U_exists) { row = 1; }
if (!X_exists && Y_exists && !Y_eq_W && !W_eq_B && !U_exists) { row = 2; }
if (!X_exists && Y_exists && W_eq_B && !U_exists) { row = 3; }
// We add a new node, but only if there would be a state change. If there would not be a
// state change, we just omit the node.
// That happens, for example, when reserving within an already reserved region with identical metadata.
stA.in = out_state(leqA_n); // .. and the region's prior state is the incoming state
if (stA.is_noop()) {
// Nothing to do.
} else {
// Add new node.
_tree.upsert(A, stA);
}
if ( X_exists && !Y_exists && !U_exists) { row = 4; }
if ( X_exists && Y_exists && Y_eq_W && !W_eq_B && !U_exists) { row = 5; }
if ( X_exists && Y_exists && !Y_eq_W && !W_eq_B && !U_exists) { row = 6; }
if ( X_exists && Y_exists && W_eq_B && !U_exists) { row = 7; }
if ( X_eq_A && !Y_exists && !U_exists) { row = 8; }
if ( X_eq_A && Y_exists && Y_eq_W && !W_eq_B && !U_exists) { row = 9; }
if ( X_eq_A && Y_exists && !Y_eq_W && !W_eq_B && !U_exists) { row = 10; }
if ( X_eq_A && Y_exists && W_eq_B && !U_exists) { row = 11; }
if (!X_exists && !Y_exists && U_exists) { row = 12; }
if (!X_exists && Y_exists && Y_eq_W && !W_eq_B && U_exists) { row = 13; }
if (!X_exists && Y_exists && !Y_eq_W && !W_eq_B && U_exists) { row = 14; }
if (!X_exists && Y_exists && W_eq_B && U_exists) { row = 15; }
if ( X_exists && !Y_exists && U_exists) { row = 16; }
if ( X_exists && Y_exists && Y_eq_W && !W_eq_B && U_exists) { row = 17; }
if ( X_exists && Y_exists && !Y_eq_W && !W_eq_B && U_exists) { row = 18; }
if ( X_exists && Y_exists && W_eq_B && U_exists) { row = 19; }
if ( X_eq_A && !Y_exists && U_exists) { row = 20; }
if ( X_eq_A && Y_exists && Y_eq_W && !W_eq_B && U_exists) { row = 21; }
if ( X_eq_A && Y_exists && !Y_eq_W && !W_eq_B && U_exists) { row = 22; }
if ( X_eq_A && Y_exists && W_eq_B && U_exists) { row = 23; }
DEBUG_ONLY(print_case();)
switch(row) {
// row 0: .........A..................B.....
case 0: {
update_A(B);
upsert_if(A);
upsert_if(B);
break;
}
}
// Now we handle B.
// We first search all nodes that are (A, B]. All of these nodes
// need to be deleted and summary accounted for. The last node before B determines B's outgoing state.
// If there is no node between A and B, its A's incoming state.
GrowableArrayCHeap<AddressState, mtNMT> to_be_deleted_inbetween_a_b;
bool B_needs_insert = true;
// Find all nodes between (A, B] and record their addresses and values. Also update B's
// outgoing state.
_tree.visit_range_in_order(A + 1, B + 1, [&](TreapNode* head) {
int cmp_B = PositionComparator::cmp(head->key(), B);
stB.out = out_state(head);
if (cmp_B < 0) {
// Record all nodes preceding B.
to_be_deleted_inbetween_a_b.push({head->key(), head->val()});
} else if (cmp_B == 0) {
// Re-purpose B node, unless it would result in a noop node, in
// which case record old node at B for deletion and summary accounting.
if (stB.is_noop()) {
to_be_deleted_inbetween_a_b.push(AddressState{B, head->val()});
} else {
head->val() = stB;
}
B_needs_insert = false;
// row 1: .........A...YW.............B.....
case 1: {
ShouldNotReachHere();
break;
}
});
// Insert B node if needed
if (B_needs_insert && // Was not already inserted
!stB.is_noop()) // The operation is differing
{
_tree.upsert(B, stB);
}
// We now need to:
// a) Delete all nodes between (A, B]. Including B in the case of a noop.
// b) Perform summary accounting
SummaryDiff diff;
if (to_be_deleted_inbetween_a_b.length() == 0 && LEQ_A_found) {
// We must have smashed a hole in an existing region (or replaced it entirely).
// LEQ_A < A < B <= C
SingleDiff& rescom = diff.tag[NMTUtil::tag_to_index(LEQ_A.out().mem_tag())];
if (LEQ_A.out().type() == StateType::Reserved) {
rescom.reserve -= B - A;
} else if (LEQ_A.out().type() == StateType::Committed) {
rescom.commit -= B - A;
rescom.reserve -= B - A;
// row 2: .........A...Y..........W...B.....
case 2: {
update_A(Y);
upsert_if(A);
update_loop();
remove_if(Y);
update(W, B);
remove_if(W);
upsert_if(B);
break;
}
}
// Track the previous node.
AddressState prev{A, stA};
for (int i = 0; i < to_be_deleted_inbetween_a_b.length(); i++) {
const AddressState delete_me = to_be_deleted_inbetween_a_b.at(i);
_tree.remove(delete_me.address);
// Perform summary accounting
SingleDiff& rescom = diff.tag[NMTUtil::tag_to_index(delete_me.in().mem_tag())];
if (delete_me.in().type() == StateType::Reserved) {
rescom.reserve -= delete_me.address - prev.address;
} else if (delete_me.in().type() == StateType::Committed) {
rescom.commit -= delete_me.address - prev.address;
rescom.reserve -= delete_me.address - prev.address;
// row 3: .........A...Y.............WB.....
case 3: {
update_A(Y);
upsert_if(A);
update_loop();
remove_if(W);
break;
}
prev = delete_me;
}
if (prev.address != A && prev.out().type() != StateType::Released) {
// The last node wasn't released, so it must be connected to a node outside of (A, B)
// A - prev - B - (some node >= B)
// It might be that prev.address == B == (some node >= B), this is fine.
if (prev.out().type() == StateType::Reserved) {
SingleDiff& rescom = diff.tag[NMTUtil::tag_to_index(prev.out().mem_tag())];
rescom.reserve -= B - prev.address;
} else if (prev.out().type() == StateType::Committed) {
SingleDiff& rescom = diff.tag[NMTUtil::tag_to_index(prev.out().mem_tag())];
rescom.commit -= B - prev.address;
rescom.reserve -= B - prev.address;
// row 4: .....X...A..................B.....
case 4: {
A->val().in = X->val().out;
update_A(B);
upsert_if(A);
upsert_if(B);
break;
}
// row 5: .....X...A...YW.............B.....
case 5: {
A->val().in = X->val().out;
update_A(Y);
upsert_if(A);
update(Y, B);
remove_if(Y);
upsert_if(B);
break;
}
// row 6: .....X...A...Y..........W...B.....
case 6: {
A->val().in = X->val().out;
update_A(Y);
upsert_if(A);
update_loop();
update(W, B);
remove_if(W);
upsert_if(B);
break;
}
// row 7: .....X...A...Y.............WB.....
case 7: {
A->val().in = X->val().out;
update_A(Y);
upsert_if(A);
update_loop();
remove_if(W);
break;
}
// row 8: ........XA..................B.....
case 8: {
update(X, B);
remove_if(X);
upsert_if(B);
break;
}
// row 9: ........XA...YW.............B.....
case 9: {
update(X, Y);
remove_if(X);
update(W, B);
remove_if(W);
upsert_if(B);
break;
}
// row 10: ........XA...Y..........W...B.....
case 10: {
update(X, Y);
remove_if(X);
update_loop();
update(W, B);
remove_if(W);
upsert_if(B);
break;
}
// row 11: ........XA...Y.............WB.....
case 11: {
update(X, Y);
remove_if(X);
update_loop();
remove_if(W);
break;
}
// row 12: .........A..................B....U
case 12: {
update_A(B);
upsert_if(A);
upsert_if(B);
break;
}
// row 13: .........A...YW.............B....U
case 13: {
update_A(Y);
upsert_if(A);
update(W, B);
remove_if(W);
B->val().out = U->val().in;
upsert_if(B);
break;
}
// row 14: .........A...Y..........W...B....U
case 14: {
update_A(Y);
upsert_if(A);
update_loop();
update(W, B);
remove_if(W);
B->val().out = U->val().in;
upsert_if(B);
break;
}
// row 15: .........A...Y.............WB....U
case 15: {
update_A(Y);
upsert_if(A);
update_loop();
remove_if(W);
break;
}
// row 16: .....X...A..................B....U
case 16: {
A->val().in = X->val().out;
update_A(B);
upsert_if(A);
B->val().out = U->val().in;
upsert_if(B);
break;
}
// row 17: .....X...A...YW.............B....U
case 17: {
A->val().in = X->val().out;
update_A(Y);
upsert_if(A);
update(W, B);
remove_if(W);
B->val().out = U->val().in;
upsert_if(B);
break;
}
// row 18: .....X...A...Y..........W...B....U
case 18: {
A->val().in = X->val().out;
update_A(Y);
upsert_if(A);
update_loop();
update(W, B);
remove_if(W);
B->val().out = U->val().in;
upsert_if(B);
break;
}
// row 19: .....X...A...Y.............WB....U
case 19: {
A->val().in = X->val().out;
update_A(Y);
upsert_if(A);
update_loop();
remove_if(W);
break;
}
// row 20: ........XA..................B....U
case 20: {
update(X, B);
remove_if(X);
B->val().out = U->val().in;
upsert_if(B);
break;
}
// row 21: ........XA...YW.............B....U
case 21: {
update(X, Y);
remove_if(X);
update(W, B);
remove_if(W);
B->val().out = U->val().in;
upsert_if(B);
break;
}
// row 22: ........XA...Y..........W...B....U
case 22: {
update(X, Y);
remove_if(X);
update_loop();
update(W, B);
remove_if(W);
B->val().out = U->val().in;
upsert_if(B);
break;
}
// row 23: ........XA...Y.............WB....U
case 23: {
update(X, Y);
remove_if(X);
update_loop();
remove_if(W);
break;
}
default:
ShouldNotReachHere();
}
// Finally, we can register the new region [A, B)'s summary data.
SingleDiff& rescom = diff.tag[NMTUtil::tag_to_index(stA.out.mem_tag())];
if (state == StateType::Reserved) {
rescom.reserve += B - A;
} else if (state == StateType::Committed) {
rescom.commit += B - A;
rescom.reserve += B - A;
// Remove the 'noop' nodes that found inside the loop
while(to_be_removed.length() != 0) {
_tree.remove(to_be_removed.pop());
}
return diff;
}
#ifdef ASSERT
void VMATree::print_on(outputStream* out) {
visit_in_order([&](TreapNode* current) {
out->print("%zu (%s) - %s - ", current->key(), NMTUtil::tag_to_name(out_state(current).mem_tag()),
statetype_to_string(out_state(current).type()));
out->print("%zu (%s) - %s [%d, %d]-> ", current->key(), NMTUtil::tag_to_name(out_state(current).mem_tag()),
statetype_to_string(out_state(current).type()), current->val().out.reserved_stack(), current->val().out.committed_stack());
});
out->cr();
}
@ -268,7 +699,7 @@ VMATree::SummaryDiff VMATree::set_tag(const position start, const size size, con
SummaryDiff diff;
// Ignore any released ranges, these must be mtNone and have no stack
if (type != StateType::Released) {
RegionData new_data = RegionData(out.stack(), tag);
RegionData new_data = RegionData(out.reserved_stack(), tag);
SummaryDiff result = register_mapping(from, end, type, new_data);
diff.add(result);
}
@ -289,7 +720,7 @@ VMATree::SummaryDiff VMATree::set_tag(const position start, const size size, con
StateType type = out.type();
if (type != StateType::Released) {
RegionData new_data = RegionData(out.stack(), tag);
RegionData new_data = RegionData(out.reserved_stack(), tag);
SummaryDiff result = register_mapping(from, end, type, new_data);
diff.add(result);
}

View File

@ -44,6 +44,7 @@ class VMATree {
public:
using position = size_t;
using size = size_t;
using SIndex = NativeCallStackStorage::StackIndex;
class PositionComparator {
public:
@ -55,7 +56,7 @@ public:
}
};
enum class StateType : uint8_t { Reserved, Committed, Released, LAST };
enum class StateType : uint8_t { Released, Reserved, Committed, LAST };
private:
static const char* statetype_strings[static_cast<uint8_t>(StateType::LAST)];
@ -70,12 +71,12 @@ public:
// Each point has some stack and a tag associated with it.
struct RegionData {
const NativeCallStackStorage::StackIndex stack_idx;
const SIndex stack_idx;
const MemTag mem_tag;
RegionData() : stack_idx(), mem_tag(mtNone) {}
RegionData(NativeCallStackStorage::StackIndex stack_idx, MemTag mem_tag)
RegionData(SIndex stack_idx, MemTag mem_tag)
: stack_idx(stack_idx), mem_tag(mem_tag) {}
static bool equals(const RegionData& a, const RegionData& b) {
@ -91,15 +92,27 @@ private:
private:
// Store the type and mem_tag as two bytes
uint8_t type_tag[2];
NativeCallStackStorage::StackIndex sidx;
NativeCallStackStorage::StackIndex _reserved_stack;
NativeCallStackStorage::StackIndex _committed_stack;
public:
IntervalState() : type_tag{0,0}, sidx() {}
IntervalState() : type_tag{0,0}, _reserved_stack(NativeCallStackStorage::invalid), _committed_stack(NativeCallStackStorage::invalid) {}
IntervalState(const StateType type,
const MemTag mt,
const NativeCallStackStorage::StackIndex res_stack,
const NativeCallStackStorage::StackIndex com_stack) {
assert(!(type == StateType::Released) || mt == mtNone, "Released state-type must have memory tag mtNone");
type_tag[0] = static_cast<uint8_t>(type);
type_tag[1] = static_cast<uint8_t>(mt);
_reserved_stack = res_stack;
_committed_stack = com_stack;
}
IntervalState(const StateType type, const RegionData data) {
assert(!(type == StateType::Released) || data.mem_tag == mtNone, "Released state-type must have memory tag mtNone");
type_tag[0] = static_cast<uint8_t>(type);
type_tag[1] = static_cast<uint8_t>(data.mem_tag);
sidx = data.stack_idx;
_reserved_stack = data.stack_idx;
_committed_stack = NativeCallStackStorage::invalid;
}
StateType type() const {
@ -110,16 +123,50 @@ private:
return static_cast<MemTag>(type_tag[1]);
}
RegionData regiondata() const {
return RegionData{sidx, mem_tag()};
RegionData reserved_regiondata() const {
return RegionData{_reserved_stack, mem_tag()};
}
RegionData committed_regiondata() const {
return RegionData{_committed_stack, mem_tag()};
}
void set_tag(MemTag tag) {
type_tag[1] = static_cast<uint8_t>(tag);
}
NativeCallStackStorage::StackIndex stack() const {
return sidx;
NativeCallStackStorage::StackIndex reserved_stack() const {
return _reserved_stack;
}
NativeCallStackStorage::StackIndex committed_stack() const {
return _committed_stack;
}
void set_reserve_stack(NativeCallStackStorage::StackIndex idx) {
_reserved_stack = idx;
}
void set_commit_stack(NativeCallStackStorage::StackIndex idx) {
_committed_stack = idx;
}
bool has_reserved_stack() {
return _reserved_stack != NativeCallStackStorage::invalid;
}
bool has_committed_stack() {
return _committed_stack != NativeCallStackStorage::invalid;
}
void set_type(StateType t) {
type_tag[0] = static_cast<uint8_t>(t);
}
bool equals(const IntervalState& other) const {
return mem_tag() == other.mem_tag() &&
type() == other.type() &&
reserved_stack() == other.reserved_stack() &&
committed_stack() == other.committed_stack();
}
};
@ -130,8 +177,14 @@ private:
IntervalState out;
bool is_noop() {
if (in.type() == StateType::Released &&
in.type() == out.type() &&
in.mem_tag() == out.mem_tag()) {
return true;
}
return in.type() == out.type() &&
RegionData::equals(in.regiondata(), out.regiondata());
RegionData::equals(in.reserved_regiondata(), out.reserved_regiondata()) &&
RegionData::equals(in.committed_regiondata(), out.committed_regiondata());
}
};
@ -193,8 +246,44 @@ public:
#endif
};
enum Operation {Release, Reserve, Commit, Uncommit};
struct RequestInfo {
position A, B;
StateType _op;
MemTag tag;
SIndex callstack;
bool use_tag_inplace;
Operation op() const {
return
_op == StateType::Reserved && !use_tag_inplace ? Operation::Reserve :
_op == StateType::Committed ? Operation::Commit :
_op == StateType::Reserved && use_tag_inplace ? Operation::Uncommit :
Operation::Release;
}
int op_to_index() const {
return
_op == StateType::Reserved && !use_tag_inplace ? 1 :
_op == StateType::Committed ? 2 :
_op == StateType::Reserved && use_tag_inplace ? 3 :
0;
}
};
private:
SummaryDiff register_mapping(position A, position B, StateType state, const RegionData& metadata, bool use_tag_inplace = false);
StateType get_new_state(const StateType existinting_state, const RequestInfo& req) const;
MemTag get_new_tag(const MemTag existinting_tag, const RequestInfo& req) const;
SIndex get_new_reserve_callstack(const SIndex existinting_stack, const StateType ex, const RequestInfo& req) const;
SIndex get_new_commit_callstack(const SIndex existinting_stack, const StateType ex, const RequestInfo& req) const;
void compute_summary_diff(const SingleDiff::delta region_size, const MemTag t1, const StateType& ex, const RequestInfo& req, const MemTag new_tag, SummaryDiff& diff) const;
void update_region(TreapNode* n1, TreapNode* n2, const RequestInfo& req, SummaryDiff& diff);
int state_to_index(const StateType st) const {
return
st == StateType::Released ? 0 :
st == StateType::Reserved ? 1 :
st == StateType::Committed ? 2 : -1;
}
public:
SummaryDiff reserve_mapping(position from, size size, const RegionData& metadata) {

View File

@ -22,8 +22,11 @@
*
*/
#include "memory/resourceArea.hpp"
#include "cds/cdsConfig.hpp"
#include "oops/fieldInfo.inline.hpp"
#include "runtime/atomic.hpp"
#include "utilities/packedTable.hpp"
void FieldInfo::print(outputStream* os, ConstantPool* cp) {
os->print_cr("index=%d name_index=%d name=%s signature_index=%d signature=%s offset=%d "
@ -37,8 +40,10 @@ void FieldInfo::print(outputStream* os, ConstantPool* cp) {
field_flags().as_uint(),
initializer_index(),
generic_signature_index(),
_field_flags.is_injected() ? lookup_symbol(generic_signature_index())->as_utf8() : cp->symbol_at(generic_signature_index())->as_utf8(),
contended_group());
_field_flags.is_generic() ? (_field_flags.is_injected() ?
lookup_symbol(generic_signature_index())->as_utf8() : cp->symbol_at(generic_signature_index())->as_utf8()
) : "",
is_contended() ? contended_group() : 0);
}
void FieldInfo::print_from_growable_array(outputStream* os, GrowableArray<FieldInfo>* array, ConstantPool* cp) {
@ -62,13 +67,17 @@ Array<u1>* FieldInfoStream::create_FieldInfoStream(GrowableArray<FieldInfo>* fie
StreamSizer s;
StreamFieldSizer sizer(&s);
assert(fields->length() == java_fields + injected_fields, "must be");
sizer.consumer()->accept_uint(java_fields);
sizer.consumer()->accept_uint(injected_fields);
for (int i = 0; i < fields->length(); i++) {
FieldInfo* fi = fields->adr_at(i);
sizer.map_field_info(*fi);
}
int storage_size = sizer.consumer()->position() + 1;
// Originally there was an extra byte with 0 terminating the reading;
// now we check limits instead.
int storage_size = sizer.consumer()->position();
Array<u1>* const fis = MetadataFactory::new_array<u1>(loader_data, storage_size, CHECK_NULL);
using StreamWriter = UNSIGNED5::Writer<Array<u1>*, int, ArrayHelper<Array<u1>*, int>>;
@ -79,15 +88,14 @@ Array<u1>* FieldInfoStream::create_FieldInfoStream(GrowableArray<FieldInfo>* fie
writer.consumer()->accept_uint(java_fields);
writer.consumer()->accept_uint(injected_fields);
for (int i = 0; i < fields->length(); i++) {
FieldInfo* fi = fields->adr_at(i);
writer.map_field_info(*fi);
writer.map_field_info(fields->at(i));
}
#ifdef ASSERT
FieldInfoReader r(fis);
int jfc = r.next_uint();
int jfc, ifc;
r.read_field_counts(&jfc, &ifc);
assert(jfc == java_fields, "Must be");
int ifc = r.next_uint();
assert(ifc == injected_fields, "Must be");
for (int i = 0; i < jfc + ifc; i++) {
FieldInfo fi;
@ -113,30 +121,221 @@ Array<u1>* FieldInfoStream::create_FieldInfoStream(GrowableArray<FieldInfo>* fie
return fis;
}
GrowableArray<FieldInfo>* FieldInfoStream::create_FieldInfoArray(const Array<u1>* fis, int* java_fields_count, int* injected_fields_count) {
int length = FieldInfoStream::num_total_fields(fis);
GrowableArray<FieldInfo>* array = new GrowableArray<FieldInfo>(length);
int FieldInfoStream::compare_name_and_sig(const Symbol* n1, const Symbol* s1, const Symbol* n2, const Symbol* s2) {
int cmp = n1->fast_compare(n2);
return cmp != 0 ? cmp : s1->fast_compare(s2);
}
// We use both name and signature during the comparison; while JLS require unique
// names for fields, JVMS requires only unique name + signature combination.
struct field_pos {
Symbol* _name;
Symbol* _signature;
int _index;
int _position;
};
class FieldInfoSupplier: public PackedTableBuilder::Supplier {
const field_pos* _positions;
size_t _elements;
public:
FieldInfoSupplier(const field_pos* positions, size_t elements): _positions(positions), _elements(elements) {}
bool next(uint32_t* key, uint32_t* value) override {
if (_elements == 0) {
return false;
}
*key = _positions->_position;
*value = _positions->_index;
++_positions;
--_elements;
return true;
}
};
Array<u1>* FieldInfoStream::create_search_table(ConstantPool* cp, const Array<u1>* fis, ClassLoaderData* loader_data, TRAPS) {
if (CDSConfig::is_dumping_dynamic_archive()) {
// We cannot use search table; in case of dynamic archives it should be sorted by "requested" addresses,
// but Symbol* addresses are coming from _constants, which has "buffered" addresses.
// For background, see new comments inside allocate_node_impl in symbolTable.cpp
return nullptr;
}
FieldInfoReader r(fis);
*java_fields_count = r.next_uint();
*injected_fields_count = r.next_uint();
int java_fields;
int injected_fields;
r.read_field_counts(&java_fields, &injected_fields);
assert(java_fields >= 0, "must be");
if (java_fields == 0 || fis->length() == 0 || static_cast<uint>(java_fields) < BinarySearchThreshold) {
return nullptr;
}
ResourceMark rm;
field_pos* positions = NEW_RESOURCE_ARRAY(field_pos, java_fields);
for (int i = 0; i < java_fields; ++i) {
assert(r.has_next(), "number of fields must match");
positions[i]._position = r.position();
FieldInfo fi;
r.read_field_info(fi);
positions[i]._name = fi.name(cp);
positions[i]._signature = fi.signature(cp);
positions[i]._index = i;
}
auto compare_pair = [](const void* v1, const void* v2) {
const field_pos* p1 = reinterpret_cast<const field_pos*>(v1);
const field_pos* p2 = reinterpret_cast<const field_pos*>(v2);
return compare_name_and_sig(p1->_name, p1->_signature, p2->_name, p2->_signature);
};
qsort(positions, java_fields, sizeof(field_pos), compare_pair);
PackedTableBuilder builder(fis->length() - 1, java_fields - 1);
Array<u1>* table = MetadataFactory::new_array<u1>(loader_data, java_fields * builder.element_bytes(), CHECK_NULL);
FieldInfoSupplier supplier(positions, java_fields);
builder.fill(table->data(), static_cast<size_t>(table->length()), supplier);
return table;
}
GrowableArray<FieldInfo>* FieldInfoStream::create_FieldInfoArray(const Array<u1>* fis, int* java_fields_count, int* injected_fields_count) {
FieldInfoReader r(fis);
r.read_field_counts(java_fields_count, injected_fields_count);
int length = *java_fields_count + *injected_fields_count;
GrowableArray<FieldInfo>* array = new GrowableArray<FieldInfo>(length);
while (r.has_next()) {
FieldInfo fi;
r.read_field_info(fi);
array->append(fi);
}
assert(array->length() == length, "Must be");
assert(array->length() == *java_fields_count + *injected_fields_count, "Must be");
return array;
}
void FieldInfoStream::print_from_fieldinfo_stream(Array<u1>* fis, outputStream* os, ConstantPool* cp) {
int length = FieldInfoStream::num_total_fields(fis);
FieldInfoReader r(fis);
int java_field_count = r.next_uint();
int injected_fields_count = r.next_uint();
int java_fields_count;
int injected_fields_count;
r.read_field_counts(&java_fields_count, &injected_fields_count);
while (r.has_next()) {
FieldInfo fi;
r.read_field_info(fi);
fi.print(os, cp);
}
}
class FieldInfoComparator: public PackedTableLookup::Comparator {
const FieldInfoReader* _reader;
ConstantPool* _cp;
const Symbol* _name;
const Symbol* _signature;
public:
FieldInfoComparator(const FieldInfoReader* reader, ConstantPool* cp, const Symbol* name, const Symbol* signature):
_reader(reader), _cp(cp), _name(name), _signature(signature) {}
int compare_to(uint32_t position) override {
FieldInfoReader r2(*_reader);
r2.set_position_and_next_index(position, -1);
u2 name_index, sig_index;
r2.read_name_and_signature(&name_index, &sig_index);
Symbol* mid_name = _cp->symbol_at(name_index);
Symbol* mid_sig = _cp->symbol_at(sig_index);
return FieldInfoStream::compare_name_and_sig(_name, _signature, mid_name, mid_sig);
}
#ifdef ASSERT
void reset(uint32_t position) override {
FieldInfoReader r2(*_reader);
r2.set_position_and_next_index(position, -1);
u2 name_index, signature_index;
r2.read_name_and_signature(&name_index, &signature_index);
_name = _cp->symbol_at(name_index);
_signature = _cp->symbol_at(signature_index);
}
#endif // ASSERT
};
#ifdef ASSERT
void FieldInfoStream::validate_search_table(ConstantPool* cp, const Array<u1>* fis, const Array<u1>* search_table) {
if (search_table == nullptr) {
return;
}
FieldInfoReader reader(fis);
int java_fields, injected_fields;
reader.read_field_counts(&java_fields, &injected_fields);
assert(java_fields > 0, "must be");
PackedTableLookup lookup(fis->length() - 1, java_fields - 1, search_table);
assert(lookup.element_bytes() * java_fields == static_cast<unsigned int>(search_table->length()), "size does not match");
FieldInfoComparator comparator(&reader, cp, nullptr, nullptr);
// Check 1: assert that elements have the correct order based on the comparison function
lookup.validate_order(comparator);
// Check 2: Iterate through the original stream (not just search_table) and try if lookup works as expected
reader.set_position_and_next_index(0, 0);
reader.read_field_counts(&java_fields, &injected_fields);
while (reader.has_next()) {
int field_start = reader.position();
FieldInfo fi;
reader.read_field_info(fi);
if (fi.field_flags().is_injected()) {
// checking only java fields that precede injected ones
break;
}
FieldInfoReader r2(fis);
int index = r2.search_table_lookup(search_table, fi.name(cp), fi.signature(cp), cp, java_fields);
assert(index == static_cast<int>(fi.index()), "wrong index: %d != %u", index, fi.index());
assert(index == r2.next_index(), "index should match");
assert(field_start == r2.position(), "must find the same position");
}
}
#endif // ASSERT
void FieldInfoStream::print_search_table(outputStream* st, ConstantPool* cp, const Array<u1>* fis, const Array<u1>* search_table) {
if (search_table == nullptr) {
return;
}
FieldInfoReader reader(fis);
int java_fields, injected_fields;
reader.read_field_counts(&java_fields, &injected_fields);
assert(java_fields > 0, "must be");
PackedTableLookup lookup(fis->length() - 1, java_fields - 1, search_table);
auto printer = [&] (size_t offset, uint32_t position, uint32_t index) {
reader.set_position_and_next_index(position, -1);
u2 name_index, sig_index;
reader.read_name_and_signature(&name_index, &sig_index);
Symbol* name = cp->symbol_at(name_index);
Symbol* sig = cp->symbol_at(sig_index);
st->print(" [%zu] #%d,#%d = ", offset, name_index, sig_index);
name->print_symbol_on(st);
st->print(":");
sig->print_symbol_on(st);
st->print(" @ %p,%p", name, sig);
st->cr();
};
lookup.iterate(printer);
}
int FieldInfoReader::search_table_lookup(const Array<u1>* search_table, const Symbol* name, const Symbol* signature, ConstantPool* cp, int java_fields) {
assert(java_fields >= 0, "must be");
if (java_fields == 0) {
return -1;
}
FieldInfoComparator comp(this, cp, name, signature);
PackedTableLookup lookup(_r.limit() - 1, java_fields - 1, search_table);
uint32_t position;
static_assert(sizeof(uint32_t) == sizeof(_next_index), "field size assert");
if (lookup.search(comp, &position, reinterpret_cast<uint32_t*>(&_next_index))) {
_r.set_position(static_cast<int>(position));
return _next_index;
} else {
return -1;
}
}

View File

@ -222,29 +222,28 @@ public:
void map_field_info(const FieldInfo& fi);
};
// Gadget for decoding and reading the stream of field records.
class FieldInfoReader {
friend class FieldInfoStream;
friend class ClassFileParser;
friend class FieldStreamBase;
friend class FieldInfo;
UNSIGNED5::Reader<const u1*, int> _r;
int _next_index;
public:
public:
FieldInfoReader(const Array<u1>* fi);
private:
uint32_t next_uint() { return _r.next_uint(); }
private:
inline uint32_t next_uint() { return _r.next_uint(); }
void skip(int n) { int s = _r.try_skip(n); assert(s == n,""); }
public:
int has_next() { return _r.has_next(); }
int position() { return _r.position(); }
int next_index() { return _next_index; }
void read_field_counts(int* java_fields, int* injected_fields);
int has_next() const { return _r.position() < _r.limit(); }
int position() const { return _r.position(); }
int next_index() const { return _next_index; }
void read_name_and_signature(u2* name_index, u2* signature_index);
void read_field_info(FieldInfo& fi);
int search_table_lookup(const Array<u1>* search_table, const Symbol* name, const Symbol* signature, ConstantPool* cp, int java_fields);
// skip a whole field record, both required and optional bits
FieldInfoReader& skip_field_info();
@ -271,6 +270,11 @@ class FieldInfoStream : AllStatic {
friend class JavaFieldStream;
friend class FieldStreamBase;
friend class ClassFileParser;
friend class FieldInfoReader;
friend class FieldInfoComparator;
private:
static int compare_name_and_sig(const Symbol* n1, const Symbol* s1, const Symbol* n2, const Symbol* s2);
public:
static int num_java_fields(const Array<u1>* fis);
@ -278,9 +282,14 @@ class FieldInfoStream : AllStatic {
static int num_total_fields(const Array<u1>* fis);
static Array<u1>* create_FieldInfoStream(GrowableArray<FieldInfo>* fields, int java_fields, int injected_fields,
ClassLoaderData* loader_data, TRAPS);
ClassLoaderData* loader_data, TRAPS);
static Array<u1>* create_search_table(ConstantPool* cp, const Array<u1>* fis, ClassLoaderData* loader_data, TRAPS);
static GrowableArray<FieldInfo>* create_FieldInfoArray(const Array<u1>* fis, int* java_fields_count, int* injected_fields_count);
static void print_from_fieldinfo_stream(Array<u1>* fis, outputStream* os, ConstantPool* cp);
DEBUG_ONLY(static void validate_search_table(ConstantPool* cp, const Array<u1>* fis, const Array<u1>* search_table);)
static void print_search_table(outputStream* st, ConstantPool* cp, const Array<u1>* fis, const Array<u1>* search_table);
};
class FieldStatus {

View File

@ -56,16 +56,27 @@ inline Symbol* FieldInfo::lookup_symbol(int symbol_index) const {
inline int FieldInfoStream::num_injected_java_fields(const Array<u1>* fis) {
FieldInfoReader fir(fis);
fir.skip(1);
return fir.next_uint();
int java_fields_count;
int injected_fields_count;
fir.read_field_counts(&java_fields_count, &injected_fields_count);
return injected_fields_count;
}
inline int FieldInfoStream::num_total_fields(const Array<u1>* fis) {
FieldInfoReader fir(fis);
return fir.next_uint() + fir.next_uint();
int java_fields_count;
int injected_fields_count;
fir.read_field_counts(&java_fields_count, &injected_fields_count);
return java_fields_count + injected_fields_count;
}
inline int FieldInfoStream::num_java_fields(const Array<u1>* fis) { return FieldInfoReader(fis).next_uint(); }
inline int FieldInfoStream::num_java_fields(const Array<u1>* fis) {
FieldInfoReader fir(fis);
int java_fields_count;
int injected_fields_count;
fir.read_field_counts(&java_fields_count, &injected_fields_count);
return java_fields_count;
}
template<typename CON>
inline void Mapper<CON>::map_field_info(const FieldInfo& fi) {
@ -94,13 +105,22 @@ inline void Mapper<CON>::map_field_info(const FieldInfo& fi) {
inline FieldInfoReader::FieldInfoReader(const Array<u1>* fi)
: _r(fi->data(), 0),
: _r(fi->data(), fi->length()),
_next_index(0) { }
inline void FieldInfoReader::read_field_counts(int* java_fields, int* injected_fields) {
*java_fields = next_uint();
*injected_fields = next_uint();
}
inline void FieldInfoReader::read_name_and_signature(u2* name_index, u2* signature_index) {
*name_index = checked_cast<u2>(next_uint());
*signature_index = checked_cast<u2>(next_uint());
}
inline void FieldInfoReader::read_field_info(FieldInfo& fi) {
fi._index = _next_index++;
fi._name_index = checked_cast<u2>(next_uint());
fi._signature_index = checked_cast<u2>(next_uint());
read_name_and_signature(&fi._name_index, &fi._signature_index);
fi._offset = next_uint();
fi._access_flags = AccessFlags(checked_cast<u2>(next_uint()));
fi._field_flags = FieldInfo::FieldFlags(next_uint());

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -56,17 +56,23 @@ class FieldStreamBase : public StackObj {
inline FieldStreamBase(const Array<u1>* fieldinfo_stream, ConstantPool* constants, int start, int limit);
inline FieldStreamBase(Array<u1>* fieldinfo_stream, ConstantPool* constants);
inline FieldStreamBase(const Array<u1>* fieldinfo_stream, ConstantPool* constants);
private:
private:
void initialize() {
int java_fields_count = _reader.next_uint();
int injected_fields_count = _reader.next_uint();
assert( _limit <= java_fields_count + injected_fields_count, "Safety check");
int java_fields_count;
int injected_fields_count;
_reader.read_field_counts(&java_fields_count, &injected_fields_count);
if (_limit < _index) {
_limit = java_fields_count + injected_fields_count;
} else {
assert( _limit <= java_fields_count + injected_fields_count, "Safety check");
}
if (_limit != 0) {
_reader.read_field_info(_fi_buf);
}
}
public:
inline FieldStreamBase(InstanceKlass* klass);
@ -138,8 +144,11 @@ class FieldStreamBase : public StackObj {
// Iterate over only the Java fields
class JavaFieldStream : public FieldStreamBase {
Array<u1>* _search_table;
public:
JavaFieldStream(const InstanceKlass* k): FieldStreamBase(k->fieldinfo_stream(), k->constants(), 0, k->java_fields_count()) {}
JavaFieldStream(const InstanceKlass* k): FieldStreamBase(k->fieldinfo_stream(), k->constants(), 0, k->java_fields_count()),
_search_table(k->fieldinfo_search_table()) {}
u2 name_index() const {
assert(!field()->field_flags().is_injected(), "regular only");
@ -149,7 +158,6 @@ class JavaFieldStream : public FieldStreamBase {
u2 signature_index() const {
assert(!field()->field_flags().is_injected(), "regular only");
return field()->signature_index();
return -1;
}
u2 generic_signature_index() const {
@ -164,6 +172,10 @@ class JavaFieldStream : public FieldStreamBase {
assert(!field()->field_flags().is_injected(), "regular only");
return field()->initializer_index();
}
// Performs either a linear search or binary search through the stream
// looking for a matching name/signature combo
bool lookup(const Symbol* name, const Symbol* signature);
};
@ -176,7 +188,6 @@ class InternalFieldStream : public FieldStreamBase {
class AllFieldStream : public FieldStreamBase {
public:
AllFieldStream(Array<u1>* fieldinfo, ConstantPool* constants): FieldStreamBase(fieldinfo, constants) {}
AllFieldStream(const InstanceKlass* k): FieldStreamBase(k->fieldinfo_stream(), k->constants()) {}
};

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -33,22 +33,18 @@
FieldStreamBase::FieldStreamBase(const Array<u1>* fieldinfo_stream, ConstantPool* constants, int start, int limit) :
_fieldinfo_stream(fieldinfo_stream),
_reader(FieldInfoReader(_fieldinfo_stream)),
_constants(constantPoolHandle(Thread::current(), constants)), _index(start) {
_index = start;
if (limit < start) {
_limit = FieldInfoStream::num_total_fields(_fieldinfo_stream);
} else {
_limit = limit;
}
_constants(constantPoolHandle(Thread::current(), constants)),
_index(start),
_limit(limit) {
initialize();
}
FieldStreamBase::FieldStreamBase(Array<u1>* fieldinfo_stream, ConstantPool* constants) :
FieldStreamBase::FieldStreamBase(const Array<u1>* fieldinfo_stream, ConstantPool* constants) :
_fieldinfo_stream(fieldinfo_stream),
_reader(FieldInfoReader(_fieldinfo_stream)),
_constants(constantPoolHandle(Thread::current(), constants)),
_index(0),
_limit(FieldInfoStream::num_total_fields(_fieldinfo_stream)) {
_limit(-1) {
initialize();
}
@ -57,9 +53,28 @@ FieldStreamBase::FieldStreamBase(InstanceKlass* klass) :
_reader(FieldInfoReader(_fieldinfo_stream)),
_constants(constantPoolHandle(Thread::current(), klass->constants())),
_index(0),
_limit(FieldInfoStream::num_total_fields(_fieldinfo_stream)) {
_limit(-1) {
assert(klass == field_holder(), "");
initialize();
}
inline bool JavaFieldStream::lookup(const Symbol* name, const Symbol* signature) {
if (_search_table != nullptr) {
int index = _reader.search_table_lookup(_search_table, name, signature, _constants(), _limit);
if (index >= 0) {
assert(index < _limit, "must be");
_index = index;
_reader.read_field_info(_fi_buf);
return true;
}
} else {
for (; !done(); next()) {
if (this->name() == name && this->signature() == signature) {
return true;
}
}
}
return false;
}
#endif // SHARE_OOPS_FIELDSTREAMS_INLINE_HPP

View File

@ -686,6 +686,11 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) {
}
set_fieldinfo_stream(nullptr);
if (fieldinfo_search_table() != nullptr && !fieldinfo_search_table()->is_shared()) {
MetadataFactory::free_array<u1>(loader_data, fieldinfo_search_table());
}
set_fieldinfo_search_table(nullptr);
if (fields_status() != nullptr && !fields_status()->is_shared()) {
MetadataFactory::free_array<FieldStatus>(loader_data, fields_status());
}
@ -1786,13 +1791,12 @@ FieldInfo InstanceKlass::field(int index) const {
}
bool InstanceKlass::find_local_field(Symbol* name, Symbol* sig, fieldDescriptor* fd) const {
for (JavaFieldStream fs(this); !fs.done(); fs.next()) {
Symbol* f_name = fs.name();
Symbol* f_sig = fs.signature();
if (f_name == name && f_sig == sig) {
fd->reinitialize(const_cast<InstanceKlass*>(this), fs.to_FieldInfo());
return true;
}
JavaFieldStream fs(this);
if (fs.lookup(name, sig)) {
assert(fs.name() == name, "name must match");
assert(fs.signature() == sig, "signature must match");
fd->reinitialize(const_cast<InstanceKlass*>(this), fs.to_FieldInfo());
return true;
}
return false;
}
@ -2610,6 +2614,7 @@ void InstanceKlass::metaspace_pointers_do(MetaspaceClosure* it) {
}
it->push(&_fieldinfo_stream);
it->push(&_fieldinfo_search_table);
// _fields_status might be written into by Rewriter::scan_method() -> fd.set_has_initialized_final_update()
it->push(&_fields_status, MetaspaceClosure::_writable);
@ -2710,6 +2715,8 @@ void InstanceKlass::remove_unshareable_info() {
DEBUG_ONLY(_shared_class_load_count = 0);
remove_unshareable_flags();
DEBUG_ONLY(FieldInfoStream::validate_search_table(_constants, _fieldinfo_stream, _fieldinfo_search_table));
}
void InstanceKlass::remove_unshareable_flags() {
@ -2816,6 +2823,8 @@ void InstanceKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handl
if (DiagnoseSyncOnValueBasedClasses && has_value_based_class_annotation() && !is_value_based()) {
set_is_value_based();
}
DEBUG_ONLY(FieldInfoStream::validate_search_table(_constants, _fieldinfo_stream, _fieldinfo_search_table));
}
// Check if a class or any of its supertypes has a version older than 50.
@ -3760,6 +3769,11 @@ void InstanceKlass::print_on(outputStream* st) const {
map++;
}
st->cr();
if (fieldinfo_search_table() != nullptr) {
st->print_cr(BULLET"---- field info search table:");
FieldInfoStream::print_search_table(st, _constants, _fieldinfo_stream, _fieldinfo_search_table);
}
}
void InstanceKlass::print_value_on(outputStream* st) const {

View File

@ -276,6 +276,7 @@ class InstanceKlass: public Klass {
// Fields information is stored in an UNSIGNED5 encoded stream (see fieldInfo.hpp)
Array<u1>* _fieldinfo_stream;
Array<u1>* _fieldinfo_search_table;
Array<FieldStatus>* _fields_status;
// embedded Java vtable follows here
@ -398,6 +399,9 @@ class InstanceKlass: public Klass {
Array<u1>* fieldinfo_stream() const { return _fieldinfo_stream; }
void set_fieldinfo_stream(Array<u1>* fis) { _fieldinfo_stream = fis; }
Array<u1>* fieldinfo_search_table() const { return _fieldinfo_search_table; }
void set_fieldinfo_search_table(Array<u1>* table) { _fieldinfo_search_table = table; }
Array<FieldStatus>* fields_status() const {return _fields_status; }
void set_fields_status(Array<FieldStatus>* array) { _fields_status = array; }

View File

@ -44,7 +44,7 @@
#include "runtime/mutexLocker.hpp"
#include "utilities/macros.hpp"
ObjArrayKlass* ObjArrayKlass::allocate(ClassLoaderData* loader_data, int n, Klass* k, Symbol* name, TRAPS) {
ObjArrayKlass* ObjArrayKlass::allocate_klass(ClassLoaderData* loader_data, int n, Klass* k, Symbol* name, TRAPS) {
assert(ObjArrayKlass::header_size() <= InstanceKlass::header_size(),
"array klasses must be same size as InstanceKlass");
@ -100,7 +100,7 @@ ObjArrayKlass* ObjArrayKlass::allocate_objArray_klass(ClassLoaderData* loader_da
}
// Initialize instance variables
ObjArrayKlass* oak = ObjArrayKlass::allocate(loader_data, n, element_klass, name, CHECK_NULL);
ObjArrayKlass* oak = ObjArrayKlass::allocate_klass(loader_data, n, element_klass, name, CHECK_NULL);
ModuleEntry* module = oak->module();
assert(module != nullptr, "No module entry for array");
@ -149,7 +149,7 @@ size_t ObjArrayKlass::oop_size(oop obj) const {
return objArrayOop(obj)->object_size();
}
objArrayOop ObjArrayKlass::allocate(int length, TRAPS) {
objArrayOop ObjArrayKlass::allocate_instance(int length, TRAPS) {
check_array_allocation_length(length, arrayOopDesc::max_array_length(T_OBJECT), CHECK_NULL);
size_t size = objArrayOopDesc::object_size(length);
return (objArrayOop)Universe::heap()->array_allocate(this, size, length,
@ -160,7 +160,7 @@ oop ObjArrayKlass::multi_allocate(int rank, jint* sizes, TRAPS) {
int length = *sizes;
ArrayKlass* ld_klass = lower_dimension();
// If length < 0 allocate will throw an exception.
objArrayOop array = allocate(length, CHECK_NULL);
objArrayOop array = allocate_instance(length, CHECK_NULL);
objArrayHandle h_array (THREAD, array);
if (rank > 1) {
if (length != 0) {

View File

@ -33,8 +33,10 @@ class ClassLoaderData;
// ObjArrayKlass is the klass for objArrays
class ObjArrayKlass : public ArrayKlass {
friend class VMStructs;
friend class Deoptimization;
friend class JVMCIVMStructs;
friend class oopFactory;
friend class VMStructs;
public:
static const KlassKind Kind = ObjArrayKlassKind;
@ -47,7 +49,9 @@ class ObjArrayKlass : public ArrayKlass {
// Constructor
ObjArrayKlass(int n, Klass* element_klass, Symbol* name);
static ObjArrayKlass* allocate(ClassLoaderData* loader_data, int n, Klass* k, Symbol* name, TRAPS);
static ObjArrayKlass* allocate_klass(ClassLoaderData* loader_data, int n, Klass* k, Symbol* name, TRAPS);
objArrayOop allocate_instance(int length, TRAPS);
public:
// For dummy objects
ObjArrayKlass() {}
@ -78,7 +82,6 @@ class ObjArrayKlass : public ArrayKlass {
static ObjArrayKlass* allocate_objArray_klass(ClassLoaderData* loader_data,
int n, Klass* element_klass, TRAPS);
objArrayOop allocate(int length, TRAPS);
oop multi_allocate(int rank, jint* sizes, TRAPS);
// Copying

View File

@ -50,7 +50,7 @@ TypeArrayKlass* TypeArrayKlass::create_klass(BasicType type,
ClassLoaderData* null_loader_data = ClassLoaderData::the_null_class_loader_data();
TypeArrayKlass* ak = TypeArrayKlass::allocate(null_loader_data, type, sym, CHECK_NULL);
TypeArrayKlass* ak = TypeArrayKlass::allocate_klass(null_loader_data, type, sym, CHECK_NULL);
// Call complete_create_array_klass after all instance variables have been initialized.
complete_create_array_klass(ak, ak->super(), ModuleEntryTable::javabase_moduleEntry(), CHECK_NULL);
@ -65,7 +65,7 @@ TypeArrayKlass* TypeArrayKlass::create_klass(BasicType type,
return ak;
}
TypeArrayKlass* TypeArrayKlass::allocate(ClassLoaderData* loader_data, BasicType type, Symbol* name, TRAPS) {
TypeArrayKlass* TypeArrayKlass::allocate_klass(ClassLoaderData* loader_data, BasicType type, Symbol* name, TRAPS) {
assert(TypeArrayKlass::header_size() <= InstanceKlass::header_size(),
"array klasses must be same size as InstanceKlass");
@ -101,7 +101,7 @@ oop TypeArrayKlass::multi_allocate(int rank, jint* last_size, TRAPS) {
// For typeArrays this is only called for the last dimension
assert(rank == 1, "just checking");
int length = *last_size;
return allocate(length, THREAD);
return allocate_instance(length, THREAD);
}

View File

@ -33,6 +33,8 @@ class ClassLoaderData;
// It contains the type and size of the elements
class TypeArrayKlass : public ArrayKlass {
friend class Deoptimization;
friend class oopFactory;
friend class VMStructs;
public:
@ -43,7 +45,10 @@ class TypeArrayKlass : public ArrayKlass {
// Constructor
TypeArrayKlass(BasicType type, Symbol* name);
static TypeArrayKlass* allocate(ClassLoaderData* loader_data, BasicType type, Symbol* name, TRAPS);
static TypeArrayKlass* allocate_klass(ClassLoaderData* loader_data, BasicType type, Symbol* name, TRAPS);
typeArrayOop allocate_common(int length, bool do_zero, TRAPS);
typeArrayOop allocate_instance(int length, TRAPS) { return allocate_common(length, true, THREAD); }
public:
TypeArrayKlass() {} // For dummy objects.
@ -66,8 +71,6 @@ class TypeArrayKlass : public ArrayKlass {
size_t oop_size(oop obj) const;
// Allocation
typeArrayOop allocate_common(int length, bool do_zero, TRAPS);
typeArrayOop allocate(int length, TRAPS) { return allocate_common(length, true, THREAD); }
oop multi_allocate(int rank, jint* sizes, TRAPS);
oop protection_domain() const { return nullptr; }

View File

@ -464,6 +464,14 @@ class PhaseCFG : public Phase {
Node* catch_cleanup_find_cloned_def(Block* use_blk, Node* def, Block* def_blk, int n_clone_idx);
void catch_cleanup_inter_block(Node *use, Block *use_blk, Node *def, Block *def_blk, int n_clone_idx);
// Ensure that n happens at b or above, i.e. at a block that dominates b.
// We expect n to be an orphan node without further inputs.
void ensure_node_is_at_block_or_above(Node* n, Block* b);
// Move node n from its current placement into the end of block b.
// Move also outgoing Mach projections.
void move_node_and_its_projections_to_block(Node* n, Block* b);
// Detect implicit-null-check opportunities. Basically, find null checks
// with suitable memory ops nearby. Use the memory op to do the null check.
// I can generate a memory op if there is not one nearby.

View File

@ -678,10 +678,12 @@
"Print progress during Iterative Global Value Numbering") \
\
develop(uint, VerifyIterativeGVN, 0, \
"Verify Iterative Global Value Numbering" \
"=XY, with Y: verify Def-Use modifications during IGVN" \
" X: verify that type(n) == n->Value() after IGVN" \
"X and Y in 0=off; 1=on") \
"Verify Iterative Global Value Numbering =DCBA, with:" \
" D: verify Node::Identity did not miss opportunities" \
" C: verify Node::Ideal did not miss opportunities" \
" B: verify that type(n) == n->Value() after IGVN" \
" A: verify Def-Use modifications during IGVN" \
"Each can be 0=off or 1=on") \
constraint(VerifyIterativeGVNConstraintFunc, AtParse) \
\
develop(bool, TraceCISCSpill, false, \

View File

@ -155,7 +155,6 @@ void IdealGraphPrinter::init(const char* file_name, bool use_multiple_files, boo
// in the mach where kill projections have no users but should
// appear in the dump.
_traverse_outs = true;
_should_send_method = true;
_output = nullptr;
buffer[0] = 0;
_depth = 0;
@ -300,13 +299,11 @@ void IdealGraphPrinter::print_inline_tree(InlineTree *tree) {
void IdealGraphPrinter::print_inlining() {
// Print inline tree
if (_should_send_method) {
InlineTree *inlineTree = C->ilt();
if (inlineTree != nullptr) {
print_inline_tree(inlineTree);
} else {
// print this method only
}
InlineTree *inlineTree = C->ilt();
if (inlineTree != nullptr) {
print_inline_tree(inlineTree);
} else {
// print this method only
}
}
@ -382,7 +379,6 @@ void IdealGraphPrinter::begin_method() {
tail(PROPERTIES_ELEMENT);
_should_send_method = true;
this->_current_method = method;
_xml->flush();
@ -975,7 +971,7 @@ void IdealGraphPrinter::print_graph(const char* name, const frame* fr) {
// Print current ideal graph
void IdealGraphPrinter::print(const char* name, Node* node, GrowableArray<const Node*>& visible_nodes, const frame* fr) {
if (!_current_method || !_should_send_method || node == nullptr) return;
if (!_current_method || node == nullptr) return;
if (name == nullptr) {
stringStream graph_name;

View File

@ -110,7 +110,6 @@ class IdealGraphPrinter : public CHeapObj<mtCompiler> {
ciMethod *_current_method;
int _depth;
char buffer[2048];
bool _should_send_method;
PhaseChaitin* _chaitin;
bool _traverse_outs;
Compile *C;

View File

@ -76,6 +76,36 @@ static bool needs_explicit_null_check_for_read(Node *val) {
return true;
}
void PhaseCFG::move_node_and_its_projections_to_block(Node* n, Block* b) {
assert(!is_CFG(n), "cannot move CFG node");
Block* old = get_block_for_node(n);
old->find_remove(n);
b->add_inst(n);
map_node_to_block(n, b);
// Check for Mach projections that also need to be moved.
for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) {
Node* out = n->fast_out(i);
if (!out->is_MachProj()) {
continue;
}
assert(!n->is_MachProj(), "nested projections are not allowed");
move_node_and_its_projections_to_block(out, b);
}
}
void PhaseCFG::ensure_node_is_at_block_or_above(Node* n, Block* b) {
assert(!is_CFG(n), "cannot move CFG node");
Block* current = get_block_for_node(n);
if (current->dominates(b)) {
return; // n is already placed above b, do nothing.
}
// We only expect nodes without further inputs, like MachTemp or load Base.
assert(n->req() == 0 || (n->req() == 1 && n->in(0) == (Node*)C->root()),
"need for recursive hoisting not expected");
assert(b->dominates(current), "precondition: can only move n to b if b dominates n");
move_node_and_its_projections_to_block(n, b);
}
//------------------------------implicit_null_check----------------------------
// Detect implicit-null-check opportunities. Basically, find null checks
// with suitable memory ops nearby. Use the memory op to do the null check.
@ -160,12 +190,14 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo
Node *m = val->out(i);
if( !m->is_Mach() ) continue;
MachNode *mach = m->as_Mach();
if (mach->barrier_data() != 0) {
if (mach->barrier_data() != 0 &&
!mach->is_late_expanded_null_check_candidate()) {
// Using memory accesses with barriers to perform implicit null checks is
// not supported. These operations might expand into multiple assembly
// instructions during code emission, including new memory accesses (e.g.
// in G1's pre-barrier), which would invalidate the implicit null
// exception table.
// only supported if these are explicit marked as emitting a candidate
// memory access instruction at their initial address. If not marked as
// such, barrier-tagged operations might expand into one or several memory
// access instructions located at arbitrary offsets from the initial
// address, which would invalidate the implicit null exception table.
continue;
}
was_store = false;
@ -321,6 +353,14 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo
// Ignore DecodeN val which could be hoisted to where needed.
if( is_decoden ) continue;
}
if (mach->in(j)->is_MachTemp()) {
assert(mach->in(j)->outcnt() == 1, "MachTemp nodes should not be shared");
// Ignore MachTemp inputs, they can be safely hoisted with the candidate.
// MachTemp nodes have no inputs themselves and are only used to reserve
// a scratch register for the implementation of the node (e.g. in
// late-expanded GC barriers).
continue;
}
// Block of memory-op input
Block *inb = get_block_for_node(mach->in(j));
Block *b = block; // Start from nul check
@ -388,38 +428,24 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo
// Hoist it up to the end of the test block together with its inputs if they exist.
for (uint i = 2; i < val->req(); i++) {
// DecodeN has 2 regular inputs + optional MachTemp or load Base inputs.
Node *temp = val->in(i);
Block *tempb = get_block_for_node(temp);
if (!tempb->dominates(block)) {
assert(block->dominates(tempb), "sanity check: temp node placement");
// We only expect nodes without further inputs, like MachTemp or load Base.
assert(temp->req() == 0 || (temp->req() == 1 && temp->in(0) == (Node*)C->root()),
"need for recursive hoisting not expected");
tempb->find_remove(temp);
block->add_inst(temp);
map_node_to_block(temp, block);
}
}
valb->find_remove(val);
block->add_inst(val);
map_node_to_block(val, block);
// DecodeN on x86 may kill flags. Check for flag-killing projections
// that also need to be hoisted.
for (DUIterator_Fast jmax, j = val->fast_outs(jmax); j < jmax; j++) {
Node* n = val->fast_out(j);
if( n->is_MachProj() ) {
get_block_for_node(n)->find_remove(n);
block->add_inst(n);
map_node_to_block(n, block);
}
// Inputs of val may already be early enough, but if not move them together with val.
ensure_node_is_at_block_or_above(val->in(i), block);
}
move_node_and_its_projections_to_block(val, block);
}
}
// Move any MachTemp inputs to the end of the test block.
for (uint i = 0; i < best->req(); i++) {
Node* n = best->in(i);
if (n == nullptr || !n->is_MachTemp()) {
continue;
}
ensure_node_is_at_block_or_above(n, block);
}
// Hoist the memory candidate up to the end of the test block.
Block *old_block = get_block_for_node(best);
old_block->find_remove(best);
block->add_inst(best);
map_node_to_block(best, block);
move_node_and_its_projections_to_block(best, block);
// Move the control dependence if it is pinned to not-null block.
// Don't change it in other cases: null or dominating control.
@ -429,17 +455,6 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo
best->set_req(0, proj->in(0)->in(0));
}
// Check for flag-killing projections that also need to be hoisted
// Should be DU safe because no edge updates.
for (DUIterator_Fast jmax, j = best->fast_outs(jmax); j < jmax; j++) {
Node* n = best->fast_out(j);
if( n->is_MachProj() ) {
get_block_for_node(n)->find_remove(n);
block->add_inst(n);
map_node_to_block(n, block);
}
}
// proj==Op_True --> ne test; proj==Op_False --> eq test.
// One of two graph shapes got matched:
// (IfTrue (If (Bool NE (CmpP ptr null))))

View File

@ -6580,6 +6580,10 @@ bool LibraryCallKit::inline_vectorizedMismatch() {
memory_phi = _gvn.transform(memory_phi);
result_phi = _gvn.transform(result_phi);
record_for_igvn(exit_block);
record_for_igvn(memory_phi);
record_for_igvn(result_phi);
set_control(exit_block);
set_all_memory(memory_phi);
set_result(result_phi);

View File

@ -1676,6 +1676,10 @@ bool PhaseIdealLoop::safe_for_if_replacement(const Node* dom) const {
// like various versions of induction variable+offset. Clone the
// computation per usage to allow it to sink out of the loop.
void PhaseIdealLoop::try_sink_out_of_loop(Node* n) {
bool is_raw_to_oop_cast = n->is_ConstraintCast() &&
n->in(1)->bottom_type()->isa_rawptr() &&
!n->bottom_type()->isa_rawptr();
if (has_ctrl(n) &&
!n->is_Phi() &&
!n->is_Bool() &&
@ -1685,7 +1689,9 @@ void PhaseIdealLoop::try_sink_out_of_loop(Node* n) {
!n->is_OpaqueNotNull() &&
!n->is_OpaqueInitializedAssertionPredicate() &&
!n->is_OpaqueTemplateAssertionPredicate() &&
!n->is_Type()) {
!is_raw_to_oop_cast && // don't extend live ranges of raw oops
(KillPathsReachableByDeadTypeNode || !n->is_Type())
) {
Node *n_ctrl = get_ctrl(n);
IdealLoopTree *n_loop = get_loop(n_ctrl);

View File

@ -386,6 +386,13 @@ public:
// Returns true if this node is a check that can be implemented with a trap.
virtual bool is_TrapBasedCheckNode() const { return false; }
// Whether this node is expanded during code emission into a sequence of
// instructions and the first instruction can perform an implicit null check.
virtual bool is_late_expanded_null_check_candidate() const {
return false;
}
void set_removed() { add_flag(Flag_is_removed_by_peephole); }
bool get_removed() { return (flags() & Flag_is_removed_by_peephole) != 0; }

View File

@ -2407,7 +2407,6 @@ void PhaseMacroExpand::eliminate_macro_nodes() {
}
}
// Next, attempt to eliminate allocations
_has_locks = false;
progress = true;
while (progress) {
progress = false;
@ -2431,7 +2430,6 @@ void PhaseMacroExpand::eliminate_macro_nodes() {
case Node::Class_Lock:
case Node::Class_Unlock:
assert(!n->as_AbstractLock()->is_eliminated(), "sanity");
_has_locks = true;
break;
case Node::Class_ArrayCopy:
break;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -83,9 +83,6 @@ private:
// projections extracted from a call node
CallProjections _callprojs;
// Additional data collected during macro expansion
bool _has_locks;
void expand_allocate(AllocateNode *alloc);
void expand_allocate_array(AllocateArrayNode *alloc);
void expand_allocate_common(AllocateNode* alloc,
@ -199,7 +196,7 @@ private:
Node* make_arraycopy_load(ArrayCopyNode* ac, intptr_t offset, Node* ctl, Node* mem, BasicType ft, const Type *ftype, AllocateNode *alloc);
public:
PhaseMacroExpand(PhaseIterGVN &igvn) : Phase(Macro_Expand), _igvn(igvn), _has_locks(false) {
PhaseMacroExpand(PhaseIterGVN &igvn) : Phase(Macro_Expand), _igvn(igvn) {
_igvn.set_delay_transform(true);
}

View File

@ -2015,8 +2015,10 @@ void PhaseOutput::FillExceptionTables(uint cnt, uint *call_returns, uint *inct_s
// Handle implicit null exception table updates
if (n->is_MachNullCheck()) {
assert(n->in(1)->as_Mach()->barrier_data() == 0,
"Implicit null checks on memory accesses with barriers are not yet supported");
MachNode* access = n->in(1)->as_Mach();
assert(access->barrier_data() == 0 ||
access->is_late_expanded_null_check_candidate(),
"Implicit null checks on memory accesses with barriers are only supported on nodes explicitly marked as null-check candidates");
uint block_num = block->non_connector_successor(0)->_pre_order;
_inc_table.append(inct_starts[inct_cnt++], blk_labels[block_num].loc_pos());
continue;

View File

@ -1072,7 +1072,11 @@ void PhaseIterGVN::optimize() {
#ifdef ASSERT
void PhaseIterGVN::verify_optimize() {
if (is_verify_Value()) {
assert(_worklist.size() == 0, "igvn worklist must be empty before verify");
if (is_verify_Value() ||
is_verify_Ideal() ||
is_verify_Identity()) {
ResourceMark rm;
Unique_Node_List worklist;
bool failure = false;
@ -1080,7 +1084,10 @@ void PhaseIterGVN::verify_optimize() {
worklist.push(C->root());
for (uint j = 0; j < worklist.size(); ++j) {
Node* n = worklist.at(j);
failure |= verify_node_value(n);
if (is_verify_Value()) { failure |= verify_Value_for(n); }
if (is_verify_Ideal()) { failure |= verify_Ideal_for(n, false); }
if (is_verify_Ideal()) { failure |= verify_Ideal_for(n, true); }
if (is_verify_Identity()) { failure |= verify_Identity_for(n); }
// traverse all inputs and outputs
for (uint i = 0; i < n->req(); i++) {
if (n->in(i) != nullptr) {
@ -1097,6 +1104,27 @@ void PhaseIterGVN::verify_optimize() {
// in the verification code above if that is not possible for some reason (like Load nodes).
assert(!failure, "Missed optimization opportunity in PhaseIterGVN");
}
verify_empty_worklist(nullptr);
}
void PhaseIterGVN::verify_empty_worklist(Node* node) {
// Verify that the igvn worklist is empty. If no optimization happened, then
// nothing needs to be on the worklist.
if (_worklist.size() == 0) { return; }
stringStream ss; // Print as a block without tty lock.
for (uint j = 0; j < _worklist.size(); j++) {
Node* n = _worklist.at(j);
ss.print("igvn.worklist[%d] ", j);
n->dump("\n", false, &ss);
}
if (_worklist.size() != 0 && node != nullptr) {
ss.print_cr("Previously optimized:");
node->dump("\n", false, &ss);
}
tty->print_cr("%s", ss.as_string());
assert(false, "igvn worklist must still be empty after verify");
}
// Check that type(n) == n->Value(), return true if we have a failure.
@ -1104,7 +1132,7 @@ void PhaseIterGVN::verify_optimize() {
// (1) Integer "widen" changes, but the range is the same.
// (2) LoadNode performs deep traversals. Load is not notified for changes far away.
// (3) CmpPNode performs deep traversals if it compares oopptr. CmpP is not notified for changes far away.
bool PhaseIterGVN::verify_node_value(Node* n) {
bool PhaseIterGVN::verify_Value_for(Node* n) {
// If we assert inside type(n), because the type is still a null, then maybe
// the node never went through gvn.transform, which would be a bug.
const Type* told = type(n);
@ -1149,15 +1177,873 @@ bool PhaseIterGVN::verify_node_value(Node* n) {
// after loop-opts, so that should take care of many of these cases.
return false;
}
tty->cr();
tty->print_cr("Missed Value optimization:");
n->dump_bfs(1, nullptr, "");
tty->print_cr("Current type:");
told->dump_on(tty);
tty->cr();
tty->print_cr("Optimized type:");
tnew->dump_on(tty);
tty->cr();
stringStream ss; // Print as a block without tty lock.
ss.cr();
ss.print_cr("Missed Value optimization:");
n->dump_bfs(1, nullptr, "", &ss);
ss.print_cr("Current type:");
told->dump_on(&ss);
ss.cr();
ss.print_cr("Optimized type:");
tnew->dump_on(&ss);
ss.cr();
tty->print_cr("%s", ss.as_string());
return true;
}
// Check that all Ideal optimizations that could be done were done.
// Returns true if it found missed optimization opportunities and
// false otherwise (no missed optimization, or skipped verification).
bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) {
// First, we check a list of exceptions, where we skip verification,
// because there are known cases where Ideal can optimize after IGVN.
// Some may be expected and cannot be fixed, and others should be fixed.
switch (n->Opcode()) {
// RangeCheckNode::Ideal looks up the chain for about 999 nodes
// (see "Range-Check scan limit"). So, it is possible that something
// is optimized in that input subgraph, and the RangeCheck was not
// added to the worklist because it would be too expensive to walk
// down the graph for 1000 nodes and put all on the worklist.
//
// Found with:
// java -XX:VerifyIterativeGVN=0100 -Xbatch --version
case Op_RangeCheck:
return false;
// IfNode::Ideal does:
// Node* prev_dom = search_identical(dist, igvn);
// which means we seach up the CFG, traversing at most up to a distance.
// If anything happens rather far away from the If, we may not put the If
// back on the worklist.
//
// Found with:
// java -XX:VerifyIterativeGVN=0100 -Xcomp --version
case Op_If:
return false;
// IfNode::simple_subsuming
// Looks for dominating test that subsumes the current test.
// Notification could be difficult because of larger distance.
//
// Found with:
// runtime/exceptionMsgs/ArrayIndexOutOfBoundsException/ArrayIndexOutOfBoundsExceptionTest.java#id1
// -XX:VerifyIterativeGVN=1110
case Op_CountedLoopEnd:
return false;
// LongCountedLoopEndNode::Ideal
// Probably same issue as above.
//
// Found with:
// compiler/predicates/assertion/TestAssertionPredicates.java#NoLoopPredicationXbatch
// -XX:StressLongCountedLoop=2000000 -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
case Op_LongCountedLoopEnd:
return false;
// RegionNode::Ideal does "Skip around the useless IF diamond".
// 245 IfTrue === 244
// 258 If === 245 257
// 259 IfTrue === 258 [[ 263 ]]
// 260 IfFalse === 258 [[ 263 ]]
// 263 Region === 263 260 259 [[ 263 268 ]]
// to
// 245 IfTrue === 244
// 263 Region === 263 245 _ [[ 263 268 ]]
//
// "Useless" means that there is no code in either branch of the If.
// I found a case where this was not done yet during IGVN.
// Why does the Region not get added to IGVN worklist when the If diamond becomes useless?
//
// Found with:
// java -XX:VerifyIterativeGVN=0100 -Xcomp --version
case Op_Region:
return false;
// In AddNode::Ideal, we call "commute", which swaps the inputs so
// that smaller idx are first. Tracking it back, it led me to
// PhaseIdealLoop::remix_address_expressions which swapped the edges.
//
// Example:
// Before PhaseIdealLoop::remix_address_expressions
// 154 AddI === _ 12 144
// After PhaseIdealLoop::remix_address_expressions
// 154 AddI === _ 144 12
// After AddNode::Ideal
// 154 AddI === _ 12 144
//
// I suspect that the node should be added to the IGVN worklist after
// PhaseIdealLoop::remix_address_expressions
//
// This is the only case I looked at, there may be others. Found like this:
// java -XX:VerifyIterativeGVN=0100 -Xbatch --version
//
// The following hit the same logic in PhaseIdealLoop::remix_address_expressions.
//
// Note: currently all of these fail also for other reasons, for example
// because of "commute" doing the reordering with the phi below. Once
// that is resolved, we can come back to this issue here.
//
// case Op_AddD:
// case Op_AddI:
// case Op_AddL:
// case Op_AddF:
// case Op_MulI:
// case Op_MulL:
// case Op_MulF:
// case Op_MulD:
// if (n->in(1)->_idx > n->in(2)->_idx) {
// // Expect "commute" to revert this case.
// return false;
// }
// break; // keep verifying
// AddFNode::Ideal calls "commute", which can reorder the inputs for this:
// Check for tight loop increments: Loop-phi of Add of loop-phi
// It wants to take the phi into in(1):
// 471 Phi === 435 38 390
// 390 AddF === _ 471 391
//
// Other Associative operators are also affected equally.
//
// Investigate why this does not happen earlier during IGVN.
//
// Found with:
// test/hotspot/jtreg/compiler/loopopts/superword/ReductionPerf.java
// -XX:VerifyIterativeGVN=1110
case Op_AddD:
//case Op_AddI: // Also affected for other reasons, see case further down.
//case Op_AddL: // Also affected for other reasons, see case further down.
case Op_AddF:
case Op_MulI:
case Op_MulL:
case Op_MulF:
case Op_MulD:
case Op_MinF:
case Op_MinD:
case Op_MaxF:
case Op_MaxD:
// XorINode::Ideal
// Found with:
// compiler/intrinsics/chacha/TestChaCha20.java
// -XX:VerifyIterativeGVN=1110
case Op_XorI:
case Op_XorL:
// It seems we may have similar issues with the HF cases.
// Found with aarch64:
// compiler/vectorization/TestFloat16VectorOperations.java
// -XX:VerifyIterativeGVN=1110
case Op_AddHF:
case Op_MulHF:
case Op_MaxHF:
case Op_MinHF:
return false;
// In MulNode::Ideal the edges can be swapped to help value numbering:
//
// // We are OK if right is a constant, or right is a load and
// // left is a non-constant.
// if( !(t2->singleton() ||
// (in(2)->is_Load() && !(t1->singleton() || in(1)->is_Load())) ) ) {
// if( t1->singleton() || // Left input is a constant?
// // Otherwise, sort inputs (commutativity) to help value numbering.
// (in(1)->_idx > in(2)->_idx) ) {
// swap_edges(1, 2);
//
// Why was this not done earlier during IGVN?
//
// Found with:
// test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithG1.java
// -XX:VerifyIterativeGVN=1110
case Op_AndI:
// Same for AndL.
// Found with:
// compiler/intrinsics/bigInteger/MontgomeryMultiplyTest.java
// -XX:VerifyIterativeGVN=1110
case Op_AndL:
return false;
// SubLNode::Ideal does transform like:
// Convert "c1 - (y+c0)" into "(c1-c0) - y"
//
// In IGVN before verification:
// 8423 ConvI2L === _ 3519 [[ 8424 ]] #long:-2
// 8422 ConvI2L === _ 8399 [[ 8424 ]] #long:3..256:www
// 8424 AddL === _ 8422 8423 [[ 8383 ]] !orig=[8382]
// 8016 ConL === 0 [[ 8383 ]] #long:0
// 8383 SubL === _ 8016 8424 [[ 8156 ]] !orig=[8154]
//
// And then in verification:
// 8338 ConL === 0 [[ 8339 8424 ]] #long:-2 <----- Was constant folded.
// 8422 ConvI2L === _ 8399 [[ 8424 ]] #long:3..256:www
// 8424 AddL === _ 8422 8338 [[ 8383 ]] !orig=[8382]
// 8016 ConL === 0 [[ 8383 ]] #long:0
// 8383 SubL === _ 8016 8424 [[ 8156 ]] !orig=[8154]
//
// So the form changed from:
// c1 - (y + [8423 ConvI2L])
// to
// c1 - (y + -2)
// but the SubL was not added to the IGVN worklist. Investigate why.
// There could be other issues too.
//
// There seems to be a related AddL IGVN optimization that triggers
// the same SubL optimization, so investigate that too.
//
// Found with:
// java -XX:VerifyIterativeGVN=0100 -Xcomp --version
case Op_SubL:
return false;
// SubINode::Ideal does
// Convert "x - (y+c0)" into "(x-y) - c0" AND
// Convert "c1 - (y+c0)" into "(c1-c0) - y"
//
// Investigate why this does not yet happen during IGVN.
//
// Found with:
// test/hotspot/jtreg/compiler/c2/IVTest.java
// -XX:VerifyIterativeGVN=1110
case Op_SubI:
return false;
// AddNode::IdealIL does transform like:
// Convert x + (con - y) into "(x - y) + con"
//
// In IGVN before verification:
// 8382 ConvI2L
// 8381 ConvI2L === _ 791 [[ 8383 ]] #long:0
// 8383 SubL === _ 8381 8382
// 8168 ConvI2L
// 8156 AddL === _ 8168 8383 [[ 8158 ]]
//
// And then in verification:
// 8424 AddL
// 8016 ConL === 0 [[ 8383 ]] #long:0 <--- Was constant folded.
// 8383 SubL === _ 8016 8424
// 8168 ConvI2L
// 8156 AddL === _ 8168 8383 [[ 8158 ]]
//
// So the form changed from:
// x + (ConvI2L(0) - [8382 ConvI2L])
// to
// x + (0 - [8424 AddL])
// but the AddL was not added to the IGVN worklist. Investigate why.
// There could be other issues, too. For example with "commute", see above.
//
// Found with:
// java -XX:VerifyIterativeGVN=0100 -Xcomp --version
case Op_AddL:
return false;
// SubTypeCheckNode::Ideal calls SubTypeCheckNode::verify_helper, which does
// Node* cmp = phase->transform(new CmpPNode(subklass, in(SuperKlass)));
// record_for_cleanup(cmp, phase);
// This verification code in the Ideal code creates new nodes, and checks
// if they fold in unexpected ways. This means some nodes are created and
// added to the worklist, even if the SubTypeCheck is not optimized. This
// goes agains the assumption of the verification here, which assumes that
// if the node is not optimized, then no new nodes should be created, and
// also no nodes should be added to the worklist.
// I see two options:
// 1) forbid what verify_helper does, because for each Ideal call it
// uses memory and that is suboptimal. But it is not clear how that
// verification can be done otherwise.
// 2) Special case the verification here. Probably the new nodes that
// were just created are dead, i.e. they are not connected down to
// root. We could verify that, and remove those nodes from the graph
// by setting all their inputs to nullptr. And of course we would
// have to remove those nodes from the worklist.
// Maybe there are other options too, I did not dig much deeper yet.
//
// Found with:
// java -XX:VerifyIterativeGVN=0100 -Xbatch --version
case Op_SubTypeCheck:
return false;
// LoopLimitNode::Ideal when stride is constant power-of-2, we can do a lowering
// to other nodes: Conv, Add, Sub, Mul, And ...
//
// 107 ConI === 0 [[ ... ]] #int:2
// 84 LoadRange === _ 7 83
// 50 ConI === 0 [[ ... ]] #int:0
// 549 LoopLimit === _ 50 84 107
//
// I stepped backward, to see how the node was generated, and I found that it was
// created in PhaseIdealLoop::exact_limit and not changed since. It is added to the
// IGVN worklist. I quickly checked when it goes into LoopLimitNode::Ideal after
// that, and it seems we want to skip lowering it until after loop-opts, but never
// add call record_for_post_loop_opts_igvn. This would be an easy fix, but there
// could be other issues too.
//
// Fond with:
// java -XX:VerifyIterativeGVN=0100 -Xcomp --version
case Op_LoopLimit:
return false;
// PhiNode::Ideal calls split_flow_path, which tries to do this:
// "This optimization tries to find two or more inputs of phi with the same constant
// value. It then splits them into a separate Phi, and according Region."
//
// Example:
// 130 DecodeN === _ 129
// 50 ConP === 0 [[ 18 91 99 18 ]] #null
// 18 Phi === 14 50 130 50 [[ 133 ]] #java/lang/Object * Oop:java/lang/Object *
//
// turns into:
//
// 50 ConP === 0 [[ 99 91 18 ]] #null
// 130 DecodeN === _ 129 [[ 18 ]]
// 18 Phi === 14 130 50 [[ 133 ]] #java/lang/Object * Oop:java/lang/Object *
//
// We would have to investigate why this optimization does not happen during IGVN.
// There could also be other issues - I did not investigate further yet.
//
// Found with:
// java -XX:VerifyIterativeGVN=0100 -Xcomp --version
case Op_Phi:
return false;
// MemBarNode::Ideal does "Eliminate volatile MemBars for scalar replaced objects".
// For examle "The allocated object does not escape".
//
// It seems the difference to earlier calls to MemBarNode::Ideal, is that there
// alloc->as_Allocate()->does_not_escape_thread() returned false, but in verification
// it returned true. Why does the MemBarStoreStore not get added to the IGVN
// worklist when this change happens?
//
// Found with:
// java -XX:VerifyIterativeGVN=0100 -Xcomp --version
case Op_MemBarStoreStore:
return false;
// ConvI2LNode::Ideal converts
// 648 AddI === _ 583 645 [[ 661 ]]
// 661 ConvI2L === _ 648 [[ 664 ]] #long:0..maxint-1:www
// into
// 772 ConvI2L === _ 645 [[ 773 ]] #long:-120..maxint-61:www
// 771 ConvI2L === _ 583 [[ 773 ]] #long:60..120:www
// 773 AddL === _ 771 772 [[ ]]
//
// We have to investigate why this does not happen during IGVN in this case.
// There could also be other issues - I did not investigate further yet.
//
// Found with:
// java -XX:VerifyIterativeGVN=0100 -Xcomp --version
case Op_ConvI2L:
return false;
// AddNode::IdealIL can do this transform (and similar other ones):
// Convert "a*b+a*c into a*(b+c)
// The example had AddI(MulI(a, b), MulI(a, c)). Why did this not happen
// during IGVN? There was a mutation for one of the MulI, and only
// after that the pattern was as needed for the optimization. The MulI
// was added to the IGVN worklist, but not the AddI. This probably
// can be fixed by adding the correct pattern in add_users_of_use_to_worklist.
//
// Found with:
// test/hotspot/jtreg/compiler/loopopts/superword/ReductionPerf.java
// -XX:VerifyIterativeGVN=1110
case Op_AddI:
return false;
// ArrayCopyNode::Ideal
// calls ArrayCopyNode::prepare_array_copy
// calls Compile::conv_I2X_index -> is called with sizetype = intcon(0), I think that
// is not expected, and we create a range int:0..-1
// calls Compile::constrained_convI2L -> creates ConvI2L(intcon(1), int:0..-1)
// note: the type is already empty!
// calls PhaseIterGVN::transform
// calls PhaseIterGVN::transform_old
// calls PhaseIterGVN::subsume_node -> subsume ConvI2L with TOP
// calls Unique_Node_List::push -> pushes TOP to worklist
//
// Once we get back to ArrayCopyNode::prepare_array_copy, we get back TOP, and
// return false. This means we eventually return nullptr from ArrayCopyNode::Ideal.
//
// Question: is it ok to push anything to the worklist during ::Ideal, if we will
// return nullptr, indicating nothing happened?
// Is it smart to do transform in Compile::constrained_convI2L, and then
// check for TOP in calls ArrayCopyNode::prepare_array_copy?
// Should we just allow TOP to land on the worklist, as an exception?
//
// Found with:
// compiler/arraycopy/TestArrayCopyAsLoadsStores.java
// -XX:VerifyIterativeGVN=1110
case Op_ArrayCopy:
return false;
// CastLLNode::Ideal
// calls ConstraintCastNode::optimize_integer_cast -> pushes CastLL through SubL
//
// Could be a notification issue, where updates inputs of CastLL do not notify
// down through SubL to CastLL.
//
// Found With:
// compiler/c2/TestMergeStoresMemorySegment.java#byte-array
// -XX:VerifyIterativeGVN=1110
case Op_CastLL:
return false;
// Similar case happens to CastII
//
// Found With:
// compiler/c2/TestScalarReplacementMaxLiveNodes.java
// -XX:VerifyIterativeGVN=1110
case Op_CastII:
return false;
// MaxLNode::Ideal
// calls AddNode::Ideal
// calls commute -> decides to swap edges
//
// Another notification issue, because we check inputs of inputs?
// MaxL -> Phi -> Loop
// MaxL -> Phi -> MaxL
//
// Found with:
// compiler/c2/irTests/TestIfMinMax.java
// -XX:VerifyIterativeGVN=1110
case Op_MaxL:
case Op_MinL:
return false;
// OrINode::Ideal
// calls AddNode::Ideal
// calls commute -> left is Load, right not -> commute.
//
// Not sure why notification does not work here, seems like
// the depth is only 1, so it should work. Needs investigation.
//
// Found with:
// compiler/codegen/TestCharVect2.java#id0
// -XX:VerifyIterativeGVN=1110
case Op_OrI:
case Op_OrL:
return false;
// Bool -> constant folded to 1.
// Issue with notification?
//
// Found with:
// compiler/c2/irTests/TestVectorizationMismatchedAccess.java
// -XX:VerifyIterativeGVN=1110
case Op_Bool:
return false;
// LShiftLNode::Ideal
// Looks at pattern: "(x + x) << c0", converts it to "x << (c0 + 1)"
// Probably a notification issue.
//
// Found with:
// compiler/conversions/TestMoveConvI2LOrCastIIThruAddIs.java
// -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
case Op_LShiftL:
return false;
// LShiftINode::Ideal
// pattern: ((x + con1) << con2) -> x << con2 + con1 << con2
// Could be issue with notification of inputs of inputs
//
// Side-note: should cases like these not be shared between
// LShiftI and LShiftL?
//
// Found with:
// compiler/escapeAnalysis/Test6689060.java
// -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
case Op_LShiftI:
return false;
// AddPNode::Ideal seems to do set_req without removing lock first.
// Found with various vector tests tier1-tier3.
case Op_AddP:
return false;
// StrIndexOfNode::Ideal
// Found in tier1-3.
case Op_StrIndexOf:
case Op_StrIndexOfChar:
return false;
// StrEqualsNode::Identity
//
// Found (linux x64 only?) with:
// serviceability/sa/ClhsdbThreadContext.java
// -XX:+UnlockExperimentalVMOptions -XX:LockingMode=1 -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
case Op_StrEquals:
return false;
// AryEqNode::Ideal
// Not investigated. Reshapes itself and adds lots of nodes to the worklist.
//
// Found with:
// vmTestbase/vm/mlvm/meth/stress/compiler/i2c_c2i/Test.java
// -XX:+UnlockDiagnosticVMOptions -XX:-TieredCompilation -XX:+StressUnstableIfTraps -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
case Op_AryEq:
return false;
// MergeMemNode::Ideal
// Found in tier1-3. Did not investigate further yet.
case Op_MergeMem:
return false;
// URShiftINode::Ideal
// Found in tier1-3. Did not investigate further yet.
case Op_URShiftI:
return false;
// CMoveINode::Ideal
// Found in tier1-3. Did not investigate further yet.
case Op_CMoveI:
return false;
// CmpPNode::Ideal calls isa_const_java_mirror
// and generates new constant nodes, even if no progress is made.
// We can probably rewrite this so that only types are generated.
// It seems that object types are not hashed, we could investigate
// if that is an option as well.
//
// Found with:
// java -XX:VerifyIterativeGVN=1110 -Xcomp --version
case Op_CmpP:
return false;
// MinINode::Ideal
// Did not investigate, but there are some patterns that might
// need more notification.
case Op_MinI:
case Op_MaxI: // preemptively removed it as well.
return false;
}
if (n->is_Load()) {
// LoadNode::Ideal uses tries to find an earlier memory state, and
// checks can_see_stored_value for it.
//
// Investigate why this was not already done during IGVN.
// A similar issue happens with Identity.
//
// There seem to be other cases where loads go up some steps, like
// LoadNode::Ideal going up 10x steps to find dominating load.
//
// Found with:
// test/hotspot/jtreg/compiler/arraycopy/TestCloneAccess.java
// -XX:VerifyIterativeGVN=1110
return false;
}
if (n->is_Store()) {
// StoreNode::Ideal can do this:
// // Capture an unaliased, unconditional, simple store into an initializer.
// // Or, if it is independent of the allocation, hoist it above the allocation.
// That replaces the Store with a MergeMem.
//
// We have to investigate why this does not happen during IGVN in this case.
// There could also be other issues - I did not investigate further yet.
//
// Found with:
// java -XX:VerifyIterativeGVN=0100 -Xcomp --version
return false;
}
if (n->is_Vector()) {
// VectorNode::Ideal swaps edges, but only for ops
// that are deemed commutable. But swap_edges
// requires the hash to be invariant when the edges
// are swapped, which is not implemented for these
// vector nodes. This seems not to create any trouble
// usually, but we can also get graphs where in the
// end the nodes are not all commuted, so there is
// definitively an issue here.
//
// Probably we have two options: kill the hash, or
// properly make the hash commutation friendly.
//
// Found with:
// compiler/vectorapi/TestMaskedMacroLogicVector.java
// -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 -XX:+UseParallelGC -XX:+UseNUMA
return false;
}
if (n->is_Region()) {
// LoopNode::Ideal calls RegionNode::Ideal.
// CountedLoopNode::Ideal calls RegionNode::Ideal too.
// But I got an issue because RegionNode::optimize_trichotomy
// then modifies another node, and pushes nodes to the worklist
// Not sure if this is ok, modifying another node like that.
// Maybe it is, then we need to look into what to do with
// the nodes that are now on the worklist, maybe just clear
// them out again. But maybe modifying other nodes like that
// is also bad design. In the end, we return nullptr for
// the current CountedLoop. But the extra nodes on the worklist
// trip the asserts later on.
//
// Found with:
// compiler/eliminateAutobox/TestShortBoxing.java
// -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
return false;
}
if (n->is_CallJava()) {
// CallStaticJavaNode::Ideal
// Led to a crash:
// assert((is_CallStaticJava() && cg->is_mh_late_inline()) || (is_CallDynamicJava() && cg->is_virtual_late_inline())) failed: mismatch
//
// Did not investigate yet, could be a bug.
// Or maybe it does not expect to be called during verification.
//
// Found with:
// test/jdk/jdk/incubator/vector/VectorRuns.java
// -XX:VerifyIterativeGVN=1110
// CallDynamicJavaNode::Ideal, and I think also for CallStaticJavaNode::Ideal
// and possibly their subclasses.
// During late inlining it can call CallJavaNode::register_for_late_inline
// That means we do more rounds of late inlining, but might fail.
// Then we do IGVN again, and register the node again for late inlining.
// This creates an endless cycle. Everytime we try late inlining, we
// are also creating more nodes, especially SafePoint and MergeMem.
// These nodes are immediately rejected when the inlining fails in the
// do_late_inline_check, but they still grow the memory, until we hit
// the MemLimit and crash.
// The assumption here seems that CallDynamicJavaNode::Ideal does not get
// called repeatedly, and eventually we terminate. I fear this is not
// a great assumption to make. We should investigate more.
//
// Found with:
// compiler/loopopts/superword/TestDependencyOffsets.java#vanilla-U
// -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
return false;
}
// The number of nodes shoud not increase.
uint old_unique = C->unique();
Node* i = n->Ideal(this, can_reshape);
// If there was no new Idealization, we are probably happy.
if (i == nullptr) {
if (old_unique < C->unique()) {
stringStream ss; // Print as a block without tty lock.
ss.cr();
ss.print_cr("Ideal optimization did not make progress but created new unused nodes.");
ss.print_cr(" old_unique = %d, unique = %d", old_unique, C->unique());
n->dump_bfs(1, nullptr, "", &ss);
tty->print_cr("%s", ss.as_string());
return true;
}
verify_empty_worklist(n);
// Everything is good.
return false;
}
// We just saw a new Idealization which was not done during IGVN.
stringStream ss; // Print as a block without tty lock.
ss.cr();
ss.print_cr("Missed Ideal optimization (can_reshape=%s):", can_reshape ? "true": "false");
if (i == n) {
ss.print_cr("The node was reshaped by Ideal.");
} else {
ss.print_cr("The node was replaced by Ideal.");
ss.print_cr("Old node:");
n->dump_bfs(1, nullptr, "", &ss);
}
ss.print_cr("The result after Ideal:");
i->dump_bfs(1, nullptr, "", &ss);
tty->print_cr("%s", ss.as_string());
return true;
}
// Check that all Identity optimizations that could be done were done.
// Returns true if it found missed optimization opportunities and
// false otherwise (no missed optimization, or skipped verification).
bool PhaseIterGVN::verify_Identity_for(Node* n) {
// First, we check a list of exceptions, where we skip verification,
// because there are known cases where Ideal can optimize after IGVN.
// Some may be expected and cannot be fixed, and others should be fixed.
switch (n->Opcode()) {
// SafePointNode::Identity can remove SafePoints, but wants to wait until
// after loopopts:
// // Transforming long counted loops requires a safepoint node. Do not
// // eliminate a safepoint until loop opts are over.
// if (in(0)->is_Proj() && !phase->C->major_progress()) {
//
// I think the check for major_progress does delay it until after loopopts
// but it does not ensure that the node is on the IGVN worklist after
// loopopts. I think we should try to instead check for
// phase->C->post_loop_opts_phase() and call record_for_post_loop_opts_igvn.
//
// Found with:
// java -XX:VerifyIterativeGVN=1000 -Xcomp --version
case Op_SafePoint:
return false;
// MergeMemNode::Identity replaces the MergeMem with its base_memory if it
// does not record any other memory splits.
//
// I did not deeply investigate, but it looks like MergeMemNode::Identity
// never got called during IGVN for this node, investigate why.
//
// Found with:
// java -XX:VerifyIterativeGVN=1000 -Xcomp --version
case Op_MergeMem:
return false;
// ConstraintCastNode::Identity finds casts that are the same, except that
// the control is "higher up", i.e. dominates. The call goes via
// ConstraintCastNode::dominating_cast to PhaseGVN::is_dominator_helper,
// which traverses up to 100 idom steps. If anything gets optimized somewhere
// away from the cast, but within 100 idom steps, the cast may not be
// put on the IGVN worklist any more.
//
// Found with:
// java -XX:VerifyIterativeGVN=1000 -Xcomp --version
case Op_CastPP:
case Op_CastII:
case Op_CastLL:
return false;
// Same issue for CheckCastPP, uses ConstraintCastNode::Identity and
// checks dominator, which may be changed, but too far up for notification
// to work.
//
// Found with:
// compiler/c2/irTests/TestSkeletonPredicates.java
// -XX:VerifyIterativeGVN=1110
case Op_CheckCastPP:
return false;
// In SubNode::Identity, we do:
// Convert "(X+Y) - Y" into X and "(X+Y) - X" into Y
// In the example, the AddI had an input replaced, the AddI is
// added to the IGVN worklist, but the SubI is one link further
// down and is not added. I checked add_users_of_use_to_worklist
// where I would expect the SubI would be added, and I cannot
// find the pattern, only this one:
// If changed AddI/SubI inputs, check CmpU for range check optimization.
//
// Fix this "notification" issue and check if there are any other
// issues.
//
// Found with:
// java -XX:VerifyIterativeGVN=1000 -Xcomp --version
case Op_SubI:
case Op_SubL:
return false;
// PhiNode::Identity checks for patterns like:
// r = (x != con) ? x : con;
// that can be constant folded to "x".
//
// Call goes through PhiNode::is_cmove_id and CMoveNode::is_cmove_id.
// I suspect there was some earlier change to one of the inputs, but
// not all relevant outputs were put on the IGVN worklist.
//
// Found with:
// test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithG1.java
// -XX:VerifyIterativeGVN=1110
case Op_Phi:
return false;
// ConvI2LNode::Identity does
// convert I2L(L2I(x)) => x
//
// Investigate why this did not already happen during IGVN.
//
// Found with:
// compiler/loopopts/superword/TestDependencyOffsets.java#vanilla-A
// -XX:VerifyIterativeGVN=1110
case Op_ConvI2L:
return false;
// MaxNode::find_identity_operation
// Finds patterns like Max(A, Max(A, B)) -> Max(A, B)
// This can be a 2-hop search, so maybe notification is not
// good enough.
//
// Found with:
// compiler/codegen/TestBooleanVect.java
// -XX:VerifyIterativeGVN=1110
case Op_MaxL:
case Op_MinL:
case Op_MaxI:
case Op_MinI:
case Op_MaxF:
case Op_MinF:
case Op_MaxHF:
case Op_MinHF:
case Op_MaxD:
case Op_MinD:
return false;
// AddINode::Identity
// Converts (x-y)+y to x
// Could be issue with notification
//
// Turns out AddL does the same.
//
// Found with:
// compiler/c2/Test6792161.java
// -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
case Op_AddI:
case Op_AddL:
return false;
// AbsINode::Identity
// Not investigated yet.
case Op_AbsI:
return false;
}
if (n->is_Load()) {
// LoadNode::Identity tries to look for an earlier store value via
// can_see_stored_value. I found an example where this led to
// an Allocation, where we could assume the value was still zero.
// So the LoadN can be replaced with a zerocon.
//
// Investigate why this was not already done during IGVN.
// A similar issue happens with Ideal.
//
// Found with:
// java -XX:VerifyIterativeGVN=1000 -Xcomp --version
return false;
}
if (n->is_Store()) {
// StoreNode::Identity
// Not investigated, but found missing optimization for StoreI.
// Looks like a StoreI is replaced with an InitializeNode.
//
// Found with:
// applications/ctw/modules/java_base_2.java
// -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -Djava.awt.headless=true -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
return false;
}
if (n->is_Vector()) {
// Found with tier1-3. Not investigated yet.
// The observed issue was with AndVNode::Identity
return false;
}
Node* i = n->Identity(this);
// If we cannot find any other Identity, we are happy.
if (i == n) {
verify_empty_worklist(n);
return false;
}
// The verification just found a new Identity that was not found during IGVN.
stringStream ss; // Print as a block without tty lock.
ss.cr();
ss.print_cr("Missed Identity optimization:");
ss.print_cr("Old node:");
n->dump_bfs(1, nullptr, "", &ss);
ss.print_cr("New node:");
i->dump_bfs(1, nullptr, "", &ss);
tty->print_cr("%s", ss.as_string());
return true;
}
#endif
@ -1890,12 +2776,12 @@ void PhaseCCP::analyze() {
#ifdef ASSERT
// For every node n on verify list, check if type(n) == n->Value()
// We have a list of exceptions, see comments in verify_node_value.
// We have a list of exceptions, see comments in verify_Value_for.
void PhaseCCP::verify_analyze(Unique_Node_List& worklist_verify) {
bool failure = false;
while (worklist_verify.size()) {
Node* n = worklist_verify.pop();
failure |= verify_node_value(n);
failure |= verify_Value_for(n);
}
// If we get this assert, check why the reported nodes were not processed again in CCP.
// We should either make sure that these nodes are properly added back to the CCP worklist

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -494,7 +494,10 @@ public:
void optimize();
#ifdef ASSERT
void verify_optimize();
bool verify_node_value(Node* n);
bool verify_Value_for(Node* n);
bool verify_Ideal_for(Node* n, bool can_reshape);
bool verify_Identity_for(Node* n);
void verify_empty_worklist(Node* n);
#endif
#ifndef PRODUCT
@ -593,6 +596,14 @@ public:
// '-XX:VerifyIterativeGVN=10'
return ((VerifyIterativeGVN % 100) / 10) == 1;
}
static bool is_verify_Ideal() {
// '-XX:VerifyIterativeGVN=100'
return ((VerifyIterativeGVN % 1000) / 100) == 1;
}
static bool is_verify_Identity() {
// '-XX:VerifyIterativeGVN=1000'
return ((VerifyIterativeGVN % 10000) / 1000) == 1;
}
protected:
// Sub-quadratic implementation of '-XX:VerifyIterativeGVN=1' (Use-Def verification).
julong _verify_counter;

View File

@ -318,8 +318,10 @@ bool LibraryCallKit::inline_vector_nary_operation(int n) {
const TypeInstPtr* elem_klass = gvn().type(argument(3))->isa_instptr();
const TypeInt* vlen = gvn().type(argument(4))->isa_int();
if (opr == nullptr || vector_klass == nullptr || elem_klass == nullptr || vlen == nullptr ||
!opr->is_con() || vector_klass->const_oop() == nullptr || elem_klass->const_oop() == nullptr || !vlen->is_con()) {
if (opr == nullptr || !opr->is_con() ||
vector_klass == nullptr || vector_klass->const_oop() == nullptr ||
elem_klass == nullptr || elem_klass->const_oop() == nullptr ||
vlen == nullptr || !vlen->is_con()) {
log_if_needed(" ** missing constant: opr=%s vclass=%s etype=%s vlen=%s",
NodeClassNames[argument(0)->Opcode()],
NodeClassNames[argument(1)->Opcode()],
@ -587,7 +589,11 @@ bool LibraryCallKit::inline_vector_mask_operation() {
const TypeInt* vlen = gvn().type(argument(3))->isa_int();
Node* mask = argument(4);
if (mask_klass == nullptr || elem_klass == nullptr || mask->is_top() || vlen == nullptr) {
if (mask_klass == nullptr || mask_klass->const_oop() == nullptr ||
elem_klass == nullptr || elem_klass->const_oop() == nullptr ||
vlen == nullptr || !vlen->is_con() ||
oper == nullptr || !oper->is_con() ||
mask->is_top()) {
return false; // dead code
}
@ -647,9 +653,11 @@ bool LibraryCallKit::inline_vector_frombits_coerced() {
// MODE_BITS_COERCED_LONG_TO_MASK for VectorMask.fromLong operation.
const TypeInt* mode = gvn().type(argument(5))->isa_int();
if (vector_klass == nullptr || elem_klass == nullptr || vlen == nullptr || mode == nullptr ||
bits_type == nullptr || vector_klass->const_oop() == nullptr || elem_klass->const_oop() == nullptr ||
!vlen->is_con() || !mode->is_con()) {
if (vector_klass == nullptr || vector_klass->const_oop() == nullptr ||
elem_klass == nullptr || elem_klass->const_oop() == nullptr ||
vlen == nullptr || !vlen->is_con() ||
bits_type == nullptr ||
mode == nullptr || !mode->is_con()) {
log_if_needed(" ** missing constant: vclass=%s etype=%s vlen=%s bitwise=%s",
NodeClassNames[argument(0)->Opcode()],
NodeClassNames[argument(1)->Opcode()],
@ -775,8 +783,10 @@ bool LibraryCallKit::inline_vector_mem_operation(bool is_store) {
const TypeInt* vlen = gvn().type(argument(2))->isa_int();
const TypeInt* from_ms = gvn().type(argument(6))->isa_int();
if (vector_klass == nullptr || elem_klass == nullptr || vlen == nullptr || !from_ms->is_con() ||
vector_klass->const_oop() == nullptr || elem_klass->const_oop() == nullptr || !vlen->is_con()) {
if (vector_klass == nullptr || vector_klass->const_oop() == nullptr ||
elem_klass == nullptr || elem_klass->const_oop() == nullptr ||
vlen == nullptr || !vlen->is_con() ||
from_ms == nullptr || !from_ms->is_con()) {
log_if_needed(" ** missing constant: vclass=%s etype=%s vlen=%s from_ms=%s",
NodeClassNames[argument(0)->Opcode()],
NodeClassNames[argument(1)->Opcode()],
@ -983,9 +993,11 @@ bool LibraryCallKit::inline_vector_mem_masked_operation(bool is_store) {
const TypeInt* vlen = gvn().type(argument(3))->isa_int();
const TypeInt* from_ms = gvn().type(argument(7))->isa_int();
if (vector_klass == nullptr || mask_klass == nullptr || elem_klass == nullptr || vlen == nullptr ||
vector_klass->const_oop() == nullptr || mask_klass->const_oop() == nullptr || from_ms == nullptr ||
elem_klass->const_oop() == nullptr || !vlen->is_con() || !from_ms->is_con()) {
if (vector_klass == nullptr || vector_klass->const_oop() == nullptr ||
mask_klass == nullptr || mask_klass->const_oop() == nullptr ||
elem_klass == nullptr || elem_klass->const_oop() == nullptr ||
vlen == nullptr || !vlen->is_con() ||
from_ms == nullptr || !from_ms->is_con()) {
log_if_needed(" ** missing constant: vclass=%s mclass=%s etype=%s vlen=%s from_ms=%s",
NodeClassNames[argument(0)->Opcode()],
NodeClassNames[argument(1)->Opcode()],
@ -1222,8 +1234,10 @@ bool LibraryCallKit::inline_vector_gather_scatter(bool is_scatter) {
const TypeInt* vlen = gvn().type(argument(3))->isa_int();
const TypeInstPtr* vector_idx_klass = gvn().type(argument(4))->isa_instptr();
if (vector_klass == nullptr || elem_klass == nullptr || vector_idx_klass == nullptr || vlen == nullptr ||
vector_klass->const_oop() == nullptr || elem_klass->const_oop() == nullptr || vector_idx_klass->const_oop() == nullptr || !vlen->is_con()) {
if (vector_klass == nullptr || vector_klass->const_oop() == nullptr ||
elem_klass == nullptr || elem_klass->const_oop() == nullptr ||
vlen == nullptr || !vlen->is_con() ||
vector_idx_klass == nullptr || vector_idx_klass->const_oop() == nullptr) {
log_if_needed(" ** missing constant: vclass=%s etype=%s vlen=%s viclass=%s",
NodeClassNames[argument(0)->Opcode()],
NodeClassNames[argument(2)->Opcode()],
@ -1409,8 +1423,10 @@ bool LibraryCallKit::inline_vector_reduction() {
const TypeInstPtr* elem_klass = gvn().type(argument(3))->isa_instptr();
const TypeInt* vlen = gvn().type(argument(4))->isa_int();
if (opr == nullptr || vector_klass == nullptr || elem_klass == nullptr || vlen == nullptr ||
!opr->is_con() || vector_klass->const_oop() == nullptr || elem_klass->const_oop() == nullptr || !vlen->is_con()) {
if (opr == nullptr || !opr->is_con() ||
vector_klass == nullptr || vector_klass->const_oop() == nullptr ||
elem_klass == nullptr || elem_klass->const_oop() == nullptr ||
vlen == nullptr || !vlen->is_con()) {
log_if_needed(" ** missing constant: opr=%s vclass=%s etype=%s vlen=%s",
NodeClassNames[argument(0)->Opcode()],
NodeClassNames[argument(1)->Opcode()],
@ -1547,8 +1563,10 @@ bool LibraryCallKit::inline_vector_test() {
const TypeInstPtr* elem_klass = gvn().type(argument(2))->isa_instptr();
const TypeInt* vlen = gvn().type(argument(3))->isa_int();
if (cond == nullptr || vector_klass == nullptr || elem_klass == nullptr || vlen == nullptr ||
!cond->is_con() || vector_klass->const_oop() == nullptr || elem_klass->const_oop() == nullptr || !vlen->is_con()) {
if (cond == nullptr || !cond->is_con() ||
vector_klass == nullptr || vector_klass->const_oop() == nullptr ||
elem_klass == nullptr || elem_klass->const_oop() == nullptr ||
vlen == nullptr || !vlen->is_con()) {
log_if_needed(" ** missing constant: cond=%s vclass=%s etype=%s vlen=%s",
NodeClassNames[argument(0)->Opcode()],
NodeClassNames[argument(1)->Opcode()],
@ -2505,10 +2523,10 @@ bool LibraryCallKit::inline_vector_extract() {
const TypeInt* vlen = gvn().type(argument(2))->isa_int();
const TypeInt* idx = gvn().type(argument(4))->isa_int();
if (vector_klass == nullptr || elem_klass == nullptr || vlen == nullptr || idx == nullptr) {
return false; // dead code
}
if (vector_klass->const_oop() == nullptr || elem_klass->const_oop() == nullptr || !vlen->is_con()) {
if (vector_klass == nullptr || vector_klass->const_oop() == nullptr ||
elem_klass == nullptr || elem_klass->const_oop() == nullptr ||
vlen == nullptr || !vlen->is_con() ||
idx == nullptr || !idx->is_con()) {
log_if_needed(" ** missing constant: vclass=%s etype=%s vlen=%s",
NodeClassNames[argument(0)->Opcode()],
NodeClassNames[argument(1)->Opcode()],
@ -2811,9 +2829,11 @@ bool LibraryCallKit::inline_vector_compress_expand() {
const TypeInstPtr* elem_klass = gvn().type(argument(3))->isa_instptr();
const TypeInt* vlen = gvn().type(argument(4))->isa_int();
if (vector_klass == nullptr || elem_klass == nullptr || mask_klass == nullptr || vlen == nullptr ||
vector_klass->const_oop() == nullptr || mask_klass->const_oop() == nullptr ||
elem_klass->const_oop() == nullptr || !vlen->is_con() || !opr->is_con()) {
if (opr == nullptr || !opr->is_con() ||
vector_klass == nullptr || vector_klass->const_oop() == nullptr ||
mask_klass == nullptr || mask_klass->const_oop() == nullptr ||
elem_klass == nullptr || elem_klass->const_oop() == nullptr ||
vlen == nullptr || !vlen->is_con()) {
log_if_needed(" ** missing constant: opr=%s vclass=%s mclass=%s etype=%s vlen=%s",
NodeClassNames[argument(0)->Opcode()],
NodeClassNames[argument(1)->Opcode()],
@ -2892,9 +2912,9 @@ bool LibraryCallKit::inline_index_vector() {
const TypeInstPtr* elem_klass = gvn().type(argument(1))->isa_instptr();
const TypeInt* vlen = gvn().type(argument(2))->isa_int();
if (vector_klass == nullptr || elem_klass == nullptr || vlen == nullptr ||
vector_klass->const_oop() == nullptr || !vlen->is_con() ||
elem_klass->const_oop() == nullptr) {
if (vector_klass == nullptr || vector_klass->const_oop() == nullptr ||
elem_klass == nullptr || elem_klass->const_oop() == nullptr ||
vlen == nullptr || !vlen->is_con() ) {
log_if_needed(" ** missing constant: vclass=%s etype=%s vlen=%s",
NodeClassNames[argument(0)->Opcode()],
NodeClassNames[argument(1)->Opcode()],
@ -3026,8 +3046,9 @@ bool LibraryCallKit::inline_index_partially_in_upper_range() {
const TypeInstPtr* elem_klass = gvn().type(argument(1))->isa_instptr();
const TypeInt* vlen = gvn().type(argument(2))->isa_int();
if (mask_klass == nullptr || elem_klass == nullptr || vlen == nullptr ||
mask_klass->const_oop() == nullptr || elem_klass->const_oop() == nullptr || !vlen->is_con()) {
if (mask_klass == nullptr || mask_klass->const_oop() == nullptr ||
elem_klass == nullptr || elem_klass->const_oop() == nullptr ||
vlen == nullptr || !vlen->is_con()) {
log_if_needed(" ** missing constant: mclass=%s etype=%s vlen=%s",
NodeClassNames[argument(0)->Opcode()],
NodeClassNames[argument(1)->Opcode()],

View File

@ -2285,9 +2285,11 @@ JNI_ENTRY(jobjectArray, jni_NewObjectArray(JNIEnv *env, jsize length, jclass ele
jobjectArray ret = nullptr;
DT_RETURN_MARK(NewObjectArray, jobjectArray, (const jobjectArray&)ret);
Klass* ek = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(elementClass));
Klass* ak = ek->array_klass(CHECK_NULL);
ObjArrayKlass::cast(ak)->initialize(CHECK_NULL);
objArrayOop result = ObjArrayKlass::cast(ak)->allocate(length, CHECK_NULL);
// Make sure bottom_klass is initialized.
ek->initialize(CHECK_NULL);
objArrayOop result = oopFactory::new_objArray(ek, length, CHECK_NULL);
oop initial_value = JNIHandles::resolve(initialElement);
if (initial_value != nullptr) { // array already initialized with null
for (int index = 0; index < length; index++) {

View File

@ -12862,16 +12862,17 @@ myInit() {
parameters uniquely identify the current location
(where the exception was detected) and allow
the mapping to source file and line number when that information is
available. The <code>exception</code> field identifies the thrown
available. The <code>exception</code> parameter identifies the thrown
exception object. The <code>catch_method</code>
and <code>catch_location</code> identify the location of the catch clause,
if any, that handles the thrown exception. If there is no such catch clause,
each field is set to 0. There is no guarantee that the thread will ever
the <code>catch_method</code> is set to null and the <code>catch_location</code>is set to 0.
There is no guarantee that the thread will ever
reach this catch clause. If there are native methods on the call stack
between the throw location and the catch clause, the exception may
be reset by one of those native methods.
Similarly, exceptions that are reported as uncaught (<code>catch_klass</code>
et al. set to 0) may in fact be caught by native code.
Similarly, exceptions that are reported as uncaught (<code>catch_method</code>
set to null) may in fact be caught by native code.
Agents can check for these occurrences by monitoring
<eventlink id="ExceptionCatch"></eventlink> events.
Note that finally clauses are implemented as catch and re-throw. Therefore they
@ -12960,7 +12961,7 @@ myInit() {
available. For exceptions caught in a Java programming language method, the
<code>exception</code> object identifies the exception object. Exceptions
caught in native methods are not necessarily available by the time the
exception catch is reported, so the <code>exception</code> field is set
exception catch is reported, so the <code>exception</code> parameter is set
to null.
</description>
<origin>jvmdi</origin>

View File

@ -3550,6 +3550,13 @@ void VM_RedefineClasses::set_new_constant_pool(
Array<u1>* new_fis = FieldInfoStream::create_FieldInfoStream(fields, java_fields, injected_fields, scratch_class->class_loader_data(), CHECK);
scratch_class->set_fieldinfo_stream(new_fis);
MetadataFactory::free_array<u1>(scratch_class->class_loader_data(), old_stream);
Array<u1>* old_table = scratch_class->fieldinfo_search_table();
Array<u1>* search_table = FieldInfoStream::create_search_table(scratch_class->constants(), new_fis, scratch_class->class_loader_data(), CHECK);
scratch_class->set_fieldinfo_search_table(search_table);
MetadataFactory::free_array<u1>(scratch_class->class_loader_data(), old_table);
DEBUG_ONLY(FieldInfoStream::validate_search_table(scratch_class->constants(), new_fis, search_table));
}
// Update constant pool indices in the inner classes info to use

View File

@ -28,6 +28,7 @@
#include "code/location.hpp"
#include "jni.h"
#include "jvm.h"
#include "memory/oopFactory.hpp"
#include "oops/klass.inline.hpp"
#include "oops/typeArrayOop.inline.hpp"
#include "prims/vectorSupport.hpp"
@ -109,9 +110,7 @@ Handle VectorSupport::allocate_vector_payload_helper(InstanceKlass* ik, frame* f
int elem_size = type2aelembytes(elem_bt);
// On-heap vector values are represented as primitive arrays.
TypeArrayKlass* tak = Universe::typeArrayKlass(elem_bt);
typeArrayOop arr = tak->allocate(num_elem, CHECK_NH); // safepoint
typeArrayOop arr = oopFactory::new_typeArray(elem_bt, num_elem, CHECK_NH); // safepoint
if (location.is_register()) {
// Value was in a callee-saved register.

View File

@ -583,28 +583,6 @@ WB_ENTRY(jboolean, WB_G1HasRegionsToUncommit(JNIEnv* env, jobject o))
THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_G1HasRegionsToUncommit: G1 GC is not enabled");
WB_END
#endif // INCLUDE_G1GC
#if INCLUDE_PARALLELGC
WB_ENTRY(jlong, WB_PSVirtualSpaceAlignment(JNIEnv* env, jobject o))
if (UseParallelGC) {
return GenAlignment;
}
THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_PSVirtualSpaceAlignment: Parallel GC is not enabled");
WB_END
WB_ENTRY(jlong, WB_PSHeapGenerationAlignment(JNIEnv* env, jobject o))
if (UseParallelGC) {
return GenAlignment;
}
THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_PSHeapGenerationAlignment: Parallel GC is not enabled");
WB_END
#endif // INCLUDE_PARALLELGC
#if INCLUDE_G1GC
WB_ENTRY(jobject, WB_G1AuxiliaryMemoryUsage(JNIEnv* env))
if (UseG1GC) {
ResourceMark rm(THREAD);
@ -1097,6 +1075,22 @@ bool WhiteBox::validate_cgroup(bool cgroups_v2_enabled,
}
#endif
bool WhiteBox::is_asan_enabled() {
#ifdef ADDRESS_SANITIZER
return true;
#else
return false;
#endif
}
bool WhiteBox::is_ubsan_enabled() {
#ifdef UNDEFINED_BEHAVIOR_SANITIZER
return true;
#else
return false;
#endif
}
bool WhiteBox::compile_method(Method* method, int comp_level, int bci, JavaThread* THREAD) {
// Screen for unavailable/bad comp level or null method
AbstractCompiler* comp = CompileBroker::compiler(comp_level);
@ -1908,6 +1902,14 @@ WB_ENTRY(jboolean, WB_IsMonitorInflated(JNIEnv* env, jobject wb, jobject obj))
return (jboolean) obj_oop->mark().has_monitor();
WB_END
WB_ENTRY(jboolean, WB_IsAsanEnabled(JNIEnv* env))
return (jboolean) WhiteBox::is_asan_enabled();
WB_END
WB_ENTRY(jboolean, WB_IsUbsanEnabled(JNIEnv* env))
return (jboolean) WhiteBox::is_ubsan_enabled();
WB_END
WB_ENTRY(jlong, WB_getInUseMonitorCount(JNIEnv* env, jobject wb))
return (jlong) WhiteBox::get_in_use_monitor_count();
WB_END
@ -2773,10 +2775,6 @@ static JNINativeMethod methods[] = {
{CC"g1MemoryNodeIds", CC"()[I", (void*)&WB_G1MemoryNodeIds },
{CC"g1GetMixedGCInfo", CC"(I)[J", (void*)&WB_G1GetMixedGCInfo },
#endif // INCLUDE_G1GC
#if INCLUDE_PARALLELGC
{CC"psVirtualSpaceAlignment",CC"()J", (void*)&WB_PSVirtualSpaceAlignment},
{CC"psHeapGenerationAlignment",CC"()J", (void*)&WB_PSHeapGenerationAlignment},
#endif
{CC"NMTMalloc", CC"(J)J", (void*)&WB_NMTMalloc },
{CC"NMTMallocWithPseudoStack", CC"(JI)J", (void*)&WB_NMTMallocWithPseudoStack},
{CC"NMTMallocWithPseudoStackAndType", CC"(JII)J", (void*)&WB_NMTMallocWithPseudoStackAndType},
@ -2908,6 +2906,8 @@ static JNINativeMethod methods[] = {
(void*)&WB_AddModuleExportsToAll },
{CC"deflateIdleMonitors", CC"()Z", (void*)&WB_DeflateIdleMonitors },
{CC"isMonitorInflated0", CC"(Ljava/lang/Object;)Z", (void*)&WB_IsMonitorInflated },
{CC"isAsanEnabled", CC"()Z", (void*)&WB_IsAsanEnabled },
{CC"isUbsanEnabled", CC"()Z", (void*)&WB_IsUbsanEnabled },
{CC"getInUseMonitorCount", CC"()J", (void*)&WB_getInUseMonitorCount },
{CC"getLockStackCapacity", CC"()I", (void*)&WB_getLockStackCapacity },
{CC"supportsRecursiveLightweightLocking", CC"()Z", (void*)&WB_supportsRecursiveLightweightLocking },

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -72,6 +72,9 @@ class WhiteBox : public AllStatic {
#ifdef LINUX
static bool validate_cgroup(bool cgroups_v2_enabled, const char* controllers_file, const char* proc_self_cgroup, const char* proc_self_mountinfo, u1* cg_flags);
#endif
// provide info about enabling of Address Sanitizer / Undefined Behavior Sanitizer
static bool is_asan_enabled();
static bool is_ubsan_enabled();
};
#endif // SHARE_PRIMS_WHITEBOX_HPP

View File

@ -528,10 +528,6 @@ static SpecialFlag const special_jvm_flags[] = {
{ "DynamicDumpSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() },
{ "RequireSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() },
{ "UseSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() },
#ifdef LINUX
{ "UseLinuxPosixThreadCPUClocks", JDK_Version::jdk(24), JDK_Version::jdk(25), JDK_Version::jdk(26) },
{ "UseOprofile", JDK_Version::jdk(25), JDK_Version::jdk(26), JDK_Version::jdk(27) },
#endif
{ "LockingMode", JDK_Version::jdk(24), JDK_Version::jdk(26), JDK_Version::jdk(27) },
#ifdef _LP64
{ "UseCompressedClassPointers", JDK_Version::jdk(25), JDK_Version::jdk(26), JDK_Version::undefined() },
@ -541,7 +537,9 @@ static SpecialFlag const special_jvm_flags[] = {
// -------------- Obsolete Flags - sorted by expired_in --------------
{ "PerfDataSamplingInterval", JDK_Version::undefined(), JDK_Version::jdk(25), JDK_Version::jdk(26) },
#ifdef LINUX
{ "UseOprofile", JDK_Version::jdk(25), JDK_Version::jdk(26), JDK_Version::jdk(27) },
#endif
{ "MetaspaceReclaimPolicy", JDK_Version::undefined(), JDK_Version::jdk(21), JDK_Version::undefined() },
{ "ZGenerational", JDK_Version::jdk(23), JDK_Version::jdk(24), JDK_Version::undefined() },
{ "ZMarkStackSpaceLimit", JDK_Version::undefined(), JDK_Version::jdk(25), JDK_Version::undefined() },

View File

@ -1274,11 +1274,11 @@ bool Deoptimization::realloc_objects(JavaThread* thread, frame* fr, RegisterMap*
assert(sv->field_size() % type2size[ak->element_type()] == 0, "non-integral array length");
int len = sv->field_size() / type2size[ak->element_type()];
InternalOOMEMark iom(THREAD);
obj = ak->allocate(len, THREAD);
obj = ak->allocate_instance(len, THREAD);
} else if (k->is_objArray_klass()) {
ObjArrayKlass* ak = ObjArrayKlass::cast(k);
InternalOOMEMark iom(THREAD);
obj = ak->allocate(sv->field_size(), THREAD);
obj = ak->allocate_instance(sv->field_size(), THREAD);
}
if (obj == nullptr) {

View File

@ -299,8 +299,9 @@ JVMFlag::Error TypeProfileLevelConstraintFunc(uint value, bool verbose) {
}
JVMFlag::Error VerifyIterativeGVNConstraintFunc(uint value, bool verbose) {
const int max_modes = 4;
uint original_value = value;
for (int i = 0; i < 2; i++) {
for (int i = 0; i < max_modes; i++) {
if (value % 10 > 1) {
JVMFlag::printError(verbose,
"Invalid value (" UINT32_FORMAT ") "
@ -312,7 +313,7 @@ JVMFlag::Error VerifyIterativeGVNConstraintFunc(uint value, bool verbose) {
if (value != 0) {
JVMFlag::printError(verbose,
"Invalid value (" UINT32_FORMAT ") "
"for VerifyIterativeGVN: maximal 2 digits\n", original_value);
"for VerifyIterativeGVN: maximal %d digits\n", original_value, max_modes);
return JVMFlag::VIOLATES_CONSTRAINT;
}
return JVMFlag::SUCCESS;

View File

@ -2005,6 +2005,10 @@ const int ObjectAlignmentInBytes = 8;
product(bool, UseThreadsLockThrottleLock, true, DIAGNOSTIC, \
"Use an extra lock during Thread start and exit to alleviate" \
"contention on Threads_lock.") \
\
develop(uint, BinarySearchThreshold, 16, \
"Minimal number of elements in a sorted collection to prefer" \
"binary search over simple linear search." ) \
// end of RUNTIME_FLAGS

View File

@ -465,9 +465,7 @@ HandshakeState::HandshakeState(JavaThread* target) :
_queue(),
_lock(Monitor::nosafepoint, "HandshakeState_lock"),
_active_handshaker(),
_async_exceptions_blocked(false),
_suspended(false),
_async_suspend_handshake(false) {
_async_exceptions_blocked(false) {
}
HandshakeState::~HandshakeState() {
@ -699,128 +697,8 @@ HandshakeState::ProcessResult HandshakeState::try_process(HandshakeOperation* ma
return op == match_op ? HandshakeState::_succeeded : HandshakeState::_processed;
}
void HandshakeState::do_self_suspend() {
assert(Thread::current() == _handshakee, "should call from _handshakee");
assert(_lock.owned_by_self(), "Lock must be held");
assert(!_handshakee->has_last_Java_frame() || _handshakee->frame_anchor()->walkable(), "should have walkable stack");
assert(_handshakee->thread_state() == _thread_blocked, "Caller should have transitioned to _thread_blocked");
while (is_suspended()) {
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " suspended", p2i(_handshakee));
_lock.wait_without_safepoint_check();
}
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " resumed", p2i(_handshakee));
}
// This is the closure that prevents a suspended JavaThread from
// escaping the suspend request.
class ThreadSelfSuspensionHandshake : public AsyncHandshakeClosure {
public:
ThreadSelfSuspensionHandshake() : AsyncHandshakeClosure("ThreadSelfSuspensionHandshake") {}
void do_thread(Thread* thr) {
JavaThread* current = JavaThread::cast(thr);
assert(current == Thread::current(), "Must be self executed.");
JavaThreadState jts = current->thread_state();
current->set_thread_state(_thread_blocked);
current->handshake_state()->do_self_suspend();
current->set_thread_state(jts);
current->handshake_state()->set_async_suspend_handshake(false);
}
virtual bool is_suspend() { return true; }
};
bool HandshakeState::suspend_with_handshake(bool register_vthread_SR) {
assert(_handshakee->threadObj() != nullptr, "cannot suspend with a null threadObj");
if (_handshakee->is_exiting()) {
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " exiting", p2i(_handshakee));
return false;
}
if (has_async_suspend_handshake()) {
if (is_suspended()) {
// Target is already suspended.
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " already suspended", p2i(_handshakee));
return false;
} else {
// Target is going to wake up and leave suspension.
// Let's just stop the thread from doing that.
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " re-suspended", p2i(_handshakee));
set_suspended(true, register_vthread_SR);
return true;
}
}
// no suspend request
assert(!is_suspended(), "cannot be suspended without a suspend request");
// Thread is safe, so it must execute the request, thus we can count it as suspended
// from this point.
set_suspended(true, register_vthread_SR);
set_async_suspend_handshake(true);
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " suspended, arming ThreadSuspension", p2i(_handshakee));
ThreadSelfSuspensionHandshake* ts = new ThreadSelfSuspensionHandshake();
Handshake::execute(ts, _handshakee);
return true;
}
// This is the closure that synchronously honors the suspend request.
class SuspendThreadHandshake : public HandshakeClosure {
bool _register_vthread_SR;
bool _did_suspend;
public:
SuspendThreadHandshake(bool register_vthread_SR) : HandshakeClosure("SuspendThread"),
_register_vthread_SR(register_vthread_SR), _did_suspend(false) {}
void do_thread(Thread* thr) {
JavaThread* target = JavaThread::cast(thr);
_did_suspend = target->handshake_state()->suspend_with_handshake(_register_vthread_SR);
}
bool did_suspend() { return _did_suspend; }
};
bool HandshakeState::suspend(bool register_vthread_SR) {
JVMTI_ONLY(assert(!_handshakee->is_in_VTMS_transition(), "no suspend allowed in VTMS transition");)
JavaThread* self = JavaThread::current();
if (_handshakee == self) {
// If target is the current thread we can bypass the handshake machinery
// and just suspend directly
ThreadBlockInVM tbivm(self);
MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag);
set_suspended(true, register_vthread_SR);
do_self_suspend();
return true;
} else {
SuspendThreadHandshake st(register_vthread_SR);
Handshake::execute(&st, _handshakee);
return st.did_suspend();
}
}
bool HandshakeState::resume(bool register_vthread_SR) {
MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag);
if (!is_suspended()) {
assert(!_handshakee->is_suspended(), "cannot be suspended without a suspend request");
return false;
}
// Resume the thread.
set_suspended(false, register_vthread_SR);
_lock.notify();
return true;
}
void HandshakeState::set_suspended(bool is_suspend, bool register_vthread_SR) {
#if INCLUDE_JVMTI
if (register_vthread_SR) {
assert(_handshakee->is_vthread_mounted(), "sanity check");
if (is_suspend) {
JvmtiVTSuspender::register_vthread_suspend(_handshakee->vthread());
} else {
JvmtiVTSuspender::register_vthread_resume(_handshakee->vthread());
}
}
#endif
Atomic::store(&_suspended, is_suspend);
}
void HandshakeState::handle_unsafe_access_error() {
if (is_suspended()) {
if (_handshakee->is_suspended()) {
// A suspend handshake was added to the queue after the
// unsafe access error. Since the suspender has already
// considered this JT as suspended and assumes it won't go

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -35,8 +35,6 @@
class HandshakeOperation;
class AsyncHandshakeOperation;
class JavaThread;
class SuspendThreadHandshake;
class ThreadSelfSuspensionHandshake;
class UnsafeAccessErrorHandshake;
class ThreadsListHandle;
@ -88,8 +86,6 @@ class JvmtiRawMonitor;
// operation is only done by either VMThread/Handshaker on behalf of the
// JavaThread or by the target JavaThread itself.
class HandshakeState {
friend ThreadSelfSuspensionHandshake;
friend SuspendThreadHandshake;
friend UnsafeAccessErrorHandshake;
friend JavaThread;
// This a back reference to the JavaThread,
@ -98,7 +94,7 @@ class HandshakeState {
// The queue containing handshake operations to be performed on _handshakee.
FilterQueue<HandshakeOperation*> _queue;
// Provides mutual exclusion to this state and queue. Also used for
// JavaThread suspend/resume operations.
// JavaThread suspend/resume operations performed by SuspendResumeManager.
Monitor _lock;
// Set to the thread executing the handshake operation.
Thread* volatile _active_handshaker;
@ -160,31 +156,5 @@ class HandshakeState {
bool async_exceptions_blocked() { return _async_exceptions_blocked; }
void set_async_exceptions_blocked(bool b) { _async_exceptions_blocked = b; }
void handle_unsafe_access_error();
// Suspend/resume support
private:
// This flag is true when the thread owning this
// HandshakeState (the _handshakee) is suspended.
volatile bool _suspended;
// This flag is true while there is async handshake (trap)
// on queue. Since we do only need one, we can reuse it if
// thread gets suspended again (after a resume)
// and we have not yet processed it.
bool _async_suspend_handshake;
// Called from the suspend handshake.
bool suspend_with_handshake(bool register_vthread_SR);
// Called from the async handshake (the trap)
// to stop a thread from continuing execution when suspended.
void do_self_suspend();
bool is_suspended() { return Atomic::load(&_suspended); }
void set_suspended(bool to, bool register_vthread_SR);
bool has_async_suspend_handshake() { return _async_suspend_handshake; }
void set_async_suspend_handshake(bool to) { _async_suspend_handshake = to; }
bool suspend(bool register_vthread_SR);
bool resume(bool register_vthread_SR);
};
#endif // SHARE_RUNTIME_HANDSHAKE_HPP

View File

@ -498,6 +498,7 @@ JavaThread::JavaThread(MemTag mem_tag) :
_pending_interrupted_exception(false),
_handshake(this),
_suspend_resume_manager(this, &_handshake._lock),
_popframe_preserved_args(nullptr),
_popframe_preserved_args_size(0),
@ -1200,13 +1201,13 @@ bool JavaThread::java_suspend(bool register_vthread_SR) {
guarantee(Thread::is_JavaThread_protected(/* target */ this),
"target JavaThread is not protected in calling context.");
return this->handshake_state()->suspend(register_vthread_SR);
return this->suspend_resume_manager()->suspend(register_vthread_SR);
}
bool JavaThread::java_resume(bool register_vthread_SR) {
guarantee(Thread::is_JavaThread_protected_by_TLH(/* target */ this),
"missing ThreadsListHandle in calling context.");
return this->handshake_state()->resume(register_vthread_SR);
return this->suspend_resume_manager()->resume(register_vthread_SR);
}
// Wait for another thread to perform object reallocation and relocking on behalf of

View File

@ -40,6 +40,7 @@
#include "runtime/safepointMechanism.hpp"
#include "runtime/stackWatermarkSet.hpp"
#include "runtime/stackOverflow.hpp"
#include "runtime/suspendResumeManager.hpp"
#include "runtime/thread.hpp"
#include "runtime/threadHeapSampler.hpp"
#include "runtime/threadIdentifier.hpp"
@ -694,9 +695,13 @@ private:
// Suspend/resume support for JavaThread
// higher-level suspension/resume logic called by the public APIs
private:
SuspendResumeManager _suspend_resume_manager;
public:
bool java_suspend(bool register_vthread_SR);
bool java_resume(bool register_vthread_SR);
bool is_suspended() { return _handshake.is_suspended(); }
bool is_suspended() { return _suspend_resume_manager.is_suspended(); }
SuspendResumeManager* suspend_resume_manager() { return &_suspend_resume_manager; }
// Check for async exception in addition to safepoint.
static void check_special_condition_for_native_trans(JavaThread *thread);

Some files were not shown because too many files have changed in this diff Show More