Annotate all instructions with position information.
This addresses several outstanding issues: blocks can now be reordered without
issue, if positions are present all instructions will be annotated, in
particular move-exceptions for synchornized blocks. This change requires that
all IR tranformations properly transfer position information for each
instruction.
Bug: 65618023
Bug: 65567013
Bug: 65474153
Change-Id: I9ded6003c2b349e738f50e7be58f35536bc5b08b
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
index a5a63e3..c52d12c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -5,11 +5,13 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.DexDebugEvent.StartLocal;
+import com.android.tools.r8.graph.DexEncodedMethod.DebugPositionRange;
import com.android.tools.r8.ir.code.Argument;
import com.android.tools.r8.ir.code.DebugLocalsChange;
-import com.android.tools.r8.ir.code.DebugPosition;
+import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.MoveException;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.utils.InternalOptions;
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap.Entry;
@@ -32,6 +34,7 @@
private final DexEncodedMethod method;
private final DexItemFactory factory;
+ private final InternalOptions options;
// In order list of non-this argument locals.
private ArrayList<DebugLocalInfo> arguments;
@@ -48,8 +51,7 @@
// State of pc, line, file and locals in the emitted event stream.
private int emittedPc = NO_PC_INFO;
- private int emittedLine = NO_LINE_INFO;
- private DexString emittedFile = null;
+ private Position emittedPosition = Position.none();
private Int2ReferenceMap<DebugLocalInfo> emittedLocals;
// Emitted events.
@@ -58,37 +60,65 @@
// Initial known line for the method.
private int startLine = NO_LINE_INFO;
- public DexDebugEventBuilder(DexEncodedMethod method, DexItemFactory factory) {
- this.method = method;
- this.factory = factory;
+ // True if running in debug-mode with input code that contains line information, otherwise false.
+ private boolean hasDebugPositions;
+
+ private DexEncodedMethod.DebugPositionRangeList.Builder debugPositionListBuilder =
+ new DexEncodedMethod.DebugPositionRangeList.Builder();
+
+ public DexDebugEventBuilder(IRCode code, InternalOptions options) {
+ this.method = code.method;
+ this.factory = options.itemFactory;
+ this.options = options;
+ hasDebugPositions = code.hasDebugPositions;
}
/** Add events at pc for instruction. */
- public void add(int pc, Instruction instruction) {
+ public void add(int pc, int postPc, Instruction instruction) {
+ boolean isBlockEntry = instruction.getBlock().entry() == instruction;
+ boolean isBlockExit = instruction.getBlock().exit() == instruction;
+
// Initialize locals state on block entry.
- if (instruction.getBlock().entry() == instruction) {
+ if (isBlockEntry) {
updateBlockEntry(instruction);
}
assert pendingLocals != null;
- if (instruction.isDebugPosition()) {
- emitDebugPosition(pc, instruction.asDebugPosition());
- } else if (instruction.isMoveException()) {
- MoveException move = instruction.asMoveException();
- if (move.getPosition() != null) {
- emitDebugPosition(pc, move.getPosition());
- }
- } else if (instruction.isArgument()) {
+ Position position = instruction.getPosition();
+ boolean pcAdvancing = pc != postPc;
+
+ // In release mode we can only check that all throwing instructions have positions.
+ // See IRCode's isConsistentGraph and computeAllThrowingInstructionsHavePositions.
+
+ // In debug mode check that all non-nop instructions have positions.
+ assert startLine == NO_LINE_INFO || !hasDebugPositions || !pcAdvancing || position.isSome()
+ : "PC-advancing instruction " + instruction + " expected to have an associated position.";
+
+ // In any mode check that nop instructions have no position info.
+ assert pcAdvancing || position.isNone()
+ : "Nop instruction " + instruction + " must never have an associated position.";
+
+ if (instruction.isArgument()) {
startArgument(instruction.asArgument());
} else if (instruction.isDebugLocalsChange()) {
updateLocals(instruction.asDebugLocalsChange());
- } else if (instruction.getBlock().exit() == instruction) {
+ }
+
+ if (!position.isNone() && !position.equals(emittedPosition)) {
+ if (options.debug || instruction.instructionInstanceCanThrow()) {
+ emitDebugPosition(pc, position);
+ }
+ }
+
+ if (!isBlockExit && emittedPc != pc && pcAdvancing) {
+ // For non-exit / pc-advancing instructions emit any pending changes.
+ emitLocalChanges(pc);
+ }
+
+ if (isBlockExit) {
// If this is the end of the block clear out the pending state.
pendingLocals = null;
pendingLocalChanges = false;
- } else if (pc != emittedPc && !instruction.isDebugLocalRead()) {
- // For non-exit / pc-advancing instructions emit any pending changes once possible.
- emitLocalChanges(pc);
}
}
@@ -110,6 +140,10 @@
return new DexDebugInfo(startLine, params, events.toArray(new DexDebugEvent[events.size()]));
}
+ public List<DebugPositionRange> buildPositionRanges() {
+ return debugPositionListBuilder.build();
+ }
+
private void updateBlockEntry(Instruction instruction) {
assert pendingLocals == null;
assert !pendingLocalChanges;
@@ -156,14 +190,7 @@
private void updateLocals(DebugLocalsChange change) {
pendingLocalChanges = true;
- for (Entry<DebugLocalInfo> end : change.getEnding().int2ReferenceEntrySet()) {
- assert pendingLocals.get(end.getIntKey()) == end.getValue();
- pendingLocals.remove(end.getIntKey());
- }
- for (Entry<DebugLocalInfo> start : change.getStarting().int2ReferenceEntrySet()) {
- assert !pendingLocals.containsKey(start.getIntKey());
- pendingLocals.put(start.getIntKey(), start.getValue());
- }
+ change.apply(pendingLocals);
}
private boolean localsChanged() {
@@ -174,32 +201,22 @@
return pendingLocalChanges;
}
- private void emitDebugPosition(int pc, DebugPosition position) {
- emitDebugPosition(pc, position.line, position.file);
- }
-
- private void emitDebugPosition(int pc, int line, DexString file) {
- if (emittedPc == pc) {
- // The pc is unchanged then this must be the same position as the previous and we do nothing.
- // If this assert fails, we have likely forgotten to insert a "nop" instruction between two
- // successive yet distinct debug positions.
- assert emittedLine == line && emittedFile == file;
- return;
- }
- boolean changedLocals = localsChanged();
- if (!changedLocals && emittedLine == line && emittedFile == file) {
- return;
- }
+ private void emitDebugPosition(int pc, Position position) {
+ assert !position.equals(emittedPosition);
if (startLine == NO_LINE_INFO) {
- assert emittedLine == NO_LINE_INFO;
- startLine = line;
- emittedLine = line;
+ if (position.synthetic) {
+ // Ignore synthetic positions prior to any actual position.
+ return;
+ }
+ assert emittedPosition.isNone();
+ startLine = position.line;
+ emittedPosition = position;
}
- emitAdvancementEvents(emittedPc, emittedLine, emittedFile, pc, line, file, events, factory);
+ debugPositionListBuilder.add(position.line, position.line);
+ emitAdvancementEvents(emittedPc, emittedPosition, pc, position, events, factory);
emittedPc = pc;
- emittedLine = line;
- emittedFile = file;
- if (changedLocals) {
+ emittedPosition = position;
+ if (localsChanged()) {
emitLocalChangeEvents(emittedLocals, pendingLocals, lastKnownLocals, events, factory);
assert localsEqual(emittedLocals, pendingLocals);
}
@@ -209,8 +226,7 @@
private void emitLocalChanges(int pc) {
// If pc advanced since the locals changed and locals indeed have changed, emit the changes.
if (localsChanged()) {
- emitAdvancementEvents(
- emittedPc, emittedLine, emittedFile, pc, emittedLine, emittedFile, events, factory);
+ emitAdvancementEvents(emittedPc, emittedPosition, pc, emittedPosition, events, factory);
emittedPc = pc;
emitLocalChangeEvents(emittedLocals, pendingLocals, lastKnownLocals, events, factory);
pendingLocalChanges = false;
@@ -220,19 +236,19 @@
private static void emitAdvancementEvents(
int previousPc,
- int previousLine,
- DexString previousFile,
+ Position previousPosition,
int nextPc,
- int nextLine,
- DexString nextFile,
+ Position nextPosition,
List<DexDebugEvent> events,
DexItemFactory factory) {
assert previousPc != nextPc;
int pcDelta = previousPc == NO_PC_INFO ? nextPc : nextPc - previousPc;
- int lineDelta = nextLine == NO_LINE_INFO ? 0 : nextLine - previousLine;
+ assert !previousPosition.isNone() || nextPosition.isNone();
+ assert nextPosition.isNone() || nextPosition.line >= 0;
+ int lineDelta = nextPosition.isNone() ? 0 : nextPosition.line - previousPosition.line;
assert pcDelta >= 0;
- if (nextFile != previousFile) {
- events.add(factory.createSetFile(nextFile));
+ if (nextPosition.file != previousPosition.file) {
+ events.add(factory.createSetFile(nextPosition.file));
}
if (lineDelta < Constants.DBG_LINE_BASE
|| lineDelta - Constants.DBG_LINE_BASE >= Constants.DBG_LINE_RANGE) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index a541f5a..882bd99 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -273,8 +273,11 @@
}
public void setCode(
- IRCode ir, RegisterAllocator registerAllocator, DexItemFactory dexItemFactory) {
- final DexBuilder builder = new DexBuilder(ir, registerAllocator, dexItemFactory);
+ IRCode ir,
+ RegisterAllocator registerAllocator,
+ DexItemFactory dexItemFactory,
+ InternalOptions options) {
+ final DexBuilder builder = new DexBuilder(ir, registerAllocator, dexItemFactory, options);
code = builder.build(method.getArity());
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Add.java b/src/main/java/com/android/tools/r8/ir/code/Add.java
index ba11abb..1577968 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Add.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Add.java
@@ -73,7 +73,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asAdd().type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/And.java b/src/main/java/com/android/tools/r8/ir/code/And.java
index a6e71ae..0c0714e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/And.java
+++ b/src/main/java/com/android/tools/r8/ir/code/And.java
@@ -69,7 +69,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asAnd().type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Argument.java b/src/main/java/com/android/tools/r8/ir/code/Argument.java
index fbf6658..0cc3962 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Argument.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Argument.java
@@ -18,6 +18,12 @@
public Argument(Value outValue) {
super(outValue);
outValue.markAsArgument();
+ super.setPosition(Position.none());
+ }
+
+ @Override
+ public void setPosition(Position position) {
+ // Arguments never have positional information as they never materialize to actual instructions.
}
@Override
@@ -45,7 +51,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
assert other.isArgument();
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
index e787e59..ee1db81 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
@@ -83,7 +83,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asArrayGet().type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
index 88325a4..0596c1d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
@@ -72,7 +72,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
assert other.isArrayLength();
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
index 11694a3..32b03f4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
@@ -120,7 +120,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asArrayPut().type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index 2324f8e..243e6d5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -770,6 +770,10 @@
return "";
}
+ private static int digits(int number) {
+ return (int) Math.ceil(Math.log10(number + 1));
+ }
+
public String toDetailedString() {
StringBuilder builder = new StringBuilder();
builder.append("block ");
@@ -813,10 +817,26 @@
StringUtils.append(builder, localsAtEntry.int2ReferenceEntrySet(), ", ", BraceType.NONE);
builder.append('\n');
}
+ int lineColumn = 0;
+ int numberColumn = 0;
for (Instruction instruction : instructions) {
- StringUtils.appendLeftPadded(builder, Integer.toString(instruction.getNumber()), 6);
+ lineColumn = Math.max(lineColumn, instruction.getPosition().toString().length());
+ numberColumn = Math.max(numberColumn, digits(instruction.getNumber()));
+ }
+ Position currentPosition = null;
+ for (Instruction instruction : instructions) {
+ if (lineColumn > 0) {
+ String line = "";
+ if (!instruction.getPosition().equals(currentPosition)) {
+ currentPosition = instruction.getPosition();
+ line = currentPosition.toString();
+ }
+ StringUtils.appendLeftPadded(builder, line, lineColumn + 1);
+ builder.append(": ");
+ }
+ StringUtils.appendLeftPadded(builder, "" + instruction.getNumber(), numberColumn + 1);
builder.append(": ");
- StringUtils.appendRightPadded(builder, instruction.toString(), 20);
+ builder.append(instruction.toString());
if (DebugLocalInfo.PRINT_LEVEL != PrintLevel.NONE) {
List<Value> localEnds = new ArrayList<>(instruction.getDebugValues().size());
List<Value> localStarts = new ArrayList<>(instruction.getDebugValues().size());
@@ -1009,6 +1029,27 @@
return instructions.size() == 1 && exit().isGoto();
}
+ // Find the final target from this goto block. Returns null if the goto chain is cyclic.
+ public BasicBlock endOfGotoChain() {
+ BasicBlock hare = this;
+ BasicBlock tortuous = this;
+ boolean advance = false;
+ while (hare.isTrivialGoto()) {
+ hare = hare.exit().asGoto().getTarget();
+ tortuous = advance ? tortuous.exit().asGoto().getTarget() : tortuous;
+ advance = !advance;
+ if (hare == tortuous) {
+ return null;
+ }
+ }
+ return hare;
+ }
+
+ public Position getPosition() {
+ BasicBlock block = endOfGotoChain();
+ return block != null ? block.entry().getPosition() : Position.none();
+ }
+
public boolean hasOneNormalExit() {
return successors.size() == 1 && exit().isGoto();
}
@@ -1199,11 +1240,10 @@
List<BasicBlock> predecessors = this.getPredecessors();
boolean hasMoveException = entry().isMoveException();
MoveException move = null;
- DebugPosition position = null;
+ Position position = entry().getPosition();
if (hasMoveException) {
// Remove the move-exception instruction.
move = entry().asMoveException();
- position = move.getPosition();
assert move.getDebugValues().isEmpty();
getInstructions().remove(0);
}
@@ -1222,9 +1262,7 @@
values.add(value);
MoveException newMove = new MoveException(value);
newBlock.add(newMove);
- if (position != null) {
- newMove.setPosition(new DebugPosition(position.line, position.file));
- }
+ newMove.setPosition(position);
}
newBlock.add(new Goto());
newBlock.close(null);
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
index 7d61783..41db4d9 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
@@ -17,6 +17,7 @@
protected final BasicBlock block;
protected final ListIterator<Instruction> listIterator;
protected Instruction current;
+ protected Position position = null;
protected BasicBlockInstructionIterator(BasicBlock block) {
this.block = block;
@@ -65,6 +66,11 @@
return listIterator.previousIndex();
}
+ @Override
+ public void setInsertionPosition(Position position) {
+ this.position = position;
+ }
+
/**
* Adds an instruction to the block. The instruction will be added just before the current
* cursor position.
@@ -77,6 +83,9 @@
public void add(Instruction instruction) {
instruction.setBlock(block);
assert instruction.getBlock() == block;
+ if (position != null) {
+ instruction.setPosition(position);
+ }
listIterator.add(instruction);
}
@@ -162,6 +171,7 @@
}
current.moveDebugValues(newInstruction);
newInstruction.setBlock(block);
+ newInstruction.setPosition(current.getPosition());
listIterator.remove();
listIterator.add(newInstruction);
current.clearBlock();
@@ -332,6 +342,11 @@
assert invokeBlock.getInstructions().size() == 2;
assert invokeBlock.getInstructions().getFirst().isInvoke();
+ // Invalidate position-on-throwing-instructions property if it does not hold for the inlinee.
+ if (!inlinee.doAllThrowingInstructionsHavePositions()) {
+ code.setAllThrowingInstructionsHavePositions(false);
+ }
+
// Split the invoke instruction into a separate block.
Invoke invoke = invokeBlock.getInstructions().getFirst().asInvoke();
BasicBlock invokePredecessor = invokeBlock.getPredecessors().get(0);
@@ -349,6 +364,7 @@
Value receiverValue = arguments.get(0);
Value value = code.createValue(MoveType.OBJECT);
castInstruction = new CheckCast(value, invokeValue, downcast);
+ castInstruction.setPosition(invoke.getPosition());
receiverValue.replaceUsers(value);
} else {
arguments.get(i).replaceUsers(invoke.inValues().get(i));
@@ -474,6 +490,8 @@
}
newReturn = new Return(value, value.outType());
}
+ // The newly constructed return will be eliminated as part of inlining so we set position none.
+ newReturn.setPosition(Position.none());
newExitBlock.add(newReturn);
for (BasicBlock exitBlock : normalExits) {
InstructionListIterator it = exitBlock.listIterator(exitBlock.getInstructions().size());
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
index 0c0797f..b6c52df 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -58,7 +58,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asCheckCast().type == type;
}
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 aeb0a51..09c1dd6 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
@@ -115,7 +115,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asCmp().bias == bias;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
index efde56a..c43ce44 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
@@ -50,7 +50,7 @@
return true;
}
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asConstClass().clazz == clazz;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
index ef5a025..2397027 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -95,6 +95,7 @@
@Override
public void buildDex(DexBuilder builder) {
if (!dest().needsRegister()) {
+ forceSetPosition(Position.none());
builder.addNop(this);
return;
}
@@ -169,7 +170,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
if (preciseTypeUnknown()) {
return false;
}
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 124ff54..9d869aa 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
@@ -33,7 +33,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asConstString().value == value;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
index f0eb4a5..1b7aaf6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
@@ -32,7 +32,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
index d3d5553..6833568 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
@@ -38,7 +38,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
assert other.isDebugLocalWrite();
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java
index f579fd6..a973ed7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringUtils;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap.Entry;
public class DebugLocalsChange extends Instruction {
@@ -24,6 +25,12 @@
assert !ending.isEmpty() || !starting.isEmpty();
this.ending = ending;
this.starting = starting;
+ super.setPosition(Position.none());
+ }
+
+ @Override
+ public void setPosition(Position position) {
+ throw new Unreachable();
}
public Int2ReferenceMap<DebugLocalInfo> getEnding() {
@@ -50,7 +57,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
assert other.isDebugLocalsChange();
DebugLocalsChange o = (DebugLocalsChange) other;
return DebugLocalInfo.localsInfoMapsEqual(ending, o.ending)
@@ -92,4 +99,15 @@
public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
return Constraint.ALWAYS;
}
+
+ public void apply(Int2ReferenceMap<DebugLocalInfo> locals) {
+ for (Entry<DebugLocalInfo> end : getEnding().int2ReferenceEntrySet()) {
+ assert locals.get(end.getIntKey()) == end.getValue();
+ locals.remove(end.getIntKey());
+ }
+ for (Entry<DebugLocalInfo> start : getStarting().int2ReferenceEntrySet()) {
+ assert !locals.containsKey(start.getIntKey());
+ locals.put(start.getIntKey(), start.getValue());
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
index d6063f3..48cc8e6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
@@ -5,7 +5,6 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
-import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
@@ -13,13 +12,8 @@
public class DebugPosition extends Instruction {
- public final int line;
- public final DexString file;
-
- public DebugPosition(int line, DexString file) {
+ public DebugPosition() {
super(null);
- this.line = line;
- this.file = file;
}
@Override
@@ -38,9 +32,9 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
assert other.isDebugPosition();
- return false;
+ return true;
}
@Override
@@ -50,15 +44,6 @@
}
@Override
- public boolean equals(Object other) {
- if (other instanceof DebugPosition) {
- DebugPosition o = (DebugPosition) other;
- return line == o.line && file == o.file;
- }
- return false;
- }
-
- @Override
public int maxInValueRegister() {
throw new Unreachable();
}
@@ -69,26 +54,12 @@
}
@Override
- public boolean canBeDeadCode(IRCode code, InternalOptions options) {
- return false;
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder(super.toString());
- printLineInfo(builder);
- return builder.toString();
- }
-
- public void printLineInfo(StringBuilder builder) {
- if (file != null) {
- builder.append(file).append(":");
- }
- builder.append(line);
- }
-
- @Override
public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
return Constraint.ALWAYS;
}
+
+ @Override
+ public boolean canBeDeadCode(IRCode code, InternalOptions options) {
+ return false;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Div.java b/src/main/java/com/android/tools/r8/ir/code/Div.java
index 7dc5e75..69541c1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Div.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Div.java
@@ -77,7 +77,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asDiv().type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Goto.java b/src/main/java/com/android/tools/r8/ir/code/Goto.java
index 5ca9199..f88020d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Goto.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Goto.java
@@ -11,13 +11,20 @@
public Goto() {
super(null);
+ super.setPosition(Position.none());
}
public Goto(BasicBlock block) {
- super(null);
+ this();
setBlock(block);
}
+ @Override
+ public void setPosition(Position position) {
+ // In general goto's do not signify program points only transitions, so we avoid
+ // associating them with positional information.
+ }
+
public BasicBlock getTarget() {
assert getBlock().exit() == this;
List<BasicBlock> successors = getBlock().getSuccessors();
@@ -66,7 +73,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asGoto().getTarget() == getTarget();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index d9fdaf2..8346c7a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -31,13 +31,22 @@
private boolean numbered = false;
private int nextInstructionNumber = 0;
+ // Initial value indicating if the code does have actual positions on all throwing instructions.
+ // If this is the case, which holds for javac code, then we want to ensure that it remains so.
+ private boolean allThrowingInstructionsHavePositions;
+
+ public final boolean hasDebugPositions;
+
public IRCode(
DexEncodedMethod method,
LinkedList<BasicBlock> blocks,
- ValueNumberGenerator valueNumberGenerator) {
+ ValueNumberGenerator valueNumberGenerator,
+ boolean hasDebugPositions) {
this.method = method;
this.blocks = blocks;
this.valueNumberGenerator = valueNumberGenerator;
+ this.hasDebugPositions = hasDebugPositions;
+ allThrowingInstructionsHavePositions = computeAllThrowingInstructionsHavePositions();
}
/**
@@ -167,6 +176,7 @@
assert consistentPredecessorSuccessors();
assert consistentCatchHandlers();
assert consistentBlockInstructions();
+ assert !allThrowingInstructionsHavePositions || computeAllThrowingInstructionsHavePositions();
return true;
}
@@ -308,6 +318,7 @@
private boolean consistentBlockInstructions() {
for (BasicBlock block : blocks) {
for (Instruction instruction : block.getInstructions()) {
+ assert instruction.getPosition() != null;
assert instruction.getBlock() == block;
}
}
@@ -431,4 +442,23 @@
Value newValue = createValue(from.outType());
return new ConstNumber(ConstType.fromMoveType(from.outType()), newValue, 0);
}
+
+ public boolean doAllThrowingInstructionsHavePositions() {
+ return allThrowingInstructionsHavePositions;
+ }
+
+ public void setAllThrowingInstructionsHavePositions(boolean value) {
+ this.allThrowingInstructionsHavePositions = value;
+ }
+
+ private boolean computeAllThrowingInstructionsHavePositions() {
+ InstructionIterator it = instructionIterator();
+ while (it.hasNext()) {
+ Instruction instruction = it.next();
+ if (instruction.instructionTypeCanThrow() && instruction.getPosition().isNone()) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/If.java b/src/main/java/com/android/tools/r8/ir/code/If.java
index 3a068f6..d2e16f6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/If.java
+++ b/src/main/java/com/android/tools/r8/ir/code/If.java
@@ -144,7 +144,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
If o = other.asIf();
return o.getTrueTarget() == getTrueTarget()
&& o.fallthroughBlock() == fallthroughBlock()
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index 1d9e853..b286544 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -83,7 +83,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
InstanceGet o = other.asInstanceGet();
return o.field == field && o.type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
index afa18e5..5662a9a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
@@ -54,7 +54,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asInstanceOf().type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index 2642de1..0866205 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -73,7 +73,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
InstancePut o = other.asInstancePut();
return o.field == field && o.type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 24c3925..72d348c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -27,6 +27,7 @@
private BasicBlock block = null;
private int number = -1;
private Set<Value> debugValues = null;
+ private Position position = null;
protected Instruction(Value outValue) {
setOutValue(outValue);
@@ -46,6 +47,20 @@
setOutValue(outValue);
}
+ public final Position getPosition() {
+ assert position != null;
+ return position;
+ }
+
+ public void setPosition(Position position) {
+ assert this.position == null;
+ this.position = position;
+ }
+
+ public final void forceSetPosition(Position position) {
+ this.position = position;
+ }
+
public List<Value> inValues() {
return inValues;
}
@@ -244,11 +259,16 @@
}
/**
- * Compare equality of two class-equivalent instructions modulo their values.
+ * Compare equality of two class-equivalent instructions modulo their values and positions.
*
* <p>It is a precondition to this method that this.getClass() == other.getClass().
*/
- public abstract boolean identicalNonValueParts(Instruction other);
+ public abstract boolean identicalNonValueNonPositionParts(Instruction other);
+
+ public boolean identicalNonValueParts(Instruction other) {
+ assert getClass() == other.getClass();
+ return position.equals(other.position) && identicalNonValueNonPositionParts(other);
+ }
public abstract int compareNonValueParts(Instruction other);
@@ -264,7 +284,7 @@
} else {
ConstNumber aNum = a.getConstInstruction().asConstNumber();
ConstNumber bNum = b.getConstInstruction().asConstNumber();
- if (!aNum.identicalNonValueParts(bNum)) {
+ if (!aNum.identicalNonValueNonPositionParts(bNum)) {
return false;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
index 92491c3..56a3e1f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -59,6 +59,10 @@
return null;
}
+ default void setInsertionPosition(Position position) {
+ // Intentionally empty.
+ }
+
/**
* Safe removal function that will insert a DebugLocalRead to take over the debug values if any
* are associated with the current instruction.
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
index f597b2c..e0e816b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
@@ -72,7 +72,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.isInvokeCustom() && callSite == other.asInvokeCustom().callSite;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index cc23ef5..a56f840 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -70,11 +70,11 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
if (!other.isInvokeDirect()) {
return false;
}
- return super.identicalNonValueParts(other);
+ return super.identicalNonValueNonPositionParts(other);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
index a639ba1..d9be255 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -57,11 +57,11 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
if (!other.isInvokeInterface()) {
return false;
}
- return super.identicalNonValueParts(other);
+ return super.identicalNonValueNonPositionParts(other);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index 70201f0..cd6497d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -28,7 +28,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return method == other.asInvokeMethod().getInvokedMethod();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
index 0049baa..5251e20 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
@@ -70,7 +70,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
if (!other.isInvokeNewArray()) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
index 5eacaa9..126bfd2 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
@@ -70,11 +70,12 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
if (!other.isInvokePolymorphic()) {
return false;
}
- return proto.equals(((InvokePolymorphic) other).proto) && super.identicalNonValueParts(other);
+ return proto.equals(((InvokePolymorphic) other).proto)
+ && super.identicalNonValueNonPositionParts(other);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index 6654c16..4d9c67a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -59,11 +59,11 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
if (!other.isInvokeStatic()) {
return false;
}
- return super.identicalNonValueParts(other);
+ return super.identicalNonValueNonPositionParts(other);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
index b3d5fe4..428be6f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
@@ -58,11 +58,11 @@
addInvokeAndMoveResult(instruction, builder);
}
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
if (!other.isInvokeSuper()) {
return false;
}
- return super.identicalNonValueParts(other);
+ return super.identicalNonValueNonPositionParts(other);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index 34793c2..c5cfd50 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -57,11 +57,11 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
if (!other.isInvokeVirtual()) {
return false;
}
- return super.identicalNonValueParts(other);
+ return super.identicalNonValueNonPositionParts(other);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Monitor.java b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
index 3c0c062..98a83c7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Monitor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
@@ -50,7 +50,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asMonitor().type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Move.java b/src/main/java/com/android/tools/r8/ir/code/Move.java
index 277c63c..08d8bf2 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Move.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Move.java
@@ -43,7 +43,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
assert other.isMove();
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/MoveException.java b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
index 61ed928..0f72182 100644
--- a/src/main/java/com/android/tools/r8/ir/code/MoveException.java
+++ b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
@@ -12,8 +12,6 @@
public class MoveException extends Instruction {
- private DebugPosition position = null;
-
public MoveException(Value dest) {
super(dest);
}
@@ -22,15 +20,6 @@
return outValue;
}
- public DebugPosition getPosition() {
- return position;
- }
-
- public void setPosition(DebugPosition position) {
- assert this.position == null;
- this.position = position;
- }
-
@Override
public void buildDex(DexBuilder builder) {
int dest = builder.allocatedRegister(dest(), getNumber());
@@ -49,7 +38,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
assert other.isMoveException();
return true;
}
@@ -76,18 +65,6 @@
}
@Override
- public String toString() {
- if (position != null) {
- StringBuilder builder = new StringBuilder(super.toString());
- builder.append("(DebugPosition ");
- position.printLineInfo(builder);
- builder.append(')');
- return builder.toString();
- }
- return super.toString();
- }
-
- @Override
public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
// TODO(64432527): Revisit this constraint.
return Constraint.NEVER;
diff --git a/src/main/java/com/android/tools/r8/ir/code/Mul.java b/src/main/java/com/android/tools/r8/ir/code/Mul.java
index 67aca89..42c7f09 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Mul.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Mul.java
@@ -79,7 +79,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asMul().type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Neg.java b/src/main/java/com/android/tools/r8/ir/code/Neg.java
index f858d70..dc0be38 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Neg.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Neg.java
@@ -50,7 +50,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asNeg().type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
index 17a08da..1e77217 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
@@ -52,7 +52,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asNewArrayEmpty().type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
index aed765e..4b01c4c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
@@ -42,7 +42,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
NewArrayFilledData o = other.asNewArrayFilledData();
return o.element_width == element_width
&& o.size == size
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index 8d1671d..434b415 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -36,7 +36,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asNewInstance().clazz == clazz;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Not.java b/src/main/java/com/android/tools/r8/ir/code/Not.java
index 4c42b80..98e2207 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Not.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Not.java
@@ -56,7 +56,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asNot().type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
index 23cbb69..67cb816 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
@@ -114,7 +114,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
NumberConversion o = other.asNumberConversion();
return o.from == from && o.to == to;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Or.java b/src/main/java/com/android/tools/r8/ir/code/Or.java
index bfa69f4..24b8995 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Or.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Or.java
@@ -68,7 +68,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asOr().type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Position.java b/src/main/java/com/android/tools/r8/ir/code/Position.java
new file mode 100644
index 0000000..a8312fb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Position.java
@@ -0,0 +1,67 @@
+// 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.ir.code;
+
+import com.android.tools.r8.graph.DexString;
+
+public class Position {
+
+ private static final Position NO_POSITION = new Position(-1, null, false);
+
+ public final int line;
+ public final DexString file;
+ public final boolean synthetic;
+
+ public Position(int line, DexString file) {
+ this(line, file, false);
+ assert line >= 0;
+ }
+
+ private Position(int line, DexString file, boolean synthetic) {
+ this.line = line;
+ this.file = file;
+ this.synthetic = synthetic;
+ }
+
+ public static Position synthetic(int line) {
+ return new Position(line, null, true);
+ }
+
+ public static Position none() {
+ return NO_POSITION;
+ }
+
+ public boolean isNone() {
+ return this == NO_POSITION;
+ }
+
+ public boolean isSome() {
+ return this != NO_POSITION;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof Position) {
+ Position o = (Position) other;
+ return !isNone() && line == o.line && file == o.file;
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ if (isNone()) {
+ return "--";
+ }
+ StringBuilder builder = new StringBuilder();
+ if (file != null) {
+ builder.append(file).append(":");
+ }
+ builder.append(line);
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Rem.java b/src/main/java/com/android/tools/r8/ir/code/Rem.java
index a257109..7232012 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Rem.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Rem.java
@@ -77,7 +77,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asRem().type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Return.java b/src/main/java/com/android/tools/r8/ir/code/Return.java
index ba2fcb0..e9311e3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Return.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Return.java
@@ -71,7 +71,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
if (isReturnVoid()) {
return other.asReturn().isReturnVoid();
} else {
diff --git a/src/main/java/com/android/tools/r8/ir/code/Shl.java b/src/main/java/com/android/tools/r8/ir/code/Shl.java
index 8236891..09c7d61 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Shl.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Shl.java
@@ -68,7 +68,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asShl().type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Shr.java b/src/main/java/com/android/tools/r8/ir/code/Shr.java
index e4bd97c..aef09b3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Shr.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Shr.java
@@ -68,7 +68,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asShr().type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index bb79d5e..5075e7a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -77,7 +77,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
StaticGet o = other.asStaticGet();
return o.field == field && o.type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index 6736d7e..8a55361 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -79,7 +79,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
StaticPut o = other.asStaticPut();
return o.field == field && o.type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Sub.java b/src/main/java/com/android/tools/r8/ir/code/Sub.java
index 8470070..7bb1107 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Sub.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Sub.java
@@ -80,7 +80,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asSub().type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Switch.java b/src/main/java/com/android/tools/r8/ir/code/Switch.java
index 08ab002..e962d90 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Switch.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Switch.java
@@ -140,7 +140,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
assert other.isSwitch();
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Throw.java b/src/main/java/com/android/tools/r8/ir/code/Throw.java
index 2f9f9f7..def92eb 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Throw.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Throw.java
@@ -41,7 +41,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
assert other.isThrow();
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Ushr.java b/src/main/java/com/android/tools/r8/ir/code/Ushr.java
index d90ce4a..c0500b1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Ushr.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Ushr.java
@@ -68,7 +68,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asUshr().type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Xor.java b/src/main/java/com/android/tools/r8/ir/code/Xor.java
index dfe47bc..779f307 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Xor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Xor.java
@@ -68,7 +68,7 @@
}
@Override
- public boolean identicalNonValueParts(Instruction other) {
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asXor().type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
index 0d96df7..7a8895d 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
@@ -33,12 +33,12 @@
import com.android.tools.r8.code.Nop;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexCode.Try;
import com.android.tools.r8.graph.DexCode.TryHandler;
import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
import com.android.tools.r8.graph.DexDebugEventBuilder;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
@@ -52,17 +52,21 @@
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.Move;
import com.android.tools.r8.ir.code.NewArrayFilledData;
+import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Return;
import com.android.tools.r8.ir.code.Switch;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
@@ -81,6 +85,8 @@
private final DexItemFactory dexItemFactory;
+ private final InternalOptions options;
+
// List of information about switch payloads that have to be created at the end of the
// dex code.
private final List<SwitchPayloadInfo> switchPayloadInfos = new ArrayList<>();
@@ -109,13 +115,18 @@
BasicBlock nextBlock;
- public DexBuilder(IRCode ir, RegisterAllocator registerAllocator, DexItemFactory dexItemFactory) {
+ public DexBuilder(
+ IRCode ir,
+ RegisterAllocator registerAllocator,
+ DexItemFactory dexItemFactory,
+ InternalOptions options) {
assert ir != null;
assert registerAllocator != null;
assert dexItemFactory != null;
this.ir = ir;
this.registerAllocator = registerAllocator;
this.dexItemFactory = dexItemFactory;
+ this.options = options;
}
private void reset() {
@@ -149,13 +160,13 @@
// Reset the state of the builder to start from scratch.
reset();
- // Populate the builder info objects.
- numberOfInstructions = 0;
-
// Remove redundant debug position instructions. They would otherwise materialize as
// unnecessary nops.
removeRedundantDebugPositions();
+ // Populate the builder info objects.
+ numberOfInstructions = 0;
+
ListIterator<BasicBlock> iterator = ir.listIterator();
assert iterator.hasNext();
BasicBlock block = iterator.next();
@@ -177,22 +188,16 @@
} while (!ifsNeedingRewrite.isEmpty());
// Build instructions.
- DexDebugEventBuilder debugEventBuilder = new DexDebugEventBuilder(ir.method, dexItemFactory);
+ DexDebugEventBuilder debugEventBuilder = new DexDebugEventBuilder(ir, options);
List<Instruction> dexInstructions = new ArrayList<>(numberOfInstructions);
int instructionOffset = 0;
InstructionIterator instructionIterator = ir.instructionIterator();
- DexEncodedMethod.DebugPositionRangeList.Builder debugPositionListBuilder =
- new DexEncodedMethod.DebugPositionRangeList.Builder();
while (instructionIterator.hasNext()) {
com.android.tools.r8.ir.code.Instruction ir = instructionIterator.next();
- if (ir.isDebugPosition()) {
- int line = ir.asDebugPosition().line;
- debugPositionListBuilder.add(line, line);
- }
Info info = getInfo(ir);
int previousInstructionCount = dexInstructions.size();
info.addInstructions(this, dexInstructions);
- debugEventBuilder.add(instructionOffset, ir);
+ int instructionStartOffset = instructionOffset;
if (previousInstructionCount < dexInstructions.size()) {
while (previousInstructionCount < dexInstructions.size()) {
Instruction instruction = dexInstructions.get(previousInstructionCount++);
@@ -200,9 +205,10 @@
instructionOffset += instruction.getSize();
}
}
+ debugEventBuilder.add(instructionStartOffset, instructionOffset, ir);
}
- ir.method.debugPositionRangeList = debugPositionListBuilder.build();
+ ir.method.debugPositionRangeList = debugEventBuilder.buildPositionRanges();
// Compute switch payloads.
for (SwitchPayloadInfo switchPayloadInfo : switchPayloadInfos) {
@@ -251,21 +257,114 @@
return code;
}
+ private static boolean verifyNopHasNoPosition(
+ com.android.tools.r8.ir.code.Instruction instruction, ListIterator<BasicBlock> blocks) {
+ BasicBlock nextBlock = null;
+ if (blocks.hasNext()) {
+ nextBlock = blocks.next();
+ blocks.previous();
+ }
+ return verifyNopHasNoPosition(instruction, nextBlock);
+ }
+
+ private static boolean verifyNopHasNoPosition(
+ com.android.tools.r8.ir.code.Instruction instruction, BasicBlock nextBlock) {
+ if (isNopInstruction(instruction, nextBlock)) {
+ assert instruction.getPosition().isNone();
+ }
+ return true;
+ }
+
+ // Eliminates unneeded debug positions.
+ //
+ // After this pass all instructions that don't materialize to an actual DEX instruction will have
+ // Position.none(). If any other instruction has a non-none position then all other instructions
+ // that do materialize to a DEX instruction (eg, non-fallthrough gotos) will have a non-none
+ // position.
+ //
+ // Remaining debug positions indicate two successive lines without intermediate instructions.
+ // For these we must emit a nop instruction to ensure they don't share the same pc.
private void removeRedundantDebugPositions() {
- ListIterator<BasicBlock> iterator = ir.listIterator();
- DebugPosition lastMoveExceptionPosition = null;
- while (iterator.hasNext()) {
- BasicBlock next = iterator.next();
- InstructionListIterator it = next.listIterator();
- while (it.hasNext()) {
- com.android.tools.r8.ir.code.Instruction instruction = it.next();
- if (instruction.isDebugPosition()) {
- if (instruction.asDebugPosition().equals(lastMoveExceptionPosition)) {
- it.remove();
+ if (!ir.hasDebugPositions) {
+ return;
+ }
+ Int2ReferenceMap[] localsMap = new Int2ReferenceMap[instructionToInfo.length];
+ // Scan forwards removing debug positions equal to the previous instruction position.
+ {
+ Int2ReferenceMap<DebugLocalInfo> locals = Int2ReferenceMaps.emptyMap();
+ Position previous = Position.none();
+ ListIterator<BasicBlock> blockIterator = ir.listIterator();
+ BasicBlock previousBlock = null;
+ while (blockIterator.hasNext()) {
+ BasicBlock block = blockIterator.next();
+ if (previousBlock != null
+ && previousBlock.exit().isGoto()
+ && !isNopInstruction(previousBlock.exit(), block)) {
+ assert previousBlock.exit().getPosition().isNone()
+ || previousBlock.exit().getPosition().equals(previous);
+ previousBlock.exit().forceSetPosition(previous);
+ }
+ InstructionListIterator instructionIterator = block.listIterator();
+ if (block.getLocalsAtEntry() != null && !locals.equals(block.getLocalsAtEntry())) {
+ locals = new Int2ReferenceOpenHashMap<>(block.getLocalsAtEntry());
+ }
+ while (instructionIterator.hasNext()) {
+ com.android.tools.r8.ir.code.Instruction instruction = instructionIterator.next();
+ if (instruction.isDebugPosition() && previous.equals(instruction.getPosition())) {
+ instructionIterator.remove();
+ } else if (instruction.isConstNumber() && !instruction.outValue().needsRegister()) {
+ instruction.forceSetPosition(Position.none());
+ } else if (instruction.getPosition().isSome()) {
+ assert verifyNopHasNoPosition(instruction, blockIterator);
+ previous = instruction.getPosition();
}
- lastMoveExceptionPosition = null;
- } else if (instruction.isMoveException()) {
- lastMoveExceptionPosition = instruction.asMoveException().getPosition();
+ if (instruction.isDebugLocalsChange()) {
+ locals = new Int2ReferenceOpenHashMap<>(locals);
+ instruction.asDebugLocalsChange().apply(locals);
+ }
+ localsMap[instructionNumberToIndex(instruction.getNumber())] = locals;
+ }
+ previousBlock = block;
+ }
+ if (previousBlock != null && previousBlock.exit().isGoto()) {
+ // If the last block ends in a goto it cannot be a fallthrough/nop.
+ assert previousBlock.exit().getPosition().isNone();
+ previousBlock.exit().forceSetPosition(previous);
+ }
+ }
+ // Scan backwards removing debug positions equal to the following instruction position.
+ {
+ ListIterator<BasicBlock> blocks = ir.blocks.listIterator(ir.blocks.size());
+ BasicBlock block = null;
+ BasicBlock nextBlock;
+ com.android.tools.r8.ir.code.Instruction next = null;
+ Int2ReferenceMap nextLocals = null;
+ while (blocks.hasPrevious()) {
+ nextBlock = block;
+ block = blocks.previous();
+ InstructionListIterator instructions = block.listIterator(block.getInstructions().size());
+ while (instructions.hasPrevious()) {
+ com.android.tools.r8.ir.code.Instruction instruction = instructions.previous();
+ int index = instructionNumberToIndex(instruction.getNumber());
+ if (instruction.isDebugPosition() && localsMap[index].equals(nextLocals)) {
+ Position nextPosition = next.getPosition();
+ Position thisPosition = instruction.getPosition();
+ if (nextPosition.isNone()) {
+ next.forceSetPosition(thisPosition);
+ instructions.remove();
+ } else if (nextPosition.equals(thisPosition)) {
+ instructions.remove();
+ } else {
+ next = instruction;
+ }
+ } else {
+ assert verifyNopHasNoPosition(instruction, nextBlock);
+ if (!isNopInstruction(instruction, nextBlock)) {
+ next = instruction;
+ nextLocals = localsMap[index];
+ assert nextLocals != null;
+ }
+ }
}
}
}
@@ -349,39 +448,22 @@
}
public void addNop(com.android.tools.r8.ir.code.Instruction instruction) {
+ assert instruction.getPosition().isNone();
add(instruction, new FallThroughInfo(instruction));
}
- private static boolean isNopInstruction(com.android.tools.r8.ir.code.Instruction instruction) {
- return instruction.isDebugLocalsChange()
- || (instruction.isConstNumber() && !instruction.outValue().needsRegister());
+ private static boolean isNopInstruction(
+ com.android.tools.r8.ir.code.Instruction instruction, BasicBlock nextBlock) {
+ return instruction.isArgument()
+ || instruction.isDebugLocalsChange()
+ || (instruction.isConstNumber() && !instruction.outValue().needsRegister())
+ || instruction.isGoto() && instruction.asGoto().getTarget() == nextBlock;
}
public void addDebugPosition(DebugPosition position) {
- BasicBlock block = position.getBlock();
- int blockIndex = ir.blocks.indexOf(block);
- Iterator<com.android.tools.r8.ir.code.Instruction> iterator = block.listIterator(position);
-
- com.android.tools.r8.ir.code.Instruction next = null;
- while (next == null) {
- next = iterator.next();
- while (isNopInstruction(next)) {
- next = iterator.next();
- }
- if (next.isGoto()) {
- ++blockIndex;
- BasicBlock nextBlock = blockIndex < ir.blocks.size() ? ir.blocks.get(blockIndex) : null;
- if (next.asGoto().getTarget() == nextBlock) {
- iterator = nextBlock.iterator();
- next = null;
- }
- }
- }
- if (next.isDebugPosition() && !position.equals(next.asDebugPosition())) {
- add(position, new FixedSizeInfo(position, new Nop()));
- } else {
- addNop(position);
- }
+ // Remaining debug positions always require we emit an actual nop instruction.
+ // See removeRedundantDebugPositions.
+ add(position, new FixedSizeInfo(position, new Nop()));
}
public void add(com.android.tools.r8.ir.code.Instruction ir, Instruction dex) {
@@ -411,30 +493,13 @@
}
public void addReturn(Return ret, Instruction dex) {
- if (nextBlock != null) {
- Return followingRet = nextBlock.exit().asReturn();
- if (nextBlock.getInstructions().size() == 1
- && followingRet != null
- && ret.getReturnType() == followingRet.getReturnType()) {
- if (ret.isReturnVoid() && followingRet.isReturnVoid()) {
- addNop(ret);
- return;
- }
- if (!ret.isReturnVoid()
- && !followingRet.isReturnVoid()
- && ret.returnValue().outType() == followingRet.returnValue().outType()) {
- int thisRegister = registerAllocator.getRegisterForValue(
- ret.returnValue(), ret.getNumber());
- int otherRegister = registerAllocator.getRegisterForValue(
- followingRet.returnValue(), followingRet.getNumber());
- if (thisRegister == otherRegister) {
- addNop(ret);
- return;
- }
- }
- }
+ if (nextBlock != null
+ && ret.identicalAfterRegisterAllocation(nextBlock.entry(), registerAllocator)) {
+ ret.forceSetPosition(Position.none());
+ addNop(ret);
+ } else {
+ add(ret, dex);
}
- add(ret, dex);
}
private void add(com.android.tools.r8.ir.code.Instruction ir, Info info) {
@@ -889,7 +954,7 @@
} else {
size = 3;
}
- if (targetInfo.getIR().isReturn() && canTargetReturn(targetInfo.getIR().asReturn())) {
+ if (targetInfo.getIR().isReturn() && targetInfo.getIR().getPosition().isNone()) {
// Set the size to the min of the size of the return and the size of the goto. When
// adding instructions, we use the return if the computed size matches the size of the
// return.
@@ -909,7 +974,7 @@
// Emit a return if the target is a return and the size of the return is the computed
// size of this instruction.
Return ret = targetInfo.getIR().asReturn();
- if (ret != null && size == targetInfo.getSize() && canTargetReturn(ret)) {
+ if (ret != null && size == targetInfo.getSize() && ret.getPosition().isNone()) {
Instruction dex = ret.createDexInstruction(builder);
dex.setOffset(getOffset()); // for better printing of the dex code.
instructions.add(dex);
@@ -950,18 +1015,6 @@
instructions.add(dex);
}
}
-
- private static boolean canTargetReturn(Return ret) {
- InstructionListIterator it = ret.getBlock().listIterator(ret);
- com.android.tools.r8.ir.code.Instruction prev = it.previous();
- while (it.hasPrevious()) {
- prev = it.previous();
- if (!DexBuilder.isNopInstruction(prev)) {
- break;
- }
- }
- return !prev.isDebugPosition();
- }
}
public static class IfInfo extends Info {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
index 543f707..a71f326 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -41,8 +41,8 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.CatchHandlers;
-import com.android.tools.r8.ir.code.DebugPosition;
import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.Position;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -67,6 +67,9 @@
private CatchHandlers<Integer> currentCatchHandlers = null;
private Instruction currentDexInstruction = null;
+ private Position currentPosition = null;
+ private Map<Position, Position> canonicalPositions = null;
+
private final List<MoveType> argumentTypes;
private List<DexDebugEntry> debugEntries = null;
@@ -79,6 +82,7 @@
DexDebugInfo info = code.getDebugInfo();
if (info != null) {
debugEntries = info.computeEntries();
+ canonicalPositions = new HashMap<>(debugEntries.size());
}
}
@@ -116,6 +120,7 @@
@Override
public void buildPrelude(IRBuilder builder) {
+ currentPosition = Position.none();
if (code.incomingRegisterSize == 0) {
return;
}
@@ -146,7 +151,7 @@
@Override
public void buildInstruction(IRBuilder builder, int instructionIndex) throws ApiLevelException {
updateCurrentCatchHandlers(instructionIndex);
- emitDebugPosition(instructionIndex, builder);
+ updateDebugPosition(instructionIndex, builder);
currentDexInstruction = code.instructions[instructionIndex];
currentDexInstruction.buildIR(builder);
}
@@ -163,11 +168,16 @@
}
@Override
- public DebugPosition getDebugPositionAtOffset(int offset) {
+ public Position getDebugPositionAtOffset(int offset) {
throw new Unreachable();
}
@Override
+ public Position getCurrentPosition() {
+ return currentPosition;
+ }
+
+ @Override
public boolean verifyCurrentInstructionCanThrow() {
return currentDexInstruction.canThrow();
}
@@ -192,22 +202,32 @@
}
}
- private void emitDebugPosition(int instructionIndex, IRBuilder builder) {
+ private void updateDebugPosition(int instructionIndex, IRBuilder builder) {
if (debugEntries == null || debugEntries.isEmpty()) {
return;
}
+ DexDebugEntry current = null;
int offset = instructionOffset(instructionIndex);
for (DexDebugEntry entry : debugEntries) {
- if (entry.address == offset) {
- builder.addDebugPosition(entry.line, entry.sourceFile);
- return;
- }
if (entry.address > offset) {
- return;
+ break;
+ }
+ current = entry;
+ }
+ if (current == null) {
+ currentPosition = Position.none();
+ } else {
+ currentPosition = getCanonicalPosition(current);
+ if (current.address == offset) {
+ builder.addDebugPosition(currentPosition);
}
}
}
+ private Position getCanonicalPosition(DexDebugEntry entry) {
+ return canonicalPositions.computeIfAbsent(new Position(entry.line, entry.sourceFile), p -> p);
+ }
+
@Override
public void clear() {
switchPayloadResolver.clear();
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 c84adcb..e9f94e3 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
@@ -66,6 +66,7 @@
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.Or;
import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Rem;
import com.android.tools.r8.ir.code.Return;
import com.android.tools.r8.ir.code.Shl;
@@ -375,6 +376,9 @@
// but before handle-exit (which does not maintain predecessor counts).
assert verifyFilledPredecessors();
+ // Insert debug positions so all position changes are marked by an explicit instruction.
+ boolean hasDebugPositions = insertDebugPositions();
+
// Clear all reaching definitions to free up memory (and avoid invalid use).
for (BasicBlock block : blocks) {
block.clearCurrentDefinitions();
@@ -389,7 +393,7 @@
splitCriticalEdges();
// Package up the IR code.
- IRCode ir = new IRCode(method, blocks, valueNumberGenerator);
+ IRCode ir = new IRCode(method, blocks, valueNumberGenerator, hasDebugPositions);
if (options.testing.invertConditionals) {
invertConditionalsForTesting(ir);
@@ -408,6 +412,41 @@
return ir;
}
+ private boolean insertDebugPositions() {
+ boolean hasDebugPositions = false;
+ if (!options.debug) {
+ return hasDebugPositions;
+ }
+ for (BasicBlock block : blocks) {
+ InstructionListIterator it = block.listIterator();
+ Position current = null;
+ while (it.hasNext()) {
+ Instruction instruction = it.next();
+ Position position = instruction.getPosition();
+ if (instruction.isMoveException()) {
+ assert current == null;
+ current = position;
+ hasDebugPositions = hasDebugPositions || position.isSome();
+ } else if (instruction.isDebugPosition()) {
+ hasDebugPositions = true;
+ if (position.equals(current)) {
+ it.removeOrReplaceByDebugLocalRead();
+ } else {
+ current = position;
+ }
+ } else if (position.isSome() && !position.equals(current)) {
+ DebugPosition positionChange = new DebugPosition();
+ positionChange.setPosition(position);
+ it.previous();
+ it.add(positionChange);
+ it.next();
+ current = position;
+ }
+ }
+ }
+ return hasDebugPositions;
+ }
+
private void clearCanonicalizationMaps() {
intConstants = null;
longConstants = null;
@@ -484,10 +523,12 @@
assert moveExceptionDest >= 0;
int targetIndex = source.instructionIndex(moveExceptionItem.targetOffset);
Value out = writeRegister(moveExceptionDest, MoveType.OBJECT, ThrowingInfo.NO_THROW, null);
+ Position position = source.getDebugPositionAtOffset(moveExceptionItem.targetOffset);
MoveException moveException = new MoveException(out);
- moveException.setPosition(source.getDebugPositionAtOffset(moveExceptionItem.targetOffset));
+ moveException.setPosition(position);
currentBlock.add(moveException);
- currentBlock.add(new Goto());
+ Goto exit = new Goto();
+ currentBlock.add(exit);
BasicBlock targetBlock = getTarget(moveExceptionItem.targetOffset);
currentBlock.link(targetBlock);
addToWorklist(targetBlock, targetIndex);
@@ -544,11 +585,6 @@
addInstruction(new DebugLocalUninitialized(type, value));
}
- public void addDebugPosition(int line, DexString file) {
- // Always add positions as they influence release builds (ie, stack traces).
- addInstruction(new DebugPosition(line, file));
- }
-
private void addDebugLocalWrite(MoveType type, int dest, Value in) {
assert options.debug;
Value out = writeRegister(dest, type, ThrowingInfo.NO_THROW);
@@ -645,6 +681,13 @@
}
}
+ public void addDebugPosition(Position position) {
+ if (options.debug) {
+ assert source.getCurrentPosition().equals(position);
+ addInstruction(new DebugPosition());
+ }
+ }
+
public void addAdd(NumericType type, int dest, int left, int right) {
Value in1 = readNumericRegister(left, type);
Value in2 = readNumericRegister(right, type);
@@ -766,6 +809,7 @@
}
}
it.add(instruction);
+ instruction.setPosition(Position.none());
} else {
add(instruction);
}
@@ -1193,12 +1237,13 @@
assert !out.hasLocalInfo();
MoveException instruction = new MoveException(out);
assert !instruction.instructionTypeCanThrow();
- if (!currentBlock.getInstructions().isEmpty()
- && currentBlock.getInstructions().getLast().isDebugPosition()) {
- DebugPosition position = currentBlock.getInstructions().getLast().asDebugPosition();
- position.moveDebugValues(instruction);
- instruction.setPosition(position);
- currentBlock.removeInstruction(position);
+ if (currentBlock.getInstructions().size() == 1 && currentBlock.entry().isDebugPosition()) {
+ InstructionListIterator it = currentBlock.listIterator();
+ Instruction entry = it.next();
+ assert entry.getPosition().equals(source.getCurrentPosition());
+ attachLocalValues(instruction);
+ it.replaceCurrentInstruction(instruction);
+ return;
}
if (!currentBlock.getInstructions().isEmpty()) {
throw new CompilationError("Invalid MoveException instruction encountered. "
@@ -1265,14 +1310,18 @@
public void addReturn(MoveType type, int value) {
Value in = readRegister(value, type);
- source.buildPostlude(this);
- addInstruction(new Return(in, type));
- closeCurrentBlock();
+ addReturn(new Return(in, type));
}
public void addReturn() {
+ addReturn(new Return());
+ }
+
+ private void addReturn(Return ret) {
+ // Attach the live locals to the return instruction to avoid a local change on monitor exit.
+ attachLocalValues(ret);
source.buildPostlude(this);
- addInstruction(new Return());
+ addInstruction(ret);
closeCurrentBlock();
}
@@ -1675,6 +1724,11 @@
// Private instruction helpers.
private void addInstruction(Instruction ir) {
+ addInstruction(ir, source.getCurrentPosition());
+ }
+
+ private void addInstruction(Instruction ir, Position position) {
+ ir.setPosition(position);
attachLocalValues(ir);
currentBlock.add(ir);
if (ir.instructionTypeCanThrow()) {
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 184087a..e83774d 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
@@ -412,7 +412,7 @@
}
assert code.isConsistentSSA();
RegisterAllocator registerAllocator = performRegisterAllocation(code, method);
- method.setCode(code, registerAllocator, appInfo.dexItemFactory);
+ method.setCode(code, registerAllocator, appInfo.dexItemFactory, options);
if (Log.ENABLED) {
Log.debug(getClass(), "Resulting dex code for %s:\n%s",
method.toSourceString(), logCode(options, method));
@@ -577,13 +577,14 @@
// Insert code to log arguments if requested.
if (options.methodMatchesLogArgumentsFilter(method)) {
codeRewriter.logArgumentTypes(method, code);
+ assert code.isConsistentSSA();
}
printMethod(code, "Optimized IR (SSA)");
// Perform register allocation.
RegisterAllocator registerAllocator = performRegisterAllocation(code, method);
- method.setCode(code, registerAllocator, appInfo.dexItemFactory);
+ method.setCode(code, registerAllocator, appInfo.dexItemFactory, options);
updateHighestSortingStrings(method);
if (Log.ENABLED) {
Log.debug(getClass(), "Resulting dex code for %s:\n%s",
@@ -615,13 +616,6 @@
// Always perform dead code elimination before register allocation. The register allocator
// does not allow dead code (to make sure that we do not waste registers for unneeded values).
DeadCodeRemover.removeDeadCode(code, codeRewriter, options);
- if (!options.debug) {
- // Remove unneeded debug positions before register allocation to avoid to process useless
- // instructions. These is safe because the register allocator can not generate new
- // instructions that will throw exceptions and thus apply removedUnneededDebugPositions before
- // register allocator will produce the same result.
- CodeRewriter.removedUnneededDebugPositions(code);
- }
LinearScanRegisterAllocator registerAllocator = new LinearScanRegisterAllocator(code, options);
registerAllocator.allocateRegisters(options.debug);
printMethod(code, "After register allocation (non-SSA)");
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 a4203a4..2e14085 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
@@ -23,13 +23,13 @@
import com.android.tools.r8.ir.code.CatchHandlers;
import com.android.tools.r8.ir.code.Cmp.Bias;
import com.android.tools.r8.ir.code.ConstType;
-import com.android.tools.r8.ir.code.DebugPosition;
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.Monitor;
import com.android.tools.r8.ir.code.MoveType;
import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.conversion.IRBuilder.BlockInfo;
import com.android.tools.r8.ir.conversion.JarState.Local;
import com.android.tools.r8.ir.conversion.JarState.Slot;
@@ -45,6 +45,7 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -173,6 +174,15 @@
// State to signal that the code currently being emitted is part of synchronization prelude/exits.
private boolean generatingMethodSynchronization = false;
+ // Current position associated with the current instruction during building.
+ private Position currentPosition;
+
+ // Canonicalized positions to lower memory usage.
+ private Int2ReferenceMap<Position> canonicalPositions = new Int2ReferenceOpenHashMap<>();
+
+ // Cooked position to indicate positions in synthesized code (ie, for synchronization).
+ private Position syntheticPosition = null;
+
public JarSourceCode(DexType clazz, MethodNode node, JarApplicationReader application) {
assert node != null;
assert node.desc != null;
@@ -303,6 +313,8 @@
locals = state.openLocals(initialLabel);
}
+ currentPosition = Position.none();
+
// Build the actual argument instructions now that type and debug information is known
// for arguments.
buildArgumentInstructions(builder);
@@ -431,6 +443,7 @@
private void buildExceptionalPostlude(IRBuilder builder) {
assert isSynchronized();
generatingMethodSynchronization = true;
+ currentPosition = getSyntheticPosition();
buildMonitorExit(builder);
builder.addThrow(getMoveExceptionRegister());
generatingMethodSynchronization = false;
@@ -472,6 +485,12 @@
preInstructionState = state.toString();
}
+ // Don't include line changes when processing a label. Doing so will end up emitting local
+ // writes after the line has changed and thus causing locals to become visible too late.
+ currentPosition =
+ getDebugPositionAtOffset(
+ insn instanceof LabelNode ? instructionIndex - 1 : instructionIndex);
+
build(insn, builder);
if (Log.ENABLED && !(insn instanceof LineNumberNode)) {
@@ -2826,24 +2845,65 @@
}
private void build(LineNumberNode insn, IRBuilder builder) {
- builder.addDebugPosition(insn.line, null);
+ currentPosition = getCanonicalPosition(insn.line);
+ builder.addDebugPosition(currentPosition);
}
@Override
- public DebugPosition getDebugPositionAtOffset(int offset) {
+ public Position getDebugPositionAtOffset(int offset) {
+ if (offset == EXCEPTIONAL_SYNC_EXIT_OFFSET) {
+ return getSyntheticPosition();
+ }
int index = instructionIndex(offset);
if (index < 0 || instructionCount() <= index) {
- return null;
+ return Position.none();
}
AbstractInsnNode insn = node.instructions.get(index);
if (insn instanceof LabelNode) {
insn = insn.getNext();
}
- if (insn instanceof LineNumberNode) {
- LineNumberNode line = (LineNumberNode) insn;
- return new DebugPosition(line.line, null);
+ while (insn != null && !(insn instanceof LineNumberNode)) {
+ insn = insn.getPrevious();
}
- return null;
+ if (insn != null) {
+ LineNumberNode line = (LineNumberNode) insn;
+ return getCanonicalPosition(line.line);
+ }
+ return Position.none();
+ }
+
+ @Override
+ public Position getCurrentPosition() {
+ return currentPosition;
+ }
+
+ private Position getCanonicalPosition(int line) {
+ return canonicalPositions.computeIfAbsent(line, l -> new Position(l, null));
+ }
+
+ // If we need to emit a synthetic position for exceptional monitor exits, we try to cook up a
+ // position that is not actually a valid program position, so as not to incorrectly position the
+ // user on an exit that is not the actual exit being taken. Our heuristic for this is that if the
+ // method has at least two positions we use the first position minus one as the synthetic exit.
+ // If the method only has one position it is safe to just use that position.
+ private Position getSyntheticPosition() {
+ if (syntheticPosition == null) {
+ int min = Integer.MAX_VALUE;
+ int max = Integer.MIN_VALUE;
+ for (Iterator it = node.instructions.iterator(); it.hasNext(); ) {
+ Object insn = it.next();
+ if (insn instanceof LineNumberNode) {
+ LineNumberNode lineNode = (LineNumberNode) insn;
+ min = Math.min(min, lineNode.line);
+ max = Math.max(max, lineNode.line);
+ }
+ }
+ syntheticPosition =
+ (min == Integer.MAX_VALUE)
+ ? Position.none()
+ : Position.synthetic(min < max ? min - 1 : min);
+ }
+ return syntheticPosition;
}
// Printing helpers.
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index 71de2b6..ab3efdb 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -111,6 +111,7 @@
newValue,
newInvoke.outValue(),
graphLense.lookupType(invokedMethod.proto.returnType, method));
+ cast.setPosition(current.getPosition());
iterator.add(cast);
// If the current block has catch handlers split the check cast into its own block.
if (newInvoke.getBlock().hasCatchHandlers()) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
index 3ed90fb..13d1551 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
@@ -7,7 +7,7 @@
import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.ir.code.CatchHandlers;
-import com.android.tools.r8.ir.code.DebugPosition;
+import com.android.tools.r8.ir.code.Position;
/**
* Abstraction of the input/source code for the IRBuilder.
@@ -24,7 +24,9 @@
DebugLocalInfo getCurrentLocal(int register);
- DebugPosition getDebugPositionAtOffset(int offset);
+ Position getCurrentPosition();
+
+ Position getDebugPositionAtOffset(int offset);
/**
* Trace block structure of the source-program.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 388c72a..acac84a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -315,6 +315,7 @@
InvokeDirect constructorCall = new InvokeDirect(
lambdaClass.constructor, null /* no return value */, arguments);
instructions.add(constructorCall);
+ constructorCall.setPosition(newInstance.getPosition());
// If we don't have catch handlers we are done.
if (!constructorCall.getBlock().hasCatchHandlers()) {
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 7cb186f..94cbccb 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
@@ -57,6 +57,7 @@
import com.android.tools.r8.ir.code.NewArrayFilledData;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Return;
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.StaticPut;
@@ -100,9 +101,6 @@
public class CodeRewriter {
- private static final int UNKNOWN_CAN_THROW = 0;
- private static final int CAN_THROW = 1;
- private static final int CANNOT_THROW = 2;
private static final int MAX_FILL_ARRAY_SIZE = 8 * Constants.KILOBYTE;
// This constant was determined by experimentation.
private static final int STOP_SHARED_CONSTANT_THRESHOLD = 50;
@@ -117,105 +115,6 @@
this.libraryMethodsReturningReceiver = libraryMethodsReturningReceiver;
}
- /**
- * Removes all debug positions that are not needed to maintain proper stack trace information.
- * If a debug position is followed by another debug position and no instructions between the two
- * can throw then it is unneeded (in a release build).
- * If a block with a position has (normal) outgoing edges, this property depends on the
- * possibility of the successors throwing before the next debug position is hit.
- */
- public static boolean removedUnneededDebugPositions(IRCode code) {
- computeThrowsColorForAllBlocks(code);
- for (BasicBlock block : code.blocks) {
- InstructionListIterator iterator = block.listIterator();
- while (iterator.hasNext()) {
- Instruction instruction = iterator.next();
- if (instruction.isDebugPosition()
- && getThrowsColorForBlock(block, iterator.nextIndex()) == CANNOT_THROW) {
- iterator.remove();
- }
- }
- }
- return true;
- }
-
- private static void computeThrowsColorForAllBlocks(IRCode code) {
- // First pass colors blocks in reverse topological order, based on the instructions.
- code.clearMarks();
- List<BasicBlock> blocks = code.blocks;
- ArrayList<BasicBlock> worklist = new ArrayList<>();
- for (int i = blocks.size() - 1; i >= 0; i--) {
- BasicBlock block = blocks.get(i);
- // Mark the block as not-throwing if no successor implies otherwise.
- // This ensures that a loop back to this block will be seen as non-throwing.
- block.setColor(CANNOT_THROW);
- int color = getThrowsColorForBlock(block, 0);
- block.setColor(color);
- if (color == UNKNOWN_CAN_THROW) {
- worklist.add(block);
- }
- }
- // A fixed point then ensures that we propagate the color backwards over normal edges.
- ArrayList<BasicBlock> remaining = new ArrayList<>(worklist.size());
- while (!worklist.isEmpty()) {
- ImmutableList<BasicBlock> work = new ImmutableList.Builder<BasicBlock>()
- .addAll(worklist)
- .addAll(remaining)
- .build();
- worklist.clear();
- remaining.clear();
- for (BasicBlock block : work) {
- if (!block.hasColor(UNKNOWN_CAN_THROW)) {
- continue;
- }
- block.setColor(CANNOT_THROW);
- int color = getThrowsColorForSuccessors(block);
- block.setColor(color);
- if (color == UNKNOWN_CAN_THROW) {
- remaining.add(block);
- } else {
- for (BasicBlock predecessor : block.getNormalPredecessors()) {
- if (predecessor.hasColor(UNKNOWN_CAN_THROW)) {
- worklist.add(predecessor);
- }
- }
- }
- }
- }
- // Any remaining set of blocks represents a cycle of blocks containing no throwing instructions.
- for (BasicBlock block : remaining) {
- assert !block.canThrow();
- block.setColor(CANNOT_THROW);
- }
- }
-
- private static int getThrowsColorForBlock(BasicBlock block, int index) {
- InstructionListIterator iterator = block.listIterator(index);
- while (iterator.hasNext()) {
- Instruction instruction = iterator.next();
- if (instruction.isDebugPosition()) {
- return CANNOT_THROW;
- }
- if (instruction.instructionTypeCanThrow()) {
- return CAN_THROW;
- }
- }
- return getThrowsColorForSuccessors(block);
- }
-
- private static int getThrowsColorForSuccessors(BasicBlock block) {
- int color = CANNOT_THROW;
- for (BasicBlock successor : block.getNormalSuccessors()) {
- if (successor.hasColor(CAN_THROW)) {
- return CAN_THROW;
- }
- if (successor.hasColor(UNKNOWN_CAN_THROW)) {
- color = UNKNOWN_CAN_THROW;
- }
- }
- return color;
- }
-
private static boolean removedTrivialGotos(IRCode code) {
ListIterator<BasicBlock> iterator = code.listIterator();
assert iterator.hasNext();
@@ -237,31 +136,6 @@
return true;
}
- private static BasicBlock endOfGotoChain(BasicBlock block) {
- block.mark();
- BasicBlock target = block;
- while (target.isTrivialGoto()) {
- BasicBlock nextTarget = target.exit().asGoto().getTarget();
- if (nextTarget.isMarked()) {
- clearTrivialGotoMarks(block);
- return nextTarget;
- }
- nextTarget.mark();
- target = nextTarget;
- }
- clearTrivialGotoMarks(block);
- return target;
- }
-
- private static void clearTrivialGotoMarks(BasicBlock block) {
- while (block.isMarked()) {
- block.clearMark();
- if (block.isTrivialGoto()) {
- block = block.exit().asGoto().getTarget();
- }
- }
- }
-
private static void collapsTrivialGoto(
BasicBlock block, BasicBlock nextBlock, List<BasicBlock> blocksToRemove) {
@@ -270,7 +144,7 @@
return;
}
- BasicBlock target = endOfGotoChain(block);
+ BasicBlock target = block.endOfGotoChain();
boolean needed = false;
if (target != nextBlock) {
@@ -284,7 +158,7 @@
// This implies we are in a loop of GOTOs. In that case, we will iteratively remove each trival
// GOTO one-by-one until the above base case (one block targeting itself) is left.
- if (target == block) {
+ if (target == null) {
target = block.exit().asGoto().getTarget();
}
@@ -307,10 +181,10 @@
private static void collapsIfTrueTarget(BasicBlock block) {
If insn = block.exit().asIf();
BasicBlock target = insn.getTrueTarget();
- BasicBlock newTarget = endOfGotoChain(target);
+ BasicBlock newTarget = target.endOfGotoChain();
BasicBlock fallthrough = insn.fallthroughBlock();
- BasicBlock newFallthrough = endOfGotoChain(fallthrough);
- if (target != newTarget) {
+ BasicBlock newFallthrough = fallthrough.endOfGotoChain();
+ if (newTarget != null && target != newTarget) {
insn.getBlock().replaceSuccessor(target, newTarget);
target.getPredecessors().remove(block);
if (!newTarget.getPredecessors().contains(block)) {
@@ -335,8 +209,8 @@
for (int j = 0; j < insn.targetBlockIndices().length; j++) {
BasicBlock target = insn.targetBlock(j);
if (target != fallthroughBlock) {
- BasicBlock newTarget = endOfGotoChain(target);
- if (target != newTarget && !replacedBlocks.contains(target)) {
+ BasicBlock newTarget = target.endOfGotoChain();
+ if (newTarget != null && target != newTarget && !replacedBlocks.contains(target)) {
insn.getBlock().replaceSuccessor(target, newTarget);
target.getPredecessors().remove(block);
if (!newTarget.getPredecessors().contains(block)) {
@@ -351,6 +225,11 @@
// TODO(sgjesse); Move this somewhere else, and reuse it for some of the other switch rewritings.
public abstract static class InstructionBuilder<T> {
protected int blockNumber;
+ protected final Position position;
+
+ protected InstructionBuilder(Position position) {
+ this.position = position;
+ }
public abstract T self();
@@ -365,6 +244,10 @@
private Int2ObjectSortedMap<BasicBlock> keyToTarget = new Int2ObjectAVLTreeMap<>();
private BasicBlock fallthrough;
+ public SwitchBuilder(Position position) {
+ super(position);
+ }
+
public SwitchBuilder self() {
return this;
}
@@ -406,6 +289,7 @@
Integer fallthroughIndex =
targetToSuccessorIndex.computeIfAbsent(fallthrough, b -> targetToSuccessorIndex.size());
Switch newSwitch = new Switch(value, keys, targetBlockIndices, fallthroughIndex);
+ newSwitch.setPosition(position);
BasicBlock newSwitchBlock = BasicBlock.createSwitchBlock(blockNumber, newSwitch);
for (BasicBlock successor : targetToSuccessorIndex.keySet()) {
newSwitchBlock.link(successor);
@@ -421,7 +305,8 @@
private BasicBlock target;
private BasicBlock fallthrough;
- public IfBuilder(IRCode code) {
+ public IfBuilder(Position position, IRCode code) {
+ super(position);
this.code = code;
}
@@ -456,12 +341,14 @@
BasicBlock ifBlock;
if (right != 0) {
ConstNumber rightConst = code.createIntConstant(right);
+ rightConst.setPosition(position);
newIf = new If(Type.EQ, ImmutableList.of(left, rightConst.dest()));
ifBlock = BasicBlock.createIfBlock(blockNumber, newIf, rightConst);
} else {
newIf = new If(Type.EQ, left);
ifBlock = BasicBlock.createIfBlock(blockNumber, newIf);
}
+ newIf.setPosition(position);
ifBlock.link(target);
ifBlock.link(fallthrough);
return ifBlock;
@@ -477,6 +364,8 @@
InstructionListIterator iterator, Switch theSwitch,
List<IntList> switches, IntList keysToRemove) {
+ Position position = theSwitch.getPosition();
+
// Extract the information from the switch before removing it.
Int2ReferenceSortedMap<BasicBlock> keyToTarget = theSwitch.getKeyToTargetMap();
@@ -503,7 +392,7 @@
// Build the switch-blocks backwards, to always have the fallthrough block in hand.
for (int i = switches.size() - 1; i >= 0; i--) {
- SwitchBuilder switchBuilder = new SwitchBuilder();
+ SwitchBuilder switchBuilder = new SwitchBuilder(position);
switchBuilder.setValue(theSwitch.value());
IntList keys = switches.get(i);
for (int j = 0; j < keys.size(); j++) {
@@ -522,7 +411,7 @@
for (int i = keysToRemove.size() - 1; i >= 0; i--) {
int key = keysToRemove.getInt(i);
BasicBlock peeledOffTarget = keyToTarget.get(key);
- IfBuilder ifBuilder = new IfBuilder(code);
+ IfBuilder ifBuilder = new IfBuilder(position, code);
ifBuilder
.setLeft(theSwitch.value())
.setRight(key)
@@ -561,6 +450,7 @@
iterator.replaceCurrentInstruction(new If(Type.EQ, theSwitch.value()));
} else {
ConstNumber labelConst = code.createIntConstant(theSwitch.getFirstKey());
+ labelConst.setPosition(theSwitch.getPosition());
iterator.previous();
iterator.add(labelConst);
Instruction dummy = iterator.next();
@@ -1155,6 +1045,7 @@
if (newNumber == null) {
newNumber = ConstNumber.copyOf(code, definition);
it.add(newNumber);
+ newNumber.setPosition(current.getPosition());
oldToNew.put(definition, newNumber);
}
invoke.inValues().set(i, newNumber.outValue());
@@ -1242,6 +1133,7 @@
assert constantValue.numberOfUsers() == constantValue.numberOfAllUsers();
for (Instruction user : constantValue.uniqueUsers()) {
ConstNumber newCstNum = ConstNumber.copyOf(code, constNumber);
+ newCstNum.setPosition(user.getPosition());
InstructionListIterator iterator = user.getBlock().listIterator(user);
iterator.previous();
iterator.add(newCstNum);
@@ -1273,7 +1165,9 @@
i.inValues().contains(instruction.outValue())
|| i.isJumpInstruction()
|| (hasCatchHandlers && i.instructionTypeCanThrow()));
- insertAt.previous();
+ Instruction next = insertAt.previous();
+ instruction.forceSetPosition(
+ next.isGoto() ? next.asGoto().getTarget().getPosition() : next.getPosition());
insertAt.add(instruction);
}
@@ -1406,6 +1300,7 @@
}
InvokeNewArray invoke = new InvokeNewArray(
dexItemFactory.stringArrayType, newArray.outValue(), stringValues);
+ invoke.setPosition(newArray.getPosition());
it.detach();
for (Value value : newArray.inValues()) {
value.removeUser(newArray);
@@ -1425,6 +1320,7 @@
int arraySize = newArray.size().getConstInstruction().asConstNumber().getIntValue();
NewArrayFilledData fillArray = new NewArrayFilledData(
newArray.outValue(), elementSize, arraySize, contents);
+ fillArray.setPosition(newArray.getPosition());
it.add(fillArray);
}
storesToRemoveForArray.put(newArray.outValue(), size);
@@ -1465,13 +1361,25 @@
if (from.getBlock() != to.getBlock()) {
return true;
}
+ if (from.getPosition().isSome()
+ && to.getPosition().isSome()
+ && !from.getPosition().equals(to.getPosition())) {
+ return true;
+ }
InstructionListIterator iterator = from.getBlock().listIterator(from);
+ Position position = null;
while (iterator.hasNext()) {
Instruction instruction = iterator.next();
+ if (position == null) {
+ if (instruction.getPosition().isSome()) {
+ position = instruction.getPosition();
+ }
+ } else if (instruction.getPosition().isSome()
+ && !position.equals(instruction.getPosition())) {
+ return true;
+ }
if (instruction == to) {
return false;
- } else if (instruction.isDebugPosition()) {
- return true;
}
}
throw new Unreachable();
@@ -1530,11 +1438,12 @@
}
}
- private static class ExpressionEquivalence extends Equivalence<Instruction> {
+ private static class CSEExpressionEquivalence extends Equivalence<Instruction> {
@Override
protected boolean doEquivalent(Instruction a, Instruction b) {
- if (a.getClass() != b.getClass() || !a.identicalNonValueParts(b)) {
+ // Note that we don't consider positions because CSE can at most remove an instruction.
+ if (a.getClass() != b.getClass() || !a.identicalNonValueNonPositionParts(b)) {
return false;
}
// For commutative binary operations any order of in-values are equal.
@@ -1596,7 +1505,7 @@
public void commonSubexpressionElimination(IRCode code) {
final ListMultimap<Wrapper<Instruction>, Value> instructionToValue = ArrayListMultimap.create();
final DominatorTree dominatorTree = new DominatorTree(code);
- final ExpressionEquivalence equivalence = new ExpressionEquivalence();
+ final CSEExpressionEquivalence equivalence = new CSEExpressionEquivalence();
for (int i = 0; i < dominatorTree.getSortedBlocks().length; i++) {
BasicBlock block = dominatorTree.getSortedBlocks()[i];
@@ -1760,6 +1669,8 @@
Instruction newInstruction = new Xor(NumericType.INT, newOutValue, testValue,
trueNumber.isIntegerOne() ? trueValue : falseValue);
newInstruction.setBlock(phi.getBlock());
+ // The xor is replacing a phi so it does not have an actual position.
+ newInstruction.setPosition(phi.getBlock().getPosition());
phi.getBlock().getInstructions().add(0, newInstruction);
deadPhis++;
}
@@ -1882,6 +1793,7 @@
// First insert the constant value *before* the current instruction.
ConstNumber zero = code.createIntConstant(0);
+ zero.setPosition(current.getPosition());
assert iterator.hasPrevious();
iterator.previous();
iterator.add(zero);
@@ -1936,6 +1848,10 @@
BasicBlock block = code.blocks.getFirst();
InstructionListIterator iterator = block.listIterator();
+ // Attach some synthetic position to all inserted code.
+ Position position = Position.synthetic(1);
+ iterator.setInsertionPosition(position);
+
// Split arguments into their own block.
iterator.nextUntil(instruction -> !instruction.isArgument());
iterator.previous();
@@ -1992,6 +1908,7 @@
// Insert "if (argument != null) ...".
successor = block.unlinkSingleSuccessor();
If theIf = new If(If.Type.NE, argument);
+ theIf.setPosition(position);
BasicBlock ifBlock = BasicBlock.createIfBlock(code.blocks.size(), theIf);
code.blocks.add(ifBlock);
// Fallthrough block must be added right after the if.
@@ -2009,8 +1926,10 @@
// Fill code into the blocks.
iterator = isNullBlock.listIterator();
+ iterator.setInsertionPosition(position);
iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, nul)));
iterator = isNotNullBlock.listIterator();
+ iterator.setInsertionPosition(position);
value = code.createValue(MoveType.OBJECT);
iterator.add(new InvokeVirtual(dexItemFactory.objectMethods.getClass, value,
ImmutableList.of(arguments.get(i))));
@@ -2018,6 +1937,7 @@
}
iterator = eol.listIterator();
+ iterator.setInsertionPosition(position);
if (i == arguments.size() - 1) {
iterator.add(new InvokeVirtual(printLn, null, ImmutableList.of(out, closeParenthesis)));
} else {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 241106c..12f1cf2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -110,6 +110,7 @@
assert replacement.outValue() != null;
current.outValue().replaceUsers(replacement.outValue());
}
+ replacement.setPosition(current.getPosition());
iterator.add(replacement);
}
}
@@ -174,6 +175,7 @@
Instruction knownConstReturn =
new ConstNumber(ConstType.fromMoveType(moveType), value, constant);
invoke.outValue().replaceUsers(value);
+ knownConstReturn.setPosition(invoke.getPosition());
iterator.add(knownConstReturn);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 20c54f3..49dcb45 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -28,7 +28,6 @@
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
import com.android.tools.r8.ir.code.CatchHandlers;
-import com.android.tools.r8.ir.code.DebugPosition;
import com.android.tools.r8.ir.code.Div;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -40,6 +39,7 @@
import com.android.tools.r8.ir.code.MoveType;
import com.android.tools.r8.ir.code.Mul;
import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Rem;
import com.android.tools.r8.ir.code.Sub;
import com.android.tools.r8.ir.code.Value;
@@ -154,7 +154,8 @@
for (int i = 0; i < instructions0.size(); i++) {
Instruction i0 = instructions0.get(i);
Instruction i1 = instructions1.get(i);
- if (i0.getClass() != i1.getClass() || !i0.identicalNonValueParts(i1)) {
+ // Note that we don't consider positions as this optimization already breaks stack traces.
+ if (i0.getClass() != i1.getClass() || !i0.identicalNonValueNonPositionParts(i1)) {
return false;
}
if ((i0.outValue() != null) != (i1.outValue() != null)) {
@@ -701,6 +702,7 @@
List<Value> in = new ArrayList<>();
returnValue = null;
argumentsMapIndex = 0;
+ Position position = Position.none();
{ // Scope for 'instructions'.
List<Instruction> instructions = getInstructionArray();
for (int i = start; i < end; i++) {
@@ -709,7 +711,9 @@
// Leave any const instructions.
continue;
}
-
+ if (position.isNone()) {
+ position = current.getPosition();
+ }
// Prepare to remove the instruction.
List<Value> inValues = orderedInValues(current, returnValue);
for (int j = 0; j < inValues.size(); j++) {
@@ -737,6 +741,12 @@
}
Invoke outlineInvoke = new InvokeStatic(m, returnValue, in);
outlineInvoke.setBlock(block);
+ outlineInvoke.setPosition(position);
+ if (position.isNone() && code.doAllThrowingInstructionsHavePositions()) {
+ // We have introduced a static invoke, but non of the outlines instructions could throw
+ // and none had a position. The code no longer has the previous property.
+ code.setAllThrowingInstructionsHavePositions(false);
+ }
InstructionListIterator endIterator = block.listIterator(end - 1);
Instruction instructionBeforeEnd = endIterator.next();
invalidateInstructionArray(); // Because we're about to modify the original linked list.
@@ -948,11 +958,16 @@
}
@Override
- public DebugPosition getDebugPositionAtOffset(int offset) {
+ public Position getDebugPositionAtOffset(int offset) {
throw new Unreachable();
}
@Override
+ public Position getCurrentPosition() {
+ return Position.none();
+ }
+
+ @Override
public boolean verifyCurrentInstructionCanThrow() {
// TODO(sgjesse): Check more here?
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
index e6916b8..39f622c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
@@ -101,6 +101,7 @@
commonSuffixSize =
Math.min(commonSuffixSize, sharedSuffixSize(firstPred, pred, allocator));
}
+ // Don't share a suffix that is just a single goto or return instruction.
if (commonSuffixSize <= 1) {
continue;
}
@@ -198,9 +199,7 @@
Instruction i0 = it0.previous();
Instruction i1 = it1.previous();
if (!i0.identicalAfterRegisterAllocation(i1, allocator)) {
- // If the shared suffix follows a debug position at least one instruction must remain
- // unshared to ensure the debug position is at a different pc than the shared suffix.
- return i0.isDebugPosition() || i1.isDebugPosition() ? suffixSize - 1 : suffixSize;
+ return suffixSize;
}
suffixSize++;
}
@@ -287,8 +286,9 @@
}
int outRegister = allocator.getRegisterForValue(outValue, instructionNumber);
ConstNumber numberInRegister = registerToNumber.get(outRegister);
- if (numberInRegister != null && numberInRegister.identicalNonValueParts(current)) {
+ if (numberInRegister != null && numberInRegister.identicalNonValueNonPositionParts(current)) {
// This instruction is not needed, the same constant is already in this register.
+ // We don't consider the positions of the two (non-throwing) instructions.
iterator.remove();
} else {
// Insert the current constant in the mapping. Make sure to clobber the second
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 7a2dc4a..21e6773 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
@@ -291,14 +291,18 @@
for (BasicBlock block : blocks) {
ListIterator<Instruction> instructionIterator = block.listIterator();
- // Close ranges up-to and including the first instruction. Ends are exclusive so the range is
- // closed at entry.
+ // Close ranges up-to but excluding the first instruction. Ends are exclusive but the values
+ // are live upon entering the first instruction.
int entryIndex = block.entry().getNumber();
+ if (block.entry().isMoveException()) {
+ // Close locals at a move exception since they close as part of the exceptional transfer.
+ entryIndex++;
+ }
{
ListIterator<LocalRange> it = openRanges.listIterator(0);
while (it.hasNext()) {
LocalRange openRange = it.next();
- if (openRange.end <= entryIndex) {
+ if (openRange.end < entryIndex) {
it.remove();
assert currentLocals.get(openRange.register) == openRange.local;
currentLocals.remove(openRange.register);
@@ -308,8 +312,9 @@
// Open ranges up-to but excluding the first instruction. Starts are inclusive but entry is
// prior to the first instruction.
while (nextStartingRange != null && nextStartingRange.start < entryIndex) {
- // If the range is live at this index open it.
- if (entryIndex < nextStartingRange.end) {
+ // If the range is live at this index open it. Again the end is inclusive here because the
+ // instruction is live at block entry if it is live at entry to the first instruction.
+ if (entryIndex <= nextStartingRange.end) {
openRanges.add(nextStartingRange);
assert !currentLocals.containsKey(nextStartingRange.register);
currentLocals.put(nextStartingRange.register, nextStartingRange.local);
@@ -341,10 +346,10 @@
ListIterator<LocalRange> it = openRanges.listIterator(0);
Int2ReferenceMap<DebugLocalInfo> ending = new Int2ReferenceOpenHashMap<>();
Int2ReferenceMap<DebugLocalInfo> starting = new Int2ReferenceOpenHashMap<>();
- int endPositionCorrection = instruction.isDebugPosition() ? 1 : 0;
while (it.hasNext()) {
LocalRange openRange = it.next();
- if (openRange.end <= index - endPositionCorrection) {
+ // Any local change is inserted after the instruction so end is inclusive.
+ if (openRange.end <= index) {
it.remove();
assert currentLocals.get(openRange.register) == openRange.local;
currentLocals.remove(openRange.register);
@@ -378,13 +383,7 @@
if (localsChanged && instruction.getBlock().exit() != instruction) {
DebugLocalsChange change = createLocalsChange(ending, starting);
if (change != null) {
- if (instruction.isDebugPosition()) {
- instructionIterator.previous();
- instructionIterator.add(change);
- instructionIterator.next();
- } else {
- instructionIterator.add(change);
- }
+ instructionIterator.add(change);
}
}
localsChanged = false;
@@ -2057,6 +2056,7 @@
newArgument = createValue(argument.outType());
Move move = new Move(newArgument, argument);
move.setBlock(invoke.getBlock());
+ move.setPosition(invoke.getPosition());
replaceArgument(invoke, i, newArgument);
insertAt.add(move);
}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
index a669737..5d7606d 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.Move;
import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Value;
import java.util.ArrayList;
import java.util.Deque;
@@ -30,12 +31,20 @@
private int usedTempRegisters = 0;
// Location at which to insert the scheduled moves.
private final InstructionListIterator insertAt;
+ // Debug position associated with insertion point.
+ private final Position position;
// The first available temporary register.
private final int tempRegister;
- public RegisterMoveScheduler(InstructionListIterator insertAt, int tempRegister) {
+ public RegisterMoveScheduler(
+ InstructionListIterator insertAt, int tempRegister, Position position) {
this.insertAt = insertAt;
this.tempRegister = tempRegister;
+ this.position = position;
+ }
+
+ public RegisterMoveScheduler(InstructionListIterator insertAt, int tempRegister) {
+ this(insertAt, tempRegister, Position.none());
}
public void addMove(RegisterMove move) {
@@ -133,6 +142,7 @@
Value from = new FixedRegisterValue(move.type, valueMap.get(move.src));
instruction = new Move(to, from);
}
+ instruction.setPosition(position);
insertAt.add(instruction);
return move.dst;
@@ -152,7 +162,9 @@
// of generating a new one.
Value to = new FixedRegisterValue(moveWithSrc.type, tempRegister + usedTempRegisters);
Value from = new FixedRegisterValue(moveWithSrc.type, valueMap.get(moveWithSrc.src));
- insertAt.add(new Move(to, from));
+ Move instruction = new Move(to, from);
+ instruction.setPosition(position);
+ insertAt.add(instruction);
valueMap.put(moveWithSrc.src, tempRegister + usedTempRegisters);
usedTempRegisters += moveWithSrc.type == MoveType.WIDE ? 2 : 1;
}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java b/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
index cd1d880..9154eff 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.Position;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
@@ -226,6 +227,19 @@
private void scheduleMovesBeforeInstruction(
int tempRegister, int instruction, InstructionListIterator insertAt) {
+
+ Position position;
+ if (insertAt.hasPrevious() && insertAt.peekPrevious().isMoveException()) {
+ position = insertAt.peekPrevious().getPosition();
+ } else {
+ Instruction next = insertAt.peekNext();
+ assert next.getNumber() == instruction;
+ position = next.getPosition();
+ if (position.isNone() && next.isGoto()) {
+ position = next.asGoto().getTarget().getPosition();
+ }
+ }
+
// Spill and restore moves for the incoming edge.
Set<SpillMove> inMoves =
instructionToInMoves.computeIfAbsent(instruction - 1, (k) -> new LinkedHashSet<>());
@@ -247,8 +261,8 @@
outMoves.addAll(phiMoves);
// Perform parallel move scheduling independently for the in and out moves.
- scheduleMoves(tempRegister, inMoves, insertAt);
- scheduleMoves(tempRegister, outMoves, insertAt);
+ scheduleMoves(tempRegister, inMoves, insertAt, position);
+ scheduleMoves(tempRegister, outMoves, insertAt, position);
}
// Remove restore moves that restore arguments. Since argument register reuse is
@@ -270,9 +284,8 @@
}
private void scheduleMoves(
- int tempRegister, Collection<SpillMove> moves, InstructionListIterator insertAt) {
- RegisterMoveScheduler scheduler =
- new RegisterMoveScheduler(insertAt, tempRegister);
+ int tempRegister, Set<SpillMove> moves, InstructionListIterator insertAt, Position position) {
+ RegisterMoveScheduler scheduler = new RegisterMoveScheduler(insertAt, tempRegister, position);
for (SpillMove move : moves) {
// Do not generate moves to spill a value that can be rematerialized.
if (move.to.isSpilledAndRematerializable(allocator)) {
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SingleBlockSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SingleBlockSourceCode.java
index c8fc818..637cbac 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/SingleBlockSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SingleBlockSourceCode.java
@@ -13,8 +13,8 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.Argument;
import com.android.tools.r8.ir.code.CatchHandlers;
-import com.android.tools.r8.ir.code.DebugPosition;
import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.IRBuilder;
import com.android.tools.r8.ir.conversion.SourceCode;
@@ -196,11 +196,16 @@
}
@Override
- public DebugPosition getDebugPositionAtOffset(int offset) {
+ public Position getDebugPositionAtOffset(int offset) {
throw new Unreachable();
}
@Override
+ public Position getCurrentPosition() {
+ return Position.none();
+ }
+
+ @Override
public final boolean verifyCurrentInstructionCanThrow() {
return true;
}
diff --git a/src/test/debugTestResources/FinallyBlock.java b/src/test/debugTestResources/FinallyBlock.java
new file mode 100644
index 0000000..4b30391
--- /dev/null
+++ b/src/test/debugTestResources/FinallyBlock.java
@@ -0,0 +1,37 @@
+// 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.
+
+public class FinallyBlock {
+
+ public static int finallyBlock(Throwable obj) throws Throwable {
+ int x = 21;
+ try {
+ if (obj != null) {
+ throw obj;
+ }
+ } catch (AssertionError e) {
+ x = e.getMessage().length() + 1;
+ } catch (RuntimeException e) {
+ x = e.getMessage().length() + 2;
+ } finally {
+ x *= 2;
+ }
+ return x;
+ }
+
+ private static int callFinallyBlock(Throwable obj) {
+ try {
+ return finallyBlock(obj);
+ } catch (Throwable e) {
+ return -1;
+ }
+ }
+
+ public static void main(String[] args) throws Throwable {
+ System.out.println(callFinallyBlock(null));
+ System.out.println(callFinallyBlock(new AssertionError("assert error")));
+ System.out.println(callFinallyBlock(new RuntimeException("runtime error")));
+ System.out.println(callFinallyBlock(new Throwable("throwable")));
+ }
+}
diff --git a/src/test/debugTestResources/SharedCode.java b/src/test/debugTestResources/SharedCode.java
new file mode 100644
index 0000000..f88fdbd
--- /dev/null
+++ b/src/test/debugTestResources/SharedCode.java
@@ -0,0 +1,25 @@
+// 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.
+
+public class SharedCode {
+
+ public static int sharedIf(int x) {
+ if (x == 0) {
+ doit(1); doit(2); doit(1); doit(2);
+ } else {
+ doit(1); doit(2); doit(1); doit(2);
+ }
+ return x;
+ }
+
+
+ public static void doit(int x) {
+ // nothing to do really.
+ }
+
+ public static void main(String[] args) {
+ System.out.println(sharedIf(0));
+ System.out.println(sharedIf(1));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java b/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java
index 6f6b7d0..7081921 100644
--- a/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java
+++ b/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.debug;
import org.junit.BeforeClass;
-import org.junit.Ignore;
import org.junit.Test;
/**
@@ -23,7 +22,6 @@
}
@Test
- @Ignore("b/65618023")
public void testConditionalReturn() throws Throwable {
final String method = "conditionalReturn";
runDebugTest(CLASS,
@@ -39,7 +37,6 @@
}
@Test
- @Ignore("b/65618023")
public void testInvertConditionalReturn() throws Throwable {
final String method = "invertConditionalReturn";
runDebugTest(CLASS,
@@ -55,7 +52,6 @@
}
@Test
- @Ignore("b/65618023")
public void testFallthroughReturn() throws Throwable {
final String method = "fallthroughReturn";
runDebugTest(CLASS,
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 ec32bd3..6686635 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -11,6 +11,8 @@
import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.ProguardRuleParserException;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -88,9 +90,16 @@
ART
}
+ enum DexCompilerKind {
+ DX,
+ D8
+ }
+
// Set to JAVA to run tests with java
private static final RuntimeKind RUNTIME_KIND = RuntimeKind.ART;
+ private static final DexCompilerKind DEX_COMPILER_KIND = DexCompilerKind.D8;
+
// Set to true to enable verbose logs
private static final boolean DEBUG_TESTS = false;
@@ -123,35 +132,63 @@
Consumer<InternalOptions> optionsConsumer,
Consumer<ProguardConfiguration.Builder> pgConsumer)
throws Exception {
- // Convert jar to dex with d8 with debug info
- jdwpDexD8 = compileToDex(null, JDWP_JAR);
// TODO(zerny): supply a set of compilers to run with.
- debuggeeDexD8 = compileToDex(optionsConsumer, DEBUGGEE_JAR);
+ // Compile the debuggee code first so potential compilation errors or debugging breakpoints hit
+ // here first.
+ debuggeeDexD8 = compileToDex(DEBUGGEE_JAR, optionsConsumer);
debuggeeDexR8 = compileToDexViaR8(optionsConsumer, pgConsumer, DEBUGGEE_JAR);
- debuggeeJava8DexD8 = compileToDex(options -> {
+ debuggeeJava8DexD8 = compileToDex(DEBUGGEE_JAVA8_JAR, options -> {
// Enable desugaring for preN runtimes
options.interfaceMethodDesugaring = OffOrAuto.Auto;
if (optionsConsumer != null) {
optionsConsumer.accept(options);
}
- }, DEBUGGEE_JAVA8_JAR);
- debuggeeKotlinDexD8 = compileToDex(optionsConsumer, DEBUGGEE_KOTLIN_JAR);
+ });
+ debuggeeKotlinDexD8 = compileToDex(DEBUGGEE_KOTLIN_JAR, optionsConsumer);
+ // Convert jar to dex with d8 with debug info
+ jdwpDexD8 = compileToDex(JDWP_JAR, null);
}
- static Path compileToDex(Consumer<InternalOptions> optionsConsumer, Path jarToCompile)
+ protected static Path compileToDex(Path jarToCompile, Consumer<InternalOptions> optionsConsumer)
+ throws IOException, CompilationException {
+ return compileToDex(DEX_COMPILER_KIND, jarToCompile, optionsConsumer);
+ }
+
+ static Path compileToDex(
+ DexCompilerKind compiler, Path jarToCompile, Consumer<InternalOptions> optionsConsumer)
throws IOException, CompilationException {
int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
assert jarToCompile.toFile().exists();
Path dexOutputDir = temp.newFolder().toPath();
- ToolHelper.runD8(
- D8Command.builder()
- .addProgramFiles(jarToCompile)
- .setOutputPath(dexOutputDir)
- .setMinApiLevel(minSdk)
- .setMode(CompilationMode.DEBUG)
- .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
- .build(),
- optionsConsumer);
+ switch (compiler) {
+ case D8:
+ {
+ ToolHelper.runD8(
+ D8Command.builder()
+ .addProgramFiles(jarToCompile)
+ .setOutputPath(dexOutputDir)
+ .setMinApiLevel(minSdk)
+ .setMode(CompilationMode.DEBUG)
+ .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
+ .build(),
+ optionsConsumer);
+ break;
+ }
+ case DX:
+ {
+ ProcessResult result =
+ ToolHelper.runDX(
+ new String[] {
+ "--output=" + dexOutputDir,
+ "--min-sdk-version=" + minSdk,
+ jarToCompile.toString()
+ });
+ Assert.assertEquals(result.stderr, 0, result.exitCode);
+ break;
+ }
+ default:
+ throw new Unreachable();
+ }
return dexOutputDir.resolve("classes.dex");
}
diff --git a/src/test/java/com/android/tools/r8/debug/FinallyBlockTest.java b/src/test/java/com/android/tools/r8/debug/FinallyBlockTest.java
new file mode 100644
index 0000000..83a0486
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/FinallyBlockTest.java
@@ -0,0 +1,79 @@
+// 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.debug;
+
+import org.junit.Test;
+
+/** Test single stepping behaviour of synchronized blocks. */
+public class FinallyBlockTest extends DebugTestBase {
+
+ public static final String CLASS = "FinallyBlock";
+ public static final String FILE = "FinallyBlock.java";
+
+ @Test
+ public void testEmptyBlock() throws Throwable {
+ final String method = "finallyBlock";
+ runDebugTest(CLASS,
+ breakpoint(CLASS, method),
+ run(),
+ checkLine(FILE, 8),
+ stepOver(),
+ checkLine(FILE, 10),
+ stepOver(),
+ checkLine(FILE, 18),
+ stepOver(),
+ checkLine(FILE, 19),
+ stepOver(),
+ checkLine(FILE, 20),
+ stepOver(),
+ checkLine(FILE, 25), // return in callFinallyBlock
+ run(),
+ checkLine(FILE, 8),
+ stepOver(),
+ checkLine(FILE, 10),
+ stepOver(),
+ checkLine(FILE, 11),
+ stepOver(),
+ checkLine(FILE, 13), // catch AE
+ stepOver(),
+ checkLine(FILE, 14),
+ stepOver(),
+ checkLine(FILE, 18),
+ stepOver(),
+ checkLine(FILE, 19),
+ stepOver(),
+ checkLine(FILE, 20),
+ stepOver(),
+ checkLine(FILE, 25), // return in callFinallyBlock
+ run(),
+ checkLine(FILE, 8),
+ stepOver(),
+ checkLine(FILE, 10),
+ stepOver(),
+ checkLine(FILE, 11),
+ stepOver(),
+ checkLine(FILE, 15), // catch RE
+ stepOver(),
+ checkLine(FILE, 16),
+ stepOver(),
+ checkLine(FILE, 18),
+ stepOver(),
+ checkLine(FILE, 19),
+ stepOver(),
+ checkLine(FILE, 20),
+ stepOver(),
+ checkLine(FILE, 25), // return in callFinallyBlock
+ run(),
+ checkLine(FILE, 8),
+ stepOver(),
+ checkLine(FILE, 10),
+ stepOver(),
+ checkLine(FILE, 11), // throw without catch
+ stepOver(),
+ checkLine(FILE, 18), // finally
+ stepOver(),
+ checkLine(FILE, 26), // catch in callFinallyBlock
+ run());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java b/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java
index 1b29472..2b0084d 100644
--- a/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java
+++ b/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java
@@ -30,23 +30,20 @@
final String className = "UselessCheckCast";
final String sourcefile = className + ".j";
final String methodName = "test";
- runDebugTest(getExtraPaths(getBuilderForUselessCheckcast(className, methodName)),
+ List<Path> paths = getExtraPaths(getBuilderForUselessCheckcast(className, methodName));
+ runDebugTest(paths,
className,
breakpoint(className, methodName),
run(),
- checkLine(sourcefile, 8),
+ checkLine(sourcefile, 1),
stepOver(),
- checkLine(sourcefile, 9),
- stepOver(),
- checkLine(sourcefile, 10),
- stepOver(),
- checkLine(sourcefile, 12),
+ checkLine(sourcefile, 2),
checkLocal("local"),
stepOver(),
- checkLine(sourcefile, 14),
+ checkLine(sourcefile, 3),
checkNoLocal("local"),
stepOver(),
- checkLine(sourcefile, 15),
+ checkLine(sourcefile, 4),
run());
}
@@ -58,13 +55,17 @@
".limit stack 1",
".limit locals 3",
".var 1 is local Ljava/lang/Object; from Label1 to Label2",
+ ".line 1",
" aload 0",
" dup",
" astore 1",
" Label1:",
+ ".line 2",
" checkcast " + testClassName,
" Label2:",
+ ".line 3",
" checkcast " + testClassName,
+ ".line 4",
"return");
clazz.addMainMethod(
@@ -84,14 +85,14 @@
for (ClassBuilder clazz : classes) {
ClassFile file = new ClassFile();
- file.readJasmin(new StringReader(clazz.toString()), clazz.name, true);
+ file.readJasmin(new StringReader(clazz.toString()), clazz.name, false);
Path path = out.toPath().resolve(clazz.name + ".class");
Files.createDirectories(path.getParent());
file.write(new FileOutputStream(path.toFile()));
if (isRunningJava()) {
extraPaths.add(path);
} else {
- extraPaths.add(compileToDex(null, path));
+ extraPaths.add(compileToDex(path, null));
}
}
diff --git a/src/test/java/com/android/tools/r8/debug/SharedCodeTest.java b/src/test/java/com/android/tools/r8/debug/SharedCodeTest.java
new file mode 100644
index 0000000..d4149e8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/SharedCodeTest.java
@@ -0,0 +1,36 @@
+// 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.debug;
+
+import org.junit.Test;
+
+public class SharedCodeTest extends DebugTestBase {
+
+ public static final String CLASS = "SharedCode";
+ public static final String FILE = "SharedCode.java";
+
+ @Test
+ public void testSharedIf() throws Throwable {
+ final String methodName = "sharedIf";
+ runDebugTest(CLASS,
+ breakpoint(CLASS, methodName),
+ run(),
+ checkMethod(CLASS, methodName),
+ checkLine(FILE, 8),
+ stepOver(),
+ checkLine(FILE, 9),
+ stepOver(),
+ checkLine(FILE, 13),
+ run(),
+ checkMethod(CLASS, methodName),
+ checkLine(FILE, 8),
+ stepOver(),
+ checkLine(FILE, 11),
+ stepOver(),
+ checkLine(FILE, 13),
+ run());
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/debug/SynchronizedBlockTest.java b/src/test/java/com/android/tools/r8/debug/SynchronizedBlockTest.java
index 42f0174..fa6aa47 100644
--- a/src/test/java/com/android/tools/r8/debug/SynchronizedBlockTest.java
+++ b/src/test/java/com/android/tools/r8/debug/SynchronizedBlockTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.debug;
-import org.junit.Ignore;
import org.junit.Test;
/**
@@ -71,7 +70,6 @@
}
@Test
- @Ignore("b/65567013")
public void testThrowingBlock() throws Throwable {
final String method = "throwingBlock";
runDebugTest(CLASS,
@@ -79,22 +77,27 @@
run(),
checkLine(FILE, 25),
checkLocal("obj"),
+ checkNoLocal("x"),
stepOver(),
checkLine(FILE, 26),
checkLocal("obj"),
checkLocal("x"),
+ checkNoLocal("y"),
stepOver(),
checkLine(FILE, 27),
checkLocal("obj"),
checkLocal("x"),
+ checkNoLocal("y"),
stepOver(),
checkLine(FILE, 28), // synchronized block end
checkLocal("obj"),
checkLocal("x"),
+ checkNoLocal("y"),
stepOver(),
checkLine(FILE, 31), // catch handler
checkLocal("obj"),
checkNoLocal("x"),
+ checkNoLocal("y"),
stepOver(),
run());
}
@@ -153,7 +156,6 @@
}
@Test
- @Ignore("b/65567013")
public void testNestedThrowingBlock() throws Throwable {
final String method = "nestedThrowingBlock";
runDebugTest(CLASS,
diff --git a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
index c911d37..7351206 100644
--- a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
+++ b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.MoveType;
import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueNumberGenerator;
import com.android.tools.r8.smali.SmaliTestBase;
@@ -373,6 +374,8 @@
iterator.previous();
iterator.add(constInstruction);
iterator.add(addInstruction);
+ addInstruction.setPosition(Position.none());
+ constInstruction.setPosition(Position.none());
}
// Run code and check result (code in the test object is updated).
String result = test.run();
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
index 6eebf6b..6969c2d 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.ir.code.ConstType;
import com.android.tools.r8.ir.code.MoveType;
import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Value;
import org.junit.Test;
@@ -54,7 +55,9 @@
ConstNumber const2 = new ConstNumber(ConstType.INT, value2, 2);
Value value3 = new Value(2, MoveType.SINGLE, null);
Add add0 = new Add(NumericType.INT, value3, value0, value1);
+ add0.setPosition(Position.none());
Add add1 = new Add(NumericType.INT, value3, value0, value2);
+ add1.setPosition(Position.none());
value0.computeNeedsRegister();
assertTrue(value0.needsRegister());
value1.computeNeedsRegister();
diff --git a/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java b/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
index 8c93a8a..2511d79 100644
--- a/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
+++ b/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
@@ -196,21 +196,23 @@
" default: CaseDefault",
"Case1:",
+ ".line 3",
" ldc 42",
" istore 1",
"LabelYStart:",
- ".line 3",
+ ".line 4",
" invokestatic Test/ensureLine()V",
" goto AfterSwitch",
"CaseDefault:",
+ ".line 5",
" ldc -42",
" istore 1",
- ".line 4",
+ ".line 6",
" invokestatic Test/ensureLine()V",
"AfterSwitch:",
- ".line 5",
+ ".line 7",
" iload 1",
" ireturn",
"LabelYEnd:",
@@ -246,9 +248,9 @@
info.checkStartLine(1);
info.checkLineHasExactLocals(1, "param", "int");
info.checkLineHasExactLocals(2, "param", "int", "x", "int");
- info.checkLineHasExactLocals(3, "param", "int", "y", "int");
info.checkLineHasExactLocals(4, "param", "int", "y", "int");
- info.checkLineHasExactLocals(5, "param", "int", "y", "int");
+ info.checkLineHasExactLocals(6, "param", "int", "y", "int");
+ info.checkLineHasExactLocals(7, "param", "int", "y", "int");
}
@Test
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
index ad625cc..c609701 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
@@ -52,7 +52,7 @@
File out = temp.newFolder("classes");
for (ClassBuilder clazz : builder.getClasses()) {
ClassFile file = new ClassFile();
- file.readJasmin(new StringReader(clazz.toString()), clazz.name, true);
+ file.readJasmin(new StringReader(clazz.toString()), clazz.name, false);
Path path = out.toPath().resolve(clazz.name + ".class");
Files.createDirectories(path.getParent());
file.write(new FileOutputStream(path.toFile()));
@@ -110,7 +110,7 @@
private ProcessResult runDx(JasminBuilder builder, File classes, Path dex) throws Exception {
for (ClassBuilder clazz : builder.getClasses()) {
ClassFile file = new ClassFile();
- file.readJasmin(new StringReader(clazz.toString()), clazz.name, true);
+ file.readJasmin(new StringReader(clazz.toString()), clazz.name, false);
file.write(new FileOutputStream(classes.toPath().resolve(clazz.name + ".class").toFile()));
}
List<String> args = new ArrayList<>();
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 15cd3b6..c289c8e 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -37,8 +37,8 @@
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.ir.code.CatchHandlers;
-import com.android.tools.r8.ir.code.DebugPosition;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.conversion.IRBuilder;
import com.android.tools.r8.ir.conversion.SourceCode;
import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
@@ -593,7 +593,7 @@
code);
IRCode ir = code.buildIR(method, options);
RegisterAllocator allocator = new LinearScanRegisterAllocator(ir, options);
- method.setCode(ir, allocator, factory);
+ method.setCode(ir, allocator, factory, options);
directMethods[i] = method;
}
builder.addProgramClass(
@@ -705,11 +705,16 @@
}
@Override
- public DebugPosition getDebugPositionAtOffset(int offset) {
+ public Position getDebugPositionAtOffset(int offset) {
throw new Unreachable();
}
@Override
+ public Position getCurrentPosition() {
+ return Position.none();
+ }
+
+ @Override
public boolean verifyRegister(int register) {
throw new Unreachable();
}