diff --git a/src/java.base/share/classes/java/lang/CharacterName.java b/src/java.base/share/classes/java/lang/CharacterName.java index 11589541127..1a586d4e580 100644 --- a/src/java.base/share/classes/java/lang/CharacterName.java +++ b/src/java.base/share/classes/java/lang/CharacterName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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 @@ -113,7 +113,7 @@ class CharacterName { } private static int hashN(byte[] a, int off, int len) { - return ArraysSupport.vectorizedHashCode(a, off, len, 1, ArraysSupport.T_BYTE); + return ArraysSupport.hashCode(a, off, len, 1); } private int addCp(int idx, int hash, int next, int cp) { diff --git a/src/java.base/share/classes/java/lang/StringLatin1.java b/src/java.base/share/classes/java/lang/StringLatin1.java index 0bb90617263..abec36af1d9 100644 --- a/src/java.base/share/classes/java/lang/StringLatin1.java +++ b/src/java.base/share/classes/java/lang/StringLatin1.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -303,11 +303,7 @@ final class StringLatin1 { } public static int hashCode(byte[] value) { - return switch (value.length) { - case 0 -> 0; - case 1 -> value[0] & 0xff; - default -> ArraysSupport.vectorizedHashCode(value, 0, value.length, 0, ArraysSupport.T_BOOLEAN); - }; + return ArraysSupport.hashCodeOfUnsigned(value, 0, value.length, 0); } // Caller must ensure that from- and toIndex are within bounds diff --git a/src/java.base/share/classes/java/lang/StringUTF16.java b/src/java.base/share/classes/java/lang/StringUTF16.java index aaf1d76ae24..000483f29bc 100644 --- a/src/java.base/share/classes/java/lang/StringUTF16.java +++ b/src/java.base/share/classes/java/lang/StringUTF16.java @@ -585,11 +585,7 @@ final class StringUTF16 { } public static int hashCode(byte[] value) { - return switch (value.length) { - case 0 -> 0; - case 2 -> getChar(value, 0); - default -> ArraysSupport.vectorizedHashCode(value, 0, value.length >> 1, 0, ArraysSupport.T_CHAR); - }; + return ArraysSupport.hashCodeOfUTF16(value, 0, value.length >> 1, 0); } // Caller must ensure that from- and toIndex are within bounds diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 1192bc50b78..c69e291d0e2 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -4098,8 +4098,7 @@ public class BigInteger extends Number implements Comparable { */ @Override public int hashCode() { - return ArraysSupport.vectorizedHashCode(mag, 0, mag.length, 0, - ArraysSupport.T_INT) * signum; + return ArraysSupport.hashCode(mag, 0, mag.length, 0) * signum; } /** diff --git a/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template b/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template index d116d487e52..8fbc1a3b69d 100644 --- a/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template +++ b/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2023, 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 @@ -707,7 +707,7 @@ class Heap$Type$Buffer$RW$ } public int hashCode() { - return ArraysSupport.vectorizedHashCode(hb, ix(position()), remaining(), 1, ArraysSupport.T_BYTE); + return ArraysSupport.hashCode(hb, ix(position()), remaining(), 1); } #end[byte] @@ -738,7 +738,7 @@ class Heap$Type$Buffer$RW$ } public int hashCode() { - return ArraysSupport.vectorizedHashCode(hb, ix(position()), remaining(), 1, ArraysSupport.T_CHAR); + return ArraysSupport.hashCode(hb, ix(position()), remaining(), 1); } #end[char] diff --git a/src/java.base/share/classes/java/util/Arrays.java b/src/java.base/share/classes/java/util/Arrays.java index c5412776d20..1703d235dfc 100644 --- a/src/java.base/share/classes/java/util/Arrays.java +++ b/src/java.base/share/classes/java/util/Arrays.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, 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 @@ -4358,11 +4358,7 @@ public final class Arrays { if (a == null) { return 0; } - return switch (a.length) { - case 0 -> 1; - case 1 -> 31 + a[0]; - default -> ArraysSupport.vectorizedHashCode(a, 0, a.length, 1, ArraysSupport.T_INT); - }; + return ArraysSupport.hashCode(a, 0, a.length, 1); } /** @@ -4385,11 +4381,7 @@ public final class Arrays { if (a == null) { return 0; } - return switch (a.length) { - case 0 -> 1; - case 1 -> 31 + (int)a[0]; - default -> ArraysSupport.vectorizedHashCode(a, 0, a.length, 1, ArraysSupport.T_SHORT); - }; + return ArraysSupport.hashCode(a, 0, a.length, 1); } /** @@ -4412,11 +4404,7 @@ public final class Arrays { if (a == null) { return 0; } - return switch (a.length) { - case 0 -> 1; - case 1 -> 31 + (int)a[0]; - default -> ArraysSupport.vectorizedHashCode(a, 0, a.length, 1, ArraysSupport.T_CHAR); - }; + return ArraysSupport.hashCode(a, 0, a.length, 1); } /** @@ -4439,11 +4427,7 @@ public final class Arrays { if (a == null) { return 0; } - return switch (a.length) { - case 0 -> 1; - case 1 -> 31 + (int)a[0]; - default -> ArraysSupport.vectorizedHashCode(a, 0, a.length, 1, ArraysSupport.T_BYTE); - }; + return ArraysSupport.hashCode(a, 0, a.length, 1); } /** @@ -4549,15 +4533,10 @@ public final class Arrays { * @since 1.5 */ public static int hashCode(Object[] a) { - if (a == null) + if (a == null) { return 0; - - int result = 1; - - for (Object element : a) - result = 31 * result + Objects.hashCode(element); - - return result; + } + return ArraysSupport.hashCode(a, 0, a.length, 1); } /** diff --git a/src/java.base/share/classes/java/util/zip/ZipCoder.java b/src/java.base/share/classes/java/util/zip/ZipCoder.java index 6eb97308832..25c6e3a2e4a 100644 --- a/src/java.base/share/classes/java/util/zip/ZipCoder.java +++ b/src/java.base/share/classes/java/util/zip/ZipCoder.java @@ -290,8 +290,7 @@ class ZipCoder { // exceptions eagerly when opening ZipFiles return hash(JLA.newStringUTF8NoRepl(a, off, len)); } - // T_BOOLEAN to treat the array as unsigned bytes, in line with StringLatin1.hashCode - int h = ArraysSupport.vectorizedHashCode(a, off, len, 0, ArraysSupport.T_BOOLEAN); + int h = ArraysSupport.hashCodeOfUnsigned(a, off, len, 0); if (a[end - 1] != '/') { h = 31 * h + '/'; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java index c86a61d64c9..a494c21085c 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -230,7 +230,7 @@ public abstract sealed class AbstractPoolEntry { */ private void inflate() { int singleBytes = JLA.countPositives(rawBytes, offset, rawLen); - int hash = ArraysSupport.vectorizedHashCode(rawBytes, offset, singleBytes, 0, ArraysSupport.T_BOOLEAN); + int hash = ArraysSupport.hashCodeOfUnsigned(rawBytes, offset, singleBytes, 0); if (singleBytes == rawLen) { this.hash = hashString(hash); charLen = rawLen; diff --git a/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java b/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java index 8f8ea651d65..a24f8389709 100644 --- a/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java +++ b/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java @@ -26,6 +26,8 @@ package jdk.internal.util; import java.util.Arrays; import java.util.Collection; +import java.util.Objects; + import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.misc.Unsafe; @@ -164,17 +166,177 @@ public class ArraysSupport { } } + /** + * Calculates the hash code for the subrange of an integer array. + * + *

This method does not perform type checks or bounds checks. It is the + * responsibility of the caller to perform such checks before calling this + * method. + * + * @param a the array + * @param fromIndex the first index of the subrange of the array + * @param length the number of elements in the subrange + * @param initialValue the initial hash value, typically 0 or 1 + * + * @return the calculated hash value + */ + public static int hashCode(int[] a, int fromIndex, int length, int initialValue) { + return switch (length) { + case 0 -> initialValue; + case 1 -> 31 * initialValue + a[fromIndex]; + default -> vectorizedHashCode(a, fromIndex, length, initialValue, T_INT); + }; + } + + /** + * Calculates the hash code for the subrange of a short array. + * + *

This method does not perform type checks or bounds checks. It is the + * responsibility of the caller to perform such checks before calling this + * method. + * + * @param a the array + * @param fromIndex the first index of the subrange of the array + * @param length the number of elements in the subrange + * @param initialValue the initial hash value, typically 0 or 1 + * + * @return the calculated hash value + */ + public static int hashCode(short[] a, int fromIndex, int length, int initialValue) { + return switch (length) { + case 0 -> initialValue; + case 1 -> 31 * initialValue + a[fromIndex]; + default -> vectorizedHashCode(a, fromIndex, length, initialValue, T_SHORT); + }; + } + + /** + * Calculates the hash code for the subrange of a char array. + * + *

This method does not perform type checks or bounds checks. It is the + * responsibility of the caller to perform such checks before calling this + * method. + * + * @param a the array + * @param fromIndex the first index of the subrange of the array + * @param length the number of elements in the subrange + * @param initialValue the initial hash value, typically 0 or 1 + * + * @return the calculated hash value + */ + public static int hashCode(char[] a, int fromIndex, int length, int initialValue) { + return switch (length) { + case 0 -> initialValue; + case 1 -> 31 * initialValue + a[fromIndex]; + default -> vectorizedHashCode(a, fromIndex, length, initialValue, T_CHAR); + }; + } + + /** + * Calculates the hash code for the subrange of a byte array. + * + *

This method does not perform type checks or bounds checks. It is the + * responsibility of the caller to perform such checks before calling this + * method. + * + * @param a the array + * @param fromIndex the first index of the subrange of the array + * @param length the number of elements in the subrange + * @param initialValue the initial hash value, typically 0 or 1 + * + * @return the calculated hash value + */ + public static int hashCode(byte[] a, int fromIndex, int length, int initialValue) { + return switch (length) { + case 0 -> initialValue; + case 1 -> 31 * initialValue + a[fromIndex]; + default -> vectorizedHashCode(a, fromIndex, length, initialValue, T_BYTE); + }; + } + + /** + * Calculates the hash code for the subrange of a byte array whose elements + * are treated as unsigned bytes. + * + *

This method does not perform type checks or bounds checks. It is the + * responsibility of the caller to perform such checks before calling this + * method. + * + * @param a the array + * @param fromIndex the first index of the subrange of the array + * @param length the number of elements in the subrange + * @param initialValue the initial hash value, typically 0 or 1 + * + * @return the calculated hash value + */ + public static int hashCodeOfUnsigned(byte[] a, int fromIndex, int length, int initialValue) { + return switch (length) { + case 0 -> initialValue; + case 1 -> 31 * initialValue + Byte.toUnsignedInt(a[fromIndex]); + default -> vectorizedHashCode(a, fromIndex, length, initialValue, T_BOOLEAN); + }; + } + + /** + * Calculates the hash code for the subrange of a byte array whose contents + * are treated as UTF-16 chars. + * + *

This method does not perform type checks or bounds checks. It is the + * responsibility of the caller to perform such checks before calling this + * method. + * + *

{@code fromIndex} and {@code length} must be scaled down to char + * indexes. + * + * @param a the array + * @param fromIndex the first index of a char in the subrange of the array + * @param length the number of chars in the subrange + * @param initialValue the initial hash value, typically 0 or 1 + * + * @return the calculated hash value + */ + public static int hashCodeOfUTF16(byte[] a, int fromIndex, int length, int initialValue) { + return switch (length) { + case 0 -> initialValue; + case 1 -> 31 * initialValue + JLA.getUTF16Char(a, fromIndex); + default -> vectorizedHashCode(a, fromIndex, length, initialValue, T_CHAR); + }; + } + + /** + * Calculates the hash code for the subrange of an object array. + * + *

This method does not perform type checks or bounds checks. It is the + * responsibility of the caller to perform such checks before calling this + * method. + * + * @param a the array + * @param fromIndex the first index of the subrange of the array + * @param length the number of elements in the subrange + * @param initialValue the initial hash value, typically 0 or 1 + * + * @return the calculated hash value + */ + public static int hashCode(Object[] a, int fromIndex, int length, int initialValue) { + int result = initialValue; + int end = fromIndex + length; + for (int i = fromIndex; i < end; i++) { + result = 31 * result + Objects.hashCode(a[i]); + } + return result; + } + // Possible values for the type operand of the NEWARRAY instruction. // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html#jvms-6.5.newarray. - public static final int T_BOOLEAN = 4; - public static final int T_CHAR = 5; - public static final int T_FLOAT = 6; - public static final int T_DOUBLE = 7; - public static final int T_BYTE = 8; - public static final int T_SHORT = 9; - public static final int T_INT = 10; - public static final int T_LONG = 11; + private static final int T_BOOLEAN = 4; + private static final int T_CHAR = 5; + private static final int T_FLOAT = 6; + private static final int T_DOUBLE = 7; + private static final int T_BYTE = 8; + private static final int T_SHORT = 9; + private static final int T_INT = 10; + private static final int T_LONG = 11; /** * Calculate the hash code for an array in a way that enables efficient @@ -197,8 +359,8 @@ public class ArraysSupport { * @return the calculated hash value */ @IntrinsicCandidate - public static int vectorizedHashCode(Object array, int fromIndex, int length, int initialValue, - int basicType) { + private static int vectorizedHashCode(Object array, int fromIndex, int length, int initialValue, + int basicType) { return switch (basicType) { case T_BOOLEAN -> unsignedHashCode(initialValue, (byte[]) array, fromIndex, length); case T_CHAR -> array instanceof byte[] @@ -214,7 +376,7 @@ public class ArraysSupport { private static int unsignedHashCode(int result, byte[] a, int fromIndex, int length) { int end = fromIndex + length; for (int i = fromIndex; i < end; i++) { - result = 31 * result + (a[i] & 0xff); + result = 31 * result + Byte.toUnsignedInt(a[i]); } return result; } @@ -255,7 +417,7 @@ public class ArraysSupport { /* * fromIndex and length must be scaled to char indexes. */ - public static int utf16hashCode(int result, byte[] value, int fromIndex, int length) { + private static int utf16hashCode(int result, byte[] value, int fromIndex, int length) { int end = fromIndex + length; for (int i = fromIndex; i < end; i++) { result = 31 * result + JLA.getUTF16Char(value, i); diff --git a/src/java.base/share/classes/sun/security/util/DerValue.java b/src/java.base/share/classes/sun/security/util/DerValue.java index a93aaaffc63..f2fcf350b39 100644 --- a/src/java.base/share/classes/sun/security/util/DerValue.java +++ b/src/java.base/share/classes/sun/security/util/DerValue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, 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 @@ -1267,7 +1267,7 @@ public class DerValue { */ @Override public int hashCode() { - return ArraysSupport.vectorizedHashCode(buffer, start, end - start, tag, ArraysSupport.T_BYTE); + return ArraysSupport.hashCode(buffer, start, end - start, tag); } /** diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixPath.java b/src/java.base/unix/classes/sun/nio/fs/UnixPath.java index 5e3a80b6063..7898acdc452 100644 --- a/src/java.base/unix/classes/sun/nio/fs/UnixPath.java +++ b/src/java.base/unix/classes/sun/nio/fs/UnixPath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -803,8 +803,7 @@ class UnixPath implements Path { // OK if two or more threads compute hash int h = hash; if (h == 0) { - h = ArraysSupport.vectorizedHashCode(path, 0, path.length, 0, - /* unsigned bytes */ ArraysSupport.T_BOOLEAN); + h = ArraysSupport.hashCodeOfUnsigned(path, 0, path.length, 0); hash = h; } return h; diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestArraysHashCode.java b/test/hotspot/jtreg/compiler/intrinsics/TestArraysHashCode.java index 2ff707a1e45..aef01d20567 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/TestArraysHashCode.java +++ b/test/hotspot/jtreg/compiler/intrinsics/TestArraysHashCode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -25,43 +25,66 @@ * @test * @bug 8301093 * @summary Verify failure to intrinsify does not pollute control flow - * @modules java.base/jdk.internal.util + * @modules java.base/jdk.internal.util:+open * * @run main/othervm -Xbatch -XX:-TieredCompilation compiler.intrinsics.TestArraysHashCode */ package compiler.intrinsics; -import jdk.internal.util.ArraysSupport; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; public class TestArraysHashCode { + private static final Method vectorizedHashCode; + private static final int T_BOOLEAN; + + static { + try { + var arraysSupport = Class.forName("jdk.internal.util.ArraysSupport"); + vectorizedHashCode = arraysSupport.getDeclaredMethod("vectorizedHashCode", + Object.class, int.class, int.class, int.class, int.class); + vectorizedHashCode.setAccessible(true); + Field f = arraysSupport.getDeclaredField("T_BOOLEAN"); + f.setAccessible(true); + T_BOOLEAN = f.getInt(null); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + static int type; static byte[] bytes; - public static void main(String[] args) { + public static void main(String[] args) + throws InvocationTargetException, IllegalAccessException { // read bytes = new byte[256]; - type = ArraysSupport.T_BOOLEAN; + type = T_BOOLEAN; testIntrinsicWithConstantType(); testIntrinsicWithNonConstantType(); } - private static void testIntrinsicWithConstantType() { + private static void testIntrinsicWithConstantType() + throws InvocationTargetException, IllegalAccessException { for (int i = 0; i < 20_000; i++) { - testIntrinsic(bytes, ArraysSupport.T_BOOLEAN); + testIntrinsic(bytes, T_BOOLEAN); } } // ok, but shouldn't be intrinsified due the non-constant type - private static void testIntrinsicWithNonConstantType() { - type = ArraysSupport.T_BOOLEAN; + private static void testIntrinsicWithNonConstantType() + throws InvocationTargetException, IllegalAccessException { + type = T_BOOLEAN; for (int i = 0; i < 20_000; i++) { testIntrinsic(bytes, type); } } - private static int testIntrinsic(byte[] bytes, int type) { - return ArraysSupport.vectorizedHashCode(bytes, 0, 256, 1, type); + private static int testIntrinsic(byte[] bytes, int type) + throws InvocationTargetException, IllegalAccessException { + return (int) vectorizedHashCode.invoke(null, bytes, 0, 256, 1, type); } } diff --git a/test/jdk/java/util/Arrays/HashCode.java b/test/jdk/java/util/Arrays/HashCode.java index c06d9100790..c2c9ca1b23b 100644 --- a/test/jdk/java/util/Arrays/HashCode.java +++ b/test/jdk/java/util/Arrays/HashCode.java @@ -24,7 +24,8 @@ /** * @test * @summary Basic array hashCode functionality - * @run main/othervm --add-exports java.base/jdk.internal.util=ALL-UNNAMED -Xcomp -Xbatch HashCode + * @run main/othervm --add-exports java.base/jdk.internal.util=ALL-UNNAMED + * --add-opens java.base/jdk.internal.util=ALL-UNNAMED -Xcomp -Xbatch HashCode */ import java.lang.reflect.Method;