blob: a83524c642aeadf7fdb3019f0f1a1a2f048a1e0b [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.graph.DexField;
import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.IfType;
import com.android.tools.r8.ir.code.NumericType;
import java.util.ArrayList;
import java.util.List;
/**
* Structured callbacks for interpreting LIR.
*
* <p>This callback parses the actual instructions and dispatches to instruction specific methods
* where the parsed data is provided as arguments. Instructions that are part of a family of
* instructions have a default implementation that will call the "instruction family" methods (e.g.,
* onInvokeVirtual will default dispatch to onInvokedMethodInstruction).
*
* <p>Due to the parsing of the individual instructions, this parser has a higher overhead than
* using the basic {@link LirInstructionView}.
*/
public abstract class LirParsedInstructionCallback<EV> implements LirInstructionCallback {
private final LirCode<EV> code;
public LirParsedInstructionCallback(LirCode<EV> code) {
this.code = code;
}
/** Returns the index for the value associated with the current argument/instruction. */
public abstract int getCurrentValueIndex();
public final int getCurrentInstructionIndex() {
return getCurrentValueIndex() - code.getArgumentCount();
}
private EV getActualValueIndex(int relativeValueIndex) {
return code.decodeValueIndex(relativeValueIndex, getCurrentValueIndex());
}
private EV getNextValueOperand(LirInstructionView view) {
return getActualValueIndex(view.getNextValueOperand());
}
public void onInstruction() {}
public void onConstNull() {
onInstruction();
}
public void onConstNumber(NumericType type, long value) {
onInstruction();
}
public void onConstInt(int value) {
onConstNumber(NumericType.INT, value);
}
public void onConstString(DexString string) {
onInstruction();
}
public void onDiv(NumericType type, EV leftValueIndex, EV rightValueIndex) {
onInstruction();
}
public void onDivInt(EV leftValueIndex, EV rightValueIndex) {
onDiv(NumericType.INT, leftValueIndex, rightValueIndex);
}
public void onIf(IfType ifKind, int blockIndex, EV valueIndex) {
onInstruction();
}
public void onGoto(int blockIndex) {
onInstruction();
}
public void onFallthrough() {
onInstruction();
}
public void onMoveException(DexType exceptionType) {
onInstruction();
}
public void onDebugLocalWrite(EV srcIndex) {
onInstruction();
}
public void onInvokeMethodInstruction(DexMethod method, List<EV> arguments) {
onInstruction();
}
public void onInvokeDirect(DexMethod method, List<EV> arguments) {
onInvokeMethodInstruction(method, arguments);
}
public void onInvokeVirtual(DexMethod method, List<EV> arguments) {
onInvokeMethodInstruction(method, arguments);
}
public void onInvokeStatic(DexMethod method, List<EV> arguments) {
onInvokeMethodInstruction(method, arguments);
}
public void onInvokeInterface(DexMethod method, List<EV> arguments) {
onInvokeMethodInstruction(method, arguments);
}
public void onNewInstance(DexType clazz) {
onInstruction();
}
public void onFieldInstruction(DexField field) {
onInstruction();
}
public void onStaticGet(DexField field) {
onFieldInstruction(field);
}
public abstract void onInstanceGet(DexField field, EV object);
public abstract void onInstancePut(DexField field, EV object, EV value);
public void onReturnVoid() {
onInstruction();
}
public void onArrayLength(EV arrayValueIndex) {
onInstruction();
}
public void onDebugPosition() {
onInstruction();
}
public void onPhi(DexType type, List<EV> operands) {
onInstruction();
}
public void onCmpInstruction(int opcode, EV leftValue, EV rightValue) {
assert LirOpcodes.LCMP <= opcode && opcode <= LirOpcodes.DCMPG;
onInstruction();
}
private DexItem getConstantItem(int index) {
return code.getConstantItem(index);
}
@Override
public void onInstructionView(LirInstructionView view) {
int opcode = view.getOpcode();
switch (opcode) {
case LirOpcodes.ACONST_NULL:
{
onConstNull();
return;
}
case LirOpcodes.LDC:
{
DexItem item = getConstantItem(view.getNextConstantOperand());
if (item instanceof DexString) {
onConstString((DexString) item);
return;
}
throw new Unimplemented();
}
case LirOpcodes.ICONST_M1:
case LirOpcodes.ICONST_0:
case LirOpcodes.ICONST_1:
case LirOpcodes.ICONST_2:
case LirOpcodes.ICONST_3:
case LirOpcodes.ICONST_4:
case LirOpcodes.ICONST_5:
{
int value = opcode - LirOpcodes.ICONST_0;
onConstInt(value);
return;
}
case LirOpcodes.ICONST:
{
int value = view.getNextIntegerOperand();
onConstInt(value);
return;
}
case LirOpcodes.IDIV:
{
EV leftValueIndex = getNextValueOperand(view);
EV rightValueIndex = getNextValueOperand(view);
onDivInt(leftValueIndex, rightValueIndex);
return;
}
case LirOpcodes.IFNE:
{
int blockIndex = view.getNextBlockOperand();
EV valueIndex = getNextValueOperand(view);
onIf(IfType.NE, blockIndex, valueIndex);
return;
}
case LirOpcodes.GOTO:
{
int blockIndex = view.getNextBlockOperand();
onGoto(blockIndex);
return;
}
case LirOpcodes.INVOKEDIRECT:
{
DexMethod target = getInvokeInstructionTarget(view);
List<EV> arguments = getInvokeInstructionArguments(view);
onInvokeDirect(target, arguments);
return;
}
case LirOpcodes.INVOKEVIRTUAL:
{
DexMethod target = getInvokeInstructionTarget(view);
List<EV> arguments = getInvokeInstructionArguments(view);
onInvokeVirtual(target, arguments);
return;
}
case LirOpcodes.INVOKESTATIC:
{
DexMethod target = getInvokeInstructionTarget(view);
List<EV> arguments = getInvokeInstructionArguments(view);
onInvokeStatic(target, arguments);
return;
}
case LirOpcodes.INVOKEINTERFACE:
{
DexMethod target = getInvokeInstructionTarget(view);
List<EV> arguments = getInvokeInstructionArguments(view);
onInvokeInterface(target, arguments);
return;
}
case LirOpcodes.NEW:
{
DexItem item = getConstantItem(view.getNextConstantOperand());
if (item instanceof DexType) {
onNewInstance((DexType) item);
return;
}
throw new Unimplemented();
}
case LirOpcodes.GETSTATIC:
{
DexField field = (DexField) getConstantItem(view.getNextConstantOperand());
onStaticGet(field);
return;
}
case LirOpcodes.GETFIELD:
{
DexField field = (DexField) getConstantItem(view.getNextConstantOperand());
EV object = getNextValueOperand(view);
onInstanceGet(field, object);
return;
}
case LirOpcodes.PUTFIELD:
{
DexField field = (DexField) getConstantItem(view.getNextConstantOperand());
EV object = getNextValueOperand(view);
EV value = getNextValueOperand(view);
onInstancePut(field, object, value);
return;
}
case LirOpcodes.RETURN:
{
onReturnVoid();
return;
}
case LirOpcodes.ARRAYLENGTH:
{
onArrayLength(getNextValueOperand(view));
return;
}
case LirOpcodes.DEBUGPOS:
{
onDebugPosition();
return;
}
case LirOpcodes.PHI:
{
DexType type = (DexType) getConstantItem(view.getNextConstantOperand());
List<EV> operands = new ArrayList<>();
while (view.hasMoreOperands()) {
operands.add(getNextValueOperand(view));
}
onPhi(type, operands);
return;
}
case LirOpcodes.FALLTHROUGH:
{
onFallthrough();
return;
}
case LirOpcodes.MOVEEXCEPTION:
{
DexType type = (DexType) getConstantItem(view.getNextConstantOperand());
onMoveException(type);
return;
}
case LirOpcodes.DEBUGLOCALWRITE:
{
EV srcIndex = getNextValueOperand(view);
onDebugLocalWrite(srcIndex);
return;
}
case LirOpcodes.LCMP:
case LirOpcodes.FCMPL:
case LirOpcodes.FCMPG:
case LirOpcodes.DCMPL:
case LirOpcodes.DCMPG:
{
EV leftValue = getNextValueOperand(view);
EV rightValue = getNextValueOperand(view);
onCmpInstruction(opcode, leftValue, rightValue);
return;
}
default:
throw new Unimplemented("No dispatch for opcode " + LirOpcodes.toString(opcode));
}
}
private DexMethod getInvokeInstructionTarget(LirInstructionView view) {
return (DexMethod) getConstantItem(view.getNextConstantOperand());
}
private List<EV> getInvokeInstructionArguments(LirInstructionView view) {
List<EV> arguments = new ArrayList<>();
while (view.hasMoreOperands()) {
arguments.add(getNextValueOperand(view));
}
return arguments;
}
}