Merge "Avoid multiple nops before payload instructions"
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 ebfc337..84a48dd 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
@@ -2075,11 +2075,10 @@
return;
}
- int color = code.reserveMarkingColor();
ListIterator<BasicBlock> blockIterator = code.listIterator();
while (blockIterator.hasNext()) {
BasicBlock block = blockIterator.next();
- if (block.isMarked(color)) {
+ if (block.getNumber() != 0 && block.getPredecessors().isEmpty()) {
continue;
}
InstructionListIterator insnIterator = block.listIterator();
@@ -2096,19 +2095,13 @@
}
// Split the block.
- BasicBlock newBlock = insnIterator.split(code, blockIterator);
- assert !insnIterator.hasNext(); // must be pointing *after* inserted GoTo.
- // Move block iterator back so current block is 'newBlock'.
- blockIterator.previous();
+ {
+ BasicBlock newBlock = insnIterator.split(code, blockIterator);
+ assert !insnIterator.hasNext(); // must be pointing *after* inserted GoTo.
+ // Move block iterator back so current block is 'newBlock'.
+ blockIterator.previous();
- // Unlink the next block representing all the instructions of the original
- // block after the invocation which never returns normally. Mark all the
- // removed blocks for deletion.
- List<BasicBlock> removedBlocks = block.unlink(newBlock, new DominatorTree(code));
- for (BasicBlock removedBlock : removedBlocks) {
- if (!removedBlock.isMarked(color)) {
- removedBlock.mark(color);
- }
+ newBlock.unlinkSinglePredecessorSiblingsAllowed();
}
// We want to follow the invoke instruction with 'throw null', which should
@@ -2137,8 +2130,7 @@
notReachableThrow.forceSetPosition(insn.getPosition());
}
}
- code.removeMarkedBlocks(color);
- code.returnMarkingColor(color);
+ code.removeUnreachableBlocks();
assert code.isConsistentSSA();
}
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 abc4730..1c0e761 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
@@ -140,6 +140,7 @@
protected PriorityQueue<LiveIntervals> unhandled = new PriorityQueue<>();
// List of intervals for the result of move-exception instructions.
+ // Always empty in mode ALLOW_ARGUMENT_REUSE.
private List<LiveIntervals> moveExceptionIntervals = new ArrayList<>();
// The first register used for parallel moves. After register allocation the parallel move
@@ -152,16 +153,17 @@
// Whether or not the code has a move exception instruction. Used to pin the move exception
// register.
- private boolean hasDedicatedMoveExceptionRegister = false;
-
- // We allocate a dedicated move exception register right after the arguments.
- private int getMoveExceptionRegister() {
- assert hasDedicatedMoveExceptionRegister;
- return numberOfArgumentRegisters;
+ private boolean hasDedicatedMoveExceptionRegister() {
+ return !moveExceptionIntervals.isEmpty();
}
- private int getNextUnusedRegisterNumber() {
- return maxRegisterNumber + 1;
+ // We allocate a dedicated move exception register right after the arguments.
+ // TODO(christofferqa): The move-exception instruction only requires its destination register to
+ // fit in 8 bits. In some situations, it might be better to use a register which is >= 16 if we
+ // end up using that many registers.
+ private int getMoveExceptionRegister() {
+ assert hasDedicatedMoveExceptionRegister();
+ return numberOfArgumentRegisters;
}
public LinearScanRegisterAllocator(IRCode code, InternalOptions options) {
@@ -740,6 +742,8 @@
} else {
// Treat the argument interval as spilled which will require a load to a different
// register for all register-constrained usages.
+ inactive.add(argumentInterval);
+ // Split argument live interval at its first constrained use.
if (argumentInterval.getUses().size() > 1) {
LiveIntervalsUse use = argumentInterval.firstUseWithConstraint();
if (use != null) {
@@ -751,12 +755,17 @@
} else {
// If there are multiple register-constrained users, split right after the definition
// to make it more likely that arguments get in usable registers from the start.
+ // TODO(christofferqa): This is not great if there are many arguments with multiple
+ // constrained uses, since we fill up all the low registers immediately, making it
+ // likely that we will have to kick them back out before they are actually used.
split = argumentInterval
.splitBefore(argumentInterval.getValue().definition.getNumber() + 1);
}
unhandled.add(split);
}
}
+ // Since we are not activating the argument live intervals, we need to free their registers.
+ freeOccupiedRegistersForIntervals(argumentInterval);
}
argumentValue = argumentValue.getNextConsecutive();
}
@@ -769,40 +778,33 @@
// When we allow argument reuse we do not allow any splitting, therefore we cannot get into
// trouble with move exception registers. When argument reuse is disallowed we block a fixed
// register to be used only by move exception instructions.
- if (mode != ArgumentReuseMode.ALLOW_ARGUMENT_REUSE) {
+ if (mode == ArgumentReuseMode.DISALLOW_ARGUMENT_REUSE) {
// Force all move exception ranges to start out with the exception in a fixed register. Split
// their live ranges which will force another register if used.
- int moveExceptionRegister = NO_REGISTER;
boolean overlappingMoveExceptionIntervals = false;
for (BasicBlock block : code.blocks) {
- for (Instruction instruction : block.getInstructions()) {
- if (instruction.isMoveException()) {
- hasDedicatedMoveExceptionRegister = true;
- Value exceptionValue = instruction.outValue();
- LiveIntervals intervals = exceptionValue.getLiveIntervals();
- unhandled.remove(intervals);
- moveExceptionIntervals.add(intervals);
- if (moveExceptionRegister == NO_REGISTER) {
- moveExceptionRegister = getFreeConsecutiveRegisters(1);
- assert moveExceptionRegister == getMoveExceptionRegister();
- assert !freeRegisters.contains(moveExceptionRegister);
- }
- intervals.setRegister(moveExceptionRegister);
- if (!overlappingMoveExceptionIntervals) {
- for (LiveIntervals other : moveExceptionIntervals) {
- overlappingMoveExceptionIntervals |= other.overlaps(intervals);
- }
+ Instruction instruction = block.entry();
+ if (instruction.isMoveException()) {
+ LiveIntervals intervals = instruction.outValue().getLiveIntervals();
+ unhandled.remove(intervals);
+ moveExceptionIntervals.add(intervals);
+ intervals.setRegister(getMoveExceptionRegister());
+ if (!overlappingMoveExceptionIntervals) {
+ for (LiveIntervals other : moveExceptionIntervals) {
+ overlappingMoveExceptionIntervals |= other.overlaps(intervals);
}
}
}
}
+ if (hasDedicatedMoveExceptionRegister()) {
+ int moveExceptionRegister = getMoveExceptionRegister();
+ assert moveExceptionRegister == maxRegisterNumber + 1;
+ increaseCapacity(moveExceptionRegister, true);
+ }
if (overlappingMoveExceptionIntervals) {
for (LiveIntervals intervals : moveExceptionIntervals) {
if (intervals.getUses().size() > 1) {
- LiveIntervalsUse firstUse = intervals.getUses().pollFirst();
- LiveIntervalsUse secondUse = intervals.getUses().pollFirst();
- intervals.getUses().add(firstUse);
- intervals.getUses().add(secondUse);
+ LiveIntervalsUse secondUse = intervals.getUses().stream().skip(1).findFirst().get();
LiveIntervals split = intervals.splitBefore(secondUse.getPosition());
unhandled.add(split);
}
@@ -812,6 +814,8 @@
// Go through each unhandled live interval and find a register for it.
while (!unhandled.isEmpty()) {
+ assert invariantsHold(mode);
+
LiveIntervals unhandledInterval = unhandled.poll();
setHintForDestRegOfCheckCast(unhandledInterval);
@@ -820,9 +824,9 @@
// If this interval value is the src of an argument move. Fix the registers for the
// consecutive arguments now and add hints to the move sources. This looks forward
// and propagate hints backwards to avoid many moves in connection with ranged invokes.
- allocateArgumentIntervalsWithSrc(unhandledInterval);
+ allocateArgumentIntervalsWithSrc(unhandledInterval, mode);
if (unhandledInterval.getRegister() != NO_REGISTER) {
- // The value itself could be in the chain that has now gotten registers allocated.
+ // The value itself is in the chain that has now gotten registers allocated.
continue;
}
@@ -833,12 +837,12 @@
LiveIntervals activeIntervals = activeIterator.next();
if (start >= activeIntervals.getEnd()) {
activeIterator.remove();
- freeRegistersForIntervals(activeIntervals);
+ freeOccupiedRegistersForIntervals(activeIntervals);
} else if (!activeIntervals.overlapsPosition(start)) {
activeIterator.remove();
assert activeIntervals.getRegister() != NO_REGISTER;
inactive.add(activeIntervals);
- freeRegistersForIntervals(activeIntervals);
+ freeOccupiedRegistersForIntervals(activeIntervals);
}
}
@@ -852,13 +856,13 @@
inactiveIterator.remove();
assert inactiveIntervals.getRegister() != NO_REGISTER;
active.add(inactiveIntervals);
- takeRegistersForIntervals(inactiveIntervals);
+ takeFreeRegistersForIntervals(inactiveIntervals);
}
}
// Perform the actual allocation.
if (unhandledInterval.isLinked() && !unhandledInterval.isArgumentInterval()) {
- allocateLinkedIntervals(unhandledInterval);
+ allocateLinkedIntervals(unhandledInterval, false);
} else {
if (!allocateSingleInterval(unhandledInterval, mode)) {
return false;
@@ -868,6 +872,76 @@
return true;
}
+ private boolean invariantsHold(ArgumentReuseMode mode) {
+ TreeSet<Integer> computedFreeRegisters = new TreeSet<>();
+ for (int register = 0; register <= maxRegisterNumber; ++register) {
+ computedFreeRegisters.add(register);
+ }
+ for (LiveIntervals activeIntervals : active) {
+ assert registersForIntervalsAreTaken(activeIntervals);
+ activeIntervals.forEachRegister(
+ register -> {
+ assert computedFreeRegisters.contains(register);
+ computedFreeRegisters.remove(register);
+ });
+ }
+ if (mode == ArgumentReuseMode.DISALLOW_ARGUMENT_REUSE) {
+ // Each time an argument interval is active, we currently require that it is present in its
+ // original, incoming argument register.
+ for (LiveIntervals activeIntervals : active) {
+ if (activeIntervals.isArgumentInterval()
+ && activeIntervals != activeIntervals.getSplitParent()) {
+ LiveIntervals parent = activeIntervals.getSplitParent();
+ if (parent.getRegister() != activeIntervals.getRegister()) {
+ activeIntervals
+ .getSplitParent()
+ .forEachRegister(
+ register -> {
+ assert computedFreeRegisters.contains(register);
+ computedFreeRegisters.remove(register);
+ });
+ }
+ }
+ }
+ }
+ if (hasDedicatedMoveExceptionRegister()) {
+ // Relax the check, since it is not currently guaranteed that the move exception register is
+ // occupied if-and-only-if there is an active live interval with the register.
+ freeRegisters.remove(getMoveExceptionRegister());
+ computedFreeRegisters.remove(getMoveExceptionRegister());
+ }
+ assert freeRegisters.equals(computedFreeRegisters);
+ return true;
+ }
+
+ private boolean freePositionsAreConsistentWithFreeRegisters(
+ RegisterPositions freePositions, int registerConstraint) {
+ int n = Math.min(maxRegisterNumber, registerConstraint);
+ for (int register = 0; register <= n; ++register) {
+ if (freePositions.get(register) > 0) {
+ // If this register is free according to freePositions, then it should also be free
+ // according to freeRegisters.
+ boolean isMoveExceptionRegister =
+ hasDedicatedMoveExceptionRegister() && register == getMoveExceptionRegister();
+ if (!isMoveExceptionRegister) {
+ assert freeRegisters.contains(register);
+ }
+ }
+ }
+ return true;
+ }
+
+ private boolean registerAssignmentNotConflictingWithArgument(LiveIntervals interval) {
+ assert interval.getRegister() != NO_REGISTER;
+ for (Value argumentValue = firstArgumentValue;
+ argumentValue != null;
+ argumentValue = argumentValue.getNextConsecutive()) {
+ assert !interval.hasConflictingRegisters(argumentValue.getLiveIntervals())
+ || !argumentValue.getLiveIntervals().anySplitOverlaps(interval);
+ }
+ return true;
+ }
+
private void setHintForDestRegOfCheckCast(LiveIntervals unhandledInterval) {
if (unhandledInterval.getHint() == null &&
unhandledInterval.getValue().definition instanceof CheckCast) {
@@ -915,7 +989,7 @@
* allocated and have been moved from unhandled to inactive. The move sources have their hints
* updated. The rest of the register allocation state is unchanged.
*/
- private void allocateArgumentIntervalsWithSrc(LiveIntervals srcInterval) {
+ private void allocateArgumentIntervalsWithSrc(LiveIntervals srcInterval, ArgumentReuseMode mode) {
Value value = srcInterval.getValue();
for (Instruction instruction : value.uniqueUsers()) {
// If there is a move user that is an argument move, we allocate the consecutive
@@ -928,8 +1002,7 @@
if (destIntervals.getRegister() == NO_REGISTER) {
// Save the current register allocation state so we can restore it at the end.
TreeSet<Integer> savedFreeRegisters = new TreeSet<>(freeRegisters);
- int savedUnusedRegisterNumber = getNextUnusedRegisterNumber();
- List<LiveIntervals> savedActive = new LinkedList<>(active);
+ int savedMaxRegisterNumber = maxRegisterNumber;
List<LiveIntervals> savedInactive = new LinkedList<>(inactive);
// Add all the active intervals to the inactive set. When allocating linked intervals we
@@ -944,23 +1017,35 @@
// registers works the best for code size.
if (active.isArgumentInterval()) {
// Allow the ranged invoke to use argument registers if free. This improves register
- // allocation for brigde methods that forwards all of their arguments after check-cast
+ // allocation for bridge methods that forwards all of their arguments after check-cast
// checks on their types.
- freeRegistersForIntervals(active);
+ freeOccupiedRegistersForIntervals(active);
}
inactive.add(active);
}
// Allocate the argument intervals.
unhandled.remove(destIntervals);
- allocateLinkedIntervals(destIntervals);
+ boolean excludeUnhandledOverlappingArgumentIntervals = false;
+ if (mode == ArgumentReuseMode.DISALLOW_ARGUMENT_REUSE) {
+ // Since we are going to do a look-ahead, there may be argument live interval splits,
+ // which are currently unhandled, but would be inactive at the invoke-range instruction.
+ // Thus, the implementation of allocateLinkedIntervals needs to exclude the argument
+ // registers for which there exists a split that overlaps with one of the inputs to the
+ // invoke-range instruction. We handle this situation by setting the following flag.
+ excludeUnhandledOverlappingArgumentIntervals = true;
+ }
+ unhandled.add(srcInterval);
+ allocateLinkedIntervals(destIntervals, excludeUnhandledOverlappingArgumentIntervals);
+ active.remove(destIntervals);
+ unhandled.remove(srcInterval);
// Restore the register allocation state.
freeRegisters = savedFreeRegisters;
// In case maxRegisterNumber has changed, update freeRegisters.
- for (int i = savedUnusedRegisterNumber, j = getNextUnusedRegisterNumber(); i < j; i++) {
+ for (int i = savedMaxRegisterNumber + 1; i <= maxRegisterNumber; i++) {
freeRegisters.add(i);
}
- active = savedActive;
+
inactive = savedInactive;
// Move all the argument intervals to the inactive set.
LiveIntervals current = destIntervals.getStartOfConsecutive();
@@ -976,35 +1061,43 @@
}
}
- private void allocateLinkedIntervals(LiveIntervals unhandledInterval) {
+ private void allocateLinkedIntervals(
+ LiveIntervals unhandledInterval, boolean excludeUnhandledOverlappingArgumentIntervals) {
+ LiveIntervals start = unhandledInterval.getStartOfConsecutive();
+
// Exclude the registers that overlap the start of one of the live ranges we are
// going to assign registers to now.
- LiveIntervals current = unhandledInterval.getStartOfConsecutive();
- Set<Integer> excludedRegisters = new HashSet<>();
- while (current != null) {
+ IntSet excludedRegisters = new IntArraySet();
+ for (LiveIntervals current = start; current != null; current = current.getNextConsecutive()) {
for (LiveIntervals inactiveIntervals : inactive) {
if (inactiveIntervals.overlaps(current)) {
excludeRegistersForInterval(inactiveIntervals, excludedRegisters);
}
}
- current = current.getNextConsecutive();
}
- current = unhandledInterval.getStartOfConsecutive();
+ if (excludeUnhandledOverlappingArgumentIntervals && firstArgumentValue != null) {
+ // Exclude the argument registers for which there exists a split that overlaps with one of
+ // the inputs to the invoke-range instruction.
+ for (LiveIntervals intervals = firstArgumentValue.getLiveIntervals();
+ intervals != null;
+ intervals = intervals.getNextConsecutive()) {
+ if (liveIntervalsHasUnhandledSplitOverlappingAnyOf(intervals, start)) {
+ excludeRegistersForInterval(intervals, excludedRegisters);
+ }
+ }
+ }
// Exclude move exception register if the first interval overlaps a move exception interval.
- if (overlapsMoveExceptionInterval(current) &&
- freeRegisters.remove(getMoveExceptionRegister())) {
+ // It is not necessary to check the remaining consecutive intervals, since we always use
+ // register 0 (after remapping) for the argument register.
+ if (overlapsMoveExceptionInterval(start) && freeRegisters.remove(getMoveExceptionRegister())) {
excludedRegisters.add(getMoveExceptionRegister());
}
// Select registers.
- int numberOfRegister = current.numberOfConsecutiveRegisters();
- int firstRegister = getFreeConsecutiveRegisters(numberOfRegister);
- for (int i = 0; i < numberOfRegister; i++) {
- assert current != null;
- current.setRegister(firstRegister + i);
- if (current.getType().isWide()) {
- assert i < numberOfRegister;
- i++;
- }
+ int numberOfRegisters = start.numberOfConsecutiveRegisters();
+ int nextRegister = getFreeConsecutiveRegisters(numberOfRegisters);
+ for (LiveIntervals current = start; current != null; current = current.getNextConsecutive()) {
+ current.setRegister(nextRegister);
+ assert registerAssignmentNotConflictingWithArgument(current);
// Propagate hints to the move sources.
Value value = current.getValue();
if (!value.isPhi() && value.definition.isMove()) {
@@ -1017,32 +1110,43 @@
// intervals in the chain have been assigned registers but their start has not yet been
// reached. Therefore, they belong in the inactive set.
unhandled.remove(current);
- assert current.getRegister() != NO_REGISTER;
inactive.add(current);
- freeRegistersForIntervals(current);
}
- current = current.getNextConsecutive();
+ nextRegister += current.requiredRegisters();
}
- assert current == null;
+
assert unhandledInterval.getRegister() != NO_REGISTER;
+ takeFreeRegistersForIntervals(unhandledInterval);
active.add(unhandledInterval);
// Include the registers for inactive ranges that we had to exclude for this allocation.
freeRegisters.addAll(excludedRegisters);
}
- // Update the information about used registers when |register| has been selected for use.
- private void updateRegisterState(int register, boolean needsRegisterPair) {
- int maxRegister = register + (needsRegisterPair ? 1 : 0);
- maxRegisterNumber = Math.max(maxRegisterNumber, maxRegister);
+ // Returns true if intervals has an unhandled split, which overlaps with chain or any of its
+ // consecutives.
+ private boolean liveIntervalsHasUnhandledSplitOverlappingAnyOf(
+ LiveIntervals intervals, LiveIntervals chain) {
+ assert intervals == intervals.getSplitParent();
+ assert chain == chain.getStartOfConsecutive();
+ for (LiveIntervals split : intervals.getSplitChildren()) {
+ if (unhandled.contains(split)) {
+ for (LiveIntervals current = chain;
+ current != null;
+ current = current.getNextConsecutive()) {
+ if (split.overlaps(current)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
}
private int getSpillRegister(LiveIntervals intervals) {
- int registerNumber = getNextUnusedRegisterNumber();
- maxRegisterNumber = registerNumber;
- if (intervals.getType().isWide()) {
- maxRegisterNumber++;
- }
- return registerNumber;
+ int register = maxRegisterNumber + 1;
+ increaseCapacity(maxRegisterNumber + intervals.requiredRegisters());
+ assert registersAreFree(register, intervals.getType().isWide());
+ return register;
}
private int toInstructionPosition(int position) {
@@ -1232,7 +1336,7 @@
// invoke method r4
// return
private boolean overlapsMoveExceptionInterval(LiveIntervals intervals) {
- if (!hasDedicatedMoveExceptionRegister) {
+ if (!hasDedicatedMoveExceptionRegister()) {
return false;
}
for (LiveIntervals moveExceptionInterval : moveExceptionIntervals) {
@@ -1254,7 +1358,7 @@
// avoid move generation for the argument.
if (registerConstraint == Constants.U16BIT_MAX && unhandledInterval.isArgumentInterval()) {
int argumentRegister = unhandledInterval.getSplitParent().getRegister();
- assignRegisterToUnhandledInterval(unhandledInterval, needsRegisterPair, argumentRegister);
+ assignFreeRegisterToUnhandledInterval(unhandledInterval, argumentRegister);
return true;
}
@@ -1333,6 +1437,8 @@
}
}
+ assert freePositionsAreConsistentWithFreeRegisters(freePositions, registerConstraint);
+
// Attempt to use register hints.
if (useRegisterHint(unhandledInterval, registerConstraint, freePositions, needsRegisterPair)) {
return true;
@@ -1376,26 +1482,35 @@
register = getSpillRegister(unhandledInterval);
}
LiveIntervals split = unhandledInterval.splitBefore(nextConstrainedPosition);
- assignRegisterToUnhandledInterval(unhandledInterval, needsRegisterPair, register);
+ assignFreeRegisterToUnhandledInterval(unhandledInterval, register);
unhandled.add(split);
} else {
allocateBlockedRegister(unhandledInterval);
}
- } else if (largestFreePosition >= unhandledInterval.getEnd()) {
- // Free for the entire interval. Allocate the register.
- assignRegisterToUnhandledInterval(unhandledInterval, needsRegisterPair, candidate);
} else {
- if (mode == ArgumentReuseMode.ALLOW_ARGUMENT_REUSE) {
- // No splitting is allowed when we allow argument reuse. Bailout and start over with
- // argument reuse disallowed.
- return false;
+ // We will use the candidate register(s) for unhandledInterval, and therefore potentially
+ // need to adjust maxRegisterNumber.
+ int candidateEnd = candidate + unhandledInterval.requiredRegisters() - 1;
+ if (candidateEnd > maxRegisterNumber) {
+ increaseCapacity(candidateEnd);
}
- // The candidate is free for the beginning of an interval. We split the interval
- // and use the register for as long as we can.
- LiveIntervals split = unhandledInterval.splitBefore(largestFreePosition);
- assert split != unhandledInterval;
- assignRegisterToUnhandledInterval(unhandledInterval, needsRegisterPair, candidate);
- unhandled.add(split);
+
+ if (largestFreePosition >= unhandledInterval.getEnd()) {
+ // Free for the entire interval. Allocate the register.
+ assignFreeRegisterToUnhandledInterval(unhandledInterval, candidate);
+ } else {
+ if (mode == ArgumentReuseMode.ALLOW_ARGUMENT_REUSE) {
+ // No splitting is allowed when we allow argument reuse. Bailout and start over with
+ // argument reuse disallowed.
+ return false;
+ }
+ // The candidate is free for the beginning of an interval. We split the interval
+ // and use the register for as long as we can.
+ LiveIntervals split = unhandledInterval.splitBefore(largestFreePosition);
+ assert split != unhandledInterval;
+ assignFreeRegisterToUnhandledInterval(unhandledInterval, candidate);
+ unhandled.add(split);
+ }
}
return true;
}
@@ -1473,7 +1588,7 @@
isArrayGetArrayRegister(unhandledInterval, register)) {
return false;
}
- assignRegisterToUnhandledInterval(unhandledInterval, needsRegisterPair, register);
+ assignFreeRegisterToUnhandledInterval(unhandledInterval, register);
return true;
}
}
@@ -1481,6 +1596,7 @@
}
private void assignRegister(LiveIntervals intervals, int register) {
+ assert register + intervals.requiredRegisters() - 1 <= maxRegisterNumber;
intervals.setRegister(register);
updateRegisterHints(intervals);
}
@@ -1520,11 +1636,10 @@
}
}
- private void assignRegisterToUnhandledInterval(
- LiveIntervals unhandledInterval, boolean needsRegisterPair, int register) {
+ private void assignFreeRegisterToUnhandledInterval(
+ LiveIntervals unhandledInterval, int register) {
assignRegister(unhandledInterval, register);
- takeRegistersForIntervals(unhandledInterval);
- updateRegisterState(register, needsRegisterPair);
+ takeFreeRegistersForIntervals(unhandledInterval);
active.add(unhandledInterval);
}
@@ -1665,7 +1780,7 @@
blockedPositions);
// Get the register (pair) that has the highest use position.
- boolean needsRegisterPair = unhandledInterval.requiredRegisters() == 2;
+ boolean needsRegisterPair = unhandledInterval.getType().isWide();
// First look for a candidate that can be rematerialized.
int candidate = getLargestValidCandidate(unhandledInterval, registerConstraint,
@@ -1705,17 +1820,26 @@
LiveIntervals split = unhandledInterval.splitBefore(splitPosition);
assert split != unhandledInterval;
int registerNumber = getSpillRegister(unhandledInterval);
- assignRegisterToUnhandledInterval(unhandledInterval, needsRegisterPair, registerNumber);
+ assignFreeRegisterToUnhandledInterval(unhandledInterval, registerNumber);
unhandledInterval.setSpilled(true);
unhandled.add(split);
- } else if (blockedPosition > unhandledInterval.getEnd()) {
- // Spilling can make a register available for the entire interval.
- assignRegisterAndSpill(unhandledInterval, needsRegisterPair, candidate);
} else {
- // Spilling only makes a register available for the first part of current.
- LiveIntervals splitChild = unhandledInterval.splitBefore(blockedPosition);
- unhandled.add(splitChild);
- assignRegisterAndSpill(unhandledInterval, needsRegisterPair, candidate);
+ // We will use the candidate register(s) for unhandledInterval, and therefore potentially
+ // need to adjust maxRegisterNumber.
+ int candidateEnd = candidate + unhandledInterval.requiredRegisters() - 1;
+ if (candidateEnd > maxRegisterNumber) {
+ increaseCapacity(candidateEnd);
+ }
+
+ if (blockedPosition > unhandledInterval.getEnd()) {
+ // Spilling can make a register available for the entire interval.
+ assignRegisterAndSpill(unhandledInterval, candidate, needsRegisterPair);
+ } else {
+ // Spilling only makes a register available for the first part of current.
+ LiveIntervals splitChild = unhandledInterval.splitBefore(blockedPosition);
+ unhandled.add(splitChild);
+ assignRegisterAndSpill(unhandledInterval, candidate, needsRegisterPair);
+ }
}
}
@@ -1729,29 +1853,25 @@
}
private void assignRegisterAndSpill(
- LiveIntervals unhandledInterval,
- boolean needsRegisterPair,
- int candidate) {
- assignRegister(unhandledInterval, candidate);
- updateRegisterState(candidate, needsRegisterPair);
+ LiveIntervals unhandledInterval, int candidate, boolean candidateIsWide) {
// Split and spill intersecting active intervals for this register.
- spillOverlappingActiveIntervals(unhandledInterval, needsRegisterPair, candidate);
- takeRegistersForIntervals(unhandledInterval);
+ spillOverlappingActiveIntervals(unhandledInterval, candidate, candidateIsWide);
+ // Now that that active intervals have been spilled, we are free to take the candidate.
+ assignRegister(unhandledInterval, candidate);
+ takeFreeRegistersForIntervals(unhandledInterval);
active.add(unhandledInterval);
// Split all overlapping inactive intervals for this register. They need to have a new
// register assigned at the next use.
- splitOverlappingInactiveIntervals(unhandledInterval, needsRegisterPair, candidate);
+ splitOverlappingInactiveIntervals(unhandledInterval, candidate, candidateIsWide);
}
protected void splitOverlappingInactiveIntervals(
- LiveIntervals unhandledInterval,
- boolean needsRegisterPair,
- int candidate) {
+ LiveIntervals unhandledInterval, int candidate, boolean candidateIsWide) {
List<LiveIntervals> newInactive = new ArrayList<>();
Iterator<LiveIntervals> inactiveIterator = inactive.iterator();
while (inactiveIterator.hasNext()) {
LiveIntervals intervals = inactiveIterator.next();
- if (intervals.usesRegister(candidate, needsRegisterPair)
+ if (intervals.usesRegister(candidate, candidateIsWide)
&& intervals.overlaps(unhandledInterval)) {
if (intervals.isLinked() && !intervals.isArgumentInterval()) {
// If the inactive register is linked but not an argument, it needs to get the
@@ -1783,21 +1903,26 @@
}
private void spillOverlappingActiveIntervals(
- LiveIntervals unhandledInterval,
- boolean needsRegisterPair,
- int candidate) {
+ LiveIntervals unhandledInterval, int candidate, boolean candidateIsWide) {
+ assert unhandledInterval.getRegister() == NO_REGISTER;
+ assert atLeastOneOfRegistersAreTaken(candidate, candidateIsWide);
+ // Spill overlapping active intervals.
List<LiveIntervals> newActive = new ArrayList<>();
Iterator<LiveIntervals> activeIterator = active.iterator();
while (activeIterator.hasNext()) {
LiveIntervals intervals = activeIterator.next();
- if (intervals.usesRegister(candidate, needsRegisterPair)) {
+ assert registersForIntervalsAreTaken(intervals);
+ if (intervals.usesRegister(candidate, candidateIsWide)) {
activeIterator.remove();
- freeRegistersForIntervals(intervals);
- LiveIntervals splitChild = intervals.splitBefore(unhandledInterval.getStart());
int registerNumber = getSpillRegister(intervals);
+ // Important not to free the registers for intervals before finding a spill register,
+ // because we might otherwise end up spilling to the current registers of intervals,
+ // depending on getSpillRegister.
+ freeOccupiedRegistersForIntervals(intervals);
+ LiveIntervals splitChild = intervals.splitBefore(unhandledInterval.getStart());
assignRegister(splitChild, registerNumber);
splitChild.setSpilled(true);
- takeRegistersForIntervals(splitChild);
+ takeFreeRegistersForIntervals(splitChild);
assert splitChild.getRegister() != NO_REGISTER;
assert intervals.getRegister() != NO_REGISTER;
newActive.add(splitChild);
@@ -1827,6 +1952,7 @@
}
}
active.addAll(newActive);
+ assert registersAreFree(candidate, candidateIsWide);
}
private void splitRangesForSpilledArgument(LiveIntervals spilled) {
@@ -1847,7 +1973,9 @@
assert spilled.isSpilled();
assert !spilled.getValue().isConstNumber();
assert !spilled.isLinked() || spilled.isArgumentInterval();
- if (spilled.isArgumentInterval()) {
+ boolean isSpillingToArgumentRegister =
+ (spilled.isArgumentInterval() || registerNumber < numberOfArgumentRegisters);
+ if (isSpillingToArgumentRegister) {
registerNumber = Constants.U16BIT_MAX;
}
LiveIntervalsUse firstUseWithLowerLimit = null;
@@ -2325,22 +2453,33 @@
private void pinArgumentRegisters() {
// Special handling for arguments. Pin their register.
if (firstArgumentValue != null) {
+ increaseCapacity(numberOfArgumentRegisters - 1, true);
int register = 0;
- Value current = firstArgumentValue;
- while (current != null) {
+ for (Value current = firstArgumentValue;
+ current != null;
+ current = current.getNextConsecutive()) {
LiveIntervals argumentLiveInterval = current.getLiveIntervals();
- // Pin the argument register. We use getFreeConsecutiveRegisters to make sure that we update
- // the max register number.
- register = getFreeConsecutiveRegisters(argumentLiveInterval.requiredRegisters());
assignRegister(argumentLiveInterval, register);
- current = current.getNextConsecutive();
+ register += current.requiredRegisters();
}
- // If the last argument is wide, then its register will be numberOfArgumentRegisters - 2.
- assert register == numberOfArgumentRegisters - 1 || register == numberOfArgumentRegisters - 2;
}
}
+ private void increaseCapacity(int newMaxRegisterNumber) {
+ increaseCapacity(newMaxRegisterNumber, false);
+ }
+
+ private void increaseCapacity(int newMaxRegisterNumber, boolean takeRegisters) {
+ if (!takeRegisters) {
+ for (int register = maxRegisterNumber + 1; register <= newMaxRegisterNumber; ++register) {
+ freeRegisters.add(register);
+ }
+ }
+ maxRegisterNumber = newMaxRegisterNumber;
+ }
+
private int getFreeConsecutiveRegisters(int numberOfRegisters) {
+ int oldMaxRegisterNumber = maxRegisterNumber;
Iterator<Integer> freeRegistersIterator = freeRegisters.iterator();
int first = getNextFreeRegister(freeRegistersIterator);
int current = first;
@@ -2357,8 +2496,9 @@
current++;
}
}
- for (int register = first; register < first + numberOfRegisters; ++register) {
- freeRegisters.remove(register);
+ for (int register = oldMaxRegisterNumber + 1; register <= maxRegisterNumber; ++register) {
+ boolean wasAdded = freeRegisters.add(register);
+ assert wasAdded;
}
// Either all the consecutive registers are from the argument registers, or all are from the
// non-argument registers.
@@ -2373,35 +2513,87 @@
if (freeRegistersIterator.hasNext()) {
return freeRegistersIterator.next();
}
- maxRegisterNumber = getNextUnusedRegisterNumber();
- return maxRegisterNumber;
+ return ++maxRegisterNumber;
}
- private void excludeRegistersForInterval(LiveIntervals intervals, Set<Integer> excluded) {
+ private void excludeRegistersForInterval(LiveIntervals intervals, IntSet excluded) {
int register = intervals.getRegister();
+ assert register != NO_REGISTER;
+
for (int i = 0; i < intervals.requiredRegisters(); i++) {
if (freeRegisters.remove(register + i)) {
excluded.add(register + i);
}
}
+
+ if (intervals.isArgumentInterval() && intervals != intervals.getSplitParent()) {
+ LiveIntervals parent = intervals.getSplitParent();
+ if (parent.getRegister() != register) {
+ excludeRegistersForInterval(parent, excluded);
+ }
+ }
}
- private void freeRegistersForIntervals(LiveIntervals intervals) {
+ private void freeOccupiedRegistersForIntervals(LiveIntervals intervals) {
+ assert registersForIntervalsAreTaken(intervals);
int register = intervals.getRegister();
+ assert register + intervals.requiredRegisters() - 1 <= maxRegisterNumber;
freeRegisters.add(register);
if (intervals.getType().isWide()) {
freeRegisters.add(register + 1);
}
+
+ if (intervals.isArgumentInterval() && intervals != intervals.getSplitParent()) {
+ LiveIntervals parent = intervals.getSplitParent();
+ if (parent.getRegister() != intervals.getRegister()) {
+ freeOccupiedRegistersForIntervals(intervals.getSplitParent());
+ }
+ }
}
- private void takeRegistersForIntervals(LiveIntervals intervals) {
- int register = intervals.getRegister();
+ private void takeFreeRegisters(int register, boolean isWide) {
+ assert registersAreFree(register, isWide);
freeRegisters.remove(register);
- if (intervals.getType().isWide()) {
+ if (isWide) {
freeRegisters.remove(register + 1);
}
}
+ private void takeFreeRegistersForIntervals(LiveIntervals intervals) {
+ takeFreeRegisters(intervals.getRegister(), intervals.getType().isWide());
+
+ if (intervals.isArgumentInterval() && intervals != intervals.getSplitParent()) {
+ LiveIntervals parent = intervals.getSplitParent();
+ if (parent.getRegister() != intervals.getRegister()) {
+ takeFreeRegistersForIntervals(parent);
+ }
+ }
+ }
+
+ private boolean registerIsFree(int register) {
+ return freeRegisters.contains(register)
+ || (hasDedicatedMoveExceptionRegister() && register == getMoveExceptionRegister());
+ }
+
+ // Note: treats a register as free if it is in the set of free registers, or it is the dedicated
+ // move exception register.
+ private boolean registersAreFree(int register, boolean isWide) {
+ return registerIsFree(register) && (!isWide || registerIsFree(register + 1));
+ }
+
+ private boolean registersAreTaken(int register, boolean isWide) {
+ return !freeRegisters.contains(register) && (!isWide || !freeRegisters.contains(register + 1));
+ }
+
+ private boolean registersForIntervalsAreTaken(LiveIntervals intervals) {
+ assert intervals.getRegister() != NO_REGISTER;
+ return registersAreTaken(intervals.getRegister(), intervals.getType().isWide());
+ }
+
+ private boolean atLeastOneOfRegistersAreTaken(int register, boolean isWide) {
+ return !freeRegisters.contains(register) || (isWide && !freeRegisters.contains(register + 1));
+ }
+
private boolean noLinkedValues() {
for (BasicBlock block : code.blocks) {
for (Phi phi : block.getPhis()) {
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 df5df48..7e398e6 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
@@ -15,6 +15,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
+import java.util.function.IntConsumer;
public class LiveIntervals implements Comparable<LiveIntervals> {
@@ -383,6 +384,14 @@
return null;
}
+ public void forEachRegister(IntConsumer consumer) {
+ assert register != NO_REGISTER;
+ consumer.accept(register);
+ if (getType().isWide()) {
+ consumer.accept(register + 1);
+ }
+ }
+
public LiveIntervals splitBefore(int start) {
if (toInstructionPosition(start) == toInstructionPosition(getStart())) {
assert uses.size() == 0 || getFirstUse() != start;
diff --git a/src/test/examples/regress_77944861/SomeView.java b/src/test/examples/regress_77944861/SomeView.java
new file mode 100644
index 0000000..483dd77
--- /dev/null
+++ b/src/test/examples/regress_77944861/SomeView.java
@@ -0,0 +1,22 @@
+// 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 regress_77944861;
+
+import regress_77944861.inner.TopLevelPolicy;
+import regress_77944861.inner.TopLevelPolicy.MobileIconState;
+
+public class SomeView {
+
+ public static String get(MobileIconState state) {
+ // Field read context. TopLevelPolicy$IconState is not accessible in this context.
+ return state.description;
+ }
+
+ public static void main(String[] args) {
+ MobileIconState state = new MobileIconState();
+ TopLevelPolicy.set(state, "foo");
+ System.out.println(get(state));
+ }
+}
+
diff --git a/src/test/examples/regress_77944861/inner/TopLevelPolicy.java b/src/test/examples/regress_77944861/inner/TopLevelPolicy.java
new file mode 100644
index 0000000..3fc97e0
--- /dev/null
+++ b/src/test/examples/regress_77944861/inner/TopLevelPolicy.java
@@ -0,0 +1,19 @@
+// 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 regress_77944861.inner;
+
+public class TopLevelPolicy {
+
+ public static void set(MobileIconState state, String desc) {
+ // Field write context. As in the same package, $IconState is accessible.
+ // If read/write contexts are not considered together, this leads to a wrong field binding.
+ state.description = desc;
+ }
+
+ private static abstract class IconState {
+ public String description;
+ }
+
+ public static class MobileIconState extends IconState {}
+}
diff --git a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
index 1236866..d371243 100644
--- a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
+++ b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
@@ -12,24 +12,32 @@
public class ContinuousSteppingTest extends DebugTestBase {
- private static DebugTestConfig config;
+ private static DebugTestConfig javaD8Config;
+ private static DebugTestConfig kotlinD8Config;
@BeforeClass
public static void setup() {
- config = new D8DebugTestResourcesConfig(temp);
+ javaD8Config = new D8DebugTestResourcesConfig(temp);
+ kotlinD8Config = new KotlinD8Config(temp);
}
@Test
public void testArithmetic() throws Throwable {
- runContinuousTest("Arithmetic");
+ runContinuousTest("Arithmetic", javaD8Config);
}
@Test
public void testLocals() throws Throwable {
- runContinuousTest("Locals");
+ runContinuousTest("Locals", javaD8Config);
}
- private void runContinuousTest(String debuggeeClassName) throws Throwable {
+ @Test
+ public void testKotlinInline() throws Throwable {
+ runContinuousTest("KotlinInline", kotlinD8Config);
+ }
+
+ private void runContinuousTest(String debuggeeClassName, DebugTestConfig config)
+ throws Throwable {
runDebugTest(
config,
debuggeeClassName,
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index 685e8f7..e8d21f2 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -1118,21 +1118,27 @@
}));
}
+ private String convertCurrentLocationToString() {
+ return getSourceFile() + ":" + getLineNumber()
+ + " (pc 0x" + Long.toHexString(location.index) + ")";
+ }
+
private void failNoLocal(String localName) {
- Assert.fail(
- "line " + getLineNumber() + ": Expected local '" + localName + "' not present");
+ String locationString = convertCurrentLocationToString();
+ Assert.fail("expected local '" + localName + "' is not present at " + locationString);
}
@Override
public void checkNoLocal(String localName) {
- Optional<Variable> localVar = JUnit3Wrapper
- .getVariableAt(mirror, getLocation(), localName);
- Assert.assertFalse("Unexpected local: " + localName, localVar.isPresent());
+ Optional<Variable> localVar = getVariableAt(mirror, getLocation(), localName);
+ if (localVar.isPresent()) {
+ String locationString = convertCurrentLocationToString();
+ Assert.fail("unexpected local '" + localName + "' is present at " + locationString);
+ }
}
public void checkLocal(String localName) {
- Optional<Variable> localVar = JUnit3Wrapper
- .getVariableAt(mirror, getLocation(), localName);
+ Optional<Variable> localVar = getVariableAt(mirror, getLocation(), localName);
if (!localVar.isPresent()) {
failNoLocal(localName);
}
@@ -1482,10 +1488,16 @@
ReplyPacket replyPacket = getMirror().getLineTable(classId, breakpointMethodId);
checkReplyPacket(replyPacket, "Failed to get method line table");
long start = replyPacket.getNextValueAsLong(); // start
- long end = replyPacket.getNextValueAsLong(); // end
+ replyPacket.getNextValueAsLong(); // end
int linesCount = replyPacket.getNextValueAsInt();
if (linesCount == 0) {
- pcs.add(-1L);
+ if (lineToSearch == FIRST_LINE) {
+ // There is no line table but we are not looking for a specific line. Therefore just
+ // set the breakpoint on the 1st instruction.
+ pcs.add(start);
+ } else {
+ pcs.add(-1L);
+ }
} else {
if (lineToSearch == FIRST_LINE) {
// Read only the 1st line because code indices are in ascending order
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinD8Config.java b/src/test/java/com/android/tools/r8/debug/KotlinD8Config.java
new file mode 100644
index 0000000..fa5f5ca
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/KotlinD8Config.java
@@ -0,0 +1,44 @@
+// 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.debug;
+
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApp;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * Shared test configuration for D8 compiled resources from the "debugTestResourcesKotlin" target.
+ */
+class KotlinD8Config extends D8DebugTestConfig {
+
+ private static final Path DEBUGGEE_KOTLIN_JAR =
+ Paths.get(ToolHelper.BUILD_DIR, "test", "debug_test_resources_kotlin.jar");
+
+ private static AndroidApp compiledResources = null;
+
+ private static synchronized AndroidApp getCompiledResources() throws Throwable {
+ if (compiledResources == null) {
+ compiledResources =
+ D8DebugTestConfig.d8Compile(
+ Collections.singletonList(DEBUGGEE_KOTLIN_JAR), null);
+ }
+ return compiledResources;
+ }
+
+ public KotlinD8Config(TemporaryFolder temp) {
+ super();
+ try {
+ Path out = temp.newFolder().toPath().resolve("d8_debug_test_resources_kotlin.jar");
+ getCompiledResources().write(out, OutputMode.DexIndexed);
+ addPaths(out);
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinDebugTestBase.java b/src/test/java/com/android/tools/r8/debug/KotlinDebugTestBase.java
index 845a450..5ddd98c 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinDebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinDebugTestBase.java
@@ -4,51 +4,20 @@
package com.android.tools.r8.debug;
-import com.android.tools.r8.OutputMode;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.utils.AndroidApp;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import org.apache.harmony.jpda.tests.framework.jdwp.Frame.Variable;
import org.apache.harmony.jpda.tests.framework.jdwp.Location;
import org.junit.BeforeClass;
-import org.junit.rules.TemporaryFolder;
/**
* A specialization for Kotlin-based tests which provides extra commands.
*/
public abstract class KotlinDebugTestBase extends DebugTestBase {
- private static final Path DEBUGGEE_KOTLIN_JAR =
- Paths.get(ToolHelper.BUILD_DIR, "test", "debug_test_resources_kotlin.jar");
-
- protected static class KotlinD8Config extends D8DebugTestConfig {
-
- private static AndroidApp compiledResources = null;
-
- private static synchronized AndroidApp getCompiledResources() throws Throwable {
- if (compiledResources == null) {
- compiledResources =
- D8DebugTestConfig.d8Compile(Collections.singletonList(DEBUGGEE_KOTLIN_JAR), null);
- }
- return compiledResources;
- }
-
- public KotlinD8Config(TemporaryFolder temp) {
- super();
- try {
- Path out = temp.newFolder().toPath().resolve("d8_debug_test_resources_kotlin.jar");
- getCompiledResources().write(out, OutputMode.DexIndexed);
- addPaths(out);
- } catch (Throwable e) {
- throw new RuntimeException(e);
- }
- }
- }
-
private static KotlinD8Config d8Config;
@BeforeClass
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/Regress68656641.java b/src/test/java/com/android/tools/r8/ir/regalloc/Regress68656641.java
index e76177c..9866b34 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/Regress68656641.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/Regress68656641.java
@@ -29,7 +29,7 @@
}
public void splitOverlappingInactiveIntervals(LiveIntervals intervals, int register) {
- splitOverlappingInactiveIntervals(intervals, false, register);
+ splitOverlappingInactiveIntervals(intervals, register, false);
}
public PriorityQueue<LiveIntervals> getUnhandled() {
diff --git a/src/test/java/com/android/tools/r8/resolution/B77944861.java b/src/test/java/com/android/tools/r8/resolution/B77944861.java
new file mode 100644
index 0000000..aa5552a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/B77944861.java
@@ -0,0 +1,86 @@
+// 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.resolution;
+
+import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.VmTestRunner;
+import com.android.tools.r8.code.IgetObject;
+import com.android.tools.r8.code.ReturnObject;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.FileUtils;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import regress_77944861.SomeView;
+
+@RunWith(VmTestRunner.class)
+public class B77944861 extends TestBase {
+
+ private static final String PRG =
+ ToolHelper.EXAMPLES_BUILD_DIR + "regress_77944861" + FileUtils.JAR_EXTENSION;
+
+ private AndroidApp runR8(AndroidApp app, Class main, Path out) throws Exception {
+ R8Command command =
+ ToolHelper.addProguardConfigurationConsumer(
+ ToolHelper.prepareR8CommandBuilder(app),
+ pgConfig -> {
+ pgConfig.setPrintMapping(true);
+ pgConfig.setPrintMappingFile(out.resolve(ToolHelper.DEFAULT_PROGUARD_MAP_FILE));
+ })
+ .addProguardConfiguration(
+ ImmutableList.of(keepMainProguardConfiguration(main)),
+ Origin.unknown())
+ .setOutput(out, OutputMode.DexIndexed)
+ .build();
+ return ToolHelper.runR8(command, o -> {
+ o.enableMinification = false;
+ o.enableInlining = false;
+ o.enableTreeShaking = false;
+ });
+ }
+
+ @Test
+ public void test() throws Exception {
+ Path out = temp.getRoot().toPath();
+ Path jarPath = Paths.get(PRG);
+ String mainName = SomeView.class.getCanonicalName();
+ ProcessResult jvmOutput = ToolHelper.runJava(ImmutableList.of(jarPath), mainName);
+ assertEquals(0, jvmOutput.exitCode);
+ AndroidApp processedApp = runR8(readJar(jarPath), SomeView.class, out);
+ DexInspector dexInspector = new DexInspector(processedApp);
+ ClassSubject view = dexInspector.clazz("regress_77944861.SomeView");
+ assertThat(view, isPresent());
+ String className = "regress_77944861.inner.TopLevelPolicy$MobileIconState";
+ MethodSubject initView = view.method("java.lang.String", "get", ImmutableList.of(className));
+ assertThat(initView, isPresent());
+ DexCode code = initView.getMethod().getCode().asDexCode();
+ checkInstructions(code, ImmutableList.of(
+ IgetObject.class,
+ ReturnObject.class));
+ IgetObject fieldAccess = (IgetObject) code.instructions[0];
+ DexField descriptionField = fieldAccess.getField();
+ assertEquals(className, descriptionField.getHolder().toSourceString());
+
+ ProcessResult artOutput = runOnArtRaw(processedApp, mainName);
+ assertEquals(0, artOutput.exitCode);
+ assertEquals(jvmOutput.stdout, artOutput.stdout);
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
index 1631a1f..22cab2a 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
@@ -42,7 +42,6 @@
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
import java.util.Collections;