| // 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.CatchHandlers.CatchHandler; |
| 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.lightir.LirBuilder.StringSwitchPayload; |
| 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 "v" + 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); |
| if (code.getTryCatchTable() != null) { |
| builder.append("try-catch-handlers:\n"); |
| code.getTryCatchTable() |
| .forEachHandler( |
| (index, handlers) -> { |
| builder.append(index).append(":\n"); |
| for (CatchHandler<Integer> handler : handlers) { |
| builder |
| .append(handler.getGuard()) |
| .append(" -> ") |
| .append(handler.getTarget()) |
| .append('\n'); |
| } |
| }); |
| } |
| 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 onConstResourceNumber(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, boolean ignoreCompatRules) { |
| 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 onStringSwitch(EV value, StringSwitchPayload 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 onNewArrayFilled(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, boolean ignoreCompatRules) { |
| appendOutValue(); |
| appendValueArguments(value); |
| builder.append(type); |
| } |
| |
| @Override |
| public void onSafeCheckCast(DexType type, EV value) { |
| onCheckCast(type, value, true); |
| } |
| |
| @Override |
| public void onInstanceOf(DexType type, EV value) { |
| appendOutValue(); |
| appendValueArguments(value); |
| builder.append(type); |
| } |
| |
| @Override |
| public void onArrayGet(MemberType 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(List<EV> operands) { |
| appendOutValue(); |
| appendValueArguments(operands); |
| } |
| |
| @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); |
| } |
| } |