Skip to content

Commit c291aa4

Browse files
committed
[GR-50016] Intrinsify TruffleString.hashCode() on AMD64.
PullRequest: graal/16049
2 parents 7ffea4b + 0347f85 commit c291aa4

File tree

11 files changed

+331
-119
lines changed

11 files changed

+331
-119
lines changed

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/substitutions/TruffleInvocationPlugins.java

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,13 @@
3131
import static jdk.graal.compiler.lir.gen.LIRGeneratorTool.CalcStringAttributesEncoding.UTF_8;
3232
import static jdk.graal.compiler.nodes.NamedLocationIdentity.getArrayLocation;
3333

34+
import org.graalvm.word.LocationIdentity;
35+
3436
import jdk.graal.compiler.core.common.Stride;
3537
import jdk.graal.compiler.core.common.StrideUtil;
3638
import jdk.graal.compiler.debug.GraalError;
3739
import jdk.graal.compiler.lir.gen.LIRGeneratorTool.ArrayIndexOfVariant;
40+
import jdk.graal.compiler.nodes.ComputeObjectAddressNode;
3841
import jdk.graal.compiler.nodes.ConstantNode;
3942
import jdk.graal.compiler.nodes.NamedLocationIdentity;
4043
import jdk.graal.compiler.nodes.NodeView;
@@ -47,15 +50,15 @@
4750
import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugins;
4851
import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugins.OptionalLazySymbol;
4952
import jdk.graal.compiler.nodes.spi.Replacements;
53+
import jdk.graal.compiler.replacements.InvocationPluginHelper;
5054
import jdk.graal.compiler.replacements.nodes.ArrayCopyWithConversionsNode;
5155
import jdk.graal.compiler.replacements.nodes.ArrayIndexOfMacroNode;
5256
import jdk.graal.compiler.replacements.nodes.ArrayIndexOfNode;
5357
import jdk.graal.compiler.replacements.nodes.ArrayRegionCompareToNode;
5458
import jdk.graal.compiler.replacements.nodes.ArrayRegionEqualsNode;
5559
import jdk.graal.compiler.replacements.nodes.CalcStringAttributesMacroNode;
5660
import jdk.graal.compiler.replacements.nodes.MacroNode;
57-
import org.graalvm.word.LocationIdentity;
58-
61+
import jdk.graal.compiler.replacements.nodes.VectorizedHashCodeNode;
5962
import jdk.vm.ci.aarch64.AArch64;
6063
import jdk.vm.ci.amd64.AMD64;
6164
import jdk.vm.ci.code.Architecture;
@@ -70,7 +73,7 @@ public class TruffleInvocationPlugins {
7073

7174
public static void register(Architecture architecture, InvocationPlugins plugins, Replacements replacements) {
7275
if (architecture instanceof AMD64 || architecture instanceof AArch64) {
73-
registerTStringPlugins(plugins, replacements);
76+
registerTStringPlugins(plugins, replacements, architecture);
7477
registerArrayUtilsPlugins(plugins, replacements);
7578
}
7679
}
@@ -263,7 +266,8 @@ public static LocationIdentity inferLocationIdentity(ValueNode isNativeA, ValueN
263266
return LocationIdentity.any();
264267
}
265268

266-
private static void registerTStringPlugins(InvocationPlugins plugins, Replacements replacements) {
269+
@SuppressWarnings("try")
270+
private static void registerTStringPlugins(InvocationPlugins plugins, Replacements replacements, Architecture arch) {
267271
plugins.registerIntrinsificationPredicate(t -> t.getName().equals("Lcom/oracle/truffle/api/strings/TStringOps;"));
268272
InvocationPlugins.Registration r = new InvocationPlugins.Registration(plugins, "com.oracle.truffle.api.strings.TStringOps", replacements);
269273

@@ -462,6 +466,28 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec
462466
return true;
463467
}
464468
});
469+
470+
r.registerConditional(VectorizedHashCodeNode.isSupported(arch), new InlineOnlyInvocationPlugin(
471+
"runHashCode", nodeType, byte[].class, long.class, int.class, int.class, boolean.class) {
472+
@Override
473+
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode location,
474+
ValueNode array, ValueNode offset, ValueNode length, ValueNode stride, ValueNode isNative) {
475+
try (InvocationPluginHelper helper = new InvocationPluginHelper(b, targetMethod)) {
476+
Stride constStride = constantStrideParam(stride);
477+
JavaKind componentType = switch (constStride) {
478+
case S1 -> JavaKind.Boolean; // unsigned bytes
479+
case S2 -> JavaKind.Char;
480+
case S4 -> JavaKind.Int;
481+
default -> throw GraalError.shouldNotReachHereUnexpectedValue(constStride);
482+
};
483+
484+
var arrayStart = b.add(new ComputeObjectAddressNode(array, offset));
485+
var initialValue = ConstantNode.forInt(0, b.getGraph());
486+
b.addPush(JavaKind.Int, new VectorizedHashCodeNode(arrayStart, length, initialValue, componentType));
487+
return true;
488+
}
489+
}
490+
});
465491
}
466492

