Skip to content

Commit 4dc61ab

Browse files
committed
Preserve already computed hash code in Java String to TruffleString conversion.
1 parent 52bbeeb commit 4dc61ab

File tree

5 files changed

+48
-18
lines changed

5 files changed

+48
-18
lines changed

truffle/src/com.oracle.truffle.api.strings/src/com/oracle/truffle/api/strings/AbstractTruffleString.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ public abstract sealed class AbstractTruffleString permits TruffleString, Mutabl
133133

134134
static final int MASKED_ZERO_HASH_CODE = -1;
135135

136-
AbstractTruffleString(Object data, int offset, int length, int stride, Encoding encoding, int flags, int codePointLength, int codeRange) {
136+
AbstractTruffleString(Object data, int offset, int length, int stride, Encoding encoding, int flags, int codePointLength, int codeRange, int hashCode) {
137137
validateData(data, offset, length, stride);
138138
assert isByte(stride);
139139
assert isByte(flags);
@@ -147,6 +147,7 @@ public abstract sealed class AbstractTruffleString permits TruffleString, Mutabl
147147
this.flags = (byte) flags;
148148
this.codeRange = (byte) codeRange;
149149
this.codePointLength = codePointLength;
150+
this.hashCode = hashCode;
150151
}
151152

152153
static boolean isByte(int i) {

truffle/src/com.oracle.truffle.api.strings/src/com/oracle/truffle/api/strings/MutableTruffleString.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -71,7 +71,7 @@
7171
public final class MutableTruffleString extends AbstractTruffleString {
7272

7373
private MutableTruffleString(Object data, int offset, int length, int stride, int codePointLength, Encoding encoding) {
74-
super(data, offset, length, stride, encoding, 0, codePointLength, TSCodeRange.getUnknownCodeRangeForEncoding(encoding.id));
74+
super(data, offset, length, stride, encoding, 0, codePointLength, TSCodeRange.getUnknownCodeRangeForEncoding(encoding.id), 0);
7575
assert data instanceof byte[] || data instanceof NativePointer;
7676
}
7777

truffle/src/com.oracle.truffle.api.strings/src/com/oracle/truffle/api/strings/TStringInternalNodes.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2009,7 +2009,8 @@ static TruffleString doNonEmpty(Node node, String javaString, int charOffset, in
20092009
TStringOps.arraycopyWithStride(node, arrayJS, offsetJS + byteArrayBaseOffset(), 0, 0, array, offset + byteArrayBaseOffset(), 0, 0, length << stride);
20102010
}
20112011
}
2012-
TruffleString ret = TruffleString.createFromByteArray(array, offset, length, stride, Encoding.UTF_16, codePointLength, codeRange, true);
2012+
int hash = TStringUnsafe.getJavaStringHashMasked(javaString);
2013+
TruffleString ret = TruffleString.createFromByteArray(array, offset, length, stride, Encoding.UTF_16, codePointLength, codeRange, hash, true);
20132014
if (length == javaString.length()) {
20142015
assert charOffset == 0;
20152016
TruffleString wrapped = TruffleString.createWrapJavaString(javaString, codePointLength, codeRange);
@@ -2228,7 +2229,7 @@ static TruffleString transcode(Node node, AbstractTruffleString a, byte[] arrayA
22282229
CompilerAsserts.partialEvaluationConstant(errorHandler);
22292230
if (AbstractTruffleString.DEBUG_STRICT_ENCODING_CHECKS && a.isImmutable() && TSCodeRange.isMoreRestrictiveThan(codeRangeA, targetEncoding.maxCompatibleCodeRange)) {
22302231
if (a.stride() == 0) {
2231-
return TruffleString.createFromArray(arrayA, a.offset(), a.length(), 0, targetEncoding, codePointLengthA, codeRangeA, false);
2232+
return TruffleString.createFromByteArray(arrayA, a.offset(), a.length(), 0, targetEncoding, codePointLengthA, codeRangeA, false);
22322233
}
22332234
int targetStride = Stride.fromCodeRange(codeRangeA, targetEncoding);
22342235
byte[] array = TStringOps.arraycopyOfWithStride(node, arrayA, offsetA, a.length(), a.stride(), a.length(), targetStride);

truffle/src/com.oracle.truffle.api.strings/src/com/oracle/truffle/api/strings/TStringUnsafe.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ private static int getJavaSpecificationVersion() {
6565
private static final long javaStringValueFieldOffset;
6666
private static final long javaStringCoderFieldOffset;
6767
private static final long javaStringHashFieldOffset;
68+
private static final long javaStringHashIsZeroFieldOffset;
6869

6970
static final boolean COMPACT_STRINGS_ENABLED;
7071

@@ -75,10 +76,12 @@ private static int getJavaSpecificationVersion() {
7576
Field valueField = getStringDeclaredField("value");
7677
Field coderField = getStringDeclaredField("coder");
7778
Field hashField = getStringDeclaredField("hash");
79+
Field hashIsZeroField = getStringDeclaredField("hashIsZero");
7880
Field compactStringsField = getStringDeclaredField("COMPACT_STRINGS");
7981
javaStringValueFieldOffset = getObjectFieldOffset(valueField);
8082
javaStringCoderFieldOffset = getObjectFieldOffset(coderField);
8183
javaStringHashFieldOffset = getObjectFieldOffset(hashField);
84+
javaStringHashIsZeroFieldOffset = getObjectFieldOffset(hashIsZeroField);
8285
COMPACT_STRINGS_ENABLED = UNSAFE.getBoolean(getStaticFieldBase(compactStringsField), getStaticFieldOffset(compactStringsField));
8386
}
8487

@@ -144,6 +147,22 @@ static int getJavaStringStride(String s) {
144147
return UNSAFE.getByte(s, javaStringCoderFieldOffset);
145148
}
146149

150+
static int getJavaStringHash(String s) {
151+
return UNSAFE.getInt(s, javaStringHashFieldOffset);
152+
}
153+
154+
static boolean getJavaStringHashIsZero(String s) {
155+
return UNSAFE.getBoolean(s, javaStringHashIsZeroFieldOffset);
156+
}
157+
158+
static int getJavaStringHashMasked(String s) {
159+
int hash = getJavaStringHash(s);
160+
if (CompilerDirectives.injectBranchProbability(CompilerDirectives.UNLIKELY_PROBABILITY, hash == 0 && getJavaStringHashIsZero(s))) {
161+
return TruffleString.HashCodeNode.maskZero(hash);
162+
}
163+
return hash;
164+
}
165+
147166
@TruffleBoundary
148167
private static String allocateJavaString() {
149168
try {

truffle/src/com.oracle.truffle.api.strings/src/com/oracle/truffle/api/strings/TruffleString.java

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -144,35 +144,43 @@ private static VarHandle initializeNextUpdater() {
144144
private static final byte FLAG_CACHE_HEAD = (byte) 0x80;
145145
TruffleString next;
146146

147-
private TruffleString(Object data, int offset, int length, int stride, Encoding encoding, int codePointLength, int codeRange, boolean isCacheHead) {
148-
super(data, offset, length, stride, encoding, isCacheHead ? FLAG_CACHE_HEAD : 0, codePointLength, codeRange);
147+
private TruffleString(Object data, int offset, int length, int stride, Encoding encoding, int codePointLength, int codeRange, int hashCode, boolean isCacheHead) {
148+
super(data, offset, length, stride, encoding, isCacheHead ? FLAG_CACHE_HEAD : 0, codePointLength, codeRange, hashCode);
149149
}
150150

151-
private static TruffleString create(Object data, int offset, int length, int stride, Encoding encoding, int codePointLength, int codeRange, boolean isCacheHead) {
152-
TruffleString string = new TruffleString(data, offset, length, stride, encoding, codePointLength, codeRange, isCacheHead);
151+
private static TruffleString create(Object data, int offset, int length, int stride, Encoding encoding, int codePointLength, int codeRange, int hashCode, boolean isCacheHead) {
152+
TruffleString string = new TruffleString(data, offset, length, stride, encoding, codePointLength, codeRange, hashCode, isCacheHead);
153153
if (AbstractTruffleString.DEBUG_ALWAYS_CREATE_JAVA_STRING) {
154154
string.toJavaStringUncached();
155155
}
156156
return string;
157157
}
158158

159159
static TruffleString createFromByteArray(byte[] bytes, int length, int stride, Encoding encoding, int codePointLength, int codeRange) {
160-
return createFromByteArray(bytes, length, stride, encoding, codePointLength, codeRange, true);
160+
return createFromByteArray(bytes, 0, length, stride, encoding, codePointLength, codeRange, true);
161161
}
162162

163163
static TruffleString createFromByteArray(byte[] bytes, int length, int stride, Encoding encoding, int codePointLength, int codeRange, boolean isCacheHead) {
164-
return createFromArray(bytes, 0, length, stride, encoding, codePointLength, codeRange, isCacheHead);
164+
return createFromByteArray(bytes, 0, length, stride, encoding, codePointLength, codeRange, 0, isCacheHead);
165165
}
166166

167167
static TruffleString createFromByteArray(byte[] bytes, int offset, int length, int stride, Encoding encoding, int codePointLength, int codeRange, boolean isCacheHead) {
168-
return createFromArray(bytes, offset, length, stride, encoding, codePointLength, codeRange, isCacheHead);
168+
return createFromByteArray(bytes, offset, length, stride, encoding, codePointLength, codeRange, 0, isCacheHead);
169+
}
170+
171+
static TruffleString createFromByteArray(byte[] bytes, int offset, int length, int stride, Encoding encoding, int codePointLength, int codeRange, int hashCode, boolean isCacheHead) {
172+
return createFromArray(bytes, offset, length, stride, encoding, codePointLength, codeRange, hashCode, isCacheHead);
169173
}
170174

171175
static TruffleString createFromArray(Object bytes, int offset, int length, int stride, Encoding encoding, int codePointLength, int codeRange) {
172176
return createFromArray(bytes, offset, length, stride, encoding, codePointLength, codeRange, true);
173177
}
174178

175179
static TruffleString createFromArray(Object bytes, int offset, int length, int stride, Encoding encoding, int codePointLength, int codeRange, boolean isCacheHead) {
180+
return createFromArray(bytes, offset, length, stride, encoding, codePointLength, codeRange, 0, isCacheHead);
181+
}
182+
183+
static TruffleString createFromArray(Object bytes, int offset, int length, int stride, Encoding encoding, int codePointLength, int codeRange, int hashCode, boolean isCacheHead) {
176184
assert bytes instanceof byte[] || isInlinedJavaString(bytes) || bytes instanceof NativePointer;
177185
assert offset >= 0;
178186
assert bytes instanceof NativePointer || offset + ((long) length << stride) <= ((byte[]) bytes).length;
@@ -183,9 +191,9 @@ static TruffleString createFromArray(Object bytes, int offset, int length, int s
183191
int add = byteLength;
184192
byte[] copy = new byte[add + byteLength];
185193
System.arraycopy(bytes, offset, copy, add, byteLength);
186-
return TruffleString.create(copy, add, length, stride, encoding, codePointLength, codeRange, isCacheHead);
194+
return TruffleString.create(copy, add, length, stride, encoding, codePointLength, codeRange, hashCode, isCacheHead);
187195
}
188-
return TruffleString.create(bytes, offset, length, stride, encoding, codePointLength, codeRange, isCacheHead);
196+
return TruffleString.create(bytes, offset, length, stride, encoding, codePointLength, codeRange, hashCode, isCacheHead);
189197
}
190198

191199
static TruffleString createConstant(byte[] bytes, int length, int stride, Encoding encoding, int codePointLength, int codeRange) {
@@ -201,7 +209,7 @@ static TruffleString createConstant(byte[] bytes, int length, int stride, Encodi
201209

202210
static TruffleString createLazyLong(long value, Encoding encoding) {
203211
int length = NumberConversion.stringLengthLong(value);
204-
return TruffleString.create(new LazyLong(value), 0, length, 0, encoding, length, TSCodeRange.get7Bit(), true);
212+
return TruffleString.create(new LazyLong(value), 0, length, 0, encoding, length, TSCodeRange.get7Bit(), 0, true);
205213
}
206214

207215
static TruffleString createLazyConcat(TruffleString a, TruffleString b, Encoding encoding, int length, int stride, int codeRange) {
@@ -210,12 +218,13 @@ static TruffleString createLazyConcat(TruffleString a, TruffleString b, Encoding
210218
assert a.isLooselyCompatibleTo(encoding);
211219
assert b.isLooselyCompatibleTo(encoding);
212220
assert length == a.length() + b.length();
213-
return TruffleString.create(new LazyConcat(a, b), 0, length, stride, encoding, a.codePointLength() + b.codePointLength(), codeRange, true);
221+
return TruffleString.create(new LazyConcat(a, b), 0, length, stride, encoding, a.codePointLength() + b.codePointLength(), codeRange, 0, true);
214222
}
215223

216224
static TruffleString createWrapJavaString(String str, int codePointLength, int codeRange) {
217225
int stride = TStringUnsafe.getJavaStringStride(str);
218-
return TruffleString.create(str, 0, str.length(), stride, Encoding.UTF_16, codePointLength, codeRange, false);
226+
int hash = TStringUnsafe.getJavaStringHashMasked(str);
227+
return TruffleString.create(str, 0, str.length(), stride, Encoding.UTF_16, codePointLength, codeRange, hash, false);
219228
}
220229

221230
private static boolean attrsAreCorrect(Object dataA, Encoding encoding, int offset, int length, int codePointLength, int codeRange, int stride) {
@@ -3083,7 +3092,7 @@ final int calculateHash(AbstractTruffleString a, Encoding expectedEncoding,
30833092
return h;
30843093
}
30853094

3086-
private static int maskZero(int rawHashCode) {
3095+
static int maskZero(int rawHashCode) {
30873096
int h = rawHashCode;
30883097
if (h == 0) {
30893098
h--;

0 commit comments

Comments
 (0)