blob: 421be9bd15599ae57f5d711f6628f115c93e1139 [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.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.Add;
import com.android.tools.r8.ir.code.And;
import com.android.tools.r8.ir.code.Argument;
import com.android.tools.r8.ir.code.ArrayGet;
import com.android.tools.r8.ir.code.ArrayLength;
import com.android.tools.r8.ir.code.ArrayPut;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.CatchHandlers;
import com.android.tools.r8.ir.code.CheckCast;
import com.android.tools.r8.ir.code.Cmp;
import com.android.tools.r8.ir.code.Cmp.Bias;
import com.android.tools.r8.ir.code.ConstClass;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.ConstString;
import com.android.tools.r8.ir.code.DebugLocalRead;
import com.android.tools.r8.ir.code.DebugLocalWrite;
import com.android.tools.r8.ir.code.DebugPosition;
import com.android.tools.r8.ir.code.DexItemBasedConstString;
import com.android.tools.r8.ir.code.Div;
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.IfType;
import com.android.tools.r8.ir.code.InitClass;
import com.android.tools.r8.ir.code.InstanceGet;
import com.android.tools.r8.ir.code.InstanceOf;
import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.IntSwitch;
import com.android.tools.r8.ir.code.InvokeCustom;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeInterface;
import com.android.tools.r8.ir.code.InvokeMultiNewArray;
import com.android.tools.r8.ir.code.InvokeNewArray;
import com.android.tools.r8.ir.code.InvokePolymorphic;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.InvokeSuper;
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.Monitor;
import com.android.tools.r8.ir.code.MonitorType;
import com.android.tools.r8.ir.code.MoveException;
import com.android.tools.r8.ir.code.Mul;
import com.android.tools.r8.ir.code.Neg;
import com.android.tools.r8.ir.code.NewArrayEmpty;
import com.android.tools.r8.ir.code.NewArrayFilledData;
import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.code.NewUnboxedEnumInstance;
import com.android.tools.r8.ir.code.Not;
import com.android.tools.r8.ir.code.NumberConversion;
import com.android.tools.r8.ir.code.NumberGenerator;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.Or;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Position.SyntheticPosition;
import com.android.tools.r8.ir.code.Rem;
import com.android.tools.r8.ir.code.Return;
import com.android.tools.r8.ir.code.Shl;
import com.android.tools.r8.ir.code.Shr;
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Sub;
import com.android.tools.r8.ir.code.Throw;
import com.android.tools.r8.ir.code.Ushr;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.code.Xor;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload;
import com.android.tools.r8.lightir.LirCode.PositionEntry;
import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
import com.android.tools.r8.utils.ListUtils;
import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
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;
import java.util.List;
public class Lir2IRConverter {
private Lir2IRConverter() {}
public static <EV> IRCode translate(
ProgramMethod method,
LirCode<EV> lirCode,
LirDecodingStrategy<Value, EV> strategy,
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);
}
/**
* When building IR the structured LIR parser is used to obtain the decoded operand indexes. The
* below parser subclass handles translation of indexes to SSA values.
*/
private static class Parser<EV> extends LirParsedInstructionCallback<EV> {
private static final int ENTRY_BLOCK_INDEX = -1;
private final AppView<?> appView;
private final LirCode<EV> code;
private final LirDecodingStrategy<Value, EV> strategy;
private final NumberGenerator valueNumberGenerator = new NumberGenerator();
private final NumberGenerator basicBlockNumberGenerator = new NumberGenerator();
private final Int2ReferenceMap<BasicBlock> blocks = new Int2ReferenceOpenHashMap<>();
private BasicBlock currentBlock = null;
private int nextInstructionIndex = 0;
private Position currentPosition;
private PositionEntry nextPositionEntry = null;
private int nextIndexInPositionsTable = 0;
public Parser(
LirCode<EV> code,
DexMethod method,
AppView<?> appView,
LirDecodingStrategy<Value, EV> strategy) {
super(code);
this.appView = appView;
this.code = code;
this.strategy = strategy;
// Recreate the preamble position. This is active for arguments and code with no positions.
currentPosition = SyntheticPosition.builder().setLine(0).setMethod(method).build();
}
@Override
public int getCurrentValueIndex() {
return nextInstructionIndex + code.getArgumentCount();
}
private void closeCurrentBlock() {
currentBlock = null;
}
private void ensureCurrentBlock() {
// Control instructions must close the block, thus the current block is null iff the
// instruction denotes a new block.
if (currentBlock == null) {
currentBlock = getBasicBlock(nextInstructionIndex);
CatchHandlers<Integer> handlers =
code.getTryCatchTable().getHandlersForBlock(nextInstructionIndex);
if (handlers != null) {
List<BasicBlock> targets = ListUtils.map(handlers.getAllTargets(), this::getBasicBlock);
targets.forEach(currentBlock::link);
currentBlock.linkCatchSuccessors(handlers.getGuards(), targets);
}
} else {
assert !blocks.containsKey(nextInstructionIndex);
}
}
private void ensureCurrentPosition() {
if (nextPositionEntry != null
&& nextPositionEntry.fromInstructionIndex <= nextInstructionIndex) {
currentPosition = nextPositionEntry.position;
advanceNextPositionEntry();
}
}
private void advanceNextPositionEntry() {
nextPositionEntry =
nextIndexInPositionsTable < code.getPositionTable().length
? code.getPositionTable()[nextIndexInPositionsTable++]
: null;
}
public void parseArguments(ProgramMethod method) {
currentBlock = getBasicBlock(ENTRY_BLOCK_INDEX);
boolean hasReceiverArgument = !method.getDefinition().isStatic();
assert code.getArgumentCount()
== method.getParameters().size() + (hasReceiverArgument ? 1 : 0);
if (hasReceiverArgument) {
addThisArgument(method.getHolderType());
}
int index = hasReceiverArgument ? 1 : 0;
for (DexType parameter : method.getParameters()) {
addArgument(parameter, index++);
}
// Set up position state after adding arguments.
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) {
LinkedList<BasicBlock> blockList = new LinkedList<>();
IntList blockIndices = new IntArrayList(blocks.keySet());
blockIndices.sort(Integer::compare);
for (int i = 0; i < blockIndices.size(); i++) {
BasicBlock block = blocks.get(blockIndices.getInt(i));
block.setFilled();
blockList.add(block);
// LIR has no value-user info so after building is done, removed unused values.
for (Instruction instruction : block.getInstructions()) {
if (instruction.hasOutValue()
&& instruction.isInvoke()
&& instruction.hasUnusedOutValue()) {
instruction.clearOutValue();
}
}
}
for (int i = 0; i < peekNextInstructionIndex(); ++i) {
valueNumberGenerator.next();
}
return new IRCode(
appView.options(),
method,
Position.syntheticNone(),
blockList,
valueNumberGenerator,
basicBlockNumberGenerator,
code.getMetadata(),
method.getOrigin(),
new MutableMethodConversionOptions(appView.options()));
}
public BasicBlock getBasicBlock(int instructionIndex) {
return blocks.computeIfAbsent(
instructionIndex,
k -> {
BasicBlock block = new BasicBlock();
block.setNumber(basicBlockNumberGenerator.next());
return block;
});
}
public Value getValue(EV encodedValue) {
return strategy.getValue(encodedValue, code.getStrategyInfo());
}
public List<Value> getValues(List<EV> indices) {
List<Value> arguments = new ArrayList<>(indices.size());
for (int i = 0; i < indices.size(); i++) {
arguments.add(getValue(indices.get(i)));
}
return arguments;
}
public int toInstructionIndexInIR(int lirIndex) {
return lirIndex + code.getArgumentCount();
}
public int peekNextInstructionIndex() {
return nextInstructionIndex;
}
public Value getOutValueForNextInstruction(TypeElement type) {
int valueIndex = toInstructionIndexInIR(peekNextInstructionIndex());
return strategy.getValueDefinitionForInstructionIndex(
valueIndex, type, code::getDebugLocalInfo);
}
public Phi getPhiForNextInstructionAndAdvanceState(TypeElement type) {
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() {
ensureCurrentBlock();
ensureCurrentPosition();
++nextInstructionIndex;
}
private void addInstruction(Instruction instruction) {
int index = toInstructionIndexInIR(peekNextInstructionIndex());
advanceInstructionState();
instruction.setPosition(currentPosition);
currentBlock.getInstructions().add(instruction);
instruction.setBlock(currentBlock);
int[] debugEndIndices = code.getDebugLocalEnds(index);
if (debugEndIndices != null) {
for (int encodedDebugEndIndex : debugEndIndices) {
EV debugEndIndex = code.decodeValueIndex(encodedDebugEndIndex, index);
Value debugValue = getValue(debugEndIndex);
debugValue.addDebugLocalEnd(instruction);
}
}
}
private void addThisArgument(DexType type) {
Argument argument = addArgument(type, 0);
argument.outValue().markAsThis();
}
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.
TypeElement typeElement = type.toTypeElement(appView);
Value dest =
strategy.getValueDefinitionForInstructionIndex(
index, typeElement, code::getDebugLocalInfo);
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
public void onInstruction() {
throw new Unimplemented("Missing IR conversion");
}
@Override
public void onConstNull() {
Value dest = getOutValueForNextInstruction(TypeElement.getNull());
addInstruction(new ConstNumber(dest, 0));
}
@Override
public void onConstInt(int value) {
Value dest = getOutValueForNextInstruction(TypeElement.getInt());
addInstruction(new ConstNumber(dest, value));
}
@Override
public void onConstFloat(int value) {
Value dest = getOutValueForNextInstruction(TypeElement.getFloat());
addInstruction(new ConstNumber(dest, value));
}
@Override
public void onConstLong(long value) {
Value dest = getOutValueForNextInstruction(TypeElement.getLong());
addInstruction(new ConstNumber(dest, value));
}
@Override
public void onConstDouble(long value) {
Value dest = getOutValueForNextInstruction(TypeElement.getDouble());
addInstruction(new ConstNumber(dest, value));
}
TypeElement valueTypeElement(NumericType type) {
return PrimitiveTypeElement.fromNumericType(type);
}
@Override
public void onAdd(NumericType type, EV leftValueIndex, EV rightValueIndex) {
Value dest = getOutValueForNextInstruction(valueTypeElement(type));
addInstruction(
Add.createNonNormalized(type, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
}
@Override
public void onSub(NumericType type, EV leftValueIndex, EV rightValueIndex) {
Value dest = getOutValueForNextInstruction(valueTypeElement(type));
addInstruction(new Sub(type, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
}
@Override
public void onMul(NumericType type, EV leftValueIndex, EV rightValueIndex) {
Value dest = getOutValueForNextInstruction(valueTypeElement(type));
addInstruction(
Mul.createNonNormalized(type, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
}
@Override
public void onDiv(NumericType type, EV leftValueIndex, EV rightValueIndex) {
Value dest = getOutValueForNextInstruction(valueTypeElement(type));
addInstruction(new Div(type, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
}
@Override
public void onRem(NumericType type, EV leftValueIndex, EV rightValueIndex) {
Value dest = getOutValueForNextInstruction(valueTypeElement(type));
addInstruction(new Rem(type, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
}
@Override
public void onNeg(NumericType type, EV value) {
Value dest = getOutValueForNextInstruction(valueTypeElement(type));
addInstruction(new Neg(type, dest, getValue(value)));
}
@Override
public void onNot(NumericType type, EV value) {
Value dest = getOutValueForNextInstruction(valueTypeElement(type));
addInstruction(new Not(type, dest, getValue(value)));
}
@Override
public void onShl(NumericType type, EV left, EV right) {
Value dest = getOutValueForNextInstruction(valueTypeElement(type));
addInstruction(new Shl(type, dest, getValue(left), getValue(right)));
}
@Override
public void onShr(NumericType type, EV left, EV right) {
Value dest = getOutValueForNextInstruction(valueTypeElement(type));
addInstruction(new Shr(type, dest, getValue(left), getValue(right)));
}
@Override
public void onUshr(NumericType type, EV left, EV right) {
Value dest = getOutValueForNextInstruction(valueTypeElement(type));
addInstruction(new Ushr(type, dest, getValue(left), getValue(right)));
}
@Override
public void onAnd(NumericType type, EV left, EV right) {
Value dest = getOutValueForNextInstruction(valueTypeElement(type));
addInstruction(And.createNonNormalized(type, dest, getValue(left), getValue(right)));
}
@Override
public void onOr(NumericType type, EV left, EV right) {
Value dest = getOutValueForNextInstruction(valueTypeElement(type));
addInstruction(Or.createNonNormalized(type, dest, getValue(left), getValue(right)));
}
@Override
public void onXor(NumericType type, EV left, EV right) {
Value dest = getOutValueForNextInstruction(valueTypeElement(type));
addInstruction(Xor.createNonNormalized(type, dest, getValue(left), getValue(right)));
}
@Override
public void onConstString(DexString string) {
Value dest =
getOutValueForNextInstruction(
TypeElement.stringClassType(appView, Nullability.definitelyNotNull()));
addInstruction(new ConstString(dest, string));
}
@Override
public void onDexItemBasedConstString(
DexReference item, NameComputationInfo<?> nameComputationInfo) {
Value dest =
getOutValueForNextInstruction(
TypeElement.stringClassType(appView, Nullability.definitelyNotNull()));
addInstruction(new DexItemBasedConstString(dest, item, nameComputationInfo));
}
@Override
public void onConstClass(DexType type) {
Value dest =
getOutValueForNextInstruction(
type.toTypeElement(appView, Nullability.definitelyNotNull()));
addInstruction(new ConstClass(dest, type));
}
@Override
public void onNumberConversion(NumericType from, NumericType to, EV value) {
Value dest =
getOutValueForNextInstruction(
to.toDexType(appView.dexItemFactory()).toTypeElement(appView));
addInstruction(new NumberConversion(from, to, dest, getValue(value)));
}
@Override
public void onIf(IfType ifKind, int blockIndex, EV 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 onIfCmp(IfType ifKind, int blockIndex, EV leftValueIndex, EV rightValueIndex) {
BasicBlock targetBlock = getBasicBlock(blockIndex);
Value leftValue = getValue(leftValueIndex);
Value rightValue = getValue(rightValueIndex);
addInstruction(new If(ifKind, ImmutableList.of(leftValue, rightValue)));
currentBlock.link(targetBlock);
currentBlock.link(getBasicBlock(nextInstructionIndex));
closeCurrentBlock();
}
@Override
public void onFallthrough() {
int nextBlockIndex = peekNextInstructionIndex() + 1;
onGoto(nextBlockIndex);
}
@Override
public void onGoto(int blockIndex) {
BasicBlock targetBlock = getBasicBlock(blockIndex);
addInstruction(new Goto());
currentBlock.link(targetBlock);
closeCurrentBlock();
}
@Override
public void onIntSwitch(EV value, IntSwitchPayload payload) {
// keys is the 'value' -> 'target index' mapping.
int[] keys = payload.keys;
// successorIndices is the 'target index' to 'IR successor index'.
int[] successorIndices = new int[keys.length];
List<BasicBlock> successorBlocks = new ArrayList<>();
{
// The mapping from instruction to successor is a temp mapping to track if any targets
// point to the same block.
Int2IntMap instructionToSuccessor = new Int2IntOpenHashMap();
for (int i = 0; i < successorIndices.length; i++) {
int instructionIndex = payload.targets[i];
if (instructionToSuccessor.containsKey(instructionIndex)) {
successorIndices[i] = instructionToSuccessor.get(instructionIndex);
} else {
int successorIndex = successorBlocks.size();
successorIndices[i] = successorIndex;
instructionToSuccessor.put(instructionIndex, successorIndex);
successorBlocks.add(getBasicBlock(instructionIndex));
}
}
}
int fallthrough = successorBlocks.size();
addInstruction(new IntSwitch(getValue(value), keys, successorIndices, fallthrough));
// The call to addInstruction will ensure the current block so don't amend to it before here.
// If the block has successors then the index mappings are not valid / need to be offset.
assert currentBlock.getSuccessors().isEmpty();
successorBlocks.forEach(currentBlock::link);
currentBlock.link(getBasicBlock(nextInstructionIndex));
closeCurrentBlock();
}
@Override
public void onInvokeDirect(DexMethod target, List<EV> arguments, boolean isInterface) {
Value dest = getInvokeInstructionOutputValue(target);
List<Value> ssaArgumentValues = getValues(arguments);
InvokeDirect instruction = new InvokeDirect(target, dest, ssaArgumentValues, isInterface);
addInstruction(instruction);
}
@Override
public void onInvokeSuper(DexMethod method, List<EV> arguments, boolean isInterface) {
Value dest = getInvokeInstructionOutputValue(method);
List<Value> ssaArgumentValues = getValues(arguments);
InvokeSuper instruction = new InvokeSuper(method, dest, ssaArgumentValues, isInterface);
addInstruction(instruction);
}
@Override
public void onInvokeVirtual(DexMethod target, List<EV> arguments) {
Value dest = getInvokeInstructionOutputValue(target);
List<Value> ssaArgumentValues = getValues(arguments);
InvokeVirtual instruction = new InvokeVirtual(target, dest, ssaArgumentValues);
addInstruction(instruction);
}
@Override
public void onInvokeStatic(DexMethod target, List<EV> arguments, boolean isInterface) {
Value dest = getInvokeInstructionOutputValue(target);
List<Value> ssaArgumentValues = getValues(arguments);
InvokeStatic instruction = new InvokeStatic(target, dest, ssaArgumentValues, isInterface);
addInstruction(instruction);
}
@Override
public void onInvokeInterface(DexMethod target, List<EV> arguments) {
Value dest = getInvokeInstructionOutputValue(target);
List<Value> ssaArgumentValues = getValues(arguments);
InvokeInterface instruction = new InvokeInterface(target, dest, ssaArgumentValues);
addInstruction(instruction);
}
@Override
public void onInvokeCustom(DexCallSite callSite, List<EV> arguments) {
Value dest = getInvokeInstructionOutputValue(callSite.methodProto);
List<Value> ssaArgumentValues = getValues(arguments);
InvokeCustom instruction = new InvokeCustom(callSite, dest, ssaArgumentValues);
addInstruction(instruction);
}
@Override
public void onInvokePolymorphic(DexMethod target, DexProto proto, List<EV> arguments) {
Value dest = getInvokeInstructionOutputValue(target);
List<Value> ssaArgumentValues = getValues(arguments);
InvokePolymorphic instruction = new InvokePolymorphic(target, proto, dest, ssaArgumentValues);
addInstruction(instruction);
}
private Value getInvokeInstructionOutputValue(DexMethod target) {
return getInvokeInstructionOutputValue(target.getProto());
}
private Value getInvokeInstructionOutputValue(DexProto target) {
DexType returnType = target.getReturnType();
return returnType.isVoidType()
? null
: getOutValueForNextInstruction(returnType.toTypeElement(appView));
}
@Override
public void onNewInstance(DexType clazz) {
TypeElement type = TypeElement.fromDexType(clazz, Nullability.definitelyNotNull(), appView);
Value dest = getOutValueForNextInstruction(type);
addInstruction(new NewInstance(clazz, dest));
}
@Override
public void onStaticGet(DexField field) {
Value dest = getOutValueForNextInstruction(field.getTypeElement(appView));
addInstruction(new StaticGet(dest, field));
}
@Override
public void onStaticPut(DexField field, EV value) {
addInstruction(new StaticPut(getValue(value), field));
}
@Override
public void onInstanceGet(DexField field, EV object) {
Value dest = getOutValueForNextInstruction(field.getTypeElement(appView));
addInstruction(new InstanceGet(dest, getValue(object), field));
}
@Override
public void onInstancePut(DexField field, EV object, EV value) {
addInstruction(
InstancePut.createPotentiallyInvalid(field, getValue(object), getValue(value)));
}
@Override
public void onNewArrayEmpty(DexType type, EV size) {
Value dest = getOutValueForNextInstruction(type.toTypeElement(appView));
addInstruction(new NewArrayEmpty(dest, getValue(size), type));
}
@Override
public void onThrow(EV exception) {
addInstruction(new Throw(getValue(exception)));
closeCurrentBlock();
}
@Override
public void onReturnVoid() {
addInstruction(new Return());
closeCurrentBlock();
}
@Override
public void onReturn(EV value) {
addInstruction(new Return(getValue(value)));
closeCurrentBlock();
}
@Override
public void onArrayLength(EV arrayValueIndex) {
Value dest = getOutValueForNextInstruction(TypeElement.getInt());
Value arrayValue = getValue(arrayValueIndex);
addInstruction(new ArrayLength(dest, arrayValue));
}
@Override
public void onCheckCast(DexType type, EV value) {
Value dest = getOutValueForNextInstruction(type.toTypeElement(appView));
addInstruction(new CheckCast(dest, getValue(value), type));
}
@Override
public void onInstanceOf(DexType type, EV value) {
Value dest = getOutValueForNextInstruction(TypeElement.getInt());
addInstruction(new InstanceOf(dest, getValue(value), type));
}
@Override
public void onDebugPosition() {
addInstruction(new DebugPosition());
}
@Override
public void onPhi(DexType type, List<EV> 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.get(i)));
}
phi.addOperands(values);
}
@Override
public void onMoveException(DexType exceptionType) {
Value dest = getOutValueForNextInstruction(exceptionType.toTypeElement(appView));
addInstruction(new MoveException(dest, exceptionType, appView.options()));
}
@Override
public void onDebugLocalWrite(EV srcIndex) {
Value src = getValue(srcIndex);
// The type is in the local table, so initialize it with bottom and reset with the local info.
Value dest = getOutValueForNextInstruction(TypeElement.getBottom());
TypeElement type = dest.getLocalInfo().type.toTypeElement(appView);
dest.setType(type);
addInstruction(new DebugLocalWrite(dest, src));
}
@Override
public void onDebugLocalRead() {
addInstruction(new DebugLocalRead());
}
@Override
public void onInvokeMultiNewArray(DexType type, List<EV> arguments) {
Value dest =
getOutValueForNextInstruction(
type.toTypeElement(appView, Nullability.definitelyNotNull()));
addInstruction(new InvokeMultiNewArray(type, dest, getValues(arguments)));
}
@Override
public void onInvokeNewArray(DexType type, List<EV> arguments) {
Value dest =
getOutValueForNextInstruction(
type.toTypeElement(appView, Nullability.definitelyNotNull()));
addInstruction(new InvokeNewArray(type, dest, getValues(arguments)));
}
@Override
public void onNewArrayFilledData(int elementWidth, long size, short[] data, EV src) {
addInstruction(new NewArrayFilledData(getValue(src), elementWidth, size, data));
}
@Override
public void onCmpInstruction(int opcode, EV leftIndex, EV rightIndex) {
NumericType type;
Bias bias;
switch (opcode) {
case LirOpcodes.LCMP:
type = NumericType.LONG;
bias = Bias.NONE;
break;
case LirOpcodes.FCMPL:
type = NumericType.FLOAT;
bias = Bias.LT;
break;
case LirOpcodes.FCMPG:
type = NumericType.FLOAT;
bias = Bias.GT;
break;
case LirOpcodes.DCMPL:
type = NumericType.DOUBLE;
bias = Bias.LT;
break;
case LirOpcodes.DCMPG:
type = NumericType.DOUBLE;
bias = Bias.GT;
break;
default:
throw new Unreachable("Unexpected cmp opcode: " + opcode);
}
Value leftValue = getValue(leftIndex);
Value rightValue = getValue(rightIndex);
Value dest = getOutValueForNextInstruction(TypeElement.getInt());
addInstruction(new Cmp(type, bias, dest, leftValue, rightValue));
}
@Override
public void onMonitorEnter(EV value) {
addInstruction(new Monitor(MonitorType.ENTER, getValue(value)));
}
@Override
public void onMonitorExit(EV value) {
addInstruction(new Monitor(MonitorType.EXIT, getValue(value)));
}
@Override
public void onArrayGetObject(DexType type, EV array, EV index) {
Value dest = getOutValueForNextInstruction(type.toTypeElement(appView));
addInstruction(new ArrayGet(MemberType.OBJECT, dest, getValue(array), getValue(index)));
}
@Override
public void onArrayGetPrimitive(MemberType type, EV array, EV index) {
// Convert the member type to a "stack value type", e.g., byte, char etc to int.
ValueType valueType = ValueType.fromMemberType(type);
DexType dexType = valueType.toDexType(appView.dexItemFactory());
Value dest = getOutValueForNextInstruction(dexType.toTypeElement(appView));
addInstruction(new ArrayGet(type, dest, getValue(array), getValue(index)));
}
@Override
public void onArrayPut(MemberType type, EV array, EV index, EV value) {
addInstruction(
ArrayPut.createWithoutVerification(
type, getValue(array), getValue(index), getValue(value)));
}
@Override
public void onNewUnboxedEnumInstance(DexType clazz, int ordinal) {
TypeElement type = TypeElement.fromDexType(clazz, Nullability.definitelyNotNull(), appView);
Value dest = getOutValueForNextInstruction(type);
addInstruction(new NewUnboxedEnumInstance(clazz, ordinal, dest));
}
@Override
public void onInitClass(DexType clazz) {
Value dest = getOutValueForNextInstruction(TypeElement.getInt());
addInstruction(new InitClass(dest, clazz));
}
}
}