Merge "Mark byte-or-bool types when propagating type information."
diff --git a/src/main/java/com/android/tools/r8/graph/JarCode.java b/src/main/java/com/android/tools/r8/graph/JarCode.java
index 9a367ff..bbf3d57 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -80,20 +80,27 @@
@Override
public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options) {
- triggerDelayedParsingIfNeccessary();
- JarSourceCode source = new JarSourceCode(clazz, node, application);
- IRBuilder builder = new IRBuilder(encodedMethod, source, options);
- return builder.build();
+ return internalBuild(encodedMethod, null, options);
}
- public IRCode buildIR(DexEncodedMethod encodedMethod, ValueNumberGenerator generator,
- InternalOptions options) {
- triggerDelayedParsingIfNeccessary();
- JarSourceCode source = new JarSourceCode(clazz, node, application);
- IRBuilder builder = new IRBuilder(encodedMethod, source, generator, options);
- return builder.build();
+ public IRCode buildIR(
+ DexEncodedMethod encodedMethod, ValueNumberGenerator generator, InternalOptions options) {
+ return internalBuild(encodedMethod, generator, options);
}
+ private IRCode internalBuild(
+ DexEncodedMethod encodedMethod, ValueNumberGenerator generator, InternalOptions options) {
+ triggerDelayedParsingIfNeccessary();
+ if (!options.debug) {
+ node.localVariables.clear();
+ }
+ JarSourceCode source = new JarSourceCode(clazz, node, application);
+ IRBuilder builder =
+ (generator == null)
+ ? new IRBuilder(encodedMethod, source, options)
+ : new IRBuilder(encodedMethod, source, generator, options);
+ return builder.build();
+ }
@Override
public void registerReachableDefinitions(UseRegistry registry) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index 12d8b6a..484354a 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -308,6 +308,7 @@
}
}
computeBlockEntryJarStates(builder);
+ state.setBuilding();
}
private void computeBlockEntryJarStates(IRBuilder builder) {
@@ -360,6 +361,7 @@
}
}
}
+ state.restoreState(0);
}
private void updateStateForLocalVariableEnd(AbstractInsnNode insn) {
@@ -1146,9 +1148,11 @@
state.pop();
Type elementType = state.pop(JarState.ARRAY_TYPE).getArrayElementType();
if (elementType == null) {
- // We propagate the null type, which will then get resolved to an
- // actual type if we have a non-null type on another flow edge.
- elementType = JarState.NULL_TYPE;
+ // We propagate the byte-or-bool type, which will then get resolved to an
+ // actual type if we have a concrete byte type or bool type on another flow edge.
+ elementType = (Opcodes.BALOAD == opcode)
+ ? JarState.BYTE_OR_BOOL_TYPE
+ : getArrayElementTypeForOpcode(opcode);
}
state.push(elementType);
break;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarState.java b/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
index 9d11fae..ee5252d 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
@@ -36,6 +36,10 @@
// Type representative for the null value (non-existent but works for tracking the types here).
public static final Type NULL_TYPE = Type.getObjectType("<null>");
+ // TODO(zerny): Define an internal Type wrapping ASM types so that we can define an actual value.
+ // Type representative for a value that may be either a boolean or a byte.
+ public static final Type BYTE_OR_BOOL_TYPE = null;
+
// Typed mapping from a local slot or stack slot to a virtual register.
public static class Slot {
public final int register;
@@ -78,6 +82,9 @@
assert type != REFERENCE_TYPE;
assert type != OBJECT_TYPE;
assert type != ARRAY_TYPE;
+ if (type == BYTE_OR_BOOL_TYPE) {
+ type = Type.BYTE_TYPE;
+ }
int sort = type.getSort();
int otherSort = other.getSort();
if (isReferenceCompatible(type, other)) {
@@ -183,6 +190,9 @@
private final Map<Integer, Snapshot> targetStates = new HashMap<>();
+ // Mode denoting that the state setup is done and we are now emitting IR.
+ // Concretely we treat all remaining byte-or-bool types as bytes (no actual type can flow there).
+ private boolean building = false;
public JarState(int maxLocals, Map<LocalVariableNode, DebugLocalInfo> localVariables) {
int localsRegistersSize = maxLocals * 3;
@@ -205,6 +215,39 @@
}
}
+ public void setBuilding() {
+ assert stack.isEmpty();
+ building = true;
+ for (int i = 0; i < locals.length; i++) {
+ Local local = locals[i];
+ if (local != null && local.slot.type == BYTE_OR_BOOL_TYPE) {
+ locals[i] = new Local(new Slot(local.slot.register, Type.BYTE_TYPE), local.info);
+ }
+ }
+ for (Entry<Integer, Snapshot> entry : targetStates.entrySet()) {
+ Local[] locals = entry.getValue().locals;
+ for (int i = 0; i < locals.length; i++) {
+ Local local = locals[i];
+ if (local != null && local.slot.type == BYTE_OR_BOOL_TYPE) {
+ locals[i] = new Local(new Slot(local.slot.register, Type.BYTE_TYPE), local.info);
+ }
+ }
+ ImmutableList.Builder<Slot> builder = ImmutableList.builder();
+ boolean found = false;
+ for (Slot slot : entry.getValue().stack) {
+ if (slot.type == BYTE_OR_BOOL_TYPE) {
+ found = true;
+ builder.add(new Slot(slot.register, Type.BYTE_TYPE));
+ } else {
+ builder.add(slot);
+ }
+ }
+ if (found) {
+ entry.setValue(new Snapshot(locals, builder.build()));
+ }
+ }
+ }
+
// Local variable procedures.
public List<Local> openLocals(LabelNode label) {
@@ -253,6 +296,10 @@
int getLocalRegister(int index, Type type) {
assert index < localsSize;
+ if (type == BYTE_OR_BOOL_TYPE) {
+ assert Slot.isCategory1(type);
+ return index + localsSize;
+ }
if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
return index;
}
@@ -287,17 +334,22 @@
}
public int writeLocal(int index, Type type) {
- assert type != null;
+ assert nonNullType(type);
Local local = getLocal(index, type);
assert local == null || local.info == null || local.slot.isCompatibleWith(type);
// We cannot assume consistency for writes because we do not have complete information about the
// scopes of locals. We assume the program to be verified and overwrite if the types mismatch.
- if (local == null || (local.info == null && !local.slot.type.equals(type))) {
+ if (local == null || (local.info == null && !typeEquals(local.slot.type, type))) {
local = setLocal(index, type, null);
}
return local.slot.register;
}
+ public boolean typeEquals(Type type1, Type type2) {
+ return (type1 == BYTE_OR_BOOL_TYPE && type2 == BYTE_OR_BOOL_TYPE)
+ || (type1 != null && type1.equals(type2));
+ }
+
public Slot readLocal(int index, Type type) {
Local local = getLocal(index, type);
assert local != null;
@@ -305,10 +357,14 @@
return local.slot;
}
+ public boolean nonNullType(Type type) {
+ return type != null || !building;
+ }
+
// Stack procedures.
public int push(Type type) {
- assert type != null;
+ assert nonNullType(type);
int top = topOfStack;
// For simplicity, every stack slot (and local variable) is wide (uses two registers).
topOfStack += 2;
@@ -332,7 +388,7 @@
// For simplicity, every stack slot (and local variable) is wide (uses two registers).
topOfStack -= 2;
Slot slot = stack.pop();
- assert slot.type != null;
+ assert nonNullType(slot.type);
assert slot.register == topOfStack;
return slot;
}
@@ -416,13 +472,17 @@
return true;
}
+ private boolean isRefinement(Type current, Type other) {
+ return (current == JarState.NULL_TYPE && other != JarState.NULL_TYPE)
+ || (current == JarState.BYTE_OR_BOOL_TYPE && other != JarState.BYTE_OR_BOOL_TYPE);
+ }
+
private ImmutableList<Slot> mergeStacks(
ImmutableList<Slot> currentStack, ImmutableList<Slot> newStack) {
assert currentStack.size() == newStack.size();
List<Slot> mergedStack = null;
for (int i = 0; i < currentStack.size(); i++) {
- if (currentStack.get(i).type == JarState.NULL_TYPE &&
- newStack.get(i).type != JarState.NULL_TYPE) {
+ if (isRefinement(currentStack.get(i).type, newStack.get(i).type)) {
if (mergedStack == null) {
mergedStack = new ArrayList<>();
mergedStack.addAll(currentStack.subList(0, i));
@@ -448,8 +508,7 @@
// If this assert triggers we can get different debug information for the same local
// on different control-flow paths and we will have to merge them.
assert currentLocal.info == newLocal.info;
- if (currentLocal.slot.type == JarState.NULL_TYPE &&
- newLocal.slot.type != JarState.NULL_TYPE) {
+ if (isRefinement(currentLocal.slot.type, newLocal.slot.type)) {
if (mergedLocals == null) {
mergedLocals = new Local[currentLocals.length];
System.arraycopy(currentLocals, 0, mergedLocals, 0, i);
@@ -463,15 +522,6 @@
return mergedLocals != null ? mergedLocals : currentLocals;
}
- private static boolean verifyStack(List<Slot> stack, List<Slot> other) {
- assert stack.size() == other.size();
- int i = 0;
- for (Slot slot : stack) {
- assert slot.isCompatibleWith(other.get(i++).type);
- }
- return true;
- }
-
// Other helpers.
private static boolean verifySlots(Slot[] slots, Type type) {
@@ -490,7 +540,11 @@
public static String stackToString(Collection<Slot> stack) {
List<String> strings = new ArrayList<>(stack.size());
for (Slot slot : stack) {
- strings.add(slot.type.toString());
+ if (slot.type == BYTE_OR_BOOL_TYPE) {
+ strings.add("<byte|bool>");
+ } else {
+ strings.add(slot.type.toString());
+ }
}
StringBuilder builder = new StringBuilder("{ ");
for (int i = strings.size() - 1; i >= 0; i--) {
@@ -516,6 +570,8 @@
builder.append("_");
} else if (local.info != null) {
builder.append(local.info);
+ } else if (local.slot.type == BYTE_OR_BOOL_TYPE) {
+ builder.append("<byte|bool>");
} else {
builder.append(local.slot.type.toString());
}