Merge "Revert "Use python executable to drive upload script""
diff --git a/src/main/java/com/android/tools/r8/errors/InvalidDebugInfoException.java b/src/main/java/com/android/tools/r8/errors/InvalidDebugInfoException.java
new file mode 100644
index 0000000..f3f99e2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/InvalidDebugInfoException.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2017, 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.errors;
+
+public class InvalidDebugInfoException extends InternalCompilerError {
+ public InvalidDebugInfoException(String message) {
+ super(message);
+ }
+}
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..6b9c06d 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import com.android.tools.r8.errors.InvalidDebugInfoException;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.ValueNumberGenerator;
import com.android.tools.r8.ir.conversion.IRBuilder;
@@ -81,19 +82,43 @@
@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 options.debug
+ ? internalBuildWithLocals(encodedMethod, null, options)
+ : internalBuild(encodedMethod, null, options);
}
- public IRCode buildIR(DexEncodedMethod encodedMethod, ValueNumberGenerator generator,
- InternalOptions options) {
+ public IRCode buildIR(
+ DexEncodedMethod encodedMethod, ValueNumberGenerator generator, InternalOptions options) {
+ assert generator != null;
triggerDelayedParsingIfNeccessary();
- JarSourceCode source = new JarSourceCode(clazz, node, application);
- IRBuilder builder = new IRBuilder(encodedMethod, source, generator, options);
- return builder.build();
+ return options.debug
+ ? internalBuildWithLocals(encodedMethod, generator, options)
+ : internalBuild(encodedMethod, generator, options);
}
+ private IRCode internalBuildWithLocals(
+ DexEncodedMethod encodedMethod, ValueNumberGenerator generator, InternalOptions options) {
+ try {
+ return internalBuild(encodedMethod, generator, options);
+ } catch (InvalidDebugInfoException e) {
+ options.warningInvalidDebugInfo(encodedMethod, e);
+ node.localVariables.clear();
+ return internalBuild(encodedMethod, generator, options);
+ }
+ }
+
+ private IRCode internalBuild(
+ DexEncodedMethod encodedMethod, ValueNumberGenerator generator, InternalOptions options) {
+ 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/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index a718da2..976020c2 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
@@ -7,6 +7,7 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.errors.InvalidDebugInfoException;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -1434,7 +1435,11 @@
DebugLocalInfo local = getCurrentLocal(register);
Value value = readRegister(register, currentBlock, EdgeType.NON_EDGE, type, local);
// Check that any information about a current-local is consistent with the read.
- assert local == null || value.getLocalInfo() == local || value.isUninitializedLocal();
+ if (local != null && value.getLocalInfo() != local && !value.isUninitializedLocal()) {
+ throw new InvalidDebugInfoException(
+ "Attempt to read local " + local
+ + " but no local information was associated with the value being read.");
+ }
// Check that any local information on the value is actually visible.
// If this assert triggers, the probable cause is that we end up reading an SSA value
// after it should have been ended on a fallthrough from a conditional jump or a trivial-phi
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 6f94b1e..23b6b70 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -459,7 +459,7 @@
codeRewriter.commonSubexpressionElimination(code);
codeRewriter.simplifyArrayConstruction(code);
codeRewriter.rewriteMoveResult(code);
- codeRewriter.splitConstants(code);
+ codeRewriter.splitRangeInvokeConstants(code);
codeRewriter.foldConstants(code);
codeRewriter.rewriteSwitch(code);
codeRewriter.simplifyIf(code);
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..5009d5e 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
@@ -3,7 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.conversion;
+import com.android.tools.r8.errors.InvalidDebugInfoException;
import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.utils.DescriptorUtils;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimap;
@@ -36,6 +38,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 +84,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 +192,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 +217,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 +298,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,28 +336,44 @@
}
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);
+ if (local != null && local.info != null && !local.slot.isCompatibleWith(type)) {
+ throw new InvalidDebugInfoException(
+ "Attempt to write value of type " + prettyType(type) + " to local " + local.info);
+ }
// 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;
+ if (local.info != null && !local.slot.isCompatibleWith(type)) {
+ throw new InvalidDebugInfoException(
+ "Attempt to read value of type " + prettyType(type) + " from local " + local.info);
+ }
assert local.slot.isCompatibleWith(type);
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,14 +397,19 @@
// 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;
}
public Slot pop(Type type) {
Slot slot = pop();
- assert slot.isCompatibleWith(type);
+ boolean compatible = slot.isCompatibleWith(type);
+ if (!compatible && !localVariables.isEmpty()) {
+ throw new InvalidDebugInfoException("Expected to read stack value of type " + prettyType(type)
+ + " but found value of type " + prettyType(slot.type));
+ }
+ assert compatible;
return slot;
}
@@ -416,13 +486,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 +522,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 +536,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 +554,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 +584,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());
}
@@ -523,4 +593,17 @@
builder.append(" }");
return builder.toString();
}
+
+ private String prettyType(Type type) {
+ if (type == BYTE_OR_BOOL_TYPE) {
+ return "<byte|bool>";
+ }
+ if (type == ARRAY_TYPE) {
+ return type.getElementType().getInternalName();
+ }
+ if (type == REFERENCE_TYPE || type == OBJECT_TYPE || type == NULL_TYPE) {
+ return type.getInternalName();
+ }
+ return DescriptorUtils.descriptorToJavaType(type.getDescriptor());
+ }
}
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 e1c0ccb..48a64b8 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
@@ -63,7 +63,6 @@
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -652,68 +651,35 @@
assert code.isConsistentSSA();
}
- // Constants are canonicalized in the entry block. We split some of them when it is likely
- // that having them canonicalized in the entry block will lead to poor code quality.
- public void splitConstants(IRCode code) {
+ // Split constants that flow into ranged invokes. This gives the register allocator more
+ // freedom in assigning register to ranged invokes which can greatly reduce the number
+ // of register needed (and thereby code size as well).
+ public void splitRangeInvokeConstants(IRCode code) {
for (BasicBlock block : code.blocks) {
- // Split constants that flow into phis. It is likely that these constants will have moves
- // generated for them anyway and we might as well insert a const instruction in the right
- // predecessor block.
- splitPhiConstants(code, block);
- // Split constants that flow into ranged invokes. This gives the register allocator more
- // freedom in assigning register to ranged invokes which can greatly reduce the number
- // of register needed (and thereby code size as well).
- splitRangedInvokeConstants(code, block);
- }
- }
-
- private void splitRangedInvokeConstants(IRCode code, BasicBlock block) {
- InstructionListIterator it = block.listIterator();
- while (it.hasNext()) {
- Instruction current = it.next();
- if (current.isInvoke() && current.asInvoke().requiredArgumentRegisters() > 5) {
- Invoke invoke = current.asInvoke();
- it.previous();
- Map<ConstNumber, ConstNumber> oldToNew = new HashMap<>();
- for (int i = 0; i < invoke.inValues().size(); i++) {
- Value value = invoke.inValues().get(i);
- if (value.isConstNumber() && value.numberOfUsers() > 1) {
- ConstNumber definition = value.getConstInstruction().asConstNumber();
- Value originalValue = definition.outValue();
- ConstNumber newNumber = oldToNew.get(definition);
- if (newNumber == null) {
- newNumber = ConstNumber.copyOf(code, definition);
- it.add(newNumber);
- oldToNew.put(definition, newNumber);
+ InstructionListIterator it = block.listIterator();
+ while (it.hasNext()) {
+ Instruction current = it.next();
+ if (current.isInvoke() && current.asInvoke().requiredArgumentRegisters() > 5) {
+ Invoke invoke = current.asInvoke();
+ it.previous();
+ Map<ConstNumber, ConstNumber> oldToNew = new HashMap<>();
+ for (int i = 0; i < invoke.inValues().size(); i++) {
+ Value value = invoke.inValues().get(i);
+ if (value.isConstNumber() && value.numberOfUsers() > 1) {
+ ConstNumber definition = value.getConstInstruction().asConstNumber();
+ Value originalValue = definition.outValue();
+ ConstNumber newNumber = oldToNew.get(definition);
+ if (newNumber == null) {
+ newNumber = ConstNumber.copyOf(code, definition);
+ it.add(newNumber);
+ oldToNew.put(definition, newNumber);
+ }
+ invoke.inValues().set(i, newNumber.outValue());
+ originalValue.removeUser(invoke);
+ newNumber.outValue().addUser(invoke);
}
- invoke.inValues().set(i, newNumber.outValue());
- originalValue.removeUser(invoke);
- newNumber.outValue().addUser(invoke);
}
- }
- it.next();
- }
- }
- }
-
- private void splitPhiConstants(IRCode code, BasicBlock block) {
- for (int i = 0; i < block.getPredecessors().size(); i++) {
- Map<ConstNumber, ConstNumber> oldToNew = new IdentityHashMap<>();
- BasicBlock predecessor = block.getPredecessors().get(i);
- for (Phi phi : block.getPhis()) {
- Value operand = phi.getOperand(i);
- if (!operand.isPhi() && operand.isConstNumber()) {
- ConstNumber definition = operand.getConstInstruction().asConstNumber();
- ConstNumber newNumber = oldToNew.get(definition);
- Value originalValue = definition.outValue();
- if (newNumber == null) {
- newNumber = ConstNumber.copyOf(code, definition);
- oldToNew.put(definition, newNumber);
- insertConstantInBlock(newNumber, predecessor);
- }
- phi.getOperands().set(i, newNumber.outValue());
- originalValue.removePhiUser(phi);
- newNumber.outValue().addPhiUser(phi);
+ it.next();
}
}
}
@@ -730,7 +696,7 @@
List<Instruction> toInsertInThisBlock = new ArrayList<>();
while (it.hasNext()) {
Instruction instruction = it.next();
- if (instruction.isConstNumber()) {
+ if (instruction.isConstNumber() && instruction.outValue().numberOfAllUsers() != 0) {
// Collect the blocks for all users of the constant.
List<BasicBlock> userBlocks = new LinkedList<>();
for (Instruction user : instruction.outValue().uniqueUsers()) {
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
index 12db3d9..9a810d9 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
@@ -96,10 +96,22 @@
// these computations. We use the unadjusted real register number to make sure that
// isRematerializable for the same intervals does not change from one phase of
// compilation to the next.
+ if (getMaxNonSpilledRegister() == NO_REGISTER) {
+ assert allSplitsAreSpilled();
+ return true;
+ }
int max = registerAllocator.unadjustedRealRegisterFromAllocated(getMaxNonSpilledRegister());
return max < Constants.U8BIT_MAX;
}
+ private boolean allSplitsAreSpilled() {
+ assert isSpilled();
+ for (LiveIntervals splitChild : splitChildren) {
+ assert splitChild.isSpilled();
+ }
+ return true;
+ }
+
public boolean isSpilledAndRematerializable(LinearScanRegisterAllocator allocator) {
return isSpilled() && isRematerializable(allocator);
}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 30472f9..70a2e53 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.InvalidDebugInfoException;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.shaking.ProguardConfigurationRule;
@@ -119,6 +120,12 @@
public boolean warningMissingEnclosingMember = false;
+ public int warningInvalidDebugInfoCount = 0;
+
+ public void warningInvalidDebugInfo(DexEncodedMethod method, InvalidDebugInfoException e) {
+ warningInvalidDebugInfoCount++;
+ }
+
public boolean printWarnings() {
boolean printed = false;
boolean printOutdatedToolchain = false;
@@ -126,6 +133,13 @@
System.out.println("Warning: " + warningInvalidParameterAnnotations);
printed = true;
}
+ if (warningInvalidDebugInfoCount > 0) {
+ System.out.println("Warning: stripped invalid locals information from "
+ + warningInvalidDebugInfoCount
+ + (warningInvalidDebugInfoCount == 1 ? " method." : " methods."));
+ printed = true;
+ printOutdatedToolchain = true;
+ }
if (warningMissingEnclosingMember) {
System.out.println(
"Warning: InnerClass annotations are missing corresponding EnclosingMember annotations."
diff --git a/src/main/java/com/android/tools/r8/utils/StringUtils.java b/src/main/java/com/android/tools/r8/utils/StringUtils.java
index 268228f..8404703 100644
--- a/src/main/java/com/android/tools/r8/utils/StringUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -12,6 +12,8 @@
public class StringUtils {
+ public final static String LINE_SEPARATOR = System.getProperty("line.separator");
+
private final static char[] IDENTIFIER_LETTERS
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_".toCharArray();
private final static int NUMBER_OF_LETTERS = IDENTIFIER_LETTERS.length;
@@ -107,7 +109,7 @@
return join(collection, separator, BraceType.NONE);
}
- public static String join(String separator, String... strings) {
+ public static <T> String join(String separator, T... strings) {
return join(Arrays.asList(strings), separator, BraceType.NONE);
}
@@ -122,6 +124,18 @@
return builder.toString();
}
+ public static <T> String lines(T... lines) {
+ StringBuilder builder = new StringBuilder();
+ for (T line : lines) {
+ builder.append(line).append(LINE_SEPARATOR);
+ }
+ return builder.toString();
+ }
+
+ public static <T> String joinLines(T... lines) {
+ return join(LINE_SEPARATOR, lines);
+ }
+
public static String zeroPrefix(int i, int width) {
return zeroPrefixString(Integer.toString(i), width);
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 562518f..fd4cdccb 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
@@ -61,7 +62,7 @@
public static final String EXAMPLES_ANDROID_O_BUILD_DIR = BUILD_DIR + "test/examplesAndroidO/";
public static final String SMALI_BUILD_DIR = BUILD_DIR + "test/smali/";
- public static final String LINE_SEPARATOR = System.getProperty("line.separator");
+ public static final String LINE_SEPARATOR = StringUtils.LINE_SEPARATOR;
private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
private static final int DEFAULT_MIN_SDK = Constants.ANDROID_I_API;
diff --git a/src/test/java/com/android/tools/r8/jasmin/InvalidDebugInfoTests.java b/src/test/java/com/android/tools/r8/jasmin/InvalidDebugInfoTests.java
index 155438f..81d299e 100644
--- a/src/test/java/com/android/tools/r8/jasmin/InvalidDebugInfoTests.java
+++ b/src/test/java/com/android/tools/r8/jasmin/InvalidDebugInfoTests.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import org.junit.Test;
@@ -82,7 +83,7 @@
JasminBuilder builder = new JasminBuilder();
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
- clazz.addStaticMethod("foo", ImmutableList.of("I","I"), "V",
+ clazz.addStaticMethod("foo", ImmutableList.of("I", "I"), "V",
".limit stack 2",
".limit locals 3",
".var 0 is x I from LabelInit to LabelExit",
@@ -123,4 +124,165 @@
assertEquals(expected, artResult);
}
+ @Test
+ public void invalidInfoBug63412730_onWrite() throws Throwable {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+ clazz.addStaticMethod("bar", ImmutableList.of(), "V",
+ ".limit stack 3",
+ ".limit locals 2",
+ ".var 1 is i I from LI to End",
+ ".var 0 is f F from LF to End",
+ "Init:",
+ " ldc 42",
+ " istore 0",
+ "LI:",
+ " ldc 7.5",
+ " fstore 1",
+ "LF:",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " dup",
+ " iload 0",
+ " invokevirtual java/io/PrintStream/println(I)V",
+ " fload 1",
+ " invokevirtual java/io/PrintStream/println(F)V",
+ " return",
+ "End:");
+
+ clazz.addMainMethod(
+ ".limit stack 1",
+ ".limit locals 1",
+ " invokestatic Test/bar()V",
+ " return");
+
+ String expected = StringUtils.lines("42", "7.5");
+ String javaResult = runOnJava(builder, clazz.name);
+ assertEquals(expected, javaResult);
+ String artResult = runOnArtD8(builder, clazz.name);
+ assertEquals(expected, artResult);
+ }
+
+ @Test
+ public void invalidInfoBug63412730_onRead() throws Throwable {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+ clazz.addStaticMethod("bar", ImmutableList.of(), "V",
+ ".limit stack 3",
+ ".limit locals 2",
+ ".var 1 is i I from Locals to End",
+ ".var 0 is f F from Locals to End",
+ "Init:",
+ " ldc 42",
+ " istore 0",
+ " ldc 7.5",
+ " fstore 1",
+ "Locals:",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " dup",
+ " iload 0",
+ " invokevirtual java/io/PrintStream/println(I)V",
+ " fload 1",
+ " invokevirtual java/io/PrintStream/println(F)V",
+ " return",
+ "End:");
+
+ clazz.addMainMethod(
+ ".limit stack 1",
+ ".limit locals 1",
+ " invokestatic Test/bar()V",
+ " return");
+
+ String expected = StringUtils.lines("42", "7.5");
+ String javaResult = runOnJava(builder, clazz.name);
+ assertEquals(expected, javaResult);
+ String artResult = runOnArtD8(builder, clazz.name);
+ assertEquals(expected, artResult);
+ }
+
+ @Test
+ public void invalidInfoBug63412730_onMove() throws Throwable {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+ clazz.addStaticMethod("bar", ImmutableList.of(), "V",
+ ".limit stack 3",
+ ".limit locals 2",
+ ".var 1 is i I from LI to End",
+ ".var 0 is j F from LJ to End",
+ "Init:",
+ " ldc 42",
+ " istore 0",
+ "LI:",
+ " ldc 0",
+ " ldc 0",
+ " ifeq LZ",
+ " ldc 75",
+ " istore 1",
+ "LJ:",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " iload 1",
+ " invokevirtual java/io/PrintStream/println(I)V",
+ " return",
+ "LZ:",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " iload 0",
+ " invokevirtual java/io/PrintStream/println(I)V",
+ " return",
+ "End:");
+
+ clazz.addMainMethod(
+ ".limit stack 1",
+ ".limit locals 1",
+ " invokestatic Test/bar()V",
+ " return");
+
+ String expected = StringUtils.lines("42");
+ String javaResult = runOnJava(builder, clazz.name);
+ assertEquals(expected, javaResult);
+ String artResult = runOnArtD8(builder, clazz.name);
+ assertEquals(expected, artResult);
+ }
+
+ @Test
+ public void invalidInfoBug63412730_onPop() throws Throwable {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+ clazz.addStaticMethod("bar", ImmutableList.of(), "V",
+ ".limit stack 3",
+ ".limit locals 2",
+ ".var 1 is a [Ljava/lang/Object; from Locals to End",
+ ".var 0 is o LObject; from Locals to End",
+ "Init:",
+ " ldc 1",
+ " anewarray java/lang/Object",
+ " astore 0",
+ " new java/lang/Integer",
+ " dup",
+ " ldc 42",
+ " invokespecial java/lang/Integer/<init>(I)V",
+ " astore 1",
+ "Locals:",
+ " aload 0",
+ " ldc 0",
+ " aload 1",
+ " aastore",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " aload 0",
+ " ldc 0",
+ " aaload",
+ " invokevirtual java/io/PrintStream/println(Ljava/lang/Object;)V",
+ " return",
+ "End:");
+
+ clazz.addMainMethod(
+ ".limit stack 1",
+ ".limit locals 1",
+ " invokestatic Test/bar()V",
+ " return");
+
+ String expected = StringUtils.lines("42");
+ String javaResult = runOnJava(builder, clazz.name);
+ assertEquals(expected, javaResult);
+ String artResult = runOnArtD8(builder, clazz.name);
+ assertEquals(expected, artResult);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java b/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
index 6b4a431..919f569 100644
--- a/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
+++ b/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
@@ -180,8 +180,8 @@
":return",
" return v0");
DexCode code = method.getCode().asDexCode();
- assertEquals(12, code.instructions.length);
- assertTrue(code.instructions[11] instanceof Return);
+ assertEquals(10, code.instructions.length);
+ assertTrue(code.instructions[9] instanceof Return);
}
@Test
@@ -443,6 +443,6 @@
DexCode code = method.getCode().asDexCode();
// TODO(sgjesse): Maybe this test is too fragile, as it leaves quite a lot of code, so the
// expectation might need changing with other optimizations.
- assertEquals(29, code.instructions.length);
+ assertEquals(27, code.instructions.length);
}
}
diff --git a/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java b/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
index f10add0..7f89da7 100644
--- a/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
@@ -7,7 +7,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.R8;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.code.Const;
import com.android.tools.r8.code.Const4;
@@ -292,12 +291,11 @@
DexCode code = method.getCode().asDexCode();
if (key == 0) {
assertEquals(5, code.instructions.length);
- assertTrue(code.instructions[0] instanceof IfEqz);
+ assertTrue(code.instructions[2] instanceof IfEqz);
} else {
assertEquals(6, code.instructions.length);
- assertTrue(some16BitConst(code.instructions[0]));
- assertTrue(code.instructions[1] instanceof IfEq);
- assertTrue(code.instructions[2] instanceof Const4);
+ assertTrue(some16BitConst(code.instructions[2]));
+ assertTrue(code.instructions[3] instanceof IfEq);
}
}
@@ -351,9 +349,9 @@
DexEncodedMethod method = getMethod(app, signature);
DexCode code = method.getCode().asDexCode();
if (twoCaseWillUsePackedSwitch(key1, key2)) {
- assertTrue(code.instructions[0] instanceof PackedSwitch);
+ assertTrue(code.instructions[3] instanceof PackedSwitch);
} else {
- assertTrue(code.instructions[0] instanceof SparseSwitch);
+ assertTrue(code.instructions[3] instanceof SparseSwitch);
}
}
@@ -423,10 +421,22 @@
MethodSignature signature = new MethodSignature("Test", "test", "int", ImmutableList.of("int"));
DexEncodedMethod method = getMethod(app, signature);
DexCode code = method.getCode().asDexCode();
+ int packedSwitchCount = 0;
+ int sparseSwitchCount = 0;
+ for (Instruction instruction : code.instructions) {
+ if (instruction instanceof PackedSwitch) {
+ packedSwitchCount++;
+ }
+ if (instruction instanceof SparseSwitch) {
+ sparseSwitchCount++;
+ }
+ }
if (keyStep <= 2) {
- assertTrue(code.instructions[0] instanceof PackedSwitch);
+ assertEquals(1, packedSwitchCount);
+ assertEquals(0, sparseSwitchCount);
} else {
- assertTrue(code.instructions[0] instanceof SparseSwitch);
+ assertEquals(0, packedSwitchCount);
+ assertEquals(1, sparseSwitchCount);
}
}
diff --git a/tools/test.py b/tools/test.py
index 68fa829..8e016c7 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -68,6 +68,7 @@
utils.upload_html_to_cloud_storage(upload_dir, destination)
url = 'http://storage.googleapis.com/%s/%s/test/index.html' % (BUCKET, u_dir)
print 'Test results available at: %s' % url
+ print '@@@STEP_LINK@Test failures@%s@@@' % url
def Main():
(options, args) = ParseOptions()
diff --git a/tools/test_framework.py b/tools/test_framework.py
index 1ade73d..a153847 100755
--- a/tools/test_framework.py
+++ b/tools/test_framework.py
@@ -71,7 +71,7 @@
if args.tool == 'goyt':
tool_file = GOYT_EXE
- tool_args = ['--num-threads=4'] + tool_args
+ tool_args = ['--num-threads=8'] + tool_args
elif args.tool == 'dx':
tool_file = DX_JAR
else: