Skip to content

Commit 097d186

Browse files
committed
[GR-70609] Improve deserialization of class constants in replay compilation
PullRequest: graal/22323
2 parents 0951a04 + b3edc27 commit 097d186

File tree

3 files changed

+115
-40
lines changed

3 files changed

+115
-40
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package jdk.graal.compiler.hotspot.replaycomp;
26+
27+
/**
28+
* A surrogate for a deserialized {@link Class} object, which is needed on libgraal since libgraal
29+
* may not be able to find a {@link Class} using {@link Class#forName(String)}. The surrogate may be
30+
* used in place of an argument value of a recorded operation (e.g.,
31+
* {@link jdk.vm.ci.meta.MetaAccessProvider#lookupJavaType(Class)}), in which case the replay code
32+
* calls {@link ClassSurrogate#equals(Object)} to check the equivalence of arguments.
33+
*
34+
* @param name the name of the class as returned by {@link Class#getName()}
35+
*/
36+
record ClassSurrogate(String name) {
37+
@Override
38+
public boolean equals(Object obj) {
39+
if (obj instanceof Class<?> clazz) {
40+
return name.equals(clazz.getName());
41+
}
42+
return false;
43+
}
44+
}

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replaycomp/RecordedOperationPersistence.java

Lines changed: 66 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import java.util.Collection;
3636
import java.util.EnumSet;
3737
import java.util.List;
38+
import java.util.Map;
3839
import java.util.Optional;
3940

4041
import org.graalvm.collections.EconomicMap;
@@ -43,7 +44,7 @@
4344
import jdk.graal.compiler.debug.GraalError;
4445
import jdk.graal.compiler.hotspot.Platform;
4546
import jdk.graal.compiler.hotspot.replaycomp.proxy.CompilationProxy;
46-
import jdk.graal.compiler.hotspot.stubs.IllegalArgumentExceptionArgumentIsNotAnArrayStub;
47+
import jdk.graal.compiler.util.EconomicHashMap;
4748
import jdk.graal.compiler.util.json.JsonBuilder;
4849
import jdk.graal.compiler.util.json.JsonParser;
4950
import jdk.graal.compiler.util.json.JsonWriter;
@@ -57,19 +58,24 @@
5758
import jdk.vm.ci.common.JVMCIError;
5859
import jdk.vm.ci.hotspot.HotSpotCompilationRequest;
5960
import jdk.vm.ci.hotspot.HotSpotCompressedNullConstant;
61+
import jdk.vm.ci.hotspot.HotSpotResolvedJavaField;
6062
import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
63+
import jdk.vm.ci.hotspot.HotSpotResolvedObjectType;
6164
import jdk.vm.ci.hotspot.HotSpotSpeculationLog;
6265
import jdk.vm.ci.hotspot.VMField;
6366
import jdk.vm.ci.hotspot.aarch64.AArch64HotSpotRegisterConfig;
6467
import jdk.vm.ci.hotspot.amd64.AMD64HotSpotRegisterConfig;
6568
import jdk.vm.ci.hotspot.riscv64.RISCV64HotSpotRegisterConfig;
69+
import jdk.vm.ci.meta.AllocatableValue;
6670
import jdk.vm.ci.meta.Assumptions;
71+
import jdk.vm.ci.meta.DeoptimizationReason;
6772
import jdk.vm.ci.meta.EncodedSpeculationReason;
6873
import jdk.vm.ci.meta.ExceptionHandler;
6974
import jdk.vm.ci.meta.JavaConstant;
7075
import jdk.vm.ci.meta.JavaKind;
7176
import jdk.vm.ci.meta.JavaType;
7277
import jdk.vm.ci.meta.JavaTypeProfile;
78+
import jdk.vm.ci.meta.MethodHandleAccessProvider;
7379
import jdk.vm.ci.meta.PrimitiveConstant;
7480
import jdk.vm.ci.meta.ResolvedJavaMethod;
7581
import jdk.vm.ci.meta.ResolvedJavaType;
@@ -187,8 +193,41 @@ private DeserializationException(ObjectSerializer serializer, EconomicMap<String
187193
}
188194

