Skip to content

Commit

Permalink
[GR-60740] [GR-61079] Refactor field inclusion policy in base layer a…
Browse files Browse the repository at this point in the history
…nd improve name checks mechanism.

PullRequest: graal/19767
  • Loading branch information
cstancu committed Jan 18, 2025
2 parents 0463e0e + 41c8156 commit bbdf41b
Show file tree
Hide file tree
Showing 12 changed files with 154 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.oracle.graal.pointsto.util.AnalysisError;
import com.oracle.graal.pointsto.util.AnalysisFuture;
import com.oracle.graal.pointsto.util.AtomicUtils;
import com.oracle.svm.common.meta.GuaranteeFolded;

import jdk.graal.compiler.debug.GraalError;
import jdk.vm.ci.code.BytecodePosition;
Expand Down Expand Up @@ -217,6 +218,7 @@ public void cleanupAfterAnalysis() {
}

public boolean registerAsAccessed(Object reason) {
checkGuaranteeFolded();
getDeclaringClass().registerAsReachable(this);

assert isValidReason(reason) : "Registering a field as accessed needs to provide a valid reason.";
Expand All @@ -231,6 +233,7 @@ public boolean registerAsAccessed(Object reason) {
* @param reason the reason why this field is read, non-null
*/
public boolean registerAsRead(Object reason) {
checkGuaranteeFolded();
getDeclaringClass().registerAsReachable(this);

assert isValidReason(reason) : "Registering a field as read needs to provide a valid reason.";
Expand All @@ -250,6 +253,7 @@ public boolean registerAsRead(Object reason) {
* @param reason the reason why this field is written, non-null
*/
public boolean registerAsWritten(Object reason) {
checkGuaranteeFolded();
getDeclaringClass().registerAsReachable(this);

assert isValidReason(reason) : "Registering a field as written needs to provide a valid reason.";
Expand All @@ -264,6 +268,14 @@ public boolean registerAsWritten(Object reason) {
});
}

public boolean isGuaranteeFolded() {
return getAnnotation(GuaranteeFolded.class) != null;
}

public void checkGuaranteeFolded() {
AnalysisError.guarantee(!isGuaranteeFolded(), "A field that is guaranteed to always be folded is seen as accessed: %s. ", this);
}

public void registerAsFolded(Object reason) {
getDeclaringClass().registerAsReachable(this);

Expand All @@ -275,6 +287,7 @@ public void registerAsFolded(Object reason) {
}

public boolean registerAsUnsafeAccessed(Object reason) {
checkGuaranteeFolded();
assert isValidReason(reason) : "Registering a field as unsafe accessed needs to provide a valid reason.";
registerAsAccessed(reason);
/*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2025, 2025, 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.svm.common.meta;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Verifies that loads of the annotated field are always folded in run time code. This annotation
* doesn't influence the folding logic itself, it just ensures that the annotated fields are not
* present in the image.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface GuaranteeFolded {
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,21 @@

import org.graalvm.collections.EconomicMap;
import org.graalvm.collections.EconomicSet;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;

import com.oracle.svm.util.ClassUtil;
import com.oracle.svm.util.StringUtil;

import jdk.graal.compiler.options.EnumMultiOptionKey;
import jdk.graal.compiler.options.OptionDescriptor;
import jdk.graal.compiler.options.OptionDescriptors;
import jdk.graal.compiler.options.OptionKey;
import jdk.graal.compiler.options.OptionType;
import jdk.graal.compiler.options.OptionsParser;

import com.oracle.svm.util.ClassUtil;
import com.oracle.svm.util.StringUtil;

public class CommonOptionParser {
@Platforms(Platform.HOSTED_ONLY.class) //
public static final String HOSTED_OPTION_PREFIX = "-H:";
public static final String RUNTIME_OPTION_PREFIX = "-R:";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.graalvm.word.Pointer;
import org.graalvm.word.UnsignedWord;

import com.oracle.svm.common.meta.GuaranteeFolded;
import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.RecomputeFieldValue;
import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind;
Expand All @@ -69,7 +70,7 @@ public class SubstrateUtil {
/**
* Field that is true during native image generation, but false at run time.
*/
public static final boolean HOSTED;
@GuaranteeFolded public static final boolean HOSTED;

static {
/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
public abstract class SystemPropertiesSupport implements RuntimeSystemPropertiesSupport {

/** System properties that are taken from the VM hosting the image generator. */
@Platforms(Platform.HOSTED_ONLY.class) //
private static final String[] HOSTED_PROPERTIES = {
"java.version",
"java.version.date",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
*/
public class SubstrateOptionsParser {

@Platforms(Platform.HOSTED_ONLY.class) //
public static final String HOSTED_OPTION_PREFIX = CommonOptionParser.HOSTED_OPTION_PREFIX;
public static final String RUNTIME_OPTION_PREFIX = CommonOptionParser.RUNTIME_OPTION_PREFIX;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,20 @@
import java.util.Enumeration;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.graalvm.collections.EconomicSet;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;

import com.oracle.svm.core.BuildPhaseProvider;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.TypeResult;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.util.LogUtils;
import com.oracle.svm.util.ReflectionUtil;

Expand All @@ -62,6 +66,8 @@ public final class ImageClassLoader {
private final EconomicSet<Class<?>> hostedOnlyClasses = EconomicSet.create();
private final EconomicSet<Method> systemMethods = EconomicSet.create();
private final EconomicSet<Field> systemFields = EconomicSet.create();
/** Modules containing all {@code svm.core} and {@code svm.hosted} classes. */
private Set<Module> builderModules;

ImageClassLoader(Platform platform, NativeImageClassLoaderSupport classLoaderSupport) {
this.platform = platform;
Expand Down Expand Up @@ -430,4 +436,17 @@ public EconomicSet<String> packages(URI container) {
public boolean noEntryForURI(EconomicSet<String> set) {
return classLoaderSupport.noEntryForURI(set);
}

public Set<Module> getBuilderModules() {
assert builderModules != null : "Builder modules not yet initialized.";
return builderModules;
}

public void initBuilderModules() {
VMError.guarantee(BuildPhaseProvider.isFeatureRegistrationFinished() && ImageSingletons.contains(VMFeature.class),
"Querying builder modules is only possible after feature registration is finished.");
Module m0 = ImageSingletons.lookup(VMFeature.class).getClass().getModule();
Module m1 = SVMHost.class.getModule();
builderModules = m0.equals(m1) ? Set.of(m0) : Set.of(m0, m1);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.file.FileSystems;
Expand Down Expand Up @@ -941,6 +942,8 @@ protected void setupNativeImage(String imageName, OptionValues options, Map<Meth
featureHandler.registerFeatures(loader, debug);
BuildPhaseProvider.markFeatureRegistrationFinished();

loader.initBuilderModules();

AfterRegistrationAccessImpl access = new AfterRegistrationAccessImpl(featureHandler, loader, originalMetaAccess, mainEntryPoint, debug);
featureHandler.forEachFeature(feature -> feature.afterRegistration(access));
setDefaultLibCIfMissing();
Expand Down Expand Up @@ -1656,17 +1659,17 @@ private void checkUniverse() {
if (SubstrateOptions.VerifyNamingConventions.getValue()) {
for (AnalysisMethod method : aUniverse.getMethods()) {
if ((method.isInvoked() || method.isReachable()) && method.getAnnotation(Fold.class) == null) {
checkName(method.format("%H.%n(%p)"), method, bb);
checkName(bb, method);
}
}
for (AnalysisField field : aUniverse.getFields()) {
if (field.isAccessed()) {
checkName(field.format("%H.%n"), null, bb);
checkName(bb, field);
}
}
for (AnalysisType type : aUniverse.getTypes()) {
if (type.isReachable()) {
checkName(type.toJavaName(true), null, bb);
checkName(bb, type);
}
}
}
Expand Down Expand Up @@ -1698,32 +1701,47 @@ protected void checkForInvalidCallsToEntryPoints() {
// the unsupported features are reported after checkUniverse is invoked
}

public static void checkName(String name, AnalysisMethod method, BigBang bb) {
public static void checkName(BigBang bb, AnalysisMethod method) {
String format = method.format("%H.%n(%p)");
checkName(bb, method, format);
}

public static void checkName(BigBang bb, AnalysisField field) {
String format = field.format("%H.%n");
checkName(bb, null, format);
}

public static void checkName(BigBang bb, Field field) {
String format = field.getType().getName() + "." + field.getName();
checkName(bb, null, format);
}

public static void checkName(BigBang bb, AnalysisType type) {
String format = type.toJavaName(true);
checkName(bb, null, format);
}

private static void checkName(BigBang bb, AnalysisMethod method, String format) {
/*
* We do not want any parts of the native image generator in the generated image. Therefore,
* no element whose name contains "hosted" must be seen as reachable by the static analysis.
* The same holds for "host VM" elements, which come from the hosting VM, unless they are
* JDK internal types.
*/
String message = checkName(name);
if (message != null) {
if (bb != null) {
bb.getUnsupportedFeatures().addMessage(name, method, message);
} else {
throw new UnsupportedFeatureException(message);
}
String lformat = format.toLowerCase(Locale.ROOT);
if (lformat.contains("hosted")) {
report(bb, format, method, "Hosted element used at run time: " + format + ".");
} else if (!lformat.startsWith("jdk.internal") && lformat.contains("hotspot")) {
report(bb, format, method, "HotSpot element used at run time: " + format + ".");
}
}

public static String checkName(String name) {
String lname = name.toLowerCase(Locale.ROOT);
String message = null;
if (lname.contains("hosted")) {
message = "Hosted element used at run time: " + name;
} else if (!name.startsWith("jdk.internal") && lname.contains("hotspot")) {
message = "HotSpot element used at run time: " + name;
private static void report(BigBang bb, String key, AnalysisMethod method, String message) {
if (bb != null) {
bb.getUnsupportedFeatures().addMessage(key, method, message);
} else {
throw new UnsupportedFeatureException(message);
}
return message;
}

@SuppressWarnings("try")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,6 @@ public void beforeUniverseBuilding(BeforeUniverseBuildingAccess access) {
}
}

public Set<Module> getBuilderModules() {
Module m0 = ImageSingletons.lookup(VMFeature.class).getClass().getModule();
Module m1 = SVMHost.class.getModule();
return m0.equals(m1) ? Set.of(m0) : Set.of(m0, m1);
}

private final Set<AnalysisType> triggeredTypes = new HashSet<>();
private final Set<AnalysisMethod> triggeredMethods = new HashSet<>();

Expand Down
Loading

0 comments on commit bbdf41b

Please sign in to comment.