Revert "Remove unneeded debug use markers."
This reverts commit a65ddf3b5a266a6bae3a04192e6b00fd3f4826ea.
Reason for revert: Test failures due to nondeterminism
Bug: 157466079
Bug: 142855947
Change-Id: I0923ad201f008e3cafca9bb4f706f24dbb56eaa9
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 b0ef472..2c3d9a5 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
@@ -1292,17 +1292,36 @@
builder.append(": ");
builder.append(instruction.toString());
if (DebugLocalInfo.PRINT_LEVEL != PrintLevel.NONE) {
- if (!instruction.getDebugValues().isEmpty()) {
- builder.append(" [end: ");
- StringUtils.append(builder, instruction.getDebugValues(), ", ", BraceType.NONE);
- builder.append("]");
+ List<Value> localEnds = new ArrayList<>(instruction.getDebugValues().size());
+ List<Value> localStarts = new ArrayList<>(instruction.getDebugValues().size());
+ List<Value> localLive = new ArrayList<>(instruction.getDebugValues().size());
+ for (Value value : instruction.getDebugValues()) {
+ if (value.getDebugLocalEnds().contains(instruction)) {
+ localEnds.add(value);
+ } else if (value.getDebugLocalStarts().contains(instruction)) {
+ localStarts.add(value);
+ } else {
+ assert value.debugUsers().contains(instruction);
+ localLive.add(value);
+ }
}
+ printDebugValueSet("live", localLive, builder);
+ printDebugValueSet("end", localEnds, builder);
+ printDebugValueSet("start", localStarts, builder);
}
builder.append("\n");
}
return builder.toString();
}
+ private void printDebugValueSet(String header, List<Value> locals, StringBuilder builder) {
+ if (!locals.isEmpty()) {
+ builder.append(" [").append(header).append(": ");
+ StringUtils.append(builder, locals, ", ", BraceType.NONE);
+ builder.append("]");
+ }
+ }
+
public void print(CfgPrinter printer) {
printer.begin("block");
printer.print("name \"B").append(number).append("\"\n");
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 6e5dfa3..9e25dcf 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
@@ -32,9 +32,9 @@
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.StringUtils.BraceType;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Iterator;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
@@ -45,7 +45,7 @@
protected final List<Value> inValues = new ArrayList<>();
private BasicBlock block = null;
private int number = -1;
- private Set<Value> debugValues = null;
+ private LinkedHashSet<Value> debugValues = null;
private Position position = null;
protected Instruction(Value outValue) {
@@ -154,9 +154,11 @@
public void addDebugValue(Value value) {
assert value.hasLocalInfo();
if (debugValues == null) {
- debugValues = Sets.newIdentityHashSet();
+ debugValues = new LinkedHashSet<>();
}
- debugValues.add(value);
+ if (debugValues.add(value)) {
+ value.addDebugUser(this);
+ }
}
public static void clearUserInfo(Instruction instruction) {
@@ -195,19 +197,17 @@
}
public void replaceDebugValue(Value oldValue, Value newValue) {
- assert oldValue.hasLocalInfo();
- assert newValue.hasLocalInfo();
- assert newValue.getLocalInfo() == oldValue.getLocalInfo()
- : "Replacing debug values with inconsistent locals "
- + oldValue.getLocalInfo()
- + " and "
- + newValue.getLocalInfo()
- + ". This is likely a code transformation bug "
- + "that has not taken local information into account";
- boolean removed = debugValues.remove(oldValue);
- assert removed;
- if (removed && newValue.hasLocalInfo()) {
- newValue.addDebugLocalEnd(this);
+ if (debugValues.remove(oldValue)) {
+ assert newValue.getLocalInfo() == oldValue.getLocalInfo()
+ : "Replacing debug values with inconsistent locals "
+ + oldValue.getLocalInfo()
+ + " and "
+ + newValue.getLocalInfo()
+ + ". This is likely a code transformation bug "
+ + "that has not taken local information into account";
+ if (newValue.hasLocalInfo()) {
+ addDebugValue(newValue);
+ }
}
}
@@ -221,6 +221,12 @@
debugValues.clear();
}
+ public void moveDebugValue(Value value, Instruction target) {
+ assert debugValues.contains(value);
+ value.replaceDebugUser(this, target);
+ debugValues.remove(value);
+ }
+
public void removeDebugValue(Value value) {
assert value.hasLocalInfo();
if (debugValues != null) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 4b9b2d1..f49c1e3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -37,10 +37,14 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.IntList;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -149,13 +153,65 @@
private static class DebugData {
final DebugLocalInfo local;
- Set<Instruction> users = Sets.newIdentityHashSet();
+ Map<Instruction, DebugUse> users = new HashMap<>();
DebugData(DebugLocalInfo local) {
this.local = local;
}
}
+ // A debug-value user represents a point where the value is live, ends or starts.
+ // If a point is marked as both ending and starting then it is simply live, but we maintain
+ // the marker so as not to unintentionally end it if marked again.
+ // TODO(b/157466079): Clean/remove the use markers. With the current local construction in the
+ // IRBuilder, the only needed markers should be 'end' markers.
+ private enum DebugUse {
+ LIVE, START, END, LIVE_FINAL;
+
+ DebugUse start() {
+ switch (this) {
+ case LIVE:
+ case START:
+ return START;
+ case END:
+ case LIVE_FINAL:
+ return LIVE_FINAL;
+ default:
+ throw new Unreachable();
+ }
+ }
+
+ DebugUse end() {
+ switch (this) {
+ case LIVE:
+ case END:
+ return END;
+ case START:
+ case LIVE_FINAL:
+ return LIVE_FINAL;
+ default:
+ throw new Unreachable();
+ }
+ }
+
+ static DebugUse join(DebugUse a, DebugUse b) {
+ if (a == LIVE_FINAL || b == LIVE_FINAL) {
+ return LIVE_FINAL;
+ }
+ if (a == b) {
+ return a;
+ }
+ if (a == LIVE) {
+ return b;
+ }
+ if (b == LIVE) {
+ return a;
+ }
+ assert (a == START && b == END) || (a == END && b == START);
+ return LIVE_FINAL;
+ }
+ }
+
public static final int UNDEFINED_NUMBER = -1;
public static final Value UNDEFINED = new Value(UNDEFINED_NUMBER, TypeElement.getBottom(), null);
@@ -272,16 +328,62 @@
debugData = new DebugData(local);
}
- public Set<Instruction> getDebugLocalEnds() {
- return debugData == null
- ? Collections.emptySet()
- : Collections.unmodifiableSet(debugData.users);
+ public void clearLocalInfo() {
+ assert debugData.users.isEmpty();
+ debugData = null;
+ }
+
+ public boolean hasSameOrNoLocal(Value other) {
+ assert other != null;
+ return hasLocalInfo()
+ ? other.getLocalInfo() == this.getLocalInfo()
+ : !other.hasLocalInfo();
+ }
+
+ public List<Instruction> getDebugLocalStarts() {
+ if (debugData == null) {
+ return Collections.emptyList();
+ }
+ List<Instruction> starts = new ArrayList<>(debugData.users.size());
+ for (Entry<Instruction, DebugUse> entry : debugData.users.entrySet()) {
+ if (entry.getValue() == DebugUse.START) {
+ starts.add(entry.getKey());
+ }
+ }
+ return starts;
+ }
+
+ public List<Instruction> getDebugLocalEnds() {
+ if (debugData == null) {
+ return Collections.emptyList();
+ }
+ List<Instruction> ends = new ArrayList<>(debugData.users.size());
+ for (Entry<Instruction, DebugUse> entry : debugData.users.entrySet()) {
+ if (entry.getValue() == DebugUse.END) {
+ ends.add(entry.getKey());
+ }
+ }
+ return ends;
+ }
+
+ public void addDebugLocalStart(Instruction start) {
+ assert start != null;
+ debugData.users.put(start, markStart(debugData.users.get(start)));
+ }
+
+ private DebugUse markStart(DebugUse use) {
+ assert use != null;
+ return use == null ? DebugUse.START : use.start();
}
public void addDebugLocalEnd(Instruction end) {
assert end != null;
- debugData.users.add(end);
- end.addDebugValue(this);
+ debugData.users.put(end, markEnd(debugData.users.get(end)));
+ }
+
+ private DebugUse markEnd(DebugUse use) {
+ assert use != null;
+ return use == null ? DebugUse.END : use.end();
}
public void linkTo(Value other) {
@@ -391,7 +493,7 @@
}
public Set<Instruction> debugUsers() {
- return debugData == null ? null : Collections.unmodifiableSet(debugData.users);
+ return debugData == null ? null : Collections.unmodifiableSet(debugData.users.keySet());
}
public boolean hasAnyUsers() {
@@ -518,6 +620,11 @@
uniquePhiUsers = null;
}
+ public void addDebugUser(Instruction user) {
+ assert hasLocalInfo();
+ debugData.users.putIfAbsent(user, DebugUse.LIVE);
+ }
+
public boolean isUninitializedLocal() {
return definition != null && definition.isDebugLocalUninitialized();
}
@@ -579,7 +686,7 @@
user.replaceOperand(this, newValue);
}
if (debugData != null) {
- for (Instruction user : debugData.users) {
+ for (Entry<Instruction, DebugUse> user : debugData.users.entrySet()) {
replaceUserInDebugData(user, newValue);
}
debugData.users.clear();
@@ -641,10 +748,10 @@
}
}
if (debugData != null) {
- Iterator<Instruction> users = debugData.users.iterator();
+ Iterator<Entry<Instruction, DebugUse>> users = debugData.users.entrySet().iterator();
while (users.hasNext()) {
- Instruction user = users.next();
- if (selectedInstructions.contains(user)) {
+ Entry<Instruction, DebugUse> user = users.next();
+ if (selectedInstructions.contains(user.getKey())) {
replaceUserInDebugData(user, newValue);
users.remove();
}
@@ -652,18 +759,30 @@
}
}
- private void replaceUserInDebugData(Instruction user, Value newValue) {
- user.replaceDebugValue(this, newValue);
+ private void replaceUserInDebugData(Entry<Instruction, DebugUse> user, Value newValue) {
+ Instruction instruction = user.getKey();
+ DebugUse debugUse = user.getValue();
+ instruction.replaceDebugValue(this, newValue);
// If user is a DebugLocalRead and now has no debug values, we would like to remove it.
// However, replaceUserInDebugData() is called in contexts where the instruction list is being
// iterated, so we cannot remove user from the instruction list at this point.
+ if (newValue.hasLocalInfo()) {
+ DebugUse existing = newValue.debugData.users.get(instruction);
+ assert existing != null;
+ newValue.debugData.users.put(instruction, DebugUse.join(debugUse, existing));
+ }
}
public void replaceDebugUser(Instruction oldUser, Instruction newUser) {
- boolean removed = debugData.users.remove(oldUser);
- assert removed;
- if (removed) {
- addDebugLocalEnd(newUser);
+ DebugUse use = debugData.users.remove(oldUser);
+ if (use == DebugUse.START && newUser.outValue == this) {
+ // Register allocation requires that debug values are live at the entry to the instruction.
+ // Remove this debug use since it is starting at the instruction that defines it.
+ return;
+ }
+ if (use != null) {
+ newUser.addDebugValue(this);
+ debugData.users.put(newUser, use);
}
}
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 60fcec2..0dd98e2 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
@@ -2377,10 +2377,12 @@
// Add a use if this instruction is overwriting a previous value of the same local.
if (previousLocalValue != null && previousLocalValue.getLocalInfo() == ir.getLocalInfo()) {
assert ir.outValue() != null;
+ ir.addDebugValue(previousLocalValue);
previousLocalValue.addDebugLocalEnd(ir);
}
// Add reads of locals if any are pending.
for (Value value : debugLocalEnds) {
+ ir.addDebugValue(value);
value.addDebugLocalEnd(ir);
}
previousLocalValue = null;
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 3181f66..172a6b3 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
@@ -2232,6 +2232,7 @@
instruction.outValue().replaceUsers(inValue);
Value overwrittenLocal = instruction.removeDebugValue(localInfo);
if (overwrittenLocal != null) {
+ inValue.definition.addDebugValue(overwrittenLocal);
overwrittenLocal.addDebugLocalEnd(inValue.definition);
}
if (prevInstruction != null &&
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 7af08f9..7eddfcc 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
@@ -347,7 +347,6 @@
while (instructionIterator.hasNext()) {
Instruction instruction = instructionIterator.next();
if (!instructionIterator.hasNext()) {
- instruction.clearDebugValues();
break;
}