Merge "Removed dex markers when performing incremental builds."
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index ce38bc9..5718ec2 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -236,10 +236,7 @@
}
if (innerClassAnnotation != null) {
if (enclosingAnnotation == null) {
- System.out.println("InnerClass annotation is missing a corresponding EnclosingMember " +
- "annotation. This is typically a sign of using an outdated Java toolchain. To fix, " +
- "recompile the source with an updated toolchain. The InnerClass annotation will be " +
- "ignored.");
+ application.options.warningMissingEnclosingMember = true;
} else {
addAnnotation(innerClassAnnotation);
addAnnotation(enclosingAnnotation);
diff --git a/src/main/java/com/android/tools/r8/ir/code/Cmp.java b/src/main/java/com/android/tools/r8/ir/code/Cmp.java
index 3d20415..9b54353 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Cmp.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Cmp.java
@@ -143,7 +143,7 @@
@Override
public boolean canBeFolded() {
- return (leftValue().isConstant() && rightValue().isConstant()) || nonOverlapingRanges();
+ return (leftValue().isConstNumber() && rightValue().isConstNumber()) || nonOverlapingRanges();
}
@Override
@@ -151,7 +151,7 @@
assert canBeFolded();
int result;
if (type == NumericType.LONG) {
- if (leftValue().isConstant() && rightValue().isConstant()) {
+ if (leftValue().isConstNumber() && rightValue().isConstNumber()) {
long left = leftValue().getConstInstruction().asConstNumber().getLongValue();
long right = rightValue().getConstInstruction().asConstNumber().getLongValue();
result = Integer.signum(Long.compare(left, right));
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstString.java b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
index bb1ff32..124ff54 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
@@ -64,6 +64,11 @@
}
@Override
+ public boolean isOutConstant() {
+ return true;
+ }
+
+ @Override
public boolean isConstString() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 035bf95..2ffb5a1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -354,7 +354,7 @@
}
public boolean internalComputeNeedsRegister() {
- if (!isConstant()) {
+ if (!isConstNumber()) {
return true;
}
if (numberOfPhiUsers() > 0) {
@@ -422,6 +422,10 @@
return definition.getOutConstantConstInstruction();
}
+ public boolean isConstNumber() {
+ return isConstant() && getConstInstruction().isConstNumber();
+ }
+
public boolean isConstant() {
return definition.isOutConstant() && getLocalInfo() == null;
}
@@ -481,11 +485,11 @@
}
public boolean hasValueRange() {
- return valueRange != null || isConstant();
+ return valueRange != null || isConstNumber();
}
public boolean isValueInRange(int value) {
- if (isConstant()) {
+ if (isConstNumber()) {
return value == getConstInstruction().asConstNumber().getIntValue();
} else {
return valueRange != null && valueRange.containsValue(value);
@@ -493,7 +497,7 @@
}
public LongInterval getValueRange() {
- if (isConstant()) {
+ if (isConstNumber()) {
if (type == MoveType.SINGLE) {
int value = getConstInstruction().asConstNumber().getIntValue();
return new LongInterval(value, value);
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 809e3ac..a718da2 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
@@ -1394,7 +1394,8 @@
Value in2 = readNumericRegister(right, type);
Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
Instruction instruction;
- if (in2.isConstant() && in2.getConstInstruction().asConstNumber().isIntegerNegativeOne(type)) {
+ if (in2.isConstNumber() &&
+ in2.getConstInstruction().asConstNumber().isIntegerNegativeOne(type)) {
instruction = new Not(type, out, in1);
} else {
instruction = new Xor(type, out, in1, in2);
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 981f673..e1c0ccb 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
@@ -20,6 +20,7 @@
import com.android.tools.r8.ir.code.CheckCast;
import com.android.tools.r8.ir.code.Cmp;
import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.ConstInstruction;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.ConstString;
import com.android.tools.r8.ir.code.DominatorTree;
@@ -32,6 +33,7 @@
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeNewArray;
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.MoveType;
@@ -675,7 +677,7 @@
Map<ConstNumber, ConstNumber> oldToNew = new HashMap<>();
for (int i = 0; i < invoke.inValues().size(); i++) {
Value value = invoke.inValues().get(i);
- if (value.isConstant() && value.numberOfUsers() > 1) {
+ if (value.isConstNumber() && value.numberOfUsers() > 1) {
ConstNumber definition = value.getConstInstruction().asConstNumber();
Value originalValue = definition.outValue();
ConstNumber newNumber = oldToNew.get(definition);
@@ -700,7 +702,7 @@
BasicBlock predecessor = block.getPredecessors().get(i);
for (Phi phi : block.getPhis()) {
Value operand = phi.getOperand(i);
- if (!operand.isPhi() && operand.isConstant()) {
+ if (!operand.isPhi() && operand.isConstNumber()) {
ConstNumber definition = operand.getConstInstruction().asConstNumber();
ConstNumber newNumber = oldToNew.get(definition);
Value originalValue = definition.outValue();
@@ -814,18 +816,16 @@
insertAt.add(instruction);
}
- private short[] computeArrayFilledData(
- NewArrayEmpty newArray, int size, BasicBlock block, int elementSize) {
- ConstNumber[] values = computeConstantArrayValues(newArray, block, size);
+ private short[] computeArrayFilledData(ConstInstruction[] values, int size, int elementSize) {
if (values == null) {
return null;
}
if (elementSize == 1) {
short[] result = new short[(size + 1) / 2];
for (int i = 0; i < size; i += 2) {
- short value = (short) (values[i].getIntValue() & 0xFF);
+ short value = (short) (values[i].asConstNumber().getIntValue() & 0xFF);
if (i + 1 < size) {
- value |= (short) ((values[i + 1].getIntValue() & 0xFF) << 8);
+ value |= (short) ((values[i + 1].asConstNumber().getIntValue() & 0xFF) << 8);
}
result[i / 2] = value;
}
@@ -835,7 +835,7 @@
int shortsPerConstant = elementSize / 2;
short[] result = new short[size * shortsPerConstant];
for (int i = 0; i < size; i++) {
- long value = values[i].getRawValue();
+ long value = values[i].asConstNumber().getRawValue();
for (int part = 0; part < shortsPerConstant; part++) {
result[i * shortsPerConstant + part] = (short) ((value >> (16 * part)) & 0xFFFFL);
}
@@ -843,12 +843,12 @@
return result;
}
- private ConstNumber[] computeConstantArrayValues(
+ private ConstInstruction[] computeConstantArrayValues(
NewArrayEmpty newArray, BasicBlock block, int size) {
if (size > MAX_FILL_ARRAY_SIZE) {
return null;
}
- ConstNumber[] values = new ConstNumber[size];
+ ConstInstruction[] values = new ConstInstruction[size];
int remaining = size;
Set<Instruction> users = newArray.outValue().uniqueUsers();
// We allow the array instantiations to cross block boundaries as long as it hasn't encountered
@@ -860,8 +860,9 @@
Instruction instruction = it.next();
// If we encounter an instruction that can throw an exception we need to bail out of the
// optimization so that we do not transform half-initialized arrays into fully initialized
- // arrays on exceptional edges.
- if (instruction.instructionInstanceCanThrow()) {
+ // arrays on exceptional edges. If the block has no handlers it is not observable so
+ // we perform the rewriting.
+ if (block.hasCatchHandlers() && instruction.instructionInstanceCanThrow()) {
return null;
}
if (!users.contains(instruction)) {
@@ -873,16 +874,15 @@
return null;
}
ArrayPut arrayPut = instruction.asArrayPut();
- if (!arrayPut.source().isConstant()) {
+ if (!(arrayPut.source().isConstant() && arrayPut.index().isConstNumber())) {
return null;
}
- assert arrayPut.index().isConstant();
int index = arrayPut.index().getConstInstruction().asConstNumber().getIntValue();
assert index >= 0 && index < values.length;
if (values[index] != null) {
return null;
}
- ConstNumber value = arrayPut.source().getConstInstruction().asConstNumber();
+ ConstInstruction value = arrayPut.source().getConstInstruction();
values[index] = value;
--remaining;
if (remaining == 0) {
@@ -895,7 +895,7 @@
return null;
}
- private boolean isPrimitiveNewArrayWithConstantPositiveSize(Instruction instruction) {
+ private boolean isPrimitiveOrStringNewArrayWithPositiveSize(Instruction instruction) {
if (!(instruction instanceof NewArrayEmpty)) {
return false;
}
@@ -903,48 +903,69 @@
if (!newArray.size().isConstant()) {
return false;
}
+ assert newArray.size().isConstNumber();
int size = newArray.size().getConstInstruction().asConstNumber().getIntValue();
if (size < 1) {
return false;
}
- if (!newArray.type.isPrimitiveArrayType()) {
- return false;
- }
- return true;
+ return newArray.type.isPrimitiveArrayType() || newArray.type == dexItemFactory.stringArrayType;
}
/**
- * Replace NewArrayEmpty followed by stores of constants to all entries with NewArrayEmpty
- * and FillArrayData.
+ * Replace new-array followed by stores of constants to all entries with new-array
+ * and fill-array-data / filled-new-array.
*/
public void simplifyArrayConstruction(IRCode code) {
for (BasicBlock block : code.blocks) {
// Map from the array value to the number of array put instruction to remove for that value.
+ Map<Value, Instruction> instructionToInsertForArray = new HashMap<>();
Map<Value, Integer> storesToRemoveForArray = new HashMap<>();
// First pass: identify candidates and insert fill array data instruction.
InstructionListIterator it = block.listIterator();
while (it.hasNext()) {
Instruction instruction = it.next();
- if (!isPrimitiveNewArrayWithConstantPositiveSize(instruction)) {
+ if (!isPrimitiveOrStringNewArrayWithPositiveSize(instruction)) {
continue;
}
NewArrayEmpty newArray = instruction.asNewArrayEmpty();
int size = newArray.size().getConstInstruction().asConstNumber().getIntValue();
- // If there is only one element it is typically smaller to generate the array put
- // instruction instead of fill array data.
- if (size == 1) {
+ ConstInstruction[] values = computeConstantArrayValues(newArray, block, size);
+ if (values == null) {
continue;
}
- int elementSize = newArray.type.elementSizeForPrimitiveArrayType();
- short[] contents = computeArrayFilledData(newArray, size, block, elementSize);
- if (contents == null) {
- continue;
+ if (newArray.type == dexItemFactory.stringArrayType) {
+ // Don't replace with filled-new-array if it requires more than 200 consecutive registers.
+ if (size > 200) {
+ continue;
+ }
+ List<Value> stringValues = new ArrayList<>(size);
+ for (ConstInstruction value : values) {
+ stringValues.add(value.outValue());
+ }
+ InvokeNewArray invoke = new InvokeNewArray(
+ dexItemFactory.stringArrayType, newArray.outValue(), stringValues);
+ it.detach();
+ for (Value value : newArray.inValues()) {
+ value.removeUser(newArray);
+ }
+ instructionToInsertForArray.put(newArray.outValue(), invoke);
+ } else {
+ // If there is only one element it is typically smaller to generate the array put
+ // instruction instead of fill array data.
+ if (size == 1) {
+ continue;
+ }
+ int elementSize = newArray.type.elementSizeForPrimitiveArrayType();
+ short[] contents = computeArrayFilledData(values, size, elementSize);
+ if (contents == null) {
+ continue;
+ }
+ int arraySize = newArray.size().getConstInstruction().asConstNumber().getIntValue();
+ NewArrayFilledData fillArray = new NewArrayFilledData(
+ newArray.outValue(), elementSize, arraySize, contents);
+ it.add(fillArray);
}
storesToRemoveForArray.put(newArray.outValue(), size);
- int arraySize = newArray.size().getConstInstruction().asConstNumber().getIntValue();
- NewArrayFilledData fillArray = new NewArrayFilledData(
- newArray.outValue(), elementSize, arraySize, contents);
- it.add(fillArray);
}
// Second pass: remove all the array put instructions for the array for which we have
// inserted a fill array data instruction instead.
@@ -956,9 +977,18 @@
if (instruction.isArrayPut()) {
Value array = instruction.asArrayPut().array();
Integer toRemoveCount = storesToRemoveForArray.get(array);
- if (toRemoveCount != null && toRemoveCount > 0) {
- storesToRemoveForArray.put(array, toRemoveCount - 1);
- it.remove();
+ if (toRemoveCount != null) {
+ if (toRemoveCount > 0) {
+ storesToRemoveForArray.put(array, --toRemoveCount);
+ it.remove();
+ }
+ if (toRemoveCount == 0) {
+ storesToRemoveForArray.put(array, --toRemoveCount);
+ Instruction construction = instructionToInsertForArray.get(array);
+ if (construction != null) {
+ it.add(construction);
+ }
+ }
}
}
}
@@ -1082,8 +1112,8 @@
If theIf = block.exit().asIf();
List<Value> inValues = theIf.inValues();
int cond;
- if (inValues.get(0).isConstant()
- && (theIf.isZeroTest() || inValues.get(1).isConstant())) {
+ if (inValues.get(0).isConstNumber()
+ && (theIf.isZeroTest() || inValues.get(1).isConstNumber())) {
// Zero test with a constant of comparison between between two constants.
if (theIf.isZeroTest()) {
cond = inValues.get(0).getConstInstruction().asConstNumber().getIntValue();
@@ -1143,8 +1173,8 @@
List<Value> inValues = theIf.inValues();
Value leftValue = inValues.get(0);
Value rightValue = inValues.get(1);
- if (leftValue.isConstant() || rightValue.isConstant()) {
- if (leftValue.isConstant()) {
+ if (leftValue.isConstNumber() || rightValue.isConstNumber()) {
+ if (leftValue.isConstNumber()) {
int left = leftValue.getConstInstruction().asConstNumber().getIntValue();
if (left == 0) {
If ifz = new If(theIf.getType().forSwappedOperands(), rightValue);
@@ -1152,7 +1182,6 @@
assert block.exit() == ifz;
}
} else {
- assert rightValue.isConstant();
int right = rightValue.getConstInstruction().asConstNumber().getIntValue();
if (right == 0) {
If ifz = new If(theIf.getType(), leftValue);
@@ -1231,7 +1260,7 @@
iterator.previous();
iterator.add(zero);
- // Then replace the invoke instruction with NewArrayEmpty instruction.
+ // Then replace the invoke instruction with new-array instruction.
Instruction next = iterator.next();
assert current == next;
NewArrayEmpty newArray = new NewArrayEmpty(destValue, zero.outValue(),
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index 38cdb29..7b7e5e7 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -1587,7 +1587,7 @@
newActive.add(splitChild);
// If the constant is split before its first actual use, mark the constant as being
// spilled. That will allows us to remove it afterwards if it is rematerializable.
- if (intervals.getValue().isConstant()
+ if (intervals.getValue().isConstNumber()
&& intervals.getStart() == intervals.getValue().definition.getNumber()
&& intervals.getUses().size() == 1) {
intervals.setSpilled(true);
@@ -1598,7 +1598,9 @@
LiveIntervals splitOfSplit = splitChild.splitBefore(splitChild.getFirstUse());
splitOfSplit.setRegister(intervals.getRegister());
inactive.add(splitOfSplit);
- } else if (intervals.getValue().isConstant()) {
+ } else if (intervals.getValue().isConstNumber()) {
+ // TODO(ager): Do this for all constants. Currently we only rematerialize const
+ // number and therefore we only do it for numbers at this point.
splitRangesForSpilledConstant(splitChild, registerNumber);
} else if (intervals.isArgumentInterval()) {
splitRangesForSpilledArgument(splitChild);
@@ -1627,7 +1629,7 @@
// Spilling a non-pinned, non-rematerializable value. We use the value in the spill
// register for as long as possible to avoid further moves.
assert spilled.isSpilled();
- assert !spilled.getValue().isConstant();
+ assert !spilled.getValue().isConstNumber();
assert !spilled.isLinked() || spilled.isArgumentInterval();
if (spilled.isArgumentInterval()) {
registerNumber = Constants.U16BIT_MAX;
@@ -1658,7 +1660,7 @@
// spill we are running low on registers and this constant should get out of the way
// as much as possible.
assert spilled.isSpilled();
- assert spilled.getValue().isConstant();
+ assert spilled.getValue().isConstNumber();
assert !spilled.isLinked() || spilled.isArgumentInterval();
// Do not split range if constant is reused by one of the eleven following instruction.
int maxGapSize = 11 * INSTRUCTION_NUMBER_DELTA;
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 06b33f4..12db3d9 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
@@ -85,7 +85,8 @@
}
public boolean isRematerializable(LinearScanRegisterAllocator registerAllocator) {
- if (!value.isConstant()) {
+ // TODO(ager): rematerialize const string as well.
+ if (!value.isConstNumber()) {
return false;
}
// If one of the non-spilled splits uses a register that is higher than U8BIT_MAX we cannot
@@ -420,7 +421,7 @@
}
public boolean isConstantNumberInterval() {
- return value.definition != null && value.isConstant();
+ return value.definition != null && value.isConstNumber();
}
public int numberOfUsesWithConstraint() {
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 5477d4d..30472f9 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -117,12 +117,27 @@
public String warningInvalidParameterAnnotations = null;
+ public boolean warningMissingEnclosingMember = false;
+
public boolean printWarnings() {
boolean printed = false;
+ boolean printOutdatedToolchain = false;
if (warningInvalidParameterAnnotations != null) {
System.out.println("Warning: " + warningInvalidParameterAnnotations);
printed = true;
}
+ if (warningMissingEnclosingMember) {
+ System.out.println(
+ "Warning: InnerClass annotations are missing corresponding EnclosingMember annotations."
+ + " Such InnerClass annotations are ignored.");
+ printed = true;
+ printOutdatedToolchain = true;
+ }
+ if (printOutdatedToolchain) {
+ System.out.println(
+ "Some warnings are typically a sign of using an outdated Java toolchain."
+ + " To fix, recompile the source with an updated toolchain.");
+ }
return printed;
}
diff --git a/tools/create_jctf_tests.py b/tools/create_jctf_tests.py
index ff9d797..b7b449d 100755
--- a/tools/create_jctf_tests.py
+++ b/tools/create_jctf_tests.py
@@ -16,10 +16,12 @@
import utils
-JCTFROOT = 'third_party/jctf'
-DESTINATION_DIR = 'build/generated/test/java/com/android/tools/r8/jctf'
+JCTFROOT = join(utils.REPO_ROOT, 'third_party', 'jctf')
+DESTINATION_DIR = join(utils.REPO_ROOT, 'build', 'generated', 'test', 'java',
+ 'com', 'android', 'tools', 'r8', 'jctf')
PACKAGE_PREFIX = 'com.google.jctf.test.lib.java.'
-RELATIVE_TESTDIR = 'LibTests/src/com/google/jctf/test/lib/java'
+RELATIVE_TESTDIR = join('LibTests', 'src', 'com', 'google', 'jctf', 'test',
+ 'lib', 'java')
TESTDIR = join(JCTFROOT, RELATIVE_TESTDIR)
TEMPLATE = Template(
"""// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
@@ -58,12 +60,17 @@
EXIT_FAILURE = 1
RE_PACKAGE = re.compile('package\\s+(com[^\\s;]*)')
+def fix_long_path(p):
+ if os.name == 'nt':
+ p = ('\\\\?\\' + p).decode('utf-8')
+ return p
+
def file_contains_string(filepath, search_string):
- with open(filepath) as f:
+ with open(fix_long_path(filepath)) as f:
return search_string in f.read()
def read_package_from_java_file(filepath):
- with open(filepath) as f:
+ with open(fix_long_path(filepath)) as f:
for line in f:
m = RE_PACKAGE.search(line)
if m:
@@ -74,7 +81,7 @@
def generate_test(class_name, compiler_under_test, compiler_under_test_enum,
relative_package):
filename = join(DESTINATION_DIR, compiler_under_test,
- relative_package.replace('.', '/'), class_name + '.java')
+ relative_package.replace('.', os.sep), class_name + '.java')
utils.makedirs_if_needed(dirname(filename))
full_class_name = '{}{}.{}'.format(PACKAGE_PREFIX, relative_package,
@@ -88,7 +95,7 @@
classFile = full_class_name.replace('.', '/') + '.class',
nameWithoutPackagePrefix = '{}.{}'.format(relative_package, class_name))
- with open(filename, 'w') as f:
+ with open(fix_long_path(filename), 'w') as f:
f.write(contents)
def Main():
@@ -98,7 +105,7 @@
return EXIT_FAILURE
for tool in ['d8', 'r8']:
- p = join(DESTINATION_DIR, tool)
+ p = fix_long_path(join(DESTINATION_DIR, tool))
if exists(p):
rmtree(p)
makedirs(p)