blob: 3c0961e51ae008b5645d2617b8adf576ae406006 [file] [log] [blame]
// Copyright (c) 2023, 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.dex.code.DexAget;
import com.android.tools.r8.dex.code.DexAput;
import com.android.tools.r8.dex.code.DexArrayLength;
import com.android.tools.r8.dex.code.DexBase1Format;
import com.android.tools.r8.dex.code.DexBase2Format;
import com.android.tools.r8.dex.code.DexBase3Format;
import com.android.tools.r8.dex.code.DexCheckCast;
import com.android.tools.r8.dex.code.DexConst16;
import com.android.tools.r8.dex.code.DexConst4;
import com.android.tools.r8.dex.code.DexConstClass;
import com.android.tools.r8.dex.code.DexConstString;
import com.android.tools.r8.dex.code.DexConstWide16;
import com.android.tools.r8.dex.code.DexFillArrayData;
import com.android.tools.r8.dex.code.DexFillArrayDataPayload;
import com.android.tools.r8.dex.code.DexFilledNewArray;
import com.android.tools.r8.dex.code.DexGoto;
import com.android.tools.r8.dex.code.DexIfEq;
import com.android.tools.r8.dex.code.DexInstanceOf;
import com.android.tools.r8.dex.code.DexInvokeCustom;
import com.android.tools.r8.dex.code.DexInvokeVirtual;
import com.android.tools.r8.dex.code.DexMonitorEnter;
import com.android.tools.r8.dex.code.DexMonitorExit;
import com.android.tools.r8.dex.code.DexMove;
import com.android.tools.r8.dex.code.DexMoveException;
import com.android.tools.r8.dex.code.DexNewArray;
import com.android.tools.r8.dex.code.DexNewInstance;
import com.android.tools.r8.dex.code.DexNotInt;
import com.android.tools.r8.dex.code.DexNotLong;
import com.android.tools.r8.dex.code.DexPackedSwitch;
import com.android.tools.r8.dex.code.DexPackedSwitchPayload;
import com.android.tools.r8.dex.code.DexSget;
import com.android.tools.r8.dex.code.DexThrow;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload;
import com.android.tools.r8.lightir.LirBuilder.StringSwitchPayload;
public class LirSizeEstimation<EV> extends LirParsedInstructionCallback<EV> implements LirOpcodes {
private int sizeEstimate = 0;
LirSizeEstimation(LirCode<EV> code) {
super(code);
}
@Override
public int getCurrentValueIndex() {
// We don't use value information.
return 0;
}
public int getSizeEstimate() {
return sizeEstimate;
}
/**
* Most size information can be found just using opcode.
*
* <p>We overwrite the base view callback and only in the few payload instruction cases do we make
* use of the parsed-instruction callbacks.
*/
@Override
public void onInstructionView(LirInstructionView view) {
sizeEstimate += instructionSize(view.getOpcode(), view);
}
private static int baseSwitchSize(int cases) {
return DexPackedSwitch.SIZE + DexPackedSwitchPayload.SIZE + ((2 + 2) * cases);
}
@Override
public void onIntSwitch(EV unusedValue, IntSwitchPayload payload) {
sizeEstimate += baseSwitchSize(payload.keys.length);
}
@Override
public void onStringSwitch(EV value, StringSwitchPayload payload) {
// Invoke hashcode and switch on it.
sizeEstimate += DexInvokeVirtual.SIZE + baseSwitchSize(payload.keys.length);
// Each case has an additional const-string, invoke equals, if check, cont-number and goto.
sizeEstimate +=
payload.keys.length
* (DexConstString.SIZE
+ DexInvokeVirtual.SIZE
+ DexIfEq.SIZE
+ DexConst4.SIZE
+ DexGoto.SIZE);
// Finally the id is then the subject of another switch.
sizeEstimate += baseSwitchSize(payload.keys.length);
}
@Override
public void onNewArrayFilledData(int elementWidth, long size, short[] data, EV unusedSrc) {
sizeEstimate += DexFillArrayData.SIZE + DexFillArrayDataPayload.SIZE + 4 + data.length;
}
private int instructionSize(int opcode, LirInstructionView view) {
switch (opcode) {
case TABLESWITCH:
case STRINGSWITCH:
case NEWARRAYFILLEDDATA:
// The payload instructions use the "parsed callback" to compute the payloads.
super.onInstructionView(view);
// The full size is added by the callbacks so return zero here.
return 0;
case ACONST_NULL:
case ICONST_M1:
case ICONST_0:
case ICONST_1:
case ICONST_2:
case ICONST_3:
case ICONST_4:
case ICONST_5:
return DexConst4.SIZE;
case LCONST_0:
case LCONST_1:
case FCONST_0:
case FCONST_1:
case FCONST_2:
case DCONST_0:
case DCONST_1:
return DexConstWide16.SIZE;
case LDC:
// Most of the const loads are the same size (2).
return DexConstString.SIZE;
case IALOAD:
case LALOAD:
case FALOAD:
case DALOAD:
case AALOAD:
case BALOAD:
case CALOAD:
case SALOAD:
// The loads are all size 2.
return DexAget.SIZE;
case IASTORE:
case LASTORE:
case FASTORE:
case DASTORE:
case AASTORE:
case BASTORE:
case CASTORE:
case SASTORE:
// The loads are all size 2.
return DexAput.SIZE;
case IADD:
case LADD:
case FADD:
case DADD:
case ISUB:
case LSUB:
case FSUB:
case DSUB:
case IMUL:
case LMUL:
case FMUL:
case DMUL:
case IDIV:
case LDIV:
case FDIV:
case DDIV:
case IREM:
case LREM:
case FREM:
case DREM:
// The binary ops are all size 2.
return DexBase2Format.SIZE;
case INEG:
case LNEG:
case FNEG:
case DNEG:
// The negs are all size 1.
return DexBase1Format.SIZE;
case ISHL:
case LSHL:
case ISHR:
case LSHR:
case IUSHR:
case LUSHR:
case IAND:
case LAND:
case IOR:
case LOR:
case IXOR:
case LXOR:
// The binary ops are all size 2.
return DexBase2Format.SIZE;
case I2L:
case I2F:
case I2D:
case L2I:
case L2F:
case L2D:
case F2I:
case F2L:
case F2D:
case D2I:
case D2L:
case D2F:
case I2B:
case I2C:
case I2S:
// Number conversions are all size 1.
return DexBase1Format.SIZE;
case LCMP:
case FCMPL:
case FCMPG:
case DCMPL:
case DCMPG:
return DexBase2Format.SIZE;
case IFEQ:
case IFNE:
case IFLT:
case IFGE:
case IFGT:
case IFLE:
case IF_ICMPEQ:
case IF_ICMPNE:
case IF_ICMPLT:
case IF_ICMPGE:
case IF_ICMPGT:
case IF_ICMPLE:
case IF_ACMPEQ:
case IF_ACMPNE:
return DexBase2Format.SIZE;
case GOTO:
return DexGoto.SIZE;
case ARETURN:
case RETURN:
return DexBase1Format.SIZE;
case GETSTATIC:
case PUTSTATIC:
case GETFIELD:
case PUTFIELD:
return DexBase2Format.SIZE;
case INVOKEVIRTUAL:
case INVOKESPECIAL:
case INVOKESTATIC:
case INVOKEINTERFACE:
return DexBase3Format.SIZE;
case INVOKEDYNAMIC:
return DexInvokeCustom.SIZE;
case NEW:
return DexNewInstance.SIZE;
case NEWARRAY:
return DexNewArray.SIZE;
case ARRAYLENGTH:
return DexArrayLength.SIZE;
case ATHROW:
return DexThrow.SIZE;
case CHECKCAST:
return DexCheckCast.SIZE;
case INSTANCEOF:
return DexInstanceOf.SIZE;
case MONITORENTER:
return DexMonitorEnter.SIZE;
case MONITOREXIT:
return DexMonitorExit.SIZE;
case MULTIANEWARRAY:
return DexFilledNewArray.SIZE;
case IFNULL:
case IFNONNULL:
return DexBase1Format.SIZE;
// Non-CF instructions.
case ICONST:
case LCONST:
case FCONST:
case DCONST:
return DexConst16.SIZE;
case INVOKESTATIC_ITF:
case INVOKEDIRECT:
case INVOKEDIRECT_ITF:
case INVOKESUPER:
case INVOKESUPER_ITF:
return DexBase3Format.SIZE;
case DEBUGPOS:
// Often debug positions will be associated with instructions so assume size 0.
return 0;
case PHI:
// Assume a move per phi.
return DexMove.SIZE;
case FALLTHROUGH:
// Hopefully fallthrough points will not materialize as instructions.
return 0;
case MOVEEXCEPTION:
return DexMoveException.SIZE;
case DEBUGLOCALWRITE:
return DexMove.SIZE;
case NEWARRAYFILLED:
return DexFilledNewArray.SIZE;
case ITEMBASEDCONSTSTRING:
return DexConstString.SIZE;
case NEWUNBOXEDENUMINSTANCE:
return DexConst16.SIZE;
case INOT:
return DexNotInt.SIZE;
case LNOT:
return DexNotLong.SIZE;
case DEBUGLOCALREAD:
// These reads do not materialize after register allocation.
return 0;
case INITCLASS:
return DexSget.SIZE;
case INVOKEPOLYMORPHIC:
return DexBase3Format.SIZE;
case RECORDFIELDVALUES:
// Rewritten to new-array in DEX.
return DexNewArray.SIZE;
case CHECKCAST_SAFE:
case CHECKCAST_IGNORE_COMPAT:
return DexCheckCast.SIZE;
case CONSTCLASS_IGNORE_COMPAT:
return DexConstClass.SIZE;
case RESOURCENUMBER:
return DexConst4.SIZE;
default:
throw new Unreachable("Unexpected LIR opcode: " + opcode);
}
}
}