Move FlowGraph classes to outer level and introduce a FlowGraphBuilder
Bug: b/296030319
Change-Id: I3badd107cbf38a6773184d6619396f7fcbd42c40
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FlowGraphStateProvider.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FlowGraphStateProvider.java
index 5fe2970..c207a11 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FlowGraphStateProvider.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FlowGraphStateProvider.java
@@ -5,7 +5,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.optimize.argumentpropagation.propagation.InFlowPropagator.FlowGraph;
+import com.android.tools.r8.optimize.argumentpropagation.propagation.FlowGraph;
import com.android.tools.r8.utils.InternalOptions;
import java.util.function.Supplier;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java
index 834f186..949bc53 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java
@@ -11,8 +11,6 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.conversion.MethodConversionOptions;
-import com.android.tools.r8.optimize.argumentpropagation.propagation.InFlowPropagator.FlowGraph;
-import com.android.tools.r8.optimize.argumentpropagation.propagation.InFlowPropagator.Node;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.ListUtils;
@@ -43,7 +41,7 @@
this.flowGraphs = flowGraphs;
}
- public Map<FlowGraph, Deque<Node>> joinDefaultFieldValuesForFieldsWithReadBeforeWrite(
+ public Map<FlowGraph, Deque<FlowGraphNode>> joinDefaultFieldValuesForFieldsWithReadBeforeWrite(
ExecutorService executorService) throws ExecutionException {
// Find all the fields where we need to determine if each field read is guaranteed to be
// dominated by a write.
@@ -192,14 +190,14 @@
}
}
- private Map<FlowGraph, Deque<Node>> updateFlowGraphs(
+ private Map<FlowGraph, Deque<FlowGraphNode>> updateFlowGraphs(
ProgramFieldSet fieldsWithLiveDefaultValue, ExecutorService executorService)
throws ExecutionException {
- Collection<Pair<FlowGraph, Deque<Node>>> worklists =
+ Collection<Pair<FlowGraph, Deque<FlowGraphNode>>> worklists =
ThreadUtils.processItemsWithResultsThatMatches(
flowGraphs,
flowGraph -> {
- Deque<Node> worklist = new ArrayDeque<>();
+ Deque<FlowGraphNode> worklist = new ArrayDeque<>();
flowGraph.forEachFieldNode(
node -> {
ProgramField field = node.getField();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraph.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraph.java
new file mode 100644
index 0000000..01a65d2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraph.java
@@ -0,0 +1,105 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.optimize.argumentpropagation.propagation;
+
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.BaseInFlow;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.FieldStateCollection;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.FieldValue;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.FlowGraphStateProvider;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodParameter;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ValueState;
+import com.android.tools.r8.optimize.argumentpropagation.utils.BidirectedGraph;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+public class FlowGraph extends BidirectedGraph<FlowGraphNode> implements FlowGraphStateProvider {
+
+ private final Map<DexField, FlowGraphFieldNode> fieldNodes;
+ private final Map<DexMethod, Int2ReferenceMap<FlowGraphParameterNode>> parameterNodes;
+
+ public FlowGraph(
+ Map<DexField, FlowGraphFieldNode> fieldNodes,
+ Map<DexMethod, Int2ReferenceMap<FlowGraphParameterNode>> parameterNodes) {
+ this.fieldNodes = fieldNodes;
+ this.parameterNodes = parameterNodes;
+ }
+
+ public FlowGraph(Collection<FlowGraphNode> nodes) {
+ this(new IdentityHashMap<>(), new IdentityHashMap<>());
+ for (FlowGraphNode node : nodes) {
+ if (node.isFieldNode()) {
+ FlowGraphFieldNode fieldNode = node.asFieldNode();
+ fieldNodes.put(fieldNode.getField().getReference(), fieldNode);
+ } else {
+ FlowGraphParameterNode parameterNode = node.asParameterNode();
+ parameterNodes
+ .computeIfAbsent(
+ parameterNode.getMethod().getReference(), ignoreKey(Int2ReferenceOpenHashMap::new))
+ .put(parameterNode.getParameterIndex(), parameterNode);
+ }
+ }
+ }
+
+ public static FlowGraphBuilder builder(
+ AppView<AppInfoWithLiveness> appView,
+ IRConverter converter,
+ FieldStateCollection fieldStates,
+ MethodStateCollectionByReference methodStates) {
+ return new FlowGraphBuilder(appView, converter, fieldStates, methodStates);
+ }
+
+ public void forEachFieldNode(Consumer<? super FlowGraphFieldNode> consumer) {
+ fieldNodes.values().forEach(consumer);
+ }
+
+ @Override
+ public void forEachNeighbor(FlowGraphNode node, Consumer<? super FlowGraphNode> consumer) {
+ node.getPredecessors().forEach(consumer);
+ node.getSuccessors().forEach(consumer);
+ }
+
+ @Override
+ public void forEachNode(Consumer<? super FlowGraphNode> consumer) {
+ forEachFieldNode(consumer);
+ parameterNodes.values().forEach(nodesForMethod -> nodesForMethod.values().forEach(consumer));
+ }
+
+ @Override
+ public ValueState getState(DexField field) {
+ return fieldNodes.get(field).getState();
+ }
+
+ @Override
+ public ValueState getState(BaseInFlow inFlow, Supplier<ValueState> defaultStateProvider) {
+ if (inFlow.isFieldValue()) {
+ FieldValue fieldValue = inFlow.asFieldValue();
+ return getState(fieldValue.getField());
+ } else {
+ assert inFlow.isMethodParameter();
+ MethodParameter methodParameter = inFlow.asMethodParameter();
+ FlowGraphParameterNode parameterNode =
+ parameterNodes
+ .getOrDefault(methodParameter.getMethod(), Int2ReferenceMaps.emptyMap())
+ .get(methodParameter.getIndex());
+ if (parameterNode != null) {
+ return parameterNode.getState();
+ }
+ return defaultStateProvider.get();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphBuilder.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphBuilder.java
new file mode 100644
index 0000000..a998a15
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphBuilder.java
@@ -0,0 +1,296 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.optimize.argumentpropagation.propagation;
+
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import static com.android.tools.r8.graph.ProgramField.asProgramFieldOrNull;
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.AbstractFunction;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.BaseInFlow;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteValueState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.FieldStateCollection;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.FieldValue;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.InFlow;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodParameter;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ValueState;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.TraversalContinuation;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class FlowGraphBuilder {
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final IRConverter converter;
+ private final FieldStateCollection fieldStates;
+ private final MethodStateCollectionByReference methodStates;
+
+ private final Map<DexField, FlowGraphFieldNode> fieldNodes = new IdentityHashMap<>();
+ private final Map<DexMethod, Int2ReferenceMap<FlowGraphParameterNode>> parameterNodes =
+ new IdentityHashMap<>();
+
+ public FlowGraphBuilder(
+ AppView<AppInfoWithLiveness> appView,
+ IRConverter converter,
+ FieldStateCollection fieldStates,
+ MethodStateCollectionByReference methodStates) {
+ this.appView = appView;
+ this.converter = converter;
+ this.fieldStates = fieldStates;
+ this.methodStates = methodStates;
+ }
+
+ public FlowGraphBuilder addClasses(Collection<DexProgramClass> classes) {
+ for (DexProgramClass clazz : classes) {
+ add(clazz);
+ }
+ return this;
+ }
+
+ public FlowGraph build() {
+ return new FlowGraph(fieldNodes, parameterNodes);
+ }
+
+ private void add(DexProgramClass clazz) {
+ clazz.forEachProgramField(this::addField);
+ clazz.forEachProgramMethod(this::addMethodParameters);
+ }
+
+ private void addField(ProgramField field) {
+ ValueState fieldState = fieldStates.get(field);
+
+ // No need to create nodes for fields with no in-flow or no useful information.
+ if (fieldState.isBottom() || fieldState.isUnknown()) {
+ return;
+ }
+
+ ConcreteValueState concreteFieldState = fieldState.asConcrete();
+
+ // No need to create a node for a field that doesn't depend on any other nodes, unless some
+ // other node depends on this field, in which case that other node will lead to creation of a
+ // node for the current field.
+ if (!concreteFieldState.hasInFlow()) {
+ return;
+ }
+
+ FlowGraphFieldNode node = getOrCreateFieldNode(field, concreteFieldState);
+ for (InFlow inFlow : concreteFieldState.getInFlow()) {
+ if (addInFlow(inFlow, node).shouldBreak()) {
+ assert node.isUnknown();
+ break;
+ }
+ }
+
+ if (!node.getState().isUnknown()) {
+ assert node.getState() == concreteFieldState;
+ node.setState(concreteFieldState.clearInFlow());
+ }
+ }
+
+ private void addMethodParameters(ProgramMethod method) {
+ MethodState methodState = methodStates.get(method);
+
+ // No need to create nodes for parameters with no in-flow or no useful information.
+ if (methodState.isBottom() || methodState.isUnknown()) {
+ return;
+ }
+
+ // Add nodes for the parameters for which we have non-trivial information.
+ ConcreteMonomorphicMethodState monomorphicMethodState = methodState.asMonomorphic();
+ List<ValueState> parameterStates = monomorphicMethodState.getParameterStates();
+ for (int parameterIndex = 0; parameterIndex < parameterStates.size(); parameterIndex++) {
+ ValueState parameterState = parameterStates.get(parameterIndex);
+ addMethodParameter(method, parameterIndex, monomorphicMethodState, parameterState);
+ }
+ }
+
+ private void addMethodParameter(
+ ProgramMethod method,
+ int parameterIndex,
+ ConcreteMonomorphicMethodState methodState,
+ ValueState parameterState) {
+ // No need to create nodes for parameters with no in-parameters and parameters we don't know
+ // anything about.
+ if (parameterState.isBottom() || parameterState.isUnknown()) {
+ return;
+ }
+
+ ConcreteValueState concreteParameterState = parameterState.asConcrete();
+
+ // No need to create a node for a parameter that doesn't depend on any other parameters,
+ // unless some other node depends on this parameter, in which case that other node will lead
+ // to the creation of a node for the current parameter.
+ if (!concreteParameterState.hasInFlow()) {
+ return;
+ }
+
+ FlowGraphParameterNode node = getOrCreateParameterNode(method, parameterIndex, methodState);
+ for (InFlow inFlow : concreteParameterState.getInFlow()) {
+ if (addInFlow(inFlow, node).shouldBreak()) {
+ assert node.isUnknown();
+ break;
+ }
+ }
+
+ if (!node.getState().isUnknown()) {
+ assert node.getState() == concreteParameterState;
+ node.setState(concreteParameterState.clearInFlow());
+ }
+ }
+
+ // Returns BREAK if the current node has been set to unknown.
+ private TraversalContinuation<?, ?> addInFlow(InFlow inFlow, FlowGraphNode node) {
+ if (inFlow.isAbstractFunction()) {
+ return addInFlow(inFlow.asAbstractFunction(), node);
+ } else if (inFlow.isFieldValue()) {
+ return addInFlow(inFlow.asFieldValue(), node);
+ } else if (inFlow.isMethodParameter()) {
+ return addInFlow(inFlow.asMethodParameter(), node);
+ } else {
+ throw new Unreachable(inFlow.getClass().getTypeName());
+ }
+ }
+
+ private TraversalContinuation<?, ?> addInFlow(AbstractFunction inFlow, FlowGraphNode node) {
+ for (BaseInFlow baseInFlow : inFlow.getBaseInFlow()) {
+ TraversalContinuation<?, ?> traversalContinuation;
+ if (baseInFlow.isFieldValue()) {
+ traversalContinuation = addInFlow(baseInFlow.asFieldValue(), node, inFlow);
+ } else {
+ assert baseInFlow.isMethodParameter();
+ traversalContinuation = addInFlow(baseInFlow.asMethodParameter(), node, inFlow);
+ }
+ if (traversalContinuation.shouldBreak()) {
+ return traversalContinuation;
+ }
+ }
+ return TraversalContinuation.doContinue();
+ }
+
+ private TraversalContinuation<?, ?> addInFlow(FieldValue inFlow, FlowGraphNode node) {
+ return addInFlow(inFlow, node, AbstractFunction.identity());
+ }
+
+ private TraversalContinuation<?, ?> addInFlow(
+ FieldValue inFlow, FlowGraphNode node, AbstractFunction transferFunction) {
+ assert !node.isUnknown();
+
+ ProgramField field = asProgramFieldOrNull(appView.definitionFor(inFlow.getField()));
+ if (field == null) {
+ assert false;
+ return TraversalContinuation.doContinue();
+ }
+
+ ValueState fieldState = getFieldState(field, fieldStates);
+ if (fieldState.isUnknown()) {
+ // The current node depends on a field for which we don't know anything.
+ node.clearPredecessors();
+ node.setStateToUnknown();
+ return TraversalContinuation.doBreak();
+ }
+
+ FlowGraphFieldNode fieldNode = getOrCreateFieldNode(field, fieldState);
+ node.addPredecessor(fieldNode, transferFunction);
+ return TraversalContinuation.doContinue();
+ }
+
+ private TraversalContinuation<?, ?> addInFlow(MethodParameter inFlow, FlowGraphNode node) {
+ return addInFlow(inFlow, node, AbstractFunction.identity());
+ }
+
+ private TraversalContinuation<?, ?> addInFlow(
+ MethodParameter inFlow, FlowGraphNode node, AbstractFunction transferFunction) {
+ ProgramMethod enclosingMethod = getEnclosingMethod(inFlow);
+ if (enclosingMethod == null) {
+ // This is a parameter of a single caller inlined method. Since this method has been
+ // pruned, the call from inside the method no longer exists, and we can therefore safely
+ // skip it.
+ assert converter.getInliner().verifyIsPrunedDueToSingleCallerInlining(inFlow.getMethod());
+ return TraversalContinuation.doContinue();
+ }
+
+ MethodState enclosingMethodState = getMethodState(enclosingMethod, methodStates);
+ if (enclosingMethodState.isBottom()) {
+ // The current node takes a value from a dead method; no need to propagate any information
+ // from the dead assignment.
+ return TraversalContinuation.doContinue();
+ }
+
+ if (enclosingMethodState.isUnknown()) {
+ // The current node depends on a parameter for which we don't know anything.
+ node.clearPredecessors();
+ node.setStateToUnknown();
+ return TraversalContinuation.doBreak();
+ }
+
+ assert enclosingMethodState.isConcrete();
+ assert enclosingMethodState.asConcrete().isMonomorphic();
+
+ FlowGraphParameterNode predecessor =
+ getOrCreateParameterNode(
+ enclosingMethod, inFlow.getIndex(), enclosingMethodState.asConcrete().asMonomorphic());
+ node.addPredecessor(predecessor, transferFunction);
+ return TraversalContinuation.doContinue();
+ }
+
+ private FlowGraphFieldNode getOrCreateFieldNode(ProgramField field, ValueState fieldState) {
+ return fieldNodes.computeIfAbsent(
+ field.getReference(), ignoreKey(() -> new FlowGraphFieldNode(field, fieldState)));
+ }
+
+ private FlowGraphParameterNode getOrCreateParameterNode(
+ ProgramMethod method, int parameterIndex, ConcreteMonomorphicMethodState methodState) {
+ Int2ReferenceMap<FlowGraphParameterNode> parameterNodesForMethod =
+ parameterNodes.computeIfAbsent(
+ method.getReference(), ignoreKey(Int2ReferenceOpenHashMap::new));
+ return parameterNodesForMethod.compute(
+ parameterIndex,
+ (ignore, parameterNode) ->
+ parameterNode != null
+ ? parameterNode
+ : new FlowGraphParameterNode(
+ method, methodState, parameterIndex, method.getArgumentType(parameterIndex)));
+ }
+
+ private ProgramMethod getEnclosingMethod(MethodParameter methodParameter) {
+ DexMethod methodReference = methodParameter.getMethod();
+ return methodReference.lookupOnProgramClass(
+ asProgramClassOrNull(appView.definitionFor(methodParameter.getMethod().getHolderType())));
+ }
+
+ private ValueState getFieldState(ProgramField field, FieldStateCollection fieldStates) {
+ if (field == null) {
+ // Conservatively return unknown if for some reason we can't find the field.
+ assert false;
+ return ValueState.unknown();
+ }
+ return fieldStates.get(field);
+ }
+
+ private MethodState getMethodState(
+ ProgramMethod method, MethodStateCollectionByReference methodStates) {
+ if (method == null) {
+ // Conservatively return unknown if for some reason we can't find the method.
+ assert false;
+ return MethodState.unknown();
+ }
+ return methodStates.get(method);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphFieldNode.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphFieldNode.java
new file mode 100644
index 0000000..c25ed35
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphFieldNode.java
@@ -0,0 +1,96 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.optimize.argumentpropagation.propagation;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteArrayTypeValueState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteClassTypeValueState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcretePrimitiveTypeValueState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.NonEmptyValueState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ValueState;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Action;
+
+public class FlowGraphFieldNode extends FlowGraphNode {
+
+ private final ProgramField field;
+ private ValueState fieldState;
+
+ FlowGraphFieldNode(ProgramField field, ValueState fieldState) {
+ this.field = field;
+ this.fieldState = fieldState;
+ }
+
+ public ProgramField getField() {
+ return field;
+ }
+
+ @Override
+ DexType getStaticType() {
+ return field.getType();
+ }
+
+ void addDefaultValue(AppView<AppInfoWithLiveness> appView, Action onChangedAction) {
+ AbstractValueFactory abstractValueFactory = appView.abstractValueFactory();
+ AbstractValue defaultValue;
+ if (field.getAccessFlags().isStatic() && field.getDefinition().hasExplicitStaticValue()) {
+ defaultValue = field.getDefinition().getStaticValue().toAbstractValue(abstractValueFactory);
+ } else if (field.getType().isPrimitiveType()) {
+ defaultValue = abstractValueFactory.createZeroValue();
+ } else {
+ defaultValue = abstractValueFactory.createUncheckedNullValue();
+ }
+ NonEmptyValueState fieldStateToAdd;
+ if (field.getType().isArrayType()) {
+ Nullability defaultNullability = Nullability.definitelyNull();
+ fieldStateToAdd = ConcreteArrayTypeValueState.create(defaultNullability);
+ } else if (field.getType().isClassType()) {
+ assert defaultValue.isNull() || defaultValue.isSingleStringValue();
+ DynamicType dynamicType =
+ defaultValue.isNull()
+ ? DynamicType.definitelyNull()
+ : DynamicType.createExact(
+ TypeElement.stringClassType(appView, Nullability.definitelyNotNull()));
+ fieldStateToAdd = ConcreteClassTypeValueState.create(defaultValue, dynamicType);
+ } else {
+ assert field.getType().isPrimitiveType();
+ fieldStateToAdd = ConcretePrimitiveTypeValueState.create(defaultValue);
+ }
+ if (fieldStateToAdd.isConcrete()) {
+ addState(appView, fieldStateToAdd.asConcrete(), onChangedAction);
+ } else {
+ // We should always be able to map static field values to an unknown abstract value.
+ assert false;
+ setStateToUnknown();
+ onChangedAction.execute();
+ }
+ }
+
+ @Override
+ ValueState getState() {
+ return fieldState;
+ }
+
+ @Override
+ void setState(ValueState fieldState) {
+ this.fieldState = fieldState;
+ }
+
+ @Override
+ boolean isFieldNode() {
+ return true;
+ }
+
+ @Override
+ FlowGraphFieldNode asFieldNode() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphNode.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphNode.java
new file mode 100644
index 0000000..4075465
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphNode.java
@@ -0,0 +1,144 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.optimize.argumentpropagation.propagation;
+
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.AbstractFunction;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteValueState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.StateCloner;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ValueState;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Action;
+import com.google.common.collect.Sets;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.BiPredicate;
+
+public abstract class FlowGraphNode {
+
+ private final Set<FlowGraphNode> predecessors = Sets.newIdentityHashSet();
+ private final Map<FlowGraphNode, Set<AbstractFunction>> successors = new IdentityHashMap<>();
+
+ private boolean inWorklist = true;
+
+ void addState(
+ AppView<AppInfoWithLiveness> appView, ConcreteValueState stateToAdd, Action onChangedAction) {
+ ValueState oldState = getState();
+ ValueState newState =
+ oldState.mutableJoin(
+ appView, stateToAdd, getStaticType(), StateCloner.getCloner(), onChangedAction);
+ if (newState != oldState) {
+ setState(newState);
+ onChangedAction.execute();
+ }
+ }
+
+ abstract ValueState getState();
+
+ abstract DexType getStaticType();
+
+ abstract void setState(ValueState valueState);
+
+ void setStateToUnknown() {
+ setState(ValueState.unknown());
+ }
+
+ void addPredecessor(FlowGraphNode predecessor, AbstractFunction abstractFunction) {
+ predecessor.successors.computeIfAbsent(this, ignoreKey(HashSet::new)).add(abstractFunction);
+ predecessors.add(predecessor);
+ }
+
+ void clearPredecessors() {
+ for (FlowGraphNode predecessor : predecessors) {
+ predecessor.successors.remove(this);
+ }
+ predecessors.clear();
+ }
+
+ void clearPredecessors(FlowGraphNode cause) {
+ for (FlowGraphNode predecessor : predecessors) {
+ if (predecessor != cause) {
+ predecessor.successors.remove(this);
+ }
+ }
+ predecessors.clear();
+ }
+
+ Set<FlowGraphNode> getPredecessors() {
+ return predecessors;
+ }
+
+ boolean hasPredecessors() {
+ return !predecessors.isEmpty();
+ }
+
+ void clearDanglingSuccessors() {
+ successors.clear();
+ }
+
+ Set<FlowGraphNode> getSuccessors() {
+ return successors.keySet();
+ }
+
+ public void forEachSuccessor(BiConsumer<FlowGraphNode, Set<AbstractFunction>> consumer) {
+ successors.forEach(consumer);
+ }
+
+ public void removeSuccessorIf(BiPredicate<FlowGraphNode, Set<AbstractFunction>> predicate) {
+ successors.entrySet().removeIf(entry -> predicate.test(entry.getKey(), entry.getValue()));
+ }
+
+ boolean hasSuccessors() {
+ return !successors.isEmpty();
+ }
+
+ boolean isBottom() {
+ return getState().isBottom();
+ }
+
+ boolean isFieldNode() {
+ return false;
+ }
+
+ FlowGraphFieldNode asFieldNode() {
+ return null;
+ }
+
+ boolean isParameterNode() {
+ return false;
+ }
+
+ FlowGraphParameterNode asParameterNode() {
+ return null;
+ }
+
+ boolean isEffectivelyUnknown() {
+ return getState().isConcrete() && getState().asConcrete().isEffectivelyUnknown();
+ }
+
+ boolean isUnknown() {
+ return getState().isUnknown();
+ }
+
+ // No need to enqueue the affected node if it is already in the worklist or if it does not have
+ // any successors (i.e., the successor is a leaf).
+ void addToWorkList(Deque<FlowGraphNode> worklist) {
+ if (!inWorklist && hasSuccessors()) {
+ worklist.add(this);
+ inWorklist = true;
+ }
+ }
+
+ void unsetInWorklist() {
+ assert inWorklist;
+ inWorklist = false;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphParameterNode.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphParameterNode.java
new file mode 100644
index 0000000..b82cecf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphParameterNode.java
@@ -0,0 +1,61 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.optimize.argumentpropagation.propagation;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ValueState;
+
+class FlowGraphParameterNode extends FlowGraphNode {
+
+ private final ProgramMethod method;
+ private final ConcreteMonomorphicMethodState methodState;
+ private final int parameterIndex;
+ private final DexType parameterType;
+
+ FlowGraphParameterNode(
+ ProgramMethod method,
+ ConcreteMonomorphicMethodState methodState,
+ int parameterIndex,
+ DexType parameterType) {
+ this.method = method;
+ this.methodState = methodState;
+ this.parameterIndex = parameterIndex;
+ this.parameterType = parameterType;
+ }
+
+ ProgramMethod getMethod() {
+ return method;
+ }
+
+ int getParameterIndex() {
+ return parameterIndex;
+ }
+
+ @Override
+ DexType getStaticType() {
+ return parameterType;
+ }
+
+ @Override
+ ValueState getState() {
+ return methodState.getParameterState(parameterIndex);
+ }
+
+ @Override
+ void setState(ValueState parameterState) {
+ methodState.setParameterState(parameterIndex, parameterState);
+ }
+
+ @Override
+ boolean isParameterNode() {
+ return true;
+ }
+
+ @Override
+ FlowGraphParameterNode asParameterNode() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphWriter.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphWriter.java
index 7b438c4..8f60f27 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphWriter.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphWriter.java
@@ -5,10 +5,6 @@
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.optimize.argumentpropagation.propagation.InFlowPropagator.FieldNode;
-import com.android.tools.r8.optimize.argumentpropagation.propagation.InFlowPropagator.FlowGraph;
-import com.android.tools.r8.optimize.argumentpropagation.propagation.InFlowPropagator.Node;
-import com.android.tools.r8.optimize.argumentpropagation.propagation.InFlowPropagator.ParameterNode;
import com.google.common.annotations.VisibleForTesting;
import java.io.PrintStream;
@@ -33,7 +29,7 @@
out.println(" -> ");
}
- private void writeNode(PrintStream out, Node node) {
+ private void writeNode(PrintStream out, FlowGraphNode node) {
if (!node.hasSuccessors()) {
writeNodeLabel(out, node);
return;
@@ -46,7 +42,7 @@
});
}
- private void writeNodeLabel(PrintStream out, Node node) {
+ private void writeNodeLabel(PrintStream out, FlowGraphNode node) {
if (node.isFieldNode()) {
writeFieldNodeLabel(out, node.asFieldNode());
} else {
@@ -55,7 +51,7 @@
}
}
- private void writeFieldNodeLabel(PrintStream out, FieldNode node) {
+ private void writeFieldNodeLabel(PrintStream out, FlowGraphFieldNode node) {
out.print("\"");
ProgramField field = node.getField();
out.print(field.getHolderType().getSimpleName());
@@ -64,7 +60,7 @@
out.print("\"");
}
- private void writeParameterNodeLabel(PrintStream out, ParameterNode node) {
+ private void writeParameterNodeLabel(PrintStream out, FlowGraphParameterNode node) {
out.print("\"");
ProgramMethod method = node.getMethod();
out.print(method.getHolderType().getSimpleName());
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java
index 5b1ccf2..894a54c 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java
@@ -3,66 +3,29 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.optimize.argumentpropagation.propagation;
-import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-import static com.android.tools.r8.graph.ProgramField.asProgramFieldOrNull;
-import static com.android.tools.r8.utils.MapUtils.ignoreKey;
-
-import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.type.DynamicType;
-import com.android.tools.r8.ir.analysis.type.Nullability;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
-import com.android.tools.r8.ir.analysis.value.AbstractValue;
-import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.AbstractFunction;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.BaseInFlow;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteArrayTypeValueState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteClassTypeValueState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMethodState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcretePrimitiveTypeValueState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteValueState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.FieldStateCollection;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.FieldValue;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.FlowGraphStateProvider;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.InFlow;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodParameter;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.NonEmptyValueState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.StateCloner;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ValueState;
-import com.android.tools.r8.optimize.argumentpropagation.utils.BidirectedGraph;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.Action;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.TraversalContinuation;
-import com.google.common.collect.Sets;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
import java.util.ArrayDeque;
-import java.util.Collection;
import java.util.Deque;
-import java.util.HashSet;
-import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-import java.util.function.BiConsumer;
-import java.util.function.BiPredicate;
-import java.util.function.Consumer;
-import java.util.function.Supplier;
public class InFlowPropagator {
@@ -94,7 +57,7 @@
// perform this analysis after having computed the initial fixpoint(s). The hypothesis is that
// many fields will have reached the unknown state after the initial fixpoint, meaning there is
// fewer fields to analyze.
- Map<FlowGraph, Deque<Node>> worklists =
+ Map<FlowGraph, Deque<FlowGraphNode>> worklists =
includeDefaultValuesInFieldStates(flowGraphs, executorService);
// Since the inclusion of default values changes the flow graphs, we need to repeat the
@@ -110,12 +73,16 @@
private List<FlowGraph> computeStronglyConnectedFlowGraphs() {
// Build a graph with an edge from parameter p -> parameter p' if all argument information for p
// must be included in the argument information for p'.
- FlowGraph flowGraph = new FlowGraph(appView.appInfo().classes());
- List<Set<Node>> stronglyConnectedComponents = flowGraph.computeStronglyConnectedComponents();
+ FlowGraph flowGraph =
+ FlowGraph.builder(appView, converter, fieldStates, methodStates)
+ .addClasses(appView.appInfo().classes())
+ .build();
+ List<Set<FlowGraphNode>> stronglyConnectedComponents =
+ flowGraph.computeStronglyConnectedComponents();
return ListUtils.map(stronglyConnectedComponents, FlowGraph::new);
}
- private Map<FlowGraph, Deque<Node>> includeDefaultValuesInFieldStates(
+ private Map<FlowGraph, Deque<FlowGraphNode>> includeDefaultValuesInFieldStates(
List<FlowGraph> flowGraphs, ExecutorService executorService) throws ExecutionException {
DefaultFieldValueJoiner joiner = new DefaultFieldValueJoiner(appView, flowGraphs);
return joiner.joinDefaultFieldValuesForFieldsWithReadBeforeWrite(executorService);
@@ -128,7 +95,7 @@
}
private void processWorklists(
- Map<FlowGraph, Deque<Node>> worklists, ExecutorService executorService)
+ Map<FlowGraph, Deque<FlowGraphNode>> worklists, ExecutorService executorService)
throws ExecutionException {
ThreadUtils.processMap(
worklists, this::process, appView.options().getThreadingModule(), executorService);
@@ -136,12 +103,12 @@
private void process(FlowGraph flowGraph) {
// Build a worklist containing all the nodes.
- Deque<Node> worklist = new ArrayDeque<>();
+ Deque<FlowGraphNode> worklist = new ArrayDeque<>();
flowGraph.forEachNode(worklist::add);
process(flowGraph, worklist);
}
- private void process(FlowGraph flowGraph, Deque<Node> worklist) {
+ private void process(FlowGraph flowGraph, Deque<FlowGraphNode> worklist) {
// Repeatedly propagate argument information through edges in the flow graph until there are no
// more changes.
// TODO(b/190154391): Consider a path p1 -> p2 -> p3 in the graph. If we process p2 first, then
@@ -149,19 +116,19 @@
// need to reprocess p2 and then p3. If we always process leaves in the graph first, we would
// process p1, then p2, then p3, and then be done.
while (!worklist.isEmpty()) {
- Node node = worklist.removeLast();
+ FlowGraphNode node = worklist.removeLast();
node.unsetInWorklist();
propagate(flowGraph, node, worklist);
}
}
- private void propagate(FlowGraph flowGraph, Node node, Deque<Node> worklist) {
+ private void propagate(FlowGraph flowGraph, FlowGraphNode node, Deque<FlowGraphNode> worklist) {
if (node.isBottom()) {
return;
}
if (node.isUnknown()) {
assert !node.hasPredecessors();
- for (Node successorNode : node.getSuccessors()) {
+ for (FlowGraphNode successorNode : node.getSuccessors()) {
assert !successorNode.isUnknown();
successorNode.clearPredecessors(node);
successorNode.setStateToUnknown();
@@ -173,7 +140,8 @@
}
}
- private void propagateNode(FlowGraph flowGraph, Node node, Deque<Node> worklist) {
+ private void propagateNode(
+ FlowGraph flowGraph, FlowGraphNode node, Deque<FlowGraphNode> worklist) {
ConcreteValueState state = node.getState().asConcrete();
node.removeSuccessorIf(
(successorNode, transferFunctions) -> {
@@ -230,562 +198,4 @@
methodStates.set(method, MethodState.unknown());
}
}
-
- public class FlowGraph extends BidirectedGraph<Node> implements FlowGraphStateProvider {
-
- private final Map<DexField, FieldNode> fieldNodes = new IdentityHashMap<>();
- private final Map<DexMethod, Int2ReferenceMap<ParameterNode>> parameterNodes =
- new IdentityHashMap<>();
-
- public FlowGraph(Iterable<DexProgramClass> classes) {
- classes.forEach(this::add);
- }
-
- public FlowGraph(Collection<Node> nodes) {
- for (Node node : nodes) {
- if (node.isFieldNode()) {
- FieldNode fieldNode = node.asFieldNode();
- fieldNodes.put(fieldNode.getField().getReference(), fieldNode);
- } else {
- ParameterNode parameterNode = node.asParameterNode();
- parameterNodes
- .computeIfAbsent(
- parameterNode.getMethod().getReference(),
- ignoreKey(Int2ReferenceOpenHashMap::new))
- .put(parameterNode.getParameterIndex(), parameterNode);
- }
- }
- }
-
- public void forEachFieldNode(Consumer<? super FieldNode> consumer) {
- fieldNodes.values().forEach(consumer);
- }
-
- @Override
- public void forEachNeighbor(Node node, Consumer<? super Node> consumer) {
- node.getPredecessors().forEach(consumer);
- node.getSuccessors().forEach(consumer);
- }
-
- @Override
- public void forEachNode(Consumer<? super Node> consumer) {
- forEachFieldNode(consumer);
- parameterNodes.values().forEach(nodesForMethod -> nodesForMethod.values().forEach(consumer));
- }
-
- private void add(DexProgramClass clazz) {
- clazz.forEachProgramField(this::addField);
- clazz.forEachProgramMethod(this::addMethodParameters);
- }
-
- private void addField(ProgramField field) {
- ValueState fieldState = fieldStates.get(field);
-
- // No need to create nodes for fields with no in-flow or no useful information.
- if (fieldState.isBottom() || fieldState.isUnknown()) {
- return;
- }
-
- ConcreteValueState concreteFieldState = fieldState.asConcrete();
-
- // No need to create a node for a field that doesn't depend on any other nodes, unless some
- // other node depends on this field, in which case that other node will lead to creation of a
- // node for the current field.
- if (!concreteFieldState.hasInFlow()) {
- return;
- }
-
- FieldNode node = getOrCreateFieldNode(field, concreteFieldState);
- for (InFlow inFlow : concreteFieldState.getInFlow()) {
- if (addInFlow(inFlow, node).shouldBreak()) {
- assert node.isUnknown();
- break;
- }
- }
-
- if (!node.getState().isUnknown()) {
- assert node.getState() == concreteFieldState;
- node.setState(concreteFieldState.clearInFlow());
- }
- }
-
- private void addMethodParameters(ProgramMethod method) {
- MethodState methodState = methodStates.get(method);
-
- // No need to create nodes for parameters with no in-flow or no useful information.
- if (methodState.isBottom() || methodState.isUnknown()) {
- return;
- }
-
- // Add nodes for the parameters for which we have non-trivial information.
- ConcreteMonomorphicMethodState monomorphicMethodState = methodState.asMonomorphic();
- List<ValueState> parameterStates = monomorphicMethodState.getParameterStates();
- for (int parameterIndex = 0; parameterIndex < parameterStates.size(); parameterIndex++) {
- ValueState parameterState = parameterStates.get(parameterIndex);
- addMethodParameter(method, parameterIndex, monomorphicMethodState, parameterState);
- }
- }
-
- private void addMethodParameter(
- ProgramMethod method,
- int parameterIndex,
- ConcreteMonomorphicMethodState methodState,
- ValueState parameterState) {
- // No need to create nodes for parameters with no in-parameters and parameters we don't know
- // anything about.
- if (parameterState.isBottom() || parameterState.isUnknown()) {
- return;
- }
-
- ConcreteValueState concreteParameterState = parameterState.asConcrete();
-
- // No need to create a node for a parameter that doesn't depend on any other parameters,
- // unless some other node depends on this parameter, in which case that other node will lead
- // to the creation of a node for the current parameter.
- if (!concreteParameterState.hasInFlow()) {
- return;
- }
-
- ParameterNode node = getOrCreateParameterNode(method, parameterIndex, methodState);
- for (InFlow inFlow : concreteParameterState.getInFlow()) {
- if (addInFlow(inFlow, node).shouldBreak()) {
- assert node.isUnknown();
- break;
- }
- }
-
- if (!node.getState().isUnknown()) {
- assert node.getState() == concreteParameterState;
- node.setState(concreteParameterState.clearInFlow());
- }
- }
-
- // Returns BREAK if the current node has been set to unknown.
- private TraversalContinuation<?, ?> addInFlow(InFlow inFlow, Node node) {
- if (inFlow.isAbstractFunction()) {
- return addInFlow(inFlow.asAbstractFunction(), node);
- } else if (inFlow.isFieldValue()) {
- return addInFlow(inFlow.asFieldValue(), node);
- } else if (inFlow.isMethodParameter()) {
- return addInFlow(inFlow.asMethodParameter(), node);
- } else {
- throw new Unreachable(inFlow.getClass().getTypeName());
- }
- }
-
- private TraversalContinuation<?, ?> addInFlow(AbstractFunction inFlow, Node node) {
- for (BaseInFlow baseInFlow : inFlow.getBaseInFlow()) {
- TraversalContinuation<?, ?> traversalContinuation;
- if (baseInFlow.isFieldValue()) {
- traversalContinuation = addInFlow(baseInFlow.asFieldValue(), node, inFlow);
- } else {
- assert baseInFlow.isMethodParameter();
- traversalContinuation = addInFlow(baseInFlow.asMethodParameter(), node, inFlow);
- }
- if (traversalContinuation.shouldBreak()) {
- return traversalContinuation;
- }
- }
- return TraversalContinuation.doContinue();
- }
-
- private TraversalContinuation<?, ?> addInFlow(FieldValue inFlow, Node node) {
- return addInFlow(inFlow, node, AbstractFunction.identity());
- }
-
- private TraversalContinuation<?, ?> addInFlow(
- FieldValue inFlow, Node node, AbstractFunction transferFunction) {
- assert !node.isUnknown();
-
- ProgramField field = asProgramFieldOrNull(appView.definitionFor(inFlow.getField()));
- if (field == null) {
- assert false;
- return TraversalContinuation.doContinue();
- }
-
- ValueState fieldState = getFieldState(field);
- if (fieldState.isUnknown()) {
- // The current node depends on a field for which we don't know anything.
- node.clearPredecessors();
- node.setStateToUnknown();
- return TraversalContinuation.doBreak();
- }
-
- FieldNode fieldNode = getOrCreateFieldNode(field, fieldState);
- node.addPredecessor(fieldNode, transferFunction);
- return TraversalContinuation.doContinue();
- }
-
- private TraversalContinuation<?, ?> addInFlow(MethodParameter inFlow, Node node) {
- return addInFlow(inFlow, node, AbstractFunction.identity());
- }
-
- private TraversalContinuation<?, ?> addInFlow(
- MethodParameter inFlow, Node node, AbstractFunction transferFunction) {
- ProgramMethod enclosingMethod = getEnclosingMethod(inFlow);
- if (enclosingMethod == null) {
- // This is a parameter of a single caller inlined method. Since this method has been
- // pruned, the call from inside the method no longer exists, and we can therefore safely
- // skip it.
- assert converter.getInliner().verifyIsPrunedDueToSingleCallerInlining(inFlow.getMethod());
- return TraversalContinuation.doContinue();
- }
-
- MethodState enclosingMethodState = getMethodState(enclosingMethod);
- if (enclosingMethodState.isBottom()) {
- // The current node takes a value from a dead method; no need to propagate any information
- // from the dead assignment.
- return TraversalContinuation.doContinue();
- }
-
- if (enclosingMethodState.isUnknown()) {
- // The current node depends on a parameter for which we don't know anything.
- node.clearPredecessors();
- node.setStateToUnknown();
- return TraversalContinuation.doBreak();
- }
-
- assert enclosingMethodState.isConcrete();
- assert enclosingMethodState.asConcrete().isMonomorphic();
-
- ParameterNode predecessor =
- getOrCreateParameterNode(
- enclosingMethod,
- inFlow.getIndex(),
- enclosingMethodState.asConcrete().asMonomorphic());
- node.addPredecessor(predecessor, transferFunction);
- return TraversalContinuation.doContinue();
- }
-
- private FieldNode getOrCreateFieldNode(ProgramField field, ValueState fieldState) {
- return fieldNodes.computeIfAbsent(
- field.getReference(), ignoreKey(() -> new FieldNode(field, fieldState)));
- }
-
- private ParameterNode getOrCreateParameterNode(
- ProgramMethod method, int parameterIndex, ConcreteMonomorphicMethodState methodState) {
- Int2ReferenceMap<ParameterNode> parameterNodesForMethod =
- parameterNodes.computeIfAbsent(
- method.getReference(), ignoreKey(Int2ReferenceOpenHashMap::new));
- return parameterNodesForMethod.compute(
- parameterIndex,
- (ignore, parameterNode) ->
- parameterNode != null
- ? parameterNode
- : new ParameterNode(
- method, methodState, parameterIndex, method.getArgumentType(parameterIndex)));
- }
-
- private ProgramMethod getEnclosingMethod(MethodParameter methodParameter) {
- DexMethod methodReference = methodParameter.getMethod();
- return methodReference.lookupOnProgramClass(
- asProgramClassOrNull(appView.definitionFor(methodParameter.getMethod().getHolderType())));
- }
-
- private ValueState getFieldState(ProgramField field) {
- if (field == null) {
- // Conservatively return unknown if for some reason we can't find the field.
- assert false;
- return ValueState.unknown();
- }
- return fieldStates.get(field);
- }
-
- private MethodState getMethodState(ProgramMethod method) {
- if (method == null) {
- // Conservatively return unknown if for some reason we can't find the method.
- assert false;
- return MethodState.unknown();
- }
- return methodStates.get(method);
- }
-
- @Override
- public ValueState getState(DexField field) {
- return fieldNodes.get(field).getState();
- }
-
- @Override
- public ValueState getState(BaseInFlow inFlow, Supplier<ValueState> defaultStateProvider) {
- if (inFlow.isFieldValue()) {
- FieldValue fieldValue = inFlow.asFieldValue();
- return getState(fieldValue.getField());
- } else {
- assert inFlow.isMethodParameter();
- MethodParameter methodParameter = inFlow.asMethodParameter();
- ParameterNode parameterNode =
- parameterNodes
- .getOrDefault(methodParameter.getMethod(), Int2ReferenceMaps.emptyMap())
- .get(methodParameter.getIndex());
- if (parameterNode != null) {
- return parameterNode.getState();
- }
- assert verifyMissingParameterStateIsBottom(methodParameter);
- return defaultStateProvider.get();
- }
- }
-
- private boolean verifyMissingParameterStateIsBottom(MethodParameter methodParameter) {
- ProgramMethod enclosingMethod = getEnclosingMethod(methodParameter);
- if (enclosingMethod == null) {
- assert converter
- .getInliner()
- .verifyIsPrunedDueToSingleCallerInlining(methodParameter.getMethod());
- return true;
- }
- MethodState enclosingMethodState = getMethodState(enclosingMethod);
- return enclosingMethodState.isBottom();
- }
- }
-
- public abstract static class Node {
-
- private final Set<Node> predecessors = Sets.newIdentityHashSet();
- private final Map<Node, Set<AbstractFunction>> successors = new IdentityHashMap<>();
-
- private boolean inWorklist = true;
-
- void addState(
- AppView<AppInfoWithLiveness> appView,
- ConcreteValueState stateToAdd,
- Action onChangedAction) {
- ValueState oldState = getState();
- ValueState newState =
- oldState.mutableJoin(
- appView, stateToAdd, getStaticType(), StateCloner.getCloner(), onChangedAction);
- if (newState != oldState) {
- setState(newState);
- onChangedAction.execute();
- }
- }
-
- abstract ValueState getState();
-
- abstract DexType getStaticType();
-
- abstract void setState(ValueState valueState);
-
- void setStateToUnknown() {
- setState(ValueState.unknown());
- }
-
- void addPredecessor(Node predecessor, AbstractFunction abstractFunction) {
- predecessor.successors.computeIfAbsent(this, ignoreKey(HashSet::new)).add(abstractFunction);
- predecessors.add(predecessor);
- }
-
- void clearPredecessors() {
- for (Node predecessor : predecessors) {
- predecessor.successors.remove(this);
- }
- predecessors.clear();
- }
-
- void clearPredecessors(Node cause) {
- for (Node predecessor : predecessors) {
- if (predecessor != cause) {
- predecessor.successors.remove(this);
- }
- }
- predecessors.clear();
- }
-
- Set<Node> getPredecessors() {
- return predecessors;
- }
-
- boolean hasPredecessors() {
- return !predecessors.isEmpty();
- }
-
- void clearDanglingSuccessors() {
- successors.clear();
- }
-
- Set<Node> getSuccessors() {
- return successors.keySet();
- }
-
- public void forEachSuccessor(BiConsumer<Node, Set<AbstractFunction>> consumer) {
- successors.forEach(consumer);
- }
-
- public void removeSuccessorIf(BiPredicate<Node, Set<AbstractFunction>> predicate) {
- successors.entrySet().removeIf(entry -> predicate.test(entry.getKey(), entry.getValue()));
- }
-
- boolean hasSuccessors() {
- return !successors.isEmpty();
- }
-
- boolean isBottom() {
- return getState().isBottom();
- }
-
- boolean isFieldNode() {
- return false;
- }
-
- FieldNode asFieldNode() {
- return null;
- }
-
- boolean isParameterNode() {
- return false;
- }
-
- ParameterNode asParameterNode() {
- return null;
- }
-
- boolean isEffectivelyUnknown() {
- return getState().isConcrete() && getState().asConcrete().isEffectivelyUnknown();
- }
-
- boolean isUnknown() {
- return getState().isUnknown();
- }
-
- // No need to enqueue the affected node if it is already in the worklist or if it does not have
- // any successors (i.e., the successor is a leaf).
- void addToWorkList(Deque<Node> worklist) {
- if (!inWorklist && hasSuccessors()) {
- worklist.add(this);
- inWorklist = true;
- }
- }
-
- void unsetInWorklist() {
- assert inWorklist;
- inWorklist = false;
- }
- }
-
- public static class FieldNode extends Node {
-
- private final ProgramField field;
- private ValueState fieldState;
-
- FieldNode(ProgramField field, ValueState fieldState) {
- this.field = field;
- this.fieldState = fieldState;
- }
-
- public ProgramField getField() {
- return field;
- }
-
- @Override
- DexType getStaticType() {
- return field.getType();
- }
-
- void addDefaultValue(AppView<AppInfoWithLiveness> appView, Action onChangedAction) {
- AbstractValueFactory abstractValueFactory = appView.abstractValueFactory();
- AbstractValue defaultValue;
- if (field.getAccessFlags().isStatic() && field.getDefinition().hasExplicitStaticValue()) {
- defaultValue = field.getDefinition().getStaticValue().toAbstractValue(abstractValueFactory);
- } else if (field.getType().isPrimitiveType()) {
- defaultValue = abstractValueFactory.createZeroValue();
- } else {
- defaultValue = abstractValueFactory.createUncheckedNullValue();
- }
- NonEmptyValueState fieldStateToAdd;
- if (field.getType().isArrayType()) {
- Nullability defaultNullability = Nullability.definitelyNull();
- fieldStateToAdd = ConcreteArrayTypeValueState.create(defaultNullability);
- } else if (field.getType().isClassType()) {
- assert defaultValue.isNull() || defaultValue.isSingleStringValue();
- DynamicType dynamicType =
- defaultValue.isNull()
- ? DynamicType.definitelyNull()
- : DynamicType.createExact(
- TypeElement.stringClassType(appView, Nullability.definitelyNotNull()));
- fieldStateToAdd = ConcreteClassTypeValueState.create(defaultValue, dynamicType);
- } else {
- assert field.getType().isPrimitiveType();
- fieldStateToAdd = ConcretePrimitiveTypeValueState.create(defaultValue);
- }
- if (fieldStateToAdd.isConcrete()) {
- addState(appView, fieldStateToAdd.asConcrete(), onChangedAction);
- } else {
- // We should always be able to map static field values to an unknown abstract value.
- assert false;
- setStateToUnknown();
- onChangedAction.execute();
- }
- }
-
- @Override
- ValueState getState() {
- return fieldState;
- }
-
- @Override
- void setState(ValueState fieldState) {
- this.fieldState = fieldState;
- }
-
- @Override
- boolean isFieldNode() {
- return true;
- }
-
- @Override
- FieldNode asFieldNode() {
- return this;
- }
- }
-
- static class ParameterNode extends Node {
-
- private final ProgramMethod method;
- private final ConcreteMonomorphicMethodState methodState;
- private final int parameterIndex;
- private final DexType parameterType;
-
- ParameterNode(
- ProgramMethod method,
- ConcreteMonomorphicMethodState methodState,
- int parameterIndex,
- DexType parameterType) {
- this.method = method;
- this.methodState = methodState;
- this.parameterIndex = parameterIndex;
- this.parameterType = parameterType;
- }
-
- ProgramMethod getMethod() {
- return method;
- }
-
- int getParameterIndex() {
- return parameterIndex;
- }
-
- @Override
- DexType getStaticType() {
- return parameterType;
- }
-
- @Override
- ValueState getState() {
- return methodState.getParameterState(parameterIndex);
- }
-
- @Override
- void setState(ValueState parameterState) {
- methodState.setParameterState(parameterIndex, parameterState);
- }
-
- @Override
- boolean isParameterNode() {
- return true;
- }
-
- @Override
- ParameterNode asParameterNode() {
- return this;
- }
- }
}