package scala.tools.nsc
package backend.jvm
package analysis
import scala.annotation.switch
import scala.tools.asm.Opcodes._
import scala.tools.asm.Type
import scala.tools.asm.tree.{MultiANewArrayInsnNode, InvokeDynamicInsnNode, MethodInsnNode, AbstractInsnNode}
import scala.tools.asm.tree.analysis.{Frame, Value}
import opt.BytecodeUtils._
import collection.immutable
object InstructionStackEffect {
private var cache: immutable.IntMap[(Int, Int)] = immutable.IntMap.empty
private def t(x: Int, y: Int): (Int, Int) = {
// x can go up to 255 (number of parameters of a method, dimensions in multianewarray) we cache
// x up to 10, which covers most cases and limits the cache. y doesn't go above 6 (see cases).
if (x > 10 || y > 6) (x, y)
else {
val key = (x << 8) + y // this would work for any x < 256
if (cache contains key) {
cache(key)
} else {
val r = (x, y)
cache += key -> r
r
}
}
}
/**
* Returns a pair with the number of stack values consumed and produced by `insn`.
* This method requires the `frame` to be in the state **before** executing / interpreting
* the `insn`.
*/
def apply[V <: Value](insn: AbstractInsnNode, frame: Frame[V]): (Int, Int) = {
def peekStack(n: Int): V = frame.peekStack(n)
(insn.getOpcode: @switch) match {
// The order of opcodes is the same as in Frame.execute.
case NOP => t(0, 0)
case ACONST_NULL |
ICONST_M1 |
ICONST_0 |
ICONST_1 |
ICONST_2 |
ICONST_3 |
ICONST_4 |
ICONST_5 |
LCONST_0 |
LCONST_1 |
FCONST_0 |
FCONST_1 |
FCONST_2 |
DCONST_0 |
DCONST_1 |
BIPUSH |
SIPUSH |
LDC |
ILOAD |
LLOAD |
FLOAD |
DLOAD |
ALOAD => t(0, 1)
case IALOAD |
LALOAD |
FALOAD |
DALOAD |
AALOAD |
BALOAD |
CALOAD |
SALOAD => t(2, 1)
case ISTORE |
LSTORE |
FSTORE |
DSTORE |
ASTORE => t(1, 0)
case IASTORE |
LASTORE |
FASTORE |
DASTORE |
AASTORE |
BASTORE |
CASTORE |
SASTORE => t(3, 0)
case POP => t(1, 0)
case POP2 =>
val isSize2 = peekStack(0).getSize == 2
if (isSize2) t(1, 0) else t(2, 0)
case DUP => t(1, 2)
case DUP_X1 => t(2, 3)
case DUP_X2 =>
val isSize2 = peekStack(1).getSize == 2
if (isSize2) t(2, 3) else t(3, 4)
case DUP2 =>
val isSize2 = peekStack(0).getSize == 2
if (isSize2) t(1, 2) else t(2, 4)
case DUP2_X1 =>
val isSize2 = peekStack(0).getSize == 2
if (isSize2) t(2, 3) else t(3, 4)
case DUP2_X2 =>
val v1isSize2 = peekStack(0).getSize == 2
if (v1isSize2) {
val v2isSize2 = peekStack(1).getSize == 2
if (v2isSize2) t(2, 3) else t(3, 4)
} else {
val v3isSize2 = peekStack(2).getSize == 2
if (v3isSize2) t(3, 5) else t(4, 6)
}
case SWAP => t(2, 2)
case IADD |
LADD |
FADD |
DADD |
ISUB |
LSUB |
FSUB |
DSUB |
IMUL |
LMUL |
FMUL |
DMUL |
IDIV |
LDIV |
FDIV |
DDIV |
IREM |
LREM |
FREM |
DREM => t(2, 1)
case INEG |
LNEG |
FNEG |
DNEG => t(1, 1)
case ISHL |
LSHL |
ISHR |
LSHR |
IUSHR |
LUSHR |
IAND |
LAND |
IOR |
LOR |
IXOR |
LXOR => t(2, 1)
case IINC => t(0, 0)
case I2L |
I2F |
I2D |
L2I |
L2F |
L2D |
F2I |
F2L |
F2D |
D2I |
D2L |
D2F |
I2B |
I2C |
I2S => t(1, 1)
case LCMP |
FCMPL |
FCMPG |
DCMPL |
DCMPG => t(2, 1)
case IFEQ |
IFNE |
IFLT |
IFGE |
IFGT |
IFLE => t(1, 0)
case IF_ICMPEQ |
IF_ICMPNE |
IF_ICMPLT |
IF_ICMPGE |
IF_ICMPGT |
IF_ICMPLE |
IF_ACMPEQ |
IF_ACMPNE => t(2, 0)
case GOTO => t(0, 0)
case JSR => t(0, 1)
case RET => t(0, 0)
case TABLESWITCH |
LOOKUPSWITCH => t(1, 0)
case IRETURN |
LRETURN |
FRETURN |
DRETURN |
ARETURN => t(1, 0) // Frame.execute consumes one stack value
case RETURN => t(0, 0) // Frame.execute does not change the stack
case GETSTATIC => t(0, 1)
case PUTSTATIC => t(1, 0)
case GETFIELD => t(1, 1)
case PUTFIELD => t(2, 0)
case INVOKEVIRTUAL |
INVOKESPECIAL |
INVOKESTATIC |
INVOKEINTERFACE =>
val desc = insn.asInstanceOf[MethodInsnNode].desc
val cons = Type.getArgumentTypes(desc).length + (if (insn.getOpcode == INVOKESTATIC) 0 else 1)
val prod = if (Type.getReturnType(desc) == Type.VOID_TYPE) 0 else 1
t(cons, prod)
case INVOKEDYNAMIC =>
val desc = insn.asInstanceOf[InvokeDynamicInsnNode].desc
val cons = Type.getArgumentTypes(desc).length
val prod = if (Type.getReturnType(desc) == Type.VOID_TYPE) 0 else 1
t(cons, prod)
case NEW => t(0, 1)
case NEWARRAY |
ANEWARRAY |
ARRAYLENGTH => t(1, 1)
case ATHROW => t(1, 0) // Frame.execute consumes one stack value
case CHECKCAST |
INSTANCEOF => t(1, 1) // Frame.execute does push(pop()) for both of them
case MONITORENTER |
MONITOREXIT => t(1, 0)
case MULTIANEWARRAY => t(insn.asInstanceOf[MultiANewArrayInsnNode].dims, 1)
case IFNULL |
IFNONNULL => t(1, 0)
}
}
}