Reland "[LIR] Strategy for indirect phi references."
Bug: b/225838009
Change-Id: Idcccf97ea08e95dbe0c22691fd6a7e2a03c4c7ce
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 c21f00e..0050541 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
@@ -65,7 +65,8 @@
import com.android.tools.r8.lightir.Lir2IRConverter;
import com.android.tools.r8.lightir.LirCode;
import com.android.tools.r8.lightir.LirStrategy;
-import com.android.tools.r8.lightir.LirStrategy.PhiInInstructionsStrategy;
+import com.android.tools.r8.lightir.LirStrategy.ExternalPhisStrategy;
+import com.android.tools.r8.lightir.PhiInInstructionsStrategy;
import com.android.tools.r8.naming.IdentifierNameStringMarker;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorIROptimizer;
import com.android.tools.r8.position.MethodPosition;
@@ -1055,12 +1056,18 @@
OptimizationFeedback feedback,
BytecodeMetadataProvider bytecodeMetadataProvider,
Timing timing) {
- LirStrategy<Value, Integer> strategy = new PhiInInstructionsStrategy();
- timing.begin("IR->LIR");
- LirCode<Integer> lirCode =
+ IRCode round1 = doRoundtripWithStrategy(code, new ExternalPhisStrategy(), "indirect phis");
+ IRCode round2 = doRoundtripWithStrategy(round1, new PhiInInstructionsStrategy(), "inline phis");
+ return round2;
+ }
+
+ private <EV, S extends LirStrategy<Value, EV>> IRCode doRoundtripWithStrategy(
+ IRCode code, S strategy, String name) {
+ timing.begin("IR->LIR (" + name + ")");
+ LirCode<EV> lirCode =
IR2LirConverter.translate(code, strategy.getEncodingStrategy(), appView.dexItemFactory());
timing.end();
- timing.begin("LIR->IR");
+ timing.begin("LIR->IR (" + name + ")");
IRCode irCode =
Lir2IRConverter.translate(
code.context(), lirCode, strategy.getDecodingStrategy(lirCode), appView);
diff --git a/src/main/java/com/android/tools/r8/lightir/ByteUtils.java b/src/main/java/com/android/tools/r8/lightir/ByteUtils.java
index f719f11..5d34483 100644
--- a/src/main/java/com/android/tools/r8/lightir/ByteUtils.java
+++ b/src/main/java/com/android/tools/r8/lightir/ByteUtils.java
@@ -51,6 +51,15 @@
return (value >= 0) && (value <= 0xFFFF);
}
+ private static int truncateToU2(int value) {
+ return value & 0xFFFF;
+ }
+
+ public static int ensureU2(int value) {
+ assert isU2(value);
+ return truncateToU2(value);
+ }
+
public static int unsetBitAtIndex(int value, int index) {
return value & ~(1 << (index - 1));
}
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 21af715..ee53ceb 100644
--- a/src/main/java/com/android/tools/r8/lightir/IR2LirConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/IR2LirConverter.java
@@ -48,6 +48,11 @@
strategy.defineBlock(block, blockIndex);
}
+ private boolean recordPhi(Phi phi, int valueIndex) {
+ recordValue(phi, valueIndex);
+ return strategy.isPhiInlineInstruction();
+ }
+
private void recordValue(Value value, int valueIndex) {
EV encodedValue = strategy.defineValue(value, valueIndex);
if (value.hasLocalInfo()) {
@@ -69,16 +74,8 @@
BasicBlockIterator blockIt = irCode.listIterator();
while (blockIt.hasNext()) {
BasicBlock block = blockIt.next();
- 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(), strategy::getBlockIndex);
- Value[] operands = new Value[block.getPredecessors().size()];
- for (Phi phi : block.getPhis()) {
- permuteOperands(phi.getOperands(), permutation, operands);
- builder.addPhi(phi.getType(), Arrays.asList(operands));
- currentValueIndex++;
- }
+ if (strategy.isPhiInlineInstruction()) {
+ currentValueIndex += computePhis(block);
}
if (block.hasCatchHandlers()) {
CatchHandlers<BasicBlock> handlers = block.getCatchHandlers();
@@ -112,6 +109,25 @@
}
assert builder.verifyCurrentValueIndex(currentValueIndex);
}
+ if (!strategy.isPhiInlineInstruction()) {
+ irCode.listIterator().forEachRemaining(this::computePhis);
+ }
+ }
+
+ private int computePhis(BasicBlock block) {
+ int valuesOffset = 0;
+ 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(), strategy::getBlockIndex);
+ Value[] operands = new Value[block.getPredecessors().size()];
+ for (Phi phi : block.getPhis()) {
+ permuteOperands(phi.getOperands(), permutation, operands);
+ builder.addPhi(phi.getType(), Arrays.asList(operands));
+ valuesOffset++;
+ }
+ }
+ return valuesOffset;
}
private void computeBlockAndValueTables() {
@@ -120,9 +136,10 @@
for (BasicBlock block : irCode.blocks) {
recordBlock(block, instructionIndex);
for (Phi phi : block.getPhis()) {
- recordValue(phi, valueIndex);
- valueIndex++;
- instructionIndex++;
+ if (recordPhi(phi, valueIndex)) {
+ valueIndex++;
+ instructionIndex++;
+ }
}
for (Instruction instruction : block.getInstructions()) {
if (instruction.hasOutValue()) {
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 b287683..e09e9f5 100644
--- a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.lightir;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
@@ -58,6 +57,7 @@
AppView<?> appView) {
Parser<EV> parser = new Parser<>(lirCode, method.getReference(), appView, strategy);
parser.parseArguments(method);
+ parser.ensureDebugInfo();
lirCode.forEach(view -> view.accept(parser));
return parser.getIRCode(method);
}
@@ -157,6 +157,21 @@
advanceNextPositionEntry();
}
+ public void ensureDebugInfo() {
+ if (code.getDebugLocalInfoTable() == null) {
+ return;
+ }
+ code.getDebugLocalInfoTable()
+ .forEachLocalDefinition(
+ (encodedValue, localInfo) -> {
+ Value value = getValue(encodedValue);
+ if (!value.hasLocalInfo()) {
+ value.setLocalInfo(localInfo);
+ }
+ assert value.getLocalInfo() == localInfo;
+ });
+ }
+
// TODO(b/270398965): Replace LinkedList.
@SuppressWarnings("JdkObsolete")
public IRCode getIRCode(ProgramMethod method) {
@@ -194,7 +209,7 @@
}
public Value getValue(EV encodedValue) {
- return strategy.getValue(encodedValue);
+ return strategy.getValue(encodedValue, code.getStrategyInfo());
}
public List<Value> getValues(List<EV> indices) {
@@ -215,20 +230,34 @@
public Value getOutValueForNextInstruction(TypeElement type) {
int valueIndex = toInstructionIndexInIR(peekNextInstructionIndex());
- DebugLocalInfo localInfo = code.getDebugLocalInfo(valueIndex);
- return strategy.getValueDefinitionForInstructionIndex(valueIndex, type, localInfo);
+ return strategy.getValueDefinitionForInstructionIndex(
+ valueIndex, type, code::getDebugLocalInfo);
}
public Phi getPhiForNextInstructionAndAdvanceState(TypeElement type) {
- int valueIndex = toInstructionIndexInIR(peekNextInstructionIndex());
- DebugLocalInfo localInfo = code.getDebugLocalInfo(valueIndex);
- // 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.
- return strategy.getPhiDefinitionForInstructionIndex(
- valueIndex, currentBlock, type, localInfo);
+ int instructionIndex = peekNextInstructionIndex();
+ int valueIndex = toInstructionIndexInIR(instructionIndex);
+ Phi phi =
+ strategy.getPhiDefinitionForInstructionIndex(
+ valueIndex,
+ blockIndex -> getBasicBlockOrEnsureCurrentBlock(blockIndex, instructionIndex),
+ type,
+ code::getDebugLocalInfo,
+ code.getStrategyInfo());
+ ensureCurrentPosition();
+ ++nextInstructionIndex;
+ return phi;
+ }
+
+ private BasicBlock getBasicBlockOrEnsureCurrentBlock(int index, int currentInstructionIndex) {
+ // If the index is at current or past it ensure the block.
+ if (index >= currentInstructionIndex) {
+ ensureCurrentBlock();
+ return currentBlock;
+ }
+ // Otherwise we assume the index is an exact block index for an existing block.
+ assert blocks.containsKey(index);
+ return getBasicBlock(index);
}
private void advanceInstructionState() {
@@ -262,8 +291,9 @@
// Arguments are not included in the "instructions" so this does not call "addInstruction"
// which would otherwise advance the state.
TypeElement typeElement = type.toTypeElement(appView);
- DebugLocalInfo localInfo = code.getDebugLocalInfo(index);
- Value dest = strategy.getValueDefinitionForInstructionIndex(index, typeElement, localInfo);
+ Value dest =
+ strategy.getValueDefinitionForInstructionIndex(
+ index, typeElement, code::getDebugLocalInfo);
Argument argument = new Argument(dest, index, type.isBooleanType());
assert currentBlock != null;
assert currentPosition.isSyntheticPosition();
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 b2fe3ba..fd4113a 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
@@ -433,6 +433,6 @@
instructionCount,
new TryCatchTable(tryCatchRanges),
debugTable,
- strategy.getSsaValueStrategy());
+ strategy.getStrategyInfo());
}
}
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 f62d9f6..3f84cdf 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirCode.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirCode.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.ir.code.Position;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import java.util.Map;
+import java.util.function.BiConsumer;
public class LirCode<EV> implements Iterable<LirInstructionView> {
@@ -48,9 +49,13 @@
this.valueToLocalMap = valueToLocalMap;
this.instructionToEndUseMap = instructionToEndUseMap;
}
+
+ public void forEachLocalDefinition(BiConsumer<EV, DebugLocalInfo> fn) {
+ valueToLocalMap.forEach(fn);
+ }
}
- private final LirSsaValueStrategy<EV> ssaValueStrategy;
+ private final LirStrategyInfo<EV> strategyInfo;
private final IRMetadata metadata;
@@ -89,7 +94,7 @@
int instructionCount,
TryCatchTable tryCatchTable,
DebugLocalInfoTable<EV> debugLocalInfoTable,
- LirSsaValueStrategy<EV> ssaValueStrategy) {
+ LirStrategyInfo<EV> strategyInfo) {
this.metadata = metadata;
this.constants = constants;
this.positionTable = positions;
@@ -98,11 +103,17 @@
this.instructionCount = instructionCount;
this.tryCatchTable = tryCatchTable;
this.debugLocalInfoTable = debugLocalInfoTable;
- this.ssaValueStrategy = ssaValueStrategy;
+ this.strategyInfo = strategyInfo;
}
public EV decodeValueIndex(int encodedValueIndex, int currentValueIndex) {
- return ssaValueStrategy.decodeValueIndex(encodedValueIndex, currentValueIndex);
+ return strategyInfo
+ .getReferenceStrategy()
+ .decodeValueIndex(encodedValueIndex, currentValueIndex);
+ }
+
+ public LirStrategyInfo<EV> getStrategyInfo() {
+ return strategyInfo;
}
public int getArgumentCount() {
@@ -137,7 +148,7 @@
return debugLocalInfoTable;
}
- public DebugLocalInfo getDebugLocalInfo(int valueIndex) {
+ public DebugLocalInfo getDebugLocalInfo(EV valueIndex) {
return debugLocalInfoTable == null ? null : debugLocalInfoTable.valueToLocalMap.get(valueIndex);
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LirDecodingStrategy.java b/src/main/java/com/android/tools/r8/lightir/LirDecodingStrategy.java
index 1e25e3c..0b0a51d 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirDecodingStrategy.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirDecodingStrategy.java
@@ -7,15 +7,21 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.Phi;
+import java.util.function.Function;
+import java.util.function.IntFunction;
/** Abstraction for how to decode SSA values (and basic blocks) when reading LIR. */
public abstract class LirDecodingStrategy<V, EV> {
- public abstract V getValue(EV encodedValue);
+ public abstract V getValue(EV encodedValue, LirStrategyInfo<EV> strategyInfo);
public abstract V getValueDefinitionForInstructionIndex(
- int instructionIndex, TypeElement type, DebugLocalInfo localInfo);
+ int instructionIndex, TypeElement type, Function<EV, DebugLocalInfo> getLocalInfo);
public abstract Phi getPhiDefinitionForInstructionIndex(
- int instructionIndex, BasicBlock block, TypeElement type, DebugLocalInfo localInfo);
+ int valueIndex,
+ IntFunction<BasicBlock> getBlock,
+ TypeElement type,
+ Function<EV, DebugLocalInfo> getLocalInfo,
+ LirStrategyInfo<EV> strategyInfo);
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LirEncodingStrategy.java b/src/main/java/com/android/tools/r8/lightir/LirEncodingStrategy.java
index 8bbba90..eb814d1 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirEncodingStrategy.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirEncodingStrategy.java
@@ -14,13 +14,17 @@
public abstract EV defineValue(V value, int index);
+ public abstract boolean isPhiInlineInstruction();
+
public abstract boolean verifyValueIndex(V value, int expectedIndex);
public abstract EV getEncodedValue(V value);
public int getEncodedValueIndexForReference(EV encodedValue, int referencingValueIndex) {
- return getSsaValueStrategy().encodeValueIndex(encodedValue, referencingValueIndex);
+ return getStrategyInfo()
+ .getReferenceStrategy()
+ .encodeValueIndex(encodedValue, referencingValueIndex);
}
- public abstract LirSsaValueStrategy<EV> getSsaValueStrategy();
+ public abstract LirStrategyInfo<EV> getStrategyInfo();
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
index 20dfc30..f4a840b 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
@@ -53,7 +53,7 @@
}
private String fmtValueIndex(EV valueIndex) {
- return "v" + valueIndex;
+ return valueIndex.toString();
}
private String fmtInsnIndex(int instructionIndex) {
diff --git a/src/main/java/com/android/tools/r8/lightir/LirStrategy.java b/src/main/java/com/android/tools/r8/lightir/LirStrategy.java
index 70cda39..108586f 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirStrategy.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirStrategy.java
@@ -3,6 +3,8 @@
// 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.errors.Unreachable;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.BasicBlock;
@@ -11,6 +13,11 @@
import com.android.tools.r8.ir.code.Value;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.function.IntFunction;
/**
* Abstraction for encoding and decoding LIR values.
@@ -26,114 +33,326 @@
public abstract LirDecodingStrategy<V, EV> getDecodingStrategy(LirCode<EV> code);
- // Strategy that implements the encoding of phi values as instructions in the LIR instruction
- // stream.
- public static class PhiInInstructionsStrategy extends LirStrategy<Value, Integer> {
+ /**
+ * Encoding of a value with a phi-bit.
+ *
+ * <p>Due to the generic signature the encoding is boxed so this just adds some convenient
+ * predicates and formatting since it is boxed anyway.
+ *
+ * <p>JVM code attribute has length u2 (16-bit / max 65536). Thus, the number of basic blocks and
+ * phi count is also bounded by the same value. The encoding here is taken to be
+ *
+ * <ul>
+ * <li>1-bit for value/phi bit (sign bit / most significant bit),
+ * <li>15-bit phi index (the following most significant bits).
+ * <li>16-bit block index (the least significant bits).
+ * </ul>
+ *
+ * <p>TODO(b/225838009): Fix this encoding to support pathological block counts above 32k.
+ */
+ public static class PhiOrValue {
+ private final int value;
+
+ public static PhiOrValue forPhi(int blockIndex, int phiIndex) {
+ int sign = Integer.MIN_VALUE;
+ int block = ensure15bit(blockIndex) << 16;
+ int phi = ByteUtils.ensureU2(phiIndex);
+ int raw = sign | block | phi;
+ assert raw < 0;
+ return new PhiOrValue(raw);
+ }
+
+ private static int ensure15bit(int value) {
+ if (value >= (1 << 15)) {
+ // TODO(b/225838009): Support 16-bit values and inline this helper.
+ throw new Unimplemented("No support for more than 15-bit block index.");
+ }
+ return ByteUtils.ensureU2(value);
+ }
+
+ public static PhiOrValue forNonPhi(int index) {
+ assert index >= 0;
+ return new PhiOrValue(index);
+ }
+
+ private PhiOrValue(int value) {
+ this.value = value;
+ }
+
+ public boolean isPhi() {
+ return value < 0;
+ }
+
+ public boolean isNonPhi() {
+ return !isPhi();
+ }
+
+ public int getRawValue() {
+ return value;
+ }
+
+ public int getDecodedValue() {
+ assert isNonPhi();
+ return value;
+ }
+
+ public int getBlockIndex() {
+ assert isPhi();
+ return (value & ~Integer.MIN_VALUE) >> 16;
+ }
+
+ public int getPhiIndex() {
+ assert isPhi();
+ return value & 0xFFFF;
+ }
@Override
- public LirEncodingStrategy<Value, Integer> getEncodingStrategy() {
+ public String toString() {
+ if (isPhi()) {
+ return "phi(" + getBlockIndex() + "," + getPhiIndex() + ")";
+ }
+ return "v" + getDecodedValue();
+ }
+
+ @Override
+ public int hashCode() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (obj == this) {
+ return true;
+ }
+ return obj instanceof PhiOrValue && (value == ((PhiOrValue) obj).value);
+ }
+ }
+
+ public static class ExternalPhisStrategy extends LirStrategy<Value, PhiOrValue> {
+
+ @Override
+ public LirEncodingStrategy<Value, PhiOrValue> getEncodingStrategy() {
return new EncodingStrategy();
}
@Override
- public LirDecodingStrategy<Value, Integer> getDecodingStrategy(LirCode<Integer> code) {
+ public LirDecodingStrategy<Value, PhiOrValue> getDecodingStrategy(LirCode<PhiOrValue> code) {
return new DecodingStrategy(code);
}
- }
- private static class EncodingStrategy extends LirEncodingStrategy<Value, Integer> {
+ private static class StrategyInfo extends LirStrategyInfo<PhiOrValue> {
+ private static final StrategyInfo EMPTY = new StrategyInfo(new int[0]);
- // EV == Integer and its definition is equal to its shifted instruction index.
- // The conversion for EV to its int-valued reference is determined by the 'valueStrategy'.
+ private final int[] phiTable;
- private final LirSsaValueStrategy<Integer> valueStrategy = LirSsaValueStrategy.get();
- private final Reference2IntMap<Value> values = new Reference2IntOpenHashMap<>();
- private final Reference2IntMap<BasicBlock> blocks = new Reference2IntOpenHashMap<>();
-
- @Override
- public void defineBlock(BasicBlock block, int index) {
- assert !blocks.containsKey(block);
- blocks.put(block, index);
- }
-
- @Override
- public Integer defineValue(Value value, int index) {
- values.put(value, index);
- return index;
- }
-
- @Override
- public boolean verifyValueIndex(Value value, int expectedIndex) {
- assert expectedIndex == values.getInt(value);
- return true;
- }
-
- @Override
- public Integer getEncodedValue(Value value) {
- return values.getInt(value);
- }
-
- @Override
- public int getBlockIndex(BasicBlock block) {
- assert blocks.containsKey(block);
- return blocks.getInt(block);
- }
-
- @Override
- public LirSsaValueStrategy<Integer> getSsaValueStrategy() {
- return valueStrategy;
- }
- }
-
- private static class DecodingStrategy extends LirDecodingStrategy<Value, Integer> {
-
- private final Value[] values;
-
- DecodingStrategy(LirCode<Integer> code) {
- values = new Value[code.getArgumentCount() + code.getInstructionCount()];
- }
-
- @Override
- public Value getValue(Integer encodedValue) {
- int index = encodedValue;
- Value value = values[index];
- if (value == null) {
- value = new Value(index, TypeElement.getBottom(), null);
- values[index] = value;
+ public StrategyInfo(int[] phiTable) {
+ this.phiTable = phiTable;
}
- return value;
+
+ @Override
+ public LirSsaValueStrategy<PhiOrValue> getReferenceStrategy() {
+ return ReferenceStrategy.INSTANCE;
+ }
}
- @Override
- public Value getValueDefinitionForInstructionIndex(
- int index, TypeElement type, DebugLocalInfo localInfo) {
- Value value = values[index];
- if (value == null) {
- value = new Value(index, type, localInfo);
- values[index] = value;
- } else {
- value.setType(type);
- if (localInfo != null) {
- value.setLocalInfo(localInfo);
+ private static class EncodingStrategy extends LirEncodingStrategy<Value, PhiOrValue> {
+ private final Map<Value, PhiOrValue> values = new IdentityHashMap<>();
+ private final Reference2IntMap<BasicBlock> blocks = new Reference2IntOpenHashMap<>();
+ private final ArrayList<Integer> phiTable = new ArrayList<>();
+
+ @Override
+ public boolean isPhiInlineInstruction() {
+ return false;
+ }
+
+ @Override
+ public void defineBlock(BasicBlock block, int index) {
+ assert !blocks.containsKey(block);
+ blocks.put(block, index);
+ if (block.getPhis().isEmpty()) {
+ return;
}
+ int i = 0;
+ for (Phi phi : block.getPhis()) {
+ values.put(phi, PhiOrValue.forPhi(index, i++));
+ }
+ // Amend the phi table with the index of the basic block and the number of its phis.
+ phiTable.add(index);
+ phiTable.add(i);
}
- return value;
+
+ @Override
+ public PhiOrValue defineValue(Value value, int index) {
+ if (value.isPhi()) {
+ // Phis are defined as part of blocks.
+ PhiOrValue encodedValue = values.get(value);
+ assert encodedValue != null;
+ return encodedValue;
+ }
+ PhiOrValue encodedValue = PhiOrValue.forNonPhi(index);
+ values.put(value, encodedValue);
+ return encodedValue;
+ }
+
+ @Override
+ public boolean verifyValueIndex(Value value, int expectedIndex) {
+ PhiOrValue encodedValue = values.get(value);
+ assert encodedValue.isNonPhi();
+ assert expectedIndex == encodedValue.getDecodedValue();
+ return true;
+ }
+
+ @Override
+ public PhiOrValue getEncodedValue(Value value) {
+ return values.get(value);
+ }
+
+ @Override
+ public int getBlockIndex(BasicBlock block) {
+ assert blocks.containsKey(block);
+ return blocks.getInt(block);
+ }
+
+ @Override
+ public LirStrategyInfo<PhiOrValue> getStrategyInfo() {
+ if (phiTable.isEmpty()) {
+ return StrategyInfo.EMPTY;
+ }
+ int[] array = new int[phiTable.size()];
+ for (int i = 0; i < phiTable.size(); i++) {
+ array[i] = phiTable.get(i);
+ }
+ return new StrategyInfo(array);
+ }
}
- @Override
- public Phi getPhiDefinitionForInstructionIndex(
- int index, BasicBlock block, TypeElement type, DebugLocalInfo localInfo) {
- Phi phi = new Phi(index, block, 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);
+ private static class DecodingStrategy extends LirDecodingStrategy<Value, PhiOrValue> {
+
+ private final Value[] values;
+ private final int firstPhiValueIndex;
+
+ DecodingStrategy(LirCode<PhiOrValue> code) {
+ values = new Value[code.getArgumentCount() + code.getInstructionCount()];
+ int phiValueIndex = -1;
+ for (LirInstructionView view : code) {
+ if (view.getOpcode() == LirOpcodes.PHI) {
+ phiValueIndex = code.getArgumentCount() + view.getInstructionIndex();
+ break;
+ }
+ }
+ this.firstPhiValueIndex = phiValueIndex;
}
- values[index] = phi;
- return phi;
+
+ private int decode(PhiOrValue encodedValue, LirStrategyInfo<PhiOrValue> strategyInfo) {
+ if (encodedValue.isNonPhi()) {
+ return encodedValue.getDecodedValue();
+ }
+ StrategyInfo info = (StrategyInfo) strategyInfo;
+ int phiBlock = encodedValue.getBlockIndex();
+ int phiIndex = encodedValue.getPhiIndex();
+ assert firstPhiValueIndex != -1;
+ int index = firstPhiValueIndex;
+ for (int i = 0; i < info.phiTable.length; i++) {
+ int blockIndex = info.phiTable[i];
+ if (blockIndex == phiBlock) {
+ return index + phiIndex;
+ }
+ index += info.phiTable[++i];
+ }
+ throw new Unreachable("Unexpectedly fell off the end of the phi table");
+ }
+
+ @Override
+ public Value getValue(PhiOrValue encodedValue, LirStrategyInfo<PhiOrValue> strategyInfo) {
+ int index = decode(encodedValue, strategyInfo);
+ Value value = values[index];
+ if (value == null) {
+ value = new Value(index, TypeElement.getBottom(), null);
+ values[index] = value;
+ }
+ return value;
+ }
+
+ @Override
+ public Value getValueDefinitionForInstructionIndex(
+ int index, TypeElement type, Function<PhiOrValue, DebugLocalInfo> getLocalInfo) {
+ PhiOrValue encodedValue = new PhiOrValue(index);
+ assert encodedValue.isNonPhi();
+ DebugLocalInfo localInfo = getLocalInfo.apply(encodedValue);
+ Value value = values[index];
+ if (value == null) {
+ value = new Value(index, type, localInfo);
+ values[index] = value;
+ } else {
+ value.setType(type);
+ if (localInfo != null && !value.hasLocalInfo()) {
+ value.setLocalInfo(localInfo);
+ }
+ assert localInfo == value.getLocalInfo();
+ }
+ return value;
+ }
+
+ @Override
+ public Phi getPhiDefinitionForInstructionIndex(
+ int valueIndex,
+ IntFunction<BasicBlock> getBlock,
+ TypeElement type,
+ Function<PhiOrValue, DebugLocalInfo> getLocalInfo,
+ LirStrategyInfo<PhiOrValue> strategyInfo) {
+ PhiOrValue encodedValue = getEncodedPhiForAbsoluteValueIndex(valueIndex, strategyInfo);
+ BasicBlock block = getBlock.apply(encodedValue.getBlockIndex());
+ DebugLocalInfo localInfo = getLocalInfo.apply(encodedValue);
+ Phi phi = new Phi(valueIndex, block, type, localInfo, RegisterReadType.NORMAL);
+ Value value = values[valueIndex];
+ 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 phi-bit in the value index
+ // and avoid the overhead of replacing users at phi-definition time.
+ assert !value.isPhi();
+ value.replaceUsers(phi);
+ }
+ values[valueIndex] = phi;
+ return phi;
+ }
+
+ private PhiOrValue getEncodedPhiForAbsoluteValueIndex(
+ int phiValueIndex, LirStrategyInfo<PhiOrValue> strategyInfo) {
+ StrategyInfo info = (StrategyInfo) strategyInfo;
+ int currentPhiValueIndex = firstPhiValueIndex;
+ for (int i = 0; i < info.phiTable.length; i += 2) {
+ assert currentPhiValueIndex <= phiValueIndex;
+ int blockIndex = info.phiTable[i];
+ int phiCount = info.phiTable[i + 1];
+ assert phiCount > 0;
+ if (phiValueIndex < currentPhiValueIndex + phiCount) {
+ int phiOffsetInBlock = phiValueIndex - currentPhiValueIndex;
+ return PhiOrValue.forPhi(blockIndex, phiOffsetInBlock);
+ }
+ currentPhiValueIndex += phiCount;
+ }
+ throw new Unreachable("Unexpected fall off the end of the phi table");
+ }
+ }
+
+ // TODO(b/225838009): Consider still encoding the local value refs as small relative indexes.
+ private static class ReferenceStrategy extends LirSsaValueStrategy<PhiOrValue> {
+
+ private static final ReferenceStrategy INSTANCE = new ReferenceStrategy();
+
+ @Override
+ public int encodeValueIndex(PhiOrValue value, int currentValueIndex) {
+ return value.getRawValue();
+ }
+
+ @Override
+ public PhiOrValue decodeValueIndex(int encodedValueIndex, int currentValueIndex) {
+ return new PhiOrValue(encodedValueIndex);
+ }
}
}
+
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LirStrategyInfo.java b/src/main/java/com/android/tools/r8/lightir/LirStrategyInfo.java
new file mode 100644
index 0000000..088786c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/lightir/LirStrategyInfo.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2023, 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.lightir;
+
+public abstract class LirStrategyInfo<EV> {
+
+ public abstract LirSsaValueStrategy<EV> getReferenceStrategy();
+}
diff --git a/src/main/java/com/android/tools/r8/lightir/PhiInInstructionsStrategy.java b/src/main/java/com/android/tools/r8/lightir/PhiInInstructionsStrategy.java
new file mode 100644
index 0000000..0b0d537
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/lightir/PhiInInstructionsStrategy.java
@@ -0,0 +1,145 @@
+// Copyright (c) 2023, 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.lightir;
+
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.code.Phi.RegisterReadType;
+import com.android.tools.r8.ir.code.Value;
+import it.unimi.dsi.fastutil.objects.Reference2IntMap;
+import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
+import java.util.function.Function;
+import java.util.function.IntFunction;
+
+/** Strategy encoding phi values as instructions in the LIR instruction stream. */
+public class PhiInInstructionsStrategy extends LirStrategy<Value, Integer> {
+
+ @Override
+ public LirEncodingStrategy<Value, Integer> getEncodingStrategy() {
+ return new EncodingStrategy();
+ }
+
+ @Override
+ public LirDecodingStrategy<Value, Integer> getDecodingStrategy(LirCode<Integer> code) {
+ return new DecodingStrategy(code);
+ }
+
+ private static class EncodingStrategy extends LirEncodingStrategy<Value, Integer> {
+
+ // EV == Integer and its definition is equal to its shifted instruction index.
+ // The conversion for EV to its int-valued reference is determined by the 'valueStrategy'.
+
+ private final LirSsaValueStrategy<Integer> referenceStrategy = LirSsaValueStrategy.get();
+ private final Reference2IntMap<Value> values = new Reference2IntOpenHashMap<>();
+ private final Reference2IntMap<BasicBlock> blocks = new Reference2IntOpenHashMap<>();
+
+ @Override
+ public boolean isPhiInlineInstruction() {
+ return true;
+ }
+
+ @Override
+ public void defineBlock(BasicBlock block, int index) {
+ assert !blocks.containsKey(block);
+ blocks.put(block, index);
+ }
+
+ @Override
+ public Integer defineValue(Value value, int index) {
+ values.put(value, index);
+ return index;
+ }
+
+ @Override
+ public boolean verifyValueIndex(Value value, int expectedIndex) {
+ assert expectedIndex == values.getInt(value);
+ return true;
+ }
+
+ @Override
+ public Integer getEncodedValue(Value value) {
+ return values.getInt(value);
+ }
+
+ @Override
+ public int getBlockIndex(BasicBlock block) {
+ assert blocks.containsKey(block);
+ return blocks.getInt(block);
+ }
+
+ @Override
+ public LirStrategyInfo<Integer> getStrategyInfo() {
+ return new LirStrategyInfo<Integer>() {
+ @Override
+ public LirSsaValueStrategy<Integer> getReferenceStrategy() {
+ return referenceStrategy;
+ }
+ };
+ }
+ }
+
+ private static class DecodingStrategy extends LirDecodingStrategy<Value, Integer> {
+
+ private final Value[] values;
+
+ DecodingStrategy(LirCode<Integer> code) {
+ values = new Value[code.getArgumentCount() + code.getInstructionCount()];
+ }
+
+ @Override
+ public Value getValue(Integer encodedValue, LirStrategyInfo<Integer> strategyInfo) {
+ int index = encodedValue;
+ Value value = values[index];
+ if (value == null) {
+ value = new Value(index, TypeElement.getBottom(), null);
+ values[index] = value;
+ }
+ return value;
+ }
+
+ @Override
+ public Value getValueDefinitionForInstructionIndex(
+ int index, TypeElement type, Function<Integer, DebugLocalInfo> getLocalInfo) {
+ DebugLocalInfo localInfo = getLocalInfo.apply(index);
+ Value value = values[index];
+ if (value == null) {
+ value = new Value(index, type, localInfo);
+ values[index] = value;
+ } else {
+ value.setType(type);
+ if (localInfo != null) {
+ if (!value.hasLocalInfo()) {
+ value.setLocalInfo(localInfo);
+ }
+ assert localInfo == value.getLocalInfo();
+ }
+ }
+ return value;
+ }
+
+ @Override
+ public Phi getPhiDefinitionForInstructionIndex(
+ int valueIndex,
+ IntFunction<BasicBlock> getBlock,
+ TypeElement type,
+ Function<Integer, DebugLocalInfo> getLocalInfo,
+ LirStrategyInfo<Integer> strategyInfo) {
+ BasicBlock block = getBlock.apply(valueIndex);
+ DebugLocalInfo localInfo = getLocalInfo.apply(valueIndex);
+ Phi phi = new Phi(valueIndex, block, type, localInfo, RegisterReadType.NORMAL);
+ Value value = values[valueIndex];
+ 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[valueIndex] = phi;
+ return phi;
+ }
+ }
+}
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 062470c..abd980d 100644
--- a/src/test/java/com/android/tools/r8/lightir/LirBasicCallbackTest.java
+++ b/src/test/java/com/android/tools/r8/lightir/LirBasicCallbackTest.java
@@ -37,6 +37,11 @@
private static class ThrowingStrategy extends LirEncodingStrategy<Value, Integer> {
@Override
+ public boolean isPhiInlineInstruction() {
+ return false;
+ }
+
+ @Override
public void defineBlock(BasicBlock block, int index) {
throw new Unreachable();
}
@@ -62,7 +67,7 @@
}
@Override
- public LirSsaValueStrategy<Integer> getSsaValueStrategy() {
+ public LirStrategyInfo<Integer> getStrategyInfo() {
return null;
}
}