Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ public boolean forRelocatedPointerFieldValue(JavaConstant receiver, AnalysisFiel
if (!field.isWritten()) {
return field.registerAsWritten(reason);
}
if (fieldValue.isNonNull()) {
FieldTypeFlow fieldTypeFlow = getFieldTypeFlow(field, receiver);
return fieldTypeFlow.addState(getAnalysis(), TypeState.anyPrimitiveState());
}
return false;
}

Expand All @@ -71,6 +75,16 @@ public boolean forNonNullFieldValue(JavaConstant receiver, AnalysisField field,
return fieldTypeFlow.addState(analysis, bb.analysisPolicy().constantTypeState(analysis, fieldValue, fieldType));
}

@Override
public boolean forPrimitiveFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) {
PointsToAnalysis analysis = getAnalysis();

/* Add the constant value object to the field's type flow. */
FieldTypeFlow fieldTypeFlow = getFieldTypeFlow(field, receiver);
/* Add the new constant to the field's flow state. */
return fieldTypeFlow.addState(analysis, TypeState.forPrimitiveConstant(fieldValue.asLong()));
}

/**
* Get the field type flow give a receiver.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ default HostedProviders getProviders(AnalysisMethod method) {

void runAnalysis(DebugContext debug, Function<AnalysisUniverse, Boolean> duringAnalysisAction) throws InterruptedException;

boolean trackPrimitiveValues();

/** You can blacklist certain callees here. */
@SuppressWarnings("unused")
default boolean isCallAllowed(PointsToAnalysis bb, AnalysisMethod caller, AnalysisMethod target, BytecodePosition srcPosition) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import com.oracle.graal.pointsto.api.PointstoOptions;
import com.oracle.graal.pointsto.constraints.UnsupportedFeatures;
import com.oracle.graal.pointsto.flow.AllSynchronizedTypeFlow;
import com.oracle.graal.pointsto.flow.AnyPrimitiveSourceTypeFlow;
import com.oracle.graal.pointsto.flow.FieldTypeFlow;
import com.oracle.graal.pointsto.flow.FormalParamTypeFlow;
import com.oracle.graal.pointsto.flow.InvokeTypeFlow;
Expand All @@ -61,8 +62,10 @@
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.PointsToAnalysisField;
import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod;
import com.oracle.graal.pointsto.reports.StatisticsPrinter;
import com.oracle.graal.pointsto.typestate.AnyPrimitiveTypeState;
import com.oracle.graal.pointsto.typestate.PointsToStats;
import com.oracle.graal.pointsto.typestate.TypeState;
import com.oracle.graal.pointsto.util.AnalysisError;
Expand All @@ -89,6 +92,20 @@
public abstract class PointsToAnalysis extends AbstractAnalysisEngine {
/** The type of {@link java.lang.Object}. */
private final AnalysisType objectType;
/**
* Enables propagating primitive values interproceduraly using the typeflow graph. Only simple
* constants are propagated. Arithmetic operations and merges of different constants result in a
* special {@link AnyPrimitiveTypeState } state that leads to immediate saturation.
* <p>
* This optimization also handles word types, which are essentially primitive values.
* <p>
* Unsafe loads and stores are NOT modeled, because it would lead to merging of primitive and
* objects states (all unsafe fields are merged into a single flow). Instead, all unsafe
* accessed primitive fields are assigned the PrimitiveTypeState state and any unsafe read is
* immediately represented as {@link com.oracle.graal.pointsto.flow.AnyPrimitiveSourceTypeFlow}.
*/
private final boolean trackPrimitiveValues;
private AnyPrimitiveSourceTypeFlow anyPrimitiveSourceTypeFlow;
private TypeFlow<?> allSynchronizedTypeFlow;

protected final boolean trackTypeFlowInputs;
Expand All @@ -107,6 +124,8 @@ public PointsToAnalysis(OptionValues options, AnalysisUniverse universe, HostVM
ConstantReflectionProvider constantReflectionProvider, WordTypes wordTypes, UnsupportedFeatures unsupportedFeatures, DebugContext debugContext, TimerCollection timerCollection) {
super(options, universe, hostVM, metaAccess, snippetReflectionProvider, constantReflectionProvider, wordTypes, unsupportedFeatures, debugContext, timerCollection);
this.typeFlowTimer = timerCollection.createTimer("(typeflow)");
this.trackPrimitiveValues = PointstoOptions.TrackPrimitiveValues.getValue(options);
this.anyPrimitiveSourceTypeFlow = new AnyPrimitiveSourceTypeFlow(null, null);

this.objectType = metaAccess.lookupJavaType(Object.class);
/*
Expand Down Expand Up @@ -199,15 +218,26 @@ public void forceUnsafeUpdate(AnalysisField field) {
}

@Override
public void registerAsJNIAccessed(AnalysisField field, boolean writable) {
public void registerAsJNIAccessed(AnalysisField f, boolean writable) {
PointsToAnalysisField field = (PointsToAnalysisField) f;
// Same as addRootField() and addRootStaticField():
// create type flows for any subtype of the field's declared type
TypeFlow<?> declaredTypeFlow = field.getType().getTypeFlow(this, true);
if (field.isStatic()) {
declaredTypeFlow.addUse(this, field.getStaticFieldFlow());
} else {
FieldTypeFlow instanceFieldFlow = field.getDeclaringClass().getContextInsensitiveAnalysisObject().getInstanceFieldFlow(this, field, writable);
declaredTypeFlow.addUse(this, instanceFieldFlow);
if (isSupportedJavaKind(field.getStorageKind())) {
if (field.isStatic()) {
if (field.getStorageKind().isObject()) {
declaredTypeFlow.addUse(this, field.getStaticFieldFlow());
} else {
field.saturatePrimitiveField();
}
} else {
FieldTypeFlow instanceFieldFlow = field.getDeclaringClass().getContextInsensitiveAnalysisObject().getInstanceFieldFlow(this, field, writable);
if (field.getStorageKind().isObject()) {
declaredTypeFlow.addUse(this, instanceFieldFlow);
} else {
field.saturatePrimitiveField();
}
}
}
}

Expand All @@ -224,6 +254,7 @@ public boolean trackConcreteAnalysisObjects(@SuppressWarnings("unused") Analysis
public void cleanupAfterAnalysis() {
super.cleanupAfterAnalysis();
allSynchronizedTypeFlow = null;
anyPrimitiveSourceTypeFlow = null;
unsafeLoads = null;
unsafeStores = null;

Expand Down Expand Up @@ -263,6 +294,10 @@ public TypeFlow<?> getAllSynchronizedTypeFlow() {
return allSynchronizedTypeFlow;
}

public AnyPrimitiveSourceTypeFlow getAnyPrimitiveSourceTypeFlow() {
return anyPrimitiveSourceTypeFlow;
}

@Override
public Iterable<AnalysisType> getAllSynchronizedTypes() {
/*
Expand Down Expand Up @@ -306,10 +341,7 @@ public AnalysisMethod addRootMethod(AnalysisMethod aMethod, boolean invokeSpecia
for (int idx = 0; idx < paramCount; idx++) {
AnalysisType declaredParamType = (AnalysisType) signature.getParameterType(idx, declaringClass);
FormalParamTypeFlow parameter = flowInfo.getParameter(idx);
if (declaredParamType.getJavaKind() == JavaKind.Object && parameter != null) {
TypeFlow<?> initialParameterFlow = declaredParamType.getTypeFlow(this, true);
initialParameterFlow.addUse(this, parameter);
}
processParam(declaredParamType, parameter);
}
});
};
Expand Down Expand Up @@ -369,17 +401,25 @@ public AnalysisMethod addRootMethod(AnalysisMethod aMethod, boolean invokeSpecia
*/
AnalysisType declaredParamType = (AnalysisType) signature.getParameterType(idx - 1, declaringClass);
TypeFlow<?> actualParameterFlow = invoke.getActualParameter(idx);
if (declaredParamType.getJavaKind() == JavaKind.Object && actualParameterFlow != null) {
TypeFlow<?> initialParameterFlow = declaredParamType.getTypeFlow(this, true);
initialParameterFlow.addUse(this, actualParameterFlow);
}
processParam(declaredParamType, actualParameterFlow);
}
});
}
return aMethod;

}

