Split type resolving in two phases.
In general we may need a fixed point computation of types to fully resolve all
types in the presence of array-put and array-get in DEX input.
Bug: 119217869, 119401913
Change-Id: I468dec7df4f714e897186f076b9b7cd8612fff8d
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
index 192b36f..dd6a952 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
@@ -26,6 +26,8 @@
NARROWING, // updating with more specific info, e.g., passing the return value of the inlinee.
}
+ private final boolean mayHaveImpreciseTypes;
+
private Mode mode = Mode.UNSET;
private final AppInfo appInfo;
@@ -33,9 +35,15 @@
private final Deque<Value> worklist = new ArrayDeque<>();
- public TypeAnalysis(AppInfo appInfo, DexEncodedMethod encodedMethod) {
+ public TypeAnalysis(
+ AppInfo appInfo, DexEncodedMethod encodedMethod, boolean mayHaveImpreciseTypes) {
this.appInfo = appInfo;
this.context = encodedMethod;
+ this.mayHaveImpreciseTypes = mayHaveImpreciseTypes;
+ }
+
+ public TypeAnalysis(AppInfo appInfo, DexEncodedMethod encodedMethod) {
+ this(appInfo, encodedMethod, false);
}
private void analyze() {
@@ -107,11 +115,13 @@
}
private void analyzeValue(Value value) {
+ TypeLatticeElement previous = value.getTypeLattice();
TypeLatticeElement derived =
value.isPhi()
? value.asPhi().computePhiType(appInfo)
: value.definition.evaluate(appInfo);
- assert derived.isPreciseType();
+ assert mayHaveImpreciseTypes || derived.isPreciseType();
+ assert !previous.isPreciseType() || derived.isPreciseType();
updateTypeOfValue(value, derived);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
index 1f4d942..edfff9c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.code.AgetShort;
import com.android.tools.r8.code.AgetWide;
import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexItemFactory;
@@ -21,12 +22,13 @@
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.conversion.TypeConstraintResolver;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
import java.util.Arrays;
-public class ArrayGet extends Instruction {
+public class ArrayGet extends Instruction implements ImpreciseMemberTypeInstruction {
private MemberType type;
@@ -47,6 +49,7 @@
return inValues.get(1);
}
+ @Override
public MemberType getMemberType() {
return type;
}
@@ -188,23 +191,30 @@
case DOUBLE:
return TypeLatticeElement.DOUBLE;
case INT_OR_FLOAT:
+ return checkConstraint(dest(), ValueType.INT_OR_FLOAT);
case LONG_OR_DOUBLE:
- throw new Unreachable("Unexpected imprecise type: " + getMemberType());
+ return checkConstraint(dest(), ValueType.LONG_OR_DOUBLE);
default:
throw new Unreachable("Unexpected member type: " + getMemberType());
}
}
+ private static TypeLatticeElement checkConstraint(Value value, ValueType constraint) {
+ TypeLatticeElement latticeElement = value.constrainedType(constraint);
+ if (latticeElement != null) {
+ return latticeElement;
+ }
+ throw new CompilationError(
+ "Failure to constrain value: " + value + " by constraint: " + constraint);
+ }
+
@Override
public boolean throwsNpeIfValueIsNull(Value value, DexItemFactory dexItemFactory) {
return array() == value;
}
@Override
- public boolean constrainType() {
- if (!type.isPrecise()) {
- type = MemberType.constrainedType(type, ValueType.fromTypeLattice(dest().getTypeLattice()));
- }
- return type != null;
+ public void constrainType(TypeConstraintResolver constraintResolver) {
+ constraintResolver.constrainArrayMemberType(type, dest(), array(), t -> type = t);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
index 32e7259..7263948 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
@@ -19,12 +19,13 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.conversion.TypeConstraintResolver;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
import java.util.Arrays;
-public class ArrayPut extends Instruction {
+public class ArrayPut extends Instruction implements ImpreciseMemberTypeInstruction {
// Input values are ordered according to the stack order of the Java bytecode astore.
private static final int ARRAY_INDEX = 0;
@@ -53,6 +54,7 @@
return inValues.get(VALUE_INDEX);
}
+ @Override
public MemberType getMemberType() {
return type;
}
@@ -181,10 +183,7 @@
}
@Override
- public boolean constrainType() {
- if (!type.isPrecise()) {
- type = MemberType.constrainedType(type, ValueType.fromTypeLattice(value().getTypeLattice()));
- }
- return type != null;
+ public void constrainType(TypeConstraintResolver constraintResolver) {
+ constraintResolver.constrainArrayMemberType(type, value(), array(), t -> type = t);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
index 9990066..4d8f8d2 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -263,7 +263,6 @@
@Override
public TypeLatticeElement evaluate(AppInfo appInfo) {
- assert outValue().getTypeLattice().isPreciseType();
return outValue().getTypeLattice();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
index e3fb465..2900491 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.code;
import com.android.tools.r8.graph.DexField;
+import java.util.Collections;
import java.util.List;
public abstract class FieldInstruction extends Instruction {
@@ -11,20 +12,15 @@
private MemberType type;
private final DexField field;
- protected FieldInstruction(MemberType type, DexField field, Value dest, Value value) {
- super(dest, value);
- assert type != null;
- assert field != null;
- this.type = type;
- this.field = field;
+ protected FieldInstruction(DexField field, Value dest, Value value) {
+ this(field, dest, Collections.singletonList(value));
}
- protected FieldInstruction(MemberType type, DexField field, Value dest, List<Value> inValues) {
+ protected FieldInstruction(DexField field, Value dest, List<Value> inValues) {
super(dest, inValues);
- assert type != null;
assert field != null;
- this.type = type;
this.field = field;
+ this.type = MemberType.fromDexType(field.type);
}
public MemberType getType() {
@@ -35,8 +31,6 @@
return field;
}
- abstract Value getFieldInOrOutValue();
-
@Override
public boolean isFieldInstruction() {
return true;
@@ -52,14 +46,4 @@
// TODO(jsjeon): what if the target field is known to be non-null?
return true;
}
-
- @Override
- public boolean constrainType() {
- if (!type.isPrecise()) {
- type =
- MemberType.constrainedType(
- type, ValueType.fromTypeLattice(getFieldInOrOutValue().getTypeLattice()));
- }
- return type != null;
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index dd0e174..58ada51 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -465,7 +465,7 @@
assert consistentDefUseChains();
assert validThrowingInstructions();
assert noCriticalEdges();
- assert verifyNoImpreciseTypes();
+ assert verifyNoImpreciseOrBottomTypes();
return true;
}
@@ -675,26 +675,19 @@
return true;
}
- public boolean verifyNoImpreciseTypes() {
+ public boolean verifyNoImpreciseOrBottomTypes() {
return verifySSATypeLattice(
v -> {
assert v.getTypeLattice().isPreciseType();
- if (v.definition != null) {
- assert !v.definition.isArrayGet()
- || v.definition.asArrayGet().getMemberType().isPrecise();
- assert !v.definition.isArrayPut()
- || v.definition.asArrayPut().getMemberType().isPrecise();
- assert !v.definition.isFieldInstruction()
- || v.definition.asFieldInstruction().getType().isPrecise();
- }
+ // For now we assume no bottom types on IR values. We may want to reconsider this for
+ // representing unreachable code.
+ assert !v.getTypeLattice().isBottom();
+ assert !(v.definition instanceof ImpreciseMemberTypeInstruction)
+ || ((ImpreciseMemberTypeInstruction) v.definition).getMemberType().isPrecise();
return true;
});
}
- public boolean verifyNoBottomTypes() {
- return verifySSATypeLattice(v -> !v.getTypeLattice().isBottom());
- }
-
private boolean verifySSATypeLattice(Predicate<Value> tester) {
for (BasicBlock block : blocks) {
for (Instruction instruction : block.getInstructions()) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/ImpreciseMemberTypeInstruction.java b/src/main/java/com/android/tools/r8/ir/code/ImpreciseMemberTypeInstruction.java
new file mode 100644
index 0000000..2b5bc02
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ImpreciseMemberTypeInstruction.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2018, 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.ir.code;
+
+import com.android.tools.r8.ir.conversion.TypeConstraintResolver;
+
+public interface ImpreciseMemberTypeInstruction {
+ MemberType getMemberType();
+
+ void constrainType(TypeConstraintResolver constraintResolver);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index e92f97b..66a6c3e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -29,8 +29,8 @@
public class InstanceGet extends FieldInstruction {
- public InstanceGet(MemberType type, Value dest, Value object, DexField field) {
- super(type, field, dest, object);
+ public InstanceGet(Value dest, Value object, DexField field) {
+ super(field, dest, object);
}
public Value dest() {
@@ -43,11 +43,6 @@
}
@Override
- Value getFieldInOrOutValue() {
- return dest();
- }
-
- @Override
public void buildDex(DexBuilder builder) {
int destRegister = builder.allocatedRegister(dest(), getNumber());
int objectRegister = builder.allocatedRegister(object(), getNumber());
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index 8bd5061..2d70317 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -27,8 +27,8 @@
public class InstancePut extends FieldInstruction {
- public InstancePut(MemberType type, DexField field, Value object, Value value) {
- super(type, field, null, Arrays.asList(object, value));
+ public InstancePut(DexField field, Value object, Value value) {
+ super(field, null, Arrays.asList(object, value));
assert object().verifyCompatible(ValueType.OBJECT);
assert value().verifyCompatible(ValueType.fromDexType(field.type));
}
@@ -42,11 +42,6 @@
}
@Override
- Value getFieldInOrOutValue() {
- return value();
- }
-
- @Override
public void buildDex(DexBuilder builder) {
com.android.tools.r8.code.Instruction instruction;
int valueRegister = builder.allocatedRegister(value(), getNumber());
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index eb4fbe7..422f160 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -1253,8 +1253,4 @@
assert !instructionTypeCanThrow() || getPosition().isSome() || getPosition().isSyntheticNone();
return true;
}
-
- public boolean constrainType() {
- return true;
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index ec150da..537ca84 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -27,8 +27,8 @@
public class StaticGet extends FieldInstruction {
- public StaticGet(MemberType type, Value dest, DexField field) {
- super(type, field, dest, (Value) null);
+ public StaticGet(Value dest, DexField field) {
+ super(field, dest, (Value) null);
}
public Value dest() {
@@ -36,11 +36,6 @@
}
@Override
- Value getFieldInOrOutValue() {
- return dest();
- }
-
- @Override
public void buildDex(DexBuilder builder) {
com.android.tools.r8.code.Instruction instruction;
int dest = builder.allocatedRegister(dest(), getNumber());
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index e36acb7..ba33cbf 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -24,8 +24,8 @@
public class StaticPut extends FieldInstruction {
- public StaticPut(MemberType type, Value source, DexField field) {
- super(type, field, null, source);
+ public StaticPut(Value source, DexField field) {
+ super(field, null, source);
}
public Value inValue() {
@@ -34,11 +34,6 @@
}
@Override
- Value getFieldInOrOutValue() {
- return inValue();
- }
-
- @Override
public void buildDex(DexBuilder builder) {
com.android.tools.r8.code.Instruction instruction;
int src = builder.allocatedRegister(inValue(), getNumber());
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index c597702..5bb39b1 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -53,6 +53,7 @@
import com.android.tools.r8.ir.code.Goto;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.If;
+import com.android.tools.r8.ir.code.ImpreciseMemberTypeInstruction;
import com.android.tools.r8.ir.code.InstanceGet;
import com.android.tools.r8.ir.code.InstanceOf;
import com.android.tools.r8.ir.code.InstancePut;
@@ -93,11 +94,9 @@
import com.android.tools.r8.ir.code.Xor;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Pair;
-import com.android.tools.r8.utils.StringDiagnostic;
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
@@ -405,7 +404,7 @@
private int nextBlockNumber = 0;
// Flag indicating if any instructions have imprecise internal types (eg, int|float member types)
- private List<Instruction> impreciseInstructions = null;
+ private List<ImpreciseMemberTypeInstruction> impreciseInstructions = null;
// Flag indicating if any values have imprecise types.
private boolean hasImpreciseValues = false;
@@ -599,30 +598,16 @@
ir.removeAllTrivialPhis(this);
ir.removeUnreachableBlocks();
- // Constrain all values to precise types.
- if (hasImpreciseValues) {
- TypeConstraintResolver resolver = new TypeConstraintResolver();
- resolver.resolve(ir, this, options.reporter);
+ // Compute precise types for all values.
+ if (hasImpreciseValues || impreciseInstructions != null) {
+ // In DEX we may need to constrain all values and instructions to precise types.
+ assert source instanceof DexSourceCode;
+ new TypeConstraintResolver(this, options.reporter)
+ .resolve(impreciseInstructions, ir, appInfo, method, context);
+ } else {
+ new TypeAnalysis(appInfo, context).widening(method, ir);
}
- // Constrain instructions to precise types (internally these will use the value types).
- if (impreciseInstructions != null) {
- for (Instruction instruction : impreciseInstructions) {
- if (!instruction.constrainType()) {
- throw options.reporter.fatalError(
- new StringDiagnostic(
- "Cannot determine precise type for instruction: " + instruction,
- origin,
- new MethodPosition(method.method)));
- }
- }
- }
-
- assert ir.verifyNoImpreciseTypes();
-
- new TypeAnalysis(appInfo, context).widening(method, ir);
-
- assert ir.verifyNoBottomTypes();
assert ir.isConsistentSSA();
// Clear the code so we don't build multiple times.
@@ -635,7 +620,7 @@
value.constrainType(constraint, method.method, origin, options.reporter);
}
- private void addImpreciseInstruction(Instruction instruction) {
+ private void addImpreciseInstruction(ImpreciseMemberTypeInstruction instruction) {
if (impreciseInstructions == null) {
impreciseInstructions = new ArrayList<>();
}
@@ -1217,16 +1202,12 @@
}
public void addInstanceGet(int dest, int object, DexField field) {
- MemberType type = MemberType.fromDexType(field.type);
Value in = readRegister(object, ValueType.OBJECT);
Value out = writeRegister(
dest, TypeLatticeElement.fromDexType(field.type, true, appInfo), ThrowingInfo.CAN_THROW);
- out.setKnownToBeBoolean(type == MemberType.BOOLEAN);
- InstanceGet instruction = new InstanceGet(type, out, in, field);
+ out.setKnownToBeBoolean(field.type == getFactory().booleanType);
+ InstanceGet instruction = new InstanceGet(out, in, field);
assert instruction.instructionTypeCanThrow();
- if (!type.isPrecise()) {
- addImpreciseInstruction(instruction);
- }
addInstruction(instruction);
}
@@ -1239,13 +1220,9 @@
}
public void addInstancePut(int value, int object, DexField field) {
- MemberType type = MemberType.fromDexType(field.type);
Value objectValue = readRegister(object, ValueType.OBJECT);
- Value valueValue = readRegister(value, ValueType.fromMemberType(type));
- InstancePut instruction = new InstancePut(type, field, objectValue, valueValue);
- if (!type.isPrecise()) {
- addImpreciseInstruction(instruction);
- }
+ Value valueValue = readRegister(value, ValueType.fromDexType(field.type));
+ InstancePut instruction = new InstancePut(field, objectValue, valueValue);
add(instruction);
}
@@ -1575,26 +1552,18 @@
}
public void addStaticGet(int dest, DexField field) {
- MemberType type = MemberType.fromDexType(field.type);
Value out = writeRegister(
dest, TypeLatticeElement.fromDexType(field.type, true, appInfo), ThrowingInfo.CAN_THROW);
- out.setKnownToBeBoolean(type == MemberType.BOOLEAN);
- StaticGet instruction = new StaticGet(type, out, field);
+ out.setKnownToBeBoolean(field.type == getFactory().booleanType);
+ StaticGet instruction = new StaticGet(out, field);
assert instruction.instructionTypeCanThrow();
- if (!type.isPrecise()) {
- addImpreciseInstruction(instruction);
- }
addInstruction(instruction);
}
public void addStaticPut(int value, DexField field) {
- MemberType type = MemberType.fromDexType(field.type);
- Value in = readRegister(value, ValueType.fromMemberType(type));
- StaticPut instruction = new StaticPut(type, in, field);
+ Value in = readRegister(value, ValueType.fromDexType(field.type));
+ StaticPut instruction = new StaticPut(in, field);
assert instruction.instructionTypeCanThrow();
- if (!type.isPrecise()) {
- addImpreciseInstruction(instruction);
- }
add(instruction);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index a42b87b..d7f56ee 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -212,7 +212,6 @@
if (actualField != field) {
InstanceGet newInstanceGet =
new InstanceGet(
- instanceGet.getType(),
makeOutValue(instanceGet, code, newSSAValues),
instanceGet.object(),
actualField);
@@ -224,8 +223,7 @@
DexField actualField = graphLense.lookupField(field);
if (actualField != field) {
InstancePut newInstancePut =
- new InstancePut(
- instancePut.getType(), actualField, instancePut.object(), instancePut.value());
+ new InstancePut(actualField, instancePut.object(), instancePut.value());
iterator.replaceCurrentInstruction(newInstancePut);
}
} else if (current.isStaticGet()) {
@@ -234,8 +232,7 @@
DexField actualField = graphLense.lookupField(field);
if (actualField != field) {
StaticGet newStaticGet =
- new StaticGet(
- staticGet.getType(), makeOutValue(staticGet, code, newSSAValues), actualField);
+ new StaticGet(makeOutValue(staticGet, code, newSSAValues), actualField);
iterator.replaceCurrentInstruction(newStaticGet);
}
} else if (current.isStaticPut()) {
@@ -243,8 +240,7 @@
DexField field = staticPut.getField();
DexField actualField = graphLense.lookupField(field);
if (actualField != field) {
- StaticPut newStaticPut =
- new StaticPut(staticPut.getType(), staticPut.inValue(), actualField);
+ StaticPut newStaticPut = new StaticPut(staticPut.inValue(), actualField);
iterator.replaceCurrentInstruction(newStaticPut);
}
} else if (current.isCheckCast()) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/TypeConstraintResolver.java b/src/main/java/com/android/tools/r8/ir/conversion/TypeConstraintResolver.java
index 927c432..01fa20c 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/TypeConstraintResolver.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/TypeConstraintResolver.java
@@ -5,21 +5,31 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.analysis.type.ArrayTypeLatticeElement;
+import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.ir.code.ArrayPut;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.If;
+import com.android.tools.r8.ir.code.ImpreciseMemberTypeInstruction;
import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
+import com.google.common.collect.ImmutableSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
/**
* Type constraint resolver that ensures that all SSA values have a "precise" type, ie, every value
@@ -39,8 +49,15 @@
*/
public class TypeConstraintResolver {
+ private final IRBuilder builder;
+ private final Reporter reporter;
private final Map<Value, Value> unificationParents = new HashMap<>();
+ public TypeConstraintResolver(IRBuilder builder, Reporter reporter) {
+ this.builder = builder;
+ this.reporter = reporter;
+ }
+
public static ValueType constraintForType(TypeLatticeElement type) {
if (type.isTop()) {
return ValueType.INT_OR_FLOAT_OR_NULL;
@@ -96,7 +113,23 @@
}
}
- public void resolve(IRCode code, IRBuilder builder, Reporter reporter) {
+ public void resolve(
+ List<ImpreciseMemberTypeInstruction> impreciseInstructions,
+ IRCode code,
+ AppInfo appInfo,
+ DexEncodedMethod method,
+ DexEncodedMethod context) {
+ // Round one will resolve at least all object vs single types.
+ List<Value> remainingImpreciseValues = resolveRoundOne(code);
+ // Round two will resolve any remaining single and wide types. These can depend on the types
+ // of array instructions, thus we need to complete the type fixed point prior to resolving.
+ new TypeAnalysis(appInfo, context, true).widening(method, code);
+ // Round two resolves any remaining imprecision and finally selects a final precise type for
+ // any unconstrained imprecise type.
+ resolveRoundTwo(code, impreciseInstructions, remainingImpreciseValues);
+ }
+
+ private List<Value> resolveRoundOne(IRCode code) {
List<Value> impreciseValues = new ArrayList<>();
for (BasicBlock block : code.blocks) {
for (Phi phi : block.getPhis()) {
@@ -123,38 +156,113 @@
}
}
}
- for (Value value : impreciseValues) {
- builder.constrainType(value, getCanonicalTypeConstraint(value));
- if (!value.getTypeLattice().isPreciseType()) {
- throw reporter.fatalError(
- new StringDiagnostic(
- "Cannot determine precise type for value: "
- + value
- + ", its imprecise type is: "
- + value.getTypeLattice(),
- code.origin,
- new MethodPosition(code.method.method)));
+ return constrainValues(false, impreciseValues);
+ }
+
+ private void resolveRoundTwo(
+ IRCode code,
+ List<ImpreciseMemberTypeInstruction> impreciseInstructions,
+ List<Value> remainingImpreciseValues) {
+ if (impreciseInstructions != null) {
+ for (ImpreciseMemberTypeInstruction impreciseInstruction : impreciseInstructions) {
+ impreciseInstruction.constrainType(this);
}
}
+ ArrayList<Value> stillImprecise = constrainValues(true, remainingImpreciseValues);
+ if (!stillImprecise.isEmpty()) {
+ throw reporter.fatalError(
+ new StringDiagnostic(
+ "Cannot determine precise type for value: "
+ + stillImprecise.get(0)
+ + ", its imprecise type is: "
+ + stillImprecise.get(0).getTypeLattice(),
+ code.origin,
+ new MethodPosition(code.method.method)));
+ }
+ }
+
+ private ArrayList<Value> constrainValues(boolean finished, List<Value> impreciseValues) {
+ ArrayList<Value> stillImprecise = new ArrayList<>(impreciseValues.size());
+ for (Value value : impreciseValues) {
+ builder.constrainType(value, getCanonicalTypeConstraint(value, finished));
+ if (!value.getTypeLattice().isPreciseType()) {
+ stillImprecise.add(value);
+ }
+ }
+ return stillImprecise;
+ }
+
+ public void constrainArrayMemberType(
+ MemberType type, Value value, Value array, Consumer<MemberType> setter) {
+ assert !type.isPrecise();
+ Value canonical = canonical(value);
+ ValueType constraint;
+ if (array.getTypeLattice().isArrayType()) {
+ // If the array type is known it uniquely defines the actual member type.
+ ArrayTypeLatticeElement arrayType = array.getTypeLattice().asArrayTypeLatticeElement();
+ constraint = ValueType.fromDexType(arrayType.getArrayElementType(builder.getFactory()));
+ } else {
+ // If not, e.g., the array input is null, the canonical value determines the final type.
+ constraint = getCanonicalTypeConstraint(canonical, true);
+ }
+ // Constrain the canonical value by the final and precise type constraint and set the member.
+ // A refinement of the value type will then be propagated in constrainValues of "round two".
+ builder.constrainType(canonical, constraint);
+ setter.accept(MemberType.constrainedType(type, constraint));
}
private void merge(Value value1, Value value2) {
link(canonical(value1), canonical(value2));
}
- private ValueType getCanonicalTypeConstraint(Value value) {
+ private ValueType getCanonicalTypeConstraint(Value value, boolean finished) {
ValueType type = constraintForType(canonical(value).getTypeLattice());
switch (type) {
- case INT_OR_FLOAT:
case INT_OR_FLOAT_OR_NULL:
- return ValueType.INT;
+ // There is never a second round for resolving object vs single.
+ assert !finished;
+ return ValueType.INT_OR_FLOAT;
+ case INT_OR_FLOAT:
+ assert !finished || verifyNoConstrainedUses(value);
+ return finished ? ValueType.INT : type;
case LONG_OR_DOUBLE:
- return ValueType.LONG;
+ assert !finished || verifyNoConstrainedUses(value);
+ return finished ? ValueType.LONG : type;
default:
return type;
}
}
+ private static boolean verifyNoConstrainedUses(Value value) {
+ return verifyNoConstrainedUses(value, ImmutableSet.of());
+ }
+
+ private static boolean verifyNoConstrainedUses(Value value, Set<Value> assumeNoConstrainedUses) {
+ for (Instruction user : value.uniqueUsers()) {
+ if (user.isIf()) {
+ If ifInstruction = user.asIf();
+ if (ifInstruction.isZeroTest()) {
+ continue;
+ }
+ Value other = ifInstruction.inValues().get(1 - ifInstruction.inValues().indexOf(value));
+ if (assumeNoConstrainedUses.contains(other)) {
+ continue;
+ }
+ assert verifyNoConstrainedUses(
+ other,
+ ImmutableSet.<Value>builder().addAll(assumeNoConstrainedUses).add(value).build());
+ } else if (user.isArrayPut()) {
+ ArrayPut put = user.asArrayPut();
+ assert value == put.value();
+ assert !put.getMemberType().isPrecise();
+ assert put.array().getTypeLattice().isDefinitelyNull();
+ } else {
+ assert false;
+ }
+ }
+ return true;
+ }
+
// Link two values as having the same type.
private void link(Value canonical1, Value canonical2) {
if (canonical1 == canonical2) {
@@ -196,5 +304,4 @@
}
return value;
}
-
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index b89a185..b92d772 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -22,7 +22,6 @@
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeCustom;
import com.android.tools.r8.ir.code.InvokeDirect;
-import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.Value;
@@ -291,7 +290,7 @@
// reading the value of INSTANCE field created for singleton lambda class.
if (lambdaClass.isStateless()) {
instructions.replaceCurrentInstruction(
- new StaticGet(MemberType.OBJECT, lambdaInstanceValue, lambdaClass.instanceField));
+ new StaticGet(lambdaInstanceValue, lambdaClass.instanceField));
// Note that since we replace one throwing operation with another we don't need
// to have any special handling for catch handlers.
return;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index c6aabc8..dfb87c6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -69,7 +69,6 @@
import com.android.tools.r8.ir.code.InvokeNewArray;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.InvokeVirtual;
-import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.NewArrayEmpty;
import com.android.tools.r8.ir.code.NewArrayFilledData;
import com.android.tools.r8.ir.code.NewInstance;
@@ -3264,8 +3263,8 @@
DexMethod printLn = dexItemFactory.createMethod(javaIoPrintStreamType, proto, "println");
iterator.add(
- new StaticGet(MemberType.OBJECT, out,
- dexItemFactory.createField(javaLangSystemType, javaIoPrintStreamType, "out")));
+ new StaticGet(
+ out, dexItemFactory.createField(javaLangSystemType, javaIoPrintStreamType, "out")));
Value value = addConstString(code, iterator, "INVOKE ");
iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, value)));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
index 51da91d..c137cfd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
@@ -148,9 +148,11 @@
// We need to insert remapped values and in case the capture field
// of type Object optionally cast to expected field.
- InstanceGet newInstanceGet = new InstanceGet(instanceGet.getType(),
- createValueForType(context, fieldType), instanceGet.object(),
- mapCaptureField(context.factory, field.clazz, field));
+ InstanceGet newInstanceGet =
+ new InstanceGet(
+ createValueForType(context, fieldType),
+ instanceGet.object(),
+ mapCaptureField(context.factory, field.clazz, field));
context.instructions().replaceCurrentInstruction(newInstanceGet);
if (fieldType.isPrimitiveType() || fieldType == context.factory.objectType) {
@@ -185,7 +187,6 @@
public void patch(CodeProcessor context, StaticGet staticGet) {
context.instructions().replaceCurrentInstruction(
new StaticGet(
- staticGet.getType(),
context.code.createValue(
TypeLatticeElement.fromDexType(staticGet.getField().type, true, context.appInfo)),
mapSingletonInstanceField(context.factory, staticGet.getField())));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index 9147fe8..093ac43 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -20,7 +20,6 @@
import com.android.tools.r8.ir.code.InstructionIterator;
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.ir.code.InvokeStatic;
-import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Value;
@@ -305,7 +304,6 @@
assert outValue != null;
it.replaceCurrentInstruction(
new StaticGet(
- MemberType.fromDexType(field.type),
code.createValue(
TypeLatticeElement.fromDexType(field.type, true, classStaticizer.appInfo),
outValue.getLocalInfo()),
@@ -320,9 +318,7 @@
StaticPut staticPut = instruction.asStaticPut();
DexField field = mapFieldIfMoved(staticPut.getField());
if (field != staticPut.getField()) {
- it.replaceCurrentInstruction(
- new StaticPut(MemberType.fromDexType(field.type), staticPut.inValue(), field)
- );
+ it.replaceCurrentInstruction(new StaticPut(staticPut.inValue(), field));
}
continue;
}
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
index 7904cd1..ac239bf 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -157,13 +157,11 @@
}
if (instruction.isStaticPut()) {
StaticPut staticPut = instruction.asStaticPut();
- iterator.replaceCurrentInstruction(
- new StaticPut(staticPut.getType(), newIn, field));
+ iterator.replaceCurrentInstruction(new StaticPut(newIn, field));
} else {
assert instruction.isInstancePut();
InstancePut instancePut = instruction.asInstancePut();
- iterator.replaceCurrentInstruction(
- new InstancePut(instancePut.getType(), field, instancePut.object(), newIn));
+ iterator.replaceCurrentInstruction(new InstancePut(field, instancePut.object(), newIn));
}
encodedMethod.markUseIdentifierNameString();
} else if (instruction.isInvokeMethod()) {
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 529f11c..3618a5e 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -553,13 +553,6 @@
// Tests where the output of R8 fails when run with Art.
private static final Multimap<String, TestCondition> failingRunWithArt =
new ImmutableListMultimap.Builder<String, TestCondition>()
- // TODO(b/119217869): Re-enable this test once fixed.
- .put(
- "614-checker-dump-constant-location",
- TestCondition.match(
- TestCondition.tools(DexTool.DX),
- TestCondition.R8_COMPILER,
- TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
// The growth limit test fails after processing by R8 because R8 will eliminate an
// "unneeded" const store. The following reflective call to the VM's GC will then see the
// large array as still live and the subsequent allocations will fail to reach the desired
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index 3eb3744..221d427 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -99,10 +99,7 @@
test));
fullTestList.add(makeTest(Input.JAVAC_ALL, CompilerUnderTest.R8, CompilationMode.DEBUG,
test));
- // TODO(b/119217869): Re-enable this test when fixed.
- if (!test.equals("regress_37726195.Regress")) {
- fullTestList.add(makeTest(Input.DX, CompilerUnderTest.R8, CompilationMode.RELEASE, test));
- }
+ fullTestList.add(makeTest(Input.DX, CompilerUnderTest.R8, CompilationMode.RELEASE, test));
}
fullTestList.add(
makeTest(
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/ArrayTypeTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/ArrayTypeTest.java
index f508934..ac73d7d 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/ArrayTypeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/ArrayTypeTest.java
@@ -16,7 +16,6 @@
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.Value;
import java.util.function.Consumer;
-import org.junit.Ignore;
import org.junit.Test;
public class ArrayTypeTest extends TypeAnalysisTestBase {
@@ -26,13 +25,11 @@
}
@Test
- @Ignore("b/119401913")
public void testArray() throws Exception {
buildAndCheckIR("arrayTest", arrayTestInspector(appInfo));
}
@Test
- @Ignore("b/119401913")
public void testNestedArray() throws Exception {
buildAndCheckIR("nestedArrayTest", nestedArrayTestInspector(appInfo));
}