Add position table to LIR.
Bug: b/225838009
Change-Id: I9f7d162a0abf667e69bc5de702e478d4aeae6abc
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 e5bdb6d..1f87da5 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
@@ -13,6 +13,7 @@
import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.lightir.LIRBuilder;
public class DebugPosition extends Instruction {
@@ -98,4 +99,9 @@
public boolean isAllowedAfterThrowingInstruction() {
return true;
}
+
+ @Override
+ public void buildLIR(LIRBuilder<Value> builder) {
+ builder.addDebugPosition(getPosition());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java b/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java
index 6b56aa3..e91fd8a 100644
--- a/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java
@@ -26,7 +26,8 @@
index++;
}
LIRBuilder<Value> builder =
- new LIRBuilder<Value>(values::getInt).setMetadata(irCode.metadata());
+ new LIRBuilder<Value>(irCode.context().getReference(), values::getInt)
+ .setMetadata(irCode.metadata());
BasicBlockIterator blockIt = irCode.listIterator();
while (blockIt.hasNext()) {
BasicBlock block = blockIt.next();
@@ -35,6 +36,7 @@
InstructionIterator it = block.iterator();
while (it.hasNext()) {
Instruction instruction = it.next();
+ builder.setCurrentPosition(instruction.getPosition());
instruction.buildLIR(builder);
}
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java b/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java
index 6d00eec..75a372b 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java
@@ -15,16 +15,19 @@
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.ConstString;
+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.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.NumberGenerator;
import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.Position.SyntheticPosition;
import com.android.tools.r8.ir.code.Return;
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
+import com.android.tools.r8.lightir.LIRCode.PositionEntry;
import it.unimi.dsi.fastutil.ints.IntList;
import java.util.ArrayList;
import java.util.LinkedList;
@@ -35,7 +38,7 @@
private LIR2IRConverter() {}
public static IRCode translate(ProgramMethod method, LIRCode lirCode, AppView<?> appView) {
- Parser parser = new Parser(lirCode, appView);
+ Parser parser = new Parser(lirCode, method.getReference(), appView);
parser.parseArguments(method);
lirCode.forEach(view -> view.accept(parser));
return parser.getIRCode(method);
@@ -55,14 +58,37 @@
private final Value[] values;
private final LinkedList<BasicBlock> blocks = new LinkedList<>();
- private BasicBlock currentBlock;
+ private BasicBlock currentBlock = null;
private int nextInstructionIndex = 0;
- public Parser(LIRCode code, AppView<?> appView) {
+ private Position currentPosition;
+ private PositionEntry nextPositionEntry = null;
+ private int nextIndexInPositionsTable = 0;
+
+ public Parser(LIRCode code, DexMethod method, AppView<?> appView) {
super(code);
+ assert code.getPositionTable().length > 0;
+ assert code.getPositionTable()[0].fromInstructionIndex == 0;
this.appView = appView;
this.code = code;
- this.values = new Value[code.getArgumentCount() + code.getInstructionCount()];
+ values = new Value[code.getArgumentCount() + code.getInstructionCount()];
+ // Recreate the preamble position. This is active for arguments and code with no positions.
+ currentPosition = SyntheticPosition.builder().setLine(0).setMethod(method).build();
+ }
+
+ private void ensureCurrentPosition() {
+ if (nextPositionEntry != null
+ && nextPositionEntry.fromInstructionIndex < nextInstructionIndex) {
+ currentPosition = nextPositionEntry.position;
+ advanceNextPositionEntry();
+ }
+ }
+
+ private void advanceNextPositionEntry() {
+ nextPositionEntry =
+ nextIndexInPositionsTable < code.getPositionTable().length
+ ? code.getPositionTable()[nextIndexInPositionsTable++]
+ : null;
}
public void parseArguments(ProgramMethod method) {
@@ -75,6 +101,8 @@
addThisArgument(method.getHolderType());
}
method.getParameters().forEach(this::addArgument);
+ // Set up position state after adding arguments.
+ advanceNextPositionEntry();
}
public IRCode getIRCode(ProgramMethod method) {
@@ -132,8 +160,8 @@
}
private void addInstruction(Instruction instruction) {
- // TODO(b/225838009): Add correct position info.
- instruction.setPosition(Position.syntheticNone());
+ ensureCurrentPosition();
+ instruction.setPosition(currentPosition);
currentBlock.getInstructions().add(instruction);
instruction.setBlock(currentBlock);
++nextInstructionIndex;
@@ -200,5 +228,10 @@
public void onReturnVoid() {
addInstruction(new Return());
}
+
+ @Override
+ public void onDebugPosition() {
+ addInstruction(new DebugPosition());
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java b/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java
index 7d3505c..fe9e5b2 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java
@@ -3,13 +3,19 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.lightir;
+import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.ir.code.IRMetadata;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.Position.SyntheticPosition;
+import com.android.tools.r8.lightir.LIRCode.PositionEntry;
+import com.android.tools.r8.utils.ListUtils;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -28,13 +34,33 @@
private final LIRWriter writer = new LIRWriter(byteWriter);
private final Reference2IntMap<DexItem> constants;
private final ValueIndexGetter<V> valueIndexGetter;
+ private final List<PositionEntry> positionTable;
private int argumentCount = 0;
private int instructionCount = 0;
private IRMetadata metadata = null;
- public LIRBuilder(ValueIndexGetter<V> valueIndexGetter) {
+ private Position currentPosition;
+ private Position flushedPosition;
+
+ public LIRBuilder(DexMethod method, ValueIndexGetter<V> valueIndexGetter) {
constants = new Reference2IntOpenHashMap<>();
+ positionTable = new ArrayList<>();
this.valueIndexGetter = valueIndexGetter;
+ currentPosition = SyntheticPosition.builder().setLine(0).setMethod(method).build();
+ flushedPosition = currentPosition;
+ }
+
+ public LIRBuilder<V> setCurrentPosition(Position position) {
+ assert position != null;
+ assert position != Position.none();
+ currentPosition = position;
+ return this;
+ }
+
+ private void setPositionIndex(int instructionIndex, Position position) {
+ assert positionTable.isEmpty()
+ || ListUtils.last(positionTable).fromInstructionIndex < instructionIndex;
+ positionTable.add(new PositionEntry(instructionIndex, position));
}
private int getConstantIndex(DexItem item) {
@@ -82,20 +108,22 @@
return this;
}
- public LIRBuilder<V> addNop() {
- instructionCount++;
- writer.writeOneByteInstruction(LIROpcodes.NOP);
- return this;
+ private void addInstruction() {
+ if (!currentPosition.equals(flushedPosition)) {
+ setPositionIndex(instructionCount, currentPosition);
+ flushedPosition = currentPosition;
+ }
+ ++instructionCount;
}
public LIRBuilder<V> addConstNull() {
- instructionCount++;
+ addInstruction();
writer.writeOneByteInstruction(LIROpcodes.ACONST_NULL);
return this;
}
public LIRBuilder<V> addConstInt(int value) {
- instructionCount++;
+ addInstruction();
if (0 <= value && value <= 5) {
writer.writeOneByteInstruction(LIROpcodes.ICONST_0 + value);
} else {
@@ -106,17 +134,17 @@
}
public LIRBuilder<V> addConstString(DexString string) {
- instructionCount++;
+ addInstruction();
return writeConstantReferencingInstruction(LIROpcodes.LDC, string);
}
public LIRBuilder<V> addStaticGet(DexField field) {
- instructionCount++;
+ addInstruction();
return writeConstantReferencingInstruction(LIROpcodes.GETSTATIC, field);
}
public LIRBuilder<V> addInvokeInstruction(int opcode, DexMethod method, List<V> arguments) {
- instructionCount++;
+ addInstruction();
int argumentOprandSize = constantIndexSize(method);
int[] argumentIndexes = new int[arguments.size()];
int i = 0;
@@ -142,12 +170,19 @@
}
public LIRBuilder<V> addReturn(V value) {
- return this;
+ throw new Unimplemented();
}
public LIRBuilder<V> addReturnVoid() {
- instructionCount++;
- writer.writeInstruction(LIROpcodes.RETURN, 0);
+ addInstruction();
+ writer.writeOneByteInstruction(LIROpcodes.RETURN);
+ return this;
+ }
+
+ public LIRBuilder<V> addDebugPosition(Position position) {
+ assert currentPosition == position;
+ addInstruction();
+ writer.writeOneByteInstruction(LIROpcodes.DEBUGPOS);
return this;
}
@@ -157,6 +192,11 @@
DexItem[] constantTable = new DexItem[constantsCount];
constants.forEach((item, index) -> constantTable[index] = item);
return new LIRCode(
- metadata, constantTable, argumentCount, byteWriter.toByteArray(), instructionCount);
+ metadata,
+ constantTable,
+ positionTable.toArray(new PositionEntry[positionTable.size()]),
+ argumentCount,
+ byteWriter.toByteArray(),
+ instructionCount);
}
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRCode.java b/src/main/java/com/android/tools/r8/lightir/LIRCode.java
index d20c4d6..04dffd4 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRCode.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRCode.java
@@ -4,18 +4,33 @@
package com.android.tools.r8.lightir;
import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.ir.code.IRMetadata;
+import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.lightir.LIRBuilder.ValueIndexGetter;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
import java.util.Arrays;
public class LIRCode implements Iterable<LIRInstructionView> {
+ public static class PositionEntry {
+ final int fromInstructionIndex;
+ final Position position;
+
+ public PositionEntry(int fromInstructionIndex, Position position) {
+ this.fromInstructionIndex = fromInstructionIndex;
+ this.position = position;
+ }
+ }
+
private final IRMetadata metadata;
/** Constant pool of items. */
private final DexItem[] constants;
+ private final PositionEntry[] positionTable;
+
/** Full number of arguments (including receiver for non-static methods). */
private final int argumentCount;
@@ -25,19 +40,21 @@
/** Cached value for the number of logical instructions (including phis). */
private final int instructionCount;
- public static <V> LIRBuilder<V> builder(ValueIndexGetter<V> valueIndexGetter) {
- return new LIRBuilder<V>(valueIndexGetter);
+ public static <V> LIRBuilder<V> builder(DexMethod method, ValueIndexGetter<V> valueIndexGetter) {
+ return new LIRBuilder<V>(method, valueIndexGetter);
}
// Should be constructed using LIRBuilder.
LIRCode(
IRMetadata metadata,
DexItem[] constants,
+ PositionEntry[] positions,
int argumentCount,
byte[] instructions,
int instructionCount) {
this.metadata = metadata;
this.constants = constants;
+ this.positionTable = positions;
this.argumentCount = argumentCount;
this.instructions = instructions;
this.instructionCount = instructionCount;
@@ -63,6 +80,10 @@
return constants[index];
}
+ public PositionEntry[] getPositionTable() {
+ return positionTable;
+ }
+
@Override
public LIRIterator iterator() {
return new LIRIterator(new ByteArrayIterator(instructions));
@@ -71,26 +92,27 @@
@Override
public String toString() {
StringBuilder builder = new StringBuilder("LIRCode{");
- builder.append("constants:");
- StringUtils.append(builder, Arrays.asList(constants));
builder
- .append(", arguments:")
+ .append("args:")
.append(argumentCount)
- .append(", instructions(size:")
+ .append(", insn(num:")
+ .append(instructionCount)
+ .append(", size:")
.append(instructions.length)
.append("):{");
int index = 0;
for (LIRInstructionView view : this) {
- builder
- .append(LIROpcodes.toString(view.getOpcode()))
- .append("(size:")
- .append(1 + view.getRemainingOperandSizeInBytes())
- .append(")");
- if (index++ < instructionCount) {
- builder.append(",");
+ builder.append(LIROpcodes.toString(view.getOpcode()));
+ if (view.getRemainingOperandSizeInBytes() > 0) {
+ builder.append("(size:").append(1 + view.getRemainingOperandSizeInBytes()).append(")");
+ }
+ if (++index < instructionCount) {
+ builder.append(", ");
}
}
- builder.append("}}");
+ builder.append("}, pool(size:").append(constants.length).append("):");
+ StringUtils.append(builder, Arrays.asList(constants), ", ", BraceType.TUBORG);
+ builder.append("}");
return builder.toString();
}
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java b/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java
index 6d906ed..089469f 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java
@@ -13,12 +13,12 @@
public interface LIROpcodes {
static boolean isOneByteInstruction(int opcode) {
- assert opcode >= NOP;
- return opcode <= DCONST_1;
+ assert opcode >= ACONST_NULL;
+ return opcode <= DCONST_1 || opcode == RETURN || opcode == DEBUGPOS;
}
// Instructions maintaining the same opcode as defined in CF.
- int NOP = 0;
+ // int NOP = 0;
int ACONST_NULL = 1;
int ICONST_M1 = 2;
int ICONST_0 = 3;
@@ -182,11 +182,11 @@
int FCONST = 202;
int DCONST = 203;
int INVOKEDIRECT = 204;
+ int DEBUGPOS = 205;
static String toString(int opcode) {
switch (opcode) {
- case NOP:
- return "NOP";
+ // case NOP: return "NOP";
case ACONST_NULL:
return "ACONST_NULL";
case ICONST_M1:
@@ -485,6 +485,10 @@
return "FCONST";
case DCONST:
return "DCONST";
+ case INVOKEDIRECT:
+ return "INVOKEDIRECT";
+ case DEBUGPOS:
+ return "DEBUGPOS";
default:
throw new Unreachable("Unexpected LIR opcode: " + opcode);
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java b/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java
index a0afc52..317eeee 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java
@@ -54,6 +54,8 @@
public void onReturnVoid() {}
+ public void onDebugPosition() {}
+
private DexItem getConstantItem(int index) {
return code.getConstantItem(index);
}
@@ -99,6 +101,11 @@
onReturnVoid();
break;
}
+ case LIROpcodes.DEBUGPOS:
+ {
+ onDebugPosition();
+ break;
+ }
default:
throw new Unimplemented("No dispatch for opcode " + LIROpcodes.toString(view.getOpcode()));
}
diff --git a/src/test/java/com/android/tools/r8/lightir/LIRBasicCallbackTest.java b/src/test/java/com/android/tools/r8/lightir/LIRBasicCallbackTest.java
index 302f82f..1273bfd 100644
--- a/src/test/java/com/android/tools/r8/lightir/LIRBasicCallbackTest.java
+++ b/src/test/java/com/android/tools/r8/lightir/LIRBasicCallbackTest.java
@@ -12,7 +12,10 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.ir.code.IRMetadata;
+import com.android.tools.r8.references.Reference;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -31,8 +34,11 @@
@Test
public void test() throws Exception {
+ DexItemFactory factory = new DexItemFactory();
+ DexMethod method = factory.createMethod(Reference.methodFromDescriptor("LFoo;", "bar", "()V"));
LIRCode code =
LIRCode.builder(
+ method,
v -> {
throw new Unreachable();
})
diff --git a/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java b/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java
index 82c0d55..a169bfc 100644
--- a/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java
+++ b/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -45,6 +46,22 @@
public void testRoundtrip() throws Exception {
testForD8(parameters.getBackend())
.release()
+ .setMinApi(AndroidApiLevel.B)
+ .addProgramClasses(TestClass.class)
+ .addOptionsModification(
+ o -> {
+ o.testing.forceIRForCfToCfDesugar = true;
+ o.testing.roundtripThroughLIR = true;
+ })
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ @Test
+ public void testRoundtripDebug() throws Exception {
+ testForD8(parameters.getBackend())
+ .debug()
+ .setMinApi(AndroidApiLevel.B)
.addProgramClasses(TestClass.class)
.addOptionsModification(
o -> {