From 6fb5b8a3d26acfcfc0c114b13feab5f9a28718e7 Mon Sep 17 00:00:00 2001 From: Alexei Voitylov Date: Wed, 5 Jun 2024 19:43:15 +0000 Subject: [PATCH 01/12] 8314794: Improve UTF8 String supports Co-authored-by: Ekaterina Vergizova Reviewed-by: mbalao Backport-of: ab2532d858de8d855529b6f2491f94c499f94009 --- src/hotspot/share/utilities/exceptions.cpp | 24 +++++++++++++--------- src/hotspot/share/utilities/utf8.cpp | 10 ++++++--- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/hotspot/share/utilities/exceptions.cpp b/src/hotspot/share/utilities/exceptions.cpp index c8f82a7e24f..98be313be44 100644 --- a/src/hotspot/share/utilities/exceptions.cpp +++ b/src/hotspot/share/utilities/exceptions.cpp @@ -41,6 +41,9 @@ #include "utilities/events.hpp" #include "utilities/exceptions.hpp" +// Limit exception message components to 64K (the same max as Symbols) +#define MAX_LEN 65535 + // Implementation of ThreadShadow void check_ThreadShadow() { const ByteSize offset1 = byte_offset_of(ThreadShadow, _pending_exception); @@ -135,10 +138,11 @@ void Exceptions::_throw(Thread* thread, const char* file, int line, Handle h_exc assert(h_exception() != NULL, "exception should not be NULL"); // tracing (do this up front - so it works during boot strapping) - log_info(exceptions)("Exception <%s%s%s> (" INTPTR_FORMAT ") \n" + log_info(exceptions)("Exception <%.*s%s%.*s> (" INTPTR_FORMAT ") \n" "thrown [%s, line %d]\nfor thread " INTPTR_FORMAT, - h_exception->print_value_string(), - message ? ": " : "", message ? message : "", + MAX_LEN, h_exception->print_value_string(), + message ? ": " : "", + MAX_LEN, message ? message : "", p2i(h_exception()), file, line, p2i(thread)); // for AbortVMOnException flag Exceptions::debug_check_abort(h_exception, message); @@ -531,13 +535,13 @@ void Exceptions::log_exception(Handle exception, const char* message) { ResourceMark rm; const char* detail_message = java_lang_Throwable::message_as_utf8(exception()); if (detail_message != NULL) { - log_info(exceptions)("Exception <%s: %s>\n thrown in %s", - exception->print_value_string(), - detail_message, - message); + log_info(exceptions)("Exception <%.*s: %.*s>\n thrown in %.*s", + MAX_LEN, exception->print_value_string(), + MAX_LEN, detail_message, + MAX_LEN, message); } else { - log_info(exceptions)("Exception <%s>\n thrown in %s", - exception->print_value_string(), - message); + log_info(exceptions)("Exception <%.*s>\n thrown in %.*s", + MAX_LEN, exception->print_value_string(), + MAX_LEN, message); } } diff --git a/src/hotspot/share/utilities/utf8.cpp b/src/hotspot/share/utilities/utf8.cpp index a67fd7134c4..1b5a0efc66b 100644 --- a/src/hotspot/share/utilities/utf8.cpp +++ b/src/hotspot/share/utilities/utf8.cpp @@ -435,12 +435,16 @@ int UNICODE::utf8_size(jbyte c) { template int UNICODE::utf8_length(T* base, int length) { - int result = 0; + size_t result = 0; for (int index = 0; index < length; index++) { T c = base[index]; - result += utf8_size(c); + int sz = utf8_size(c); + if (result + sz > INT_MAX-1) { + break; + } + result += sz; } - return result; + return checked_cast(result); } template From fcfbef7f66dc31978bcb228dd8a042f9aa190ff7 Mon Sep 17 00:00:00 2001 From: Alexei Voitylov Date: Mon, 3 Jun 2024 21:16:00 +0000 Subject: [PATCH 02/12] 8319859: Better symbol storage Reviewed-by: yan, mbalao Backport-of: f7857e9106841f402ade56bad0338fc53159fb9a --- src/hotspot/share/classfile/symbolTable.cpp | 15 ++++++++++++++- src/hotspot/share/oops/symbol.cpp | 3 ++- src/hotspot/share/oops/symbol.hpp | 3 ++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/classfile/symbolTable.cpp b/src/hotspot/share/classfile/symbolTable.cpp index 6aec989ffd4..06f4c0f7592 100644 --- a/src/hotspot/share/classfile/symbolTable.cpp +++ b/src/hotspot/share/classfile/symbolTable.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, 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 @@ -301,7 +301,18 @@ unsigned int SymbolTable::hash_shared_symbol(const char* s, int len) { // entries in the symbol table during normal execution (only during // safepoints). +// Symbols should represent entities from the constant pool that are +// limited to 64K in length, but usage errors creep in allowing Symbols +// to be used for arbitrary strings. For debug builds we will assert if +// a string is too long, whereas product builds will truncate it. Symbol* SymbolTable::lookup(const char* name, int len, TRAPS) { + assert(len <= Symbol::max_length(), + "String length %d exceeds the maximum Symbol length of %d", len, Symbol::max_length()); + if (len > Symbol::max_length()) { + warning("A string \"%.80s ... %.80s\" exceeds the maximum Symbol " + "length of %d and has been truncated", name, (name + len - 80), Symbol::max_length()); + len = Symbol::max_length(); + } unsigned int hashValue = hash_symbol(name, len); int index = the_table()->hash_to_index(hashValue); @@ -432,6 +443,7 @@ void SymbolTable::add(ClassLoaderData* loader_data, const constantPoolHandle& cp for (int i=0; ihash_to_index(hashValues[i]); bool c_heap = !loader_data->is_the_null_class_loader_data(); + assert(lengths[i] <= Symbol::max_length(), "must be - these come from the constant pool"); Symbol* sym = table->basic_add(index, (u1*)names[i], lengths[i], hashValues[i], c_heap, CHECK); cp->symbol_at_put(cp_indices[i], sym); } @@ -456,6 +468,7 @@ Symbol* SymbolTable::basic_add(int index_arg, u1 *name, int len, unsigned int hashValue_arg, bool c_heap, TRAPS) { assert(!Universe::heap()->is_in_reserved(name), "proposed name of symbol must be stable"); + assert(len <= Symbol::max_length(), "caller should have ensured this"); // Don't allow symbols to be created which cannot fit in a Symbol*. if (len > Symbol::max_length()) { diff --git a/src/hotspot/share/oops/symbol.cpp b/src/hotspot/share/oops/symbol.cpp index aef6d37a612..45ae3d22968 100644 --- a/src/hotspot/share/oops/symbol.cpp +++ b/src/hotspot/share/oops/symbol.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, 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 @@ -37,6 +37,7 @@ #include "runtime/signature.hpp" Symbol::Symbol(const u1* name, int length, int refcount) { + assert(length <= max_length(), "SymbolTable should have caught this!"); _refcount = refcount; _length = length; _identity_hash = (short)os::random(); diff --git a/src/hotspot/share/oops/symbol.hpp b/src/hotspot/share/oops/symbol.hpp index 78d413736df..9c88b962487 100644 --- a/src/hotspot/share/oops/symbol.hpp +++ b/src/hotspot/share/oops/symbol.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, 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 @@ -128,6 +128,7 @@ class Symbol : public MetaspaceObj { return (int)heap_word_size(byte_size(length)); } + // Constructor is private for use only by SymbolTable. Symbol(const u1* name, int length, int refcount); void* operator new(size_t size, int len, TRAPS) throw(); void* operator new(size_t size, int len, Arena* arena, TRAPS) throw(); From c08f0f66c06913c569ad918436137661c116086b Mon Sep 17 00:00:00 2001 From: Alexei Voitylov Date: Wed, 19 Jun 2024 11:04:19 +0000 Subject: [PATCH 03/12] 8325600: Better symbol storage Reviewed-by: mbalao Backport-of: da06689bf6fde7b6dd8efc2f0c39fc95adcdb69d --- src/hotspot/share/classfile/symbolTable.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/classfile/symbolTable.cpp b/src/hotspot/share/classfile/symbolTable.cpp index 06f4c0f7592..edc7ed8601d 100644 --- a/src/hotspot/share/classfile/symbolTable.cpp +++ b/src/hotspot/share/classfile/symbolTable.cpp @@ -302,10 +302,10 @@ unsigned int SymbolTable::hash_shared_symbol(const char* s, int len) { // safepoints). // Symbols should represent entities from the constant pool that are -// limited to 64K in length, but usage errors creep in allowing Symbols +// limited to <64K in length, but usage errors creep in allowing Symbols // to be used for arbitrary strings. For debug builds we will assert if // a string is too long, whereas product builds will truncate it. -Symbol* SymbolTable::lookup(const char* name, int len, TRAPS) { +static int check_length(const char* name, int len) { assert(len <= Symbol::max_length(), "String length %d exceeds the maximum Symbol length of %d", len, Symbol::max_length()); if (len > Symbol::max_length()) { @@ -313,6 +313,11 @@ Symbol* SymbolTable::lookup(const char* name, int len, TRAPS) { "length of %d and has been truncated", name, (name + len - 80), Symbol::max_length()); len = Symbol::max_length(); } + return len; +} + +Symbol* SymbolTable::lookup(const char* name, int len, TRAPS) { + len = check_length(name, len); unsigned int hashValue = hash_symbol(name, len); int index = the_table()->hash_to_index(hashValue); @@ -452,7 +457,8 @@ void SymbolTable::add(ClassLoaderData* loader_data, const constantPoolHandle& cp Symbol* SymbolTable::new_permanent_symbol(const char* name, TRAPS) { unsigned int hash; - Symbol* result = SymbolTable::lookup_only((char*)name, (int)strlen(name), hash); + int len = check_length(name, (int)strlen(name)); + Symbol* result = SymbolTable::lookup_only((char*)name, len, hash); if (result != NULL) { return result; } @@ -461,7 +467,7 @@ Symbol* SymbolTable::new_permanent_symbol(const char* name, TRAPS) { SymbolTable* table = the_table(); int index = table->hash_to_index(hash); - return table->basic_add(index, (u1*)name, (int)strlen(name), hash, false, THREAD); + return table->basic_add(index, (u1*)name, len, hash, false, THREAD); } Symbol* SymbolTable::basic_add(int index_arg, u1 *name, int len, From 31035f07d3cd4a7a06767674f16b8459ae66542e Mon Sep 17 00:00:00 2001 From: Yuri Nesterenko Date: Tue, 2 Jul 2024 16:34:12 +0400 Subject: [PATCH 04/12] 8320097: Improve Image transformations Reviewed-by: mbalao Backport-of: 1401634b21b76db90291011bcae68c461742e687 --- make/autoconf/flags-cflags.m4 | 2 +- make/lib/Awt2dLibraries.gmk | 4 +--- .../share/classes/sun/java2d/pipe/DrawImage.java | 9 ++++++++- .../share/native/libawt/java2d/loops/TransformHelper.c | 4 ++-- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4 index 522204d78c7..6a6db9089eb 100644 --- a/make/autoconf/flags-cflags.m4 +++ b/make/autoconf/flags-cflags.m4 @@ -556,7 +556,7 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_HELPER], TOOLCHAIN_CFLAGS="-errshort=tags" TOOLCHAIN_CFLAGS_JDK="-mt $TOOLCHAIN_FLAGS" - TOOLCHAIN_CFLAGS_JDK_CONLY="-xc99=%none -xCC -Xa -W0,-noglobal $TOOLCHAIN_CFLAGS" # C only + TOOLCHAIN_CFLAGS_JDK_CONLY="-xCC -Xa -W0,-noglobal $TOOLCHAIN_CFLAGS" # C only TOOLCHAIN_CFLAGS_JDK_CXXONLY="-features=no%except -norunpath -xnolib" # CXX only TOOLCHAIN_CFLAGS_JVM="-template=no%extdef -features=no%split_init \ -library=stlport4 -mt -features=no%except $TOOLCHAIN_FLAGS" diff --git a/make/lib/Awt2dLibraries.gmk b/make/lib/Awt2dLibraries.gmk index 37f35dd3214..d50e5fa4cf0 100644 --- a/make/lib/Awt2dLibraries.gmk +++ b/make/lib/Awt2dLibraries.gmk @@ -358,8 +358,6 @@ endif # The fast floor code loses precision. LCMS_CFLAGS=-DCMS_DONT_USE_FAST_FLOOR -LCMS_CFLAGS_JDKLIB := $(filter-out -xc99=%none, $(CFLAGS_JDKLIB)) - ifeq ($(USE_EXTERNAL_LCMS), true) # If we're using an external library, we'll just need the wrapper part. # By including it explicitly, all other files will be excluded. @@ -382,7 +380,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBLCMS, \ NAME := lcms, \ INCLUDE_FILES := $(BUILD_LIBLCMS_INCLUDE_FILES), \ OPTIMIZATION := HIGHEST, \ - CFLAGS := $(LCMS_CFLAGS_JDKLIB) \ + CFLAGS := $(CFLAGS_JDKLIB) \ $(LCMS_CFLAGS), \ CFLAGS_solaris := -xc99=no_lib, \ CFLAGS_windows := -DCMS_IS_WINDOWS_, \ diff --git a/src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java b/src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java index 1c9665cf563..274cc5c0a47 100644 --- a/src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java +++ b/src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2024, 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 @@ -367,6 +367,13 @@ protected void renderImageXform(SunGraphics2D sg, Image img, final AffineTransform itx; try { itx = tx.createInverse(); + double[] mat = new double[6]; + itx.getMatrix(mat); + for (double d : mat) { + if (!Double.isFinite(d)) { + return; + } + } } catch (final NoninvertibleTransformException ignored) { // Non-invertible transform means no output return; diff --git a/src/java.desktop/share/native/libawt/java2d/loops/TransformHelper.c b/src/java.desktop/share/native/libawt/java2d/loops/TransformHelper.c index dc4ab2fe3e6..c7e2832c09d 100644 --- a/src/java.desktop/share/native/libawt/java2d/loops/TransformHelper.c +++ b/src/java.desktop/share/native/libawt/java2d/loops/TransformHelper.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2024, 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 @@ -120,7 +120,7 @@ TransformInterpFunc *pBicubicFunc = BicubicInterp; /* We reject coordinates not less than 1<<30 so that the distance between */ /* any 2 of them is less than 1<<31 which would overflow into the sign */ /* bit of a signed long value used to represent fixed point coordinates. */ -#define TX_FIXED_UNSAFE(v) (fabs(v) >= (1<<30)) +#define TX_FIXED_UNSAFE(v) (isinf(v) || isnan(v) || fabs(v) >= (1<<30)) static jboolean checkOverflow(jint dxoff, jint dyoff, SurfaceDataBounds *pBounds, From 4efcece1f87abc80a74acd19d5be573e18aa72a6 Mon Sep 17 00:00:00 2001 From: Alexey Bakhtin Date: Fri, 10 May 2024 13:49:58 -0700 Subject: [PATCH 05/12] 8322106: Enhance Pack 200 loading Reviewed-by: mbalao --- .../share/native/common-unpack/defines.h | 6 +- .../share/native/common-unpack/unpack.cpp | 142 ++++++++++-------- .../share/native/common-unpack/unpack.h | 26 ++-- .../share/native/common-unpack/utils.cpp | 18 +-- .../share/native/common-unpack/utils.h | 2 +- 5 files changed, 103 insertions(+), 91 deletions(-) diff --git a/src/jdk.pack/share/native/common-unpack/defines.h b/src/jdk.pack/share/native/common-unpack/defines.h index e2235fb7a1f..ee2f555ea0c 100644 --- a/src/jdk.pack/share/native/common-unpack/defines.h +++ b/src/jdk.pack/share/native/common-unpack/defines.h @@ -86,9 +86,9 @@ extern int assert_failed(const char*); #define lengthof(array) (sizeof(array)/sizeof(array[0])) -#define NEW(T, n) (T*) must_malloc((int)(scale_size(n, sizeof(T)))) -#define U_NEW(T, n) (T*) u->alloc(scale_size(n, sizeof(T))) -#define T_NEW(T, n) (T*) u->temp_alloc(scale_size(n, sizeof(T))) +#define NEW(T, n) (T*) must_calloc(n, sizeof(T)) +#define U_NEW(T, n) (T*) u->calloc(n, sizeof(T)) +#define T_NEW(T, n) (T*) u->temp_calloc(n, sizeof(T)) // Dealing with big-endian arch #ifdef _BIG_ENDIAN diff --git a/src/jdk.pack/share/native/common-unpack/unpack.cpp b/src/jdk.pack/share/native/common-unpack/unpack.cpp index 05f04f2942a..878b6c89375 100644 --- a/src/jdk.pack/share/native/common-unpack/unpack.cpp +++ b/src/jdk.pack/share/native/common-unpack/unpack.cpp @@ -510,19 +510,20 @@ static int hash_probes[] = {0, 0}; enum { CHUNK = (1 << 14), SMALL = (1 << 9) }; -// Call malloc. Try to combine small blocks and free much later. -void* unpacker::alloc_heap(size_t size, bool smallOK, bool temp) { - if (!smallOK || size > SMALL) { - void* res = must_malloc((int)size); +// Call calloc. Try to combine small blocks and free much later. +void* unpacker::calloc_heap(size_t count, size_t size, bool smallOK, bool temp) { + size_t ssize = scale_size(count, size); + if (!smallOK || ssize > SMALL) { + void* res = must_calloc(count, size); (temp ? &tmallocs : &mallocs)->add(res); return res; } fillbytes& xsmallbuf = *(temp ? &tsmallbuf : &smallbuf); - if (!xsmallbuf.canAppend(size+1)) { + if (!xsmallbuf.canAppend(ssize+1)) { xsmallbuf.init(CHUNK); (temp ? &tmallocs : &mallocs)->add(xsmallbuf.base()); } - int growBy = (int)size; + int growBy = (int)ssize; growBy += -growBy & 7; // round up mod 8 return xsmallbuf.grow(growBy); } @@ -987,11 +988,6 @@ void cpool::init(unpacker* u_, int counts[CONSTANT_Limit]) { tag_index[tag].init(tag_count[tag], cpMap, tag); } - // Initialize *all* our entries once - for (uint i = 0 ; i < maxentries ; i++) { - entries[i].outputIndex = REQUESTED_NONE; - } - initGroupIndexes(); // Initialize hashTab to a generous power-of-two size. uint pow2 = 1; @@ -1061,7 +1057,7 @@ static int compare_Utf8_chars(bytes& b1, bytes& b2) { // Cf. PackageReader.readUtf8Bands local_inline -void unpacker::read_Utf8_values(entry* cpMap, int len) { +void unpacker::read_Utf8_values(entry* cpMap, int len, byte tag) { // Implicit first Utf8 string is the empty string. enum { // certain bands begin with implicit zeroes @@ -1087,10 +1083,11 @@ void unpacker::read_Utf8_values(entry* cpMap, int len) { int nbigsuf = 0; fillbytes charbuf; // buffer to allocate small strings charbuf.init(); - // Third band: Read the char values in the unshared suffixes: cp_Utf8_chars.readData(cp_Utf8_suffix.getIntTotal()); for (i = 0; i < len; i++) { + cp.initValues(cpMap[i], tag, i); + int suffix = (i < SUFFIX_SKIP_1)? 0: cp_Utf8_suffix.getInt(); if (suffix < 0) { abort("bad utf8 suffix"); @@ -1239,28 +1236,32 @@ void unpacker::read_Utf8_values(entry* cpMap, int len) { } local_inline -void unpacker::read_single_words(band& cp_band, entry* cpMap, int len) { +void unpacker::read_single_words(band& cp_band, entry* cpMap, int len, byte tag, int loadable_base) { cp_band.readData(len); for (int i = 0; i < len; i++) { - cpMap[i].value.i = cp_band.getInt(); // coding handles signs OK + entry& e = cpMap[i]; + cp.initValues(e, tag, i, loadable_base); + e.value.i = cp_band.getInt(); // coding handles signs OK } } maybe_inline -void unpacker::read_double_words(band& cp_bands, entry* cpMap, int len) { +void unpacker::read_double_words(band& cp_bands, entry* cpMap, int len, byte tag, int loadable_base) { band& cp_band_hi = cp_bands; band& cp_band_lo = cp_bands.nextBand(); cp_band_hi.readData(len); cp_band_lo.readData(len); for (int i = 0; i < len; i++) { - cpMap[i].value.l = cp_band_hi.getLong(cp_band_lo, true); + entry& e = cpMap[i]; + cp.initValues(e, tag, i, loadable_base); + e.value.l = cp_band_hi.getLong(cp_band_lo, true); } //cp_band_hi.done(); //cp_band_lo.done(); } maybe_inline -void unpacker::read_single_refs(band& cp_band, byte refTag, entry* cpMap, int len) { +void unpacker::read_single_refs(band& cp_band, byte refTag, entry* cpMap, int len, byte tag, int loadable_base) { assert(refTag == CONSTANT_Utf8); cp_band.setIndexByTag(refTag); cp_band.readData(len); @@ -1268,6 +1269,7 @@ void unpacker::read_single_refs(band& cp_band, byte refTag, entry* cpMap, int le int indexTag = (cp_band.bn == e_cp_Class) ? CONSTANT_Class : 0; for (int i = 0; i < len; i++) { entry& e = cpMap[i]; + cp.initValues(e, tag, i, loadable_base); e.refs = U_NEW(entry*, e.nrefs = 1); entry* utf = cp_band.getRef(); CHECK; @@ -1288,7 +1290,7 @@ void unpacker::read_single_refs(band& cp_band, byte refTag, entry* cpMap, int le maybe_inline void unpacker::read_double_refs(band& cp_band, byte ref1Tag, byte ref2Tag, - entry* cpMap, int len) { + entry* cpMap, int len, byte tag) { band& cp_band1 = cp_band; band& cp_band2 = cp_band.nextBand(); cp_band1.setIndexByTag(ref1Tag); @@ -1298,6 +1300,7 @@ void unpacker::read_double_refs(band& cp_band, byte ref1Tag, byte ref2Tag, CHECK; for (int i = 0; i < len; i++) { entry& e = cpMap[i]; + cp.initValues(e, tag, i); e.refs = U_NEW(entry*, e.nrefs = 2); e.refs[0] = cp_band1.getRef(); CHECK; @@ -1310,7 +1313,7 @@ void unpacker::read_double_refs(band& cp_band, byte ref1Tag, byte ref2Tag, // Cf. PackageReader.readSignatureBands maybe_inline -void unpacker::read_signature_values(entry* cpMap, int len) { +void unpacker::read_signature_values(entry* cpMap, int len, byte tag) { cp_Signature_form.setIndexByTag(CONSTANT_Utf8); cp_Signature_form.readData(len); CHECK; @@ -1318,6 +1321,7 @@ void unpacker::read_signature_values(entry* cpMap, int len) { int i; for (i = 0; i < len; i++) { entry& e = cpMap[i]; + cp.initValues(e, tag, i); entry& form = *cp_Signature_form.getRef(); CHECK; int nc = 0; @@ -1354,7 +1358,7 @@ void unpacker::checkLegacy(const char* name) { } maybe_inline -void unpacker::read_method_handle(entry* cpMap, int len) { +void unpacker::read_method_handle(entry* cpMap, int len, byte tag, int loadable_base) { if (len > 0) { checkLegacy(cp_MethodHandle_refkind.name); } @@ -1363,6 +1367,7 @@ void unpacker::read_method_handle(entry* cpMap, int len) { cp_MethodHandle_member.readData(len); for (int i = 0 ; i < len ; i++) { entry& e = cpMap[i]; + cp.initValues(e, tag, i, loadable_base); e.value.i = cp_MethodHandle_refkind.getInt(); e.refs = U_NEW(entry*, e.nrefs = 1); e.refs[0] = cp_MethodHandle_member.getRef(); @@ -1371,7 +1376,7 @@ void unpacker::read_method_handle(entry* cpMap, int len) { } maybe_inline -void unpacker::read_method_type(entry* cpMap, int len) { +void unpacker::read_method_type(entry* cpMap, int len, byte tag, int loadable_base) { if (len > 0) { checkLegacy(cp_MethodType.name); } @@ -1379,6 +1384,7 @@ void unpacker::read_method_type(entry* cpMap, int len) { cp_MethodType.readData(len); for (int i = 0 ; i < len ; i++) { entry& e = cpMap[i]; + cp.initValues(e, tag, i, loadable_base); e.refs = U_NEW(entry*, e.nrefs = 1); e.refs[0] = cp_MethodType.getRef(); CHECK; @@ -1386,7 +1392,7 @@ void unpacker::read_method_type(entry* cpMap, int len) { } maybe_inline -void unpacker::read_bootstrap_methods(entry* cpMap, int len) { +void unpacker::read_bootstrap_methods(entry* cpMap, int len, byte tag) { if (len > 0) { checkLegacy(cp_BootstrapMethod_ref.name); } @@ -1400,6 +1406,7 @@ void unpacker::read_bootstrap_methods(entry* cpMap, int len) { for (int i = 0; i < len; i++) { entry& e = cpMap[i]; int argc = cp_BootstrapMethod_arg_count.getInt(); + cp.initValues(e, tag, i); e.value.i = argc; e.refs = U_NEW(entry*, e.nrefs = argc + 1); e.refs[0] = cp_BootstrapMethod_ref.getRef(); @@ -1409,23 +1416,22 @@ void unpacker::read_bootstrap_methods(entry* cpMap, int len) { } } } + +static bool isLoadableValue(int tag); // Cf. PackageReader.readConstantPool void unpacker::read_cp() { byte* rp0 = rp; - - int i; + uint cpentries = 0; + int loadable_count = 0; for (int k = 0; k < (int)N_TAGS_IN_ORDER; k++) { byte tag = TAGS_IN_ORDER[k]; int len = cp.tag_count[tag]; int base = cp.tag_base[tag]; + int loadable_base = -1; PRINTCR((1,"Reading %d %s entries...", len, NOT_PRODUCT(TAG_NAME[tag])+0)); entry* cpMap = &cp.entries[base]; - for (i = 0; i < len; i++) { - cpMap[i].tag = tag; - cpMap[i].inord = i; - } // Initialize the tag's CP index right away, since it might be needed // in the next pass to initialize the CP for another tag. #ifndef PRODUCT @@ -1435,67 +1441,73 @@ void unpacker::read_cp() { assert(ix->base1 == cpMap); #endif + if (isLoadableValue(tag)) { + loadable_base = loadable_count; + loadable_count += len; + } + + cpentries += len; switch (tag) { case CONSTANT_Utf8: - read_Utf8_values(cpMap, len); + read_Utf8_values(cpMap, len, tag); break; case CONSTANT_Integer: - read_single_words(cp_Int, cpMap, len); + read_single_words(cp_Int, cpMap, len, tag, loadable_base); break; case CONSTANT_Float: - read_single_words(cp_Float, cpMap, len); + read_single_words(cp_Float, cpMap, len, tag, loadable_base); break; case CONSTANT_Long: - read_double_words(cp_Long_hi /*& cp_Long_lo*/, cpMap, len); + read_double_words(cp_Long_hi /*& cp_Long_lo*/, cpMap, len, tag, loadable_base); break; case CONSTANT_Double: - read_double_words(cp_Double_hi /*& cp_Double_lo*/, cpMap, len); + read_double_words(cp_Double_hi /*& cp_Double_lo*/, cpMap, len, tag, loadable_base); break; case CONSTANT_String: - read_single_refs(cp_String, CONSTANT_Utf8, cpMap, len); + read_single_refs(cp_String, CONSTANT_Utf8, cpMap, len, tag, loadable_base); break; case CONSTANT_Class: - read_single_refs(cp_Class, CONSTANT_Utf8, cpMap, len); + read_single_refs(cp_Class, CONSTANT_Utf8, cpMap, len, tag, loadable_base); break; case CONSTANT_Signature: - read_signature_values(cpMap, len); + read_signature_values(cpMap, len, tag); break; case CONSTANT_NameandType: read_double_refs(cp_Descr_name /*& cp_Descr_type*/, CONSTANT_Utf8, CONSTANT_Signature, - cpMap, len); + cpMap, len, tag); break; case CONSTANT_Fieldref: read_double_refs(cp_Field_class /*& cp_Field_desc*/, CONSTANT_Class, CONSTANT_NameandType, - cpMap, len); + cpMap, len, tag); break; case CONSTANT_Methodref: read_double_refs(cp_Method_class /*& cp_Method_desc*/, CONSTANT_Class, CONSTANT_NameandType, - cpMap, len); + cpMap, len, tag); break; case CONSTANT_InterfaceMethodref: read_double_refs(cp_Imethod_class /*& cp_Imethod_desc*/, CONSTANT_Class, CONSTANT_NameandType, - cpMap, len); + cpMap, len, tag); break; case CONSTANT_MethodHandle: // consumes cp_MethodHandle_refkind and cp_MethodHandle_member - read_method_handle(cpMap, len); + read_method_handle(cpMap, len, tag, loadable_base); break; case CONSTANT_MethodType: // consumes cp_MethodType - read_method_type(cpMap, len); + read_method_type(cpMap, len, tag, loadable_base); break; case CONSTANT_InvokeDynamic: read_double_refs(cp_InvokeDynamic_spec, CONSTANT_BootstrapMethod, CONSTANT_NameandType, - cpMap, len); + cpMap, len, tag); break; case CONSTANT_BootstrapMethod: // consumes cp_BootstrapMethod_ref, cp_BootstrapMethod_arg_count and cp_BootstrapMethod_arg - read_bootstrap_methods(cpMap, len); + read_bootstrap_methods(cpMap, len, tag); break; default: assert(false); @@ -1504,6 +1516,11 @@ void unpacker::read_cp() { CHECK; } + // Initialize extra entries + for (; cpentries < cp.maxentries; cpentries++) { + cp.entries[cpentries].outputIndex = REQUESTED_NONE; + } + cp.expandSignatures(); CHECK; cp.initMemberIndexes(); @@ -3385,25 +3402,15 @@ bool isLoadableValue(int tag) { return false; } } -/* - * this method can be used to size an array using null as the parameter, - * thereafter can be reused to initialize the array using a valid pointer - * as a parameter. - */ -int cpool::initLoadableValues(entry** loadable_entries) { - int loadable_count = 0; - for (int i = 0; i < (int)N_TAGS_IN_ORDER; i++) { - int tag = TAGS_IN_ORDER[i]; - if (!isLoadableValue(tag)) - continue; - if (loadable_entries != NULL) { - for (int n = 0 ; n < tag_count[tag] ; n++) { - loadable_entries[loadable_count + n] = &entries[tag_base[tag] + n]; - } - } - loadable_count += tag_count[tag]; + +void cpool::initValues(entry& e, byte tag, int n, int loadable_base) { + e.tag = tag; + e.inord = n; + e.outputIndex = REQUESTED_NONE; + if (loadable_base >= 0) { + entry** loadable_entries = tag_group_index[CONSTANT_LoadableValue - CONSTANT_All].base2; + loadable_entries[loadable_base + n] = &e; } - return loadable_count; } // Initialize various views into the constant pool. @@ -3418,9 +3425,14 @@ void cpool::initGroupIndexes() { tag_group_index[CONSTANT_All - CONSTANT_All].init(all_count, all_entries, CONSTANT_All); // Initialize LoadableValues - int loadable_count = initLoadableValues(NULL); + int loadable_count = 0; + for (int i = 0; i < (int)N_TAGS_IN_ORDER; i++) { + int tag = TAGS_IN_ORDER[i]; + if (isLoadableValue(tag)) { + loadable_count += tag_count[tag]; + } + } entry** loadable_entries = U_NEW(entry*, loadable_count); - initLoadableValues(loadable_entries); tag_group_count[CONSTANT_LoadableValue - CONSTANT_All] = loadable_count; tag_group_index[CONSTANT_LoadableValue - CONSTANT_All].init(loadable_count, loadable_entries, CONSTANT_LoadableValue); diff --git a/src/jdk.pack/share/native/common-unpack/unpack.h b/src/jdk.pack/share/native/common-unpack/unpack.h index 0357ff457b1..dd9d641da1d 100644 --- a/src/jdk.pack/share/native/common-unpack/unpack.h +++ b/src/jdk.pack/share/native/common-unpack/unpack.h @@ -130,7 +130,7 @@ struct cpool { void expandSignatures(); void initGroupIndexes(); void initMemberIndexes(); - int initLoadableValues(entry** loadable_entries); + void initValues(entry& e, byte tag, int n, int loadable_base=-1); void computeOutputOrder(); void computeOutputIndexes(); @@ -411,9 +411,9 @@ struct unpacker { file* get_next_file(); // returns null on last file // General purpose methods - void* alloc(size_t size) { return alloc_heap(size, true); } - void* temp_alloc(size_t size) { return alloc_heap(size, true, true); } - void* alloc_heap(size_t size, bool smallOK = false, bool temp = false); + void* calloc(size_t count, size_t size) { return calloc_heap(count, size, true); } + void* temp_calloc(size_t count, size_t size) { return calloc_heap(count, size, true, true); } + void* calloc_heap(size_t count, size_t size, bool smallOK = false, bool temp = false); void saveTo(bytes& b, const char* str) { saveTo(b, (byte*)str, strlen(str)); } void saveTo(bytes& b, bytes& data) { saveTo(b, data.ptr, data.len); } void saveTo(bytes& b, byte* ptr, size_t len); //{ b.ptr = U_NEW...} @@ -494,15 +494,15 @@ struct unpacker { void read_bcs(); void read_bc_ops(); void read_files(); - void read_Utf8_values(entry* cpMap, int len); - void read_single_words(band& cp_band, entry* cpMap, int len); - void read_double_words(band& cp_bands, entry* cpMap, int len); - void read_single_refs(band& cp_band, byte refTag, entry* cpMap, int len); - void read_double_refs(band& cp_band, byte ref1Tag, byte ref2Tag, entry* cpMap, int len); - void read_signature_values(entry* cpMap, int len); - void read_method_handle(entry* cpMap, int len); - void read_method_type(entry* cpMap, int len); - void read_bootstrap_methods(entry* cpMap, int len); + void read_Utf8_values(entry* cpMap, int len, byte tag); + void read_single_words(band& cp_band, entry* cpMap, int len, byte tag, int loadable_base); + void read_double_words(band& cp_bands, entry* cpMap, int len, byte tag, int loadable_base); + void read_single_refs(band& cp_band, byte refTag, entry* cpMap, int len, byte tag, int loadable_base); + void read_double_refs(band& cp_band, byte ref1Tag, byte ref2Tag, entry* cpMap, int len, byte tag); + void read_signature_values(entry* cpMap, int len, byte tag); + void read_method_handle(entry* cpMap, int len, byte tag, int loadable_base); + void read_method_type(entry* cpMap, int len, byte tag, int loadable_base); + void read_bootstrap_methods(entry* cpMap, int len, byte tag); }; inline void cpool::abort(const char* msg) { u->abort(msg); } diff --git a/src/jdk.pack/share/native/common-unpack/utils.cpp b/src/jdk.pack/share/native/common-unpack/utils.cpp index a14126c1952..5838dc40e45 100644 --- a/src/jdk.pack/share/native/common-unpack/utils.cpp +++ b/src/jdk.pack/share/native/common-unpack/utils.cpp @@ -46,19 +46,19 @@ #include "unpack.h" -void* must_malloc(size_t size) { - size_t msize = size; +void* must_calloc(size_t count, size_t size) { + size_t msize = scale_size(count, size); #ifdef USE_MTRACE - if (msize >= 0 && msize < sizeof(int)) - msize = sizeof(int); // see 0xbaadf00d below + if (msize >= 0 && msize < sizeof(int)) { + size = msize = sizeof(int); // see 0xbaadf00d below + count = 1; + } #endif - void* ptr = (msize > PSIZE_MAX || msize <= 0) ? null : malloc(msize); - if (ptr != null) { - memset(ptr, 0, size); - } else { + void* ptr = (msize > PSIZE_MAX || msize <= 0) ? null : calloc(count, size); + if (ptr == null) { unpack_abort(ERROR_ENOMEM); } - mtrace('m', ptr, size); + mtrace('m', ptr, msize); return ptr; } diff --git a/src/jdk.pack/share/native/common-unpack/utils.h b/src/jdk.pack/share/native/common-unpack/utils.h index 5cd11edd7e9..2cf556eae03 100644 --- a/src/jdk.pack/share/native/common-unpack/utils.h +++ b/src/jdk.pack/share/native/common-unpack/utils.h @@ -25,7 +25,7 @@ //Definitions of our util functions -void* must_malloc(size_t size); +void* must_calloc(size_t count, size_t size); #ifndef USE_MTRACE #define mtrace(c, ptr, size) #else From 806882b1c8e2e0d067d870ebe08965d959ae4269 Mon Sep 17 00:00:00 2001 From: Martin Balao Date: Wed, 24 Jan 2024 06:21:44 +0000 Subject: [PATCH 06/12] 8323231: Improve array management Reviewed-by: mbaesken Backport-of: cf20364d0cb3f182880ee91c7fb023615e27becf --- src/hotspot/share/c1/c1_RangeCheckElimination.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/c1/c1_RangeCheckElimination.cpp b/src/hotspot/share/c1/c1_RangeCheckElimination.cpp index cf5b81e032d..4339e1cc82c 100644 --- a/src/hotspot/share/c1/c1_RangeCheckElimination.cpp +++ b/src/hotspot/share/c1/c1_RangeCheckElimination.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, 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 @@ -446,14 +446,14 @@ void RangeCheckEliminator::in_block_motion(BlockBegin *block, AccessIndexedList if (c) { jint value = c->type()->as_IntConstant()->value(); - if (value != min_jint) { - if (ao->op() == Bytecodes::_isub) { - value = -value; - } + if (ao->op() == Bytecodes::_iadd) { base = java_add(base, value); - last_integer = base; - last_instruction = other; + } else { + assert(ao->op() == Bytecodes::_isub, "unexpected bytecode"); + base = java_subtract(base, value); } + last_integer = base; + last_instruction = other; index = other; } else { break; From 0533bad7dbe58586491fc12c824532bce38f47cb Mon Sep 17 00:00:00 2001 From: Alexei Voitylov Date: Tue, 11 Jun 2024 11:29:35 +0000 Subject: [PATCH 07/12] 8323390: Enhance mask blit functionality Reviewed-by: mbalao Backport-of: 895893f2fbf1c521d4a263d505f0ecbda8d2eeea --- .../classes/sun/java2d/SunGraphics2D.java | 42 +++++++++++-------- .../share/native/libawt/java2d/SurfaceData.h | 11 ++++- .../native/libawt/java2d/loops/MaskBlit.c | 16 ++++++- 3 files changed, 49 insertions(+), 20 deletions(-) diff --git a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java index 37054ca25dc..573afc12977 100644 --- a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java +++ b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2024, 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 @@ -2153,27 +2153,33 @@ private void doCopyArea(int x, int y, int w, int h, int dx, int dy) { } Blit ob = lastCAblit; - if (dy == 0 && dx > 0 && dx < w) { - while (w > 0) { - int partW = Math.min(w, dx); - w -= partW; - int sx = x + w; - ob.Blit(theData, theData, comp, clip, - sx, y, sx+dx, y+dy, partW, h); + try { + if (dy == 0 && dx > 0 && dx < w) { + while (w > 0) { + int partW = Math.min(w, dx); + w -= partW; + int sx = Math.addExact(x, w); + ob.Blit(theData, theData, comp, clip, + sx, y, sx+dx, y+dy, partW, h); + } + return; } - return; - } - if (dy > 0 && dy < h && dx > -w && dx < w) { - while (h > 0) { - int partH = Math.min(h, dy); - h -= partH; - int sy = y + h; - ob.Blit(theData, theData, comp, clip, - x, sy, x+dx, sy+dy, w, partH); + if (dy > 0 && dy < h && dx > -w && dx < w) { + while (h > 0) { + int partH = Math.min(h, dy); + h -= partH; + int sy = Math.addExact(y, h); + ob.Blit(theData, theData, comp, clip, + x, sy, Math.addExact(x, dx), sy+dy, w, partH); + } + return; } + ob.Blit(theData, theData, comp, clip, x, y, + Math.addExact(x, dx), Math.addExact(y, dy), w, h); + } catch (ArithmeticException ex) { + // We are hitting integer overflow in Math.addExact() return; } - ob.Blit(theData, theData, comp, clip, x, y, x+dx, y+dy, w, h); } /* diff --git a/src/java.desktop/share/native/libawt/java2d/SurfaceData.h b/src/java.desktop/share/native/libawt/java2d/SurfaceData.h index 48504629703..f8fd8e62a82 100644 --- a/src/java.desktop/share/native/libawt/java2d/SurfaceData.h +++ b/src/java.desktop/share/native/libawt/java2d/SurfaceData.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, 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 @@ -32,6 +32,7 @@ #define _Included_SurfaceData #include +#include #ifdef __cplusplus extern "C" { @@ -53,6 +54,14 @@ typedef struct { #define SD_RASINFO_PRIVATE_SIZE 64 +#define UNSAFE_TO_ADD(a, b) \ + (((a >= 0) && (b >= 0) && (a > (INT_MAX - b))) || \ + ((a < 0) && (b < 0) && (a < (INT_MIN - b)))) \ + +#define UNSAFE_TO_SUB(a, b) \ + (((b >= 0) && (a < 0) && (a < (INT_MIN + b))) || \ + ((b < 0) && (a >= 0) && (-b > (INT_MAX - a)))) \ + /* * The SurfaceDataRasInfo structure is used to pass in and return various * pieces of information about the destination drawable. In particular: diff --git a/src/java.desktop/share/native/libawt/java2d/loops/MaskBlit.c b/src/java.desktop/share/native/libawt/java2d/loops/MaskBlit.c index 21b716e3bcd..e8c8765dd2c 100644 --- a/src/java.desktop/share/native/libawt/java2d/loops/MaskBlit.c +++ b/src/java.desktop/share/native/libawt/java2d/loops/MaskBlit.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, 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 @@ -68,14 +68,28 @@ Java_sun_java2d_loops_MaskBlit_MaskBlit return; } + if (width <= 0 || height <= 0) { + return; + } + srcInfo.bounds.x1 = srcx; srcInfo.bounds.y1 = srcy; + if (UNSAFE_TO_ADD(srcx, width) || + UNSAFE_TO_ADD(srcy, height) || + UNSAFE_TO_ADD(dstx, width) || + UNSAFE_TO_ADD(dsty, height)) { + return; + } srcInfo.bounds.x2 = srcx + width; srcInfo.bounds.y2 = srcy + height; dstInfo.bounds.x1 = dstx; dstInfo.bounds.y1 = dsty; dstInfo.bounds.x2 = dstx + width; dstInfo.bounds.y2 = dsty + height; + if (UNSAFE_TO_SUB(srcx, dstx) || + UNSAFE_TO_SUB(srcy, dsty)) { + return; + } srcx -= dstx; srcy -= dsty; SurfaceData_IntersectBounds(&dstInfo.bounds, &clipInfo.bounds); From 3d25b4a34aced8fa14a28d8c45fde6fb61a070c0 Mon Sep 17 00:00:00 2001 From: Yuri Nesterenko Date: Fri, 15 Mar 2024 15:35:52 +0000 Subject: [PATCH 08/12] 8324559: Improve 2D image handling Reviewed-by: mbalao Backport-of: 1a5a44019871c411148ca85d0491cc68d2256e4e --- .../share/native/libawt/java2d/loops/MaskFill.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/java.desktop/share/native/libawt/java2d/loops/MaskFill.c b/src/java.desktop/share/native/libawt/java2d/loops/MaskFill.c index 354934069c0..fe0bc406860 100644 --- a/src/java.desktop/share/native/libawt/java2d/loops/MaskFill.c +++ b/src/java.desktop/share/native/libawt/java2d/loops/MaskFill.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, 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 @@ -467,7 +467,7 @@ storePgram(EdgeInfo *pLeftEdge, EdgeInfo *pRightEdge, #define INSERT_ACCUM(pACCUM, IMIN, IMAX, X0, Y0, X1, Y1, CX1, CX2, MULT) \ do { \ jdouble xmid = ((X0) + (X1)) * 0.5; \ - if (xmid <= (CX2)) { \ + if (xmid < (CX2)) { \ jdouble sliceh = ((Y1) - (Y0)); \ jdouble slicearea; \ jint i; \ @@ -556,7 +556,7 @@ fillAAPgram(NativePrimitive *pPrim, SurfaceDataRasInfo *pRasInfo, jint cy2 = pRasInfo->bounds.y2; jint width = cx2 - cx1; EdgeInfo edges[4]; - jfloat localaccum[MASK_BUF_LEN + 1]; + jfloat localaccum[MASK_BUF_LEN + 2]; jfloat *pAccum; if (!storePgram(edges + 0, edges + 2, @@ -568,12 +568,12 @@ fillAAPgram(NativePrimitive *pPrim, SurfaceDataRasInfo *pRasInfo, } pAccum = ((width > MASK_BUF_LEN) - ? malloc((width + 1) * sizeof(jfloat)) + ? malloc((width + 2) * sizeof(jfloat)) : localaccum); if (pAccum == NULL) { return; } - memset(pAccum, 0, (width+1) * sizeof(jfloat)); + memset(pAccum, 0, (width + 2) * sizeof(jfloat)); while (cy1 < cy2) { jint lmin, lmax, rmin, rmax; @@ -794,7 +794,7 @@ drawAAPgram(NativePrimitive *pPrim, SurfaceDataRasInfo *pRasInfo, jint cy2 = pRasInfo->bounds.y2; jint width = cx2 - cx1; EdgeInfo edges[8]; - jfloat localaccum[MASK_BUF_LEN + 1]; + jfloat localaccum[MASK_BUF_LEN + 2]; jfloat *pAccum; if (!storePgram(edges + 0, edges + 6, @@ -815,12 +815,12 @@ drawAAPgram(NativePrimitive *pPrim, SurfaceDataRasInfo *pRasInfo, JNI_TRUE); pAccum = ((width > MASK_BUF_LEN) - ? malloc((width + 1) * sizeof(jfloat)) + ? malloc((width + 2) * sizeof(jfloat)) : localaccum); if (pAccum == NULL) { return; } - memset(pAccum, 0, (width+1) * sizeof(jfloat)); + memset(pAccum, 0, (width + 2) * sizeof(jfloat)); while (cy1 < cy2) { jint lmin, lmax, rmin, rmax; From 89fd88ed819a0a1865c1a288cfb3e1f3999e3bb1 Mon Sep 17 00:00:00 2001 From: Martin Balao Date: Mon, 6 May 2024 11:43:35 +0000 Subject: [PATCH 09/12] 8327413: Enhance compilation efficiency Reviewed-by: andrew Backport-of: eb1f33f525846e6020185cbc5d6e48cbc760dbb2 --- src/hotspot/share/opto/ifnode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp index 2570f85d17f..3272ecd16e7 100644 --- a/src/hotspot/share/opto/ifnode.cpp +++ b/src/hotspot/share/opto/ifnode.cpp @@ -717,6 +717,7 @@ bool IfNode::cmpi_folds(PhaseIterGVN* igvn) { bool IfNode::is_ctrl_folds(Node* ctrl, PhaseIterGVN* igvn) { return ctrl != NULL && ctrl->is_Proj() && + ctrl->outcnt() == 1 && // No side-effects ctrl->in(0) != NULL && ctrl->in(0)->Opcode() == Op_If && ctrl->in(0)->outcnt() == 2 && @@ -1266,8 +1267,7 @@ Node* IfNode::fold_compares(PhaseIterGVN* igvn) { if (cmpi_folds(igvn)) { Node* ctrl = in(0); - if (is_ctrl_folds(ctrl, igvn) && - ctrl->outcnt() == 1) { + if (is_ctrl_folds(ctrl, igvn)) { // A integer comparison immediately dominated by another integer // comparison ProjNode* success = NULL; From 69ee1d0005366d8db45554ad3b68d7057a98e1df Mon Sep 17 00:00:00 2001 From: Martin Balao Date: Thu, 27 Jun 2024 15:35:11 +0000 Subject: [PATCH 10/12] 8303466: C2: failed: malformed control flow. Limit type made precise with MaxL/MinL Reviewed-by: roland Backport-of: cc894d849aa5f730d5a806acfc7a237cf5170af1 --- src/hotspot/share/opto/addnode.cpp | 134 ++++++++++++++++++ src/hotspot/share/opto/addnode.hpp | 34 +++++ src/hotspot/share/opto/classes.hpp | 2 + src/hotspot/share/opto/convertnode.cpp | 14 ++ src/hotspot/share/opto/convertnode.hpp | 1 + src/hotspot/share/opto/loopTransform.cpp | 103 ++++++-------- src/hotspot/share/opto/macro.cpp | 14 ++ src/hotspot/share/runtime/vmStructs.cpp | 2 + .../loopopts/TestUnrollLimitPreciseType.java | 92 ++++++++++++ 9 files changed, 334 insertions(+), 62 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/loopopts/TestUnrollLimitPreciseType.java diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 6fb27389ccb..bdde4fe8dfe 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -1034,6 +1034,140 @@ const Type *MinINode::add_ring( const Type *t0, const Type *t1 ) const { return TypeInt::make( MIN2(r0->_lo,r1->_lo), MIN2(r0->_hi,r1->_hi), MAX2(r0->_widen,r1->_widen) ); } +// Collapse the "addition with overflow-protection" pattern, and the symmetrical +// "subtraction with underflow-protection" pattern. These are created during the +// unrolling, when we have to adjust the limit by subtracting the stride, but want +// to protect against underflow: MaxL(SubL(limit, stride), min_jint). +// If we have more than one of those in a sequence: +// +// x con2 +// | | +// AddL clamp2 +// | | +// Max/MinL con1 +// | | +// AddL clamp1 +// | | +// Max/MinL (n) +// +// We want to collapse it to: +// +// x con1 con2 +// | | | +// | AddLNode (new_con) +// | | +// AddLNode clamp1 +// | | +// Max/MinL (n) +// +// Note: we assume that SubL was already replaced by an AddL, and that the stride +// has its sign flipped: SubL(limit, stride) -> AddL(limit, -stride). +static bool is_clamp(PhaseGVN* phase, Node* n, Node* c) { + // Check that the two clamps have the correct values. + jlong clamp = (n->Opcode() == Op_MaxL) ? min_jint : max_jint; + const TypeLong* t = phase->type(c)->isa_long(); + return t != NULL && t->is_con() && + t->get_con() == clamp; +} + +static bool is_sub_con(PhaseGVN* phase, Node* n, Node* c) { + // Check that the constants are negative if MaxL, and positive if MinL. + const TypeLong* t = phase->type(c)->isa_long(); + return t != NULL && t->is_con() && + t->get_con() < max_jint && t->get_con() > min_jint && + (t->get_con() < 0) == (n->Opcode() == Op_MaxL); +} + +Node* fold_subI_no_underflow_pattern(Node* n, PhaseGVN* phase) { + assert(n->Opcode() == Op_MaxL || n->Opcode() == Op_MinL, "sanity"); + // Verify the graph level by level: + Node* add1 = n->in(1); + Node* clamp1 = n->in(2); + if (add1->Opcode() == Op_AddL && is_clamp(phase, n, clamp1)) { + Node* max2 = add1->in(1); + Node* con1 = add1->in(2); + if (max2->Opcode() == n->Opcode() && is_sub_con(phase, n, con1)) { + Node* add2 = max2->in(1); + Node* clamp2 = max2->in(2); + if (add2->Opcode() == Op_AddL && is_clamp(phase, n, clamp2)) { + Node* x = add2->in(1); + Node* con2 = add2->in(2); + if (is_sub_con(phase, n, con2)) { + Node* new_con = phase->transform(new AddLNode(con1, con2)); + Node* new_sub = phase->transform(new AddLNode(x, new_con)); + n->set_req_X(1, new_sub, phase); + return n; + } + } + } + } + return NULL; +} + +const Type* MaxLNode::add_ring(const Type* t0, const Type* t1) const { + const TypeLong* r0 = t0->is_long(); + const TypeLong* r1 = t1->is_long(); + + return TypeLong::make(MAX2(r0->_lo, r1->_lo), MAX2(r0->_hi, r1->_hi), MAX2(r0->_widen, r1->_widen)); +} + +Node* MaxLNode::Identity(PhaseGVN* phase) { + const TypeLong* t1 = phase->type(in(1))->is_long(); + const TypeLong* t2 = phase->type(in(2))->is_long(); + + // Can we determine maximum statically? + if (t1->_lo >= t2->_hi) { + return in(1); + } else if (t2->_lo >= t1->_hi) { + return in(2); + } + + return MaxNode::Identity(phase); +} + +Node* MaxLNode::Ideal(PhaseGVN* phase, bool can_reshape) { + Node* n = AddNode::Ideal(phase, can_reshape); + if (n != NULL) { + return n; + } + if (can_reshape) { + return fold_subI_no_underflow_pattern(this, phase); + } + return NULL; +} + +const Type* MinLNode::add_ring(const Type* t0, const Type* t1) const { + const TypeLong* r0 = t0->is_long(); + const TypeLong* r1 = t1->is_long(); + + return TypeLong::make(MIN2(r0->_lo, r1->_lo), MIN2(r0->_hi, r1->_hi), MIN2(r0->_widen, r1->_widen)); +} + +Node* MinLNode::Identity(PhaseGVN* phase) { + const TypeLong* t1 = phase->type(in(1))->is_long(); + const TypeLong* t2 = phase->type(in(2))->is_long(); + + // Can we determine minimum statically? + if (t1->_lo >= t2->_hi) { + return in(2); + } else if (t2->_lo >= t1->_hi) { + return in(1); + } + + return MaxNode::Identity(phase); +} + +Node* MinLNode::Ideal(PhaseGVN* phase, bool can_reshape) { + Node* n = AddNode::Ideal(phase, can_reshape); + if (n != NULL) { + return n; + } + if (can_reshape) { + return fold_subI_no_underflow_pattern(this, phase); + } + return NULL; +} + //------------------------------add_ring--------------------------------------- const Type *MinFNode::add_ring( const Type *t0, const Type *t1 ) const { const TypeF *r0 = t0->is_float_constant(); diff --git a/src/hotspot/share/opto/addnode.hpp b/src/hotspot/share/opto/addnode.hpp index abe686e1bdb..30319a7150c 100644 --- a/src/hotspot/share/opto/addnode.hpp +++ b/src/hotspot/share/opto/addnode.hpp @@ -279,6 +279,40 @@ class MinINode : public MaxNode { virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); }; +//------------------------------MaxLNode--------------------------------------- +// MAXimum of 2 longs. +class MaxLNode : public MaxNode { +public: + MaxLNode(Compile* C, Node* in1, Node* in2) : MaxNode(in1, in2) { + init_flags(Flag_is_macro); + C->add_macro_node(this); + } + virtual int Opcode() const; + virtual const Type* add_ring(const Type* t0, const Type* t1) const; + virtual const Type* add_id() const { return TypeLong::make(min_jlong); } + virtual const Type* bottom_type() const { return TypeLong::LONG; } + virtual uint ideal_reg() const { return Op_RegL; } + virtual Node* Identity(PhaseGVN* phase); + virtual Node* Ideal(PhaseGVN *phase, bool can_reshape); +}; + +//------------------------------MinLNode--------------------------------------- +// MINimum of 2 longs. +class MinLNode : public MaxNode { +public: + MinLNode(Compile* C, Node* in1, Node* in2) : MaxNode(in1, in2) { + init_flags(Flag_is_macro); + C->add_macro_node(this); + } + virtual int Opcode() const; + virtual const Type* add_ring(const Type* t0, const Type* t1) const; + virtual const Type* add_id() const { return TypeLong::make(max_jlong); } + virtual const Type* bottom_type() const { return TypeLong::LONG; } + virtual uint ideal_reg() const { return Op_RegL; } + virtual Node* Identity(PhaseGVN* phase); + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); +}; + //------------------------------MaxFNode--------------------------------------- // Maximum of 2 floats. class MaxFNode : public MaxNode { diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index 9d8aacfee28..7a9bd911175 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -206,6 +206,7 @@ macro(MachProj) macro(MaxD) macro(MaxF) macro(MaxI) +macro(MaxL) macro(MemBarAcquire) macro(LoadFence) macro(SetVectMaskI) @@ -220,6 +221,7 @@ macro(MergeMem) macro(MinD) macro(MinF) macro(MinI) +macro(MinL) macro(ModD) macro(ModF) macro(ModI) diff --git a/src/hotspot/share/opto/convertnode.cpp b/src/hotspot/share/opto/convertnode.cpp index ee2373dc3a9..835b101be88 100644 --- a/src/hotspot/share/opto/convertnode.cpp +++ b/src/hotspot/share/opto/convertnode.cpp @@ -260,6 +260,20 @@ const Type* ConvI2LNode::Value(PhaseGVN* phase) const { return tl; } +Node* ConvI2LNode::Identity(PhaseGVN* phase) { + // If type is in "int" sub-range, we can + // convert I2L(L2I(x)) => x + // since the conversions have no effect. + if (in(1)->Opcode() == Op_ConvL2I) { + Node* x = in(1)->in(1); + const TypeLong* t = phase->type(x)->isa_long(); + if (t != NULL && t->_lo >= min_jint && t->_hi <= max_jint) { + return x; + } + } + return this; +} + #ifdef _LP64 static inline bool long_ranges_overlap(jlong lo1, jlong hi1, jlong lo2, jlong hi2) { diff --git a/src/hotspot/share/opto/convertnode.hpp b/src/hotspot/share/opto/convertnode.hpp index 625e6eb3576..9fcb1c189b0 100644 --- a/src/hotspot/share/opto/convertnode.hpp +++ b/src/hotspot/share/opto/convertnode.hpp @@ -151,6 +151,7 @@ class ConvI2LNode : public TypeNode { virtual int Opcode() const; virtual const Type* Value(PhaseGVN* phase) const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual Node* Identity(PhaseGVN* phase); virtual uint ideal_reg() const { return Op_RegL; } }; diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 9d75bfe9c52..7f8abbad9b7 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -2017,7 +2017,7 @@ void PhaseIdealLoop::do_unroll(IdealLoopTree *loop, Node_List &old_new, bool adj new_limit = _igvn.intcon(limit->get_int() - stride_con); set_ctrl(new_limit, C->root()); } else { - // Limit is not constant. + // Limit is not constant. Int subtraction could lead to underflow. if (loop_head->unrolled_count() == 1) { // only for first unroll // Separate limit by Opaque node in case it is an incremented // variable from previous loop to avoid using pre-incremented @@ -2029,53 +2029,37 @@ void PhaseIdealLoop::do_unroll(IdealLoopTree *loop, Node_List &old_new, bool adj Node* opaq_ctrl = get_ctrl(opaq); limit = new Opaque2Node(C, limit); register_new_node(limit, opaq_ctrl); + + // The Opaque2 node created above (in the case of the first unrolling) hides the type of the loop limit. + // Propagate this precise type information. + limit = new CastIINode(limit, limit_type); + register_new_node(limit, opaq_ctrl); } - if ((stride_con > 0 && (java_subtract(limit_type->_lo, stride_con) < limit_type->_lo)) || - (stride_con < 0 && (java_subtract(limit_type->_hi, stride_con) > limit_type->_hi))) { - // No underflow. - new_limit = new SubINode(limit, stride); + // (1) Convert to long. + Node* limit_l = new ConvI2LNode(limit); + register_new_node(limit_l, get_ctrl(limit)); + Node* stride_l = _igvn.longcon(stride_con); + set_ctrl(stride_l, C->root()); + + // (2) Subtract: compute in long, to prevent underflow. + Node* new_limit_l = new SubLNode(limit_l, stride_l); + register_new_node(new_limit_l, ctrl); + + // (3) Clamp to int range, in case we had subtraction underflow. + Node* underflow_clamp_l = _igvn.longcon((stride_con > 0) ? min_jint : max_jint); + set_ctrl(underflow_clamp_l, C->root()); + Node* new_limit_no_underflow_l = NULL; + if (stride_con > 0) { + // limit = MaxL(limit - stride, min_jint) + new_limit_no_underflow_l = new MaxLNode(C, new_limit_l, underflow_clamp_l); } else { - // (limit - stride) may underflow. - // Clamp the adjustment value with MININT or MAXINT: - // - // new_limit = limit-stride - // if (stride > 0) - // new_limit = (limit < new_limit) ? MININT : new_limit; - // else - // new_limit = (limit > new_limit) ? MAXINT : new_limit; - // - BoolTest::mask bt = loop_end->test_trip(); - assert(bt == BoolTest::lt || bt == BoolTest::gt, "canonical test is expected"); - Node* adj_max = _igvn.intcon((stride_con > 0) ? min_jint : max_jint); - set_ctrl(adj_max, C->root()); - Node* old_limit = NULL; - Node* adj_limit = NULL; - Node* bol = limit->is_CMove() ? limit->in(CMoveNode::Condition) : NULL; - if (loop_head->unrolled_count() > 1 && - limit->is_CMove() && limit->Opcode() == Op_CMoveI && - limit->in(CMoveNode::IfTrue) == adj_max && - bol->as_Bool()->_test._test == bt && - bol->in(1)->Opcode() == Op_CmpI && - bol->in(1)->in(2) == limit->in(CMoveNode::IfFalse)) { - // Loop was unrolled before. - // Optimize the limit to avoid nested CMove: - // use original limit as old limit. - old_limit = bol->in(1)->in(1); - // Adjust previous adjusted limit. - adj_limit = limit->in(CMoveNode::IfFalse); - adj_limit = new SubINode(adj_limit, stride); - } else { - old_limit = limit; - adj_limit = new SubINode(limit, stride); - } - assert(old_limit != NULL && adj_limit != NULL, ""); - register_new_node(adj_limit, ctrl); // adjust amount - Node* adj_cmp = new CmpINode(old_limit, adj_limit); - register_new_node(adj_cmp, ctrl); - Node* adj_bool = new BoolNode(adj_cmp, bt); - register_new_node(adj_bool, ctrl); - new_limit = new CMoveINode(adj_bool, adj_limit, adj_max, TypeInt::INT); + // limit = MinL(limit - stride, max_jint) + new_limit_no_underflow_l = new MinLNode(C, new_limit_l, underflow_clamp_l); } + register_new_node(new_limit_no_underflow_l, ctrl); + + // (4) Convert back to int. + new_limit = new ConvL2INode(new_limit_no_underflow_l); register_new_node(new_limit, ctrl); } @@ -2284,6 +2268,9 @@ void PhaseIdealLoop::mark_reductions(IdealLoopTree *loop) { //------------------------------adjust_limit----------------------------------- // Helper function that computes new loop limit as (rc_limit-offset)/scale Node* PhaseIdealLoop::adjust_limit(bool is_positive_stride, Node* scale, Node* offset, Node* rc_limit, Node* old_limit, Node* pre_ctrl, bool round) { + Node* old_limit_long = new ConvI2LNode(old_limit); + register_new_node(old_limit_long, pre_ctrl); + Node* sub = new SubLNode(rc_limit, offset); register_new_node(sub, pre_ctrl); Node* limit = new DivLNode(NULL, sub, scale); @@ -2309,27 +2296,19 @@ Node* PhaseIdealLoop::adjust_limit(bool is_positive_stride, Node* scale, Node* o // - integer underflow of limit: MAXL chooses old_limit (>= MIN_INT > limit) // INT() is finally converting the limit back to an integer value. - // We use CMove nodes to implement long versions of min/max (MINL/MAXL). - // We use helper methods for inner MINL/MAXL which return CMoveL nodes to keep a long value for the outer MINL/MAXL comparison: - Node* inner_result_long; + Node* inner_result_long = NULL; + Node* outer_result_long = NULL; if (is_positive_stride) { - inner_result_long = MaxNode::signed_max(limit, _igvn.longcon(min_jint), TypeLong::LONG, _igvn); + inner_result_long = new MaxLNode(C, limit, _igvn.longcon(min_jint)); + outer_result_long = new MinLNode(C, inner_result_long, old_limit_long); } else { - inner_result_long = MaxNode::signed_min(limit, _igvn.longcon(max_jint), TypeLong::LONG, _igvn); + inner_result_long = new MinLNode(C, limit, _igvn.longcon(max_jint)); + outer_result_long = new MaxLNode(C, inner_result_long, old_limit_long); } - set_subtree_ctrl(inner_result_long); + register_new_node(inner_result_long, pre_ctrl); + register_new_node(outer_result_long, pre_ctrl); - // Outer MINL/MAXL: - // The comparison is done with long values but the result is the converted back to int by using CmovI. - Node* old_limit_long = new ConvI2LNode(old_limit); - register_new_node(old_limit_long, pre_ctrl); - Node* cmp = new CmpLNode(old_limit_long, limit); - register_new_node(cmp, pre_ctrl); - Node* bol = new BoolNode(cmp, is_positive_stride ? BoolTest::gt : BoolTest::lt); - register_new_node(bol, pre_ctrl); - Node* inner_result_int = new ConvL2INode(inner_result_long); // Could under-/overflow but that's fine as comparison was done with CmpL - register_new_node(inner_result_int, pre_ctrl); - limit = new CMoveINode(bol, old_limit, inner_result_int, TypeInt::INT); + limit = new ConvL2INode(outer_result_long); register_new_node(limit, pre_ctrl); return limit; } diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index b66982ffdd5..155c6893e48 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -2630,6 +2630,8 @@ void PhaseMacroExpand::eliminate_macro_nodes() { assert(n->Opcode() == Op_LoopLimit || n->Opcode() == Op_Opaque2 || n->Opcode() == Op_Opaque3 || + n->Opcode() == Op_MaxL || + n->Opcode() == Op_MinL || BarrierSet::barrier_set()->barrier_set_c2()->is_gc_barrier_node(n), "unknown node type in macro list"); } @@ -2701,6 +2703,18 @@ bool PhaseMacroExpand::expand_macro_nodes() { n->as_OuterStripMinedLoop()->adjust_strip_mined_loop(&_igvn); C->remove_macro_node(n); success = true; + } else if (n->Opcode() == Op_MaxL) { + // Since MaxL and MinL are not implemented in the backend, we expand them to + // a CMoveL construct now. At least until here, the type could be computed + // precisely. CMoveL is not so smart, but we can give it at least the best + // type we know abouot n now. + Node* repl = MaxNode::signed_max(n->in(1), n->in(2), _igvn.type(n), _igvn); + _igvn.replace_node(n, repl); + success = true; + } else if (n->Opcode() == Op_MinL) { + Node* repl = MaxNode::signed_min(n->in(1), n->in(2), _igvn.type(n), _igvn); + _igvn.replace_node(n, repl); + success = true; } assert(success == (C->macro_count() < old_macro_count), "elimination reduces macro count"); progress = progress || success; diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 85530164e31..adce6da6a3b 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -1552,6 +1552,8 @@ typedef PaddedEnd PaddedObjectMonitor; declare_c2_type(MaxNode, AddNode) \ declare_c2_type(MaxINode, MaxNode) \ declare_c2_type(MinINode, MaxNode) \ + declare_c2_type(MaxLNode, MaxNode) \ + declare_c2_type(MinLNode, MaxNode) \ declare_c2_type(MaxFNode, MaxNode) \ declare_c2_type(MinFNode, MaxNode) \ declare_c2_type(MaxDNode, MaxNode) \ diff --git a/test/hotspot/jtreg/compiler/loopopts/TestUnrollLimitPreciseType.java b/test/hotspot/jtreg/compiler/loopopts/TestUnrollLimitPreciseType.java new file mode 100644 index 00000000000..18a58aa7ae5 --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestUnrollLimitPreciseType.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2023, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test id=test1 + * @bug 8298935 + * @summary CMoveI for underflow protection of the limit did not compute a type that was precise enough. + * This lead to dead data but zero-trip-guard control did not die -> "malformed control flow". + * @requires vm.compiler2.enabled + * @run main/othervm + * -XX:CompileCommand=compileonly,compiler.loopopts.TestUnrollLimitPreciseType::test1 + * -XX:CompileCommand=dontinline,compiler.loopopts.TestUnrollLimitPreciseType::* + * -XX:MaxVectorSize=64 + * -Xcomp + * -XX:+UnlockExperimentalVMOptions -XX:PerMethodSpecTrapLimit=0 -XX:PerMethodTrapLimit=0 + * compiler.loopopts.TestUnrollLimitPreciseType test1 + */ + +/* + * @test id=test2 + * @bug 8298935 + * @summary CMoveI for underflow protection of the limit did not compute a type that was precise enough. + * This lead to dead data but zero-trip-guard control did not die -> "malformed control flow". + * @requires vm.compiler2.enabled + * @run main/othervm + * -XX:CompileCommand=compileonly,compiler.loopopts.TestUnrollLimitPreciseType::* + * -Xcomp + * compiler.loopopts.TestUnrollLimitPreciseType test2 + */ + + +package compiler.loopopts; + +public class TestUnrollLimitPreciseType { + static final int RANGE = 512; + + public static void main(String args[]) { + if (args.length != 1) { + throw new RuntimeException("Need exactly one argument."); + } + if (args[0].equals("test1")) { + byte[] data = new byte[RANGE]; + test1(data); + } else if (args[0].equals("test2")) { + test2(); + } else { + throw new RuntimeException("Do not have: " + args[0]); + } + } + + public static void test1(byte[] data) { + // Did not fully analyze this. But it is also unrolled, SuperWorded, + // and further unrolled with vectorlized post loop. + // Only seems to reproduce with avx512, and not with avx2. + for (int j = 192; j < RANGE; j++) { + data[j - 192] = (byte)(data[j] * 11); + } + } + + static void test2() { + // Loop is SuperWord'ed. + // We unroll more afterwards, and so add vectorized post loop. + // But it turns out that the vectorized post loop is never entered. + // This lead to assert, because the zero-trip-guard did not collaspse, + // but the CastII with the trip count did die. + // Only seems to reproduce with avx512, and not with avx2. + double dArr[][] = new double[100][100]; + for (int i = 2, j = 2; j < 68; j++) { + dArr[i][j] = 8; + } + } +} From e3e776509ac23a02bf5cc844dba18c26615500e1 Mon Sep 17 00:00:00 2001 From: Martin Balao Date: Tue, 30 Jan 2024 08:41:36 +0000 Subject: [PATCH 11/12] 8320548: Improved loop handling Backport-of: 72eed2f709caa48c82f58fe75c7e94d2c45947e1 --- src/hotspot/share/opto/superword.cpp | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp index fed52e48831..60c2c8e6fab 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -3410,20 +3410,39 @@ void SuperWord::align_initial_loop_index(MemNode* align_to_ref) { _igvn.register_new_node_with_optimizer(N); _phase->set_ctrl(N, pre_ctrl); + // The computation of the new pre-loop limit could overflow or underflow the int range. This is problematic in + // combination with Range Check Elimination (RCE), which determines a "safe" range where a RangeCheck will always + // succeed. RCE adjusts the pre-loop limit such that we only enter the main-loop once we have reached the "safe" + // range, and adjusts the main-loop limit so that we exit the main-loop before we leave the "safe" range. After RCE, + // the range of the main-loop can only be safely narrowed, and should never be widened. Hence, the pre-loop limit + // can only be increased (for stride > 0), but an add overflow might decrease it, or decreased (for stride < 0), but + // a sub underflow might increase it. To prevent that, we perform the Sub / Add and Max / Min with long operations. + lim0 = new ConvI2LNode(lim0); + N = new ConvI2LNode(N); + orig_limit = new ConvI2LNode(orig_limit); + _igvn.register_new_node_with_optimizer(lim0); + _igvn.register_new_node_with_optimizer(N); + _igvn.register_new_node_with_optimizer(orig_limit); + // substitute back into (1), so that new limit // lim = lim0 + N Node* lim; if (stride < 0) { - lim = new SubINode(lim0, N); + lim = new SubLNode(lim0, N); } else { - lim = new AddINode(lim0, N); + lim = new AddLNode(lim0, N); } _igvn.register_new_node_with_optimizer(lim); _phase->set_ctrl(lim, pre_ctrl); Node* constrained = - (stride > 0) ? (Node*) new MinINode(lim, orig_limit) - : (Node*) new MaxINode(lim, orig_limit); + (stride > 0) ? (Node*) new MinLNode(_phase->C, lim, orig_limit) + : (Node*) new MaxLNode(_phase->C, lim, orig_limit); _igvn.register_new_node_with_optimizer(constrained); + + // We know that the result is in the int range, there is never truncation + constrained = new ConvL2INode(constrained); + _igvn.register_new_node_with_optimizer(constrained); + _phase->set_ctrl(constrained, pre_ctrl); _igvn.replace_input_of(pre_opaq, 1, constrained); } From ff537af466ee74e914c5cee1042de650bfe0da5f Mon Sep 17 00:00:00 2001 From: Christoph Langer Date: Tue, 9 Jul 2024 10:41:55 +0200 Subject: [PATCH 12/12] 8335973: [11u] Remove designator DEFAULT_PROMOTED_VERSION_PRE=ea for release 11.0.24 Reviewed-by: mbaesken --- make/autoconf/version-numbers | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/make/autoconf/version-numbers b/make/autoconf/version-numbers index 45d037c02f2..d6b92cdb115 100644 --- a/make/autoconf/version-numbers +++ b/make/autoconf/version-numbers @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2024, 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 @@ -37,7 +37,7 @@ DEFAULT_VERSION_DATE=2024-07-16 DEFAULT_VERSION_CLASSFILE_MAJOR=55 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`" DEFAULT_VERSION_CLASSFILE_MINOR=0 DEFAULT_ACCEPTABLE_BOOT_VERSIONS="10 11" -DEFAULT_PROMOTED_VERSION_PRE=ea +DEFAULT_PROMOTED_VERSION_PRE= LAUNCHER_NAME=openjdk PRODUCT_NAME=OpenJDK