467493
private static boolean applyArrayCopy(GraphBuilderContext b, ValueNode arrayA, ValueNode offsetA, ValueNode arrayB, ValueNode offsetB, ValueNode length, ValueNode dynamicStrides) {
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/*
2+
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.truffle.api.strings.bench;
42+
43+
import java.io.ByteArrayOutputStream;
44+
import java.lang.invoke.MethodHandle;
45+
import java.lang.invoke.MethodHandles;
46+
import java.nio.charset.StandardCharsets;
47+
import java.util.Random;
48+
import java.util.concurrent.TimeUnit;
49+
50+
import org.junit.Assert;
51+
import org.openjdk.jmh.annotations.Benchmark;
52+
import org.openjdk.jmh.annotations.Fork;
53+
import org.openjdk.jmh.annotations.OutputTimeUnit;
54+
import org.openjdk.jmh.annotations.Param;
55+
import org.openjdk.jmh.annotations.Scope;
56+
import org.openjdk.jmh.annotations.Setup;
57+
import org.openjdk.jmh.annotations.State;
58+
59+
import com.oracle.truffle.api.strings.AbstractTruffleString;
60+
import com.oracle.truffle.api.strings.TruffleString;
61+
62+
@OutputTimeUnit(TimeUnit.MILLISECONDS)
63+
@Fork(value = TStringBenchmarkBase.Defaults.FORKS, jvmArgsAppend = {"--add-opens=org.graalvm.truffle/com.oracle.truffle.api.strings=ALL-UNNAMED"})
64+
public class VectorizedHashCodeBenchmark extends TStringBenchmarkBase {
65+
66+
private static final int RANDOM_SEED = 42;
67+
68+
private static Random random() {
69+
return new Random(RANDOM_SEED);
70+
}
71+
72+
@State(Scope.Benchmark)
73+
public static class BenchState {
74+
75+
@Param({"1", "10", "100", "1000", "10000"}) int length;
76+
77+
byte[] b8;
78+
byte[] b16;
79+
byte[] b32;
80+
81+
TruffleString s8;
82+
TruffleString s16;
83+
TruffleString s32;
84+
85+
int h8;
86+
int h16;
87+
int h32;
88+
89+
int[] i16;
90+
int[] i32;
91+
92+
public BenchState() {
93+
}
94+
95+
@Setup
96+
public void setUp() {
97+
b8 = random().ints(length, 0, 0xff + 1).collect(ByteArrayOutputStream::new,
98+
(bytes, i) -> bytes.write((byte) i),
99+
(bytes, next) -> bytes.write(next.toByteArray(), 0, next.size())).toByteArray();
100+
101+
i16 = random().ints(length, 0, 0xffff + 1).toArray();
102+
b16 = TruffleString.fromIntArrayUTF32Uncached(i16).switchEncodingUncached(TruffleString.Encoding.UTF_16).copyToByteArrayUncached(TruffleString.Encoding.UTF_16);
103+
104+
i32 = random().ints(length, 0, 0x10_ffff + 1).toArray();
105+
b32 = TruffleString.fromIntArrayUTF32Uncached(i32).copyToByteArrayUncached(TruffleString.Encoding.UTF_32);
106+
107+
s8 = TruffleString.fromByteArrayUncached(b8, TruffleString.Encoding.ISO_8859_1, false);
108+
s16 = TruffleString.fromByteArrayUncached(b16, TruffleString.Encoding.UTF_16, false);
109+
s32 = TruffleString.fromByteArrayUncached(b32, TruffleString.Encoding.UTF_32, false);
110+
111+
h8 = new String(b8, StandardCharsets.ISO_8859_1).hashCode();
112+
h16 = hashCodeFromArray(i16);
113+
h32 = hashCodeFromArray(i32);
114+
}
115+
}
116+
117+
private static final MethodHandle INVALIDATE_HASH_CODE;
118+
static {
119+
try {
120+
var lookup = MethodHandles.lookup();
121+
var method = AbstractTruffleString.class.getDeclaredMethod("invalidateHashCode");
122+
method.setAccessible(true);
123+
INVALIDATE_HASH_CODE = lookup.unreflect(method);
124+
} catch (IllegalAccessException | NoSuchMethodException | SecurityException e) {
125+
throw new IllegalStateException(e);
126+
}
127+
}
128+
129+
static void resetHashCode(TruffleString s) {
130+
try {
131+
INVALIDATE_HASH_CODE.invokeExact((AbstractTruffleString) s);
132+
} catch (Throwable e) {
133+
throw new IllegalStateException(e);
134+
}
135+
}
136+
137+
@Benchmark
138+
public int hashCode8(BenchState state) {
139+
TruffleString.Encoding encoding = TruffleString.Encoding.ISO_8859_1;
140+
TruffleString string = state.s8;
141+
resetHashCode(string);
142+
int hashCode = string.hashCodeUncached(encoding);
143+
checkResult(state.h8, hashCode);
144+
return hashCode;
145+
}
146+
147+
@Benchmark
148+
public int hashCode16(BenchState state) {
149+
TruffleString.Encoding encoding = TruffleString.Encoding.UTF_16;
150+
TruffleString string = state.s16;
151+
resetHashCode(string);
152+
int hashCode = string.hashCodeUncached(encoding);
153+
checkResult(state.h16, hashCode);
154+
return hashCode;
155+
}
156+
157+
@Benchmark
158+
public int hashCode32(BenchState state) {
159+
TruffleString.Encoding encoding = TruffleString.Encoding.UTF_32;
160+
TruffleString string = state.s32;
161+
resetHashCode(string);
162+
int hashCode = string.hashCodeUncached(encoding);
163+
checkResult(state.h32, hashCode);
164+
return hashCode;
165+
}
166+
167+
private static void checkResult(int expectedHashCode, int hashCode) {
168+
Assert.assertEquals(expectedHashCode, hashCode);
169+
}
170+
171+
private static int hashCodeFromArray(int[] a) {
172+
return hashCode(0, a, 0, a.length);
173+
}
174+
175+
private static int hashCode(int initialValue, int[] a, int fromIndex, int length) {
176+
int end = fromIndex + length;
177+
int result = initialValue;
178+
for (int i = fromIndex; i < end; i++) {
179+
result = 31 * result + a[i];
180+
}
181+
return result;
182+
}
183+
}

0 commit comments

Comments
 (0)