From dfe7b7ba14f49d4615ac865cbc3726ba11d83a0d Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Wed, 5 Oct 2022 19:14:17 +0200 Subject: [PATCH] Allow simulated constants in the image heap. --- .../common/type}/CompressibleConstant.java | 12 +- .../core/common/type/TypedConstant.java | 33 +++ .../AnalysisObjectScanningObserver.java | 4 +- .../oracle/graal/pointsto/ObjectScanner.java | 94 ++++---- .../com/oracle/graal/pointsto/api/HostVM.java | 7 +- .../pointsto/flow/MethodTypeFlowBuilder.java | 3 +- .../flow/context/object/AnalysisObject.java | 10 +- .../pointsto/heap/HeapSnapshotVerifier.java | 69 +++--- .../oracle/graal/pointsto/heap/ImageHeap.java | 16 +- .../graal/pointsto/heap/ImageHeapArray.java | 103 +++++++++ .../pointsto/heap/ImageHeapConstant.java | 158 +++++++++++++ ...HeapObject.java => ImageHeapInstance.java} | 118 +++++----- .../graal/pointsto/heap/ImageHeapScanner.java | 212 +++++++++++------ .../infrastructure/UniverseMetaAccess.java | 21 +- .../graal/pointsto/meta/AnalysisUniverse.java | 14 +- .../ReachabilityObjectScanner.java | 8 +- .../AbstractImageHeapLayouter.java | 4 +- .../ChunkedImageHeapPartition.java | 20 +- .../aarch64/SubstrateAArch64Backend.java | 17 +- .../graal/amd64/SubstrateAMD64Backend.java | 19 +- .../svm/core/graal/llvm/LLVMGenerator.java | 6 +- .../SharedConstantReflectionProvider.java | 25 ++ .../graal/nodes/SubstrateCompressionNode.java | 2 +- .../graal/nodes/SubstrateNarrowOopStamp.java | 2 +- .../svm/core/image/ImageHeapObject.java | 6 + .../svm/core/meta/CompressedNullConstant.java | 7 +- .../core/meta/SubstrateObjectConstant.java | 8 +- .../hosted/SubstrateGraalCompilerSetup.java | 4 +- .../SubstrateConstantReflectionProvider.java | 1 + .../SubstrateRuntimeConfigurationBuilder.java | 4 +- .../com/oracle/svm/hosted/FeatureImpl.java | 3 +- .../svm/hosted/NativeImageGenerator.java | 2 +- .../oracle/svm/hosted/ProgressReporter.java | 16 +- .../src/com/oracle/svm/hosted/SVMHost.java | 6 +- .../ameta/AnalysisConstantFieldProvider.java | 4 +- .../AnalysisConstantReflectionProvider.java | 104 +++++++-- .../flow/SVMMethodTypeFlowBuilder.java | 42 ++-- .../AnalysisToHostedGraphTransplanter.java | 6 + .../code/HostedImageHeapConstantPatch.java | 6 +- .../HostedRuntimeConfigurationBuilder.java | 2 +- .../SharedRuntimeConfigurationBuilder.java | 6 +- .../code/amd64/AMD64HostedPatcherFeature.java | 4 +- .../svm/hosted/heap/SVMImageHeapScanner.java | 24 +- .../hosted/image/LIRNativeImageCodeCache.java | 3 +- .../oracle/svm/hosted/image/NativeImage.java | 6 +- .../hosted/image/NativeImageCodeCache.java | 12 +- .../image/NativeImageDebugInfoProvider.java | 12 +- .../svm/hosted/image/NativeImageHeap.java | 213 +++++++++++++----- .../hosted/image/NativeImageHeapWriter.java | 105 ++++++--- .../hosted/image/ObjectGroupHistogram.java | 12 +- .../HostedConstantReflectionProvider.java | 31 ++- .../oracle/svm/hosted/meta/HostedField.java | 13 +- .../meta/HostedMemoryAccessProvider.java | 2 +- .../svm/hosted/meta/HostedMetaAccess.java | 5 + .../meta/SharedConstantFieldProvider.java | 6 +- ...trinsifyMethodHandlesInvocationPlugin.java | 13 +- 56 files changed, 1197 insertions(+), 468 deletions(-) rename {substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta => compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type}/CompressibleConstant.java (81%) create mode 100644 compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/TypedConstant.java create mode 100644 substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapArray.java create mode 100644 substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapConstant.java rename substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/{ImageHeapObject.java => ImageHeapInstance.java} (60%) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/CompressibleConstant.java b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/CompressibleConstant.java similarity index 81% rename from substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/CompressibleConstant.java rename to compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/CompressibleConstant.java index 863d1d182267..ca6043f5eac1 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/CompressibleConstant.java +++ b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/CompressibleConstant.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -22,15 +22,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.core.meta; +package org.graalvm.compiler.core.common.type; -import jdk.vm.ci.meta.Constant; +import jdk.vm.ci.meta.JavaConstant; // Subject to unification with HotspotConstant -public interface CompressibleConstant extends Constant { +public interface CompressibleConstant extends JavaConstant { boolean isCompressed(); - Constant compress(); + JavaConstant compress(); - Constant uncompress(); + JavaConstant uncompress(); } diff --git a/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/TypedConstant.java b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/TypedConstant.java new file mode 100644 index 000000000000..1bbe0fd2776b --- /dev/null +++ b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/TypedConstant.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022, 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 org.graalvm.compiler.core.common.type; + +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.MetaAccessProvider; +import jdk.vm.ci.meta.ResolvedJavaType; + +public interface TypedConstant extends JavaConstant { + ResolvedJavaType getType(MetaAccessProvider provider); +} diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AnalysisObjectScanningObserver.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AnalysisObjectScanningObserver.java index 28d73673ec54..95735017afa0 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AnalysisObjectScanningObserver.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AnalysisObjectScanningObserver.java @@ -63,7 +63,7 @@ public boolean forNullFieldValue(JavaConstant receiver, AnalysisField field, Sca @Override public boolean forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { PointsToAnalysis analysis = getAnalysis(); - AnalysisType fieldType = analysis.getMetaAccess().lookupJavaType(analysis.getSnippetReflectionProvider().asObject(Object.class, fieldValue).getClass()); + AnalysisType fieldType = analysis.getMetaAccess().lookupJavaType(fieldValue); /* Add the constant value object to the field's type flow. */ FieldTypeFlow fieldTypeFlow = getFieldTypeFlow(field, receiver); @@ -87,7 +87,7 @@ private FieldTypeFlow getFieldTypeFlow(AnalysisField field, JavaConstant receive * constant object. */ PointsToAnalysis analysis = getAnalysis(); - AnalysisType receiverType = analysis.getMetaAccess().lookupJavaType(analysis.getSnippetReflectionProvider().asObject(Object.class, receiver).getClass()); + AnalysisType receiverType = analysis.getMetaAccess().lookupJavaType(receiver); AnalysisObject constantReceiverObj = analysis.analysisPolicy().createConstantObject(analysis, receiver, receiverType); return constantReceiverObj.getInstanceFieldFlow(analysis, field, true); } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java index dea48b9290b8..3d95d0b85564 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java @@ -37,6 +37,8 @@ import org.graalvm.word.WordBase; import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException; +import com.oracle.graal.pointsto.heap.ImageHeapArray; +import com.oracle.graal.pointsto.heap.ImageHeapConstant; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; @@ -164,7 +166,7 @@ protected final void scanField(AnalysisField field, JavaConstant receiver, ScanR System.lineSeparator() + backtrace); } - if (fieldValue.getJavaKind() == JavaKind.Object && bb.getHostVM().isRelocatedPointer(constantAsObject(bb, fieldValue))) { + if (fieldValue.getJavaKind() == JavaKind.Object && bb.getHostVM().isRelocatedPointer(bb.getMetaAccess(), fieldValue)) { scanningObserver.forRelocatedPointerFieldValue(receiver, field, fieldValue, reason); } else if (fieldValue.isNull()) { scanningObserver.forNullFieldValue(receiver, field, reason); @@ -191,45 +193,60 @@ protected final void scanField(AnalysisField field, JavaConstant receiver, ScanR */ protected final void scanArray(JavaConstant array, ScanReason prevReason) { - Object valueObj = constantAsObject(bb, array); - AnalysisType arrayType = analysisType(bb, valueObj); - assert valueObj instanceof Object[]; - + AnalysisType arrayType = bb.getMetaAccess().lookupJavaType(array); ScanReason reason = new ArrayScan(arrayType, array, prevReason); - Object[] arrayObject = (Object[]) valueObj; - for (int idx = 0; idx < arrayObject.length; idx++) { - Object e = arrayObject[idx]; - if (e == null) { - scanningObserver.forNullArrayElement(array, arrayType, idx, reason); - } else { - try { - Object element = bb.getUniverse().replaceObject(e); - JavaConstant elementConstant = bb.getSnippetReflectionProvider().forObject(element); - AnalysisType elementType = analysisType(bb, element); - /* First notify the observer about the array element value... */ - scanningObserver.forNonNullArrayElement(array, arrayType, elementConstant, elementType, idx, reason); - /* - * ... and only then scan the new value, i.e., follow its references. The order - * is important for observers that expect to see the receiver before any of its - * referenced elements are being scanned. - */ - scanConstant(elementConstant, reason); - } catch (UnsupportedFeatureException ex) { - unsupportedFeatureDuringConstantScan(bb, bb.getSnippetReflectionProvider().forObject(e), ex, reason); + + if (array instanceof ImageHeapConstant) { + if (!arrayType.getComponentType().isPrimitive()) { + ImageHeapArray heapArray = (ImageHeapArray) array; + for (int idx = 0; idx < heapArray.getLength(); idx++) { + final JavaConstant element = heapArray.getElement(idx); + if (element.isNull()) { + scanningObserver.forNullArrayElement(array, arrayType, idx, reason); + } else { + scanArrayElement(array, arrayType, reason, idx, element); + } + } + } + } else { + Object[] arrayObject = (Object[]) constantAsObject(bb, array); + for (int idx = 0; idx < arrayObject.length; idx++) { + Object e = arrayObject[idx]; + if (e == null) { + scanningObserver.forNullArrayElement(array, arrayType, idx, reason); + } else { + try { + JavaConstant element = bb.getSnippetReflectionProvider().forObject(bb.getUniverse().replaceObject(e)); + scanArrayElement(array, arrayType, reason, idx, element); + } catch (UnsupportedFeatureException ex) { /* Object replacement can throw. */ + unsupportedFeatureDuringConstantScan(bb, bb.getSnippetReflectionProvider().forObject(e), ex, reason); + } } } } } + private void scanArrayElement(JavaConstant array, AnalysisType arrayType, ScanReason reason, int idx, JavaConstant elementConstant) { + AnalysisType elementType = bb.getMetaAccess().lookupJavaType(elementConstant); + /* First notify the observer about the array element value... */ + scanningObserver.forNonNullArrayElement(array, arrayType, elementConstant, elementType, idx, reason); + /* + * ... and only then scan the new value, i.e., follow its references. The order is important + * for observers that expect to see the receiver before any of its referenced elements are + * being scanned. + */ + scanConstant(elementConstant, reason); + } + public final void scanConstant(JavaConstant value, ScanReason reason) { - Object valueObj = constantAsObject(bb, value); - if (valueObj == null || valueObj instanceof WordBase) { + if (value.isNull() || bb.getMetaAccess().isInstanceOf(value, WordBase.class)) { return; } if (!bb.scanningPolicy().scanConstant(bb, value)) { - bb.markTypeInHeap(analysisType(bb, valueObj)); + bb.markTypeInHeap(bb.getMetaAccess().lookupJavaType(value)); return; } + Object valueObj = (value instanceof ImageHeapConstant) ? value : constantAsObject(bb, value); if (scannedObjects.putAndAcquire(valueObj) == null) { try { scanningObserver.forScannedConstant(value, reason); @@ -316,14 +333,17 @@ public static String asString(BigBang bb, JavaConstant constant) { } public static String asString(BigBang bb, JavaConstant constant, boolean appendToString) { - if (constant == null) { + if (constant == null || constant.isNull()) { return "null"; } - Object obj = constantAsObject(bb, constant); - if (obj == null) { - return "null"; + AnalysisType type = bb.getMetaAccess().lookupJavaType(constant); + if (constant instanceof ImageHeapConstant) { + // Checkstyle: allow Class.getSimpleName + return constant.getClass().getSimpleName() + "<" + type.toJavaName() + ">"; + // Checkstyle: disallow Class.getSimpleName } - String str = analysisType(bb, obj).toJavaName() + '@' + Integer.toHexString(System.identityHashCode(obj)); + Object obj = constantAsObject(bb, constant); + String str = type.toJavaName() + '@' + Integer.toHexString(System.identityHashCode(obj)); if (appendToString) { try { str += ": " + limit(obj.toString(), 80).replace(System.lineSeparator(), ""); @@ -351,10 +371,8 @@ public static String limit(String value, int length) { * element constants. */ private void doScan(WorklistEntry entry) { - Object valueObj = constantAsObject(bb, entry.constant); - try { - AnalysisType type = analysisType(bb, valueObj); + AnalysisType type = bb.getMetaAccess().lookupJavaType(entry.constant); type.registerAsReachable(); if (type.isInstanceClass()) { @@ -392,10 +410,6 @@ protected void finish() { } } - protected static AnalysisType analysisType(BigBang bb, Object constant) { - return bb.getMetaAccess().lookupJavaType(constant.getClass()); - } - public static AnalysisType constantType(BigBang bb, JavaConstant constant) { return bb.getMetaAccess().lookupJavaType(constant); } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java index 9a66ef69be47..b0720d1baf61 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java @@ -45,12 +45,14 @@ import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess; import com.oracle.graal.pointsto.BigBang; +import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.graal.pointsto.meta.HostedProviders; import com.oracle.graal.pointsto.phases.InlineBeforeAnalysisPolicy; +import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; @@ -78,9 +80,10 @@ public OptionValues options() { /** * Check if the provided object is a relocated pointer. * - * @param originalObject the object to check + * @param metaAccess the meta-access provider + * @param constant the constant to check */ - public boolean isRelocatedPointer(Object originalObject) { + public boolean isRelocatedPointer(UniverseMetaAccess metaAccess, JavaConstant constant) { return false; } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java index 1ba797ea98c7..45eacc7748d0 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java @@ -430,7 +430,8 @@ protected void apply() { AnalysisType type = (AnalysisType) StampTool.typeOrNull(node); assert type.isInstantiated(); TypeFlowBuilder sourceBuilder = TypeFlowBuilder.create(bb, node, ConstantTypeFlow.class, () -> { - ConstantTypeFlow constantSource = new ConstantTypeFlow(sourcePosition(node), type, TypeState.forConstant(this.bb, node.asJavaConstant(), type)); + JavaConstant heapConstant = bb.getUniverse().getHeapScanner().toImageHeapObject(node.asJavaConstant()); + ConstantTypeFlow constantSource = new ConstantTypeFlow(sourcePosition(node), type, TypeState.forConstant(this.bb, heapConstant, type)); flowsGraph.addMiscEntryFlow(constantSource); return constantSource; }); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/AnalysisObject.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/AnalysisObject.java index 8abe8633df5e..25fe1b281b5f 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/AnalysisObject.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/AnalysisObject.java @@ -289,14 +289,8 @@ public boolean isObjectArray() { */ public static boolean isEmptyObjectArrayConstant(PointsToAnalysis bb, JavaConstant constant) { assert constant.getJavaKind() == JavaKind.Object; - Object valueObj = bb.getProviders().getSnippetReflection().asObject(Object.class, constant); - if (valueObj instanceof Object[]) { - Object[] arrayValueObj = (Object[]) valueObj; - if (arrayValueObj.length == 0) { - return true; - } - } - return false; + Integer length = bb.getConstantReflectionProvider().readArrayLength(constant); + return length != null && length == 0; } @Override diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java index 01802174d168..28f6a09c7269 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java @@ -26,7 +26,6 @@ import static com.oracle.graal.pointsto.ObjectScanner.ScanReason; import static com.oracle.graal.pointsto.ObjectScanner.asString; -import static com.oracle.graal.pointsto.ObjectScanner.constantAsObject; import java.util.Objects; import java.util.function.Consumer; @@ -141,12 +140,12 @@ public boolean forNonNullFieldValue(JavaConstant receiver, AnalysisField field, Object fieldValueTask = typeData.getFieldValue(field); if (fieldValueTask instanceof JavaConstant) { JavaConstant fieldSnapshot = (JavaConstant) fieldValueTask; - verifyStaticFieldValue(typeData, field, fieldSnapshot, fieldValue, reason); + verifyStaticFieldValue(typeData, field, maybeUnwrapSnapshot(fieldSnapshot, fieldValue instanceof ImageHeapConstant), fieldValue, reason); } else if (fieldValueTask instanceof AnalysisFuture) { AnalysisFuture future = (AnalysisFuture) fieldValueTask; if (future.isDone()) { JavaConstant fieldSnapshot = future.guardedGet(); - verifyStaticFieldValue(typeData, field, fieldSnapshot, fieldValue, reason); + verifyStaticFieldValue(typeData, field, maybeUnwrapSnapshot(fieldSnapshot, fieldValue instanceof ImageHeapConstant), fieldValue, reason); } else { onStaticFieldNotComputed(field, fieldValue, reason); } @@ -156,12 +155,12 @@ public boolean forNonNullFieldValue(JavaConstant receiver, AnalysisField field, Object fieldValueTask = receiverObject.getFieldValue(field); if (fieldValueTask instanceof JavaConstant) { JavaConstant fieldSnapshot = (JavaConstant) fieldValueTask; - verifyInstanceFieldValue(field, receiverObject, fieldSnapshot, fieldValue, reason); + verifyInstanceFieldValue(field, receiverObject, maybeUnwrapSnapshot(fieldSnapshot, fieldValue instanceof ImageHeapConstant), fieldValue, reason); } else if (fieldValueTask instanceof AnalysisFuture) { AnalysisFuture future = (AnalysisFuture) fieldValueTask; if (future.isDone()) { JavaConstant fieldSnapshot = future.guardedGet(); - verifyInstanceFieldValue(field, receiverObject, fieldSnapshot, fieldValue, reason); + verifyInstanceFieldValue(field, receiverObject, maybeUnwrapSnapshot(fieldSnapshot, fieldValue instanceof ImageHeapConstant), fieldValue, reason); } else { /* * There may be some instance fields not yet computed because the verifier @@ -207,24 +206,24 @@ public boolean forNullArrayElement(JavaConstant array, AnalysisType arrayType, i public boolean forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementValue, AnalysisType elementType, int index, ScanReason reason) { ImageHeapArray arrayObject = (ImageHeapArray) getReceiverObject(array, reason); JavaConstant elementSnapshot = arrayObject.getElement(index); - if (!Objects.equals(elementSnapshot, elementValue)) { + if (!Objects.equals(maybeUnwrapSnapshot(elementSnapshot, elementValue instanceof ImageHeapConstant), elementValue)) { Consumer onAnalysisModified = (deepReason) -> onArrayElementMismatch(elementSnapshot, elementValue, deepReason); - arrayObject.setElement(index, scanner.onArrayElementReachable(array, arrayType, elementValue, index, reason, onAnalysisModified)); + arrayObject.setElement(index, scanner.onArrayElementReachable(arrayObject, arrayType, elementValue, index, reason, onAnalysisModified)); heapPatched = true; } return false; } @SuppressWarnings({"unchecked", "rawtypes"}) - private ImageHeapObject getReceiverObject(JavaConstant constant, ScanReason reason) { + private ImageHeapConstant getReceiverObject(JavaConstant constant, ScanReason reason) { Object task = imageHeap.getTask(constant); if (task == null) { throw error(reason, "Task is null for constant %s.", constant); - } else if (task instanceof ImageHeapObject) { - return (ImageHeapObject) task; + } else if (task instanceof ImageHeapConstant) { + return (ImageHeapConstant) task; } else { assert task instanceof AnalysisFuture; - AnalysisFuture future = ((AnalysisFuture) task); + AnalysisFuture future = ((AnalysisFuture) task); if (future.isDone()) { return future.guardedGet(); } else { @@ -239,20 +238,36 @@ public void forEmbeddedRoot(JavaConstant root, ScanReason reason) { Object rootTask = imageHeap.getTask(root); if (rootTask == null) { throw error(reason, "No snapshot task found for embedded root %s %n", root); - } else if (rootTask instanceof ImageHeapObject) { - JavaConstant snapshot = ((ImageHeapObject) rootTask).getObject(); - verifyEmbeddedRoot(snapshot, root, reason); + } else if (rootTask instanceof ImageHeapConstant) { + ImageHeapConstant snapshot = (ImageHeapConstant) rootTask; + verifyEmbeddedRoot(maybeUnwrapSnapshot(snapshot, root instanceof ImageHeapConstant), root, reason); } else { - AnalysisFuture future = ((AnalysisFuture) rootTask); + AnalysisFuture future = (AnalysisFuture) rootTask; if (future.isDone()) { - JavaConstant snapshot = future.guardedGet().getObject(); - verifyEmbeddedRoot(snapshot, root, reason); + ImageHeapConstant snapshot = future.guardedGet(); + verifyEmbeddedRoot(maybeUnwrapSnapshot(snapshot, root instanceof ImageHeapConstant), root, reason); } else { throw error(reason, "Snapshot not yet computed for embedded root %n new value: %s %n", root); } } } + /** + * Since embedded constants or constants reachable when scanning from roots can also be + * ImageHeapObject that are not backed by a hosted object, we need to make sure that we + * compare it with the correct representation of the snapshot, i.e., without unwrapping it. + * Moreover, since embedded ImageHeapObject can reference JavaConstant values directly (see + * the comment in + * {@link ImageHeapScanner#scanImageHeapObject(ImageHeapConstant, ScanReason, Consumer)} for + * details why that happens), then the 'snapshot' itself can be a JavaConstant. + */ + private JavaConstant maybeUnwrapSnapshot(JavaConstant snapshot, boolean asImageHeapObject) { + if (snapshot instanceof ImageHeapConstant) { + return asImageHeapObject ? snapshot : ((ImageHeapConstant) snapshot).getHostedObject(); + } + return snapshot; + } + private void verifyEmbeddedRoot(JavaConstant rootSnapshot, JavaConstant root, ScanReason reason) { if (!Objects.equals(rootSnapshot, root)) { throw error(reason, "Value mismatch for embedded root %n snapshot: %s %n new value: %s %n", rootSnapshot, root); @@ -261,11 +276,9 @@ private void verifyEmbeddedRoot(JavaConstant rootSnapshot, JavaConstant root, Sc @Override public void forScannedConstant(JavaConstant value, ScanReason reason) { - Object object = constantAsObject(bb, value); - Class objectClass = object.getClass(); - if (objectClass.equals(Class.class)) { + if (bb.getMetaAccess().isInstanceOf(value, Class.class)) { /* Ensure that java.lang.Class constants are scanned. */ - AnalysisType type = bb.getMetaAccess().lookupJavaType((Class) object); + AnalysisType type = (AnalysisType) bb.getConstantReflectionProvider().asJavaType(value); ensureTypeScanned(value, type, reason); } else { /* @@ -274,7 +287,7 @@ public void forScannedConstant(JavaConstant value, ScanReason reason) { * example com.oracle.svm.hosted.annotation.AnnotationObjectReplacer creates * annotation proxy types on the fly for constant annotation objects. */ - AnalysisType type = bb.getMetaAccess().lookupJavaType(objectClass); + AnalysisType type = bb.getMetaAccess().lookupJavaType(value); ensureTypeScanned(value, bb.getConstantReflectionProvider().asJavaClass(type), type, reason); } } @@ -292,14 +305,14 @@ private void ensureTypeScanned(JavaConstant value, JavaConstant typeConstant, An onNoTaskForClassConstant(type, reason); scanner.toImageHeapObject(typeConstant, reason, null); heapPatched = true; - } else if (task instanceof ImageHeapObject) { - JavaConstant snapshot = ((ImageHeapObject) task).getObject(); + } else if (task instanceof ImageHeapConstant) { + JavaConstant snapshot = ((ImageHeapConstant) task).getHostedObject(); verifyTypeConstant(snapshot, typeConstant, reason); } else { assert task instanceof AnalysisFuture; - AnalysisFuture future = ((AnalysisFuture) task); + AnalysisFuture future = ((AnalysisFuture) task); if (future.isDone()) { - JavaConstant snapshot = future.guardedGet().getObject(); + JavaConstant snapshot = future.guardedGet().getHostedObject(); verifyTypeConstant(snapshot, typeConstant, reason); } else { onTaskForClassConstantNotDone(value, type, reason); @@ -356,7 +369,7 @@ private void onInstanceFieldMismatch(ImageHeapInstance receiver, AnalysisField f analysisModified = true; if (printWarning()) { analysisWarning(reason, "Value mismatch for instance field %s of %s %n snapshot: %s %n new value: %s %n", - field, receiver.getObject(), fieldSnapshot, fieldValue); + field, receiver, fieldSnapshot, fieldValue); } } @@ -364,7 +377,7 @@ private void onInstanceFieldNotComputed(ImageHeapInstance receiver, AnalysisFiel analysisModified = true; if (printWarning()) { analysisWarning(reason, "Snapshot not yet computed for instance field %s of %s %n new value: %s %n", - field, receiver.getObject(), fieldValue); + field, receiver, fieldValue); } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeap.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeap.java index c60737c3775e..f1d0563f5ced 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeap.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeap.java @@ -41,12 +41,12 @@ public class ImageHeap { /** * Map the original object *and* the replaced object to the same snapshot. The value is either a - * not-yet-executed {@link AnalysisFuture} of {@link ImageHeapObject} or its results, an - * {@link ImageHeapObject}. + * not-yet-executed {@link AnalysisFuture} of {@link ImageHeapConstant} or its results, an + * {@link ImageHeapConstant}. */ private final ConcurrentHashMap heapObjects; /** Store a mapping from types to object snapshots. */ - private final Map> typesToObjects; + private final Map> typesToObjects; /* * Note on the idea of merging the heapObjects and typesToObjects maps: @@ -80,21 +80,21 @@ public Object getTask(JavaConstant constant) { } /** Record the future computing the snapshot in the heap. */ - public Object setTask(JavaConstant constant, AnalysisFuture task) { + public Object setTask(JavaConstant constant, AnalysisFuture task) { return heapObjects.putIfAbsent(constant, task); } /** Record the snapshot in the heap. */ - public void setValue(JavaConstant constant, ImageHeapObject value) { + public void setValue(JavaConstant constant, ImageHeapConstant value) { heapObjects.put(constant, value); } - public Set getObjects(AnalysisType type) { + public Set getObjects(AnalysisType type) { return typesToObjects.getOrDefault(type, Collections.emptySet()); } - public boolean add(AnalysisType type, ImageHeapObject heapObj) { - Set objectSet = typesToObjects.computeIfAbsent(type, t -> ConcurrentHashMap.newKeySet()); + public boolean add(AnalysisType type, ImageHeapConstant heapObj) { + Set objectSet = typesToObjects.computeIfAbsent(type, t -> ConcurrentHashMap.newKeySet()); return objectSet.add(heapObj); } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapArray.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapArray.java new file mode 100644 index 000000000000..3a2346ea0b5d --- /dev/null +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapArray.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2022, 2022, 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 com.oracle.graal.pointsto.heap; + +import java.util.function.Consumer; + +import com.oracle.graal.pointsto.ObjectScanner; +import com.oracle.graal.pointsto.meta.AnalysisType; + +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.ResolvedJavaType; + +public final class ImageHeapArray extends ImageHeapConstant { + + /** + * Contains the already scanned array elements. + */ + private final JavaConstant[] arrayElementValues; + + public ImageHeapArray(ResolvedJavaType type, int length) { + this(type, null, new JavaConstant[length], false); + } + + public ImageHeapArray(ResolvedJavaType type, JavaConstant object, int length) { + this(type, object, new JavaConstant[length], false); + } + + ImageHeapArray(ResolvedJavaType type, JavaConstant object, JavaConstant[] arrayElementValues) { + this(type, object, arrayElementValues, false); + } + + private ImageHeapArray(ResolvedJavaType type, JavaConstant object, JavaConstant[] arrayElementValues, boolean compressed) { + super(type, object, compressed); + assert type.isArray(); + this.arrayElementValues = arrayElementValues; + } + + /** + * Return the value of the element at the specified index as computed by + * {@link ImageHeapScanner#onArrayElementReachable(ImageHeapArray, AnalysisType, JavaConstant, int, ObjectScanner.ScanReason, Consumer)}. + */ + public JavaConstant getElement(int idx) { + return arrayElementValues[idx]; + } + + public void setElement(int idx, JavaConstant value) { + arrayElementValues[idx] = value; + } + + public int getLength() { + return arrayElementValues.length; + } + + @Override + public JavaConstant compress() { + assert !compressed; + return new ImageHeapArray(type, hostedObject, arrayElementValues, true); + } + + @Override + public JavaConstant uncompress() { + assert compressed; + return new ImageHeapArray(type, hostedObject, arrayElementValues, false); + } + + @Override + public boolean equals(Object o) { + if (o instanceof ImageHeapArray) { + return super.equals(o) && this.arrayElementValues == ((ImageHeapArray) o).arrayElementValues; + } + return false; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + System.identityHashCode(arrayElementValues); + return result; + } +} diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapConstant.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapConstant.java new file mode 100644 index 000000000000..9ee269e87773 --- /dev/null +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapConstant.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2021, 2021, 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 com.oracle.graal.pointsto.heap; + +import java.util.Objects; + +import org.graalvm.compiler.core.common.type.CompressibleConstant; +import org.graalvm.compiler.core.common.type.TypedConstant; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.MetaAccessProvider; +import jdk.vm.ci.meta.ResolvedJavaType; +import jdk.vm.ci.meta.VMConstant; + +/** + * It represents an object snapshot. It stores the replaced object, i.e., the result of applying + * object replacers on the original hosted object, and the instance field values or array elements + * of this object. The field values are stored as JavaConstant to also encode primitive values. + * ImageHeapObject are created only after an object is processed through the object replacers. + */ +@Platforms(Platform.HOSTED_ONLY.class) +public abstract class ImageHeapConstant implements JavaConstant, TypedConstant, CompressibleConstant, VMConstant { + /** Stores the type of this object. */ + protected ResolvedJavaType type; + /** + * Stores the hosted object, already processed by the object transformers. It is null for + * instances of partially evaluated classes. + */ + protected final JavaConstant hostedObject; + + protected final boolean compressed; + + public ImageHeapConstant(ResolvedJavaType type) { + this(type, null, false); + } + + ImageHeapConstant(ResolvedJavaType type, JavaConstant object) { + this(type, object, false); + } + + ImageHeapConstant(ResolvedJavaType type, JavaConstant object, boolean compressed) { + this.type = type; + this.hostedObject = object; + this.compressed = compressed; + } + + public JavaConstant getHostedObject() { + return hostedObject; + } + + @Override + public JavaKind getJavaKind() { + return JavaKind.Object; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + public boolean isDefaultForKind() { + return false; + } + + @Override + public ResolvedJavaType getType(MetaAccessProvider provider) { + return type; + } + + public void setType(ResolvedJavaType type) { + this.type = type; + } + + @Override + public Object asBoxedPrimitive() { + return null; + } + + @Override + public int asInt() { + return 0; + } + + @Override + public boolean asBoolean() { + return false; + } + + @Override + public long asLong() { + return 0; + } + + @Override + public float asFloat() { + return 0; + } + + @Override + public double asDouble() { + return 0; + } + + @Override + public boolean isCompressed() { + return compressed; + } + + @Override + public String toValueString() { + return type.getName(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof ImageHeapConstant) { + ImageHeapConstant other = (ImageHeapConstant) o; + /* + * Object identity doesn't take into account the compressed flag. This is done to match + * the previous behavior where the raw object was extracted and used as a key when + * constructing the image heap map. + */ + return Objects.equals(this.type, other.type) && Objects.equals(this.hostedObject, other.hostedObject); + } + return false; + } + + @Override + public int hashCode() { + return hostedObject != null ? hostedObject.hashCode() : 0; + } +} diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapObject.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapInstance.java similarity index 60% rename from substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapObject.java rename to substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapInstance.java index 37f338afd7ca..8e9e3c421737 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapObject.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapInstance.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2022, 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 @@ -30,47 +30,10 @@ import com.oracle.graal.pointsto.ObjectScanner; import com.oracle.graal.pointsto.meta.AnalysisField; -import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.util.AnalysisFuture; import jdk.vm.ci.meta.JavaConstant; - -/** - * It represents an object snapshot. It stores the replaced object, i.e., the result of applying - * object replacers on the original object, and the instance field values or array elements of this - * object. The field values are stored as JavaConstant to also encode primitive values. - * ImageHeapObject are created only after an object is processed through the object replacers. - */ -public class ImageHeapObject { - /** - * Store the object, already processed by the object transformers. - */ - private final JavaConstant object; - - ImageHeapObject(JavaConstant object) { - this.object = object; - } - - public JavaConstant getObject() { - return object; - } - - /* Equals and hashCode just compare the replaced constant. */ - - @Override - public boolean equals(Object o) { - if (o instanceof ImageHeapObject) { - ImageHeapObject other = (ImageHeapObject) o; - return this.object.equals(other.object); - } - return false; - } - - @Override - public int hashCode() { - return object.hashCode(); - } -} +import jdk.vm.ci.meta.ResolvedJavaType; /** * This class implements an instance object snapshot. It stores the field values in an Object[], @@ -78,24 +41,36 @@ public int hashCode() { *
  • a not-yet-executed {@link AnalysisFuture} of {@link JavaConstant} which captures the * original, hosted field value and contains logic to transform and replace this value
  • , or *
  • the result of executing the future, a replaced {@link JavaConstant}, i.e., the snapshot.
  • - * + *

    * The future task is executed when the field is marked as read. Moreover, the future is * self-replacing, i.e., when it is executed it also calls * {@link #setFieldValue(AnalysisField, JavaConstant)} and updates the corresponding entry. */ -final class ImageHeapInstance extends ImageHeapObject { +public final class ImageHeapInstance extends ImageHeapConstant { private static final VarHandle arrayHandle = MethodHandles.arrayElementVarHandle(Object[].class); /** * Stores either an {@link AnalysisFuture} of {@link JavaConstant} or its result, a - * {@link JavaConstant}. + * {@link JavaConstant}, indexed by {@link AnalysisField#getPosition()}. */ - private final Object[] values; + private final Object[] fieldValues; + + public ImageHeapInstance(ResolvedJavaType type) { + this(type, null, type.getInstanceFields(true).length); + } ImageHeapInstance(JavaConstant object, int length) { - super(object); - this.values = new Object[length]; + this(null, object, length); + } + + ImageHeapInstance(ResolvedJavaType type, JavaConstant object, int length) { + this(type, object, new Object[length], false); + } + + private ImageHeapInstance(ResolvedJavaType type, JavaConstant object, Object[] fieldValues, boolean compressed) { + super(type, object, compressed); + this.fieldValues = fieldValues; } /** @@ -103,7 +78,7 @@ final class ImageHeapInstance extends ImageHeapObject { * is marked as read. */ public void setFieldTask(AnalysisField field, AnalysisFuture task) { - arrayHandle.setVolatile(this.values, field.getPosition(), task); + arrayHandle.setVolatile(this.fieldValues, field.getPosition(), task); } /** @@ -112,44 +87,53 @@ public void setFieldTask(AnalysisField field, AnalysisFuture task) * and replaced. */ public void setFieldValue(AnalysisField field, JavaConstant value) { - arrayHandle.setVolatile(this.values, field.getPosition(), value); + arrayHandle.setVolatile(this.fieldValues, field.getPosition(), value); } /** * Return either a task for transforming the field value, effectively a future for - * {@link ImageHeapScanner#onFieldValueReachable(AnalysisField, JavaConstant, JavaConstant, ObjectScanner.ScanReason, Consumer)}, + * {@link ImageHeapScanner#onFieldValueReachable(AnalysisField, ImageHeapInstance, JavaConstant, ObjectScanner.ScanReason, Consumer)}, * or the result of executing the task, i.e., a {@link JavaConstant}. */ public Object getFieldValue(AnalysisField field) { - return arrayHandle.getVolatile(this.values, field.getPosition()); + return arrayHandle.getVolatile(this.fieldValues, field.getPosition()); } -} - -final class ImageHeapArray extends ImageHeapObject { /** - * Contains the already scanned array elements. + * Returns the field value, i.e., a {@link JavaConstant}. If the value is not yet materialized + * then the future is executed on the current thread. */ - private final JavaConstant[] arrayElementValues; + @SuppressWarnings({"unchecked", "rawtypes"}) + public JavaConstant readFieldValue(AnalysisField field) { + Object value = getFieldValue(field); + return value instanceof JavaConstant ? (JavaConstant) value : ((AnalysisFuture) value).ensureDone(); + } - ImageHeapArray(JavaConstant object, JavaConstant[] arrayElementValues) { - super(object); - this.arrayElementValues = arrayElementValues; + @Override + public JavaConstant compress() { + assert !compressed; + return new ImageHeapInstance(type, hostedObject, fieldValues, true); } - /** - * Return the value of the element at the specified index as computed by - * {@link ImageHeapScanner#onArrayElementReachable(JavaConstant, AnalysisType, JavaConstant, int, ObjectScanner.ScanReason)}. - */ - public JavaConstant getElement(int idx) { - return arrayElementValues[idx]; + @Override + public JavaConstant uncompress() { + assert compressed; + return new ImageHeapInstance(type, hostedObject, fieldValues, false); } - public void setElement(int idx, JavaConstant value) { - arrayElementValues[idx] = value; + @Override + public boolean equals(Object o) { + if (o instanceof ImageHeapInstance) { + return super.equals(o) && this.fieldValues == ((ImageHeapInstance) o).fieldValues; + } + return false; } - public int getLength() { - return arrayElementValues.length; + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + System.identityHashCode(fieldValues); + return result; } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java index dcd50af414f2..161a4166ee7e 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java @@ -91,7 +91,7 @@ public abstract class ImageHeapScanner { protected ObjectScanningObserver scanningObserver; /** Marker object installed when encountering scanning issues like illegal objects. */ - private static final ImageHeapObject NULL_IMAGE_HEAP_OBJECT = new ImageHeapInstance(JavaConstant.NULL_POINTER, 0); + private static final ImageHeapConstant NULL_IMAGE_HEAP_OBJECT = new ImageHeapInstance(JavaConstant.NULL_POINTER, 0); public ImageHeapScanner(BigBang bb, ImageHeap heap, AnalysisMetaAccess aMetaAccess, SnippetReflectionProvider aSnippetReflection, ConstantReflectionProvider aConstantReflection, ObjectScanningObserver aScanningObserver) { @@ -131,8 +131,8 @@ public void onFieldRead(AnalysisField field) { private void onInstanceFieldRead(AnalysisField field, AnalysisType type) { for (AnalysisType subtype : type.getSubTypes()) { - for (ImageHeapObject imageHeapObject : imageHeap.getObjects(subtype)) { - snapshotFieldValue(field, ((ImageHeapInstance) imageHeapObject).getFieldValue(field)); + for (ImageHeapConstant imageHeapConstant : imageHeap.getObjects(subtype)) { + snapshotFieldValue(field, ((ImageHeapInstance) imageHeapConstant).getFieldValue(field)); } /* Subtypes include this type itself. */ if (!subtype.equals(type)) { @@ -176,49 +176,139 @@ void markTypeInstantiated(AnalysisType type) { JavaConstant markConstantReachable(JavaConstant constant, ScanReason reason, Consumer onAnalysisModified) { if (isNonNullObjectConstant(constant)) { - return getOrCreateConstantReachableTask(constant, reason, onAnalysisModified).getObject(); + return getOrCreateConstantReachableTask(constant, reason, onAnalysisModified); } - return constant; } - protected ImageHeapObject toImageHeapObject(JavaConstant constant) { + public ImageHeapConstant toImageHeapObject(JavaConstant constant) { return toImageHeapObject(constant, OtherReason.RESCAN, null); } - protected ImageHeapObject toImageHeapObject(JavaConstant constant, ScanReason reason, Consumer onAnalysisModified) { + protected ImageHeapConstant toImageHeapObject(JavaConstant constant, ScanReason reason, Consumer onAnalysisModified) { assert constant != null && isNonNullObjectConstant(constant); return getOrCreateConstantReachableTask(constant, reason, onAnalysisModified); } @SuppressWarnings({"unchecked", "rawtypes"}) - protected ImageHeapObject getOrCreateConstantReachableTask(JavaConstant javaConstant, ScanReason reason, Consumer onAnalysisModified) { + protected ImageHeapConstant getOrCreateConstantReachableTask(JavaConstant javaConstant, ScanReason reason, Consumer onAnalysisModified) { ScanReason nonNullReason = Objects.requireNonNull(reason); Object existingTask = imageHeap.getTask(javaConstant); if (existingTask == null) { if (universe.sealed()) { throw AnalysisError.shouldNotReachHere("Universe is sealed. New constant reachable: " + javaConstant.toValueString()); } - AnalysisFuture newTask = new AnalysisFuture<>(() -> { - ImageHeapObject imageHeapObject = createImageHeapObject(javaConstant, nonNullReason, onAnalysisModified); - /* When the image heap object is created replace the future in the map. */ - imageHeap.setValue(javaConstant, imageHeapObject); - return imageHeapObject; - }); - existingTask = imageHeap.setTask(javaConstant, newTask); - if (existingTask == null) { - /* - * Immediately schedule the new task. There is no need to have not-yet-reachable - * ImageHeapObject. - */ - postTask(newTask); - return newTask.ensureDone(); + if (javaConstant instanceof ImageHeapConstant) { + /* This is already an ImageHeapObject. */ + ImageHeapConstant imageHeapConstant = (ImageHeapConstant) javaConstant; + imageHeap.setValue(javaConstant, imageHeapConstant); + imageHeap.add((AnalysisType) imageHeapConstant.getType(metaAccess), imageHeapConstant); + /* Ensure all the referenced objects are scanned. */ + scanImageHeapObject(imageHeapConstant, nonNullReason, onAnalysisModified); + existingTask = javaConstant; + } else { + AnalysisFuture newTask = new AnalysisFuture<>(() -> { + ImageHeapConstant imageHeapConstant = createImageHeapObject(javaConstant, nonNullReason, onAnalysisModified); + /* When the image heap object is created replace the future in the map. */ + imageHeap.setValue(javaConstant, imageHeapConstant); + return imageHeapConstant; + }); + existingTask = imageHeap.setTask(javaConstant, newTask); + if (existingTask == null) { + /* + * Immediately schedule the new task. There is no need to have not-yet-reachable + * ImageHeapObject. + */ + postTask(newTask); + return newTask.ensureDone(); + } } } - return existingTask instanceof ImageHeapObject ? (ImageHeapObject) existingTask : ((AnalysisFuture) existingTask).ensureDone(); + return existingTask instanceof ImageHeapConstant ? (ImageHeapConstant) existingTask : ((AnalysisFuture) existingTask).ensureDone(); } - protected ImageHeapObject createImageHeapObject(JavaConstant constant, ScanReason reason, Consumer onAnalysisModified) { + /** + * Scan injected heap objects, i.e., heap objects that do not originate from scanning underlying + * hosted constants. + */ + protected void scanImageHeapObject(ImageHeapConstant object, ScanReason reason, Consumer onAnalysisModified) { + assert object.getJavaKind() == JavaKind.Object && !object.isNull(); + + /* + * Access the constant type after the replacement. Some constants may have types that should + * not be reachable at run time and thus are replaced. + */ + AnalysisType type = (AnalysisType) object.getType(metaAccess); + + if (type.isArray()) { + if (!type.getComponentType().isPrimitive()) { + ImageHeapArray array = (ImageHeapArray) object; + ScanReason arrayReason = new ArrayScan(type, object, reason); + for (int idx = 0; idx < array.getLength(); idx++) { + final JavaConstant elementValue = array.getElement(idx); + onArrayElementReachable(array, type, elementValue, idx, arrayReason, onAnalysisModified); + } + } + markTypeInstantiated(type); + } else { + ImageHeapInstance instance = (ImageHeapInstance) object; + /* We are about to query the type's fields, the type must be marked as reachable. */ + markTypeInstantiated(type); + for (AnalysisField field : type.getInstanceFields(true)) { + final ScanReason fieldReason = new FieldScan(field, object, reason); + if (field.isRead() && isValueAvailable(field)) { + final JavaConstant fieldValue = instance.readFieldValue(field); + /* If field is read scan its value immediately. */ + onFieldValueReachable(field, instance, fieldValue, fieldReason, onAnalysisModified); + } else { + /* + * If field is not read replace the constant value a future that will scan it + * when the field is marked as reachable. + */ + final JavaConstant originalFieldValue = (JavaConstant) instance.getFieldValue(field); + instance.setFieldTask(field, new AnalysisFuture<>(() -> { + /* + * After scanning a field of an injected ImageHeapInstance that references a + * regular JavaConstant object should the field value be replaced with the + * snapshot version, i.e., an ImageHeapInstance, or should it keep pointing + * to the original value? In the long term it should be replaced, but that's + * not yet generally possible. First ImageHeapInstance needs to have + * complete support for all use cases, it needs to be a complete replacement + * for JavaConstant. For example, it needs to be able to efficiently + * represent string values and be able to extract the String object. + * + * So for now we just reinstall the original JavaConstant value when the + * future is completed. + * + * More specifically, the long term plan is that after scanning + * instance.field will refer to the `scannedFieldValue`, so any future read + * of `instance.field` will return an ImageHeapInstance. Moreover, + * `instance` is also reached when scanning from roots for verification. In + * that case, if we do set the field to the scannedFieldValue returned by + * `onFieldValueReachable()`, i.e., an ImageHeapInstance, then we also need + * to register a mapping in the heap for the snapshot: scannedFieldValue -> + * scannedFieldValue. Otherwise, we only have the originalFieldValue -> + * scannedFieldValue mapping and a lookup of scannedFieldValue will fail + * during verification. See the snippet below for details. + */ + // @formatter:off + // JavaConstant scannedFieldValue = onFieldValueReachable(field, instance, fieldValue, fieldReason, onAnalysisModified); + // instance.setFieldValue(field, scannedFieldValue); + // imageHeap.setValue(value, (ImageHeapObject) scannedFieldValue); + // @formatter:on + + onFieldValueReachable(field, instance, originalFieldValue, fieldReason, onAnalysisModified); + /* Re-install the original constant value. */ + instance.setFieldValue(field, originalFieldValue); + + return originalFieldValue; + })); + } + } + } + } + + protected ImageHeapConstant createImageHeapObject(JavaConstant constant, ScanReason reason, Consumer onAnalysisModified) { assert constant.getJavaKind() == JavaKind.Object && !constant.isNull(); Optional replaced = maybeReplace(constant, reason); @@ -241,23 +331,24 @@ protected ImageHeapObject createImageHeapObject(JavaConstant constant, ScanReaso */ AnalysisType type = metaAccess.lookupJavaType(constant); - ImageHeapObject newImageHeapObject; + ImageHeapConstant newImageHeapConstant; if (type.isArray()) { if (type.getComponentType().isPrimitive()) { /* * The shadow heap is only used for points-to analysis currently, we don't need to * track individual elements for primitive arrays. */ - newImageHeapObject = new ImageHeapArray(constant, emptyConstantArray); + newImageHeapConstant = new ImageHeapArray(type, constant, emptyConstantArray); } else { int length = constantReflection.readArrayLength(constant); - JavaConstant[] arrayElements = new JavaConstant[length]; + newImageHeapConstant = new ImageHeapArray(type, constant, length); ScanReason arrayReason = new ArrayScan(type, constant, reason); + ImageHeapArray array = (ImageHeapArray) newImageHeapConstant; for (int idx = 0; idx < length; idx++) { final JavaConstant rawElementValue = constantReflection.readArrayElement(constant, idx); - arrayElements[idx] = onArrayElementReachable(constant, type, rawElementValue, idx, arrayReason, onAnalysisModified); + JavaConstant arrayElement = onArrayElementReachable(array, type, rawElementValue, idx, arrayReason, onAnalysisModified); + array.setElement(idx, arrayElement); } - newImageHeapObject = new ImageHeapArray(constant, arrayElements); } markTypeInstantiated(type); } else { @@ -269,7 +360,7 @@ protected ImageHeapObject createImageHeapObject(JavaConstant constant, ScanReaso /* We are about to query the type's fields, the type must be marked as reachable. */ markTypeInstantiated(type); AnalysisField[] instanceFields = type.getInstanceFields(true); - newImageHeapObject = new ImageHeapInstance(constant, instanceFields.length); + newImageHeapConstant = new ImageHeapInstance(type, constant, instanceFields.length); for (AnalysisField field : instanceFields) { ScanReason fieldReason = new FieldScan(field, constant, reason); ValueSupplier rawFieldValue; @@ -279,9 +370,9 @@ protected ImageHeapObject createImageHeapObject(JavaConstant constant, ScanReaso /* Ignore missing type errors. */ continue; } - ImageHeapInstance finalObject = (ImageHeapInstance) newImageHeapObject; + ImageHeapInstance finalObject = (ImageHeapInstance) newImageHeapConstant; finalObject.setFieldTask(field, new AnalysisFuture<>(() -> { - JavaConstant value = onFieldValueReachable(field, constant, rawFieldValue, fieldReason, onAnalysisModified); + JavaConstant value = onFieldValueReachable(field, finalObject, rawFieldValue, fieldReason, onAnalysisModified); finalObject.setFieldValue(field, value); return value; })); @@ -291,15 +382,15 @@ protected ImageHeapObject createImageHeapObject(JavaConstant constant, ScanReaso /* * Following all the array elements and reachable field values can be done asynchronously. */ - postTask(() -> onObjectReachable(newImageHeapObject)); - return newImageHeapObject; + postTask(() -> onObjectReachable(newImageHeapConstant)); + return newImageHeapConstant; } private Optional maybeReplace(JavaConstant constant, ScanReason reason) { Object unwrapped = unwrapObject(constant); if (unwrapped == null) { throw GraalError.shouldNotReachHere(formatReason("Could not unwrap constant", reason)); - } else if (unwrapped instanceof ImageHeapObject) { + } else if (unwrapped instanceof ImageHeapConstant) { throw GraalError.shouldNotReachHere(formatReason("Double wrapping of constant. Most likely, the reachability analysis code itself is seen as reachable.", reason)); } @@ -332,11 +423,11 @@ JavaConstant onFieldValueReachable(AnalysisField field, JavaConstant fieldValue, return onFieldValueReachable(field, null, ValueSupplier.eagerValue(fieldValue), reason, onAnalysisModified); } - JavaConstant onFieldValueReachable(AnalysisField field, JavaConstant receiver, JavaConstant fieldValue, ScanReason reason, Consumer onAnalysisModified) { + JavaConstant onFieldValueReachable(AnalysisField field, ImageHeapInstance receiver, JavaConstant fieldValue, ScanReason reason, Consumer onAnalysisModified) { return onFieldValueReachable(field, receiver, ValueSupplier.eagerValue(fieldValue), reason, onAnalysisModified); } - JavaConstant onFieldValueReachable(AnalysisField field, JavaConstant receiver, ValueSupplier rawValue, ScanReason reason, Consumer onAnalysisModified) { + JavaConstant onFieldValueReachable(AnalysisField field, ImageHeapInstance receiver, ValueSupplier rawValue, ScanReason reason, Consumer onAnalysisModified) { AnalysisError.guarantee(field.isReachable(), "Field value is only reachable when field is reachable " + field.format("%H.%n")); /* @@ -363,13 +454,12 @@ JavaConstant onFieldValueReachable(AnalysisField field, JavaConstant receiver, V onAnalysisModified.accept(reason); } } - /* Return the transformed value, but NOT the image heap object. */ return fieldValue; } private boolean notifyAnalysis(AnalysisField field, JavaConstant receiver, JavaConstant fieldValue, ScanReason reason) { boolean analysisModified = false; - if (fieldValue.getJavaKind() == JavaKind.Object && hostVM.isRelocatedPointer(asObject(fieldValue))) { + if (fieldValue.getJavaKind() == JavaKind.Object && hostVM.isRelocatedPointer(metaAccess, fieldValue)) { analysisModified = scanningObserver.forRelocatedPointerFieldValue(receiver, field, fieldValue, reason); } else if (fieldValue.isNull()) { analysisModified = scanningObserver.forNullFieldValue(receiver, field, reason); @@ -384,11 +474,7 @@ protected JavaConstant transformFieldValue(AnalysisField field, JavaConstant rec return originalValueConstant; } - protected JavaConstant onArrayElementReachable(JavaConstant array, AnalysisType arrayType, JavaConstant rawElementValue, int elementIndex, ScanReason reason) { - return onArrayElementReachable(array, arrayType, rawElementValue, elementIndex, reason, null); - } - - protected JavaConstant onArrayElementReachable(JavaConstant array, AnalysisType arrayType, JavaConstant rawElementValue, int elementIndex, ScanReason reason, + protected JavaConstant onArrayElementReachable(ImageHeapArray array, AnalysisType arrayType, JavaConstant rawElementValue, int elementIndex, ScanReason reason, Consumer onAnalysisModified) { JavaConstant elementValue = markConstantReachable(rawElementValue, reason, onAnalysisModified); if (scanningObserver != null && arrayType.getComponentType().getJavaKind() == JavaKind.Object) { @@ -406,8 +492,7 @@ private boolean isNonNullObjectConstant(JavaConstant constant) { } private boolean isWordType(JavaConstant rawElementValue) { - Object obj = snippetReflection.asObject(Object.class, rawElementValue); - return obj instanceof WordBase; + return metaAccess.isInstanceOf(rawElementValue, WordBase.class); } private boolean notifyAnalysis(JavaConstant array, AnalysisType arrayType, JavaConstant elementValue, int elementIndex, ScanReason reason) { @@ -424,14 +509,14 @@ private boolean notifyAnalysis(JavaConstant array, AnalysisType arrayType, JavaC return analysisModified; } - protected void onObjectReachable(ImageHeapObject imageHeapObject) { - AnalysisType objectType = metaAccess.lookupJavaType(imageHeapObject.getObject()); - imageHeap.add(objectType, imageHeapObject); + protected void onObjectReachable(ImageHeapConstant imageHeapConstant) { + AnalysisType objectType = metaAccess.lookupJavaType(imageHeapConstant); + imageHeap.add(objectType, imageHeapConstant); markTypeInstantiated(objectType); - if (imageHeapObject instanceof ImageHeapInstance) { - ImageHeapInstance imageHeapInstance = (ImageHeapInstance) imageHeapObject; + if (imageHeapConstant instanceof ImageHeapInstance) { + ImageHeapInstance imageHeapInstance = (ImageHeapInstance) imageHeapConstant; for (AnalysisField field : objectType.getInstanceFields(true)) { if (field.isRead() && isValueAvailable(field)) { snapshotFieldValue(field, imageHeapInstance.getFieldValue(field)); @@ -502,8 +587,7 @@ public void rescanRoot(Field reflectionField) { TypeData typeData = field.getDeclaringClass().getOrComputeData(); AnalysisFuture fieldTask = patchStaticField(typeData, field, fieldValue, OtherReason.RESCAN, null); if (field.isRead() || field.isFolded()) { - Object root = asObject(fieldTask.ensureDone()); - rescanCollectionElements(root); + rescanCollectionElements(fieldTask.ensureDone()); } } }); @@ -532,7 +616,7 @@ public void rescanField(Object receiver, Field reflectionField) { ImageHeapInstance receiverObject = (ImageHeapInstance) toImageHeapObject(receiverConstant); AnalysisFuture fieldTask = patchInstanceField(receiverObject, field, fieldValue, OtherReason.RESCAN, null); if (field.isRead() || field.isFolded()) { - rescanCollectionElements(asObject(fieldTask.ensureDone())); + rescanCollectionElements(fieldTask.ensureDone()); } } } @@ -552,7 +636,7 @@ protected AnalysisFuture patchStaticField(TypeData typeData, Analy protected AnalysisFuture patchInstanceField(ImageHeapInstance receiverObject, AnalysisField field, JavaConstant fieldValue, ScanReason reason, Consumer onAnalysisModified) { AnalysisFuture task = new AnalysisFuture<>(() -> { - JavaConstant value = onFieldValueReachable(field, receiverObject.getObject(), fieldValue, reason, onAnalysisModified); + JavaConstant value = onFieldValueReachable(field, receiverObject, fieldValue, reason, onAnalysisModified); receiverObject.setFieldValue(field, value); return value; }); @@ -584,6 +668,12 @@ public void rescanObject(Object object, ScanReason reason) { }); } + private void rescanCollectionElements(JavaConstant constant) { + if (isNonNullObjectConstant(constant)) { + rescanCollectionElements(asObject(((ImageHeapConstant) constant).getHostedObject())); + } + } + private void rescanCollectionElements(Object object) { if (object instanceof Object[]) { Object[] array = (Object[]) object; @@ -617,17 +707,7 @@ void doScan(JavaConstant constant) { } void doScan(JavaConstant constant, ScanReason reason) { - if (isNonNullObjectConstant(constant)) { - getOrCreateConstantReachableTask(constant, reason, null); - } - } - - protected AnalysisType analysisType(Object constant) { - return metaAccess.lookupJavaType(constant.getClass()); - } - - protected AnalysisType constantType(JavaConstant constant) { - return metaAccess.lookupJavaType(constant); + markConstantReachable(constant, reason, null); } protected Object asObject(JavaConstant constant) { diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/UniverseMetaAccess.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/UniverseMetaAccess.java index 0c6d310d229c..c651b13a41c7 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/UniverseMetaAccess.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/UniverseMetaAccess.java @@ -31,6 +31,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; +import org.graalvm.compiler.core.common.type.TypedConstant; + import com.oracle.graal.pointsto.meta.AnalysisUniverse; import jdk.vm.ci.meta.DeoptimizationAction; @@ -74,7 +76,10 @@ public ResolvedJavaType lookupJavaType(JavaConstant constant) { if (constant.getJavaKind() != JavaKind.Object || constant.isNull()) { return null; } - return lookupJavaType(universe.getSnippetReflection().asObject(Object.class, constant).getClass()); + if (constant instanceof TypedConstant) { + return ((TypedConstant) constant).getType(this); + } + return universe.lookup(wrapped.lookupJavaType(constant)); } private final ConcurrentHashMap, ResolvedJavaType> typeCache = new ConcurrentHashMap<>(AnalysisUniverse.ESTIMATED_NUMBER_OF_TYPES); @@ -84,6 +89,20 @@ public ResolvedJavaType lookupJavaType(Class clazz) { return typeCache.computeIfAbsent(clazz, computeJavaType); } + public boolean isInstanceOf(JavaConstant constant, Class clazz) { + if (constant == null || constant.isNull()) { + return false; + } + return lookupJavaType(clazz).isAssignableFrom(lookupJavaType(constant)); + } + + public boolean isInstanceOf(JavaConstant constant, ResolvedJavaType type) { + if (constant == null || constant.isNull()) { + return false; + } + return type.isAssignableFrom(lookupJavaType(constant)); + } + protected ResolvedJavaType getTypeCacheEntry(Class clazz) { return typeCache.get(clazz); } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java index 6166a20f3e37..396daafea7d6 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java @@ -47,6 +47,7 @@ import com.oracle.graal.pointsto.api.HostVM; import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException; import com.oracle.graal.pointsto.heap.HeapSnapshotVerifier; +import com.oracle.graal.pointsto.heap.ImageHeapConstant; import com.oracle.graal.pointsto.heap.ImageHeapScanner; import com.oracle.graal.pointsto.infrastructure.AnalysisConstantPool; import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor; @@ -497,7 +498,18 @@ public JavaConstant lookup(JavaConstant constant) { if (constant == null) { return null; } else if (constant.getJavaKind().isObject() && !constant.isNull()) { - return snippetReflection.forObject(originalSnippetReflection.asObject(Object.class, constant)); + Object original = originalSnippetReflection.asObject(Object.class, constant); + if (original instanceof ImageHeapConstant) { + /* + * The value is an ImageHeapObject, i.e., it already has a build time + * representation, so there is no need to re-wrap it. The value likely comes from + * reading a field of a normal object that is referencing a simulated object. The + * originalConstantReflection provider is not aware of simulated constants, and it + * always wraps them into a HotSpotObjectConstant when reading fields. + */ + return (JavaConstant) original; + } + return snippetReflection.forObject(original); } else { return constant; } diff --git a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityObjectScanner.java b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityObjectScanner.java index 58247fb187a5..a2a53c6fc58b 100644 --- a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityObjectScanner.java +++ b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityObjectScanner.java @@ -30,6 +30,7 @@ import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisType; + import jdk.vm.ci.meta.JavaConstant; /** @@ -97,11 +98,6 @@ public void forScannedConstant(JavaConstant scannedValue, ObjectScanner.ScanReas } private AnalysisType constantType(JavaConstant constant) { - return access.lookupJavaType(constantAsObject(constant).getClass()); - } - - private Object constantAsObject(JavaConstant constant) { - return bb.getSnippetReflectionProvider().asObject(Object.class, constant); + return access.lookupJavaType(constant); } - } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractImageHeapLayouter.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractImageHeapLayouter.java index f2d7ceb6f7f6..08c92e726628 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractImageHeapLayouter.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractImageHeapLayouter.java @@ -186,12 +186,12 @@ private T choosePartition(@SuppressWarnings("unused") ImageHeapObject info, bool return getReadOnlyRelocatable(); } if (info.getSize() >= getHugeObjectThreshold()) { - VMError.guarantee(!(info.getObject() instanceof DynamicHub), "Class metadata (dynamic hubs) cannot be huge objects"); + VMError.guarantee(info.getObjectClass() != DynamicHub.class, "Class metadata (dynamic hubs) cannot be huge objects"); return getReadOnlyHuge(); } return hasReferences ? getReadOnlyReference() : getReadOnlyPrimitive(); } else { - assert !(info.getObject() instanceof DynamicHub) : "Class metadata (dynamic hubs) cannot be writable"; + assert info.getObjectClass() != DynamicHub.class : "Class metadata (dynamic hubs) cannot be writable"; if (info.getSize() >= getHugeObjectThreshold()) { return getWritableHuge(); } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ChunkedImageHeapPartition.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ChunkedImageHeapPartition.java index 664b82e0a2e7..45e387f62a18 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ChunkedImageHeapPartition.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ChunkedImageHeapPartition.java @@ -36,6 +36,7 @@ import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.genscavenge.AbstractImageHeapLayouter.AbstractImageHeapPartition; import com.oracle.svm.core.image.ImageHeapObject; +import com.oracle.svm.core.meta.SubstrateObjectConstant; /** * An unstructured image heap partition that just contains a linear sequence of image heap objects. @@ -145,13 +146,28 @@ private static NavigableMap> createSortedObjectsMap private void appendAllocatedObject(ImageHeapObject info, long allocationOffset) { if (firstObject == null) { - firstObject = info.getObject(); + firstObject = extractObject(info); } assert info.getPartition() == this; long offsetInPartition = allocationOffset - startOffset; assert ConfigurationValues.getObjectLayout().isAligned(offsetInPartition) : "start: " + offsetInPartition + " must be aligned."; info.setOffsetInPartition(offsetInPartition); - lastObject = info.getObject(); + lastObject = extractObject(info); + } + + private static Object extractObject(ImageHeapObject info) { + if (info.getConstant() instanceof SubstrateObjectConstant) { + return info.getObject(); + } else { + /* + * The info wraps an ImageHeapObject, i.e., a build time representation of an object + * that is not backed by a raw hosted object. We set the partition limit to the actual + * constant. The constant reflection provider knows that this is a build time value, and + * it will not wrap it in a JavaConstant when reading it. This case is not different + * from normal objects referencing simulated objects. + */ + return info.getConstant(); + } } @Override diff --git a/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64Backend.java b/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64Backend.java index c6db68d951f7..d21fa1028761 100755 --- a/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64Backend.java +++ b/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64Backend.java @@ -60,6 +60,7 @@ import org.graalvm.compiler.core.common.memory.MemoryOrderMode; import org.graalvm.compiler.core.common.spi.ForeignCallLinkage; import org.graalvm.compiler.core.common.spi.LIRKindTool; +import org.graalvm.compiler.core.common.type.CompressibleConstant; import org.graalvm.compiler.core.gen.DebugInfoBuilder; import org.graalvm.compiler.core.gen.LIRGenerationProvider; import org.graalvm.compiler.core.gen.NodeLIRBuilder; @@ -1031,8 +1032,8 @@ private static JavaConstant getZeroConstant(AllocatableValue dst) { public AArch64LIRInstruction createLoad(AllocatableValue dst, Constant src) { if (CompressedNullConstant.COMPRESSED_NULL.equals(src)) { return super.createLoad(dst, getZeroConstant(dst)); - } else if (src instanceof SubstrateObjectConstant) { - return loadObjectConstant(dst, (SubstrateObjectConstant) src); + } else if (src instanceof CompressibleConstant) { + return loadObjectConstant(dst, (CompressibleConstant) src); } else if (src instanceof SubstrateMethodPointerConstant) { return new AArch64LoadMethodPointerConstantOp(dst, (SubstrateMethodPointerConstant) src); } @@ -1043,15 +1044,15 @@ public AArch64LIRInstruction createLoad(AllocatableValue dst, Constant src) { public LIRInstruction createStackLoad(AllocatableValue dst, Constant src) { if (CompressedNullConstant.COMPRESSED_NULL.equals(src)) { return super.createStackLoad(dst, getZeroConstant(dst)); - } else if (src instanceof SubstrateObjectConstant) { - return loadObjectConstant(dst, (SubstrateObjectConstant) src); + } else if (src instanceof CompressibleConstant) { + return loadObjectConstant(dst, (CompressibleConstant) src); } else if (src instanceof SubstrateMethodPointerConstant) { return new AArch64LoadMethodPointerConstantOp(dst, (SubstrateMethodPointerConstant) src); } return super.createStackLoad(dst, src); } - protected AArch64LIRInstruction loadObjectConstant(AllocatableValue dst, SubstrateObjectConstant constant) { + protected AArch64LIRInstruction loadObjectConstant(AllocatableValue dst, CompressibleConstant constant) { if (ReferenceAccess.singleton().haveCompressedReferences()) { RegisterValue heapBase = ReservedRegisters.singleton().getHeapBaseRegister().asValue(); return new LoadCompressedObjectConstantOp(dst, constant, heapBase, getCompressEncoding(), lirKindTool); @@ -1073,14 +1074,14 @@ protected AArch64LIRInstruction loadObjectConstant(AllocatableValue dst, Substra public static final class LoadCompressedObjectConstantOp extends PointerCompressionOp implements LoadConstantOp { public static final LIRInstructionClass TYPE = LIRInstructionClass.create(LoadCompressedObjectConstantOp.class); - static JavaConstant asCompressed(SubstrateObjectConstant constant) { + static Constant asCompressed(CompressibleConstant constant) { // We only want compressed references in code return constant.isCompressed() ? constant : constant.compress(); } - private final SubstrateObjectConstant constant; + private final CompressibleConstant constant; - public LoadCompressedObjectConstantOp(AllocatableValue result, SubstrateObjectConstant constant, AllocatableValue baseRegister, CompressEncoding encoding, LIRKindTool lirKindTool) { + public LoadCompressedObjectConstantOp(AllocatableValue result, CompressibleConstant constant, AllocatableValue baseRegister, CompressEncoding encoding, LIRKindTool lirKindTool) { super(TYPE, result, new ConstantValue(lirKindTool.getNarrowOopKind(), asCompressed(constant)), baseRegister, encoding, true, lirKindTool); this.constant = constant; } diff --git a/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/SubstrateAMD64Backend.java b/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/SubstrateAMD64Backend.java index c3aa1f4e87b4..30cce51b2420 100644 --- a/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/SubstrateAMD64Backend.java +++ b/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/SubstrateAMD64Backend.java @@ -64,6 +64,7 @@ import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor; import org.graalvm.compiler.core.common.spi.ForeignCallLinkage; import org.graalvm.compiler.core.common.spi.LIRKindTool; +import org.graalvm.compiler.core.common.type.CompressibleConstant; import org.graalvm.compiler.core.gen.DebugInfoBuilder; import org.graalvm.compiler.core.gen.LIRGenerationProvider; import org.graalvm.compiler.core.gen.NodeLIRBuilder; @@ -400,7 +401,7 @@ public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) { } } - public static Object addressDisplacementAnnotation(SubstrateObjectConstant constant) { + public static Object addressDisplacementAnnotation(JavaConstant constant) { if (SubstrateUtil.HOSTED) { /* * AOT compilation during image generation happens before the image heap objects are @@ -414,7 +415,7 @@ public static Object addressDisplacementAnnotation(SubstrateObjectConstant const } } - public static int addressDisplacement(SubstrateObjectConstant constant, SharedConstantReflectionProvider constantReflection) { + public static int addressDisplacement(JavaConstant constant, SharedConstantReflectionProvider constantReflection) { if (SubstrateUtil.HOSTED) { return 0; } else { @@ -1228,8 +1229,8 @@ private static JavaConstant getZeroConstant(AllocatableValue dst) { public AMD64LIRInstruction createLoad(AllocatableValue dst, Constant src) { if (CompressedNullConstant.COMPRESSED_NULL.equals(src)) { return super.createLoad(dst, getZeroConstant(dst)); - } else if (src instanceof SubstrateObjectConstant) { - return loadObjectConstant(dst, (SubstrateObjectConstant) src); + } else if (src instanceof CompressibleConstant) { + return loadObjectConstant(dst, (CompressibleConstant) src); } else if (src instanceof SubstrateMethodPointerConstant) { return new AMD64LoadMethodPointerConstantOp(dst, (SubstrateMethodPointerConstant) src); } @@ -1240,7 +1241,7 @@ public AMD64LIRInstruction createLoad(AllocatableValue dst, Constant src) { public LIRInstruction createStackLoad(AllocatableValue dst, Constant src) { if (CompressedNullConstant.COMPRESSED_NULL.equals(src)) { return super.createStackLoad(dst, getZeroConstant(dst)); - } else if (src instanceof SubstrateObjectConstant) { + } else if (src instanceof CompressibleConstant) { return loadObjectConstant(dst, (SubstrateObjectConstant) src); } else if (src instanceof SubstrateMethodPointerConstant) { return new AMD64LoadMethodPointerConstantOp(dst, (SubstrateMethodPointerConstant) src); @@ -1248,7 +1249,7 @@ public LIRInstruction createStackLoad(AllocatableValue dst, Constant src) { return super.createStackLoad(dst, src); } - protected AMD64LIRInstruction loadObjectConstant(AllocatableValue dst, SubstrateObjectConstant constant) { + protected AMD64LIRInstruction loadObjectConstant(AllocatableValue dst, CompressibleConstant constant) { if (ReferenceAccess.singleton().haveCompressedReferences()) { RegisterValue heapBase = ReservedRegisters.singleton().getHeapBaseRegister().asValue(); return new LoadCompressedObjectConstantOp(dst, constant, heapBase, getCompressEncoding(), lirKindTool); @@ -1269,14 +1270,14 @@ protected AMD64LIRInstruction loadObjectConstant(AllocatableValue dst, Substrate */ public static final class LoadCompressedObjectConstantOp extends PointerCompressionOp implements LoadConstantOp { public static final LIRInstructionClass TYPE = LIRInstructionClass.create(LoadCompressedObjectConstantOp.class); - private final SubstrateObjectConstant constant; + private final CompressibleConstant constant; - static JavaConstant asCompressed(SubstrateObjectConstant constant) { + static Constant asCompressed(CompressibleConstant constant) { // We only want compressed references in code return constant.isCompressed() ? constant : constant.compress(); } - LoadCompressedObjectConstantOp(AllocatableValue result, SubstrateObjectConstant constant, AllocatableValue baseRegister, CompressEncoding encoding, LIRKindTool lirKindTool) { + LoadCompressedObjectConstantOp(AllocatableValue result, CompressibleConstant constant, AllocatableValue baseRegister, CompressEncoding encoding, LIRKindTool lirKindTool) { super(TYPE, result, new ConstantValue(lirKindTool.getNarrowOopKind(), asCompressed(constant)), baseRegister, encoding, true, lirKindTool); this.constant = constant; } diff --git a/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMGenerator.java b/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMGenerator.java index 5f906cfc6347..008e96a69074 100644 --- a/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMGenerator.java +++ b/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMGenerator.java @@ -57,6 +57,7 @@ import org.graalvm.compiler.core.common.spi.ForeignCallLinkage; import org.graalvm.compiler.core.common.spi.ForeignCallsProvider; import org.graalvm.compiler.core.common.spi.LIRKindTool; +import org.graalvm.compiler.core.common.type.CompressibleConstant; import org.graalvm.compiler.core.common.type.IllegalStamp; import org.graalvm.compiler.core.common.type.RawPointerStamp; import org.graalvm.compiler.core.common.type.Stamp; @@ -114,7 +115,6 @@ import com.oracle.svm.core.graal.nodes.WriteCurrentVMThreadNode; import com.oracle.svm.core.graal.nodes.WriteHeapBaseNode; import com.oracle.svm.core.heap.ReferenceAccess; -import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.core.snippets.SnippetRuntime; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.code.CEntryPointData; @@ -568,7 +568,7 @@ private LLVMValueRef getLLVMPlaceholderForConstant(Constant constant) { symbolName = "constant_" + functionName + "#" + nextConstantId++; constants.put(constant, symbolName); - Constant storedConstant = uncompressedObject ? ((SubstrateObjectConstant) constant).compress() : constant; + Constant storedConstant = uncompressedObject ? ((CompressibleConstant) constant).compress() : constant; DataSectionReference reference = compilationResult.getDataSection().insertData(dataBuilder.createDataItem(storedConstant)); compilationResult.recordDataPatchWithNote(0, reference, symbolName); } @@ -576,7 +576,7 @@ private LLVMValueRef getLLVMPlaceholderForConstant(Constant constant) { } private static boolean isUncompressedObjectConstant(Constant constant) { - return SubstrateOptions.SpawnIsolates.getValue() && constant instanceof SubstrateObjectConstant && !((SubstrateObjectConstant) constant).isCompressed(); + return SubstrateOptions.SpawnIsolates.getValue() && constant instanceof CompressibleConstant && !((CompressibleConstant) constant).isCompressed(); } private static boolean isUncompressedObjectKind(LIRKind kind) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SharedConstantReflectionProvider.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SharedConstantReflectionProvider.java index 7b6772070106..44708e8e4186 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SharedConstantReflectionProvider.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SharedConstantReflectionProvider.java @@ -27,6 +27,7 @@ import static com.oracle.svm.core.util.VMError.shouldNotReachHere; import java.lang.reflect.Array; +import java.util.function.ObjIntConsumer; import com.oracle.svm.core.meta.ObjectConstantEquality; import com.oracle.svm.core.meta.SubstrateObjectConstant; @@ -80,6 +81,30 @@ public JavaConstant readArrayElement(JavaConstant array, int index) { } } + public void forEachArrayElement(JavaConstant array, ObjIntConsumer consumer) { + if (array.getJavaKind() != JavaKind.Object || array.isNull()) { + return; + } + + Object obj = SubstrateObjectConstant.asObject(array); + + if (!obj.getClass().isArray()) { + return; + } + + if (obj instanceof Object[]) { + Object[] a = (Object[]) obj; + for (int index = 0; index < a.length; index++) { + consumer.accept((SubstrateObjectConstant.forObject(a[index])), index); + } + } else { + for (int index = 0; index < Array.getLength(obj); index++) { + Object element = Array.get(obj, index); + consumer.accept(JavaConstant.forBoxedPrimitive((element)), index); + } + } + } + @Override public JavaConstant boxPrimitive(JavaConstant source) { if (!source.getJavaKind().isPrimitive()) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/SubstrateCompressionNode.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/SubstrateCompressionNode.java index b487139ae49d..f75e438ed615 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/SubstrateCompressionNode.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/SubstrateCompressionNode.java @@ -28,6 +28,7 @@ import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_2; import org.graalvm.compiler.core.common.CompressEncoding; +import org.graalvm.compiler.core.common.type.CompressibleConstant; import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.graph.NodeClass; @@ -38,7 +39,6 @@ import org.graalvm.compiler.nodes.ValueNode; import com.oracle.svm.core.meta.CompressedNullConstant; -import com.oracle.svm.core.meta.CompressibleConstant; import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.JavaConstant; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/SubstrateNarrowOopStamp.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/SubstrateNarrowOopStamp.java index 7cca142fdc4f..9d49465de12a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/SubstrateNarrowOopStamp.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/SubstrateNarrowOopStamp.java @@ -26,6 +26,7 @@ import org.graalvm.compiler.core.common.CompressEncoding; import org.graalvm.compiler.core.common.type.AbstractObjectStamp; +import org.graalvm.compiler.core.common.type.CompressibleConstant; import org.graalvm.compiler.core.common.type.ObjectStamp; import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.debug.GraalError; @@ -35,7 +36,6 @@ import com.oracle.svm.core.graal.meta.SubstrateMemoryAccessProvider; import com.oracle.svm.core.heap.ReferenceAccess; import com.oracle.svm.core.meta.CompressedNullConstant; -import com.oracle.svm.core.meta.CompressibleConstant; import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.JavaConstant; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/image/ImageHeapObject.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/image/ImageHeapObject.java index 4c1853561750..9e4fd57d4e44 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/image/ImageHeapObject.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/image/ImageHeapObject.java @@ -24,11 +24,17 @@ */ package com.oracle.svm.core.image; +import jdk.vm.ci.meta.JavaConstant; + public interface ImageHeapObject { long getSize(); Object getObject(); + Class getObjectClass(); + + JavaConstant getConstant(); + void setHeapPartition(ImageHeapPartition value); void setOffsetInPartition(long value); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/CompressedNullConstant.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/CompressedNullConstant.java index 5754bd5001a7..b9420063003e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/CompressedNullConstant.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/CompressedNullConstant.java @@ -24,7 +24,8 @@ */ package com.oracle.svm.core.meta; -import jdk.vm.ci.meta.Constant; +import org.graalvm.compiler.core.common.type.CompressibleConstant; + import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; @@ -55,12 +56,12 @@ public boolean isCompressed() { } @Override - public Constant compress() { + public JavaConstant compress() { throw new IllegalArgumentException(); } @Override - public Constant uncompress() { + public JavaConstant uncompress() { return NULL_POINTER; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/SubstrateObjectConstant.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/SubstrateObjectConstant.java index 66f1befb3c5d..a829c76a679c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/SubstrateObjectConstant.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/SubstrateObjectConstant.java @@ -24,6 +24,9 @@ */ package com.oracle.svm.core.meta; +import org.graalvm.compiler.core.common.type.CompressibleConstant; +import org.graalvm.compiler.core.common.type.TypedConstant; + import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.util.ClassUtil; @@ -31,11 +34,10 @@ import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaType; import jdk.vm.ci.meta.VMConstant; -public abstract class SubstrateObjectConstant implements JavaConstant, CompressibleConstant, VMConstant { +public abstract class SubstrateObjectConstant implements JavaConstant, TypedConstant, CompressibleConstant, VMConstant { public static JavaConstant forObject(Object object) { return forObject(object, false); } @@ -175,8 +177,6 @@ public String toString() { return ClassUtil.getUnqualifiedName(getClass()) + '[' + getJavaKind().getJavaName() + ']'; } - public abstract ResolvedJavaType getType(MetaAccessProvider provider); - @Override public abstract SubstrateObjectConstant compress(); diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/SubstrateGraalCompilerSetup.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/SubstrateGraalCompilerSetup.java index 25db2c1e4ba9..712b93a34f2f 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/SubstrateGraalCompilerSetup.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/SubstrateGraalCompilerSetup.java @@ -30,6 +30,7 @@ import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.phases.util.Providers; +import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.svm.core.SubstrateOptions; @@ -44,7 +45,6 @@ import com.oracle.svm.hosted.code.SharedRuntimeConfigurationBuilder; import jdk.vm.ci.meta.ConstantReflectionProvider; -import jdk.vm.ci.meta.MetaAccessProvider; public class SubstrateGraalCompilerSetup { @@ -67,7 +67,7 @@ public SubstrateProviders getSubstrateProviders(AnalysisMetaAccess aMetaAccess) } } - public SharedRuntimeConfigurationBuilder createRuntimeConfigurationBuilder(OptionValues options, SVMHost hostVM, AnalysisUniverse aUniverse, MetaAccessProvider metaAccess, + public SharedRuntimeConfigurationBuilder createRuntimeConfigurationBuilder(OptionValues options, SVMHost hostVM, AnalysisUniverse aUniverse, UniverseMetaAccess metaAccess, ConstantReflectionProvider originalReflectionProvider, Function backendProvider, NativeLibraries nativeLibraries, ClassInitializationSupport classInitializationSupport, LoopsDataProvider loopsDataProvider) { return new SubstrateRuntimeConfigurationBuilder(options, hostVM, aUniverse, metaAccess, originalReflectionProvider, backendProvider, nativeLibraries, classInitializationSupport, diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateConstantReflectionProvider.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateConstantReflectionProvider.java index abf5f7b7df5d..d16cd1b7001d 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateConstantReflectionProvider.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateConstantReflectionProvider.java @@ -171,6 +171,7 @@ public int getImageHeapOffset(JavaConstant constant) { if (constant instanceof SubstrateObjectConstant) { return getImageHeapOffsetInternal((SubstrateObjectConstant) constant); } + /* Primitive values, null values. */ return 0; } diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateRuntimeConfigurationBuilder.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateRuntimeConfigurationBuilder.java index e7e8bbffbc9f..9c5770b9f53a 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateRuntimeConfigurationBuilder.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateRuntimeConfigurationBuilder.java @@ -35,6 +35,7 @@ import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.phases.util.Providers; +import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.svm.core.SubstrateOptions; @@ -53,14 +54,13 @@ import jdk.vm.ci.code.RegisterConfig; import jdk.vm.ci.meta.ConstantReflectionProvider; -import jdk.vm.ci.meta.MetaAccessProvider; public class SubstrateRuntimeConfigurationBuilder extends SharedRuntimeConfigurationBuilder { private final AnalysisUniverse aUniverse; private final ConstantReflectionProvider originalReflectionProvider; - public SubstrateRuntimeConfigurationBuilder(OptionValues options, SVMHost hostVM, AnalysisUniverse aUniverse, MetaAccessProvider metaAccess, + public SubstrateRuntimeConfigurationBuilder(OptionValues options, SVMHost hostVM, AnalysisUniverse aUniverse, UniverseMetaAccess metaAccess, ConstantReflectionProvider originalReflectionProvider, Function backendProvider, NativeLibraries nativeLibraries, ClassInitializationSupport classInitializationSupport, LoopsDataProvider loopsDataProvider) { super(options, hostVM, metaAccess, backendProvider, nativeLibraries, classInitializationSupport, loopsDataProvider); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java index baf4f13cc5b0..47acd21e7020 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java @@ -85,7 +85,6 @@ import com.oracle.svm.hosted.meta.HostedField; import com.oracle.svm.hosted.meta.HostedMetaAccess; import com.oracle.svm.hosted.meta.HostedMethod; -import com.oracle.svm.hosted.meta.HostedType; import com.oracle.svm.hosted.meta.HostedUniverse; import com.oracle.svm.hosted.option.HostedOptionProvider; import com.oracle.svm.util.GuardedAnnotationAccess; @@ -581,7 +580,7 @@ public void registerAsImmutable(Object root, Predicate includeObject) { } } else { JavaConstant constant = SubstrateObjectConstant.forObject(cur); - for (HostedField field : ((HostedType) getMetaAccess().lookupJavaType(constant)).getInstanceFields(true)) { + for (HostedField field : getMetaAccess().lookupJavaType(constant).getInstanceFields(true)) { if (field.isAccessed() && field.getStorageKind() == JavaKind.Object) { Object fieldValue = SubstrateObjectConstant.asObject(field.readValue(constant)); addToWorklist(fieldValue, includeObject, worklist, registeredObjects); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java index 8fde5a0347f7..27df21f53e59 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java @@ -697,7 +697,7 @@ private void doRun(Map entryPoints, } } - ProgressReporter.singleton().createBreakdowns(compileQueue.getCompilationTasks(), image.getHeap().getObjects()); + ProgressReporter.singleton().createBreakdowns(hMetaAccess, compileQueue.getCompilationTasks(), image.getHeap().getObjects()); compileQueue.purge(); int numCompilations = codeCache.getOrderedCompilations().size(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java index 58e40b9df34c..bcd60b80a0a9 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java @@ -78,6 +78,7 @@ import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.jdk.Resources; import com.oracle.svm.core.jdk.resources.ResourceStorageEntry; +import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.core.option.HostedOptionValues; import com.oracle.svm.core.reflect.ReflectionMetadataDecoder; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; @@ -91,10 +92,13 @@ import com.oracle.svm.hosted.code.CompileQueue.CompileTask; import com.oracle.svm.hosted.image.AbstractImage.NativeImageKind; import com.oracle.svm.hosted.image.NativeImageHeap.ObjectInfo; +import com.oracle.svm.hosted.meta.HostedMetaAccess; import com.oracle.svm.hosted.reflect.ReflectionHostedSupport; import com.oracle.svm.util.ImageBuildStatistics; import com.oracle.svm.util.ReflectionUtil; +import jdk.vm.ci.meta.JavaConstant; + public class ProgressReporter { private static final int CHARACTERS_PER_LINE; private static final String HEADLINE_SEPARATOR; @@ -482,12 +486,12 @@ public void ensureCreationStageEndCompleted() { } } - public void createBreakdowns(Collection compilationTasks, Collection heapObjects) { + public void createBreakdowns(HostedMetaAccess metaAccess, Collection compilationTasks, Collection heapObjects) { if (!SubstrateOptions.BuildOutputBreakdowns.getValue()) { return; } calculateCodeBreakdown(compilationTasks); - calculateHeapBreakdown(heapObjects); + calculateHeapBreakdown(metaAccess, heapObjects); } private void calculateCodeBreakdown(Collection compilationTasks) { @@ -501,13 +505,13 @@ private void calculateCodeBreakdown(Collection compilationTasks) { } } - private void calculateHeapBreakdown(Collection heapObjects) { + private void calculateHeapBreakdown(HostedMetaAccess metaAccess, Collection heapObjects) { long stringByteLength = 0; for (ObjectInfo o : heapObjects) { heapBreakdown.merge(o.getClazz().toJavaName(true), o.getSize(), Long::sum); - Object javaObject = o.getObject(); - if (reportStringBytes && javaObject instanceof String) { - stringByteLength += Utils.getInternalByteArrayLength((String) javaObject); + JavaConstant javaObject = o.getConstant(); + if (reportStringBytes && metaAccess.isInstanceOf(javaObject, String.class)) { + stringByteLength += Utils.getInternalByteArrayLength((String) SubstrateObjectConstant.asObject(javaObject)); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java index bc5bf09bd90d..b1d6b073394a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java @@ -74,6 +74,7 @@ import com.oracle.graal.pointsto.api.HostVM; import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException; import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; +import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; @@ -123,6 +124,7 @@ import com.oracle.svm.util.ReflectionUtil; import jdk.vm.ci.meta.DeoptimizationReason; +import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; @@ -225,8 +227,8 @@ public String getImageName() { } @Override - public boolean isRelocatedPointer(Object originalObject) { - return originalObject instanceof RelocatedPointer; + public boolean isRelocatedPointer(UniverseMetaAccess metaAccess, JavaConstant constant) { + return metaAccess.isInstanceOf(constant, RelocatedPointer.class); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantFieldProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantFieldProvider.java index 57323bdc6bf3..7b7875389dea 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantFieldProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantFieldProvider.java @@ -42,14 +42,12 @@ @Platforms(Platform.HOSTED_ONLY.class) public class AnalysisConstantFieldProvider extends SharedConstantFieldProvider { private final AnalysisUniverse universe; - private final AnalysisMetaAccess metaAccess; private final AnalysisConstantReflectionProvider constantReflection; public AnalysisConstantFieldProvider(AnalysisUniverse universe, AnalysisMetaAccess metaAccess, AnalysisConstantReflectionProvider constantReflection, ClassInitializationSupport classInitializationSupport) { super(metaAccess, classInitializationSupport); this.universe = universe; - this.metaAccess = metaAccess; this.constantReflection = constantReflection; } @@ -65,7 +63,7 @@ public T readConstantField(ResolvedJavaField field, ConstantFieldTool ana if (readableField.allowConstantFolding() && readableField.isValueAvailable()) { JavaConstant fieldValue = readableField.readValue(metaAccess, universe.toHosted(analysisTool.getReceiver())); if (fieldValue != null) { - foldedValue = analysisTool.foldConstant(constantReflection.interceptValue(f, universe.lookup(fieldValue))); + foldedValue = analysisTool.foldConstant(constantReflection.interceptValue(metaAccess, f, universe.lookup(fieldValue))); } } } else { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java index da97022ed703..7c7e255ca5a7 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java @@ -24,7 +24,18 @@ */ package com.oracle.svm.hosted.ameta; +import java.util.function.ObjIntConsumer; + +import org.graalvm.compiler.word.Word; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.word.WordBase; + +import com.oracle.graal.pointsto.heap.ImageHeapArray; +import com.oracle.graal.pointsto.heap.ImageHeapConstant; +import com.oracle.graal.pointsto.heap.ImageHeapInstance; import com.oracle.graal.pointsto.heap.value.ValueSupplier; +import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; @@ -46,22 +57,17 @@ import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.MemoryAccessProvider; -import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaType; -import org.graalvm.compiler.word.Word; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; -import org.graalvm.word.WordBase; @Platforms(Platform.HOSTED_ONLY.class) public class AnalysisConstantReflectionProvider extends SharedConstantReflectionProvider { private final AnalysisUniverse universe; - private final MetaAccessProvider metaAccess; + private final UniverseMetaAccess metaAccess; private final ConstantReflectionProvider originalConstantReflection; private final ClassInitializationSupport classInitializationSupport; - public AnalysisConstantReflectionProvider(AnalysisUniverse universe, MetaAccessProvider metaAccess, + public AnalysisConstantReflectionProvider(AnalysisUniverse universe, UniverseMetaAccess metaAccess, ConstantReflectionProvider originalConstantReflection, ClassInitializationSupport classInitializationSupport) { this.universe = universe; this.metaAccess = metaAccess; @@ -74,13 +80,66 @@ public MemoryAccessProvider getMemoryAccessProvider() { return EmptyMemoryAcessProvider.SINGLETON; } + @Override + public Integer readArrayLength(JavaConstant array) { + if (array.getJavaKind() != JavaKind.Object || array.isNull()) { + return null; + } + if (array instanceof ImageHeapConstant) { + if (array instanceof ImageHeapArray) { + return ((ImageHeapArray) array).getLength(); + } + return null; + } + return super.readArrayLength(array); + } + + @Override + public JavaConstant readArrayElement(JavaConstant array, int index) { + if (array.getJavaKind() != JavaKind.Object || array.isNull()) { + return null; + } + if (array instanceof ImageHeapConstant) { + if (array instanceof ImageHeapArray) { + ImageHeapArray heapArray = (ImageHeapArray) array; + if (index < 0 || index >= heapArray.getLength()) { + return null; + } + return replaceObject(heapArray.getElement(index)); + } + return null; + } + JavaConstant element = super.readArrayElement(array, index); + return element == null ? null : replaceObject(element); + } + + @Override + public void forEachArrayElement(JavaConstant array, ObjIntConsumer consumer) { + if (array instanceof ImageHeapConstant) { + if (array instanceof ImageHeapArray) { + ImageHeapArray heapArray = (ImageHeapArray) array; + for (int index = 0; index < heapArray.getLength(); index++) { + JavaConstant element = heapArray.getElement(index); + consumer.accept(replaceObject(element), index); + } + } + return; + } + /* Intercept the original consumer and apply object replacement. */ + super.forEachArrayElement(array, (element, index) -> consumer.accept(replaceObject(element), index)); + } + @Override public final JavaConstant readFieldValue(ResolvedJavaField field, JavaConstant receiver) { return readValue(metaAccess, (AnalysisField) field, receiver); } - public JavaConstant readValue(MetaAccessProvider suppliedMetaAccess, AnalysisField field, JavaConstant receiver) { + public JavaConstant readValue(UniverseMetaAccess suppliedMetaAccess, AnalysisField field, JavaConstant receiver) { JavaConstant value; + if (receiver instanceof ImageHeapConstant) { + ImageHeapInstance heapObject = (ImageHeapInstance) receiver; + return interceptValue(suppliedMetaAccess, field, heapObject.readFieldValue(field)); + } if (classInitializationSupport.shouldInitializeAtRuntime(field.getDeclaringClass())) { if (field.isStatic()) { value = readUninitializedStaticValue(field); @@ -97,7 +156,7 @@ public JavaConstant readValue(MetaAccessProvider suppliedMetaAccess, AnalysisFie value = universe.lookup(ReadableJavaField.readFieldValue(suppliedMetaAccess, originalConstantReflection, field.wrapped, universe.toHosted(receiver))); } - return interceptValue(field, value); + return interceptValue(suppliedMetaAccess, field, value); } /** Read the field value and wrap it in a value supplier without performing any replacements. */ @@ -141,13 +200,13 @@ public JavaConstant readUninitializedStaticValue(AnalysisField field) { return UninitializedStaticFieldValueReader.readUninitializedStaticValue(field, val -> SubstrateObjectConstant.forObject(val)); } - public JavaConstant interceptValue(AnalysisField field, JavaConstant value) { + public JavaConstant interceptValue(UniverseMetaAccess suppliedMetaAccess, AnalysisField field, JavaConstant value) { JavaConstant result = value; if (result != null) { result = filterInjectedAccessor(field, result); result = replaceObject(result); result = interceptAssertionStatus(field, result); - result = interceptWordType(field, result); + result = interceptWordType(suppliedMetaAccess, field, result); } return result; } @@ -172,6 +231,10 @@ private JavaConstant replaceObject(JavaConstant value) { if (value == JavaConstant.NULL_POINTER) { return JavaConstant.NULL_POINTER; } + if (value instanceof ImageHeapConstant) { + /* The value is replaced when the object is snapshotted. */ + return value; + } if (value.getJavaKind() == JavaKind.Object) { Object oldObject = universe.getSnippetReflection().asObject(Object.class, value); Object newObject = universe.replaceObject(oldObject); @@ -202,10 +265,9 @@ private static JavaConstant interceptAssertionStatus(AnalysisField field, JavaCo * Intercept {@link Word} types. They are boxed objects in the hosted world, but primitive * values in the runtime world. */ - private JavaConstant interceptWordType(AnalysisField field, JavaConstant value) { + private JavaConstant interceptWordType(UniverseMetaAccess suppliedMetaAccess, AnalysisField field, JavaConstant value) { if (value.getJavaKind() == JavaKind.Object) { - Object originalObject = universe.getSnippetReflection().asObject(Object.class, value); - if (universe.hostVM().isRelocatedPointer(originalObject)) { + if (universe.hostVM().isRelocatedPointer(suppliedMetaAccess, value)) { /* * Such pointers are subject to relocation therefore we don't know their values yet. * Therefore there should not be a relocated pointer constant in a function which is @@ -213,9 +275,10 @@ private JavaConstant interceptWordType(AnalysisField field, JavaConstant value) * of readValue is responsible of handling the returned value correctly. */ return value; - } else if (originalObject instanceof WordBase) { + } else if (suppliedMetaAccess.isInstanceOf(value, WordBase.class)) { + Object originalObject = universe.getSnippetReflection().asObject(Object.class, value); return JavaConstant.forIntegerKind(universe.getWordKind(), ((WordBase) originalObject).rawValue()); - } else if (originalObject == null && field.getType().isWordType()) { + } else if (value.isNull() && field.getType().isWordType()) { return JavaConstant.forIntegerKind(universe.getWordKind(), 0); } } @@ -229,8 +292,12 @@ public ResolvedJavaType asJavaType(Constant constant) { if (obj instanceof DynamicHub) { return getHostVM().lookupType((DynamicHub) obj); } else if (obj instanceof Class) { - // TODO do we need to make sure the hub is scanned? - return metaAccess.lookupJavaType((Class) obj); + throw VMError.shouldNotReachHere("Must not have java.lang.Class object: " + obj); + } + } + if (constant instanceof ImageHeapConstant) { + if (metaAccess.isInstanceOf((JavaConstant) constant, Class.class)) { + throw VMError.shouldNotReachHere("ConstantReflectionProvider.asJavaType(Constant) not yet implemented for ImageHeapObject"); } } return null; @@ -240,7 +307,6 @@ public ResolvedJavaType asJavaType(Constant constant) { public JavaConstant asJavaClass(ResolvedJavaType type) { DynamicHub dynamicHub = getHostVM().dynamicHub(type); registerAsReachable(getHostVM(), dynamicHub); - assert dynamicHub != null : type.toClassName() + " has a null dynamicHub."; return SubstrateObjectConstant.forObject(dynamicHub); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/flow/SVMMethodTypeFlowBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/flow/SVMMethodTypeFlowBuilder.java index c1b39ef20d48..dfcb80e918c0 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/flow/SVMMethodTypeFlowBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/flow/SVMMethodTypeFlowBuilder.java @@ -40,6 +40,7 @@ import com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder; import com.oracle.graal.pointsto.flow.TypeFlow; import com.oracle.graal.pointsto.flow.builder.TypeFlowBuilder; +import com.oracle.graal.pointsto.heap.ImageHeapConstant; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod; @@ -52,6 +53,7 @@ import com.oracle.svm.hosted.substitute.ComputedValueField; import jdk.vm.ci.code.BytecodePosition; +import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; public class SVMMethodTypeFlowBuilder extends MethodTypeFlowBuilder { @@ -75,23 +77,29 @@ public void registerUsedElements(boolean registerEmbeddedRoots) { for (Node n : graph.getNodes()) { if (n instanceof ConstantNode) { ConstantNode cn = (ConstantNode) n; - if (cn.hasUsages() && cn.isJavaConstant() && cn.asJavaConstant().getJavaKind() == JavaKind.Object && cn.asJavaConstant().isNonNull()) { - /* - * Constants that are embedded into graphs via constant folding of static fields - * have already been replaced. But constants embedded manually by graph builder - * plugins, or class constants that come directly from constant bytecodes, are - * not replaced. We verify here that the object replacer would not replace such - * objects. - * - * But more importantly, some object replacers also perform actions like forcing - * eager initialization of fields. We need to make sure that these object - * replacers really see all objects that are embedded into compiled code. - */ - Object value = SubstrateObjectConstant.asObject(cn.asJavaConstant()); - Object replaced = bb.getUniverse().replaceObject(value); - if (value != replaced) { - throw GraalError.shouldNotReachHere("Missed object replacement during graph building: " + - value + " (" + value.getClass() + ") != " + replaced + " (" + replaced.getClass() + ")"); + JavaConstant constant = cn.asJavaConstant(); + if (cn.hasUsages() && cn.isJavaConstant() && constant.getJavaKind() == JavaKind.Object && constant.isNonNull()) { + if (constant instanceof ImageHeapConstant) { + /* No replacement for ImageHeapObject. */ + } else { + /* + * Constants that are embedded into graphs via constant folding of static + * fields have already been replaced. But constants embedded manually by + * graph builder plugins, or class constants that come directly from + * constant bytecodes, are not replaced. We verify here that the object + * replacer would not replace such objects. + * + * But more importantly, some object replacers also perform actions like + * forcing eager initialization of fields. We need to make sure that these + * object replacers really see all objects that are embedded into compiled + * code. + */ + Object value = SubstrateObjectConstant.asObject(constant); + Object replaced = bb.getUniverse().replaceObject(value); + if (value != replaced) { + throw GraalError.shouldNotReachHere("Missed object replacement during graph building: " + + value + " (" + value.getClass() + ") != " + replaced + " (" + replaced.getClass() + ")"); + } } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/AnalysisToHostedGraphTransplanter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/AnalysisToHostedGraphTransplanter.java index 4b824fcabe47..31f2b267ce47 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/AnalysisToHostedGraphTransplanter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/AnalysisToHostedGraphTransplanter.java @@ -51,6 +51,7 @@ import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.replacements.SnippetTemplate; +import com.oracle.graal.pointsto.heap.ImageHeapConstant; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; @@ -316,6 +317,11 @@ static Object replaceAnalysisObjects(Object obj, Node node, IdentityHashMap backendProvider; @@ -79,7 +79,7 @@ public abstract class SharedRuntimeConfigurationBuilder { protected final ClassInitializationSupport classInitializationSupport; protected final LoopsDataProvider originalLoopsDataProvider; - public SharedRuntimeConfigurationBuilder(OptionValues options, SVMHost hostVM, MetaAccessProvider metaAccess, Function backendProvider, + public SharedRuntimeConfigurationBuilder(OptionValues options, SVMHost hostVM, UniverseMetaAccess metaAccess, Function backendProvider, NativeLibraries nativeLibraries, ClassInitializationSupport classInitializationSupport, LoopsDataProvider originalLoopsDataProvider) { this.options = options; this.hostVM = hostVM; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/amd64/AMD64HostedPatcherFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/amd64/AMD64HostedPatcherFeature.java index 47a654fba80a..1ff7ee46ee87 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/amd64/AMD64HostedPatcherFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/amd64/AMD64HostedPatcherFeature.java @@ -42,7 +42,6 @@ import com.oracle.svm.core.graal.code.PatchConsumerFactory; import com.oracle.svm.core.meta.MethodPointer; import com.oracle.svm.core.meta.SubstrateMethodPointerConstant; -import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.code.HostedImageHeapConstantPatch; import com.oracle.svm.hosted.code.HostedPatcher; @@ -52,6 +51,7 @@ import jdk.vm.ci.code.site.ConstantReference; import jdk.vm.ci.code.site.DataSectionReference; import jdk.vm.ci.code.site.Reference; +import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.VMConstant; @AutomaticallyRegisteredFeature @@ -70,7 +70,7 @@ public void accept(Assembler.CodeAnnotation annotation) { } else if (annotation instanceof AddressDisplacementAnnotation) { AddressDisplacementAnnotation dispAnnotation = (AddressDisplacementAnnotation) annotation; - compilationResult.addAnnotation(new HostedImageHeapConstantPatch(dispAnnotation.operandPosition, (SubstrateObjectConstant) dispAnnotation.annotation)); + compilationResult.addAnnotation(new HostedImageHeapConstantPatch(dispAnnotation.operandPosition, (JavaConstant) dispAnnotation.annotation)); } } }; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java index 947a6e315700..10ce05aac2ae 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java @@ -31,13 +31,14 @@ import org.graalvm.collections.EconomicMap; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; +import org.graalvm.compiler.core.common.type.TypedConstant; import org.graalvm.nativeimage.ImageSingletons; import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.ObjectScanner.ScanReason; import com.oracle.graal.pointsto.ObjectScanningObserver; import com.oracle.graal.pointsto.heap.ImageHeap; -import com.oracle.graal.pointsto.heap.ImageHeapObject; +import com.oracle.graal.pointsto.heap.ImageHeapConstant; import com.oracle.graal.pointsto.heap.ImageHeapScanner; import com.oracle.graal.pointsto.heap.value.ValueSupplier; import com.oracle.graal.pointsto.meta.AnalysisField; @@ -90,8 +91,8 @@ protected Class getClass(String className) { } @Override - protected ImageHeapObject getOrCreateConstantReachableTask(JavaConstant javaConstant, ScanReason reason, Consumer onAnalysisModified) { - VMError.guarantee(javaConstant instanceof SubstrateObjectConstant, "Not a substrate constant " + javaConstant); + protected ImageHeapConstant getOrCreateConstantReachableTask(JavaConstant javaConstant, ScanReason reason, Consumer onAnalysisModified) { + VMError.guarantee(javaConstant instanceof TypedConstant, "Not a substrate constant " + javaConstant); return super.getOrCreateConstantReachableTask(javaConstant, reason, onAnalysisModified); } @@ -124,7 +125,7 @@ protected ValueSupplier readHostedFieldValue(AnalysisField field, @Override protected JavaConstant transformFieldValue(AnalysisField field, JavaConstant receiverConstant, JavaConstant originalValueConstant) { - return ((AnalysisConstantReflectionProvider) constantReflection).interceptValue(field, originalValueConstant); + return ((AnalysisConstantReflectionProvider) constantReflection).interceptValue(metaAccess, field, originalValueConstant); } @Override @@ -144,14 +145,13 @@ protected void rescanEconomicMap(EconomicMap map) { } @Override - protected void onObjectReachable(ImageHeapObject imageHeapObject) { - super.onObjectReachable(imageHeapObject); - - Object object = SubstrateObjectConstant.asObject(imageHeapObject.getObject()); - if (object instanceof Field || object instanceof Executable) { - reflectionSupport.registerHeapReflectionObject((AccessibleObject) object); - } else if (object instanceof DynamicHub) { - reflectionSupport.registerHeapDynamicHub(object); + protected void onObjectReachable(ImageHeapConstant imageHeapConstant) { + super.onObjectReachable(imageHeapConstant); + + if (metaAccess.isInstanceOf(imageHeapConstant, Field.class) || metaAccess.isInstanceOf(imageHeapConstant, Executable.class)) { + reflectionSupport.registerHeapReflectionObject((AccessibleObject) SubstrateObjectConstant.asObject(imageHeapConstant.getHostedObject())); + } else if (metaAccess.isInstanceOf(imageHeapConstant, DynamicHub.class)) { + reflectionSupport.registerHeapDynamicHub(SubstrateObjectConstant.asObject(imageHeapConstant.getHostedObject())); } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/LIRNativeImageCodeCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/LIRNativeImageCodeCache.java index 9693fc87f46a..ea30cbee121b 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/LIRNativeImageCodeCache.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/LIRNativeImageCodeCache.java @@ -47,7 +47,6 @@ import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.meta.MethodPointer; -import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.code.HostedDirectCallTrampolineSupport; import com.oracle.svm.hosted.code.HostedImageHeapConstantPatch; @@ -370,7 +369,7 @@ public void patchMethods(DebugContext debug, RelocatableBuffer relocs, ObjectFil } else if (codeAnnotation instanceof HostedImageHeapConstantPatch) { HostedImageHeapConstantPatch patch = (HostedImageHeapConstantPatch) codeAnnotation; - ObjectInfo objectInfo = imageHeap.getObjectInfo(SubstrateObjectConstant.asObject(patch.constant)); + ObjectInfo objectInfo = imageHeap.getConstantInfo(patch.constant); long objectAddress = objectInfo.getAddress(); if (targetCode == null) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java index c029b73772b2..07cb8a3c5458 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java @@ -98,7 +98,6 @@ import com.oracle.svm.core.image.ImageHeapLayoutInfo; import com.oracle.svm.core.image.ImageHeapPartition; import com.oracle.svm.core.meta.MethodPointer; -import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.core.option.HostedOptionValues; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.util.UserError; @@ -125,6 +124,7 @@ import jdk.vm.ci.code.Architecture; import jdk.vm.ci.code.site.ConstantReference; import jdk.vm.ci.code.site.DataSectionReference; +import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaMethod.Parameter; import jdk.vm.ci.meta.ResolvedJavaType; @@ -661,8 +661,8 @@ private void markDataRelocationSiteFromText(RelocatableBuffer buffer, final Prog } } else if (target instanceof ConstantReference) { // Direct object reference in code that must be patched (not a linker relocation) - Object object = SubstrateObjectConstant.asObject(((ConstantReference) target).getConstant()); - long address = heap.getObjectInfo(object).getAddress(); + JavaConstant constant = (JavaConstant) ((ConstantReference) target).getConstant(); + long address = heap.getConstantInfo(constant).getAddress(); int encShift = ImageSingletons.lookup(CompressEncoding.class).getShift(); long targetValue = address >>> encShift; assert (targetValue << encShift) == address : "Reference compression shift discards non-zero bits: " + Long.toHexString(address); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java index d5b4f8ea0ffa..af2ff678f19e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java @@ -75,7 +75,6 @@ import com.oracle.svm.core.deopt.DeoptEntryInfopoint; import com.oracle.svm.core.graal.code.SubstrateDataBuilder; import com.oracle.svm.core.meta.SubstrateMethodPointerConstant; -import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.option.HostedOptionValues; import com.oracle.svm.core.reflect.target.EncodedReflectionMetadataSupplier; @@ -224,14 +223,13 @@ private void addConstantToHeap(Constant constant, Object reason) { */ return; } - Object obj = SubstrateObjectConstant.asObject(constant); - if (!imageHeap.getMetaAccess().lookupJavaType(obj.getClass()).getWrapped().isInstantiated()) { - throw shouldNotReachHere("Non-instantiated type referenced by a compiled method: " + obj.getClass().getName() + "." + + HostedType hostedType = imageHeap.getMetaAccess().lookupJavaType((JavaConstant) constant); + if (!hostedType.isInstantiated()) { + throw shouldNotReachHere("Non-instantiated type referenced by a compiled method: " + hostedType.getName() + "." + (reason != null ? " Method: " + reason : "")); } - - imageHeap.addObject(obj, false, reason != null ? reason : constantReasons.get(constant)); + imageHeap.addConstant((JavaConstant) constant, false, reason != null ? reason : constantReasons.get(constant)); } protected int getConstantsSize() { @@ -518,7 +516,7 @@ private boolean verifyMethods(CodeInfoEncoder codeInfoEncoder, CodeInfo codeInfo public void writeConstants(NativeImageHeapWriter writer, RelocatableBuffer buffer) { ByteBuffer bb = buffer.getByteBuffer(); dataSection.buildDataSection(bb, (position, constant) -> { - writer.writeReference(buffer, position, SubstrateObjectConstant.asObject(constant), "VMConstant: " + constant); + writer.writeReference(buffer, position, (JavaConstant) constant, "VMConstant: " + constant); }); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index 360765ad4839..1e36a60a7db2 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -34,6 +34,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Objects; @@ -68,7 +69,6 @@ import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.heap.ObjectHeader; import com.oracle.svm.core.image.ImageHeapPartition; -import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.hosted.annotation.CustomSubstitutionType; import com.oracle.svm.hosted.image.NativeImageHeap.ObjectInfo; import com.oracle.svm.hosted.image.sources.SourceManager; @@ -108,7 +108,6 @@ import jdk.vm.ci.meta.ResolvedJavaType; import jdk.vm.ci.meta.Signature; import jdk.vm.ci.meta.Value; -import java.util.HashMap; /** * Implementation of the DebugInfoProvider API interface that allows type, code and heap data info @@ -247,12 +246,9 @@ static ObjectLayout getObjectLayout() { */ public long objectOffset(JavaConstant constant) { assert constant.getJavaKind() == JavaKind.Object && !constant.isNull() : "invalid constant for object offset lookup"; - if (constant instanceof SubstrateObjectConstant) { - Object object = SubstrateObjectConstant.asObject(constant); - ObjectInfo objectInfo = heap.getObjectInfo(object); - if (objectInfo != null) { - return objectInfo.getAddress(); - } + ObjectInfo objectInfo = heap.getConstantInfo(constant); + if (objectInfo != null) { + return objectInfo.getAddress(); } return -1; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java index 64342c48756a..a84c748fac93 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java @@ -43,11 +43,13 @@ import org.graalvm.compiler.core.common.CompressEncoding; import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.core.common.SuppressFBWarnings; +import org.graalvm.compiler.core.common.type.CompressibleConstant; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.c.function.RelocatedPointer; import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordBase; +import com.oracle.graal.pointsto.heap.ImageHeapConstant; import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.graal.pointsto.util.AnalysisError; import com.oracle.svm.core.StaticFieldsSupport; @@ -100,7 +102,7 @@ public final class NativeImageHeap implements ImageHeap { * * More than one host object may be represented by a single native image object. */ - protected final IdentityHashMap objects = new IdentityHashMap<>(); + private final HashMap objects = new HashMap<>(); /** Objects that must not be written to the native image heap. */ private final Set blacklist = Collections.newSetFromMap(new IdentityHashMap<>()); @@ -144,7 +146,11 @@ public int getObjectCount() { } public ObjectInfo getObjectInfo(Object obj) { - return objects.get(obj); + return objects.get(SubstrateObjectConstant.forObject(obj)); + } + + public ObjectInfo getConstantInfo(JavaConstant constant) { + return objects.get(uncompress(constant)); } protected HostedUniverse getUniverse() { @@ -234,6 +240,10 @@ private static Object readObjectField(HostedField field, JavaConstant receiver) return SubstrateObjectConstant.asObject(field.readStorageValue(receiver)); } + private static JavaConstant readConstantField(HostedField field, JavaConstant receiver) { + return field.readStorageValue(receiver); + } + private void addStaticFields() { addObject(StaticFieldsSupport.getStaticObjectFields(), false, "staticObjectFields"); addObject(StaticFieldsSupport.getStaticPrimitiveFields(), false, "staticPrimitiveFields"); @@ -245,7 +255,7 @@ private void addStaticFields() { for (HostedField field : getUniverse().getFields()) { if (Modifier.isStatic(field.getModifiers()) && field.hasLocation() && field.getType().getStorageKind() == JavaKind.Object && field.isRead()) { assert field.isWritten() || MaterializedConstantFields.singleton().contains(field.wrapped); - addObject(readObjectField(field, null), false, field); + addConstant(readConstantField(field, null), false, field); } } } @@ -262,37 +272,85 @@ public void registerAsImmutable(Object object) { * Not every object is added to the heap, for various reasons. */ public void addObject(final Object original, boolean immutableFromParent, final Object reason) { + addConstant(SubstrateObjectConstant.forObject(original), immutableFromParent, reason); + } + + public void addConstant(final JavaConstant constant, boolean immutableFromParent, final Object reason) { assert addObjectsPhase.isAllowed() : "Objects cannot be added at phase: " + addObjectsPhase.toString() + " with reason: " + reason; - if (original == null || original instanceof WordBase) { + if (constant.isNull() || metaAccess.isInstanceOf(constant, WordBase.class)) { return; } - if (original instanceof Class) { - throw VMError.shouldNotReachHere("Must not have Class in native image heap: " + original); - } - if (original instanceof DynamicHub && ((DynamicHub) original).getClassInitializationInfo() == null) { - /* - * All DynamicHub instances written into the image heap must have a - * ClassInitializationInfo, otherwise we can get a NullPointerException at run time. - * When this check fails, then the DynamicHub has not been seen during static analysis. - * Since many other objects are reachable from the DynamicHub (annotations, enum values, - * ...) this can also mean that types are used in the image that the static analysis has - * not seen - so this check actually protects against much more than just missing class - * initialization information. - */ - throw reportIllegalType(original, reason); + + if (metaAccess.isInstanceOf(constant, Class.class)) { + Object original = SubstrateObjectConstant.asObject(constant); + if (original instanceof Class) { + throw VMError.shouldNotReachHere("Must not have Class in native image heap: " + original); + } + assert original instanceof DynamicHub; + if (((DynamicHub) original).getClassInitializationInfo() == null) { + /* + * All DynamicHub instances written into the image heap must have a + * ClassInitializationInfo, otherwise we can get a NullPointerException at run time. + * When this check fails, then the DynamicHub has not been seen during static + * analysis. Since many other objects are reachable from the DynamicHub + * (annotations, enum values, ...) this can also mean that types are used in the + * image that the static analysis has not seen - so this check actually protects + * against much more than just missing class initialization information. + */ + throw reportIllegalType(original, reason); + } } - int identityHashCode = SubstrateObjectConstant.computeIdentityHashCode(original); + JavaConstant uncompressed = uncompress(constant); + + int identityHashCode = computeIdentityHashCode(uncompressed); VMError.guarantee(identityHashCode != 0, "0 is used as a marker value for 'hash code not yet computed'"); - if (original instanceof String) { - handleImageString((String) original); + if (metaAccess.isInstanceOf(uncompressed, String.class)) { + handleImageString((String) SubstrateObjectConstant.asObject(uncompressed)); } - final ObjectInfo existing = objects.get(original); + final ObjectInfo existing = objects.get(uncompressed); if (existing == null) { - addObjectToImageHeap(original, immutableFromParent, identityHashCode, reason); + addObjectToImageHeap(uncompressed, immutableFromParent, identityHashCode, reason); + } + } + + /** + * The constants stored in the image heap, i.g., the {@link #objects} map, are always + * uncompressed. The same object info is returned whenever the map is queried regardless of the + * compressed flag value. + */ + private static JavaConstant uncompress(JavaConstant constant) { + if (constant instanceof CompressibleConstant) { + CompressibleConstant compressible = (CompressibleConstant) constant; + if (compressible.isCompressed()) { + return compressible.uncompress(); + } + } + return constant; + } + + private static boolean isCompressed(JavaConstant constant) { + if (constant instanceof CompressibleConstant) { + CompressibleConstant compressible = (CompressibleConstant) constant; + return compressible.isCompressed(); + } + return false; + } + + private static int computeIdentityHashCode(JavaConstant constant) { + if (constant instanceof ImageHeapConstant) { + /* + * For ImageHeapConstant we use the identity hash code of the constant object itself as + * there is no hosted object. The compressed flag changes the identity o an + * ImageHeapConstant, but at this point we shouldn't encounter any compressed constants. + */ + VMError.guarantee(!((ImageHeapConstant) constant).isCompressed(), constant + ""); + return System.identityHashCode(constant); + } else { + return SubstrateObjectConstant.computeIdentityHashCode(SubstrateObjectConstant.asObject(constant)); } } @@ -300,7 +358,7 @@ public void addObject(final Object original, boolean immutableFromParent, final public int countDynamicHubs() { int count = 0; for (ObjectInfo o : getObjects()) { - if (o.getObject() instanceof DynamicHub) { + if (metaAccess.isInstanceOf(o.getConstant(), DynamicHub.class)) { count++; } } @@ -362,14 +420,13 @@ private static void forceHashCodeComputation(final String str) { * This is the mechanics of recursively adding the object and all its fields and array elements * to the model of the native image heap. */ - private void addObjectToImageHeap(final Object object, boolean immutableFromParent, final int identityHashCode, final Object reason) { + private void addObjectToImageHeap(final JavaConstant constant, boolean immutableFromParent, final int identityHashCode, final Object reason) { - final Optional optionalType = getMetaAccess().optionalLookupJavaType(object.getClass()); - final HostedType type = requireType(optionalType, object, reason); + final HostedType type = metaAccess.lookupJavaType(constant); final DynamicHub hub = type.getHub(); final ObjectInfo info; - boolean immutable = immutableFromParent || isKnownImmutable(object); + boolean immutable = immutableFromParent || isKnownImmutableConstant(constant); boolean written = false; boolean references = false; boolean relocatable = false; /* always false when !spawnIsolates() */ @@ -383,7 +440,6 @@ private void addObjectToImageHeap(final Object object, boolean immutableFromPare // also not immutable: users of registerAsImmutable() must take precautions } - final JavaConstant con = SubstrateObjectConstant.forObject(object); HostedField hybridTypeIDSlotsField = null; HostedField hybridArrayField = null; Object hybridArray = null; @@ -405,14 +461,14 @@ private void addObjectToImageHeap(final Object object, boolean immutableFromPare boolean shouldBlacklist = !HybridLayout.canHybridFieldsBeDuplicated(clazz); hybridTypeIDSlotsField = hybridLayout.getTypeIDSlotsField(); if (hybridTypeIDSlotsField != null && shouldBlacklist) { - Object typeIDSlots = readObjectField(hybridTypeIDSlotsField, con); + Object typeIDSlots = readObjectField(hybridTypeIDSlotsField, constant); if (typeIDSlots != null) { blacklist.add(typeIDSlots); } } hybridArrayField = hybridLayout.getArrayField(); - hybridArray = readObjectField(hybridArrayField, con); + hybridArray = readObjectField(hybridArrayField, constant); if (hybridArray != null && shouldBlacklist) { blacklist.add(hybridArray); written = true; @@ -424,11 +480,11 @@ private void addObjectToImageHeap(final Object object, boolean immutableFromPare size = LayoutEncoding.getPureInstanceSize(hub.getLayoutEncoding()).rawValue(); } - info = addToImageHeap(object, clazz, size, identityHashCode, reason); + info = addToImageHeap(constant, clazz, size, identityHashCode, reason); try { recursiveAddObject(hub, false, info); // Recursively add all the fields of the object. - final boolean fieldsAreImmutable = object instanceof String; + final boolean fieldsAreImmutable = metaAccess.isInstanceOf(constant, String.class); for (HostedField field : clazz.getInstanceFields(true)) { boolean fieldRelocatable = false; if (field.isRead() && @@ -436,13 +492,12 @@ private void addObjectToImageHeap(final Object object, boolean immutableFromPare !field.equals(hybridTypeIDSlotsField)) { if (field.getJavaKind() == JavaKind.Object) { assert field.hasLocation(); - JavaConstant fieldValueConstant = field.readValue(con); + JavaConstant fieldValueConstant = field.readValue(constant); if (fieldValueConstant.getJavaKind() == JavaKind.Object) { - Object fieldValue = SubstrateObjectConstant.asObject(fieldValueConstant); if (spawnIsolates()) { - fieldRelocatable = fieldValue instanceof RelocatedPointer; + fieldRelocatable = metaAccess.isInstanceOf(fieldValueConstant, RelocatedPointer.class); } - recursiveAddObject(fieldValue, fieldsAreImmutable, info); + recursiveAddConstant(fieldValueConstant, fieldsAreImmutable, info); references = true; } } @@ -465,12 +520,18 @@ private void addObjectToImageHeap(final Object object, boolean immutableFromPare } else if (type.isArray()) { HostedArrayClass clazz = (HostedArrayClass) type; - final long size = objectLayout.getArraySize(type.getComponentType().getStorageKind(), Array.getLength(object)); - info = addToImageHeap(object, clazz, size, identityHashCode, reason); + int length = universe.getConstantReflectionProvider().readArrayLength(constant); + final long size = objectLayout.getArraySize(type.getComponentType().getStorageKind(), length); + info = addToImageHeap(constant, clazz, size, identityHashCode, reason); try { recursiveAddObject(hub, false, info); - if (object instanceof Object[]) { - relocatable = addArrayElements((Object[]) object, false, info); + if (metaAccess.isInstanceOf(constant, Object[].class)) { + if (constant instanceof ImageHeapConstant) { + relocatable = addConstantArrayElements(constant, length, false, info); + } else { + Object object = SubstrateObjectConstant.asObject(constant); + relocatable = addArrayElements((Object[]) object, false, info); + } references = true; } written = true; /* How to know if any of the array elements are written? */ @@ -482,8 +543,8 @@ private void addObjectToImageHeap(final Object object, boolean immutableFromPare throw shouldNotReachHere(); } - if (relocatable && !isKnownImmutable(object)) { - VMError.shouldNotReachHere("Object with relocatable pointers must be explicitly immutable: " + object); + if (relocatable && !isKnownImmutableConstant(constant)) { + VMError.shouldNotReachHere("Object with relocatable pointers must be explicitly immutable: " + SubstrateObjectConstant.asObject(constant)); } heapLayouter.assignObjectToPartition(info, !written || immutable, references, relocatable); } @@ -523,6 +584,15 @@ private static StringBuilder fillReasonStack(StringBuilder msg, Object reason) { return msg.append(" root: ").append(reason).append(System.lineSeparator()); } + /** Determine if a constant will be immutable in the native image heap. */ + private boolean isKnownImmutableConstant(final JavaConstant constant) { + if (constant instanceof ImageHeapConstant) { + /* Currently injected ImageHeapObject cannot be marked as immutable. */ + return false; + } + return isKnownImmutable(SubstrateObjectConstant.asObject(constant)); + } + /** Determine if an object in the host heap will be immutable in the native image heap. */ private boolean isKnownImmutable(final Object obj) { if (obj instanceof String) { @@ -536,9 +606,13 @@ private boolean isKnownImmutable(final Object obj) { /** Add an object to the model of the native image heap. */ private ObjectInfo addToImageHeap(Object object, HostedClass clazz, long size, int identityHashCode, Object reason) { - ObjectInfo info = new ObjectInfo(object, size, clazz, identityHashCode, reason); - assert !objects.containsKey(object); - objects.put(object, info); + return addToImageHeap(SubstrateObjectConstant.forObject(object), clazz, size, identityHashCode, reason); + } + + private ObjectInfo addToImageHeap(JavaConstant constant, HostedClass clazz, long size, int identityHashCode, Object reason) { + ObjectInfo info = new ObjectInfo(constant, size, clazz, identityHashCode, reason); + assert !objects.containsKey(constant) && !isCompressed(constant); + objects.put(constant, info); return info; } @@ -582,32 +656,51 @@ private boolean addArrayElements(Object[] array, boolean otherFieldsRelocatable, return relocatable; } + private boolean addConstantArrayElements(JavaConstant array, int length, boolean otherFieldsRelocatable, Object reason) { + boolean relocatable = otherFieldsRelocatable; + for (int idx = 0; idx < length; idx++) { + JavaConstant value = universe.getConstantReflectionProvider().readArrayElement(array, idx); + /* Object replacement is done as part as constant refection. */ + if (spawnIsolates()) { + relocatable = relocatable || metaAccess.isInstanceOf(value, RelocatedPointer.class); + } + recursiveAddConstant(value, false, reason); + } + return relocatable; + } + /* * Break recursion using a worklist, to support large object graphs that would lead to a stack * overflow. */ private void recursiveAddObject(Object original, boolean immutableFromParent, Object reason) { if (original != null) { - addObjectWorklist.push(new AddObjectData(original, immutableFromParent, reason)); + addObjectWorklist.push(new AddObjectData(SubstrateObjectConstant.forObject(original), immutableFromParent, reason)); + } + } + + private void recursiveAddConstant(JavaConstant constant, boolean immutableFromParent, Object reason) { + if (constant.isNonNull()) { + addObjectWorklist.push(new AddObjectData(constant, immutableFromParent, reason)); } } private void processAddObjectWorklist() { while (!addObjectWorklist.isEmpty()) { AddObjectData data = addObjectWorklist.pop(); - addObject(data.original, data.immutableFromParent, data.reason); + addConstant(data.original, data.immutableFromParent, data.reason); } } static class AddObjectData { - AddObjectData(Object original, boolean immutableFromParent, Object reason) { + AddObjectData(JavaConstant original, boolean immutableFromParent, Object reason) { this.original = original; this.immutableFromParent = immutableFromParent; this.reason = reason; } - final Object original; + final JavaConstant original; final boolean immutableFromParent; final Object reason; } @@ -615,7 +708,7 @@ static class AddObjectData { private final int imageHeapOffsetInAddressSpace = Heap.getHeap().getImageHeapOffsetInAddressSpace(); public final class ObjectInfo implements ImageHeapObject { - private final Object object; + private final JavaConstant constant; private final HostedClass clazz; private final long size; private final int identityHashCode; @@ -631,7 +724,11 @@ public final class ObjectInfo implements ImageHeapObject { final Object reason; ObjectInfo(Object object, long size, HostedClass clazz, int identityHashCode, Object reason) { - this.object = object; + this(SubstrateObjectConstant.forObject(object), size, clazz, identityHashCode, reason); + } + + ObjectInfo(JavaConstant constant, long size, HostedClass clazz, int identityHashCode, Object reason) { + this.constant = constant; this.clazz = clazz; this.partition = null; this.offsetInPartition = -1L; @@ -642,7 +739,17 @@ public final class ObjectInfo implements ImageHeapObject { @Override public Object getObject() { - return object; + return SubstrateObjectConstant.asObject(constant); + } + + @Override + public Class getObjectClass() { + return clazz.getJavaClass(); + } + + @Override + public JavaConstant getConstant() { + return constant; } public HostedClass getClazz() { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java index f496056c67c1..023f361dcae2 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java @@ -30,6 +30,7 @@ import java.lang.reflect.Modifier; import java.nio.ByteBuffer; +import com.oracle.graal.pointsto.heap.ImageHeapConstant; import org.graalvm.compiler.core.common.CompressEncoding; import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.debug.DebugContext; @@ -51,6 +52,8 @@ import com.oracle.svm.core.image.ImageHeapLayoutInfo; import com.oracle.svm.core.meta.MethodPointer; import com.oracle.svm.core.meta.SubstrateObjectConstant; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.ameta.AnalysisConstantReflectionProvider; import com.oracle.svm.hosted.config.HybridLayout; import com.oracle.svm.hosted.image.NativeImageHeap.ObjectInfo; import com.oracle.svm.hosted.meta.HostedClass; @@ -84,7 +87,7 @@ public NativeImageHeapWriter(NativeImageHeap heap, ImageHeapLayoutInfo heapLayou public long writeHeap(DebugContext debug, RelocatableBuffer buffer) { try (Indent perHeapIndent = debug.logAndIndent("NativeImageHeap.writeHeap:")) { for (ObjectInfo info : heap.getObjects()) { - assert !heap.isBlacklisted(info.getObject()); + assert !(info.getConstant() instanceof SubstrateObjectConstant) || !heap.isBlacklisted(info.getObject()); writeObject(info, buffer); } @@ -142,7 +145,7 @@ private void writeField(RelocatableBuffer buffer, ObjectInfo fields, HostedField throw NativeImageHeap.reportIllegalType(ex.getType(), info); } - if (value.getJavaKind() == JavaKind.Object && SubstrateObjectConstant.asObject(value) instanceof RelocatedPointer) { + if (value.getJavaKind() == JavaKind.Object && heap.getMetaAccess().isInstanceOf(value, RelocatedPointer.class)) { addNonDataRelocation(buffer, index, (RelocatedPointer) SubstrateObjectConstant.asObject(value)); } else { write(buffer, index, value, info != null ? info : field); @@ -151,7 +154,7 @@ private void writeField(RelocatableBuffer buffer, ObjectInfo fields, HostedField private void write(RelocatableBuffer buffer, int index, JavaConstant con, Object reason) { if (con.getJavaKind() == JavaKind.Object) { - writeReference(buffer, index, SubstrateObjectConstant.asObject(con), reason); + writeReference(buffer, index, con, reason); } else { writePrimitive(buffer, index, con); } @@ -160,21 +163,39 @@ private void write(RelocatableBuffer buffer, int index, JavaConstant con, Object private final boolean useHeapBase = NativeImageHeap.useHeapBase(); private final CompressEncoding compressEncoding = ImageSingletons.lookup(CompressEncoding.class); - void writeReference(RelocatableBuffer buffer, int index, Object target, Object reason) { - assert !(target instanceof WordBase) : "word values are not references"; + void writeReference(RelocatableBuffer buffer, int index, JavaConstant target, Object reason) { + assert !(heap.getMetaAccess().isInstanceOf(target, WordBase.class)) : "word values are not references"; mustBeReferenceAligned(index); - if (target != null) { - ObjectInfo targetInfo = heap.getObjectInfo(target); + if (target.isNonNull()) { + ObjectInfo targetInfo = heap.getConstantInfo(target); verifyTargetDidNotChange(target, reason, targetInfo); if (useHeapBase) { int shift = compressEncoding.getShift(); writeReferenceValue(buffer, index, targetInfo.getAddress() >>> shift); } else { - addDirectRelocationWithoutAddend(buffer, index, referenceSize(), target); + addDirectRelocationWithoutAddend(buffer, index, referenceSize(), SubstrateObjectConstant.asObject(target)); } } } + private void writeConstant(RelocatableBuffer buffer, int index, JavaKind kind, JavaConstant constant, ObjectInfo info) { + if (heap.getMetaAccess().isInstanceOf(constant, RelocatedPointer.class)) { + addNonDataRelocation(buffer, index, (RelocatedPointer) SubstrateObjectConstant.asObject(constant)); + return; + } + + final JavaConstant con; + if (heap.getMetaAccess().isInstanceOf(constant, WordBase.class)) { + Object value = heap.getUniverse().getSnippetReflection().asObject(Object.class, constant); + con = JavaConstant.forIntegerKind(FrameAccess.getWordKind(), ((WordBase) value).rawValue()); + } else if (constant.isNull() && kind == FrameAccess.getWordKind()) { + con = JavaConstant.forIntegerKind(FrameAccess.getWordKind(), 0); + } else { + con = constant; + } + write(buffer, index, con, info); + } + private void writeConstant(RelocatableBuffer buffer, int index, JavaKind kind, Object value, ObjectInfo info) { if (value instanceof RelocatedPointer) { addNonDataRelocation(buffer, index, (RelocatedPointer) value); @@ -297,7 +318,7 @@ private void writeObject(ObjectInfo info, RelocatableBuffer buffer) { ByteBuffer bufferBytes = buffer.getByteBuffer(); HostedClass clazz = info.getClazz(); if (clazz.isInstanceClass()) { - JavaConstant con = SubstrateObjectConstant.forObject(info.getObject()); + JavaConstant con = info.getConstant(); HybridLayout hybridLayout = heap.getHybridLayout(clazz); HostedField hybridArrayField = null; @@ -355,36 +376,54 @@ private void writeObject(ObjectInfo info, RelocatableBuffer buffer) { } } else if (clazz.isArray()) { - JavaKind kind = clazz.getComponentType().getStorageKind(); - Object array = info.getObject(); - int length = Array.getLength(array); - bufferBytes.putInt(info.getIndexInBuffer(objectLayout.getArrayLengthOffset()), length); - bufferBytes.putInt(info.getIndexInBuffer(objectLayout.getIdentityHashCodeOffset()), info.getIdentityHashCode()); - if (array instanceof Object[]) { - Object[] oarray = (Object[]) array; - assert oarray.length == length; - for (int i = 0; i < length; i++) { - final int elementIndex = info.getIndexInBuffer(objectLayout.getArrayElementOffset(kind, i)); - Object element; - try { - element = heap.getAnalysisUniverse().replaceObject(oarray[i]); - } catch (AnalysisError.TypeNotFoundError ex) { - throw NativeImageHeap.reportIllegalType(ex.getType(), info); - } - assert (oarray[i] instanceof RelocatedPointer) == (element instanceof RelocatedPointer); - writeConstant(buffer, elementIndex, kind, element, info); + JavaKind kind = clazz.getComponentType().getStorageKind(); + JavaConstant constant = info.getConstant(); + if (constant instanceof ImageHeapConstant) { + if (!clazz.getComponentType().isPrimitive()) { + AnalysisConstantReflectionProvider constantReflection = heap.getUniverse().getConstantReflectionProvider(); + int length = constantReflection.readArrayLength(constant); + bufferBytes.putInt(info.getIndexInBuffer(objectLayout.getArrayLengthOffset()), length); + bufferBytes.putInt(info.getIndexInBuffer(objectLayout.getIdentityHashCodeOffset()), info.getIdentityHashCode()); + constantReflection.forEachArrayElement(constant, (element, index) -> { + final int elementIndex = info.getIndexInBuffer(objectLayout.getArrayElementOffset(kind, index)); + writeConstant(buffer, elementIndex, kind, element, info); + }); + } else { + throw VMError.shouldNotReachHere("Heap writing for primitive type ImageHeapArray not yet implemented."); } } else { - int elementIndex = info.getIndexInBuffer(objectLayout.getArrayElementOffset(kind, 0)); - int elementTypeSize = Unsafe.getUnsafe().arrayIndexScale(array.getClass()); - assert elementTypeSize == kind.getByteCount(); - Unsafe.getUnsafe().copyMemory(array, Unsafe.getUnsafe().arrayBaseOffset(array.getClass()), buffer.getBackingArray(), Unsafe.ARRAY_BYTE_BASE_OFFSET + elementIndex, - length * elementTypeSize); + Object array = info.getObject(); + int length = Array.getLength(array); + bufferBytes.putInt(info.getIndexInBuffer(objectLayout.getArrayLengthOffset()), length); + bufferBytes.putInt(info.getIndexInBuffer(objectLayout.getIdentityHashCodeOffset()), info.getIdentityHashCode()); + if (array instanceof Object[]) { + Object[] oarray = (Object[]) array; + assert oarray.length == length; + for (int i = 0; i < length; i++) { + final int elementIndex = info.getIndexInBuffer(objectLayout.getArrayElementOffset(kind, i)); + Object element = maybeReplace(oarray[i], info); + assert (oarray[i] instanceof RelocatedPointer) == (element instanceof RelocatedPointer); + writeConstant(buffer, elementIndex, kind, element, info); + } + } else { + int elementIndex = info.getIndexInBuffer(objectLayout.getArrayElementOffset(kind, 0)); + int elementTypeSize = Unsafe.getUnsafe().arrayIndexScale(array.getClass()); + assert elementTypeSize == kind.getByteCount(); + Unsafe.getUnsafe().copyMemory(array, Unsafe.getUnsafe().arrayBaseOffset(array.getClass()), buffer.getBackingArray(), + Unsafe.ARRAY_BYTE_BASE_OFFSET + elementIndex, length * elementTypeSize); + } } - } else { throw shouldNotReachHere(); } } + + private Object maybeReplace(Object object, Object reason) { + try { + return heap.getAnalysisUniverse().replaceObject(object); + } catch (AnalysisError.TypeNotFoundError ex) { + throw NativeImageHeap.reportIllegalType(ex.getType(), reason); + } + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/ObjectGroupHistogram.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/ObjectGroupHistogram.java index b657820b92fb..5e89608f7522 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/ObjectGroupHistogram.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/ObjectGroupHistogram.java @@ -113,9 +113,9 @@ private void doPrint() { try { Field field = Class.forName("com.oracle.svm.graal.SubstrateRuntimeProvider").getDeclaredField("graphObjects"); - Object object = SubstrateObjectConstant.asObject(heap.getMetaAccess().lookupJavaField(field).readValue(null)); - processObject(heap.getObjectInfo(object), "CompressedGraphObjects", true, null, ObjectGroupHistogram::filterObjectConstantField); - } catch (Throwable ex) { + JavaConstant fieldValue = heap.getMetaAccess().lookupJavaField(field).readValue(null); + processObject(heap.getConstantInfo(fieldValue), "CompressedGraphObjects", true, 1, null, ObjectGroupHistogram::filterObjectConstantField); + } catch (LinkageError | ClassNotFoundException | NoSuchFieldException ex) { /* Ignore. When we build an image without Graal support, the class is not present. */ } @@ -181,9 +181,9 @@ private void processObject(ObjectInfo info, String group, boolean addObject, int for (HostedField field : info.getClazz().getInstanceFields(true)) { if (field.getType().getStorageKind() == JavaKind.Object && !HybridLayout.isHybridField(field) && field.isAccessed()) { if (fieldFilter == null || fieldFilter.test(info, field)) { - Object fieldValue = SubstrateObjectConstant.asObject(field.readStorageValue(con)); - if (fieldValue != null) { - processObject(heap.getObjectInfo(fieldValue), group, true, recursionLevel + 1, objectFilter, fieldFilter); + JavaConstant fieldValue = field.readStorageValue(con); + if (fieldValue.isNonNull()) { + processObject(heap.getConstantInfo(fieldValue), group, true, recursionLevel + 1, objectFilter, fieldFilter); } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedConstantReflectionProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedConstantReflectionProvider.java index 1237efc27fd1..465659f7276a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedConstantReflectionProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedConstantReflectionProvider.java @@ -24,9 +24,13 @@ */ package com.oracle.svm.hosted.meta; +import java.util.function.ObjIntConsumer; + import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import com.oracle.graal.pointsto.heap.ImageHeapConstant; +import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess; import com.oracle.svm.core.graal.meta.SharedConstantReflectionProvider; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.meta.SubstrateObjectConstant; @@ -43,11 +47,13 @@ public class HostedConstantReflectionProvider extends SharedConstantReflectionProvider { private final SVMHost hostVM; private final HostedUniverse universe; + private final UniverseMetaAccess metaAccess; private final HostedMemoryAccessProvider memoryAccess; - public HostedConstantReflectionProvider(SVMHost hostVM, HostedUniverse universe, HostedMemoryAccessProvider memoryAccess) { + public HostedConstantReflectionProvider(SVMHost hostVM, HostedUniverse universe, UniverseMetaAccess metaAccess, HostedMemoryAccessProvider memoryAccess) { this.hostVM = hostVM; this.universe = universe; + this.metaAccess = metaAccess; this.memoryAccess = memoryAccess; } @@ -66,6 +72,11 @@ public ResolvedJavaType asJavaType(Constant constant) { throw VMError.shouldNotReachHere("Must not have java.lang.Class object: " + obj); } } + if (constant instanceof ImageHeapConstant) { + if (metaAccess.isInstanceOf((JavaConstant) constant, Class.class)) { + throw VMError.shouldNotReachHere("ConstantReflectionProvider.asJavaType(Constant) not yet implemented for ImageHeapObject"); + } + } return null; } @@ -74,6 +85,24 @@ public JavaConstant asJavaClass(ResolvedJavaType type) { return SubstrateObjectConstant.forObject(hostVM.dynamicHub(((HostedType) type).wrapped)); } + @Override + public Integer readArrayLength(JavaConstant array) { + /* Delegate to the AnalysisConstantReflectionProvider. */ + return universe.getConstantReflectionProvider().readArrayLength(array); + } + + @Override + public JavaConstant readArrayElement(JavaConstant array, int index) { + /* Delegate to the AnalysisConstantReflectionProvider. */ + return universe.getConstantReflectionProvider().readArrayElement(array, index); + } + + @Override + public void forEachArrayElement(JavaConstant array, ObjIntConsumer consumer) { + /* Delegate to the AnalysisConstantReflectionProvider. */ + universe.getConstantReflectionProvider().forEachArrayElement(array, consumer); + } + @Override public JavaConstant readFieldValue(ResolvedJavaField field, JavaConstant receiver) { return ((HostedField) field).readValue(receiver); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedField.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedField.java index 0fdfd06d7f30..79284cc72585 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedField.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedField.java @@ -30,8 +30,10 @@ import com.oracle.graal.pointsto.infrastructure.OriginalFieldProvider; import com.oracle.graal.pointsto.infrastructure.WrappedJavaField; import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.meta.SharedField; import com.oracle.svm.core.meta.SubstrateObjectConstant; +import com.oracle.svm.core.util.VMError; import com.oracle.svm.util.AnnotationWrapper; import jdk.vm.ci.meta.JavaConstant; @@ -152,9 +154,14 @@ public int hashCode() { public JavaConstant readValue(JavaConstant receiver) { JavaConstant wrappedReceiver; - if (receiver != null && SubstrateObjectConstant.asObject(receiver) instanceof Class) { - /* Manual object replacement from java.lang.Class to DynamicHub. */ - wrappedReceiver = SubstrateObjectConstant.forObject(metaAccess.lookupJavaType((Class) SubstrateObjectConstant.asObject(receiver)).getHub()); + if (metaAccess.isInstanceOf(receiver, Class.class)) { + Object classObject = SubstrateObjectConstant.asObject(receiver); + if (classObject instanceof Class) { + throw VMError.shouldNotReachHere("Receiver " + receiver + " of field " + this + " read should not be java.lang.Class. Expecting to see DynamicHub here."); + } else { + VMError.guarantee(classObject instanceof DynamicHub); + wrappedReceiver = receiver; + } } else { wrappedReceiver = receiver; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMemoryAccessProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMemoryAccessProvider.java index 4d8f9253ef96..877ae92c3c52 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMemoryAccessProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMemoryAccessProvider.java @@ -98,7 +98,7 @@ private JavaConstant doRead(JavaKind stackKind, JavaConstant base, long displace if (base.getJavaKind() != JavaKind.Object) { return null; } - HostedType type = (HostedType) metaAccess.lookupJavaType(base); + HostedType type = metaAccess.lookupJavaType(base); HostedField field = (HostedField) type.findInstanceFieldWithOffset(displacement, null); if (field == null) { return null; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMetaAccess.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMetaAccess.java index 51ad8431f7a6..74f6ee8ee5fb 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMetaAccess.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMetaAccess.java @@ -54,6 +54,11 @@ public HostedType lookupJavaType(Class clazz) { return (HostedType) super.lookupJavaType(clazz); } + @Override + public HostedType lookupJavaType(JavaConstant constant) { + return (HostedType) super.lookupJavaType(constant); + } + public Optional optionalLookupJavaType(Class clazz) { HostedType result = (HostedType) getTypeCacheEntry(clazz); if (result != null) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/SharedConstantFieldProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/SharedConstantFieldProvider.java index 998dd48728ff..2147e7acaac1 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/SharedConstantFieldProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/SharedConstantFieldProvider.java @@ -28,8 +28,8 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess; import com.oracle.svm.core.meta.MethodPointer; -import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; import jdk.vm.ci.meta.JavaConstant; @@ -41,10 +41,12 @@ public abstract class SharedConstantFieldProvider extends JavaConstantFieldProvider { protected final ClassInitializationSupport classInitializationSupport; + protected final UniverseMetaAccess metaAccess; public SharedConstantFieldProvider(MetaAccessProvider metaAccess, ClassInitializationSupport classInitializationSupport) { super(metaAccess); this.classInitializationSupport = classInitializationSupport; + this.metaAccess = (UniverseMetaAccess) metaAccess; } @Override @@ -57,7 +59,7 @@ public boolean isFinalField(ResolvedJavaField field, ConstantFieldTool tool) @Override protected boolean isFinalFieldValueConstant(ResolvedJavaField field, JavaConstant value, ConstantFieldTool tool) { - if (value.getJavaKind() == JavaKind.Object && SubstrateObjectConstant.asObject(value) instanceof MethodPointer) { + if (value.getJavaKind() == JavaKind.Object && metaAccess.isInstanceOf(value, MethodPointer.class)) { /* * Prevent the constant folding of MethodPointer objects. MethodPointer is a "hosted" * type, so it cannot be present in compiler graphs. diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/IntrinsifyMethodHandlesInvocationPlugin.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/IntrinsifyMethodHandlesInvocationPlugin.java index 9fe54bf716a5..cc45bde4a90f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/IntrinsifyMethodHandlesInvocationPlugin.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/IntrinsifyMethodHandlesInvocationPlugin.java @@ -36,7 +36,6 @@ import java.util.Objects; import java.util.stream.StreamSupport; -import com.oracle.svm.hosted.meta.HostedType; import org.graalvm.collections.Pair; import org.graalvm.collections.UnmodifiableEconomicMap; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; @@ -102,7 +101,9 @@ import org.graalvm.compiler.word.WordOperationPlugin; import org.graalvm.nativeimage.ImageSingletons; +import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisUniverse; +import com.oracle.graal.pointsto.meta.HostedProviders; import com.oracle.graal.pointsto.phases.NoClassInitializationPlugin; import com.oracle.graal.pointsto.util.GraalAccess; import com.oracle.svm.core.FrameAccess; @@ -114,6 +115,7 @@ import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.NativeImageUtil; import com.oracle.svm.hosted.SVMHost; +import com.oracle.svm.hosted.meta.HostedType; import com.oracle.svm.hosted.meta.HostedUniverse; import com.oracle.svm.hosted.snippets.IntrinsificationPluginRegistry; import com.oracle.svm.util.ReflectionUtil; @@ -179,7 +181,7 @@ public static class IntrinsificationRegistry extends IntrinsificationPluginRegis private final ParsingReason reason; private final Providers parsingProviders; - private final Providers universeProviders; + private final HostedProviders universeProviders; private final AnalysisUniverse aUniverse; private final HostedUniverse hUniverse; @@ -190,7 +192,7 @@ public static class IntrinsificationRegistry extends IntrinsificationPluginRegis private final ResolvedJavaType methodHandleType; private final ResolvedJavaType varHandleType; - public IntrinsifyMethodHandlesInvocationPlugin(ParsingReason reason, Providers providers, AnalysisUniverse aUniverse, HostedUniverse hUniverse) { + public IntrinsifyMethodHandlesInvocationPlugin(ParsingReason reason, HostedProviders providers, AnalysisUniverse aUniverse, HostedUniverse hUniverse) { this.reason = reason; this.aUniverse = aUniverse; this.hUniverse = hUniverse; @@ -280,9 +282,10 @@ private static ValueNode filterNullPhiInputs(PhiNode phi) { return notAlwaysNullPhiInput; } - private static boolean hasMethodHandleArgument(ValueNode[] args) { + private boolean hasMethodHandleArgument(ValueNode[] args) { for (ValueNode argument : args) { - if (argument.isConstant() && argument.getStackKind() == JavaKind.Object && SubstrateObjectConstant.asObject(argument.asJavaConstant()) instanceof MethodHandle) { + if (argument.isConstant() && argument.getStackKind() == JavaKind.Object && + (((UniverseMetaAccess) universeProviders.getMetaAccess()).isInstanceOf(argument.asJavaConstant(), methodHandleType))) { return true; } }