Support basic control flow in LIR.
Bug: b/225838009
Change-Id: Id2f6efa8a76f3fbcf7b82b67d674a29509de817a
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 76e9d15..182b9b4 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
@@ -177,7 +177,7 @@
}
@Override
- public void buildLIR(LIRBuilder<Value> builder) {
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
builder.addArgument(index, knownToBeBoolean);
}
}
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 14837ee..5715e54 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
@@ -17,6 +17,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.lightir.LIRBuilder;
public class ArrayLength extends Instruction {
@@ -151,4 +152,9 @@
public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
return false;
}
+
+ @Override
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
+ builder.addArrayLength(array());
+ }
}
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 afb3203..900fe91 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
@@ -182,7 +182,7 @@
}
@Override
- public void buildLIR(LIRBuilder<Value> builder) {
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
builder.addConstString(value);
}
}
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 1f87da5..651fb35 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
@@ -101,7 +101,7 @@
}
@Override
- public void buildLIR(LIRBuilder<Value> builder) {
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
builder.addDebugPosition(getPosition());
}
}
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 025dec0..b953a68 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
@@ -7,6 +7,7 @@
import com.android.tools.r8.cf.code.CfGoto;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.lightir.LIRBuilder;
import com.android.tools.r8.utils.CfgPrinter;
import java.util.List;
import java.util.ListIterator;
@@ -126,6 +127,11 @@
return true;
}
+ @Override
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
+ builder.addGoto(getTarget());
+ }
+
public static class Builder extends BuilderBase<Builder, Goto> {
private BasicBlock target;
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 9269104..12089ec 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
@@ -13,6 +13,7 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.lightir.LIRBuilder;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.InternalOutputMode;
@@ -289,4 +290,16 @@
assert inValues.get(0).outType() == inValues.get(1).outType();
builder.add(new CfIfCmp(type, ifType, builder.getLabel(getTrueTarget())), this);
}
+
+ @Override
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
+ ValueType ifType = inValues.get(0).outType();
+ if (inValues.size() == 1) {
+ builder.addIf(type, ifType, inValues.get(0), getTrueTarget());
+ return;
+ }
+ assert inValues.size() == 2;
+ assert inValues.get(0).outType() == inValues.get(1).outType();
+ builder.addIfCmp(type, ifType, inValues, getTrueTarget());
+ }
}
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 7aba8bd..c99ee11 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
@@ -1559,7 +1559,7 @@
return false;
}
- public void buildLIR(LIRBuilder<Value> builder) {
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
throw new Unimplemented("Missing impl for " + getClass().getSimpleName());
}
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 09f6e36..9203e93 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
@@ -218,7 +218,7 @@
}
@Override
- public void buildLIR(LIRBuilder<Value> builder) {
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
builder.addInvokeDirect(getInvokedMethod(), arguments());
}
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 1af5563..0cbf370 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
@@ -184,7 +184,7 @@
}
@Override
- public void buildLIR(LIRBuilder<Value> builder) {
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
builder.addInvokeVirtual(getInvokedMethod(), arguments());
}
}
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 935e9df..d8882e4 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
@@ -153,7 +153,7 @@
}
@Override
- public void buildLIR(LIRBuilder<Value> builder) {
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
if (hasReturnValue()) {
builder.addReturn(returnValue());
} else {
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 19986d0..afad1ff 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
@@ -300,7 +300,7 @@
}
@Override
- public void buildLIR(LIRBuilder<Value> builder) {
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
builder.addStaticGet(getField());
}
}
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 9967ae3..ab044e3 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
@@ -1656,10 +1656,14 @@
code = roundtripThroughLIR(code, feedback, bytecodeMetadataProvider, timing);
}
if (options.isGeneratingClassFiles()) {
+ timing.begin("IR->CF");
finalizeToCf(code, feedback, bytecodeMetadataProvider, timing);
+ timing.end();
} else {
assert options.isGeneratingDex();
+ timing.begin("IR->DEX");
finalizeToDex(code, feedback, bytecodeMetadataProvider, timing);
+ timing.end();
}
}
@@ -1668,8 +1672,12 @@
OptimizationFeedback feedback,
BytecodeMetadataProvider bytecodeMetadataProvider,
Timing timing) {
- LIRCode lirCode = IR2LIRConverter.translate(code);
+ timing.begin("IR->LIR");
+ LIRCode lirCode = IR2LIRConverter.translate(code, appView.dexItemFactory());
+ timing.end();
+ timing.begin("LIR->IR");
IRCode irCode = LIR2IRConverter.translate(code.context(), lirCode, appView);
+ timing.end();
return irCode;
}
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 e91fd8a..9908098 100644
--- a/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java
@@ -3,36 +3,70 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.lightir;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.BasicBlockIterator;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.lightir.LIRBuilder.BlockIndexGetter;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
+import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
public class IR2LIRConverter {
private IR2LIRConverter() {}
- public static LIRCode translate(IRCode irCode) {
+ public static LIRCode translate(IRCode irCode, DexItemFactory factory) {
+ irCode.traceBlocks();
+ Reference2IntMap<BasicBlock> blocks = new Reference2IntOpenHashMap<>();
Reference2IntMap<Value> values = new Reference2IntOpenHashMap<>();
- int index = 0;
- for (Instruction instruction : irCode.instructions()) {
- if (instruction.hasOutValue()) {
- values.put(instruction.outValue(), index);
+ int instructionIndex = 0;
+ int valueIndex = 0;
+ for (BasicBlock block : irCode.blocks) {
+ blocks.put(block, instructionIndex);
+ for (Phi phi : block.getPhis()) {
+ values.put(phi, valueIndex);
+ valueIndex++;
+ instructionIndex++;
}
- index++;
+ for (Instruction instruction : block.getInstructions()) {
+ if (instruction.hasOutValue()) {
+ values.put(instruction.outValue(), valueIndex);
+ }
+ valueIndex++;
+ if (!instruction.isArgument()) {
+ instructionIndex++;
+ }
+ }
}
- LIRBuilder<Value> builder =
- new LIRBuilder<Value>(irCode.context().getReference(), values::getInt)
+ LIRBuilder<Value, BasicBlock> builder =
+ new LIRBuilder<Value, BasicBlock>(
+ irCode.context().getReference(), values::getInt, blocks::getInt, factory)
.setMetadata(irCode.metadata());
BasicBlockIterator blockIt = irCode.listIterator();
while (blockIt.hasNext()) {
BasicBlock block = blockIt.next();
- // TODO(b/225838009): Support control flow.
- assert !block.hasPhis();
+ if (block.hasPhis()) {
+ // The block order of the predecessors may change, since the LIR does not encode the
+ // direct links, the block order is used to determine predecessor order.
+ int[] permutation = computePermutation(block.getPredecessors(), blocks::getInt);
+ Value[] operands = new Value[block.getPredecessors().size()];
+ for (Phi phi : block.getPhis()) {
+ permuteOperands(phi.getOperands(), permutation, operands);
+ builder.addPhi(phi.getType(), Arrays.asList(operands));
+ }
+ }
InstructionIterator it = block.iterator();
while (it.hasNext()) {
Instruction instruction = it.next();
@@ -42,4 +76,49 @@
}
return builder.build();
}
+
+ private static void permuteOperands(List<Value> operands, int[] permutation, Value[] output) {
+ for (int i = 0; i < operands.size(); i++) {
+ Value operand = operands.get(i);
+ output[permutation[i]] = operand;
+ }
+ }
+
+ private static int[] computePermutation(
+ List<BasicBlock> originalPredecessors, BlockIndexGetter<BasicBlock> blockIndexGetter) {
+ int predecessorCount = originalPredecessors.size();
+ // The final predecessor list is sorted by block order.
+ List<BasicBlock> sortedPredecessors = new ArrayList<>(originalPredecessors);
+ sortedPredecessors.sort(Comparator.comparingInt(blockIndexGetter::getBlockIndex));
+ // Since predecessors are not unique, build a map from each unique block to its set of indices.
+ Reference2ReferenceMap<BasicBlock, IntList> mapping =
+ new Reference2ReferenceOpenHashMap<>(predecessorCount);
+ for (int originalIndex = 0; originalIndex < predecessorCount; originalIndex++) {
+ BasicBlock predecessor = originalPredecessors.get(originalIndex);
+ mapping.computeIfAbsent(predecessor, k -> new IntArrayList(1)).add(originalIndex);
+ }
+ // Assign an original index to each sorted index.
+ int[] permutation = new int[predecessorCount];
+ for (int sortedIndex = 0; sortedIndex < predecessorCount; ) {
+ BasicBlock predecessor = sortedPredecessors.get(sortedIndex);
+ IntList originalIndices = mapping.get(predecessor);
+ assert verifySameBlock(sortedPredecessors, sortedIndex, originalIndices.size());
+ for (int originalIndex : originalIndices) {
+ permutation[originalIndex] = sortedIndex++;
+ }
+ }
+ return permutation;
+ }
+
+ private static boolean verifySameBlock(List<BasicBlock> predecessors, int startIndex, int size) {
+ if (size == 1) {
+ return true;
+ }
+ BasicBlock block = predecessors.get(startIndex);
+ for (int i = startIndex + 1; i < startIndex + size; i++) {
+ BasicBlock other = predecessors.get(i);
+ assert block == other;
+ }
+ return true;
+ }
}
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 75a372b..efccdb9 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java
@@ -12,15 +12,21 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.Argument;
+import com.android.tools.r8.ir.code.ArrayLength;
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.Goto;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.If;
+import com.android.tools.r8.ir.code.If.Type;
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.Phi;
+import com.android.tools.r8.ir.code.Phi.RegisterReadType;
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;
@@ -28,6 +34,9 @@
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.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import java.util.ArrayList;
import java.util.LinkedList;
@@ -39,6 +48,7 @@
public static IRCode translate(ProgramMethod method, LIRCode lirCode, AppView<?> appView) {
Parser parser = new Parser(lirCode, method.getReference(), appView);
+ parser.computeControlFlowGraph();
parser.parseArguments(method);
lirCode.forEach(view -> view.accept(parser));
return parser.getIRCode(method);
@@ -50,13 +60,15 @@
*/
private static class Parser extends LIRParsedInstructionCallback {
+ private static final int ENTRY_BLOCK_INDEX = -1;
+
private final AppView<?> appView;
private final LIRCode code;
private final NumberGenerator valueNumberGenerator = new NumberGenerator();
private final NumberGenerator basicBlockNumberGenerator = new NumberGenerator();
private final Value[] values;
- private final LinkedList<BasicBlock> blocks = new LinkedList<>();
+ private final Int2ReferenceMap<BasicBlock> blocks = new Int2ReferenceOpenHashMap<>();
private BasicBlock currentBlock = null;
private int nextInstructionIndex = 0;
@@ -76,9 +88,22 @@
currentPosition = SyntheticPosition.builder().setLine(0).setMethod(method).build();
}
+ private void closeCurrentBlock() {
+ currentBlock = null;
+ }
+
+ private void ensureCurrentBlock() {
+ BasicBlock nextBlock = blocks.get(nextInstructionIndex);
+ if (nextBlock != null) {
+ assert currentBlock != nextBlock;
+ currentBlock = nextBlock;
+ }
+ assert currentBlock != null;
+ }
+
private void ensureCurrentPosition() {
if (nextPositionEntry != null
- && nextPositionEntry.fromInstructionIndex < nextInstructionIndex) {
+ && nextPositionEntry.fromInstructionIndex <= nextInstructionIndex) {
currentPosition = nextPositionEntry.position;
advanceNextPositionEntry();
}
@@ -91,16 +116,44 @@
: null;
}
+ public void computeControlFlowGraph() {
+ // TODO(b/225838009): Since each basic block has a terminal instruction we can compute block
+ // structure lazily while iterating the instructions and avoid this additional pass.
+ // Always create an entry block as it cannot be targeted from code.
+ blocks.put(ENTRY_BLOCK_INDEX, createBlock());
+ for (LIRInstructionView view : code) {
+ int opcode = view.getOpcode();
+ if (LIROpcodes.isControlFlowInstruction(opcode)) {
+ // TODO(b/225838009): Deal with multi-target instructions.
+ int target = view.getNextBlockOperand();
+ blocks.computeIfAbsent(target, k -> createBlock());
+ if (LIROpcodes.isControlFlowInstructionWithFallthrough(opcode)) {
+ blocks.computeIfAbsent(view.getInstructionIndex() + 1, k -> createBlock());
+ }
+ }
+ }
+ }
+
+ public BasicBlock createBlock() {
+ BasicBlock block = new BasicBlock();
+ block.setNumber(basicBlockNumberGenerator.next());
+ // The LIR is in SSA with accurate phis. The blocks are thus filled by construction.
+ block.setFilled();
+ return block;
+ }
+
public void parseArguments(ProgramMethod method) {
- currentBlock = new BasicBlock();
- currentBlock.setNumber(basicBlockNumberGenerator.next());
+ currentBlock = blocks.get(ENTRY_BLOCK_INDEX);
boolean hasReceiverArgument = !method.getDefinition().isStatic();
assert code.getArgumentCount()
== method.getParameters().size() + (hasReceiverArgument ? 1 : 0);
if (hasReceiverArgument) {
addThisArgument(method.getHolderType());
}
- method.getParameters().forEach(this::addArgument);
+ int index = hasReceiverArgument ? 1 : 0;
+ for (DexType parameter : method.getParameters()) {
+ addArgument(parameter, index++);
+ }
// Set up position state after adding arguments.
advanceNextPositionEntry();
}
@@ -108,12 +161,17 @@
public IRCode getIRCode(ProgramMethod method) {
// TODO(b/225838009): Support control flow.
currentBlock.setFilled();
- blocks.add(currentBlock);
+ LinkedList<BasicBlock> blockList = new LinkedList<>();
+ IntList blockIndices = new IntArrayList(blocks.keySet());
+ blockIndices.sort(Integer::compare);
+ for (int i = 0; i < blockIndices.size(); i++) {
+ blockList.add(blocks.get(blockIndices.getInt(i)));
+ }
return new IRCode(
appView.options(),
method,
Position.syntheticNone(),
- blocks,
+ blockList,
valueNumberGenerator,
basicBlockNumberGenerator,
code.getMetadata(),
@@ -121,7 +179,13 @@
new MutableMethodConversionOptions(appView.options()));
}
- public Value getSsaValue(int index) {
+ public BasicBlock getBasicBlock(int instructionIndex) {
+ BasicBlock block = blocks.get(instructionIndex);
+ assert block != null;
+ return block;
+ }
+
+ public Value getValue(int index) {
Value value = values[index];
if (value == null) {
value = new Value(valueNumberGenerator.next(), TypeElement.getBottom(), null);
@@ -130,10 +194,10 @@
return value;
}
- public List<Value> getSsaValues(IntList indices) {
+ public List<Value> getValues(IntList indices) {
List<Value> arguments = new ArrayList<>(indices.size());
for (int i = 0; i < indices.size(); i++) {
- arguments.add(getSsaValue(indices.getInt(i)));
+ arguments.add(getValue(indices.getInt(i)));
}
return arguments;
}
@@ -145,7 +209,7 @@
public Value getOutValueForNextInstruction(TypeElement type) {
// TODO(b/225838009): Support debug locals.
DebugLocalInfo localInfo = null;
- int index = peekNextInstructionIndex();
+ int index = peekNextInstructionIndex() + code.getArgumentCount();
Value value = values[index];
if (value == null) {
value = new Value(valueNumberGenerator.next(), type, localInfo);
@@ -159,27 +223,59 @@
return value;
}
- private void addInstruction(Instruction instruction) {
+ public Phi getPhiForNextInstructionAndAdvanceState(TypeElement type) {
+ int index = peekNextInstructionIndex() + code.getArgumentCount();
+ // TODO(b/225838009): The phi constructor implicitly adds to the block, so we need to ensure
+ // the block. However, we must grab the index above. Find a way to clean this up so it is
+ // uniform with instructions.
+ advanceInstructionState();
+ // Creating the phi implicitly adds it to currentBlock.
+ DebugLocalInfo localInfo = null;
+ Phi phi =
+ new Phi(
+ valueNumberGenerator.next(), currentBlock, type, localInfo, RegisterReadType.NORMAL);
+ Value value = values[index];
+ if (value != null) {
+ // A fake ssa value has already been created, replace the users by the actual phi.
+ // TODO(b/225838009): We could consider encoding the value type as a bit in the value index
+ // and avoid the overhead of replacing users at phi-definition time.
+ assert !value.isPhi();
+ value.replaceUsers(phi);
+ }
+ values[index] = phi;
+ return phi;
+ }
+
+ private void advanceInstructionState() {
+ ensureCurrentBlock();
ensureCurrentPosition();
- instruction.setPosition(currentPosition);
- currentBlock.getInstructions().add(instruction);
- instruction.setBlock(currentBlock);
++nextInstructionIndex;
}
+ private void addInstruction(Instruction instruction) {
+ advanceInstructionState();
+ instruction.setPosition(currentPosition);
+ currentBlock.getInstructions().add(instruction);
+ instruction.setBlock(currentBlock);
+ }
+
private void addThisArgument(DexType type) {
- Argument argument = addArgument(type);
+ Argument argument = addArgument(type, 0);
argument.outValue().markAsThis();
}
- private Argument addArgument(DexType type) {
- Argument instruction =
- new Argument(
- getOutValueForNextInstruction(type.toTypeElement(appView)),
- peekNextInstructionIndex(),
- type.isBooleanType());
- addInstruction(instruction);
- return instruction;
+ private Argument addArgument(DexType type, int index) {
+ // Arguments are not included in the "instructions" so this does not call "addInstruction"
+ // which would otherwise advance the state.
+ Value dest = getValue(index);
+ dest.setType(type.toTypeElement(appView));
+ Argument argument = new Argument(dest, index, type.isBooleanType());
+ assert currentBlock != null;
+ assert currentPosition.isSyntheticPosition();
+ argument.setPosition(currentPosition);
+ currentBlock.getInstructions().add(argument);
+ argument.setBlock(currentBlock);
+ return argument;
}
@Override
@@ -195,10 +291,28 @@
}
@Override
+ public void onIf(Type ifKind, int blockIndex, int valueIndex) {
+ BasicBlock targetBlock = getBasicBlock(blockIndex);
+ Value value = getValue(valueIndex);
+ addInstruction(new If(ifKind, value));
+ currentBlock.link(targetBlock);
+ currentBlock.link(getBasicBlock(nextInstructionIndex));
+ closeCurrentBlock();
+ }
+
+ @Override
+ public void onGoto(int blockIndex) {
+ BasicBlock targetBlock = getBasicBlock(blockIndex);
+ addInstruction(new Goto());
+ currentBlock.link(targetBlock);
+ closeCurrentBlock();
+ }
+
+ @Override
public void onInvokeDirect(DexMethod target, IntList arguments) {
// TODO(b/225838009): Maintain is-interface bit.
Value dest = getInvokeInstructionOutputValue(target);
- List<Value> ssaArgumentValues = getSsaValues(arguments);
+ List<Value> ssaArgumentValues = getValues(arguments);
InvokeDirect instruction = new InvokeDirect(target, dest, ssaArgumentValues);
addInstruction(instruction);
}
@@ -207,7 +321,7 @@
public void onInvokeVirtual(DexMethod target, IntList arguments) {
// TODO(b/225838009): Maintain is-interface bit.
Value dest = getInvokeInstructionOutputValue(target);
- List<Value> ssaArgumentValues = getSsaValues(arguments);
+ List<Value> ssaArgumentValues = getValues(arguments);
InvokeVirtual instruction = new InvokeVirtual(target, dest, ssaArgumentValues);
addInstruction(instruction);
}
@@ -230,8 +344,25 @@
}
@Override
+ public void onArrayLength(int arrayIndex) {
+ Value dest = getOutValueForNextInstruction(TypeElement.getInt());
+ Value arrayValue = getValue(arrayIndex);
+ addInstruction(new ArrayLength(dest, arrayValue));
+ }
+
+ @Override
public void onDebugPosition() {
addInstruction(new DebugPosition());
}
+
+ @Override
+ public void onPhi(DexType type, IntList operands) {
+ Phi phi = getPhiForNextInstructionAndAdvanceState(type.toTypeElement(appView));
+ List<Value> values = new ArrayList<>(operands.size());
+ for (int i = 0; i < operands.size(); i++) {
+ values.add(getValue(operands.getInt(i)));
+ }
+ phi.addOperands(values);
+ }
}
}
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 fe9e5b2..409f02e 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java
@@ -4,18 +4,25 @@
package com.android.tools.r8.lightir;
import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.IRMetadata;
+import com.android.tools.r8.ir.code.If.Type;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Position.SyntheticPosition;
+import com.android.tools.r8.ir.code.ValueType;
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.Collections;
import java.util.List;
/**
@@ -23,17 +30,27 @@
*
* @param <V> Type of SSA values. This is abstract to ensure that value internals are not used in
* building.
+ * @param <B> Type of basic blocks. This is abstract to ensure that basic block internals are not
+ * used in building.
*/
-public class LIRBuilder<V> {
+public class LIRBuilder<V, B> {
+ // Abstraction for the only accessible properties of an SSA value.
public interface ValueIndexGetter<V> {
int getValueIndex(V value);
}
+ // Abstraction for the only accessible properties of a basic block.
+ public interface BlockIndexGetter<B> {
+ int getBlockIndex(B block);
+ }
+
+ private final DexItemFactory factory;
private final ByteArrayWriter byteWriter = new ByteArrayWriter();
private final LIRWriter writer = new LIRWriter(byteWriter);
private final Reference2IntMap<DexItem> constants;
private final ValueIndexGetter<V> valueIndexGetter;
+ private final BlockIndexGetter<B> blockIndexGetter;
private final List<PositionEntry> positionTable;
private int argumentCount = 0;
private int instructionCount = 0;
@@ -42,15 +59,26 @@
private Position currentPosition;
private Position flushedPosition;
- public LIRBuilder(DexMethod method, ValueIndexGetter<V> valueIndexGetter) {
+ // TODO(b/225838009): Reconsider this fixed space as the operand count for phis is much larger.
+ // Pre-allocated space for caching value indexes when writing instructions.
+ private static final int MAX_VALUE_COUNT = 10;
+ private int[] valueIndexBuffer = new int[MAX_VALUE_COUNT];
+
+ public LIRBuilder(
+ DexMethod method,
+ ValueIndexGetter<V> valueIndexGetter,
+ BlockIndexGetter<B> blockIndexGetter,
+ DexItemFactory factory) {
+ this.factory = factory;
constants = new Reference2IntOpenHashMap<>();
positionTable = new ArrayList<>();
this.valueIndexGetter = valueIndexGetter;
+ this.blockIndexGetter = blockIndexGetter;
currentPosition = SyntheticPosition.builder().setLine(0).setMethod(method).build();
flushedPosition = currentPosition;
}
- public LIRBuilder<V> setCurrentPosition(Position position) {
+ public LIRBuilder<V, B> setCurrentPosition(Position position) {
assert position != null;
assert position != Position.none();
currentPosition = position;
@@ -75,6 +103,7 @@
private void writeConstantIndex(DexItem item) {
int index = getConstantIndex(item);
+ assert constantIndexSize(item) == ByteUtils.intEncodingSize(index);
ByteUtils.writeEncodedInt(index, writer::writeOperand);
}
@@ -90,25 +119,31 @@
ByteUtils.writeEncodedInt(index, writer::writeOperand);
}
- public LIRBuilder<V> setMetadata(IRMetadata metadata) {
+ private int getBlockIndex(B block) {
+ return blockIndexGetter.getBlockIndex(block);
+ }
+
+ private int blockIndexSize(int index) {
+ return ByteUtils.intEncodingSize(index);
+ }
+
+ private void writeBlockIndex(int index) {
+ ByteUtils.writeEncodedInt(index, writer::writeOperand);
+ }
+
+ public LIRBuilder<V, B> setMetadata(IRMetadata metadata) {
this.metadata = metadata;
return this;
}
- public LIRBuilder<V> writeConstantReferencingInstruction(int opcode, DexItem item) {
- writer.writeInstruction(opcode, constantIndexSize(item));
- writeConstantIndex(item);
- return this;
- }
-
- public LIRBuilder<V> addArgument(int index, boolean knownToBeBoolean) {
+ public LIRBuilder<V, B> addArgument(int index, boolean knownToBeBoolean) {
// Arguments are implicitly given by method descriptor and not an actual instruction.
assert argumentCount == index;
argumentCount++;
return this;
}
- private void addInstruction() {
+ private void advanceInstructionState() {
if (!currentPosition.equals(flushedPosition)) {
setPositionIndex(instructionCount, currentPosition);
flushedPosition = currentPosition;
@@ -116,76 +151,185 @@
++instructionCount;
}
- public LIRBuilder<V> addConstNull() {
- addInstruction();
- writer.writeOneByteInstruction(LIROpcodes.ACONST_NULL);
+ private LIRBuilder<V, B> addNoOperandInstruction(int opcode) {
+ advanceInstructionState();
+ writer.writeOneByteInstruction(opcode);
return this;
}
- public LIRBuilder<V> addConstInt(int value) {
- addInstruction();
+ private LIRBuilder<V, B> addOneItemInstruction(int opcode, DexItem item) {
+ return addInstructionTemplate(opcode, Collections.singletonList(item), Collections.emptyList());
+ }
+
+ private LIRBuilder<V, B> addOneValueInstruction(int opcode, V value) {
+ return addInstructionTemplate(
+ opcode, Collections.emptyList(), Collections.singletonList(value));
+ }
+
+ private LIRBuilder<V, B> addInstructionTemplate(int opcode, List<DexItem> items, List<V> values) {
+ assert values.size() < MAX_VALUE_COUNT;
+ advanceInstructionState();
+ int operandSize = 0;
+ for (DexItem item : items) {
+ operandSize += constantIndexSize(item);
+ }
+ for (int i = 0; i < values.size(); i++) {
+ V value = values.get(i);
+ int valueIndex = getValueIndex(value);
+ operandSize += valueIndexSize(valueIndex);
+ valueIndexBuffer[i] = valueIndex;
+ }
+ writer.writeInstruction(opcode, operandSize);
+ for (DexItem item : items) {
+ writeConstantIndex(item);
+ }
+ for (int i = 0; i < values.size(); i++) {
+ writeValueIndex(valueIndexBuffer[i]);
+ }
+ return this;
+ }
+
+ public LIRBuilder<V, B> addConstNull() {
+ return addNoOperandInstruction(LIROpcodes.ACONST_NULL);
+ }
+
+ public LIRBuilder<V, B> addConstInt(int value) {
if (0 <= value && value <= 5) {
- writer.writeOneByteInstruction(LIROpcodes.ICONST_0 + value);
+ addNoOperandInstruction(LIROpcodes.ICONST_0 + value);
} else {
+ advanceInstructionState();
writer.writeInstruction(LIROpcodes.ICONST, ByteUtils.intEncodingSize(value));
ByteUtils.writeEncodedInt(value, writer::writeOperand);
}
return this;
}
- public LIRBuilder<V> addConstString(DexString string) {
- addInstruction();
- return writeConstantReferencingInstruction(LIROpcodes.LDC, string);
+ public LIRBuilder<V, B> addConstString(DexString string) {
+ return addOneItemInstruction(LIROpcodes.LDC, string);
}
- public LIRBuilder<V> addStaticGet(DexField field) {
- addInstruction();
- return writeConstantReferencingInstruction(LIROpcodes.GETSTATIC, field);
+ public LIRBuilder<V, B> addArrayLength(V array) {
+ return addOneValueInstruction(LIROpcodes.ARRAYLENGTH, array);
}
- public LIRBuilder<V> addInvokeInstruction(int opcode, DexMethod method, List<V> arguments) {
- addInstruction();
- int argumentOprandSize = constantIndexSize(method);
- int[] argumentIndexes = new int[arguments.size()];
- int i = 0;
- for (V argument : arguments) {
- int argumentIndex = getValueIndex(argument);
- argumentIndexes[i++] = argumentIndex;
- argumentOprandSize += valueIndexSize(argumentIndex);
- }
- writer.writeInstruction(opcode, argumentOprandSize);
- writeConstantIndex(method);
- for (int argumentIndex : argumentIndexes) {
- writeValueIndex(argumentIndex);
- }
- return this;
+ public LIRBuilder<V, B> addStaticGet(DexField field) {
+ return addOneItemInstruction(LIROpcodes.GETSTATIC, field);
}
- public LIRBuilder<V> addInvokeDirect(DexMethod method, List<V> arguments) {
+ public LIRBuilder<V, B> addInvokeInstruction(int opcode, DexMethod method, List<V> arguments) {
+ return addInstructionTemplate(opcode, Collections.singletonList(method), arguments);
+ }
+
+ public LIRBuilder<V, B> addInvokeDirect(DexMethod method, List<V> arguments) {
return addInvokeInstruction(LIROpcodes.INVOKEDIRECT, method, arguments);
}
- public LIRBuilder<V> addInvokeVirtual(DexMethod method, List<V> arguments) {
+ public LIRBuilder<V, B> addInvokeVirtual(DexMethod method, List<V> arguments) {
return addInvokeInstruction(LIROpcodes.INVOKEVIRTUAL, method, arguments);
}
- public LIRBuilder<V> addReturn(V value) {
+ public LIRBuilder<V, B> addReturn(V value) {
throw new Unimplemented();
}
- public LIRBuilder<V> addReturnVoid() {
- addInstruction();
- writer.writeOneByteInstruction(LIROpcodes.RETURN);
+ public LIRBuilder<V, B> addReturnVoid() {
+ return addNoOperandInstruction(LIROpcodes.RETURN);
+ }
+
+ public LIRBuilder<V, B> addDebugPosition(Position position) {
+ assert currentPosition == position;
+ return addNoOperandInstruction(LIROpcodes.DEBUGPOS);
+ }
+
+ public LIRBuilder<V, B> addGoto(B target) {
+ int targetIndex = getBlockIndex(target);
+ int operandSize = blockIndexSize(targetIndex);
+ advanceInstructionState();
+ writer.writeInstruction(LIROpcodes.GOTO, operandSize);
+ writeBlockIndex(targetIndex);
return this;
}
- public LIRBuilder<V> addDebugPosition(Position position) {
- assert currentPosition == position;
- addInstruction();
- writer.writeOneByteInstruction(LIROpcodes.DEBUGPOS);
+ public LIRBuilder<V, B> addIf(Type ifKind, ValueType valueType, V value, B trueTarget) {
+ int opcode;
+ switch (ifKind) {
+ case EQ:
+ opcode = valueType.isObject() ? LIROpcodes.IFNULL : LIROpcodes.IFEQ;
+ break;
+ case GE:
+ opcode = LIROpcodes.IFGE;
+ break;
+ case GT:
+ opcode = LIROpcodes.IFGT;
+ break;
+ case LE:
+ opcode = LIROpcodes.IFLE;
+ break;
+ case LT:
+ opcode = LIROpcodes.IFLT;
+ break;
+ case NE:
+ opcode = valueType.isObject() ? LIROpcodes.IFNONNULL : LIROpcodes.IFNE;
+ break;
+ default:
+ throw new Unreachable("Unexpected if kind: " + ifKind);
+ }
+ int targetIndex = getBlockIndex(trueTarget);
+ int valueIndex = getValueIndex(value);
+ int operandSize = blockIndexSize(targetIndex) + valueIndexSize(valueIndex);
+ advanceInstructionState();
+ writer.writeInstruction(opcode, operandSize);
+ writeBlockIndex(targetIndex);
+ writeValueIndex(valueIndex);
return this;
}
+ public LIRBuilder<V, B> addIfCmp(
+ Type ifKind, ValueType valueType, List<V> inValues, B trueTarget) {
+ int opcode;
+ switch (ifKind) {
+ case EQ:
+ opcode = valueType.isObject() ? LIROpcodes.IF_ACMPEQ : LIROpcodes.IF_ICMPEQ;
+ break;
+ case GE:
+ opcode = LIROpcodes.IF_ICMPGE;
+ break;
+ case GT:
+ opcode = LIROpcodes.IF_ICMPGT;
+ break;
+ case LE:
+ opcode = LIROpcodes.IF_ICMPLE;
+ break;
+ case LT:
+ opcode = LIROpcodes.IF_ICMPLT;
+ break;
+ case NE:
+ opcode = valueType.isObject() ? LIROpcodes.IF_ACMPNE : LIROpcodes.IF_ICMPNE;
+ break;
+ default:
+ throw new Unreachable("Unexpected if kind " + ifKind);
+ }
+ int targetIndex = getBlockIndex(trueTarget);
+ int valueOneIndex = getValueIndex(inValues.get(0));
+ int valueTwoIndex = getValueIndex(inValues.get(1));
+ int operandSize =
+ blockIndexSize(targetIndex) + valueIndexSize(valueOneIndex) + valueIndexSize(valueTwoIndex);
+ advanceInstructionState();
+ writer.writeInstruction(opcode, operandSize);
+ writeBlockIndex(targetIndex);
+ writeValueIndex(valueOneIndex);
+ writeValueIndex(valueTwoIndex);
+ return this;
+ }
+
+ public LIRBuilder<V, B> addPhi(TypeElement type, List<V> operands) {
+ DexType dexType =
+ type.isPrimitiveType()
+ ? type.asPrimitiveType().toDexType(factory)
+ : type.asReferenceType().toDexType(factory);
+ return addInstructionTemplate(LIROpcodes.PHI, Collections.singletonList(dexType), operands);
+ }
+
public LIRCode build() {
assert metadata != null;
int constantsCount = constants.size();
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 04dffd4..858de2a 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRCode.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRCode.java
@@ -4,9 +4,11 @@
package com.android.tools.r8.lightir;
import com.android.tools.r8.graph.DexItem;
+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.ir.code.Position;
+import com.android.tools.r8.lightir.LIRBuilder.BlockIndexGetter;
import com.android.tools.r8.lightir.LIRBuilder.ValueIndexGetter;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.StringUtils.BraceType;
@@ -34,14 +36,18 @@
/** Full number of arguments (including receiver for non-static methods). */
private final int argumentCount;
- /** Byte encoding of the instructions (including phis). */
+ /** Byte encoding of the instructions (excludes arguments, includes phis). */
private final byte[] instructions;
- /** Cached value for the number of logical instructions (including phis). */
+ /** Cached value for the number of logical instructions (excludes arguments, includes phis). */
private final int instructionCount;
- public static <V> LIRBuilder<V> builder(DexMethod method, ValueIndexGetter<V> valueIndexGetter) {
- return new LIRBuilder<V>(method, valueIndexGetter);
+ public static <V, B> LIRBuilder<V, B> builder(
+ DexMethod method,
+ ValueIndexGetter<V> valueIndexGetter,
+ BlockIndexGetter<B> blockIndexGetter,
+ DexItemFactory factory) {
+ return new LIRBuilder<V, B>(method, valueIndexGetter, blockIndexGetter, factory);
}
// Should be constructed using LIRBuilder.
@@ -102,6 +108,7 @@
.append("):{");
int index = 0;
for (LIRInstructionView view : this) {
+ builder.append(index).append(':');
builder.append(LIROpcodes.toString(view.getOpcode()));
if (view.getRemainingOperandSizeInBytes() > 0) {
builder.append("(size:").append(1 + view.getRemainingOperandSizeInBytes()).append(")");
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRInstructionView.java b/src/main/java/com/android/tools/r8/lightir/LIRInstructionView.java
index f42abdb..e67e6db 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRInstructionView.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRInstructionView.java
@@ -15,6 +15,9 @@
/** Convenience method to forward control to a callback. */
void accept(LIRInstructionCallback eventCallback);
+ /** Get the instruction index. */
+ int getInstructionIndex();
+
/** The opcode of the instruction (See {@code LIROpcodes} for values). */
int getOpcode();
@@ -29,4 +32,7 @@
/** Get the next operand as an SSA value index. */
int getNextValueOperand();
+
+ /** Get the next operand as a basic-block index. */
+ int getNextBlockOperand();
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRIterator.java b/src/main/java/com/android/tools/r8/lightir/LIRIterator.java
index 9659aa5..fabe52e 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRIterator.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRIterator.java
@@ -16,10 +16,14 @@
private final ByteIterator iterator;
+ // State of the byte offsets into the iterator.
private int currentByteIndex = 0;
- private int currentOpcode = -1;
private int endOfCurrentInstruction = 0;
+ // State of the instruction interpretation.
+ private int currentInstructionIndex = -1;
+ private int currentOpcode = -1;
+
public LIRIterator(ByteIterator iterator) {
this.iterator = iterator;
}
@@ -39,6 +43,7 @@
@Override
public LIRInstructionView next() {
skipRemainingOperands();
+ ++currentInstructionIndex;
currentOpcode = u1();
if (LIROpcodes.isOneByteInstruction(currentOpcode)) {
endOfCurrentInstruction = currentByteIndex;
@@ -57,6 +62,11 @@
}
@Override
+ public int getInstructionIndex() {
+ return currentInstructionIndex;
+ }
+
+ @Override
public int getOpcode() {
return currentOpcode;
}
@@ -83,6 +93,12 @@
return u4();
}
+ @Override
+ public int getNextBlockOperand() {
+ assert hasMoreOperands();
+ return u4();
+ }
+
private void skip(int i) {
currentByteIndex += i;
iterator.skip(i);
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 089469f..7e0c520 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java
@@ -17,6 +17,25 @@
return opcode <= DCONST_1 || opcode == RETURN || opcode == DEBUGPOS;
}
+ static boolean isControlFlowInstruction(int opcode) {
+ return opcode == GOTO || isControlFlowInstructionWithFallthrough(opcode);
+ }
+
+ static boolean isControlFlowInstructionWithFallthrough(int opcode) {
+ switch (opcode) {
+ case IFEQ:
+ case IFNE:
+ case IFLT:
+ case IFGE:
+ case IFGT:
+ case IFLE:
+ // TODO(b/225838009): put in the rest!
+ return true;
+ default:
+ return false;
+ }
+ }
+
// Instructions maintaining the same opcode as defined in CF.
// int NOP = 0;
int ACONST_NULL = 1;
@@ -183,6 +202,7 @@
int DCONST = 203;
int INVOKEDIRECT = 204;
int DEBUGPOS = 205;
+ int PHI = 206;
static String toString(int opcode) {
switch (opcode) {
@@ -489,6 +509,8 @@
return "INVOKEDIRECT";
case DEBUGPOS:
return "DEBUGPOS";
+ case PHI:
+ return "PHI";
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 317eeee..8f32cf5 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java
@@ -8,6 +8,8 @@
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.graph.DexType;
+import com.android.tools.r8.ir.code.If;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
@@ -34,6 +36,10 @@
public void onConstString(DexString string) {}
+ public void onIf(If.Type ifKind, int blockIndex, int valueIndex) {}
+
+ public void onGoto(int blockIndex) {}
+
public void onInvokeMethodInstruction(DexMethod method, IntList arguments) {}
public void onInvokeDirect(DexMethod method, IntList arguments) {
@@ -54,8 +60,12 @@
public void onReturnVoid() {}
+ public void onArrayLength(int arrayIndex) {}
+
public void onDebugPosition() {}
+ public void onPhi(DexType type, IntList operands) {}
+
private DexItem getConstantItem(int index) {
return code.getConstantItem(index);
}
@@ -76,6 +86,19 @@
}
break;
}
+ case LIROpcodes.IFNE:
+ {
+ int blockIndex = view.getNextBlockOperand();
+ int valueIndex = view.getNextValueOperand();
+ onIf(If.Type.NE, blockIndex, valueIndex);
+ break;
+ }
+ case LIROpcodes.GOTO:
+ {
+ int blockIndex = view.getNextBlockOperand();
+ onGoto(blockIndex);
+ break;
+ }
case LIROpcodes.INVOKEDIRECT:
{
DexMethod target = getInvokeInstructionTarget(view);
@@ -101,11 +124,26 @@
onReturnVoid();
break;
}
+ case LIROpcodes.ARRAYLENGTH:
+ {
+ onArrayLength(view.getNextValueOperand());
+ break;
+ }
case LIROpcodes.DEBUGPOS:
{
onDebugPosition();
break;
}
+ case LIROpcodes.PHI:
+ {
+ DexType type = (DexType) getConstantItem(view.getNextConstantOperand());
+ IntList operands = new IntArrayList();
+ while (view.hasMoreOperands()) {
+ operands.add(view.getNextValueOperand());
+ }
+ onPhi(type, operands);
+ 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 1273bfd..c8e9e8a 100644
--- a/src/test/java/com/android/tools/r8/lightir/LIRBasicCallbackTest.java
+++ b/src/test/java/com/android/tools/r8/lightir/LIRBasicCallbackTest.java
@@ -41,7 +41,11 @@
method,
v -> {
throw new Unreachable();
- })
+ },
+ b -> {
+ throw new Unreachable();
+ },
+ factory)
.setMetadata(IRMetadata.unknown())
.addConstNull()
.addConstInt(42)
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 a169bfc..3d0b390 100644
--- a/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java
+++ b/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java
@@ -18,7 +18,7 @@
static class TestClass {
public static void main(String[] args) {
- System.out.println("Hello, world!");
+ System.out.println(args.length == 0 ? "Hello, world!" : "Oh no!");
}
}