| // 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 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 onThrow(EV exception) { |
| appendValueArguments(exception); |
| } |
| |
| @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); |
| } |
| } |