From ebc86cf979e1ef34641733422278f121759bcd58 Mon Sep 17 00:00:00 2001 From: Peter Hofer Date: Thu, 3 Oct 2024 11:33:58 +0200 Subject: [PATCH 01/13] Disable strengthened graphs for now. --- .../graal/pointsto/heap/ImageLayerLoader.java | 21 +------------------ .../graal/pointsto/heap/ImageLayerWriter.java | 6 ++++++ .../svm/hosted/heap/SVMImageLayerLoader.java | 20 ------------------ 3 files changed, 7 insertions(+), 40 deletions(-) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerLoader.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerLoader.java index c4d70dc9e08f..59f41684ea3e 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerLoader.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerLoader.java @@ -132,9 +132,6 @@ import com.oracle.graal.pointsto.meta.BaseLayerField; import com.oracle.graal.pointsto.meta.BaseLayerMethod; import com.oracle.graal.pointsto.meta.BaseLayerType; -import com.oracle.graal.pointsto.meta.PointsToAnalysisField; -import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod; -import com.oracle.graal.pointsto.meta.PointsToAnalysisType; import com.oracle.graal.pointsto.util.AnalysisError; import com.oracle.graal.pointsto.util.AnalysisFuture; import com.oracle.svm.util.ReflectionUtil; @@ -818,7 +815,7 @@ public AnalysisParsedGraph getAnalysisParsedGraph(AnalysisMethod analysisMethod) Boolean intrinsic = get(methodData, INTRINSIC_TAG); EncodedGraph analyzedGraph = (EncodedGraph) ObjectCopier.decode(imageLayerSnapshotUtil.getGraphDecoder(this, analysisMethod, universe.getSnippetReflection()), encodedAnalyzedGraph); if (hasStrengthenedGraph(analysisMethod)) { - loadAllAnalysisElements(readEncodedGraph(methodData, STRENGTHENED_GRAPH_TAG)); + throw AnalysisError.shouldNotReachHere("Strengthened graphs are not supported until late loading is implemented."); } afterGraphDecodeHook(analyzedGraph); return new AnalysisParsedGraph(analyzedGraph, intrinsic); @@ -865,22 +862,6 @@ protected void afterGraphDecodeHook(EncodedGraph encodedGraph) { } - private void loadAllAnalysisElements(String encoding) { - encoding.lines().forEach(this::loadEncodedGraphLineAnalysisElements); - } - - protected void loadEncodedGraphLineAnalysisElements(String line) { - if (line.contains(PointsToAnalysisType.class.getName())) { - getAnalysisType(getId(line)); - } else if (line.contains(PointsToAnalysisMethod.class.getName())) { - getAnalysisMethod(getId(line)); - } else if (line.contains(PointsToAnalysisField.class.getName())) { - getAnalysisField(getId(line)); - } else if (line.contains(ImageHeapInstance.class.getName()) || line.contains(ImageHeapObjectArray.class.getName()) || line.contains(ImageHeapPrimitiveArray.class.getName())) { - getOrCreateConstant(getId(line)); - } - } - protected static int getId(String line) { return Integer.parseInt(line.split(" = ")[1]); } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerWriter.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerWriter.java index 68d939aa2795..43e9d6a064f3 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerWriter.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerWriter.java @@ -161,6 +161,7 @@ public class ImageLayerWriter { private FileInfo fileInfo; private GraphsOutput graphsOutput; private final boolean useSharedLayerGraphs; + private final boolean useSharedLayerStrengthenedGraphs; protected final Set> elementsToPersist = ConcurrentHashMap.newKeySet(); @@ -213,6 +214,7 @@ public ImageLayerWriter() { @SuppressWarnings({"this-escape", "unused"}) public ImageLayerWriter(boolean useSharedLayerGraphs) { this.useSharedLayerGraphs = useSharedLayerGraphs; + this.useSharedLayerStrengthenedGraphs = false; this.jsonMap = EconomicMap.create(); this.constantsToRelink = new ArrayList<>(); this.persistedTypeIds = ConcurrentHashMap.newKeySet(); @@ -469,6 +471,10 @@ public void persistAnalysisParsedGraph(AnalysisMethod method) { } public void persistMethodStrengthenedGraph(AnalysisMethod method) { + if (!useSharedLayerStrengthenedGraphs) { + return; + } + EconomicMap methodMap = getMethodMap(method); if (!methodMap.containsKey(STRENGTHENED_GRAPH_TAG)) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerLoader.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerLoader.java index a6903f1beba8..5071af10b285 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerLoader.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerLoader.java @@ -77,12 +77,6 @@ import com.oracle.svm.hosted.SVMHost; import com.oracle.svm.hosted.c.CGlobalDataFeature; import com.oracle.svm.hosted.imagelayer.HostedDynamicLayerInfo; -import com.oracle.svm.hosted.meta.HostedArrayClass; -import com.oracle.svm.hosted.meta.HostedField; -import com.oracle.svm.hosted.meta.HostedInstanceClass; -import com.oracle.svm.hosted.meta.HostedInterface; -import com.oracle.svm.hosted.meta.HostedMethod; -import com.oracle.svm.hosted.meta.HostedPrimitiveType; import com.oracle.svm.hosted.meta.HostedUniverse; import com.oracle.svm.hosted.meta.RelocatableConstant; import com.oracle.svm.hosted.util.IdentityHashCodeUtil; @@ -201,20 +195,6 @@ protected void afterGraphDecodeHook(EncodedGraph encodedGraph) { } } - @Override - protected void loadEncodedGraphLineAnalysisElements(String line) { - if (line.contains(HostedInstanceClass.class.getName()) || line.contains(HostedPrimitiveType.class.getName()) || line.contains(HostedArrayClass.class.getName()) || - line.contains(HostedInterface.class.getName())) { - getAnalysisType(getId(line)); - } else if (line.contains(HostedMethod.class.getName())) { - getAnalysisMethod(getId(line)); - } else if (line.contains(HostedField.class.getName())) { - getAnalysisField(getId(line)); - } else { - super.loadEncodedGraphLineAnalysisElements(line); - } - } - @Override protected void prepareConstantRelinking(EconomicMap constantData, int identityHashCode, int id) { Integer tid = get(constantData, CLASS_ID_TAG); From ab6a45df68c03edaa8c99f999f37f3132f637b41 Mon Sep 17 00:00:00 2001 From: Peter Hofer Date: Tue, 1 Oct 2024 17:22:42 +0200 Subject: [PATCH 02/13] Binary ObjectCopier encoding. --- .../compiler/util/test/ObjectCopierTest.java | 11 +- .../compiler/hotspot/libgraal/BuildTime.java | 6 +- .../hotspot/libgraal/CompilerConfig.java | 4 +- .../jdk/graal/compiler/util/ObjectCopier.java | 254 +++++++----------- .../util/ObjectCopierInputStream.java | 34 +++ .../util/ObjectCopierOutputStream.java | 34 +++ .../graal/pointsto/heap/ImageLayerLoader.java | 8 +- .../pointsto/heap/ImageLayerSnapshotUtil.java | 56 ++-- .../graal/pointsto/heap/ImageLayerWriter.java | 30 ++- .../hotspot/libgraal/LibGraalFeature.java | 22 +- .../svm/graal/hotspot/GetCompilerConfig.java | 8 +- .../heap/SVMImageLayerSnapshotUtil.java | 67 ++--- 12 files changed, 287 insertions(+), 247 deletions(-) create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/util/test/ObjectCopierTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/util/test/ObjectCopierTest.java index 98b6513eafbb..213ebc8f1d3c 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/util/test/ObjectCopierTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/util/test/ObjectCopierTest.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Base64; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.LinkedHashMap; @@ -175,20 +176,20 @@ public void testIt() { List externalValueFields = List.of(ObjectCopier.getField(BaseClass.class, "BASE_SINGLETON"), ObjectCopier.getField(TestObject.class, "TEST_OBJECT_SINGLETON")); - String encoded = ObjectCopier.encode(new ObjectCopier.Encoder(externalValueFields), root); + byte[] encoded = ObjectCopier.encode(new ObjectCopier.Encoder(externalValueFields), root); if (DEBUG) { - System.out.printf("encoded:%n%s%n", encoded); + System.out.printf("encoded:%n%s%n", Base64.getEncoder().encodeToString(encoded)); } Object decoded = ObjectCopier.decode(encoded, loader); if (DEBUG) { System.out.printf("root:%n%s%n", root); System.out.printf("decoded:%n%s%n", decoded); } - String reencoded = ObjectCopier.encode(new ObjectCopier.Encoder(externalValueFields), decoded); + byte[] reencoded = ObjectCopier.encode(new ObjectCopier.Encoder(externalValueFields), decoded); if (DEBUG) { - System.out.printf("reencoded:%n%s%n", reencoded); + System.out.printf("reencoded:%n%s%n", Base64.getEncoder().encodeToString(reencoded)); } - Assert.assertEquals(encoded, reencoded); + Assert.assertArrayEquals(encoded, reencoded); Map root2 = (Map) ObjectCopier.decode(reencoded, loader); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/BuildTime.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/BuildTime.java index 125e6f54bbfa..4d23c3ac3989 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/BuildTime.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/BuildTime.java @@ -39,14 +39,14 @@ import java.util.function.Consumer; import java.util.function.Supplier; -import jdk.graal.compiler.core.ArchitectureSpecific; -import jdk.graal.compiler.hotspot.CompilerConfigurationFactory; import org.graalvm.collections.EconomicMap; +import jdk.graal.compiler.core.ArchitectureSpecific; import jdk.graal.compiler.core.common.spi.ForeignCallSignature; import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.debug.TTY; import jdk.graal.compiler.graph.NodeClass; +import jdk.graal.compiler.hotspot.CompilerConfigurationFactory; import jdk.graal.compiler.hotspot.EncodedSnippets; import jdk.graal.compiler.hotspot.HotSpotForeignCallLinkage; import jdk.graal.compiler.hotspot.HotSpotReplacementsImpl; @@ -148,7 +148,7 @@ public static void configureGraalForLibGraal(String arch, Consumer> registerAsInHeap, Consumer>> hostedGraalSetFoldNodePluginClasses, String nativeImageLocationQualifier, - String encodedGuestObjects) { + byte[] encodedGuestObjects) { GraalError.guarantee(VALID_LOADER_NAME.equals(LOADER.getName()), "Only call this method from classloader " + VALID_LOADER_NAME); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/CompilerConfig.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/CompilerConfig.java index 28e61f20f7ea..70bae2af222e 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/CompilerConfig.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/CompilerConfig.java @@ -93,9 +93,9 @@ protected ClassInfo makeClassInfo(Class declaringClass) { return ci; } }; - String encoded = ObjectCopier.encode(encoder, encodedObjects); + byte[] encoded = ObjectCopier.encode(encoder, encodedObjects); - Files.writeString(Path.of(args[0]), encoded); + Files.write(Path.of(args[0]), encoded); } private static EncodedSnippets getEncodedSnippets(HotSpotReplacementsImpl replacements, OptionValues options) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java index 4be038275550..db69c3f4ff51 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java @@ -24,9 +24,10 @@ */ package jdk.graal.compiler.util; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.PrintStream; +import java.io.IOException; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -66,35 +67,18 @@ import jdk.internal.misc.Unsafe; /** - * Support for deep copying an object across processes by {@linkplain #encode encoding} it to a - * String in the first process and {@linkplain #decode decoding} it back into an object in the - * second process. This copying requires that the classes of the copied objects are the same in both + * Support for deep copying an object across processes by {@linkplain #encode encoding} it to bytes + * in the first process and {@linkplain #decode decoding} it back into an object in the second + * process. This copying requires that the classes of the copied objects are the same in both * processes with respect to fields. * - * Encoded format in EBNF: - * - *
- *  enc = line "\n" { line "\n" }
- *  line = header | objectField
- *  header = id ":" ( builtin | object | array | fieldRef )
- *  object = "{" className ":" fieldCount "}"
- *  objectField = "  " fieldName ":" id " = " fieldValue
- *  fieldValue = ( primitive | id )
- *  id = int
- *  array = "[" className "] = " elements
- *  elements = [ fieldValue { " " fieldValue } ]
- *  fieldRef = "@" className "." fieldName
- *  builtin = "<"  className [ ":" encodingName ] "> = " builtinValue
- * 
- * - * See the {@link Builtin} subclasses for the EBNF of builtinValue. + * See the {@link Builtin} subclasses for encoding of specific types. */ public class ObjectCopier { - private static final Pattern BUILTIN_LINE = Pattern.compile("<(?[^:}]+)(?::(?\\w+))?> = (?.*)"); private static final Pattern OBJECT_LINE = Pattern.compile("\\{(?[\\w.$]+):(?\\d+)}"); private static final Pattern ARRAY_LINE = Pattern.compile("\\[(?[^]]+)] = (?.*)"); - private static final Pattern FIELD_LINE = Pattern.compile("\\s*(?[^:]+):(?[^ ]+) = (?.*)"); + private static final Pattern FIELD_LINE = Pattern.compile("(?[^:]+):(?[^ ]+) = (?.*)"); /** * A builtin is specialized support for encoded and decoding values of specific types. @@ -133,16 +117,6 @@ final void checkClass(Class c) { "Unsupported %s type: %s", this.clazz.getName(), c.getName()); } - /** - * Gets the name of a non-default encoded used by this builtin for {@code obj}. - * - * @return null if the default encoded is used for {@code obj} - */ - @SuppressWarnings("unused") - String encodingName(Object obj) { - return null; - } - /** * Ensures object ids have are created for the values referenced by {@code obj} that will be * handled by this builtin when {@code obj} is encoded. For example, the values in a map. @@ -155,15 +129,12 @@ void makeChildIds(Encoder encoder, Object obj, ObjectPath objectPath) { * Encodes the value of {@code obj} to a String that does not contain {@code '\n'} or * {@code '\r'}. */ - protected abstract String encode(Encoder encoder, Object obj); + protected abstract void encode(Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException; /** * Decodes {@code encoded} to an object of a type handled by this builtin. - * - * @param encoding the non-default encoded used when encoded the object or null if the - * default encoded was used */ - protected abstract Object decode(Decoder decoder, Class concreteType, String encoding, String encoded); + protected abstract Object decode(Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException; @Override public String toString() { @@ -172,15 +143,8 @@ public String toString() { } /** - * Builtin for handling {@link Class} values. - * - * EBNF: - * - *
-     * builtinValue = className
-     * 
- * - * The className is in {@link Class#getName()} format. + * Builtin for handling {@link Class} values. The className is in {@link Class#getName()} + * format. */ static final class ClassBuiltin extends Builtin { @@ -189,12 +153,13 @@ static final class ClassBuiltin extends Builtin { } @Override - protected String encode(Encoder encoder, Object obj) { - return ((Class) obj).getName(); + protected void encode(Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { + stream.writeUTF(((Class) obj).getName()); } @Override - protected Object decode(Decoder decoder, Class concreteType, String encoding, String encoded) { + protected Object decode(Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { + String encoded = stream.readUTF(); return switch (encoded) { case "boolean" -> boolean.class; case "byte" -> byte.class; @@ -212,14 +177,6 @@ protected Object decode(Decoder decoder, Class concreteType, String encoding, /** * Builtin for handling {@link String} values. - * - * EBNF: - * - *
-     * builtinValue = string
-     * 
- * - * The string has no embedded \r or \n characters. */ static final class StringBuiltin extends Builtin { @@ -228,30 +185,14 @@ static final class StringBuiltin extends Builtin { } @Override - String encodingName(Object obj) { + protected void encode(Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { String s = obj instanceof String ? (String) obj : new String((char[]) obj); - if (s.indexOf('\n') != -1 || s.indexOf('\r') != -1) { - return "escaped"; - } - return super.encodingName(obj); + stream.writeUTF(s); } @Override - protected String encode(Encoder encoder, Object obj) { - String s = obj instanceof String ? (String) obj : new String((char[]) obj); - if ("escaped".equals(encodingName(s))) { - return s.replace("\\", "\\\\").replace("\n", "\\n").replace("\r", "\\r"); - } - return s; - } - - @Override - protected Object decode(Decoder decoder, Class concreteType, String encoding, String encoded) { - String s = encoded; - if (encoding != null) { - GraalError.guarantee(encoding.equals("escaped"), "Unknown encoded: %s", encoding); - s = encoded.replace("\\r", "\r").replace("\\n", "\n").replace("\\\\", "\\"); - } + protected Object decode(Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { + String s = stream.readUTF(); if (concreteType == char[].class) { return s.toCharArray(); } @@ -260,15 +201,8 @@ protected Object decode(Decoder decoder, Class concreteType, String encoding, } /** - * Builtin for handling {@link Enum} values. - * - * EBNF: - * - *
-     * builtinValue = enumName
-     * 
- * - * The enumName is given by {@link Enum#name()}. + * Builtin for handling {@link Enum} values. The value is described by its + * {@link Enum#ordinal()}. */ static final class EnumBuiltin extends Builtin { @@ -277,14 +211,15 @@ static final class EnumBuiltin extends Builtin { } @Override - protected String encode(Encoder encoder, Object obj) { - return ((Enum) obj).name(); + protected void encode(Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { + stream.writeInt(((Enum) obj).ordinal()); } - @SuppressWarnings({"unchecked", "rawtypes"}) @Override - protected Object decode(Decoder decoder, Class concreteType, String encoding, String encoded) { - return Enum.valueOf((Class) concreteType, encoded); + protected Object decode(Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { + int ord = stream.readInt(); + Object[] constants = concreteType.getEnumConstants(); + return constants[ord]; } } @@ -320,15 +255,17 @@ void makeChildIds(Encoder encoder, Object obj, ObjectPath objectPath) { } @Override - protected String encode(Encoder encoder, Object obj) { + protected void encode(Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { Map map = (Map) obj; - return encoder.encodeMap(new EconomicMapWrap<>(map)); + String encoded = encoder.encodeMap(new EconomicMapWrap<>(map)); + stream.writeUTF(encoded); } @SuppressWarnings("unchecked") @Override - protected Object decode(Decoder decoder, Class concreteType, String encoding, String encoded) { + protected Object decode(Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { Map map = (Map) factories.get(concreteType).get(); + String encoded = stream.readUTF(); decoder.decodeMap(encoded, map::put); return map; } @@ -359,14 +296,16 @@ void makeChildIds(Encoder encoder, Object obj, ObjectPath objectPath) { } @Override - protected String encode(Encoder encoder, Object obj) { - return encoder.encodeMap((UnmodifiableEconomicMap) obj); + protected void encode(Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { + String encoded = encoder.encodeMap((UnmodifiableEconomicMap) obj); + stream.writeUTF(encoded); } @Override - protected Object decode(Decoder decoder, Class concreteType, String encoding, String encoded) { + protected Object decode(Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { if (EconomicMap.class.isAssignableFrom(concreteType)) { EconomicMap map = EconomicMap.create(); + String encoded = stream.readUTF(); decoder.decodeMap(encoded, map::put); return map; } else { @@ -447,22 +386,24 @@ static String[] splitSpaceSeparatedElements(String elements) { /** * Encodes {@code root} to a String using {@code encoder}. */ - public static String encode(Encoder encoder, Object root) { + public static byte[] encode(Encoder encoder, Object root) { int rootId = encoder.makeId(root, ObjectPath.of("[root:" + root.getClass().getName() + "]")).id(); GraalError.guarantee(rootId == 1, "The root object should have id of 1, not %d", rootId); ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try (PrintStream ps = new PrintStream(baos)) { - encoder.encode(ps); + try (ObjectCopierOutputStream cos = new ObjectCopierOutputStream(baos)) { + encoder.encode(cos); + } catch (IOException e) { + throw new RuntimeException(e); } - return baos.toString(); + return baos.toByteArray(); } - public static Object decode(String encoded, ClassLoader loader) { + public static Object decode(byte[] encoded, ClassLoader loader) { Decoder decoder = new Decoder(loader); return decode(decoder, encoded); } - public static Object decode(Decoder decoder, String encoded) { + public static Object decode(Decoder decoder, byte[] encoded) { return decoder.decode(encoded); } @@ -513,41 +454,40 @@ private static void writeField(Field field, Object receiver, Object value) { /** * Action deferred due to unresolved object id. */ - record Deferred(Runnable runnable, int lineNum) { + record Deferred(Runnable runnable, int recordNum, int fieldNum) { } List deferred; - int lineNum = -1; + int recordNum = -1; + int fieldNum = -1; - private Object decode(String encoded) { + private Object decode(byte[] encoded) { deferred = new ArrayList<>(); - lineNum = 0; - - Iterator iter = encoded.lines().iterator(); - try { - while (iter.hasNext()) { - String line = iter.next(); - lineNum++; - int colon = line.indexOf(':'); - GraalError.guarantee(colon != -1, "Missing ':' in line: %s", line); - int id = Integer.parseInt(line.substring(0, colon)); - switch (line.charAt(colon + 1)) { + recordNum = 0; + + try (ObjectCopierInputStream stream = new ObjectCopierInputStream(new ByteArrayInputStream(encoded))) { + for (;;) { + recordNum++; + fieldNum = -1; + int c = stream.read(); + if (c == -1) { + break; + } + int id = stream.readInt(); + switch (c) { case '<': { - Matcher matcher = BUILTIN_LINE.matcher(line.substring(colon + 1)); - GraalError.guarantee(matcher.matches(), "Invalid builtin line: %s", line); - String className = matcher.group("class"); - String encodingName = matcher.group("encodingName"); - String value = matcher.group("value"); + String className = stream.readUTF(); Class clazz = loadClass(className); Builtin builtin = getBuiltin(clazz); - GraalError.guarantee(builtin != null, "No builtin for %s: %s", className, line); + GraalError.guarantee(builtin != null, "No builtin for %s in record %d", className, recordNum); builtin.checkClass(clazz); - addDecodedObject(id, builtin.decode(this, clazz, encodingName, value)); + addDecodedObject(id, builtin.decode(this, clazz, stream)); break; } case '[': { - Matcher matcher = ARRAY_LINE.matcher(line.substring(colon + 1)); - GraalError.guarantee(matcher.matches(), "Invalid array line: %s", line); + String legacy = stream.readUTF(); + Matcher matcher = ARRAY_LINE.matcher(legacy); + GraalError.guarantee(matcher.matches(), "Invalid array record: %s", legacy); String componentTypeName = matcher.group("componentType"); String[] elements = splitSpaceSeparatedElements(matcher.group("elements")); switch (componentTypeName) { @@ -625,19 +565,17 @@ private Object decode(String encoded) { break; } case '@': { - String fieldDesc = line.substring(colon + 2); - int lastDot = fieldDesc.lastIndexOf('.'); - GraalError.guarantee(lastDot != -1, "Invalid field name: %s", fieldDesc); - String className = fieldDesc.substring(0, lastDot); - String fieldName = fieldDesc.substring(lastDot + 1); + String className = stream.readUTF(); + String fieldName = stream.readUTF(); Class declaringClass = loadClass(className); Field field = getField(declaringClass, fieldName); addDecodedObject(id, readField(field, null)); break; } case '{': { - Matcher matcher = OBJECT_LINE.matcher(line.substring(colon + 1)); - GraalError.guarantee(matcher.matches(), "Invalid object line: %s", line); + String legacy = stream.readUTF(); + Matcher matcher = OBJECT_LINE.matcher(legacy); + GraalError.guarantee(matcher.matches(), "Invalid object record: %s", legacy); String className = matcher.group("class"); int fieldCount = Integer.parseInt(matcher.group("fieldCount")); Class clazz = loadClass(className); @@ -645,9 +583,8 @@ private Object decode(String encoded) { addDecodedObject(id, obj); ClassInfo classInfo = classInfos.computeIfAbsent(clazz, ClassInfo::of); for (int i = 0; i < fieldCount; i++) { - GraalError.guarantee(iter.hasNext(), "Truncated input"); - String fieldLine = iter.next(); - lineNum++; + fieldNum = i; + String fieldLine = stream.readUTF(); Matcher fieldMatcher = FIELD_LINE.matcher(fieldLine); GraalError.guarantee(fieldMatcher.matches(), "Invalid field line: %s", fieldLine); String fieldDesc = fieldMatcher.group("desc"); @@ -704,20 +641,21 @@ private Object decode(String encoded) { break; } default: { - throw new GraalError("Invalid char after ':' in line: %s", line); + throw new GraalError("Invalid char '%c' for kind in record %d", c, recordNum); } } } for (Deferred d : deferred) { - lineNum = d.lineNum(); + recordNum = d.recordNum(); + fieldNum = d.fieldNum; d.runnable().run(); } } catch (Throwable e) { - String line = encoded.lines().skip(lineNum - 1).findFirst().get(); - throw new GraalError(e, "Error on line %d: %s", lineNum, line); + throw new GraalError(e, "Error in record %d (field %d)", recordNum, fieldNum); } finally { deferred = null; - lineNum = -1; + recordNum = -1; + fieldNum = -1; } return getObject(1, true); } @@ -736,7 +674,7 @@ void resolveId(int id, Consumer c) { if (objValue != null) { c.accept(objValue); } else { - deferred.add(new Deferred(() -> c.accept(getObject(id, true)), lineNum)); + deferred.add(new Deferred(() -> c.accept(getObject(id, true)), recordNum, fieldNum)); } } else { c.accept(null); @@ -945,35 +883,47 @@ private ClassInfo makeClassInfo(Class clazz, ObjectPath objectPath) { } } - private void encode(PrintStream out) { + private void encode(ObjectCopierOutputStream out) throws IOException { for (int id = 1; id < objects.size(); id++) { Object obj = objects.get(id); Class clazz = obj.getClass(); Builtin builtin = getBuiltin(clazz); if (builtin != null) { - String encodingName = builtin.encodingName(obj); - String encoding = encodingName == null ? "" : ":" + encodingName; - out.printf("%d:<%s%s> = %s%n", id, clazz.getName(), encoding, builtin.encode(this, obj)); + out.writeByte('<'); + out.writeInt(id); + out.writeUTF(clazz.getName()); + try { + builtin.encode(this, out, obj); + } catch (IOException e) { + throw new RuntimeException(e); + } } else if (clazz.isArray()) { + out.writeByte('['); + out.writeInt(id); Class componentType = clazz.getComponentType(); if (!componentType.isPrimitive()) { String elements = Stream.of((Object[]) obj).map(this::getIdString).collect(Collectors.joining(" ")); - out.printf("%d:[%s] = %s%n", id, componentType.getName(), elements); + out.writeUTF(String.format("[%s] = %s", componentType.getName(), elements)); } else { int length = Array.getLength(obj); StringBuilder elements = new StringBuilder(length * 5); for (int i = 0; i < length; i++) { elements.append(' ').append(Array.get(obj, i)); } - out.printf("%d:[%s] =%s%n", id, componentType.getName(), elements); + out.writeUTF(String.format("[%s] =%s", componentType.getName(), elements)); } } else { if (clazz == Field.class) { Field field = (Field) obj; - out.printf("%d:@%s.%s%n", id, field.getDeclaringClass().getName(), field.getName()); + out.writeByte('@'); + out.writeInt(id); + out.writeUTF(field.getDeclaringClass().getName()); + out.writeUTF(field.getName()); } else { ClassInfo classInfo = classInfos.get(clazz); - out.printf("%d:{%s:%d}%n", id, clazz.getName(), classInfo.fields().size()); + out.writeByte('{'); + out.writeInt(id); + out.writeUTF(String.format("{%s:%d}", clazz.getName(), classInfo.fields().size())); for (var e : classInfo.fields().entrySet()) { Field f = e.getValue(); Object fValue = readField(f, obj); @@ -984,7 +934,7 @@ private void encode(PrintStream out) { } else if (fieldType == char.class) { fValue = (int) (Character) fValue; } - out.printf(" %s:%d = %s%n", e.getKey(), fieldTypeId, fValue); + out.writeUTF(String.format("%s:%d = %s", e.getKey(), fieldTypeId, fValue)); } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java new file mode 100644 index 000000000000..8826b36c7a7a --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ +package jdk.graal.compiler.util; + +import java.io.DataInputStream; +import java.io.InputStream; + +public class ObjectCopierInputStream extends DataInputStream { + public ObjectCopierInputStream(InputStream in) { + super(in); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java new file mode 100644 index 000000000000..a6a8891102a8 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ +package jdk.graal.compiler.util; + +import java.io.DataOutputStream; +import java.io.OutputStream; + +public class ObjectCopierOutputStream extends DataOutputStream { + public ObjectCopierOutputStream(OutputStream out) { + super(out); + } +} diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerLoader.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerLoader.java index 59f41684ea3e..8bd065e7ed11 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerLoader.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerLoader.java @@ -811,7 +811,7 @@ public boolean hasAnalysisParsedGraph(AnalysisMethod analysisMethod) { public AnalysisParsedGraph getAnalysisParsedGraph(AnalysisMethod analysisMethod) { EconomicMap methodData = getMethodData(analysisMethod); - String encodedAnalyzedGraph = readEncodedGraph(methodData, ANALYSIS_PARSED_GRAPH_TAG); + byte[] encodedAnalyzedGraph = readEncodedGraph(methodData, ANALYSIS_PARSED_GRAPH_TAG); Boolean intrinsic = get(methodData, INTRINSIC_TAG); EncodedGraph analyzedGraph = (EncodedGraph) ObjectCopier.decode(imageLayerSnapshotUtil.getGraphDecoder(this, analysisMethod, universe.getSnippetReflection()), encodedAnalyzedGraph); if (hasStrengthenedGraph(analysisMethod)) { @@ -821,7 +821,7 @@ public AnalysisParsedGraph getAnalysisParsedGraph(AnalysisMethod analysisMethod) return new AnalysisParsedGraph(analyzedGraph, intrinsic); } - private String readEncodedGraph(EconomicMap methodData, String elementIdentifier) { + private byte[] readEncodedGraph(EconomicMap methodData, String elementIdentifier) { String location = get(methodData, elementIdentifier); int closingBracketAt = location.length() - 1; AnalysisError.guarantee(location.charAt(0) == '@' && location.charAt(closingBracketAt) == ']', "Location must start with '@' and end with ']': %s", location); @@ -841,7 +841,7 @@ private String readEncodedGraph(EconomicMap methodData, String e } catch (IOException e) { throw AnalysisError.shouldNotReachHere("Failed reading a graph from location: " + location, e); } - return new String(bb.array(), ImageLayerWriter.GRAPHS_CHARSET); + return bb.array(); } public boolean hasStrengthenedGraph(AnalysisMethod analysisMethod) { @@ -851,7 +851,7 @@ public boolean hasStrengthenedGraph(AnalysisMethod analysisMethod) { public void setStrengthenedGraph(AnalysisMethod analysisMethod) { EconomicMap methodData = getMethodData(analysisMethod); - String encodedAnalyzedGraph = readEncodedGraph(methodData, STRENGTHENED_GRAPH_TAG); + byte[] encodedAnalyzedGraph = readEncodedGraph(methodData, STRENGTHENED_GRAPH_TAG); EncodedGraph analyzedGraph = (EncodedGraph) ObjectCopier.decode(imageLayerSnapshotUtil.getGraphDecoder(this, analysisMethod, universe.getSnippetReflection()), encodedAnalyzedGraph); afterGraphDecodeHook(analyzedGraph); analysisMethod.setAnalyzedGraph(analyzedGraph); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java index 68df6f379968..a5b4a0033309 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java @@ -46,6 +46,8 @@ import jdk.graal.compiler.nodes.EncodedGraph; import jdk.graal.compiler.nodes.FieldLocationIdentity; import jdk.graal.compiler.util.ObjectCopier; +import jdk.graal.compiler.util.ObjectCopierInputStream; +import jdk.graal.compiler.util.ObjectCopierOutputStream; public class ImageLayerSnapshotUtil { public static final String FILE_NAME_PREFIX = "layer-snapshot-"; @@ -275,15 +277,16 @@ protected ImageHeapConstantBuiltIn(ImageLayerWriter imageLayerWriter, ImageLayer } @Override - public String encode(ObjectCopier.Encoder encoder, Object obj) { + public void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { ImageHeapConstant imageHeapConstant = (ImageHeapConstant) obj; imageLayerWriter.elementsToPersist.add(new AnalysisFuture<>(() -> imageLayerWriter.persistConstant(UNDEFINED_CONSTANT_ID, UNDEFINED_FIELD_INDEX, imageHeapConstant))); - return String.valueOf(imageHeapConstant.getConstantData().id); + stream.writeInt(imageHeapConstant.getConstantData().id); } @Override - protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { - return imageLayerLoader.getOrCreateConstant(Integer.parseInt(encoded)); + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { + int id = stream.readInt(); + return imageLayerLoader.getOrCreateConstant(id); } } @@ -298,15 +301,16 @@ protected AnalysisTypeBuiltIn(ImageLayerWriter imageLayerWriter, ImageLayerLoade } @Override - public String encode(ObjectCopier.Encoder encoder, Object obj) { + public void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { AnalysisType type = (AnalysisType) obj; imageLayerWriter.persistType(type); - return String.valueOf(type.getId()); + stream.writeInt(type.getId()); } @Override - protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { - return imageLayerLoader.getAnalysisType(Integer.parseInt(encoded)); + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { + int id = stream.readInt(); + return imageLayerLoader.getAnalysisType(id); } } @@ -323,7 +327,7 @@ protected AnalysisMethodBuiltIn(ImageLayerWriter imageLayerWriter, ImageLayerLoa } @Override - public String encode(ObjectCopier.Encoder encoder, Object obj) { + public void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { AnalysisMethod method = (AnalysisMethod) obj; AnalysisType declaringClass = method.getDeclaringClass(); imageLayerWriter.elementsToPersist.add(new AnalysisFuture<>(() -> { @@ -334,12 +338,12 @@ public String encode(ObjectCopier.Encoder encoder, Object obj) { imageLayerWriter.persistType(parameter); } imageLayerWriter.persistType(declaringClass); - return String.valueOf(method.getId()); + stream.writeInt(method.getId()); } @Override - protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { - int id = Integer.parseInt(encoded); + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { + int id = stream.readInt(); if (id == analysisMethod.getId()) { return analysisMethod; } @@ -358,14 +362,16 @@ protected AnalysisFieldBuiltIn(ImageLayerWriter imageLayerWriter, ImageLayerLoad } @Override - public String encode(ObjectCopier.Encoder encoder, Object obj) { + public void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { AnalysisField field = (AnalysisField) obj; - return encodeField(field, imageLayerWriter); + int id = encodeField(field, imageLayerWriter); + stream.writeInt(id); } @Override - protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { - return decodeField(imageLayerLoader, encoded); + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { + int id = stream.readInt(); + return decodeField(imageLayerLoader, id); } } @@ -380,27 +386,29 @@ protected FieldLocationIdentityBuiltIn(ImageLayerWriter imageLayerWriter, ImageL } @Override - public String encode(ObjectCopier.Encoder encoder, Object obj) { + public void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { FieldLocationIdentity fieldLocationIdentity = (FieldLocationIdentity) obj; AnalysisField field = (AnalysisField) fieldLocationIdentity.getField(); - return encodeField(field, imageLayerWriter); + int id = encodeField(field, imageLayerWriter); + stream.writeInt(id); } @Override - protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { - return new FieldLocationIdentity(decodeField(imageLayerLoader, encoded)); + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { + int id = stream.readInt(); + return new FieldLocationIdentity(decodeField(imageLayerLoader, id)); } } - private static String encodeField(AnalysisField field, ImageLayerWriter imageLayerWriter) { + private static int encodeField(AnalysisField field, ImageLayerWriter imageLayerWriter) { String declaringClassId = String.valueOf(field.getDeclaringClass().getId()); if (!imageLayerWriter.fieldsMap.containsKey(declaringClassId) || !imageLayerWriter.fieldsMap.get(declaringClassId).containsKey(field.getName())) { imageLayerWriter.persistField(field); } - return String.valueOf(field.getId()); + return field.getId(); } - private static AnalysisField decodeField(ImageLayerLoader imageLayerLoader, String encoded) { - return imageLayerLoader.getAnalysisField(Integer.parseInt(encoded)); + private static AnalysisField decodeField(ImageLayerLoader imageLayerLoader, int id) { + return imageLayerLoader.getAnalysisField(id); } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerWriter.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerWriter.java index 43e9d6a064f3..30e323228ba8 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerWriter.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerWriter.java @@ -101,7 +101,7 @@ import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.ArrayList; @@ -142,8 +142,6 @@ import jdk.vm.ci.meta.ResolvedJavaField; public class ImageLayerWriter { - static final Charset GRAPHS_CHARSET = Charset.defaultCharset(); - protected ImageLayerSnapshotUtil imageLayerSnapshotUtil; private ImageLayerWriterHelper imageLayerWriterHelper; private ImageHeap imageHeap; @@ -185,16 +183,14 @@ private static class GraphsOutput { } } - String add(String encodedGraph) { - ByteBuffer encoded = GRAPHS_CHARSET.encode(encodedGraph); - int size = encoded.limit(); - long offset = currentOffset.getAndAdd(size); + String add(byte[] encodedGraph) { + long offset = currentOffset.getAndAdd(encodedGraph.length); try { - tempChannel.write(encoded, offset); + tempChannel.write(ByteBuffer.wrap(encodedGraph), offset); } catch (Exception e) { throw GraalError.shouldNotReachHere(e, "Error during graphs file dumping."); } - return new StringBuilder("@").append(offset).append("[").append(size).append("]").toString(); + return new StringBuilder("@").append(offset).append("[").append(encodedGraph.length).append("]").toString(); } void finish() { @@ -487,8 +483,8 @@ private boolean persistGraph(AnalysisMethod method, EncodedGraph analyzedGraph, if (!useSharedLayerGraphs) { return false; } - String encodedGraph = ObjectCopier.encode(imageLayerSnapshotUtil.getGraphEncoder(this), analyzedGraph); - if (encodedGraph.contains(LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING)) { + byte[] encodedGraph = ObjectCopier.encode(imageLayerSnapshotUtil.getGraphEncoder(this), analyzedGraph); + if (contains(encodedGraph, LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING.getBytes(StandardCharsets.UTF_8))) { throw AnalysisError.shouldNotReachHere("The graph for the method %s contains a reference to a lambda type, which cannot be decoded: %s".formatted(method, encodedGraph)); } String location = graphsOutput.add(encodedGraph); @@ -496,6 +492,18 @@ private boolean persistGraph(AnalysisMethod method, EncodedGraph analyzedGraph, return true; } + private static boolean contains(byte[] data, byte[] seq) { + outer: for (int i = 0; i <= data.length - seq.length; i++) { + for (int j = 0; j < seq.length; j++) { + if (data[i + j] != seq[j]) { + continue outer; + } + } + return true; + } + return false; + } + private EconomicMap getMethodMap(AnalysisMethod method) { String name = imageLayerSnapshotUtil.getMethodIdentifier(method); EconomicMap methodMap = methodsMap.get(name); diff --git a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java index 8b66364b8b12..d616773b8544 100644 --- a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java +++ b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java @@ -43,16 +43,6 @@ import java.util.function.Function; import java.util.regex.Pattern; -import com.oracle.graal.pointsto.ObjectScanner; -import com.oracle.graal.pointsto.meta.ObjectReachableCallback; -import com.oracle.svm.core.util.VMError; -import com.oracle.svm.graal.hotspot.GetCompilerConfig; -import com.oracle.svm.graal.hotspot.GetJNIConfig; -import jdk.graal.compiler.hotspot.CompilerConfigurationFactory; -import jdk.graal.compiler.hotspot.libgraal.BuildTime; -import jdk.graal.compiler.options.OptionDescriptor; -import jdk.graal.compiler.options.OptionKey; -import jdk.graal.compiler.serviceprovider.LibGraalService; import org.graalvm.collections.EconomicMap; import org.graalvm.jniutils.NativeBridgeSupport; import org.graalvm.nativeimage.ImageSingletons; @@ -63,10 +53,15 @@ import org.graalvm.nativeimage.hosted.RuntimeReflection; import com.oracle.graal.pointsto.BigBang; +import com.oracle.graal.pointsto.ObjectScanner; import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.graal.pointsto.meta.ObjectReachableCallback; import com.oracle.graal.pointsto.reports.CallTreePrinter; import com.oracle.svm.core.SubstrateTargetDescription; import com.oracle.svm.core.option.HostedOptionKey; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.graal.hotspot.GetCompilerConfig; +import com.oracle.svm.graal.hotspot.GetJNIConfig; import com.oracle.svm.hosted.FeatureImpl.AfterAnalysisAccessImpl; import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; @@ -75,8 +70,13 @@ import com.oracle.svm.util.ReflectionUtil; import jdk.graal.compiler.debug.DebugContext; +import jdk.graal.compiler.hotspot.CompilerConfigurationFactory; +import jdk.graal.compiler.hotspot.libgraal.BuildTime; import jdk.graal.compiler.nodes.graphbuilderconf.GeneratedInvocationPlugin; import jdk.graal.compiler.options.Option; +import jdk.graal.compiler.options.OptionDescriptor; +import jdk.graal.compiler.options.OptionKey; +import jdk.graal.compiler.serviceprovider.LibGraalService; import jdk.vm.ci.code.TargetDescription; /** @@ -390,7 +390,7 @@ public void beforeAnalysis(BeforeAnalysisAccess baa) { Consumer.class, // registerAsInHeap Consumer.class, // hostedGraalSetFoldNodePluginClasses String.class, // nativeImageLocationQualifier - String.class // encodedGuestObjects + byte[].class // encodedGuestObjects )); GetCompilerConfig.Result configResult = GetCompilerConfig.from(Options.LibGraalJavaHome.getValue(), bb.getOptions()); for (var e : configResult.opens().entrySet()) { diff --git a/substratevm/src/com.oracle.svm.graal.hotspot/src/com/oracle/svm/graal/hotspot/GetCompilerConfig.java b/substratevm/src/com.oracle.svm.graal.hotspot/src/com/oracle/svm/graal/hotspot/GetCompilerConfig.java index 6ba8b24ac4c4..905eee0745ed 100644 --- a/substratevm/src/com.oracle.svm.graal.hotspot/src/com/oracle/svm/graal/hotspot/GetCompilerConfig.java +++ b/substratevm/src/com.oracle.svm.graal.hotspot/src/com/oracle/svm/graal/hotspot/GetCompilerConfig.java @@ -35,7 +35,6 @@ import java.util.Set; import java.util.stream.Collectors; -import jdk.graal.compiler.util.ObjectCopier; import org.graalvm.collections.UnmodifiableEconomicMap; import org.graalvm.collections.UnmodifiableMapCursor; @@ -45,9 +44,10 @@ import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.hotspot.HotSpotGraalOptionValues; +import jdk.graal.compiler.hotspot.libgraal.CompilerConfig; import jdk.graal.compiler.options.OptionKey; import jdk.graal.compiler.options.OptionValues; -import jdk.graal.compiler.hotspot.libgraal.CompilerConfig; +import jdk.graal.compiler.util.ObjectCopier; /** * Gets the map created in a JVM subprocess by running {@link CompilerConfig}. @@ -64,7 +64,7 @@ public class GetCompilerConfig { * {@link ObjectCopier}. These packages need to be opened when decoding the returned * string back to an object. */ - public record Result(String encodedConfig, Map> opens) { + public record Result(byte[] encodedConfig, Map> opens) { } /** @@ -171,7 +171,7 @@ public static Result from(Path javaHome, OptionValues options) { throw new GraalError("Interrupted waiting for command: %s", quotedCommand); } try { - String encodedConfig = Files.readString(encodedConfigPath); + byte[] encodedConfig = Files.readAllBytes(encodedConfigPath); if (DEBUG) { System.out.printf("[%d] Executed: %s%n", p.pid(), quotedCommand); System.out.printf("[%d] Output saved in %s%n", p.pid(), encodedConfigPath); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerSnapshotUtil.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerSnapshotUtil.java index 49d4b01b674e..1d671fc30193 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerSnapshotUtil.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerSnapshotUtil.java @@ -29,6 +29,7 @@ import static com.oracle.svm.hosted.reflect.proxy.ProxyRenamingSubstitutionProcessor.isProxyType; import static jdk.graal.compiler.java.LambdaUtils.isLambdaType; +import java.io.IOException; import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.net.URI; @@ -71,6 +72,8 @@ import jdk.graal.compiler.api.replacements.SnippetReflectionProvider; import jdk.graal.compiler.nodes.EncodedGraph; import jdk.graal.compiler.util.ObjectCopier; +import jdk.graal.compiler.util.ObjectCopierInputStream; +import jdk.graal.compiler.util.ObjectCopierOutputStream; import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaMethod; @@ -269,13 +272,15 @@ protected HostedTypeBuiltIn(SVMImageLayerLoader svmImageLayerLoader) { } @Override - protected String encode(ObjectCopier.Encoder encoder, Object obj) { - return String.valueOf(((HostedType) obj).getWrapped().getId()); + protected void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { + int id = ((HostedType) obj).getWrapped().getId(); + stream.writeInt(id); } @Override - protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { - AnalysisType type = svmImageLayerLoader.getAnalysisType(Integer.parseInt(encoded)); + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { + int id = stream.readInt(); + AnalysisType type = svmImageLayerLoader.getAnalysisType(id); return svmImageLayerLoader.getHostedUniverse().lookup(type); } } @@ -289,13 +294,14 @@ protected HostedMethodBuiltIn(SVMImageLayerLoader svmImageLayerLoader) { } @Override - protected String encode(ObjectCopier.Encoder encoder, Object obj) { - return String.valueOf(((HostedMethod) obj).getWrapped().getId()); + protected void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { + stream.writeInt(((HostedMethod) obj).getWrapped().getId()); } @Override - protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { - AnalysisMethod method = svmImageLayerLoader.getAnalysisMethod(Integer.parseInt(encoded)); + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { + int id = stream.readInt(); + AnalysisMethod method = svmImageLayerLoader.getAnalysisMethod(id); return svmImageLayerLoader.getHostedUniverse().lookup(method); } } @@ -306,12 +312,11 @@ protected HostedOptionValuesBuiltIn() { } @Override - protected String encode(ObjectCopier.Encoder encoder, Object obj) { - return ""; + protected void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { } @Override - protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { return HostedOptionValues.singleton(); } } @@ -325,12 +330,11 @@ protected HostedSnippetReflectionProviderBuiltIn(SnippetReflectionProvider snipp } @Override - protected String encode(ObjectCopier.Encoder encoder, Object obj) { - return ""; + protected void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { } @Override - protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { return snippetReflectionProvider; } } @@ -341,13 +345,14 @@ protected CInterfaceLocationIdentityBuiltIn() { } @Override - protected String encode(ObjectCopier.Encoder encoder, Object obj) { + protected void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { CInterfaceLocationIdentity cInterfaceLocationIdentity = (CInterfaceLocationIdentity) obj; - return cInterfaceLocationIdentity.toString(); + stream.writeUTF(cInterfaceLocationIdentity.toString()); } @Override - protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { + String encoded = stream.readUTF(); return new CInterfaceLocationIdentity(encoded); } } @@ -358,15 +363,15 @@ protected FastThreadLocalLocationIdentityBuiltIn() { } @Override - protected String encode(ObjectCopier.Encoder encoder, Object obj) { + protected void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { FastThreadLocal.FastThreadLocalLocationIdentity fastThreadLocalLocationIdentity = (FastThreadLocal.FastThreadLocalLocationIdentity) obj; FastThreadLocal fastThreadLocal = ReflectionUtil.readField(FastThreadLocal.FastThreadLocalLocationIdentity.class, "this$0", fastThreadLocalLocationIdentity); - return encodeStaticField(encoder, fastThreadLocal); + writeStaticField(encoder, stream, fastThreadLocal); } @Override - protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { - FastThreadLocal fastThreadLocal = getObjectFromStaticField(encoded); + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { + FastThreadLocal fastThreadLocal = readStaticFieldAndGetObject(stream); return fastThreadLocal.getLocationIdentity(); } } @@ -377,29 +382,29 @@ protected VMThreadLocalInfoBuiltIn() { } @Override - protected String encode(ObjectCopier.Encoder encoder, Object obj) { + protected void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { VMThreadLocalInfo vmThreadLocalInfo = (VMThreadLocalInfo) obj; VMThreadLocalCollector vmThreadLocalCollector = ImageSingletons.lookup(VMThreadLocalCollector.class); FastThreadLocal fastThreadLocal = vmThreadLocalCollector.getThreadLocal(vmThreadLocalInfo); - return encodeStaticField(encoder, fastThreadLocal); + writeStaticField(encoder, stream, fastThreadLocal); } @Override - protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { - FastThreadLocal fastThreadLocal = getObjectFromStaticField(encoded); + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { + FastThreadLocal fastThreadLocal = readStaticFieldAndGetObject(stream); return ImageSingletons.lookup(VMThreadLocalCollector.class).forFastThreadLocal(fastThreadLocal); } } - private static String encodeStaticField(ObjectCopier.Encoder encoder, Object object) { + private static void writeStaticField(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object object) throws IOException { Field staticField = encoder.getExternalValues().get(object); - return staticField.getDeclaringClass().getName() + ":" + staticField.getName(); + stream.writeUTF(staticField.getDeclaringClass().getName()); + stream.writeUTF(staticField.getName()); } - private static T getObjectFromStaticField(String staticField) { - String[] fieldParts = staticField.split(":"); - String className = fieldParts[0]; - String fieldName = fieldParts[1]; + private static T readStaticFieldAndGetObject(ObjectCopierInputStream stream) throws IOException { + String className = stream.readUTF(); + String fieldName = stream.readUTF(); Class declaringClass = ReflectionUtil.lookupClass(false, className); return ReflectionUtil.readStaticField(declaringClass, fieldName); } From 9687489f3742c7d7b873b8ec6a7f4008a5dc32ea Mon Sep 17 00:00:00 2001 From: Peter Hofer Date: Wed, 2 Oct 2024 15:29:14 +0200 Subject: [PATCH 03/13] Binary ObjectCopier array encoding. --- .../jdk/graal/compiler/util/ObjectCopier.java | 119 +++++------------- .../util/ObjectCopierInputStream.java | 66 ++++++++++ .../util/ObjectCopierOutputStream.java | 51 ++++++++ 3 files changed, 145 insertions(+), 91 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java index db69c3f4ff51..e17e26b649f2 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java @@ -51,7 +51,6 @@ import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import java.util.stream.Stream; import org.graalvm.collections.EconomicMap; @@ -77,7 +76,6 @@ public class ObjectCopier { private static final Pattern OBJECT_LINE = Pattern.compile("\\{(?[\\w.$]+):(?\\d+)}"); - private static final Pattern ARRAY_LINE = Pattern.compile("\\[(?[^]]+)] = (?.*)"); private static final Pattern FIELD_LINE = Pattern.compile("(?[^:]+):(?[^ ]+) = (?.*)"); /** @@ -484,83 +482,20 @@ private Object decode(byte[] encoded) { addDecodedObject(id, builtin.decode(this, clazz, stream)); break; } - case '[': { - String legacy = stream.readUTF(); - Matcher matcher = ARRAY_LINE.matcher(legacy); - GraalError.guarantee(matcher.matches(), "Invalid array record: %s", legacy); - String componentTypeName = matcher.group("componentType"); - String[] elements = splitSpaceSeparatedElements(matcher.group("elements")); - switch (componentTypeName) { - case "boolean": { - boolean[] arr = new boolean[elements.length]; - for (int i = 0; i < elements.length; i++) { - arr[i] = Boolean.parseBoolean(elements[i]); - } - addDecodedObject(id, arr); - break; - } - case "byte": { - byte[] arr = new byte[elements.length]; - for (int i = 0; i < elements.length; i++) { - arr[i] = Byte.parseByte(elements[i]); - } - addDecodedObject(id, arr); - break; - } - case "char": { - throw GraalError.shouldNotReachHere("char[] should be handled by " + StringBuiltin.class); - } - case "short": { - short[] arr = new short[elements.length]; - for (int i = 0; i < elements.length; i++) { - arr[i] = Short.parseShort(elements[i]); - } - addDecodedObject(id, arr); - break; - } - case "int": { - int[] arr = new int[elements.length]; - for (int i = 0; i < elements.length; i++) { - arr[i] = Integer.parseInt(elements[i]); - } - addDecodedObject(id, arr); - break; - } - case "float": { - float[] arr = new float[elements.length]; - for (int i = 0; i < elements.length; i++) { - arr[i] = Float.parseFloat(elements[i]); - } - addDecodedObject(id, arr); - break; - } - case "long": { - long[] arr = new long[elements.length]; - for (int i = 0; i < elements.length; i++) { - arr[i] = Long.parseLong(elements[i]); - } - addDecodedObject(id, arr); - break; - } - case "double": { - double[] arr = new double[elements.length]; - for (int i = 0; i < elements.length; i++) { - arr[i] = Double.parseDouble(elements[i]); - } - addDecodedObject(id, arr); - break; - } - default: { - Class componentType = loadClass(componentTypeName); - Object[] arr = (Object[]) Array.newInstance(componentType, elements.length); - addDecodedObject(id, arr); - for (int i = 0; i < elements.length; i++) { - int elementId = Integer.parseInt(elements[i]); - int index = i; - resolveId(elementId, o -> arr[index] = o); - } - break; - } + case '[': { // primitive array + Object arr = stream.readTypedPrimitiveArray(); + addDecodedObject(id, arr); + break; + } + case ']': { // object array + String componentTypeName = stream.readUTF(); + Class componentType = loadClass(componentTypeName); + int[] elements = (int[]) stream.readTypedPrimitiveArray(); + Object[] arr = (Object[]) Array.newInstance(componentType, elements.length); + addDecodedObject(id, arr); + for (int i = 0; i < elements.length; i++) { + int index = i; + resolveId(elements[i], o -> arr[index] = o); } break; } @@ -898,19 +833,17 @@ private void encode(ObjectCopierOutputStream out) throws IOException { throw new RuntimeException(e); } } else if (clazz.isArray()) { - out.writeByte('['); - out.writeInt(id); Class componentType = clazz.getComponentType(); if (!componentType.isPrimitive()) { - String elements = Stream.of((Object[]) obj).map(this::getIdString).collect(Collectors.joining(" ")); - out.writeUTF(String.format("[%s] = %s", componentType.getName(), elements)); + out.writeByte(']'); + out.writeInt(id); + out.writeUTF(componentType.getName()); + int[] ids = Stream.of((Object[]) obj).mapToInt(this::getId).toArray(); + out.writeTypedPrimitiveArray(ids); } else { - int length = Array.getLength(obj); - StringBuilder elements = new StringBuilder(length * 5); - for (int i = 0; i < length; i++) { - elements.append(' ').append(Array.get(obj, i)); - } - out.writeUTF(String.format("[%s] =%s", componentType.getName(), elements)); + out.writeByte('['); + out.writeInt(id); + out.writeTypedPrimitiveArray(obj); } } else { if (clazz == Field.class) { @@ -941,9 +874,13 @@ private void encode(ObjectCopierOutputStream out) throws IOException { } } - private String getIdString(Object o) { + private int getId(Object o) { GraalError.guarantee(objectToId.containsKey(o), "Unknown object: %s", o); - return String.valueOf(objectToId.get(o).id()); + return objectToId.get(o).id(); + } + + private String getIdString(Object o) { + return String.valueOf(getId(o)); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java index 8826b36c7a7a..77c938651138 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java @@ -25,10 +25,76 @@ package jdk.graal.compiler.util; import java.io.DataInputStream; +import java.io.IOException; import java.io.InputStream; public class ObjectCopierInputStream extends DataInputStream { public ObjectCopierInputStream(InputStream in) { super(in); } + + public Object readTypedPrimitiveArray() throws IOException { + final int length = readInt(); + final byte type = readByte(); + switch (type) { + case 'Z': { + boolean[] a = new boolean[length]; + for (int i = 0; i < length; i++) { + a[i] = readBoolean(); + } + return a; + } + case 'B': { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) { + a[i] = readByte(); + } + return a; + } + case 'S': { + short[] a = new short[length]; + for (int i = 0; i < length; i++) { + a[i] = readShort(); + } + return a; + } + case 'C': { + char[] a = new char[length]; + for (int i = 0; i < length; i++) { + a[i] = readChar(); + } + return a; + } + case 'I': { + int[] a = new int[length]; + for (int i = 0; i < length; i++) { + a[i] = readInt(); + } + return a; + } + case 'J': { + long[] a = new long[length]; + for (int i = 0; i < length; i++) { + a[i] = readLong(); + } + return a; + } + case 'F': { + float[] a = new float[length]; + for (int i = 0; i < length; i++) { + a[i] = readFloat(); + } + return a; + } + case 'D': { + double[] a = new double[length]; + for (int i = 0; i < length; i++) { + a[i] = readDouble(); + } + return a; + } + default: + throw new IOException("Unsupported type: " + Integer.toHexString(type)); + } + } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java index a6a8891102a8..df989a0a144e 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java @@ -25,10 +25,61 @@ package jdk.graal.compiler.util; import java.io.DataOutputStream; +import java.io.IOException; import java.io.OutputStream; +import java.lang.reflect.Array; public class ObjectCopierOutputStream extends DataOutputStream { public ObjectCopierOutputStream(OutputStream out) { super(out); } + + public void writeTypedPrimitiveArray(Object value) throws IOException { + Class compClz = value.getClass().componentType(); + int length = Array.getLength(value); + this.writeInt(length); + if (compClz == boolean.class) { + this.writeByte('Z'); + for (int i = 0; i < length; i++) { + this.writeBoolean(Array.getBoolean(value, i)); + } + } else if (compClz == byte.class) { + this.writeByte('B'); + for (int i = 0; i < length; i++) { + this.writeByte(Array.getByte(value, i)); + } + } else if (compClz == short.class) { + this.writeByte('S'); + for (int i = 0; i < length; i++) { + this.writeShort(Array.getShort(value, i)); + } + } else if (compClz == char.class) { + this.writeByte('C'); + for (int i = 0; i < length; i++) { + this.writeChar(Array.getChar(value, i)); + } + } else if (compClz == int.class) { + this.writeByte('I'); + for (int i = 0; i < length; i++) { + this.writeInt(Array.getInt(value, i)); + } + } else if (compClz == long.class) { + this.writeByte('J'); + for (int i = 0; i < length; i++) { + this.writeLong(Array.getLong(value, i)); + } + } else if (compClz == float.class) { + this.writeByte('F'); + for (int i = 0; i < length; i++) { + this.writeFloat(Array.getFloat(value, i)); + } + } else if (compClz == double.class) { + this.writeByte('D'); + for (int i = 0; i < length; i++) { + this.writeDouble(Array.getDouble(value, i)); + } + } else { + throw new IllegalArgumentException(String.format("Unsupported array: Value: %s, Value type: %s", value, value.getClass())); + } + } } From dae7277ff82fa0d8f50e431160c681da137ba396 Mon Sep 17 00:00:00 2001 From: Peter Hofer Date: Thu, 3 Oct 2024 13:08:40 +0200 Subject: [PATCH 04/13] Use FrequencyEncoder in ObjectCopier. --- .../jdk/graal/compiler/util/ObjectCopier.java | 149 +++++++++--------- .../util/ObjectCopierInputStream.java | 30 ++++ .../util/ObjectCopierOutputStream.java | 39 +++++ 3 files changed, 142 insertions(+), 76 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java index e17e26b649f2..f35dc00480c7 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java @@ -61,6 +61,7 @@ import org.graalvm.word.LocationIdentity; import jdk.graal.compiler.core.common.FieldIntrospection; +import jdk.graal.compiler.core.common.util.FrequencyEncoder; import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.replacements.SnippetTemplate; import jdk.internal.misc.Unsafe; @@ -385,11 +386,10 @@ static String[] splitSpaceSeparatedElements(String elements) { * Encodes {@code root} to a String using {@code encoder}. */ public static byte[] encode(Encoder encoder, Object root) { - int rootId = encoder.makeId(root, ObjectPath.of("[root:" + root.getClass().getName() + "]")).id(); - GraalError.guarantee(rootId == 1, "The root object should have id of 1, not %d", rootId); + encoder.makeId(root, ObjectPath.of("[root:" + root.getClass().getName() + "]")); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectCopierOutputStream cos = new ObjectCopierOutputStream(baos)) { - encoder.encode(cos); + encoder.encode(cos, root); } catch (IOException e) { throw new RuntimeException(e); } @@ -460,10 +460,12 @@ record Deferred(Runnable runnable, int recordNum, int fieldNum) { int fieldNum = -1; private Object decode(byte[] encoded) { + int rootId; deferred = new ArrayList<>(); recordNum = 0; try (ObjectCopierInputStream stream = new ObjectCopierInputStream(new ByteArrayInputStream(encoded))) { + rootId = readId(stream); for (;;) { recordNum++; fieldNum = -1; @@ -471,7 +473,7 @@ private Object decode(byte[] encoded) { if (c == -1) { break; } - int id = stream.readInt(); + int id = readId(stream); switch (c) { case '<': { String className = stream.readUTF(); @@ -592,7 +594,11 @@ private Object decode(byte[] encoded) { recordNum = -1; fieldNum = -1; } - return getObject(1, true); + return getObject(rootId, true); + } + + private static int readId(ObjectCopierInputStream stream) throws IOException { + return (int) stream.readPackedUnsigned(); } void resolveId(String id, Consumer c) { @@ -660,8 +666,7 @@ final Builtin getBuiltin(Class clazz, boolean onlyCheck) { public static class Encoder extends ObjectCopier { - final Map objectToId = new IdentityHashMap<>(); - final List objects = new ArrayList<>(); + final FrequencyEncoder objects = FrequencyEncoder.createIdentityEncoder(); /** * Map from values to static final fields. In a serialized object graph, references to such @@ -678,8 +683,7 @@ public Encoder(List externalValueFields) { * Use precomputed {@code externalValues} to avoid recomputing them. */ public Encoder(Map externalValues) { - objects.add(null); - objectToId.put(null, new ObjectID(0, null)); + objects.addObject(null); this.externalValues = externalValues; } @@ -729,7 +733,7 @@ private String encodeMap(UnmodifiableEconomicMap map) { if (!value.isEmpty()) { value.append(" "); } - value.append(makeId(cursor.getKey(), null).id()).append(":").append(makeId(cursor.getValue(), null).id()); + value.append(getId(cursor.getKey())).append(":").append(getId(cursor.getValue())); } return value.toString(); } @@ -756,58 +760,52 @@ static void checkIllegalValue(Class type, Object value, ObjectPath objectPath } } - ObjectID makeId(Object obj, ObjectPath objectPath) { - if (!objectToId.containsKey(obj)) { - ObjectID id = new ObjectID(objects.size(), objectPath); - Field field = externalValues.get(obj); - if (field != null) { - objects.add(field); - objectToId.put(obj, id); - objectToId.put(field, id); - return id; - } - - objects.add(obj); - objectToId.put(obj, id); + void makeId(Object obj, ObjectPath objectPath) { + Field field = externalValues.get(obj); + if (field != null) { + objects.addObject(field); + return; + } - Class clazz = obj.getClass(); - Builtin builtin = getBuiltin(clazz); - if (builtin != null) { - builtin.checkObject(obj); - builtin.makeChildIds(this, obj, objectPath); - return id; - } + if (!objects.addObject(obj)) { + return; // already known + } - checkIllegalValue(Field.class, obj, objectPath, "Field type is used in object copying implementation"); - checkIllegalValue(FieldIntrospection.class, obj, objectPath, "Graal metadata type cannot be copied"); + Class clazz = obj.getClass(); + Builtin builtin = getBuiltin(clazz); + if (builtin != null) { + builtin.checkObject(obj); + builtin.makeChildIds(this, obj, objectPath); + return; + } - if (clazz.isArray()) { - Class componentType = clazz.getComponentType(); - if (!componentType.isPrimitive()) { - Object[] objArray = (Object[]) obj; - int index = 0; - for (Object element : objArray) { - makeId(element, objectPath.add(index)); - index++; - } + checkIllegalValue(Field.class, obj, objectPath, "Field type is used in object copying implementation"); + checkIllegalValue(FieldIntrospection.class, obj, objectPath, "Graal metadata type cannot be copied"); + + if (clazz.isArray()) { + Class componentType = clazz.getComponentType(); + if (!componentType.isPrimitive()) { + Object[] objArray = (Object[]) obj; + int index = 0; + for (Object element : objArray) { + makeId(element, objectPath.add(index)); + index++; } - } else { - checkIllegalValue(LocationIdentity.class, obj, objectPath, "must come from a static field"); - checkIllegalValue(HashSet.class, obj, objectPath, "hashes are typically not stable across VM executions"); - - ClassInfo classInfo = makeClassInfo(clazz, objectPath); - for (Field f : classInfo.fields().values()) { - String fieldName = f.getDeclaringClass().getSimpleName() + "#" + f.getName(); - makeId(f.getType(), objectPath.add(fieldName + ":type")); - if (!f.getType().isPrimitive()) { - Object fieldValue = readField(f, obj); - makeId(fieldValue, objectPath.add(fieldName)); - } + } + } else { + checkIllegalValue(LocationIdentity.class, obj, objectPath, "must come from a static field"); + checkIllegalValue(HashSet.class, obj, objectPath, "hashes are typically not stable across VM executions"); + + ClassInfo classInfo = makeClassInfo(clazz, objectPath); + for (Field f : classInfo.fields().values()) { + String fieldName = f.getDeclaringClass().getSimpleName() + "#" + f.getName(); + makeId(f.getType(), objectPath.add(fieldName + ":type")); + if (!f.getType().isPrimitive()) { + Object fieldValue = readField(f, obj); + makeId(fieldValue, objectPath.add(fieldName)); } } - } - return objectToId.get(obj); } private ClassInfo makeClassInfo(Class clazz, ObjectPath objectPath) { @@ -818,14 +816,16 @@ private ClassInfo makeClassInfo(Class clazz, ObjectPath objectPath) { } } - private void encode(ObjectCopierOutputStream out) throws IOException { - for (int id = 1; id < objects.size(); id++) { - Object obj = objects.get(id); + private void encode(ObjectCopierOutputStream out, Object root) throws IOException { + Object[] encodedObjects = objects.encodeAll(new Object[objects.getLength()]); + writeId(out, getId(root)); + for (int id = 1; id < encodedObjects.length; id++) { + Object obj = encodedObjects[id]; Class clazz = obj.getClass(); Builtin builtin = getBuiltin(clazz); if (builtin != null) { out.writeByte('<'); - out.writeInt(id); + writeId(out, id); out.writeUTF(clazz.getName()); try { builtin.encode(this, out, obj); @@ -836,34 +836,34 @@ private void encode(ObjectCopierOutputStream out) throws IOException { Class componentType = clazz.getComponentType(); if (!componentType.isPrimitive()) { out.writeByte(']'); - out.writeInt(id); + writeId(out, id); out.writeUTF(componentType.getName()); int[] ids = Stream.of((Object[]) obj).mapToInt(this::getId).toArray(); out.writeTypedPrimitiveArray(ids); } else { out.writeByte('['); - out.writeInt(id); + writeId(out, id); out.writeTypedPrimitiveArray(obj); } } else { if (clazz == Field.class) { Field field = (Field) obj; out.writeByte('@'); - out.writeInt(id); + writeId(out, id); out.writeUTF(field.getDeclaringClass().getName()); out.writeUTF(field.getName()); } else { ClassInfo classInfo = classInfos.get(clazz); out.writeByte('{'); - out.writeInt(id); + writeId(out, id); out.writeUTF(String.format("{%s:%d}", clazz.getName(), classInfo.fields().size())); for (var e : classInfo.fields().entrySet()) { Field f = e.getValue(); Object fValue = readField(f, obj); Class fieldType = f.getType(); - int fieldTypeId = makeId(fieldType, null).id(); + int fieldTypeId = getId(fieldType); if (!fieldType.isPrimitive()) { - fValue = getIdString(fValue); + fValue = String.valueOf(getId(fValue)); } else if (fieldType == char.class) { fValue = (int) (Character) fValue; } @@ -874,13 +874,16 @@ private void encode(ObjectCopierOutputStream out) throws IOException { } } - private int getId(Object o) { - GraalError.guarantee(objectToId.containsKey(o), "Unknown object: %s", o); - return objectToId.get(o).id(); + private static void writeId(ObjectCopierOutputStream out, int id) throws IOException { + out.writePackedUnsigned(id); } - private String getIdString(Object o) { - return String.valueOf(getId(o)); + private int getId(Object o) { + Field field = externalValues.get(o); + if (field != null) { + return objects.getIndex(field); + } + return objects.getIndex(o); } } @@ -1040,10 +1043,4 @@ public String toString() { return String.join(".", components.reversed()); } } - - /** - * A unique int id for an object as well as the path by which it was (first) reached. - */ - record ObjectID(int id, ObjectPath path) { - } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java index 77c938651138..299ae6e2f6fe 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java @@ -97,4 +97,34 @@ public Object readTypedPrimitiveArray() throws IOException { throw new IOException("Unsupported type: " + Integer.toHexString(type)); } } + + public long readPackedSigned() throws IOException { + return decodeSign(readPacked()); + } + + public long readPackedUnsigned() throws IOException { + return readPacked(); + } + + private static long decodeSign(long value) { + return (value >>> 1) ^ -(value & 1); + } + + private long readPacked() throws IOException { + int b0 = readUnsignedByte(); + if (b0 < ObjectCopierOutputStream.NUM_LOW_CODES) { + return b0; + } + assert b0 >= ObjectCopierOutputStream.NUM_LOW_CODES : b0; + long sum = b0; + long shift = ObjectCopierOutputStream.HIGH_WORD_SHIFT; + for (int i = 2;; i++) { + long b = readUnsignedByte(); + sum += b << shift; + if (b < ObjectCopierOutputStream.NUM_LOW_CODES || i == ObjectCopierOutputStream.MAX_BYTES) { + return sum; + } + shift += ObjectCopierOutputStream.HIGH_WORD_SHIFT; + } + } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java index df989a0a144e..c9e351b99e7c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java @@ -29,7 +29,15 @@ import java.io.OutputStream; import java.lang.reflect.Array; +import jdk.graal.compiler.core.common.calc.UnsignedMath; + public class ObjectCopierOutputStream extends DataOutputStream { + // Constants for UNSIGNED5 coding of Pack200 + protected static final long HIGH_WORD_SHIFT = 6; + protected static final long NUM_HIGH_CODES = 1 << HIGH_WORD_SHIFT; // number of high codes (64) + protected static final long NUM_LOW_CODES = (1 << Byte.SIZE) - NUM_HIGH_CODES; + protected static final long MAX_BYTES = 11; + public ObjectCopierOutputStream(OutputStream out) { super(out); } @@ -82,4 +90,35 @@ public void writeTypedPrimitiveArray(Object value) throws IOException { throw new IllegalArgumentException(String.format("Unsupported array: Value: %s, Value type: %s", value, value.getClass())); } } + + static long encodeSign(long value) { + return (value << 1) ^ (value >> 63); + } + + public void writePackedSigned(long value) throws IOException { + // this is a modified version of the SIGNED5 encoding from Pack200 + writePacked(encodeSign(value)); + } + + public void writePackedUnsigned(long value) throws IOException { + // this is a modified version of the UNSIGNED5 encoding from Pack200 + writePacked(value); + } + + private void writePacked(long value) throws IOException { + if (UnsignedMath.belowThan(value, NUM_LOW_CODES)) { + writeByte((int) value); + return; + } + long sum = value; + for (int i = 1; UnsignedMath.aboveOrEqual(sum, NUM_LOW_CODES) && i < MAX_BYTES; i++) { + sum -= NUM_LOW_CODES; + long u1 = NUM_LOW_CODES + (sum & (NUM_HIGH_CODES - 1)); // this is a "high code" + sum >>>= HIGH_WORD_SHIFT; // extracted 6 bits + writeByte((int) u1); + } + // remainder is either a "low code" or the last byte + assert sum == (sum & 0xFF) : "not a byte"; + writeByte((int) (sum & 0xFF)); + } } From 815a4691fca6da5af2623d8434840ebc6a8ad05e Mon Sep 17 00:00:00 2001 From: Peter Hofer Date: Thu, 3 Oct 2024 16:56:05 +0200 Subject: [PATCH 05/13] Use a string table for compact indexes (using a frequency encoder) and deduplication. --- .../jdk/graal/compiler/util/ObjectCopier.java | 136 ++++++++++++------ .../heap/SVMImageLayerSnapshotUtil.java | 65 ++++++--- 2 files changed, 142 insertions(+), 59 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java index f35dc00480c7..c858b37cfe14 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java @@ -27,7 +27,6 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.IOException; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -41,7 +40,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; -import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -117,11 +115,11 @@ final void checkClass(Class c) { } /** - * Ensures object ids have are created for the values referenced by {@code obj} that will be + * Ensures object ids are created for the values referenced by {@code obj} that will be * handled by this builtin when {@code obj} is encoded. For example, the values in a map. */ @SuppressWarnings("unused") - void makeChildIds(Encoder encoder, Object obj, ObjectPath objectPath) { + protected void makeChildIds(Encoder encoder, Object obj, ObjectPath objectPath) { } /** @@ -151,14 +149,24 @@ static final class ClassBuiltin extends Builtin { super(Class.class); } + private static String getName(Object obj) { + return ((Class) obj).getName(); + } + + @Override + protected void makeChildIds(Encoder encoder, Object obj, ObjectPath objectPath) { + encoder.makeStringId(getName(obj), objectPath); + } + @Override protected void encode(Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { - stream.writeUTF(((Class) obj).getName()); + String name = getName(obj); + encoder.writeString(stream, name); } @Override protected Object decode(Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { - String encoded = stream.readUTF(); + String encoded = decoder.readString(stream); return switch (encoded) { case "boolean" -> boolean.class; case "byte" -> byte.class; @@ -183,15 +191,23 @@ static final class StringBuiltin extends Builtin { super(String.class, char[].class); } + private static String asString(Object obj) { + return obj instanceof String ? (String) obj : new String((char[]) obj); + } + + @Override + protected void makeChildIds(Encoder encoder, Object obj, ObjectPath objectPath) { + encoder.makeStringId(asString(obj), objectPath); + } + @Override protected void encode(Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { - String s = obj instanceof String ? (String) obj : new String((char[]) obj); - stream.writeUTF(s); + encoder.writeString(stream, asString(obj)); } @Override protected Object decode(Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { - String s = stream.readUTF(); + String s = decoder.readString(stream); if (concreteType == char[].class) { return s.toCharArray(); } @@ -248,7 +264,7 @@ static final class HashMapBuiltin extends Builtin { } @Override - void makeChildIds(Encoder encoder, Object obj, ObjectPath objectPath) { + protected void makeChildIds(Encoder encoder, Object obj, ObjectPath objectPath) { Map map = (Map) obj; encoder.makeMapChildIds(new EconomicMapWrap<>(map), objectPath); } @@ -287,7 +303,7 @@ static final class EconomicMapBuiltin extends Builtin { } @Override - void makeChildIds(Encoder encoder, Object obj, ObjectPath objectPath) { + protected void makeChildIds(Encoder encoder, Object obj, ObjectPath objectPath) { EconomicMap map = (EconomicMap) obj; GraalError.guarantee(map.getEquivalenceStrategy() == Equivalence.DEFAULT, "Only DEFAULT strategy supported: %s", map.getEquivalenceStrategy()); @@ -458,6 +474,7 @@ record Deferred(Runnable runnable, int recordNum, int fieldNum) { List deferred; int recordNum = -1; int fieldNum = -1; + String[] strings; private Object decode(byte[] encoded) { int rootId; @@ -465,6 +482,11 @@ private Object decode(byte[] encoded) { recordNum = 0; try (ObjectCopierInputStream stream = new ObjectCopierInputStream(new ByteArrayInputStream(encoded))) { + int nstrings = Math.toIntExact(stream.readPackedUnsigned()); + strings = new String[nstrings]; + for (int i = 0; i < nstrings; i++) { + strings[i] = stream.readUTF(); + } rootId = readId(stream); for (;;) { recordNum++; @@ -476,7 +498,7 @@ private Object decode(byte[] encoded) { int id = readId(stream); switch (c) { case '<': { - String className = stream.readUTF(); + String className = readString(stream); Class clazz = loadClass(className); Builtin builtin = getBuiltin(clazz); GraalError.guarantee(builtin != null, "No builtin for %s in record %d", className, recordNum); @@ -490,7 +512,7 @@ private Object decode(byte[] encoded) { break; } case ']': { // object array - String componentTypeName = stream.readUTF(); + String componentTypeName = readString(stream); Class componentType = loadClass(componentTypeName); int[] elements = (int[]) stream.readTypedPrimitiveArray(); Object[] arr = (Object[]) Array.newInstance(componentType, elements.length); @@ -502,8 +524,8 @@ private Object decode(byte[] encoded) { break; } case '@': { - String className = stream.readUTF(); - String fieldName = stream.readUTF(); + String className = readString(stream); + String fieldName = readString(stream); Class declaringClass = loadClass(className); Field field = getField(declaringClass, fieldName); addDecodedObject(id, readField(field, null)); @@ -622,6 +644,11 @@ void resolveId(int id, Consumer c) { } } + public String readString(ObjectCopierInputStream stream) throws IOException { + int id = Math.toIntExact(stream.readPackedUnsigned()); + return strings[id]; + } + private void checkFieldType(int expectTypeId, Field field) { Class actualType = field.getType(); Class expectType = (Class) idToObject.get(expectTypeId); @@ -668,6 +695,13 @@ public static class Encoder extends ObjectCopier { final FrequencyEncoder objects = FrequencyEncoder.createIdentityEncoder(); + /** + * We use a separate string table to deduplicate strings and to make sure they are available + * upfront during decoding so that deferred processing with transitive dependencies is not + * needed. + */ + final FrequencyEncoder strings = FrequencyEncoder.createEqualityEncoder(); + /** * Map from values to static final fields. In a serialized object graph, references to such * values are encoded using the static final field they come from. This field is then looked @@ -748,6 +782,16 @@ void makeMapChildIds(EconomicMap map, ObjectPath objectPath) { } } + public void writeString(ObjectCopierOutputStream stream, String s) throws IOException { + int id = strings.getIndex(s); + stream.writePackedUnsigned(id); + } + + public void makeStringId(String s, ObjectPath objectPath) { + GraalError.guarantee(s != null, "Illegal null string: Path %s", objectPath); + strings.addObject(s); + } + /** * Checks that {@code value} is not an instance of {@code type}. * @@ -763,7 +807,10 @@ static void checkIllegalValue(Class type, Object value, ObjectPath objectPath void makeId(Object obj, ObjectPath objectPath) { Field field = externalValues.get(obj); if (field != null) { - objects.addObject(field); + if (objects.addObject(field)) { + makeStringId(field.getDeclaringClass().getName(), objectPath); + makeStringId(field.getName(), objectPath); + } return; } @@ -775,6 +822,7 @@ void makeId(Object obj, ObjectPath objectPath) { Builtin builtin = getBuiltin(clazz); if (builtin != null) { builtin.checkObject(obj); + makeStringId(clazz.getName(), objectPath); builtin.makeChildIds(this, obj, objectPath); return; } @@ -785,6 +833,7 @@ void makeId(Object obj, ObjectPath objectPath) { if (clazz.isArray()) { Class componentType = clazz.getComponentType(); if (!componentType.isPrimitive()) { + strings.addObject(componentType.getName()); Object[] objArray = (Object[]) obj; int index = 0; for (Object element : objArray) { @@ -817,6 +866,11 @@ private ClassInfo makeClassInfo(Class clazz, ObjectPath objectPath) { } private void encode(ObjectCopierOutputStream out, Object root) throws IOException { + String[] encodedStrings = strings.encodeAll(new String[strings.getLength()]); + out.writePackedUnsigned(encodedStrings.length); + for (String s : encodedStrings) { + out.writeUTF(s); + } Object[] encodedObjects = objects.encodeAll(new Object[objects.getLength()]); writeId(out, getId(root)); for (int id = 1; id < encodedObjects.length; id++) { @@ -826,7 +880,7 @@ private void encode(ObjectCopierOutputStream out, Object root) throws IOExceptio if (builtin != null) { out.writeByte('<'); writeId(out, id); - out.writeUTF(clazz.getName()); + writeString(out, clazz.getName()); try { builtin.encode(this, out, obj); } catch (IOException e) { @@ -837,7 +891,7 @@ private void encode(ObjectCopierOutputStream out, Object root) throws IOExceptio if (!componentType.isPrimitive()) { out.writeByte(']'); writeId(out, id); - out.writeUTF(componentType.getName()); + writeString(out, componentType.getName()); int[] ids = Stream.of((Object[]) obj).mapToInt(this::getId).toArray(); out.writeTypedPrimitiveArray(ids); } else { @@ -845,30 +899,28 @@ private void encode(ObjectCopierOutputStream out, Object root) throws IOExceptio writeId(out, id); out.writeTypedPrimitiveArray(obj); } + } else if (clazz == Field.class) { + Field field = (Field) obj; + out.writeByte('@'); + writeId(out, id); + writeString(out, field.getDeclaringClass().getName()); + writeString(out, field.getName()); } else { - if (clazz == Field.class) { - Field field = (Field) obj; - out.writeByte('@'); - writeId(out, id); - out.writeUTF(field.getDeclaringClass().getName()); - out.writeUTF(field.getName()); - } else { - ClassInfo classInfo = classInfos.get(clazz); - out.writeByte('{'); - writeId(out, id); - out.writeUTF(String.format("{%s:%d}", clazz.getName(), classInfo.fields().size())); - for (var e : classInfo.fields().entrySet()) { - Field f = e.getValue(); - Object fValue = readField(f, obj); - Class fieldType = f.getType(); - int fieldTypeId = getId(fieldType); - if (!fieldType.isPrimitive()) { - fValue = String.valueOf(getId(fValue)); - } else if (fieldType == char.class) { - fValue = (int) (Character) fValue; - } - out.writeUTF(String.format("%s:%d = %s", e.getKey(), fieldTypeId, fValue)); + ClassInfo classInfo = classInfos.get(clazz); + out.writeByte('{'); + writeId(out, id); + out.writeUTF(String.format("{%s:%d}", clazz.getName(), classInfo.fields().size())); + for (var e : classInfo.fields().entrySet()) { + Field f = e.getValue(); + Object fValue = readField(f, obj); + Class fieldType = f.getType(); + int fieldTypeId = getId(fieldType); + if (!fieldType.isPrimitive()) { + fValue = String.valueOf(getId(fValue)); + } else if (fieldType == char.class) { + fValue = (int) (Character) fValue; } + out.writeUTF(String.format("%s:%d = %s", e.getKey(), fieldTypeId, fValue)); } } } @@ -991,14 +1043,14 @@ private static void addImmutableCollectionsFields(List fields) { * @param prefix the prefix path * @param name the last field or array index read in the path */ - record ObjectPath(ObjectPath prefix, Object name) { + public record ObjectPath(ObjectPath prefix, Object name) { /** * Creates an object path for a root object. * * @param rootName names a reference to the root object (e.g. "[root]" or the qualified name * of a static field) */ - public static ObjectPath of(String rootName) { + static ObjectPath of(String rootName) { return new ObjectPath(null, rootName); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerSnapshotUtil.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerSnapshotUtil.java index 1d671fc30193..8d55517f5f62 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerSnapshotUtil.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerSnapshotUtil.java @@ -344,15 +344,25 @@ protected CInterfaceLocationIdentityBuiltIn() { super(CInterfaceLocationIdentity.class); } + private static String asString(Object obj) { + var cInterfaceLocationIdentity = (CInterfaceLocationIdentity) obj; + return cInterfaceLocationIdentity.toString(); + } + + @Override + protected void makeChildIds(ObjectCopier.Encoder encoder, Object obj, ObjectCopier.ObjectPath objectPath) { + encoder.makeStringId(asString(obj), objectPath); + } + @Override protected void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { - CInterfaceLocationIdentity cInterfaceLocationIdentity = (CInterfaceLocationIdentity) obj; - stream.writeUTF(cInterfaceLocationIdentity.toString()); + String string = asString(obj); + encoder.writeString(stream, string); } @Override protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { - String encoded = stream.readUTF(); + String encoded = decoder.readString(stream); return new CInterfaceLocationIdentity(encoded); } } @@ -362,16 +372,24 @@ protected FastThreadLocalLocationIdentityBuiltIn() { super(FastThreadLocal.FastThreadLocalLocationIdentity.class); } + private static FastThreadLocal getFastThreadLocal(Object obj) { + var fastThreadLocalLocationIdentity = (FastThreadLocal.FastThreadLocalLocationIdentity) obj; + return ReflectionUtil.readField(FastThreadLocal.FastThreadLocalLocationIdentity.class, "this$0", fastThreadLocalLocationIdentity); + } + + @Override + protected void makeChildIds(ObjectCopier.Encoder encoder, Object obj, ObjectCopier.ObjectPath objectPath) { + makeStaticFieldIds(encoder, objectPath, getFastThreadLocal(obj)); + } + @Override protected void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { - FastThreadLocal.FastThreadLocalLocationIdentity fastThreadLocalLocationIdentity = (FastThreadLocal.FastThreadLocalLocationIdentity) obj; - FastThreadLocal fastThreadLocal = ReflectionUtil.readField(FastThreadLocal.FastThreadLocalLocationIdentity.class, "this$0", fastThreadLocalLocationIdentity); - writeStaticField(encoder, stream, fastThreadLocal); + writeStaticField(encoder, stream, getFastThreadLocal(obj)); } @Override protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { - FastThreadLocal fastThreadLocal = readStaticFieldAndGetObject(stream); + FastThreadLocal fastThreadLocal = readStaticFieldAndGetObject(decoder, stream); return fastThreadLocal.getLocationIdentity(); } } @@ -381,30 +399,43 @@ protected VMThreadLocalInfoBuiltIn() { super(VMThreadLocalInfo.class); } + private static FastThreadLocal getThreadLocal(Object obj) { + VMThreadLocalCollector vmThreadLocalCollector = ImageSingletons.lookup(VMThreadLocalCollector.class); + return vmThreadLocalCollector.getThreadLocal((VMThreadLocalInfo) obj); + } + + @Override + protected void makeChildIds(ObjectCopier.Encoder encoder, Object obj, ObjectCopier.ObjectPath objectPath) { + makeStaticFieldIds(encoder, objectPath, getThreadLocal(obj)); + } + @Override protected void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { - VMThreadLocalInfo vmThreadLocalInfo = (VMThreadLocalInfo) obj; - VMThreadLocalCollector vmThreadLocalCollector = ImageSingletons.lookup(VMThreadLocalCollector.class); - FastThreadLocal fastThreadLocal = vmThreadLocalCollector.getThreadLocal(vmThreadLocalInfo); - writeStaticField(encoder, stream, fastThreadLocal); + writeStaticField(encoder, stream, getThreadLocal(obj)); } @Override protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { - FastThreadLocal fastThreadLocal = readStaticFieldAndGetObject(stream); + FastThreadLocal fastThreadLocal = readStaticFieldAndGetObject(decoder, stream); return ImageSingletons.lookup(VMThreadLocalCollector.class).forFastThreadLocal(fastThreadLocal); } } + private static void makeStaticFieldIds(ObjectCopier.Encoder encoder, ObjectCopier.ObjectPath objectPath, Object object) { + Field staticField = encoder.getExternalValues().get(object); + encoder.makeStringId(staticField.getDeclaringClass().getName(), objectPath); + encoder.makeStringId(staticField.getName(), objectPath); + } + private static void writeStaticField(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object object) throws IOException { Field staticField = encoder.getExternalValues().get(object); - stream.writeUTF(staticField.getDeclaringClass().getName()); - stream.writeUTF(staticField.getName()); + encoder.writeString(stream, staticField.getDeclaringClass().getName()); + encoder.writeString(stream, staticField.getName()); } - private static T readStaticFieldAndGetObject(ObjectCopierInputStream stream) throws IOException { - String className = stream.readUTF(); - String fieldName = stream.readUTF(); + private static T readStaticFieldAndGetObject(ObjectCopier.Decoder decoder, ObjectCopierInputStream stream) throws IOException { + String className = decoder.readString(stream); + String fieldName = decoder.readString(stream); Class declaringClass = ReflectionUtil.lookupClass(false, className); return ReflectionUtil.readStaticField(declaringClass, fieldName); } From 38ca85e0ddcee4dd7ed8c8681d1ce8155e46d9df Mon Sep 17 00:00:00 2001 From: Peter Hofer Date: Fri, 4 Oct 2024 15:22:35 +0200 Subject: [PATCH 06/13] Compact binary map encoding in ObjectCopier. --- .../jdk/graal/compiler/util/ObjectCopier.java | 64 +++++-------------- 1 file changed, 15 insertions(+), 49 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java index c858b37cfe14..6c84c86abd44 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java @@ -238,17 +238,6 @@ protected Object decode(Decoder decoder, Class concreteType, ObjectCopierInpu } } - /** - * Builtin for handling {@link HashMap} or {@link IdentityHashMap} values. - * - * EBNF: - * - *
-     *  builtinValue = [ key ":" value { " " } key ":" value ]
-     *  key = fieldValue
-     *  value = fieldValue
-     * 
- */ static final class HashMapBuiltin extends Builtin { final Map, Supplier> factories; @@ -272,31 +261,18 @@ protected void makeChildIds(Encoder encoder, Object obj, ObjectPath objectPath) @Override protected void encode(Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { Map map = (Map) obj; - String encoded = encoder.encodeMap(new EconomicMapWrap<>(map)); - stream.writeUTF(encoded); + encoder.encodeMap(stream, new EconomicMapWrap<>(map)); } @SuppressWarnings("unchecked") @Override protected Object decode(Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { Map map = (Map) factories.get(concreteType).get(); - String encoded = stream.readUTF(); - decoder.decodeMap(encoded, map::put); + decoder.decodeMap(stream, map::put); return map; } } - /** - * Builtin for handling {@link EconomicMap} values. - * - * EBNF: - * - *
-     *  builtinValue = [ key ":" value { " " } key ":" value ]
-     *  key = fieldValue
-     *  value = fieldValue
-     * 
- */ static final class EconomicMapBuiltin extends Builtin { EconomicMapBuiltin() { super(EconomicMap.class, EconomicMap.create().getClass()); @@ -312,16 +288,14 @@ protected void makeChildIds(Encoder encoder, Object obj, ObjectPath objectPath) @Override protected void encode(Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { - String encoded = encoder.encodeMap((UnmodifiableEconomicMap) obj); - stream.writeUTF(encoded); + encoder.encodeMap(stream, (UnmodifiableEconomicMap) obj); } @Override protected Object decode(Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { if (EconomicMap.class.isAssignableFrom(concreteType)) { EconomicMap map = EconomicMap.create(); - String encoded = stream.readUTF(); - decoder.decodeMap(encoded, map::put); + decoder.decodeMap(stream, map::put); return map; } else { throw new GraalError("Unexpected concrete Map type: ", concreteType); @@ -391,13 +365,6 @@ public ObjectCopier() { addBuiltin(stringBuiltin, char[].class); } - static String[] splitSpaceSeparatedElements(String elements) { - if (elements.isEmpty()) { - return new String[0]; - } - return elements.split(" "); - } - /** * Encodes {@code root} to a String using {@code encoder}. */ @@ -444,11 +411,12 @@ Object getObject(int id, boolean requireNonNull) { return obj; } - void decodeMap(String encoded, BiConsumer putMethod) { - for (String e : splitSpaceSeparatedElements(encoded)) { - String[] keyValue = e.split(":"); - GraalError.guarantee(keyValue.length == 2, "Invalid encoded key:value: %s", e); - resolveId(keyValue[0], k -> resolveId(keyValue[1], v -> putMethod.accept(k, v))); + void decodeMap(ObjectCopierInputStream stream, BiConsumer putMethod) throws IOException { + int size = Math.toIntExact(stream.readPackedUnsigned()); + for (int i = 0; i < size; i++) { + int keyId = readId(stream); + int valueId = readId(stream); + resolveId(keyId, k -> resolveId(valueId, v -> putMethod.accept(k, v))); } } @@ -760,16 +728,14 @@ public Map getExternalValues() { return Collections.unmodifiableMap(externalValues); } - private String encodeMap(UnmodifiableEconomicMap map) { + private void encodeMap(ObjectCopierOutputStream stream, UnmodifiableEconomicMap map) throws IOException { + stream.writePackedUnsigned(map.size()); + UnmodifiableMapCursor cursor = map.getEntries(); - StringBuilder value = new StringBuilder(); while (cursor.advance()) { - if (!value.isEmpty()) { - value.append(" "); - } - value.append(getId(cursor.getKey())).append(":").append(getId(cursor.getValue())); + writeId(stream, getId(cursor.getKey())); + writeId(stream, getId(cursor.getValue())); } - return value.toString(); } void makeMapChildIds(EconomicMap map, ObjectPath objectPath) { From cf1f54aebf6170a7fac0cca438aa81b9269bb7d7 Mon Sep 17 00:00:00 2001 From: Peter Hofer Date: Fri, 4 Oct 2024 19:19:56 +0200 Subject: [PATCH 07/13] Encode ObjectCopier objects and fields in compact binary format. --- .../jdk/graal/compiler/util/ObjectCopier.java | 101 +++++------------- .../util/ObjectCopierInputStream.java | 3 +- .../util/ObjectCopierOutputStream.java | 3 +- 3 files changed, 29 insertions(+), 78 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java index 6c84c86abd44..861fc378eb3e 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java @@ -47,8 +47,6 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Supplier; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Stream; import org.graalvm.collections.EconomicMap; @@ -74,9 +72,6 @@ */ public class ObjectCopier { - private static final Pattern OBJECT_LINE = Pattern.compile("\\{(?[\\w.$]+):(?\\d+)}"); - private static final Pattern FIELD_LINE = Pattern.compile("(?[^:]+):(?[^ ]+) = (?.*)"); - /** * A builtin is specialized support for encoded and decoding values of specific types. */ @@ -500,68 +495,29 @@ private Object decode(byte[] encoded) { break; } case '{': { - String legacy = stream.readUTF(); - Matcher matcher = OBJECT_LINE.matcher(legacy); - GraalError.guarantee(matcher.matches(), "Invalid object record: %s", legacy); - String className = matcher.group("class"); - int fieldCount = Integer.parseInt(matcher.group("fieldCount")); + String className = readString(stream); + int fieldCount = Math.toIntExact(stream.readPackedUnsigned()); Class clazz = loadClass(className); Object obj = allocateInstance(clazz); addDecodedObject(id, obj); ClassInfo classInfo = classInfos.computeIfAbsent(clazz, ClassInfo::of); for (int i = 0; i < fieldCount; i++) { fieldNum = i; - String fieldLine = stream.readUTF(); - Matcher fieldMatcher = FIELD_LINE.matcher(fieldLine); - GraalError.guarantee(fieldMatcher.matches(), "Invalid field line: %s", fieldLine); - String fieldDesc = fieldMatcher.group("desc"); - String value = fieldMatcher.group("value"); + String fieldDesc = readString(stream); Field field = classInfo.fields().get(fieldDesc); GraalError.guarantee(field != null, "Unknown field: %s", fieldDesc); Class type = field.getType(); - int expectTypeId = Integer.parseInt(fieldMatcher.group("typeId")); - resolveId(expectTypeId, o -> checkFieldType(expectTypeId, field)); - if (type.isPrimitive()) { - switch (type.getName()) { - case "boolean": { - writeField(field, obj, Boolean.parseBoolean(value)); - break; - } - case "byte": { - writeField(field, obj, Byte.parseByte(value)); - break; - } - case "char": { - writeField(field, obj, (char) Integer.parseInt(value)); - break; - } - case "short": { - writeField(field, obj, Short.parseShort(value)); - break; - } - case "int": { - writeField(field, obj, Integer.parseInt(value)); - break; - } - case "float": { - writeField(field, obj, Float.parseFloat(value)); - break; - } - case "long": { - writeField(field, obj, Long.parseLong(value)); - break; - } - case "double": { - writeField(field, obj, Double.parseDouble(value)); - break; - } - default: { - throw new GraalError("Unexpected primitive type: %s", type.getName()); - } - } + Object value = stream.readTypedValue(); + GraalError.guarantee(type == value.getClass().getDeclaredField("TYPE").get(null), + "Different primitive type: %s, expected %s", value.getClass(), type.getName()); + writeField(field, obj, value); } else { + GraalError.guarantee(stream.readByte() == 'L', "Expecting object type"); + int expectTypeId = readId(stream); + int value = readId(stream); + resolveId(expectTypeId, o -> checkFieldType(expectTypeId, field)); resolveId(value, o -> writeField(field, obj, o)); } } @@ -591,14 +547,6 @@ private static int readId(ObjectCopierInputStream stream) throws IOException { return (int) stream.readPackedUnsigned(); } - void resolveId(String id, Consumer c) { - try { - resolveId(Integer.parseInt(id), c); - } catch (NumberFormatException e) { - throw new GraalError(e, "Invalid object id: %s", id); - } - } - void resolveId(int id, Consumer c) { if (id != 0) { Object objValue = getObject(id, false); @@ -811,15 +759,17 @@ void makeId(Object obj, ObjectPath objectPath) { checkIllegalValue(LocationIdentity.class, obj, objectPath, "must come from a static field"); checkIllegalValue(HashSet.class, obj, objectPath, "hashes are typically not stable across VM executions"); + makeStringId(clazz.getName(), objectPath); ClassInfo classInfo = makeClassInfo(clazz, objectPath); - for (Field f : classInfo.fields().values()) { + classInfo.fields().forEach((fieldDesc, f) -> { String fieldName = f.getDeclaringClass().getSimpleName() + "#" + f.getName(); - makeId(f.getType(), objectPath.add(fieldName + ":type")); + makeStringId(fieldDesc, objectPath.add(fieldName + ":name")); if (!f.getType().isPrimitive()) { + makeId(f.getType(), objectPath.add(fieldName + ":type")); Object fieldValue = readField(f, obj); makeId(fieldValue, objectPath.add(fieldName)); } - } + }); } } @@ -875,18 +825,21 @@ private void encode(ObjectCopierOutputStream out, Object root) throws IOExceptio ClassInfo classInfo = classInfos.get(clazz); out.writeByte('{'); writeId(out, id); - out.writeUTF(String.format("{%s:%d}", clazz.getName(), classInfo.fields().size())); + writeString(out, clazz.getName()); + out.writePackedUnsigned(classInfo.fields.size()); for (var e : classInfo.fields().entrySet()) { Field f = e.getValue(); - Object fValue = readField(f, obj); Class fieldType = f.getType(); - int fieldTypeId = getId(fieldType); - if (!fieldType.isPrimitive()) { - fValue = String.valueOf(getId(fValue)); - } else if (fieldType == char.class) { - fValue = (int) (Character) fValue; + Object fValue = readField(f, obj); + writeString(out, e.getKey()); + if (fieldType.isPrimitive()) { + out.writeTypedValue(fValue); + } else { + int fieldTypeId = getId(fieldType); + out.writeByte('L'); + writeId(out, fieldTypeId); + writeId(out, getId(fValue)); } - out.writeUTF(String.format("%s:%d = %s", e.getKey(), fieldTypeId, fValue)); } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java index 299ae6e2f6fe..c046048743f6 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java @@ -24,11 +24,10 @@ */ package jdk.graal.compiler.util; -import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; -public class ObjectCopierInputStream extends DataInputStream { +public class ObjectCopierInputStream extends TypedDataInputStream { public ObjectCopierInputStream(InputStream in) { super(in); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java index c9e351b99e7c..ba6ccd895b07 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java @@ -24,14 +24,13 @@ */ package jdk.graal.compiler.util; -import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.Array; import jdk.graal.compiler.core.common.calc.UnsignedMath; -public class ObjectCopierOutputStream extends DataOutputStream { +public class ObjectCopierOutputStream extends TypedDataOutputStream { // Constants for UNSIGNED5 coding of Pack200 protected static final long HIGH_WORD_SHIFT = 6; protected static final long NUM_HIGH_CODES = 1 << HIGH_WORD_SHIFT; // number of high codes (64) From dad70fd4cd7a0e1f34fca89f1f9f8d8c39596428 Mon Sep 17 00:00:00 2001 From: Peter Hofer Date: Fri, 4 Oct 2024 19:28:22 +0200 Subject: [PATCH 08/13] Omit implicit incrementing ids in ObjectCopier. --- .../src/jdk/graal/compiler/util/ObjectCopier.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java index 861fc378eb3e..950e8f6970be 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java @@ -451,14 +451,13 @@ private Object decode(byte[] encoded) { strings[i] = stream.readUTF(); } rootId = readId(stream); - for (;;) { - recordNum++; + for (int id = 1;; id++) { + recordNum = id; fieldNum = -1; int c = stream.read(); if (c == -1) { break; } - int id = readId(stream); switch (c) { case '<': { String className = readString(stream); @@ -795,7 +794,6 @@ private void encode(ObjectCopierOutputStream out, Object root) throws IOExceptio Builtin builtin = getBuiltin(clazz); if (builtin != null) { out.writeByte('<'); - writeId(out, id); writeString(out, clazz.getName()); try { builtin.encode(this, out, obj); @@ -806,25 +804,21 @@ private void encode(ObjectCopierOutputStream out, Object root) throws IOExceptio Class componentType = clazz.getComponentType(); if (!componentType.isPrimitive()) { out.writeByte(']'); - writeId(out, id); writeString(out, componentType.getName()); int[] ids = Stream.of((Object[]) obj).mapToInt(this::getId).toArray(); out.writeTypedPrimitiveArray(ids); } else { out.writeByte('['); - writeId(out, id); out.writeTypedPrimitiveArray(obj); } } else if (clazz == Field.class) { Field field = (Field) obj; out.writeByte('@'); - writeId(out, id); writeString(out, field.getDeclaringClass().getName()); writeString(out, field.getName()); } else { ClassInfo classInfo = classInfos.get(clazz); out.writeByte('{'); - writeId(out, id); writeString(out, clazz.getName()); out.writePackedUnsigned(classInfo.fields.size()); for (var e : classInfo.fields().entrySet()) { From 4ab62281f3ddada4f200ecf9b2408f777a959857 Mon Sep 17 00:00:00 2001 From: Peter Hofer Date: Fri, 4 Oct 2024 21:47:24 +0200 Subject: [PATCH 09/13] Use packing for most integer values. --- .../jdk/graal/compiler/util/ObjectCopier.java | 41 ++++--- .../util/ObjectCopierInputStream.java | 102 +++++++----------- .../util/ObjectCopierOutputStream.java | 62 +++++++---- .../compiler/util/TypedDataInputStream.java | 60 ++++------- .../compiler/util/TypedDataOutputStream.java | 9 +- .../pointsto/heap/ImageLayerSnapshotUtil.java | 20 ++-- .../heap/SVMImageLayerSnapshotUtil.java | 8 +- 7 files changed, 147 insertions(+), 155 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java index 950e8f6970be..91f7b859ea9a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java @@ -222,12 +222,12 @@ static final class EnumBuiltin extends Builtin { @Override protected void encode(Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { - stream.writeInt(((Enum) obj).ordinal()); + stream.writePackedUnsignedInt(((Enum) obj).ordinal()); } @Override protected Object decode(Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { - int ord = stream.readInt(); + int ord = stream.readPackedUnsignedInt(); Object[] constants = concreteType.getEnumConstants(); return constants[ord]; } @@ -407,7 +407,7 @@ Object getObject(int id, boolean requireNonNull) { } void decodeMap(ObjectCopierInputStream stream, BiConsumer putMethod) throws IOException { - int size = Math.toIntExact(stream.readPackedUnsigned()); + int size = stream.readPackedUnsignedInt(); for (int i = 0; i < size; i++) { int keyId = readId(stream); int valueId = readId(stream); @@ -445,10 +445,10 @@ private Object decode(byte[] encoded) { recordNum = 0; try (ObjectCopierInputStream stream = new ObjectCopierInputStream(new ByteArrayInputStream(encoded))) { - int nstrings = Math.toIntExact(stream.readPackedUnsigned()); + int nstrings = stream.readPackedUnsignedInt(); strings = new String[nstrings]; for (int i = 0; i < nstrings; i++) { - strings[i] = stream.readUTF(); + strings[i] = stream.readStringValue(); } rootId = readId(stream); for (int id = 1;; id++) { @@ -476,7 +476,11 @@ private Object decode(byte[] encoded) { case ']': { // object array String componentTypeName = readString(stream); Class componentType = loadClass(componentTypeName); - int[] elements = (int[]) stream.readTypedPrimitiveArray(); + int length = stream.readPackedUnsignedInt(); + int[] elements = new int[length]; + for (int i = 0; i < length; i++) { + elements[i] = readId(stream); + } Object[] arr = (Object[]) Array.newInstance(componentType, elements.length); addDecodedObject(id, arr); for (int i = 0; i < elements.length; i++) { @@ -495,7 +499,7 @@ private Object decode(byte[] encoded) { } case '{': { String className = readString(stream); - int fieldCount = Math.toIntExact(stream.readPackedUnsigned()); + int fieldCount = stream.readPackedUnsignedInt(); Class clazz = loadClass(className); Object obj = allocateInstance(clazz); addDecodedObject(id, obj); @@ -543,7 +547,7 @@ private Object decode(byte[] encoded) { } private static int readId(ObjectCopierInputStream stream) throws IOException { - return (int) stream.readPackedUnsigned(); + return stream.readPackedUnsignedInt(); } void resolveId(int id, Consumer c) { @@ -560,7 +564,7 @@ void resolveId(int id, Consumer c) { } public String readString(ObjectCopierInputStream stream) throws IOException { - int id = Math.toIntExact(stream.readPackedUnsigned()); + int id = stream.readPackedUnsignedInt(); return strings[id]; } @@ -676,7 +680,7 @@ public Map getExternalValues() { } private void encodeMap(ObjectCopierOutputStream stream, UnmodifiableEconomicMap map) throws IOException { - stream.writePackedUnsigned(map.size()); + stream.writePackedUnsignedInt(map.size()); UnmodifiableMapCursor cursor = map.getEntries(); while (cursor.advance()) { @@ -697,7 +701,7 @@ void makeMapChildIds(EconomicMap map, ObjectPath objectPath) { public void writeString(ObjectCopierOutputStream stream, String s) throws IOException { int id = strings.getIndex(s); - stream.writePackedUnsigned(id); + stream.writePackedUnsignedInt(id); } public void makeStringId(String s, ObjectPath objectPath) { @@ -782,9 +786,9 @@ private ClassInfo makeClassInfo(Class clazz, ObjectPath objectPath) { private void encode(ObjectCopierOutputStream out, Object root) throws IOException { String[] encodedStrings = strings.encodeAll(new String[strings.getLength()]); - out.writePackedUnsigned(encodedStrings.length); + out.writePackedUnsignedInt(encodedStrings.length); for (String s : encodedStrings) { - out.writeUTF(s); + out.writeStringValue(s); } Object[] encodedObjects = objects.encodeAll(new Object[objects.getLength()]); writeId(out, getId(root)); @@ -805,8 +809,11 @@ private void encode(ObjectCopierOutputStream out, Object root) throws IOExceptio if (!componentType.isPrimitive()) { out.writeByte(']'); writeString(out, componentType.getName()); - int[] ids = Stream.of((Object[]) obj).mapToInt(this::getId).toArray(); - out.writeTypedPrimitiveArray(ids); + Object[] objs = (Object[]) obj; + out.writePackedUnsignedInt(objs.length); + for (Object o : objs) { + writeId(out, getId(o)); + } } else { out.writeByte('['); out.writeTypedPrimitiveArray(obj); @@ -820,7 +827,7 @@ private void encode(ObjectCopierOutputStream out, Object root) throws IOExceptio ClassInfo classInfo = classInfos.get(clazz); out.writeByte('{'); writeString(out, clazz.getName()); - out.writePackedUnsigned(classInfo.fields.size()); + out.writePackedUnsignedInt(classInfo.fields.size()); for (var e : classInfo.fields().entrySet()) { Field f = e.getValue(); Class fieldType = f.getType(); @@ -840,7 +847,7 @@ private void encode(ObjectCopierOutputStream out, Object root) throws IOExceptio } private static void writeId(ObjectCopierOutputStream out, int id) throws IOException { - out.writePackedUnsigned(id); + out.writePackedUnsignedInt(id); } private int getId(Object o) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java index c046048743f6..d54d8e4a6b4e 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java @@ -26,83 +26,57 @@ import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Array; +import java.nio.charset.StandardCharsets; public class ObjectCopierInputStream extends TypedDataInputStream { public ObjectCopierInputStream(InputStream in) { super(in); } + @Override + protected Object readUntypedValue(int type) throws IOException { + return switch (type) { + case 'I' -> (int) readPackedSignedLong(); + case 'J' -> readPackedSignedLong(); + default -> super.readUntypedValue(type); + }; + } + + @Override + protected String readStringValue() throws IOException { + int len = readPackedUnsignedInt(); + byte[] bytes = new byte[len]; + readFully(bytes); + return new String(bytes, StandardCharsets.UTF_8); + } + public Object readTypedPrimitiveArray() throws IOException { - final int length = readInt(); - final byte type = readByte(); - switch (type) { - case 'Z': { - boolean[] a = new boolean[length]; - for (int i = 0; i < length; i++) { - a[i] = readBoolean(); - } - return a; - } - case 'B': { - byte[] a = new byte[length]; - for (int i = 0; i < length; i++) { - a[i] = readByte(); - } - return a; - } - case 'S': { - short[] a = new short[length]; - for (int i = 0; i < length; i++) { - a[i] = readShort(); - } - return a; - } - case 'C': { - char[] a = new char[length]; - for (int i = 0; i < length; i++) { - a[i] = readChar(); - } - return a; - } - case 'I': { - int[] a = new int[length]; - for (int i = 0; i < length; i++) { - a[i] = readInt(); - } - return a; - } - case 'J': { - long[] a = new long[length]; - for (int i = 0; i < length; i++) { - a[i] = readLong(); - } - return a; - } - case 'F': { - float[] a = new float[length]; - for (int i = 0; i < length; i++) { - a[i] = readFloat(); - } - return a; - } - case 'D': { - double[] a = new double[length]; - for (int i = 0; i < length; i++) { - a[i] = readDouble(); - } - return a; - } - default: - throw new IOException("Unsupported type: " + Integer.toHexString(type)); + int len = readPackedUnsignedInt(); + int type = readUnsignedByte(); + Object arr = switch (type) { + case 'Z' -> new boolean[len]; + case 'B' -> new byte[len]; + case 'S' -> new short[len]; + case 'C' -> new char[len]; + case 'I' -> new int[len]; + case 'J' -> new long[len]; + case 'F' -> new float[len]; + case 'D' -> new double[len]; + default -> throw new IOException("Unsupported type: " + Integer.toHexString(type)); + }; + for (int i = 0; i < len; i++) { + Array.set(arr, i, readUntypedValue(type)); } + return arr; } - public long readPackedSigned() throws IOException { + public long readPackedSignedLong() throws IOException { return decodeSign(readPacked()); } - public long readPackedUnsigned() throws IOException { - return readPacked(); + public int readPackedUnsignedInt() throws IOException { + return Math.toIntExact(readPacked()); } private static long decodeSign(long value) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java index ba6ccd895b07..9b2794f330e1 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.Array; +import java.nio.charset.StandardCharsets; import jdk.graal.compiler.core.common.calc.UnsignedMath; @@ -41,49 +42,72 @@ public ObjectCopierOutputStream(OutputStream out) { super(out); } + @Override + public void writeTypedValue(Object value) throws IOException { + if (value instanceof Enum) { + throw new IllegalArgumentException(String.format("Unsupported type: Value: %s, Value type: %s", value, value.getClass())); + } + if (value instanceof Integer) { + writeByte('I'); + writePackedSignedLong((int) value); + } else if (value instanceof Long) { + writeByte('J'); + writePackedSignedLong((long) value); + } else { + super.writeTypedValue(value); + } + } + + @Override + protected void writeStringValue(String value) throws IOException { + byte[] bytes = value.getBytes(StandardCharsets.UTF_8); + this.writePackedUnsignedInt(bytes.length); + this.write(bytes); + } + public void writeTypedPrimitiveArray(Object value) throws IOException { Class compClz = value.getClass().componentType(); int length = Array.getLength(value); - this.writeInt(length); + writePackedUnsignedInt(length); if (compClz == boolean.class) { - this.writeByte('Z'); + writeByte('Z'); for (int i = 0; i < length; i++) { - this.writeBoolean(Array.getBoolean(value, i)); + writeBoolean(Array.getBoolean(value, i)); } } else if (compClz == byte.class) { - this.writeByte('B'); + writeByte('B'); for (int i = 0; i < length; i++) { - this.writeByte(Array.getByte(value, i)); + writeByte(Array.getByte(value, i)); } } else if (compClz == short.class) { - this.writeByte('S'); + writeByte('S'); for (int i = 0; i < length; i++) { - this.writeShort(Array.getShort(value, i)); + writeShort(Array.getShort(value, i)); } } else if (compClz == char.class) { - this.writeByte('C'); + writeByte('C'); for (int i = 0; i < length; i++) { - this.writeChar(Array.getChar(value, i)); + writeChar(Array.getChar(value, i)); } } else if (compClz == int.class) { - this.writeByte('I'); + writeByte('I'); for (int i = 0; i < length; i++) { - this.writeInt(Array.getInt(value, i)); + writePackedSignedLong(Array.getInt(value, i)); } } else if (compClz == long.class) { - this.writeByte('J'); + writeByte('J'); for (int i = 0; i < length; i++) { - this.writeLong(Array.getLong(value, i)); + writePackedSignedLong(Array.getLong(value, i)); } } else if (compClz == float.class) { - this.writeByte('F'); + writeByte('F'); for (int i = 0; i < length; i++) { - this.writeFloat(Array.getFloat(value, i)); + writeFloat(Array.getFloat(value, i)); } } else if (compClz == double.class) { - this.writeByte('D'); + writeByte('D'); for (int i = 0; i < length; i++) { - this.writeDouble(Array.getDouble(value, i)); + writeDouble(Array.getDouble(value, i)); } } else { throw new IllegalArgumentException(String.format("Unsupported array: Value: %s, Value type: %s", value, value.getClass())); @@ -94,12 +118,12 @@ static long encodeSign(long value) { return (value << 1) ^ (value >> 63); } - public void writePackedSigned(long value) throws IOException { + public void writePackedSignedLong(long value) throws IOException { // this is a modified version of the SIGNED5 encoding from Pack200 writePacked(encodeSign(value)); } - public void writePackedUnsigned(long value) throws IOException { + public void writePackedUnsignedInt(int value) throws IOException { // this is a modified version of the UNSIGNED5 encoding from Pack200 writePacked(value); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/TypedDataInputStream.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/TypedDataInputStream.java index a9db7dd3afd4..eb0c8f9a4a45 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/TypedDataInputStream.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/TypedDataInputStream.java @@ -45,42 +45,28 @@ public TypedDataInputStream(InputStream in) { * @exception IOException in case of an I/O error. */ public Object readTypedValue() throws IOException { - Object value; - final byte type = readByte(); - switch (type) { - case 'Z': - value = readBoolean(); - break; - case 'B': - value = readByte(); - break; - case 'S': - value = readShort(); - break; - case 'C': - value = readChar(); - break; - case 'I': - value = readInt(); - break; - case 'J': - value = readLong(); - break; - case 'F': - value = readFloat(); - break; - case 'D': - value = readDouble(); - break; - case 'U': - int len = readInt(); - byte[] bytes = new byte[len]; - readFully(bytes); - value = new String(bytes, StandardCharsets.UTF_8); - break; - default: - throw new IOException("Unsupported type: " + Integer.toHexString(type)); - } - return value; + return readUntypedValue(readUnsignedByte()); + } + + protected Object readUntypedValue(int type) throws IOException { + return switch (type) { + case 'Z' -> readBoolean(); + case 'B' -> readByte(); + case 'S' -> readShort(); + case 'C' -> readChar(); + case 'I' -> readInt(); + case 'J' -> readLong(); + case 'F' -> readFloat(); + case 'D' -> readDouble(); + case 'U' -> readStringValue(); + default -> throw new IOException("Unsupported type: " + Integer.toHexString(type)); + }; + } + + protected String readStringValue() throws IOException { + int len = readInt(); + byte[] bytes = new byte[len]; + readFully(bytes); + return new String(bytes, StandardCharsets.UTF_8); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/TypedDataOutputStream.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/TypedDataOutputStream.java index 2756885007f4..4356ce305e5b 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/TypedDataOutputStream.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/TypedDataOutputStream.java @@ -91,16 +91,17 @@ public void writeTypedValue(Object value) throws IOException { this.writeByte('D'); this.writeDouble((Double) value); } else if (valueClz == String.class) { - writeStringValue((String) value); + this.writeByte('U'); + this.writeStringValue((String) value); } else if (valueClz.isEnum()) { - writeStringValue(((Enum) value).name()); + this.writeByte('U'); + this.writeStringValue(((Enum) value).name()); } else { throw new IllegalArgumentException(String.format("Unsupported type: Value: %s, Value type: %s", value, valueClz)); } } - private void writeStringValue(String value) throws IOException { - this.writeByte('U'); + protected void writeStringValue(String value) throws IOException { byte[] bytes = value.getBytes(StandardCharsets.UTF_8); this.writeInt(bytes.length); this.write(bytes); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java index a5b4a0033309..a9967dd39d1f 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java @@ -280,12 +280,12 @@ protected ImageHeapConstantBuiltIn(ImageLayerWriter imageLayerWriter, ImageLayer public void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { ImageHeapConstant imageHeapConstant = (ImageHeapConstant) obj; imageLayerWriter.elementsToPersist.add(new AnalysisFuture<>(() -> imageLayerWriter.persistConstant(UNDEFINED_CONSTANT_ID, UNDEFINED_FIELD_INDEX, imageHeapConstant))); - stream.writeInt(imageHeapConstant.getConstantData().id); + stream.writePackedUnsignedInt(imageHeapConstant.getConstantData().id); } @Override protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { - int id = stream.readInt(); + int id = stream.readPackedUnsignedInt(); return imageLayerLoader.getOrCreateConstant(id); } } @@ -304,12 +304,12 @@ protected AnalysisTypeBuiltIn(ImageLayerWriter imageLayerWriter, ImageLayerLoade public void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { AnalysisType type = (AnalysisType) obj; imageLayerWriter.persistType(type); - stream.writeInt(type.getId()); + stream.writePackedUnsignedInt(type.getId()); } @Override protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { - int id = stream.readInt(); + int id = stream.readPackedUnsignedInt(); return imageLayerLoader.getAnalysisType(id); } } @@ -338,12 +338,12 @@ public void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream imageLayerWriter.persistType(parameter); } imageLayerWriter.persistType(declaringClass); - stream.writeInt(method.getId()); + stream.writePackedUnsignedInt(method.getId()); } @Override protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { - int id = stream.readInt(); + int id = stream.readPackedUnsignedInt(); if (id == analysisMethod.getId()) { return analysisMethod; } @@ -365,12 +365,12 @@ protected AnalysisFieldBuiltIn(ImageLayerWriter imageLayerWriter, ImageLayerLoad public void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { AnalysisField field = (AnalysisField) obj; int id = encodeField(field, imageLayerWriter); - stream.writeInt(id); + stream.writePackedUnsignedInt(id); } @Override protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { - int id = stream.readInt(); + int id = stream.readPackedUnsignedInt(); return decodeField(imageLayerLoader, id); } } @@ -390,12 +390,12 @@ public void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream FieldLocationIdentity fieldLocationIdentity = (FieldLocationIdentity) obj; AnalysisField field = (AnalysisField) fieldLocationIdentity.getField(); int id = encodeField(field, imageLayerWriter); - stream.writeInt(id); + stream.writePackedUnsignedInt(id); } @Override protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { - int id = stream.readInt(); + int id = stream.readPackedUnsignedInt(); return new FieldLocationIdentity(decodeField(imageLayerLoader, id)); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerSnapshotUtil.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerSnapshotUtil.java index 8d55517f5f62..3e8e8466f8ad 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerSnapshotUtil.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerSnapshotUtil.java @@ -274,12 +274,12 @@ protected HostedTypeBuiltIn(SVMImageLayerLoader svmImageLayerLoader) { @Override protected void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { int id = ((HostedType) obj).getWrapped().getId(); - stream.writeInt(id); + stream.writePackedUnsignedInt(id); } @Override protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { - int id = stream.readInt(); + int id = stream.readPackedUnsignedInt(); AnalysisType type = svmImageLayerLoader.getAnalysisType(id); return svmImageLayerLoader.getHostedUniverse().lookup(type); } @@ -295,12 +295,12 @@ protected HostedMethodBuiltIn(SVMImageLayerLoader svmImageLayerLoader) { @Override protected void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { - stream.writeInt(((HostedMethod) obj).getWrapped().getId()); + stream.writePackedUnsignedInt(((HostedMethod) obj).getWrapped().getId()); } @Override protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { - int id = stream.readInt(); + int id = stream.readPackedUnsignedInt(); AnalysisMethod method = svmImageLayerLoader.getAnalysisMethod(id); return svmImageLayerLoader.getHostedUniverse().lookup(method); } From 5c55805c2b01df7b3d81ffee6816af4d06c1c583 Mon Sep 17 00:00:00 2001 From: Peter Hofer Date: Mon, 7 Oct 2024 12:16:52 +0200 Subject: [PATCH 10/13] Omit field counts, names and types. --- .../jdk/graal/compiler/util/ObjectCopier.java | 40 +++++-------------- .../util/ObjectCopierOutputStream.java | 25 ++++++++++++ 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java index 91f7b859ea9a..d17d6299ce65 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java @@ -44,6 +44,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Supplier; @@ -306,9 +308,9 @@ protected Object decode(Decoder decoder, Class concreteType, ObjectCopierInpu * have the same name in which case the descriptor includes the qualified name of the * class declaring the field as a prefix. */ - public record ClassInfo(Class clazz, Map fields) { + public record ClassInfo(Class clazz, SortedMap fields) { public static ClassInfo of(Class declaringClass) { - Map fields = new HashMap<>(); + SortedMap fields = new TreeMap<>(); for (Class c = declaringClass; !c.equals(Object.class); c = c.getSuperclass()) { for (Field f : c.getDeclaredFields()) { if (!Modifier.isStatic(f.getModifiers())) { @@ -499,30 +501,22 @@ private Object decode(byte[] encoded) { } case '{': { String className = readString(stream); - int fieldCount = stream.readPackedUnsignedInt(); Class clazz = loadClass(className); Object obj = allocateInstance(clazz); addDecodedObject(id, obj); ClassInfo classInfo = classInfos.computeIfAbsent(clazz, ClassInfo::of); - for (int i = 0; i < fieldCount; i++) { - fieldNum = i; - String fieldDesc = readString(stream); - Field field = classInfo.fields().get(fieldDesc); - GraalError.guarantee(field != null, "Unknown field: %s", fieldDesc); + fieldNum = 0; + for (Field field : classInfo.fields.values()) { Class type = field.getType(); - if (type.isPrimitive()) { - Object value = stream.readTypedValue(); - GraalError.guarantee(type == value.getClass().getDeclaredField("TYPE").get(null), - "Different primitive type: %s, expected %s", value.getClass(), type.getName()); + char typeCh = type.descriptorString().charAt(0); + Object value = stream.readUntypedValue(typeCh); writeField(field, obj, value); } else { - GraalError.guarantee(stream.readByte() == 'L', "Expecting object type"); - int expectTypeId = readId(stream); int value = readId(stream); - resolveId(expectTypeId, o -> checkFieldType(expectTypeId, field)); resolveId(value, o -> writeField(field, obj, o)); } + fieldNum++; } break; } @@ -568,12 +562,6 @@ public String readString(ObjectCopierInputStream stream) throws IOException { return strings[id]; } - private void checkFieldType(int expectTypeId, Field field) { - Class actualType = field.getType(); - Class expectType = (Class) idToObject.get(expectTypeId); - GraalError.guarantee(actualType.equals(expectType), "Type of %s has changed: %s != %s", field, expectType, actualType); - } - private static Object allocateInstance(Class clazz) { try { return UNSAFE.allocateInstance(clazz); @@ -766,9 +754,7 @@ void makeId(Object obj, ObjectPath objectPath) { ClassInfo classInfo = makeClassInfo(clazz, objectPath); classInfo.fields().forEach((fieldDesc, f) -> { String fieldName = f.getDeclaringClass().getSimpleName() + "#" + f.getName(); - makeStringId(fieldDesc, objectPath.add(fieldName + ":name")); if (!f.getType().isPrimitive()) { - makeId(f.getType(), objectPath.add(fieldName + ":type")); Object fieldValue = readField(f, obj); makeId(fieldValue, objectPath.add(fieldName)); } @@ -827,18 +813,13 @@ private void encode(ObjectCopierOutputStream out, Object root) throws IOExceptio ClassInfo classInfo = classInfos.get(clazz); out.writeByte('{'); writeString(out, clazz.getName()); - out.writePackedUnsignedInt(classInfo.fields.size()); for (var e : classInfo.fields().entrySet()) { Field f = e.getValue(); Class fieldType = f.getType(); Object fValue = readField(f, obj); - writeString(out, e.getKey()); if (fieldType.isPrimitive()) { - out.writeTypedValue(fValue); + out.writeUntypedValue(fValue); } else { - int fieldTypeId = getId(fieldType); - out.writeByte('L'); - writeId(out, fieldTypeId); writeId(out, getId(fValue)); } } @@ -856,6 +837,7 @@ private int getId(Object o) { return objects.getIndex(field); } return objects.getIndex(o); + } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java index 9b2794f330e1..9a81bb770863 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java @@ -58,6 +58,31 @@ public void writeTypedValue(Object value) throws IOException { } } + public void writeUntypedValue(Object value) throws IOException { + Class valueClz = value.getClass(); + if (valueClz == Boolean.class) { + writeBoolean((Boolean) value); + } else if (valueClz == Byte.class) { + writeByte((Byte) value); + } else if (valueClz == Short.class) { + writeShort((Short) value); + } else if (valueClz == Character.class) { + writeChar((Character) value); + } else if (valueClz == Integer.class) { + writePackedSignedLong((int) value); + } else if (valueClz == Long.class) { + writePackedSignedLong((long) value); + } else if (valueClz == Float.class) { + writeFloat((Float) value); + } else if (valueClz == Double.class) { + writeDouble((Double) value); + } else if (valueClz == String.class) { + writeStringValue((String) value); + } else { + throw new IllegalArgumentException(String.format("Unsupported type: Value: %s, Value type: %s", value, valueClz)); + } + } + @Override protected void writeStringValue(String value) throws IOException { byte[] bytes = value.getBytes(StandardCharsets.UTF_8); From 693732b8a8202613ed51b826d559607e77b1a088 Mon Sep 17 00:00:00 2001 From: Peter Hofer Date: Tue, 15 Oct 2024 09:58:53 +0200 Subject: [PATCH 11/13] Different extension for graphs file. --- .../oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java index a9967dd39d1f..af4b6d892904 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java @@ -51,8 +51,9 @@ public class ImageLayerSnapshotUtil { public static final String FILE_NAME_PREFIX = "layer-snapshot-"; - public static final String GRAPHS_FILE_NAME_PREFIX = "layer-snapshot-graphs-"; public static final String FILE_EXTENSION = ".json"; + public static final String GRAPHS_FILE_NAME_PREFIX = "layer-snapshot-graphs-"; + public static final String GRAPHS_FILE_EXTENSION = ".big"; public static final String CONSTRUCTOR_NAME = ""; @@ -190,7 +191,7 @@ public static String snapshotFileName(String imageName) { } public static String snapshotGraphsFileName(String imageName) { - return GRAPHS_FILE_NAME_PREFIX + imageName + FILE_EXTENSION; + return GRAPHS_FILE_NAME_PREFIX + imageName + GRAPHS_FILE_EXTENSION; } public String getTypeIdentifier(AnalysisType type) { From 91384f58ee551f976b556eb5c7b7f63564a7b23b Mon Sep 17 00:00:00 2001 From: Peter Hofer Date: Wed, 16 Oct 2024 12:31:52 +0200 Subject: [PATCH 12/13] Add human-readable debug output for maintainability. --- .../compiler/util/test/ObjectCopierTest.java | 21 +-- .../jdk/graal/compiler/util/ObjectCopier.java | 64 +++++++-- .../util/ObjectCopierInputStream.java | 31 ++-- .../util/ObjectCopierOutputStream.java | 135 +++++++++++------- 4 files changed, 175 insertions(+), 76 deletions(-) diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/util/test/ObjectCopierTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/util/test/ObjectCopierTest.java index 213ebc8f1d3c..706305ef9591 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/util/test/ObjectCopierTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/util/test/ObjectCopierTest.java @@ -25,9 +25,9 @@ package jdk.graal.compiler.util.test; import java.io.IOException; +import java.io.PrintStream; import java.lang.reflect.Field; import java.util.ArrayList; -import java.util.Base64; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.LinkedHashMap; @@ -176,19 +176,13 @@ public void testIt() { List externalValueFields = List.of(ObjectCopier.getField(BaseClass.class, "BASE_SINGLETON"), ObjectCopier.getField(TestObject.class, "TEST_OBJECT_SINGLETON")); - byte[] encoded = ObjectCopier.encode(new ObjectCopier.Encoder(externalValueFields), root); - if (DEBUG) { - System.out.printf("encoded:%n%s%n", Base64.getEncoder().encodeToString(encoded)); - } + byte[] encoded = encode(externalValueFields, root, "encoded"); Object decoded = ObjectCopier.decode(encoded, loader); if (DEBUG) { System.out.printf("root:%n%s%n", root); System.out.printf("decoded:%n%s%n", decoded); } - byte[] reencoded = ObjectCopier.encode(new ObjectCopier.Encoder(externalValueFields), decoded); - if (DEBUG) { - System.out.printf("reencoded:%n%s%n", Base64.getEncoder().encodeToString(reencoded)); - } + byte[] reencoded = encode(externalValueFields, decoded, "reencoded"); Assert.assertArrayEquals(encoded, reencoded); Map root2 = (Map) ObjectCopier.decode(reencoded, loader); @@ -202,6 +196,15 @@ public void testIt() { Assert.assertSame(root2.get("singleton2"), root2.get("singleton2_2")); } + private static byte[] encode(List externalValueFields, Object root, String debugLabel) { + PrintStream debugStream = null; + if (DEBUG) { + debugStream = System.out; + debugStream.printf("%s:%n", debugLabel); + } + return ObjectCopier.encode(new ObjectCopier.Encoder(externalValueFields, debugStream), root); + } + @Test public void test() throws IOException, InterruptedException { launchSubprocess(this::testIt, diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java index d17d6299ce65..86906a61ae79 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java @@ -27,6 +27,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.PrintStream; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -368,7 +369,7 @@ public ObjectCopier() { public static byte[] encode(Encoder encoder, Object root) { encoder.makeId(root, ObjectPath.of("[root:" + root.getClass().getName() + "]")); ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try (ObjectCopierOutputStream cos = new ObjectCopierOutputStream(baos)) { + try (ObjectCopierOutputStream cos = new ObjectCopierOutputStream(baos, encoder.debugOutput)) { encoder.encode(cos, root); } catch (IOException e) { throw new RuntimeException(e); @@ -616,16 +617,27 @@ public static class Encoder extends ObjectCopier { */ final Map externalValues; + private final PrintStream debugOutput; + public Encoder(List externalValueFields) { - this(gatherExternalValues(externalValueFields)); + this(externalValueFields, null); + } + + public Encoder(List externalValueFields, PrintStream debugOutput) { + this(gatherExternalValues(externalValueFields), debugOutput); } /** * Use precomputed {@code externalValues} to avoid recomputing them. */ public Encoder(Map externalValues) { + this(externalValues, null); + } + + public Encoder(Map externalValues, PrintStream debugOutput) { objects.addObject(null); this.externalValues = externalValues; + this.debugOutput = debugOutput; } public static Map gatherExternalValues(List externalValueFields) { @@ -668,11 +680,13 @@ public Map getExternalValues() { } private void encodeMap(ObjectCopierOutputStream stream, UnmodifiableEconomicMap map) throws IOException { - stream.writePackedUnsignedInt(map.size()); + stream.internalWritePackedUnsignedInt(map.size()); UnmodifiableMapCursor cursor = map.getEntries(); while (cursor.advance()) { + debugf("%n "); writeId(stream, getId(cursor.getKey())); + debugf(" :"); writeId(stream, getId(cursor.getValue())); } } @@ -689,7 +703,10 @@ void makeMapChildIds(EconomicMap map, ObjectPath objectPath) { public void writeString(ObjectCopierOutputStream stream, String s) throws IOException { int id = strings.getIndex(s); - stream.writePackedUnsignedInt(id); + stream.internalWritePackedUnsignedInt(id); + if (debugOutput != null) { + debugf(" %s", escapeDebugStringValue(s)); + } } public void makeStringId(String s, ObjectPath objectPath) { @@ -772,19 +789,23 @@ private ClassInfo makeClassInfo(Class clazz, ObjectPath objectPath) { private void encode(ObjectCopierOutputStream out, Object root) throws IOException { String[] encodedStrings = strings.encodeAll(new String[strings.getLength()]); - out.writePackedUnsignedInt(encodedStrings.length); + out.internalWritePackedUnsignedInt(encodedStrings.length); for (String s : encodedStrings) { out.writeStringValue(s); } Object[] encodedObjects = objects.encodeAll(new Object[objects.getLength()]); + debugf("root:"); writeId(out, getId(root)); + debugf("%n"); for (int id = 1; id < encodedObjects.length; id++) { Object obj = encodedObjects[id]; Class clazz = obj.getClass(); Builtin builtin = getBuiltin(clazz); if (builtin != null) { - out.writeByte('<'); + out.internalWriteByte('<'); + debugf("%d:<", id); writeString(out, clazz.getName()); + debugf(" > ="); try { builtin.encode(this, out, obj); } catch (IOException e) { @@ -793,28 +814,35 @@ private void encode(ObjectCopierOutputStream out, Object root) throws IOExceptio } else if (clazz.isArray()) { Class componentType = clazz.getComponentType(); if (!componentType.isPrimitive()) { - out.writeByte(']'); + out.internalWriteByte(']'); + debugf("%d:[", id); writeString(out, componentType.getName()); + debugf(" ] ="); Object[] objs = (Object[]) obj; - out.writePackedUnsignedInt(objs.length); + out.internalWritePackedUnsignedInt(objs.length); for (Object o : objs) { writeId(out, getId(o)); } } else { - out.writeByte('['); + out.internalWriteByte('['); + debugf("%d:[ %s ] =", id, componentType.getName()); out.writeTypedPrimitiveArray(obj); } } else if (clazz == Field.class) { Field field = (Field) obj; - out.writeByte('@'); + out.internalWriteByte('@'); + debugf("%d:@", id); writeString(out, field.getDeclaringClass().getName()); writeString(out, field.getName()); } else { ClassInfo classInfo = classInfos.get(clazz); - out.writeByte('{'); + out.internalWriteByte('{'); + debugf("%d:{", id); writeString(out, clazz.getName()); + debugf(" }"); for (var e : classInfo.fields().entrySet()) { Field f = e.getValue(); + debugf("%n "); Class fieldType = f.getType(); Object fValue = readField(f, obj); if (fieldType.isPrimitive()) { @@ -822,8 +850,13 @@ private void encode(ObjectCopierOutputStream out, Object root) throws IOExceptio } else { writeId(out, getId(fValue)); } + debugf("\t # %s", f.getName()); + if (!fieldType.isPrimitive()) { + debugf(" (object)"); + } } } + debugf("%n"); } } @@ -837,7 +870,16 @@ private int getId(Object o) { return objects.getIndex(field); } return objects.getIndex(o); + } + + private void debugf(String format, Object... args) { + if (debugOutput != null) { + debugOutput.printf(format, args); + } + } + static String escapeDebugStringValue(String s) { + return s.replace("\\", "\\\\").replace("\n", "\\n").replace("\r", "\\r"); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java index d54d8e4a6b4e..4ff414f92983 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java @@ -29,31 +29,46 @@ import java.lang.reflect.Array; import java.nio.charset.StandardCharsets; -public class ObjectCopierInputStream extends TypedDataInputStream { +/** + * Delegates to, but does not subclass, {@link TypedDataInputStream} for symmetry with + * {@link ObjectCopierOutputStream}, see the reasoning there. Add methods as needed. + */ +public class ObjectCopierInputStream extends InputStream { + private final TypedDataInputStream in; + public ObjectCopierInputStream(InputStream in) { - super(in); + this.in = new TypedDataInputStream(in); + } + + @Override + public int read() throws IOException { + return in.read(); } @Override + public void close() throws IOException { + in.close(); + } + protected Object readUntypedValue(int type) throws IOException { return switch (type) { case 'I' -> (int) readPackedSignedLong(); case 'J' -> readPackedSignedLong(); - default -> super.readUntypedValue(type); + case 'U' -> readStringValue(); + default -> in.readUntypedValue(type); }; } - @Override protected String readStringValue() throws IOException { int len = readPackedUnsignedInt(); byte[] bytes = new byte[len]; - readFully(bytes); + in.readFully(bytes); return new String(bytes, StandardCharsets.UTF_8); } public Object readTypedPrimitiveArray() throws IOException { int len = readPackedUnsignedInt(); - int type = readUnsignedByte(); + int type = in.readUnsignedByte(); Object arr = switch (type) { case 'Z' -> new boolean[len]; case 'B' -> new byte[len]; @@ -84,7 +99,7 @@ private static long decodeSign(long value) { } private long readPacked() throws IOException { - int b0 = readUnsignedByte(); + int b0 = in.readUnsignedByte(); if (b0 < ObjectCopierOutputStream.NUM_LOW_CODES) { return b0; } @@ -92,7 +107,7 @@ private long readPacked() throws IOException { long sum = b0; long shift = ObjectCopierOutputStream.HIGH_WORD_SHIFT; for (int i = 2;; i++) { - long b = readUnsignedByte(); + long b = in.readUnsignedByte(); sum += b << shift; if (b < ObjectCopierOutputStream.NUM_LOW_CODES || i == ObjectCopierOutputStream.MAX_BYTES) { return sum; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java index 9a81bb770863..79c8608df2a0 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java @@ -24,138 +24,177 @@ */ package jdk.graal.compiler.util; +import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.io.PrintStream; import java.lang.reflect.Array; import java.nio.charset.StandardCharsets; import jdk.graal.compiler.core.common.calc.UnsignedMath; -public class ObjectCopierOutputStream extends TypedDataOutputStream { +/** + * Wraps an {@link OutputStream} and writes binary data of certain kinds to it. Optionally prints a + * text representation to a separate stream for debugging. This delegates to, but does not subclass, + * {@link TypedDataOutputStream} because it cannot override final public methods such as + * {@link DataOutputStream#writeInt} to intercept them for debug printing. Add such methods as + * needed. + */ +public class ObjectCopierOutputStream extends OutputStream { // Constants for UNSIGNED5 coding of Pack200 protected static final long HIGH_WORD_SHIFT = 6; protected static final long NUM_HIGH_CODES = 1 << HIGH_WORD_SHIFT; // number of high codes (64) protected static final long NUM_LOW_CODES = (1 << Byte.SIZE) - NUM_HIGH_CODES; protected static final long MAX_BYTES = 11; - public ObjectCopierOutputStream(OutputStream out) { - super(out); + private final TypedDataOutputStream out; + private final PrintStream debugOut; + + /** + * @param debugOut {@code null} or a stream to print a text representation of the written binary + * data to. This stream is never closed. + */ + public ObjectCopierOutputStream(OutputStream out, PrintStream debugOut) { + this.out = new TypedDataOutputStream(out); + this.debugOut = debugOut; } @Override - public void writeTypedValue(Object value) throws IOException { - if (value instanceof Enum) { - throw new IllegalArgumentException(String.format("Unsupported type: Value: %s, Value type: %s", value, value.getClass())); - } - if (value instanceof Integer) { - writeByte('I'); - writePackedSignedLong((int) value); - } else if (value instanceof Long) { - writeByte('J'); - writePackedSignedLong((long) value); - } else { - super.writeTypedValue(value); + public void write(int b) throws IOException { + internalWriteByte(b); + if (debugOut != null) { + debugOut.printf(" 0x%02x", b); } } + protected void internalWriteByte(int v) throws IOException { + out.writeByte(v); + } + + @Override + public void close() throws IOException { + out.close(); + } + public void writeUntypedValue(Object value) throws IOException { Class valueClz = value.getClass(); if (valueClz == Boolean.class) { - writeBoolean((Boolean) value); + out.writeBoolean((Boolean) value); } else if (valueClz == Byte.class) { - writeByte((Byte) value); + internalWriteByte((Byte) value); } else if (valueClz == Short.class) { - writeShort((Short) value); + out.writeShort((Short) value); } else if (valueClz == Character.class) { - writeChar((Character) value); + out.writeChar((Character) value); } else if (valueClz == Integer.class) { - writePackedSignedLong((int) value); + internalWritePackedSigned((int) value); } else if (valueClz == Long.class) { - writePackedSignedLong((long) value); + internalWritePackedSigned((long) value); } else if (valueClz == Float.class) { - writeFloat((Float) value); + out.writeFloat((Float) value); } else if (valueClz == Double.class) { - writeDouble((Double) value); + out.writeDouble((Double) value); } else if (valueClz == String.class) { writeStringValue((String) value); } else { throw new IllegalArgumentException(String.format("Unsupported type: Value: %s, Value type: %s", value, valueClz)); } + debugPrintValue(value); + } + + protected void debugPrintValue(Object value) { + if (debugOut != null) { + Object debugValue = switch (value) { + case String s -> ObjectCopier.Encoder.escapeDebugStringValue(s); + case Character c -> (int) c; + default -> value; + }; + debugOut.printf(" %s", debugValue); + } } - @Override protected void writeStringValue(String value) throws IOException { byte[] bytes = value.getBytes(StandardCharsets.UTF_8); - this.writePackedUnsignedInt(bytes.length); - this.write(bytes); + internalWritePackedUnsignedInt(bytes.length); + out.write(bytes); } public void writeTypedPrimitiveArray(Object value) throws IOException { Class compClz = value.getClass().componentType(); int length = Array.getLength(value); - writePackedUnsignedInt(length); + internalWritePackedUnsignedInt(length); if (compClz == boolean.class) { - writeByte('Z'); + internalWriteByte('Z'); for (int i = 0; i < length; i++) { - writeBoolean(Array.getBoolean(value, i)); + out.writeBoolean(Array.getBoolean(value, i)); } } else if (compClz == byte.class) { - writeByte('B'); + internalWriteByte('B'); for (int i = 0; i < length; i++) { - writeByte(Array.getByte(value, i)); + internalWriteByte(Array.getByte(value, i)); } } else if (compClz == short.class) { - writeByte('S'); + internalWriteByte('S'); for (int i = 0; i < length; i++) { - writeShort(Array.getShort(value, i)); + out.writeShort(Array.getShort(value, i)); } } else if (compClz == char.class) { - writeByte('C'); + internalWriteByte('C'); for (int i = 0; i < length; i++) { - writeChar(Array.getChar(value, i)); + out.writeChar(Array.getChar(value, i)); } } else if (compClz == int.class) { - writeByte('I'); + internalWriteByte('I'); for (int i = 0; i < length; i++) { - writePackedSignedLong(Array.getInt(value, i)); + internalWritePackedSigned(Array.getInt(value, i)); } } else if (compClz == long.class) { - writeByte('J'); + internalWriteByte('J'); for (int i = 0; i < length; i++) { - writePackedSignedLong(Array.getLong(value, i)); + internalWritePackedSigned(Array.getLong(value, i)); } } else if (compClz == float.class) { - writeByte('F'); + internalWriteByte('F'); for (int i = 0; i < length; i++) { - writeFloat(Array.getFloat(value, i)); + out.writeFloat(Array.getFloat(value, i)); } } else if (compClz == double.class) { - writeByte('D'); + internalWriteByte('D'); for (int i = 0; i < length; i++) { - writeDouble(Array.getDouble(value, i)); + out.writeDouble(Array.getDouble(value, i)); } } else { throw new IllegalArgumentException(String.format("Unsupported array: Value: %s, Value type: %s", value, value.getClass())); } + if (debugOut != null) { + for (int i = 0; i < length; i++) { + debugPrintValue(Array.get(value, i)); + } + } } - static long encodeSign(long value) { + private static long encodeSign(long value) { return (value << 1) ^ (value >> 63); } - public void writePackedSignedLong(long value) throws IOException { + protected void internalWritePackedSigned(long value) throws IOException { // this is a modified version of the SIGNED5 encoding from Pack200 writePacked(encodeSign(value)); } public void writePackedUnsignedInt(int value) throws IOException { + internalWritePackedUnsignedInt(value); + debugPrintValue(value); + } + + protected void internalWritePackedUnsignedInt(int value) throws IOException { // this is a modified version of the UNSIGNED5 encoding from Pack200 writePacked(value); } private void writePacked(long value) throws IOException { if (UnsignedMath.belowThan(value, NUM_LOW_CODES)) { - writeByte((int) value); + internalWriteByte((int) value); return; } long sum = value; @@ -163,10 +202,10 @@ private void writePacked(long value) throws IOException { sum -= NUM_LOW_CODES; long u1 = NUM_LOW_CODES + (sum & (NUM_HIGH_CODES - 1)); // this is a "high code" sum >>>= HIGH_WORD_SHIFT; // extracted 6 bits - writeByte((int) u1); + internalWriteByte((int) u1); } // remainder is either a "low code" or the last byte assert sum == (sum & 0xFF) : "not a byte"; - writeByte((int) (sum & 0xFF)); + internalWriteByte((int) (sum & 0xFF)); } } From 1e0bcc3cfa42efe89b120466e25ec1217241cd15 Mon Sep 17 00:00:00 2001 From: Peter Hofer Date: Thu, 17 Oct 2024 12:55:35 +0200 Subject: [PATCH 13/13] Add fingerprints for ObjectCopier types and enum constants for safety. --- .../jdk/graal/compiler/util/ObjectCopier.java | 31 ++++++++++++++++--- .../util/ObjectCopierInputStream.java | 7 ++++- .../util/ObjectCopierOutputStream.java | 9 ++++-- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java index 86906a61ae79..2838cd3298a3 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java @@ -225,14 +225,23 @@ static final class EnumBuiltin extends Builtin { @Override protected void encode(Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { - stream.writePackedUnsignedInt(((Enum) obj).ordinal()); + Enum con = (Enum) obj; + stream.writePackedUnsignedInt(con.ordinal()); + stream.writeShort(fingerprint(con)); + } + + private static short fingerprint(Enum con) { + int h = con.name().hashCode(); + return hashIntToShort(h); } @Override protected Object decode(Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { int ord = stream.readPackedUnsignedInt(); - Object[] constants = concreteType.getEnumConstants(); - return constants[ord]; + int fingerprint = stream.readShort(); + Enum con = (Enum) concreteType.getEnumConstants()[ord]; + GraalError.guarantee(fingerprint(con) == fingerprint, "Enum constant type mismatch: %s ordinal %d not expected to be %s", concreteType.getName(), ord, con); + return con; } } @@ -309,7 +318,7 @@ protected Object decode(Decoder decoder, Class concreteType, ObjectCopierInpu * have the same name in which case the descriptor includes the qualified name of the * class declaring the field as a prefix. */ - public record ClassInfo(Class clazz, SortedMap fields) { + public record ClassInfo(Class clazz, SortedMap fields, short fingerprint) { public static ClassInfo of(Class declaringClass) { SortedMap fields = new TreeMap<>(); for (Class c = declaringClass; !c.equals(Object.class); c = c.getSuperclass()) { @@ -325,10 +334,19 @@ public static ClassInfo of(Class declaringClass) { } } } - return new ClassInfo(declaringClass, fields); + int h = fields.size(); + for (var entry : fields.entrySet()) { + h = h * 31 + entry.getKey().hashCode(); + h = h * 31 + entry.getValue().getType().getName().hashCode(); + } + return new ClassInfo(declaringClass, fields, hashIntToShort(h)); } } + private static short hashIntToShort(int h) { + return (short) (h ^ (h >>> 16)); + } + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); final Map, ClassInfo> classInfos = new HashMap<>(); @@ -506,6 +524,8 @@ private Object decode(byte[] encoded) { Object obj = allocateInstance(clazz); addDecodedObject(id, obj); ClassInfo classInfo = classInfos.computeIfAbsent(clazz, ClassInfo::of); + short fingerprint = stream.readShort(); + GraalError.guarantee(fingerprint == classInfo.fingerprint, "Type mismatch on %s", clazz); fieldNum = 0; for (Field field : classInfo.fields.values()) { Class type = field.getType(); @@ -840,6 +860,7 @@ private void encode(ObjectCopierOutputStream out, Object root) throws IOExceptio debugf("%d:{", id); writeString(out, clazz.getName()); debugf(" }"); + out.writeShort(classInfo.fingerprint); for (var e : classInfo.fields().entrySet()) { Field f = e.getValue(); debugf("%n "); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java index 4ff414f92983..5256aee057da 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierInputStream.java @@ -31,7 +31,8 @@ /** * Delegates to, but does not subclass, {@link TypedDataInputStream} for symmetry with - * {@link ObjectCopierOutputStream}, see the reasoning there. Add methods as needed. + * {@link ObjectCopierOutputStream}, see the reasoning there. Add methods such as {@link #readShort} + * as needed. */ public class ObjectCopierInputStream extends InputStream { private final TypedDataInputStream in; @@ -50,6 +51,10 @@ public void close() throws IOException { in.close(); } + public short readShort() throws IOException { + return in.readShort(); + } + protected Object readUntypedValue(int type) throws IOException { return switch (type) { case 'I' -> (int) readPackedSignedLong(); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java index 79c8608df2a0..fb6e73ec40fe 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopierOutputStream.java @@ -37,8 +37,8 @@ * Wraps an {@link OutputStream} and writes binary data of certain kinds to it. Optionally prints a * text representation to a separate stream for debugging. This delegates to, but does not subclass, * {@link TypedDataOutputStream} because it cannot override final public methods such as - * {@link DataOutputStream#writeInt} to intercept them for debug printing. Add such methods as - * needed. + * {@link DataOutputStream#writeShort} to intercept them for debug printing. Add such methods such + * as {@link #writeShort} as needed. */ public class ObjectCopierOutputStream extends OutputStream { // Constants for UNSIGNED5 coding of Pack200 @@ -76,6 +76,11 @@ public void close() throws IOException { out.close(); } + public void writeShort(int v) throws IOException { + out.writeShort(v); + debugPrintValue(v); + } + public void writeUntypedValue(Object value) throws IOException { Class valueClz = value.getClass(); if (valueClz == Boolean.class) {