blob: 509289453d271617620214cff78500731e828b28 [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.DexCallSite;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodHandle;
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.ir.code.IfType;
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload;
import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
import com.android.tools.r8.utils.StringUtils;
import java.util.Arrays;
import java.util.List;
public class LirPrinter<EV> extends LirParsedInstructionCallback<EV> {
private static final String SEPERATOR = "\n";
private final LirCode<EV> code;
private final StringBuilder builder = new StringBuilder();
private final int instructionIndexPadding;
private final int instructionNamePadding;
private int valueIndex = 0;
private LirInstructionView view;
public LirPrinter(LirCode<EV> code) {
super(code);
this.code = code;
instructionIndexPadding =
Math.max(
fmtInsnIndex(-code.getArgumentCount()).length(),
fmtInsnIndex(code.getInstructionCount() - 1).length());
int maxNameLength = 0;
for (LirInstructionView view : code) {
maxNameLength = Math.max(maxNameLength, LirOpcodes.toString(view.getOpcode()).length());
}
instructionNamePadding = maxNameLength;
}
@Override
public int getCurrentValueIndex() {
return valueIndex;
}
private void advanceToNextValueIndex() {
valueIndex++;
}
private String fmtValueIndex(int valueIndex) {
return "v" + valueIndex;
}
private String fmtValueIndex(EV valueIndex) {
return valueIndex.toString();
}
private String fmtInsnIndex(int instructionIndex) {
return instructionIndex < 0 ? "--" : ("" + instructionIndex);
}
@SafeVarargs
private void appendValueArguments(EV... arguments) {
appendValueArguments(Arrays.asList(arguments));
}
private void appendValueArguments(List<EV> arguments) {
for (int i = 0; i < arguments.size(); i++) {
builder.append(fmtValueIndex(arguments.get(i))).append(' ');
}
}
public String prettyPrint() {
for (int i = 0; i < code.getArgumentCount(); i++) {
addInstructionHeader("ARG", 0);
appendOutValue();
advanceToNextValueIndex();
}
code.forEach(this::onInstructionView);
return builder.toString();
}
@Override
public void onInstructionView(LirInstructionView view) {
this.view = view;
assert view.getInstructionIndex() == getCurrentInstructionIndex();
int operandSizeInBytes = view.getRemainingOperandSizeInBytes();
int instructionSizeInBytes = operandSizeInBytes == 0 ? 1 : 2 + operandSizeInBytes;
String opcode = LirOpcodes.toString(view.getOpcode());
addInstructionHeader(opcode, instructionSizeInBytes);
super.onInstructionView(view);
advanceToNextValueIndex();
}
private void addInstructionHeader(String opcode, int instructionSize) {
if (getCurrentValueIndex() > 0) {
builder.append(SEPERATOR);
}
StringUtils.appendLeftPadded(
builder, fmtInsnIndex(getCurrentInstructionIndex()), instructionIndexPadding);
builder.append(':');
StringUtils.appendLeftPadded(
builder, Integer.toString(instructionSize), instructionIndexPadding);
builder.append(": ");
StringUtils.appendRightPadded(builder, opcode, instructionNamePadding);
builder.append(' ');
}
@Override
public void onInstruction() {
throw new Unimplemented(
"Printing of instruction missing: " + LirOpcodes.toString(view.getOpcode()));
}
private StringBuilder appendOutValue() {
return builder.append(fmtValueIndex(getCurrentValueIndex())).append(" <- ");
}
@Override
public void onConstNull() {
appendOutValue().append("null");
}
@Override
public void onConstInt(int value) {
appendOutValue().append(value);
}
@Override
public void onConstFloat(int value) {
appendOutValue().append(Float.intBitsToFloat(value));
}
@Override
public void onConstLong(long value) {
appendOutValue().append(value);
}
@Override
public void onConstDouble(long value) {
appendOutValue().append(Double.longBitsToDouble(value));
}
@Override
public void onConstString(DexString string) {
appendOutValue().append("str(").append(string).append(")");
}
@Override
public void onDexItemBasedConstString(
DexReference item, NameComputationInfo<?> nameComputationInfo) {
appendOutValue().append("item(").append(item).append(")");
}
@Override
public void onConstClass(DexType type) {
appendOutValue().append("class(").append(type).append(")");
}
@Override
public void onConstMethodHandle(DexMethodHandle methodHandle) {
appendOutValue().append("methodHandle(").append(methodHandle).append(")");
}
@Override
public void onConstMethodType(DexProto methodType) {
appendOutValue().append("methodType(").append(methodType).append(")");
}
@Override
public void onBinop(NumericType type, EV left, EV right) {
appendOutValue();
appendValueArguments(left, right);
}
@Override
public void onNeg(NumericType type, EV value) {
appendOutValue();
appendValueArguments(value);
}
@Override
public void onNot(NumericType type, EV value) {
appendOutValue();
appendValueArguments(value);
}
@Override
public void onNumberConversion(int opcode, EV value) {
appendOutValue();
appendValueArguments(value);
}
@Override
public void onIf(IfType ifKind, int blockIndex, EV valueIndex) {
appendValueArguments(valueIndex);
builder.append(fmtInsnIndex(blockIndex));
}
@Override
public void onIfCmp(IfType ifKind, int blockIndex, EV leftValueIndex, EV rightValueIndex) {
appendValueArguments(leftValueIndex, rightValueIndex);
builder.append(fmtInsnIndex(blockIndex));
}
@Override
public void onGoto(int blockIndex) {
builder.append(fmtInsnIndex(blockIndex));
}
@Override
public void onIntSwitch(EV value, IntSwitchPayload payload) {
appendValueArguments(value);
// TODO(b/225838009): Consider printing the switch payload info.
}
@Override
public void onFallthrough() {
// Nothing to append.
}
@Override
public void onMoveException(DexType exceptionType) {
appendOutValue().append(exceptionType);
}
@Override
public void onDebugLocalWrite(EV srcIndex) {
appendOutValue().append(fmtValueIndex(srcIndex));
}
@Override
public void onDebugLocalRead() {
// Nothing to add.
}
@Override
public void onInvokeMultiNewArray(DexType type, List<EV> arguments) {
appendOutValue();
appendValueArguments(arguments);
builder.append(type);
}
@Override
public void onInvokeNewArray(DexType type, List<EV> arguments) {
appendOutValue();
appendValueArguments(arguments);
builder.append(type);
}
@Override
public void onNewArrayFilledData(int elementWidth, long size, short[] data, EV src) {
appendValueArguments(src);
builder.append("w:").append(elementWidth).append(",s:").append(size);
}
@Override
public void onNewInstance(DexType clazz) {
appendOutValue();
builder.append(clazz);
}
@Override
public void onInvokeMethodInstruction(DexMethod method, List<EV> arguments) {
if (!method.getReturnType().isVoidType()) {
appendOutValue();
}
appendValueArguments(arguments);
builder.append(method);
}
@Override
public void onInvokeCustom(DexCallSite callSite, List<EV> arguments) {
appendValueArguments(arguments);
builder.append(callSite);
}
@Override
public void onInvokePolymorphic(DexMethod target, DexProto proto, List<EV> arguments) {
appendValueArguments(arguments);
builder.append(target).append(' ').append(proto);
}
@Override
public void onStaticGet(DexField field) {
appendOutValue();
builder.append(field).append(' ');
}
@Override
public void onStaticPut(DexField field, EV value) {
builder.append(field).append(' ');
appendValueArguments(value);
}
@Override
public void onInstanceGet(DexField field, EV object) {
appendOutValue();
builder.append(field).append(' ');
appendValueArguments(object);
}
@Override
public void onInstancePut(DexField field, EV object, EV value) {
builder.append(field).append(' ');
appendValueArguments(object, value);
}
@Override
public void onNewArrayEmpty(DexType type, EV size) {
appendOutValue();
builder.append(type).append(' ');
appendValueArguments(size);
}
@Override
public void onThrow(EV exception) {
appendValueArguments(exception);
}
@Override
public void onReturnVoid() {
// Nothing to append.
}
@Override
public void onReturn(EV value) {
appendValueArguments(value);
}
@Override
public void onArrayLength(EV arrayValueIndex) {
appendOutValue().append(fmtValueIndex(arrayValueIndex));
}
@Override
public void onCheckCast(DexType type, EV value) {
appendOutValue();
appendValueArguments(value);
builder.append(type);
}
@Override
public void onSafeCheckCast(DexType type, EV value) {
onCheckCast(type, value);
}
@Override
public void onInstanceOf(DexType type, EV value) {
appendOutValue();
appendValueArguments(value);
builder.append(type);
}
@Override
public void onArrayGetPrimitive(MemberType type, EV array, EV index) {
appendOutValue();
appendValueArguments(array, index);
builder.append(type);
}
@Override
public void onArrayGetObject(DexType type, EV array, EV index) {
appendOutValue();
appendValueArguments(array, index);
builder.append(type);
}
@Override
public void onArrayPut(MemberType type, EV array, EV index, EV value) {
appendValueArguments(array, index, value);
builder.append(type);
}
@Override
public void onDebugPosition() {
// Nothing to append.
}
@Override
public void onPhi(DexType type, List<EV> operands) {
appendOutValue();
appendValueArguments(operands);
builder.append(type);
}
@Override
public void onCmpInstruction(int opcode, EV leftValue, EV rightValue) {
appendOutValue();
appendValueArguments(leftValue, rightValue);
}
@Override
public void onMonitorEnter(EV value) {
appendValueArguments(value);
}
@Override
public void onMonitorExit(EV value) {
appendValueArguments(value);
}
@Override
public void onNewUnboxedEnumInstance(DexType type, int ordinal) {
appendOutValue().append("type(").append(type).append(") ordinal(").append(ordinal).append(")");
}
@Override
public void onInitClass(DexType clazz) {
builder.append(clazz);
}
@Override
public void onRecordFieldValues(DexField[] fields, List<EV> values) {
appendValueArguments(values);
}
}