blob: a705fd236c62fbe9566162df73ac4445d21d5e16 [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.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 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 onConstString(DexString string) {
appendOutValue().append("str(").append(string).append(")");
}
@Override
public void onDiv(NumericType type, EV leftValueIndex, EV rightValueIndex) {
appendOutValue()
.append(fmtValueIndex(leftValueIndex))
.append(' ')
.append(fmtValueIndex(rightValueIndex))
.append(' ')
.append(type);
}
@Override
public void onIf(IfType ifKind, int blockIndex, EV valueIndex) {
builder.append(fmtValueIndex(valueIndex)).append(' ').append(fmtInsnIndex(blockIndex));
}
@Override
public void onGoto(int blockIndex) {
builder.append(fmtInsnIndex(blockIndex));
}
@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 onInvokeMethodInstruction(DexMethod method, List<EV> arguments) {
if (!method.getReturnType().isVoidType()) {
appendOutValue();
}
appendValueArguments(arguments);
builder.append(method);
}
@Override
public void onFieldInstruction(DexField field) {
builder.append(field);
}
@Override
public void onStaticGet(DexField field) {
appendOutValue();
super.onStaticGet(field);
}
@Override
public void onReturnVoid() {
// Nothing to append.
}
@Override
public void onArrayLength(EV arrayValueIndex) {
appendOutValue().append(fmtValueIndex(arrayValueIndex));
}
@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);
}
}