189195
private static final class ClassSerializer implements ObjectSerializer {
190-
private static final Class<?>[] knownClasses = new Class<?>[]{String.class, System.class, Object[].class,
191-
ExceptionHandler.class, IllegalArgumentExceptionArgumentIsNotAnArrayStub.class};
196+
/**
197+
* Class constants mapped by name used during deserialization. Classes not present in this
198+
* map are deserialized as {@link ClassSurrogate}.
199+
*/
200+
private static final Map<String, Class<?>> knownClasses = createKnownClasses();
201+
202+
private static Map<String, Class<?>> createKnownClasses() {
203+
Class<?>[] classes = {
204+
// Needed to deserialize enum constants
205+
AMD64.CPUFeature.class, AArch64.CPUFeature.class, RISCV64.CPUFeature.class,
206+
DeoptimizationReason.class, JavaKind.class, MethodHandleAccessProvider.IntrinsicMethod.class,
207+
// Needed to deserialize array component types
208+
HotSpotResolvedJavaMethod.class, HotSpotResolvedJavaField.class, HotSpotResolvedObjectType.class,
209+
Object.class, ExceptionHandler.class, TriState.class, AllocatableValue.class, Value.class,
210+
// Needed to deserialize Field objects
211+
String.class,
212+
};
213+
Map<String, Class<?>> map = new EconomicHashMap<>();
214+
for (Class<?> clazz : classes) {
215+
map.put(clazz.getName(), clazz);
216+
}
217+
return map;
218+
}
219+
220+
/**
221+
* Casts a deserialized object to a {@link Class}, providing an actionable error message if
222+
* the class was deserialized as a {@link ClassSurrogate}.
223+
*/
224+
@SuppressWarnings("unchecked")
225+
public static <C> Class<C> classCast(Object clazz) {
226+
if (clazz instanceof ClassSurrogate(String name)) {
227+
throw new GraalError(String.format("Failed to find a Class object for %s. This can be fixed by adding %s.class to ClassSerializer#knownClasses.", name, name));
228+
}
229+
return (Class<C>) clazz;
230+
}
192231

193232
@Override
194233
public Class<?> clazz() {
@@ -206,22 +245,17 @@ public void serialize(Object instance, JsonBuilder.ObjectBuilder objectBuilder,
206245
}
207246

208247
@Override
209-
public Class<?> deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, ProxyFactory proxyFactory) throws DeserializationException {
248+
public Object deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, ProxyFactory proxyFactory) {
210249
String name = (String) json.get("name");
211250
Class<?> primitiveClass = Class.forPrimitiveName(name);
212251
if (primitiveClass != null) {
213252
return primitiveClass;
214253
}
215-
for (Class<?> knownClass : knownClasses) {
216-
if (knownClass.getName().equals(name)) {
217-
return knownClass;
218-
}
219-
}
220-
try {
221-
return Class.forName(name);
222-
} catch (ClassNotFoundException e) {
223-
throw new DeserializationException(this, json, e);
254+
Class<?> knownClass = knownClasses.get(name);
255+
if (knownClass != null) {
256+
return knownClass;
224257
}
258+
return new ClassSurrogate(name);
225259
}
226260
}
227261

@@ -404,7 +438,7 @@ public void serialize(Object instance, JsonBuilder.ObjectBuilder objectBuilder,
404438
}
405439

406440
@Override
407-
public Object deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, ProxyFactory proxyFactory) throws DeserializationException {
441+
public Object deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, ProxyFactory proxyFactory) {
408442
String loader = (String) json.get("loader");
409443
String module = (String) json.get("module");
410444
String moduleVer = (String) json.get("moduleVer");
@@ -434,7 +468,7 @@ public void serialize(Object instance, JsonBuilder.ObjectBuilder objectBuilder,
434468
}
435469

436470
@Override
437-
public Object deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, ProxyFactory proxyFactory) throws DeserializationException {
471+
public Object deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, ProxyFactory proxyFactory) {
438472
return json.get("content");
439473
}
440474
}
@@ -457,7 +491,7 @@ public void serialize(Object instance, JsonBuilder.ObjectBuilder objectBuilder,
457491
}
458492