private void processParam(AnalysisType declaredParamType, TypeFlow<?> actualParameterFlow) {
if (actualParameterFlow != null && isSupportedJavaKind(declaredParamType.getStorageKind())) {
if (declaredParamType.getStorageKind() == JavaKind.Object) {
TypeFlow<?> initialParameterFlow = declaredParamType.getTypeFlow(this, true);
initialParameterFlow.addUse(this, actualParameterFlow);
} else {
actualParameterFlow.addState(this, TypeState.anyPrimitiveState());
}
}
}

public static PointsToAnalysisMethod assertPointsToAnalysisMethod(AnalysisMethod aMethod) {
assert aMethod instanceof PointsToAnalysisMethod : "Only points-to analysis methods are supported";
return ((PointsToAnalysisMethod) aMethod);
Expand All @@ -400,12 +440,7 @@ public AnalysisType addRootClass(AnalysisType type, boolean addFields, boolean a
if (addFields) {
field.registerAsAccessed("field of root class");
}
/*
* For system classes any instantiated (sub)type of the declared field type can be
* written to the field flow.
*/
TypeFlow<?> fieldDeclaredTypeFlow = field.getType().getTypeFlow(this, true);
fieldDeclaredTypeFlow.addUse(this, type.getContextInsensitiveAnalysisObject().getInstanceFieldFlow(this, field, true));
processRootField(type, field);
}
if (type.getSuperclass() != null) {
addRootClass(type.getSuperclass(), addFields, addArrayClass);
Expand All @@ -424,16 +459,28 @@ public AnalysisType addRootField(Class<?> clazz, String fieldName) {
AnalysisField field = (AnalysisField) javaField;
if (field.getName().equals(fieldName)) {
field.registerAsAccessed("root field");
processRootField(type, field);
return field.getType();
}
}
throw shouldNotReachHere("field not found: " + fieldName);
}

private void processRootField(AnalysisType type, AnalysisField field) {
JavaKind storageKind = field.getStorageKind();
if (isSupportedJavaKind(storageKind)) {
var fieldFlow = type.getContextInsensitiveAnalysisObject().getInstanceFieldFlow(this, field, true);
if (storageKind.isObject()) {
/*
* For system classes any instantiated (sub)type of the declared field type can be
* written to the field flow.
*/
TypeFlow<?> fieldDeclaredTypeFlow = field.getType().getTypeFlow(this, true);
fieldDeclaredTypeFlow.addUse(this, type.getContextInsensitiveAnalysisObject().getInstanceFieldFlow(this, field, true));
return field.getType();
fieldDeclaredTypeFlow.addUse(this, fieldFlow);
} else {
fieldFlow.addState(this, TypeState.anyPrimitiveState());
}
}
throw shouldNotReachHere("field not found: " + fieldName);
}

@SuppressWarnings({"try", "unused"})
Expand All @@ -444,8 +491,15 @@ public AnalysisType addRootStaticField(Class<?> clazz, String fieldName) {
reflectField = clazz.getField(fieldName);
AnalysisField field = metaAccess.lookupJavaField(reflectField);
field.registerAsAccessed("static root field");
TypeFlow<?> fieldFlow = field.getType().getTypeFlow(this, true);
fieldFlow.addUse(this, field.getStaticFieldFlow());
JavaKind storageKind = field.getStorageKind();
if (isSupportedJavaKind(storageKind)) {
if (storageKind.isObject()) {
TypeFlow<?> fieldFlow = field.getType().getTypeFlow(this, true);
fieldFlow.addUse(this, field.getStaticFieldFlow());
} else {
field.getStaticFieldFlow().addState(this, TypeState.anyPrimitiveState());
}
}
return field.getType();

} catch (NoSuchFieldException e) {
Expand All @@ -457,6 +511,15 @@ public AnalysisType addRootStaticField(Class<?> clazz, String fieldName) {
public void checkUserLimitations() {
}

public boolean isSupportedJavaKind(JavaKind javaKind) {
return javaKind == JavaKind.Object || (trackPrimitiveValues && javaKind.isNumericInteger());
}

@Override
public boolean trackPrimitiveValues() {
return trackPrimitiveValues;
}

public interface TypeFlowRunnable extends DebugContextRunnable {
TypeFlow<?> getTypeFlow();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@

public class PointstoOptions {

@Option(help = "Track primitive values using the infrastructure of points-to analysis.")//
public static final OptionKey<Boolean> TrackPrimitiveValues = new OptionKey<>(false);

@Option(help = "Use experimental Reachability Analysis instead of points-to.")//
public static final OptionKey<Boolean> UseExperimentalReachabilityAnalysis = new OptionKey<>(false);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2023, 2023, 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.flow;

import com.oracle.graal.pointsto.meta.AnalysisType;
import com.oracle.graal.pointsto.typestate.TypeState;

import jdk.vm.ci.code.BytecodePosition;

/**
* Produces AnyPrimitive state that leads to immediate saturation of all uses. Used to represent any
* operation that is not modeled by the analysis.
*/
public final class AnyPrimitiveSourceTypeFlow extends TypeFlow<BytecodePosition> implements PrimitiveFlow {

public AnyPrimitiveSourceTypeFlow(BytecodePosition source, AnalysisType type) {
super(source, type, TypeState.anyPrimitiveState());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2023, 2023, 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.flow;

import com.oracle.graal.pointsto.PointsToAnalysis;
import com.oracle.graal.pointsto.meta.AnalysisType;
import com.oracle.graal.pointsto.typestate.TypeState;

import jdk.vm.ci.code.BytecodePosition;

public class ConstantPrimitiveSourceTypeFlow extends TypeFlow<BytecodePosition> implements PrimitiveFlow {

public ConstantPrimitiveSourceTypeFlow(BytecodePosition source, AnalysisType type, long value) {
super(source, type, TypeState.forPrimitiveConstant(value));
}

public ConstantPrimitiveSourceTypeFlow(ConstantPrimitiveSourceTypeFlow original, MethodFlowsGraph methodFlows) {
super(original, methodFlows, original.getState());
}

@Override
public TypeFlow<BytecodePosition> copy(PointsToAnalysis bb, MethodFlowsGraph methodFlows) {
return new ConstantPrimitiveSourceTypeFlow(this, methodFlows);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,16 @@ public FieldFilterTypeFlow(AnalysisField field) {

@Override
public TypeState filter(PointsToAnalysis bb, TypeState update) {
if (declaredType.equals(bb.getObjectType())) {
if (isPrimitiveFlow) {
if (!update.isPrimitive()) {
/*
* Sources for these inconsistent updates are unsafe flows, but unsafe accessed
* primitive fields are saturated anyway, so we can return any primitive state.
*/
return TypeState.anyPrimitiveState();
}
return update;
} else if (declaredType.equals(bb.getObjectType())) {
/* No need to filter. */
return update;
} else {
Expand Down
Loading