blob: a4400f408f272e36568a7a591e73610cfe0a9c57 [file] [log] [blame]
// Copyright (c) 2022, 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.dex.code.CfOrDexInstruction;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ArgumentUse;
import com.android.tools.r8.graph.ClasspathMethod;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadata;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.code.CanonicalPositions;
import com.android.tools.r8.ir.code.CatchHandlers;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.NumberGenerator;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Position.SourcePosition;
import com.android.tools.r8.ir.code.Position.SyntheticPosition;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.ir.conversion.MethodConversionOptions;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
import com.android.tools.r8.lightir.LirConstant.LirConstantStructuralAcceptor;
import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.ComparatorUtils;
import com.android.tools.r8.utils.FastMapUtils;
import com.android.tools.r8.utils.IntBox;
import com.android.tools.r8.utils.IntObjPredicate;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.RetracerForCodePrinting;
import com.android.tools.r8.utils.structural.CompareToVisitor;
import com.android.tools.r8.utils.structural.HashingVisitor;
import com.android.tools.r8.utils.structural.StructuralAcceptor;
import com.android.tools.r8.utils.structural.StructuralItem;
import com.android.tools.r8.utils.structural.StructuralMapping;
import com.android.tools.r8.utils.structural.StructuralSpecification;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
public class LirCode<EV> extends Code
implements StructuralItem<LirCode<EV>>, Iterable<LirInstructionView> {
public abstract static class PositionEntry implements StructuralItem<PositionEntry> {
public static final PositionEntry[] EMPTY_ARRAY = new PositionEntry[0];
private final int fromInstructionIndex;
PositionEntry(int fromInstructionIndex) {
this.fromInstructionIndex = fromInstructionIndex;
}
public int getFromInstructionIndex() {
return fromInstructionIndex;
}
public abstract Position getPosition(DexMethod method, boolean isD8R8Synthesized);
public abstract boolean hasCallerPosition();
abstract int getOrder();
abstract int internalAcceptCompareTo(PositionEntry other, CompareToVisitor visitor);
abstract void internalAcceptHashing(HashingVisitor visitor);
@Override
public final PositionEntry self() {
return this;
}
@Override
public final StructuralMapping<PositionEntry> getStructuralMapping() {
throw new Unreachable();
}
@Override
public final int acceptCompareTo(PositionEntry other, CompareToVisitor visitor) {
int diff = visitor.visitInt(getOrder(), other.getOrder());
if (diff != 0) {
return diff;
}
return internalAcceptCompareTo(other, visitor);
}
@Override
public final void acceptHashing(HashingVisitor visitor) {
visitor.visitInt(getOrder());
internalAcceptHashing(visitor);
}
}
public static class LinePositionEntry extends PositionEntry {
private final int line;
public LinePositionEntry(int fromInstructionIndex, int line) {
super(fromInstructionIndex);
this.line = line;
}
public int getLine() {
return line;
}
@Override
public boolean hasCallerPosition() {
return false;
}
@Override
public Position getPosition(DexMethod method, boolean isD8R8Synthesized) {
return (isD8R8Synthesized ? SyntheticPosition.builder() : SourcePosition.builder())
.setMethod(method)
.setIsD8R8Synthesized(isD8R8Synthesized)
.setLine(line)
.build();
}
@Override
int getOrder() {
return 0;
}
@Override
int internalAcceptCompareTo(PositionEntry other, CompareToVisitor visitor) {
return visitor.visitInt(line, ((LinePositionEntry) other).line);
}
@Override
void internalAcceptHashing(HashingVisitor visitor) {
visitor.visitInt(line);
}
}
public static class StructuredPositionEntry extends PositionEntry {
private final Position position;
public StructuredPositionEntry(int fromInstructionIndex, Position position) {
super(fromInstructionIndex);
this.position = position;
}
@Override
public boolean hasCallerPosition() {
return position.hasCallerPosition();
}
@Override
public Position getPosition(DexMethod method, boolean isD8R8Synthesized) {
return position;
}
@Override
int getOrder() {
return 1;
}
@Override
int internalAcceptCompareTo(PositionEntry other, CompareToVisitor visitor) {
return position.acceptCompareTo(((StructuredPositionEntry) other).position, visitor);
}
@Override
void internalAcceptHashing(HashingVisitor visitor) {
position.acceptHashing(visitor);
}
}
public static class TryCatchTable implements StructuralItem<TryCatchTable> {
private final Int2ReferenceMap<CatchHandlers<Integer>> tryCatchHandlers;
public TryCatchTable(Int2ReferenceMap<CatchHandlers<Integer>> tryCatchHandlers) {
assert !tryCatchHandlers.isEmpty();
// Copy the map to ensure it has not over-allocated the backing store.
this.tryCatchHandlers = new Int2ReferenceOpenHashMap<>(tryCatchHandlers);
}
public boolean hasHandlerThatMatches(IntObjPredicate<CatchHandlers<Integer>> predicate) {
return Iterables.any(
tryCatchHandlers.int2ReferenceEntrySet(),
entry -> predicate.test(entry.getIntKey(), entry.getValue()));
}
public CatchHandlers<Integer> getHandlersForBlock(int blockIndex) {
return tryCatchHandlers.get(blockIndex);
}
public void forEachHandler(BiConsumer<Integer, CatchHandlers<Integer>> fn) {
tryCatchHandlers.forEach(fn);
}
@Override
public TryCatchTable self() {
return this;
}
@Override
public StructuralMapping<TryCatchTable> getStructuralMapping() {
return TryCatchTable::specify;
}
public TryCatchTable rewriteWithLens(GraphLens graphLens, GraphLens codeLens) {
Int2ReferenceMap<CatchHandlers<Integer>> newTryCatchHandlers =
FastMapUtils.mapInt2ReferenceOpenHashMapOrElse(
tryCatchHandlers,
(block, blockHandlers) -> blockHandlers.rewriteWithLens(graphLens, codeLens),
null);
return newTryCatchHandlers != null ? new TryCatchTable(newTryCatchHandlers) : this;
}
private static void specify(StructuralSpecification<TryCatchTable, ?> spec) {
spec.withInt2CustomItemMap(
s -> s.tryCatchHandlers,
new StructuralAcceptor<>() {
@Override
public int acceptCompareTo(
CatchHandlers<Integer> item1,
CatchHandlers<Integer> item2,
CompareToVisitor visitor) {
int diff = visitor.visitItemCollection(item1.getGuards(), item2.getGuards());
if (diff != 0) {
return diff;
}
return ComparatorUtils.compareLists(item1.getAllTargets(), item2.getAllTargets());
}
@Override
public void acceptHashing(CatchHandlers<Integer> item, HashingVisitor visitor) {
List<Integer> targets = item.getAllTargets();
for (int i = 0; i < targets.size(); i++) {
visitor.visitDexType(item.getGuard(i));
visitor.visitInt(targets.get(i));
}
}
});
}
}
public static class DebugLocalInfoTable<EV> implements StructuralItem<DebugLocalInfoTable<EV>> {
// TODO(b/225838009): Once EV is removed use an int2ref map here.
private final Map<EV, DebugLocalInfo> valueToLocalMap;
private final Int2ReferenceMap<int[]> instructionToEndUseMap;
public DebugLocalInfoTable(
Map<EV, DebugLocalInfo> valueToLocalMap, Int2ReferenceMap<int[]> instructionToEndUseMap) {
assert !valueToLocalMap.isEmpty();
// TODO(b/283049198): Debug ends may not be maintained so we can't assume they are non-empty.
// Copy the maps to ensure they have not over-allocated the backing store.
this.valueToLocalMap = ImmutableMap.copyOf(valueToLocalMap);
this.instructionToEndUseMap =
instructionToEndUseMap.isEmpty()
? null
: new Int2ReferenceOpenHashMap<>(instructionToEndUseMap);
}
public int[] getEnds(int index) {
if (instructionToEndUseMap == null) {
return null;
}
return instructionToEndUseMap.get(index);
}
public void forEachLocalDefinition(BiConsumer<EV, DebugLocalInfo> fn) {
valueToLocalMap.forEach(fn);
}
@Override
public DebugLocalInfoTable<EV> self() {
return this;
}
@Override
public StructuralMapping<DebugLocalInfoTable<EV>> getStructuralMapping() {
throw new Unreachable();
}
@Override
public int acceptCompareTo(DebugLocalInfoTable<EV> other, CompareToVisitor visitor) {
int size = valueToLocalMap.size();
int diff = Integer.compare(size, other.valueToLocalMap.size());
if (diff != 0) {
return diff;
}
if ((instructionToEndUseMap == null) != (other.instructionToEndUseMap == null)) {
return instructionToEndUseMap == null ? -1 : 1;
}
if (instructionToEndUseMap != null) {
assert other.instructionToEndUseMap != null;
diff =
ComparatorUtils.compareInt2ReferenceMap(
instructionToEndUseMap,
other.instructionToEndUseMap,
ComparatorUtils::compareIntArray);
if (diff != 0) {
return diff;
}
}
// We know EV is only instantiated with Integer so this is safe.
assert !(valueToLocalMap instanceof Int2ReferenceMap);
Int2ReferenceOpenHashMap<DebugLocalInfo> map1 = new Int2ReferenceOpenHashMap<>(size);
Int2ReferenceOpenHashMap<DebugLocalInfo> map2 = new Int2ReferenceOpenHashMap<>(size);
valueToLocalMap.forEach((k, v) -> map1.put((int) k, v));
other.valueToLocalMap.forEach((k, v) -> map2.put((int) k, v));
return ComparatorUtils.compareInt2ReferenceMap(
map1, map2, (i1, i2) -> i1.acceptCompareTo(i2, visitor));
}
@Override
public void acceptHashing(HashingVisitor visitor) {
visitor.visitInt(valueToLocalMap.size());
ArrayList<Integer> keys = new ArrayList<>(valueToLocalMap.size());
valueToLocalMap.forEach((k, v) -> keys.add((int) k));
keys.sort(Integer::compareTo);
for (int key : keys) {
visitor.visitInt(key);
valueToLocalMap.get(key).acceptHashing(visitor);
}
if (instructionToEndUseMap != null) {
// Instead of sorting the end map we just sum the keys and values which is commutative.
IntBox keySum = new IntBox();
IntBox valueSum = new IntBox();
instructionToEndUseMap.forEach(
(k, v) -> {
keySum.increment(k);
valueSum.increment(Arrays.hashCode(v));
});
visitor.visitInt(keySum.get());
visitor.visitInt(valueSum.get());
}
}
}
private final LirStrategyInfo<EV> strategyInfo;
private final boolean useDexEstimationStrategy;
/** Constant pool of items. */
private final LirConstant[] constants;
private final PositionEntry[] positionTable;
/** Full number of arguments (including receiver for non-static methods). */
private final int argumentCount;
/** Byte encoding of the instructions (excludes arguments, includes phis). */
private final byte[] instructions;
/** Cached value for the number of logical instructions (excludes arguments, includes phis). */
private final int instructionCount;
/** Table of try-catch handlers for each basic block (if present). */
private final TryCatchTable tryCatchTable;
/** Table of debug local information for each SSA value (if present). */
private final DebugLocalInfoTable<EV> debugLocalInfoTable;
/** Table of metadata for each instruction (if present). */
private Int2ReferenceMap<BytecodeInstructionMetadata> metadataMap;
public static <V, EV> LirBuilder<V, EV> builder(
DexMethod method,
boolean isD8R8Synthesized,
LirEncodingStrategy<V, EV> strategy,
InternalOptions options) {
return new LirBuilder<>(method, isD8R8Synthesized, strategy, options);
}
private static <EV> void specify(StructuralSpecification<LirCode<EV>, ?> spec) {
// strategyInfo is compiler meta-data (constant for a given compilation unit).
// useDexEstimationStrategy is compiler meta-data (constant for a given compilation unit).
spec.withCustomItemArray(c -> c.constants, LirConstantStructuralAcceptor.getInstance())
.withItemArray(c -> c.positionTable)
.withInt(c -> c.argumentCount)
.withByteArray(c -> c.instructions)
.withInt(c -> c.instructionCount)
.withNullableItem(c -> c.tryCatchTable)
.withNullableItem(c -> c.debugLocalInfoTable)
.withAssert(c -> c.metadataMap == null);
}
protected LirCode(LirCode<EV> code) {
this(
code.constants,
code.positionTable,
code.argumentCount,
code.instructions,
code.instructionCount,
code.tryCatchTable,
code.debugLocalInfoTable,
code.strategyInfo,
code.useDexEstimationStrategy,
code.metadataMap);
}
/** Should be constructed using {@link LirBuilder}. */
LirCode(
LirConstant[] constants,
PositionEntry[] positionTable,
int argumentCount,
byte[] instructions,
int instructionCount,
TryCatchTable tryCatchTable,
DebugLocalInfoTable<EV> debugLocalInfoTable,
LirStrategyInfo<EV> strategyInfo,
boolean useDexEstimationStrategy,
Int2ReferenceMap<BytecodeInstructionMetadata> metadataMap) {
assert positionTable != null;
this.constants = constants;
this.positionTable = positionTable;
this.argumentCount = argumentCount;
this.instructions = instructions;
this.instructionCount = instructionCount;
this.tryCatchTable = tryCatchTable;
this.debugLocalInfoTable = debugLocalInfoTable;
this.strategyInfo = strategyInfo;
this.useDexEstimationStrategy = useDexEstimationStrategy;
this.metadataMap = metadataMap;
}
@SuppressWarnings("unchecked")
@Override
public LirCode<Integer> asLirCode() {
// TODO(b/225838009): Unchecked cast will be removed once the encoding strategy is definitive.
return (LirCode<Integer>) this;
}
@Override
public LirCode<EV> self() {
return this;
}
@Override
public StructuralMapping<LirCode<EV>> getStructuralMapping() {
return LirCode::specify;
}
@Override
protected int computeHashCode() {
throw new Unreachable("LIR code should not be subject to hashing.");
}
@Override
protected boolean computeEquals(Object other) {
throw new Unreachable("LIR code should not be subject to equality checks.");
}
public EV decodeValueIndex(int encodedValueIndex, int currentValueIndex) {
return strategyInfo
.getReferenceStrategy()
.decodeValueIndex(encodedValueIndex, currentValueIndex);
}
public LirStrategyInfo<EV> getStrategyInfo() {
return strategyInfo;
}
public int getArgumentCount() {
return argumentCount;
}
public byte[] getInstructionBytes() {
return instructions;
}
public int getInstructionCount() {
return instructionCount;
}
public LirConstant getConstantItem(int index) {
return constants[index];
}
public LirConstant[] getConstantPool() {
return constants;
}
public PositionEntry[] getPositionTable() {
return positionTable;
}
public boolean hasTryCatchTable() {
return tryCatchTable != null;
}
public TryCatchTable getTryCatchTable() {
return tryCatchTable;
}
public DebugLocalInfoTable<EV> getDebugLocalInfoTable() {
return debugLocalInfoTable;
}
public DebugLocalInfo getDebugLocalInfo(EV valueIndex) {
return debugLocalInfoTable == null ? null : debugLocalInfoTable.valueToLocalMap.get(valueIndex);
}
public int[] getDebugLocalEnds(int instructionValueIndex) {
return debugLocalInfoTable == null ? null : debugLocalInfoTable.getEnds(instructionValueIndex);
}
@Override
public BytecodeMetadata<? extends CfOrDexInstruction> getMetadata() {
// Bytecode metadata is recomputed when finalizing via IR.
throw new Unreachable();
}
@Override
public BytecodeInstructionMetadata getMetadata(CfOrDexInstruction instruction) {
// Bytecode metadata is recomputed when finalizing via IR.
throw new Unreachable();
}
@Override
public void clearMetadata() {
metadataMap = null;
}
@Override
public LirIterator iterator() {
return new LirIterator(new ByteArrayIterator(instructions));
}
@Override
public IRCode buildIR(
ProgramMethod method,
AppView<?> appView,
MutableMethodConversionOptions conversionOptions) {
GraphLens codeLens = method.getDefinition().getCode().getCodeLens(appView);
RewrittenPrototypeDescription protoChanges =
appView
.graphLens()
.lookupPrototypeChangesForMethodDefinition(method.getReference(), codeLens);
return internalBuildIR(
method, appView, new NumberGenerator(), null, protoChanges, conversionOptions);
}
@Override
public IRCode buildInliningIR(
ProgramMethod context,
ProgramMethod method,
AppView<?> appView,
GraphLens codeLens,
NumberGenerator valueNumberGenerator,
Position callerPosition,
RewrittenPrototypeDescription protoChanges) {
assert valueNumberGenerator != null;
assert callerPosition != null;
assert protoChanges != null;
return internalBuildIR(
method,
appView,
valueNumberGenerator,
callerPosition,
protoChanges,
MethodConversionOptions.nonConverting());
}
private IRCode internalBuildIR(
ProgramMethod method,
AppView<?> appView,
NumberGenerator valueNumberGenerator,
Position callerPosition,
RewrittenPrototypeDescription protoChanges,
MutableMethodConversionOptions conversionOptions) {
LirCode<Integer> typedLir = asLirCode();
return Lir2IRConverter.translate(
method,
typedLir,
LirStrategy.getDefaultStrategy().getDecodingStrategy(typedLir, valueNumberGenerator),
appView,
callerPosition,
protoChanges,
conversionOptions);
}
@Override
public void registerCodeReferences(ProgramMethod method, UseRegistry registry) {
assert registry.getTraversalContinuation().shouldContinue();
for (PositionEntry positionEntry : positionTable) {
if (positionEntry.hasCallerPosition()) {
registry.registerInliningPosition(
positionEntry.getPosition(
method.getReference(), method.getDefinition().isD8R8Synthesized()));
}
}
LirUseRegistryCallback<EV> registryCallbacks = new LirUseRegistryCallback<>(this, registry);
for (LirInstructionView view : this) {
if (metadataMap != null) {
registryCallbacks.setCurrentMetadata(metadataMap.get(view.getInstructionIndex()));
}
registryCallbacks.onInstructionView(view);
if (registry.getTraversalContinuation().shouldBreak()) {
return;
}
}
if (tryCatchTable != null) {
for (CatchHandlers<Integer> handler : tryCatchTable.tryCatchHandlers.values()) {
for (DexType guard : handler.getGuards()) {
registry.registerExceptionGuard(guard);
if (registry.getTraversalContinuation().shouldBreak()) {
return;
}
}
}
}
}
@Override
public void registerCodeReferencesForDesugaring(ClasspathMethod method, UseRegistry registry) {
throw new Unimplemented();
}
@Override
public Int2ReferenceMap<DebugLocalInfo> collectParameterInfo(
DexEncodedMethod encodedMethod, AppView<?> appView) {
throw new Unimplemented();
}
@Override
public void registerArgumentReferences(DexEncodedMethod method, ArgumentUse registry) {
throw new Unimplemented();
}
@Override
public String toString() {
return new LirPrinter<>(this).prettyPrint();
}
@Override
public String toString(DexEncodedMethod method, RetracerForCodePrinting retracer) {
// TODO(b/225838009): Add retracing to printer.
return toString();
}
@Override
public int estimatedDexCodeSizeUpperBoundInBytes() {
return getEstimatedDexSizeForInlining();
}
@Override
public int getEstimatedSizeForInliningIfLessThanOrEquals(int threshold) {
if (useDexEstimationStrategy) {
return getEstimatedDexSizeForInliningIfLessThanOrEquals(threshold);
} else {
// TODO(b/225838009): Currently the size estimation for CF has size one for each instruction
// (even switches!) and ignores stack instructions, thus loads to arguments are not included.
// The result is a much smaller estimate than for DEX. Once LIR is in place we should use the
// same estimate for both.
int estimatedSizedForInlining = instructionCount;
if (estimatedSizedForInlining <= threshold) {
return estimatedSizedForInlining;
}
return -1;
}
}
private int getEstimatedDexSizeForInlining() {
return getEstimatedDexSizeForInliningIfLessThanOrEquals(Integer.MAX_VALUE);
}
private int getEstimatedDexSizeForInliningIfLessThanOrEquals(int threshold) {
LirSizeEstimation<EV> estimation = new LirSizeEstimation<>(this);
for (LirInstructionView view : this) {
estimation.onInstructionView(view);
if (estimation.getSizeEstimate() > threshold) {
return -1;
}
}
return estimation.getSizeEstimate();
}
public Position getPreamblePosition(DexMethod method, boolean isD8R8Synthesized) {
if (positionTable.length > 0 && positionTable[0].fromInstructionIndex == 0) {
return positionTable[0].getPosition(method, isD8R8Synthesized);
}
return SyntheticPosition.builder()
.setLine(0)
.setMethod(method)
.setIsD8R8Synthesized(isD8R8Synthesized)
.build();
}
public PositionEntry[] getPositionTableAsInlining(
Position callerPosition,
DexMethod callee,
boolean isCalleeD8R8Synthesized,
Consumer<Position> preamblePositionConsumer) {
// Fast path for moving a synthetic method with no actual line info.
if (isCalleeD8R8Synthesized && positionTable.length == 0) {
preamblePositionConsumer.accept(callerPosition);
return PositionEntry.EMPTY_ARRAY;
}
Position calleePreamble = getPreamblePosition(callee, isCalleeD8R8Synthesized);
CanonicalPositions canonicalPositions =
new CanonicalPositions(
callerPosition, positionTable.length, callee, isCalleeD8R8Synthesized, calleePreamble);
PositionEntry[] newPositionTable;
if (positionTable.length == 0) {
newPositionTable =
new PositionEntry[] {
new StructuredPositionEntry(0, canonicalPositions.getPreamblePosition())
};
} else {
newPositionTable = new PositionEntry[positionTable.length];
for (int i = 0; i < positionTable.length; i++) {
PositionEntry inlineeEntry = positionTable[i];
Position inlineePosition = inlineeEntry.getPosition(callee, isCalleeD8R8Synthesized);
newPositionTable[i] =
new StructuredPositionEntry(
inlineeEntry.getFromInstructionIndex(),
canonicalPositions.canonicalizePositionWithCaller(inlineePosition));
}
}
preamblePositionConsumer.accept(canonicalPositions.getPreamblePosition());
return newPositionTable;
}
@Override
public Code getCodeAsInlining(
DexMethod caller,
boolean isCallerD8R8Synthesized,
DexMethod callee,
boolean isCalleeD8R8Synthesized,
DexItemFactory factory) {
Position callerPosition =
SyntheticPosition.builder().setLine(0).setMethod(caller).setIsD8R8Synthesized(true).build();
PositionEntry[] newPositionTable =
getPositionTableAsInlining(callerPosition, callee, isCalleeD8R8Synthesized, unused -> {});
if (Arrays.equals(positionTable, newPositionTable)) {
return this;
}
return new LirCode<>(
constants,
newPositionTable,
argumentCount,
instructions,
instructionCount,
tryCatchTable,
debugLocalInfoTable,
strategyInfo,
useDexEstimationStrategy,
metadataMap);
}
@Override
public boolean isEmptyVoidMethod() {
for (LirInstructionView view : this) {
int opcode = view.getOpcode();
if (opcode != LirOpcodes.RETURN && opcode != LirOpcodes.DEBUGPOS) {
return false;
}
}
return true;
}
@Override
public boolean hasMonitorInstructions() {
for (LirInstructionView view : this) {
int opcode = view.getOpcode();
if (opcode == LirOpcodes.MONITORENTER || opcode == LirOpcodes.MONITOREXIT) {
return true;
}
}
return false;
}
@Override
public void forEachPosition(
DexMethod method, boolean isD8R8Synthesized, Consumer<Position> positionConsumer) {
for (PositionEntry entry : positionTable) {
positionConsumer.accept(entry.getPosition(method, isD8R8Synthesized));
}
}
public LirCode<EV> newCodeWithRewrittenConstantPool(Function<LirConstant, LirConstant> rewriter) {
LirConstant[] rewrittenConstants = ArrayUtils.map(constants, rewriter, new LirConstant[0]);
if (constants == rewrittenConstants) {
return this;
}
return new LirCode<>(
rewrittenConstants,
positionTable,
argumentCount,
instructions,
instructionCount,
tryCatchTable,
debugLocalInfoTable,
strategyInfo,
useDexEstimationStrategy,
metadataMap);
}
public LirCode<EV> newCodeWithRewrittenTryCatchTable(TryCatchTable rewrittenTryCatchTable) {
if (rewrittenTryCatchTable == tryCatchTable) {
return this;
}
return new LirCode<>(
constants,
positionTable,
argumentCount,
instructions,
instructionCount,
rewrittenTryCatchTable,
debugLocalInfoTable,
strategyInfo,
useDexEstimationStrategy,
metadataMap);
}
public LirCode<EV> rewriteWithLens(
ProgramMethod context,
AppView<? extends AppInfoWithClassHierarchy> appView,
LensCodeRewriterUtils rewriterUtils) {
GraphLens graphLens = appView.graphLens();
assert graphLens.isNonIdentityLens();
if (graphLens.isMemberRebindingIdentityLens()) {
return this;
}
LirLensCodeRewriter<EV> rewriter =
new LirLensCodeRewriter<>(appView, this, context, rewriterUtils);
return rewriter.rewrite();
}
public LirCode<EV> copyWithNewConstantsAndInstructions(
LirConstant[] constants, byte[] instructions) {
return new LirCode<>(
constants,
positionTable,
argumentCount,
instructions,
instructionCount,
tryCatchTable,
debugLocalInfoTable,
strategyInfo,
useDexEstimationStrategy,
metadataMap);
}
}