459493
@Override
460-
public Object deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, ProxyFactory proxyFactory) throws DeserializationException {
494+
public Object deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, ProxyFactory proxyFactory) {
461495
return json.get("value");
462496
}
463497
}
@@ -513,7 +547,7 @@ public void serialize(Object instance, JsonBuilder.ObjectBuilder objectBuilder,
513547
}
514548

515549
@Override
516-
public Object deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, ProxyFactory proxyFactory) throws DeserializationException {
550+
public Object deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, ProxyFactory proxyFactory) {
517551
String className = (String) json.get("class");
518552
String value = (String) json.get("value");
519553
switch (className) {
@@ -609,22 +643,15 @@ public String tag() {
609643
@Override
610644
public void serialize(Object instance, JsonBuilder.ObjectBuilder objectBuilder, RecursiveSerializer serializer) throws IOException {
611645
Enum<?> en = (Enum<?>) instance;
612-
objectBuilder.append("holder", en.getClass().getName());
646+
serializer.serialize(en.getClass(), objectBuilder.append("holder"));
613647
objectBuilder.append("constant", en.name());
614648
}
615649

616650
@SuppressWarnings("unchecked")
617651
@Override
618652
public Object deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, ProxyFactory proxyFactory) throws DeserializationException {
619-
String holderName = (String) json.get("holder");
653+
Class<? extends Enum<?>> enumClass = (Class<? extends Enum<?>>) deserializer.deserialize(json.get("holder"), proxyFactory);
620654
String constantName = (String) json.get("constant");
621-
Class<? extends Enum<?>> enumClass;
622-
try {
623-
enumClass = (Class<? extends Enum<?>>) Class.forName(holderName);
624-
} catch (ClassNotFoundException e) {
625-
throw new DeserializationException(this, json, e);
626-
}
627-
// TODO This could be more efficient.
628655
for (Enum<?> constant : enumClass.getEnumConstants()) {
629656
if (constant.name().equals(constantName)) {
630657
return constant;
@@ -676,7 +703,7 @@ public void serialize(Object instance, JsonBuilder.ObjectBuilder objectBuilder,
676703
@SuppressWarnings("unchecked")
677704
@Override
678705
public Object deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, ProxyFactory proxyFactory) throws DeserializationException {
679-
Class<?> component = (Class<?>) deserializer.deserialize(json.get("component"), proxyFactory);
706+
Class<?> component = ClassSerializer.classCast(deserializer.deserialize(json.get("component"), proxyFactory));
680707
List<Object> elements = (List<Object>) json.get("elements");
681708
Object[] array = (Object[]) Array.newInstance(component, elements.size());
682709
for (int i = 0; i < elements.size(); i++) {
@@ -813,7 +840,7 @@ public void serialize(Object instance, JsonBuilder.ObjectBuilder objectBuilder,
813840
}
814841

815842
@Override
816-
public Object deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, ProxyFactory proxyFactory) throws DeserializationException {
843+
public Object deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, ProxyFactory proxyFactory) {
817844
return singleton;
818845
}
819846
}
@@ -868,7 +895,7 @@ public void serialize(Object instance, JsonBuilder.ObjectBuilder objectBuilder,
868895

869896
@Override
870897
public Object deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, ProxyFactory proxyFactory) throws DeserializationException {
871-
Class<?> holder = (Class<?>) deserializer.deserialize(json.get("holder"), proxyFactory);
898+
Class<?> holder = ClassSerializer.classCast(deserializer.deserialize(json.get("holder"), proxyFactory));
872899
String name = (String) json.get("name");
873900
try {
874901
if (holder == String.class) {
@@ -1077,7 +1104,7 @@ public void serialize(Object instance, JsonBuilder.ObjectBuilder objectBuilder,
10771104
}
10781105

10791106
@Override
1080-
public Object deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, ProxyFactory proxyFactory) throws DeserializationException {
1107+
public Object deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, ProxyFactory proxyFactory) {
10811108
String name = (String) json.get("name");
10821109
return UnresolvedJavaType.create(name);
10831110
}
@@ -1190,11 +1217,11 @@ public String tag() {
11901217
}
11911218

11921219
@Override
1193-
public void serialize(Object instance, JsonBuilder.ObjectBuilder objectBuilder, RecursiveSerializer serializer) throws IOException {
1220+
public void serialize(Object instance, JsonBuilder.ObjectBuilder objectBuilder, RecursiveSerializer serializer) {
11941221
}
11951222

11961223
@Override
1197-
public Object deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, ProxyFactory proxyFactory) throws DeserializationException {
1224+
public Object deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, ProxyFactory proxyFactory) {
11981225
return new ForeignCallDescriptorSurrogate();
11991226
}
12001227
}
@@ -1621,7 +1648,7 @@ public void serialize(Object instance, JsonBuilder.ObjectBuilder objectBuilder,
16211648
@Override
16221649
@SuppressWarnings("unchecked")
16231650
public Object deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, ProxyFactory proxyFactory) throws DeserializationException {
1624-
Class<UnknownEnum> elementType = (Class<UnknownEnum>) deserializer.deserialize(json.get("enum"), proxyFactory);
1651+
Class<UnknownEnum> elementType = ClassSerializer.classCast(deserializer.deserialize(json.get("enum"), proxyFactory));
16251652
return asEnumSet(elementType, (List<Object>) json.get("ordinals"));
16261653
}
16271654

@@ -1790,19 +1817,19 @@ public String tag() {
17901817
@Override
17911818
public void serialize(Object instance, JsonBuilder.ObjectBuilder objectBuilder, RecursiveSerializer serializer) throws IOException {
17921819
Throwable throwable = (Throwable) instance;
1793-
serializer.serialize(throwable.getClass(), objectBuilder.append("class"));
1820+
objectBuilder.append("class", throwable.getClass().getName());
17941821
objectBuilder.append("message", throwable.getMessage());
17951822
}
17961823

17971824
@Override
1798-
public Object deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, ProxyFactory proxyFactory) throws DeserializationException {
1799-
Class<?> clazz = (Class<?>) deserializer.deserialize(json.get("class"), proxyFactory);
1825+
public Object deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, ProxyFactory proxyFactory) {
1826+
String clazz = (String) json.get("class");
18001827
String message = (String) json.get("message");
1801-
if (clazz == JVMCIError.class) {
1828+
if (clazz.equals(JVMCIError.class.getName())) {
18021829
return new JVMCIError(message);
1803-
} else if (clazz == GraalError.class) {
1830+
} else if (clazz.equals(GraalError.class.getName())) {
18041831
return new GraalError(message);
1805-
} else if (clazz == IllegalArgumentException.class) {
1832+
} else if (clazz.equals(IllegalArgumentException.class.getName())) {
18061833
return new IllegalArgumentException(message);
18071834
} else {
18081835
return new Throwable(message);

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replaycomp/ReplayCompilationProxies.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,11 @@ private Object findResult(ProxyInfo proxyInfo, CompilationProxy.SymbolicMethod m
569569
entry: for (int j = 0; j < entries; j++) {
570570
for (int k = 0; k < method.paramCount(); k++) {
571571
Object arg = operationResults[i++];
572-
if (!Objects.equals(args[k], arg)) {
572+
/*
573+
* Call equals on the deserialized object to allow the deserialized object
574+
* to override equals behavior (i.e., to support ClassSurrogate).
575+
*/
576+
if (!Objects.equals(arg, args[k])) {
573577
i += method.paramCount() - k;
574578
continue entry;
575579
}

0 commit comments

Comments
 (0)