From abbabb787ee9488ffa873a40f5fb28580cb05742 Mon Sep 17 00:00:00 2001 From: Miguel Garcia Date: Sun, 1 Jul 2012 14:14:05 +0200 Subject: GenASM gets a 10% performance boost via switch --- .../scala/tools/nsc/backend/icode/Opcodes.scala | 152 +++++-- .../scala/tools/nsc/backend/jvm/GenASM.scala | 446 +++++++++++---------- 2 files changed, 353 insertions(+), 245 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala index 5f495c8456..13457bfe58 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala @@ -15,37 +15,48 @@ import scala.reflect.internal.util.{Position,NoPosition} /* A pattern match - case THIS(clasz) => - case STORE_THIS(kind) => - case CONSTANT(const) => - case LOAD_ARRAY_ITEM(kind) => - case LOAD_LOCAL(local) => - case LOAD_FIELD(field, isStatic) => - case LOAD_MODULE(module) => - case STORE_ARRAY_ITEM(kind) => - case STORE_LOCAL(local) => - case STORE_FIELD(field, isStatic) => - case CALL_PRIMITIVE(primitive) => - case CALL_METHOD(method, style) => - case NEW(kind) => - case CREATE_ARRAY(elem, dims) => - case IS_INSTANCE(tpe) => - case CHECK_CAST(tpe) => - case SWITCH(tags, labels) => - case JUMP(whereto) => - case CJUMP(success, failure, cond, kind) => - case CZJUMP(success, failure, cond, kind) => - case RETURN(kind) => - case THROW(clasz) => - case DROP(kind) => - case DUP(kind) => - case MONITOR_ENTER() => - case MONITOR_EXIT() => - case BOX(boxType) => - case UNBOX(tpe) => - case SCOPE_ENTER(lv) => - case SCOPE_EXIT(lv) => - case LOAD_EXCEPTION(clasz) => + // locals + case THIS(clasz) => + case STORE_THIS(kind) => + case LOAD_LOCAL(local) => + case STORE_LOCAL(local) => + case SCOPE_ENTER(lv) => + case SCOPE_EXIT(lv) => + // stack + case LOAD_MODULE(module) => + case LOAD_EXCEPTION(clasz) => + case DROP(kind) => + case DUP(kind) => + // constants + case CONSTANT(const) => + // arithlogic + case CALL_PRIMITIVE(primitive) => + // casts + case IS_INSTANCE(tpe) => + case CHECK_CAST(tpe) => + // objs + case NEW(kind) => + case MONITOR_ENTER() => + case MONITOR_EXIT() => + case BOX(boxType) => + case UNBOX(tpe) => + // flds + case LOAD_FIELD(field, isStatic) => + case STORE_FIELD(field, isStatic) => + // mthds + case CALL_METHOD(method, style) => + // arrays + case LOAD_ARRAY_ITEM(kind) => + case STORE_ARRAY_ITEM(kind) => + case CREATE_ARRAY(elem, dims) => + // jumps + case SWITCH(tags, labels) => + case JUMP(whereto) => + case CJUMP(success, failure, cond, kind) => + case CZJUMP(success, failure, cond, kind) => + // ret + case RETURN(kind) => + case THROW(clasz) => */ @@ -58,11 +69,26 @@ import scala.reflect.internal.util.{Position,NoPosition} trait Opcodes { self: ICodes => import global.{Symbol, NoSymbol, Type, Name, Constant}; + // categories of ICode instructions + final val localsCat = 1 + final val stackCat = 2 + final val constCat = 3 + final val arilogCat = 4 + final val castsCat = 5 + final val objsCat = 6 + final val fldsCat = 7 + final val mthdsCat = 8 + final val arraysCat = 9 + final val jumpsCat = 10 + final val retCat = 11 + /** This class represents an instruction of the intermediate code. * Each case subclass will represent a specific operation. */ abstract class Instruction extends Cloneable { + def category: Int = 0 // undefined + /** This abstract method returns the number of used elements on the stack */ def consumed : Int = 0 @@ -118,6 +144,8 @@ trait Opcodes { self: ICodes => override def produced = 1 override def producedTypes = List(REFERENCE(clasz)) + + override def category = localsCat } /** Loads a constant on the stack. @@ -130,6 +158,8 @@ trait Opcodes { self: ICodes => override def produced = 1 override def producedTypes = List(toTypeKind(constant.tpe)) + + override def category = constCat } /** Loads an element of an array. The array and the index should @@ -143,6 +173,8 @@ trait Opcodes { self: ICodes => override def consumedTypes = List(ARRAY(kind), INT) override def producedTypes = List(kind) + + override def category = arraysCat } /** Load a local variable on the stack. It can be a method argument. @@ -154,6 +186,8 @@ trait Opcodes { self: ICodes => override def produced = 1 override def producedTypes = List(local.kind) + + override def category = localsCat } /** Load a field on the stack. The object to which it refers should be @@ -176,6 +210,8 @@ trait Opcodes { self: ICodes => // see #4283 var hostClass: Symbol = field.owner def setHostClass(cls: Symbol): this.type = { hostClass = cls; this } + + override def category = fldsCat } case class LOAD_MODULE(module: Symbol) extends Instruction { @@ -187,6 +223,8 @@ trait Opcodes { self: ICodes => override def produced = 1 override def producedTypes = List(REFERENCE(module)) + + override def category = stackCat } /** Store a value into an array at a specified index. @@ -198,6 +236,8 @@ trait Opcodes { self: ICodes => override def produced = 0 override def consumedTypes = List(ARRAY(kind), INT, kind) + + override def category = arraysCat } /** Store a value into a local variable. It can be an argument. @@ -209,6 +249,8 @@ trait Opcodes { self: ICodes => override def produced = 0 override def consumedTypes = List(local.kind) + + override def category = localsCat } /** Store a value into a field. @@ -228,6 +270,8 @@ trait Opcodes { self: ICodes => List(toTypeKind(field.tpe)) else List(REFERENCE(field.owner), toTypeKind(field.tpe)); + + override def category = fldsCat } /** Store a value into the 'this' pointer. @@ -238,6 +282,7 @@ trait Opcodes { self: ICodes => override def consumed = 1 override def produced = 0 override def consumedTypes = List(kind) + override def category = localsCat } /** Call a primitive function. @@ -292,6 +337,8 @@ trait Opcodes { self: ICodes => case StartConcat => List(ConcatClass) case EndConcat => List(REFERENCE(global.definitions.StringClass)) } + + override def category = arilogCat } /** This class represents a CALL_METHOD instruction @@ -347,6 +394,8 @@ trait Opcodes { self: ICodes => * being able to store such instructions into maps, when more * than one CALL_METHOD to the same method might exist. */ + + override def category = mthdsCat } case class BOX(boxType: TypeKind) extends Instruction { @@ -355,6 +404,7 @@ trait Opcodes { self: ICodes => override def consumed = 1 override def consumedTypes = boxType :: Nil override def produced = 1 + override def category = objsCat } case class UNBOX(boxType: TypeKind) extends Instruction { @@ -363,6 +413,7 @@ trait Opcodes { self: ICodes => override def consumed = 1 override def consumedTypes = ObjectReference :: Nil override def produced = 1 + override def category = objsCat } /** Create a new instance of a class through the specified constructor @@ -378,6 +429,8 @@ trait Opcodes { self: ICodes => /** The corresponding constructor call. */ var init: CALL_METHOD = _ + + override def category = objsCat } @@ -392,6 +445,8 @@ trait Opcodes { self: ICodes => override def consumed = dims; override def consumedTypes = List.fill(dims)(INT) override def produced = 1; + + override def category = arraysCat } /** This class represents a IS_INSTANCE instruction @@ -405,6 +460,8 @@ trait Opcodes { self: ICodes => override def consumed = 1 override def consumedTypes = ObjectReference :: Nil override def produced = 1 + + override def category = castsCat } /** This class represents a CHECK_CAST instruction @@ -419,6 +476,8 @@ trait Opcodes { self: ICodes => override def produced = 1 override val consumedTypes = List(ObjectReference) override def producedTypes = List(typ) + + override def category = castsCat } /** This class represents a SWITCH instruction @@ -439,6 +498,8 @@ trait Opcodes { self: ICodes => override val consumedTypes = List(INT) def flatTagsCount: Int = { var acc = 0; var rest = tags; while(rest.nonEmpty) { acc += rest.head.length; rest = rest.tail }; acc } // a one-liner + + override def category = jumpsCat } /** This class represents a JUMP instruction @@ -451,6 +512,8 @@ trait Opcodes { self: ICodes => override def consumed = 0 override def produced = 0 + + override def category = jumpsCat } /** This class represents a CJUMP instruction @@ -474,6 +537,8 @@ trait Opcodes { self: ICodes => override def produced = 0 override val consumedTypes = List(kind, kind) + + override def category = jumpsCat } /** This class represents a CZJUMP instruction @@ -495,6 +560,8 @@ trait Opcodes { self: ICodes => override def produced = 0 override val consumedTypes = List(kind) + + override def category = jumpsCat } @@ -507,6 +574,8 @@ trait Opcodes { self: ICodes => override def produced = 0 // TODO override val consumedTypes = List(kind) + + override def category = retCat } /** This class represents a THROW instruction @@ -522,6 +591,8 @@ trait Opcodes { self: ICodes => override def consumed = 1 override def produced = 0 + + override def category = retCat } /** This class represents a DROP instruction @@ -534,6 +605,8 @@ trait Opcodes { self: ICodes => override def consumed = 1 override def produced = 0 + + override def category = stackCat } /** This class represents a DUP instruction @@ -543,6 +616,7 @@ trait Opcodes { self: ICodes => case class DUP (typ: TypeKind) extends Instruction { override def consumed = 1 override def produced = 2 + override def category = stackCat } /** This class represents a MONITOR_ENTER instruction @@ -555,6 +629,8 @@ trait Opcodes { self: ICodes => override def consumed = 1 override def produced = 0 + + override def category = objsCat } /** This class represents a MONITOR_EXIT instruction @@ -567,6 +643,8 @@ trait Opcodes { self: ICodes => override def consumed = 1; override def produced = 0; + + override def category = objsCat } /** A local variable becomes visible at this point in code. @@ -577,6 +655,7 @@ trait Opcodes { self: ICodes => override def toString(): String = "SCOPE_ENTER " + lv override def consumed = 0 override def produced = 0 + override def category = localsCat } /** A local variable leaves its scope at this point in code. @@ -587,6 +666,7 @@ trait Opcodes { self: ICodes => override def toString(): String = "SCOPE_EXIT " + lv override def consumed = 0 override def produced = 0 + override def category = localsCat } /** Fake instruction. It designates the VM who pushes an exception @@ -598,6 +678,7 @@ trait Opcodes { self: ICodes => override def consumed = sys.error("LOAD_EXCEPTION does clean the whole stack, no idea how many things it consumes!") override def produced = 1 override def producedTypes = REFERENCE(clasz) :: Nil + override def category = stackCat } /** This class represents a method invocation style. */ @@ -658,6 +739,8 @@ trait Opcodes { self: ICodes => override def produced = 1 override def producedTypes = List(msil_mgdptr(local.kind)) + + override def category = localsCat } case class CIL_LOAD_FIELD_ADDRESS(field: Symbol, isStatic: Boolean) extends Instruction { @@ -670,6 +753,8 @@ trait Opcodes { self: ICodes => override def consumedTypes = if (isStatic) Nil else List(REFERENCE(field.owner)); override def producedTypes = List(msil_mgdptr(REFERENCE(field.owner))); + + override def category = fldsCat } case class CIL_LOAD_ARRAY_ITEM_ADDRESS(kind: TypeKind) extends Instruction { @@ -681,6 +766,8 @@ trait Opcodes { self: ICodes => override def consumedTypes = List(ARRAY(kind), INT) override def producedTypes = List(msil_mgdptr(kind)) + + override def category = arraysCat } case class CIL_UNBOX(valueType: TypeKind) extends Instruction { @@ -689,6 +776,7 @@ trait Opcodes { self: ICodes => override def consumedTypes = ObjectReference :: Nil // actually consumes a 'boxed valueType' override def produced = 1 override def producedTypes = List(msil_mgdptr(valueType)) + override def category = objsCat } case class CIL_INITOBJ(valueType: TypeKind) extends Instruction { @@ -696,6 +784,7 @@ trait Opcodes { self: ICodes => override def consumed = 1 override def consumedTypes = ObjectReference :: Nil // actually consumes a managed pointer override def produced = 0 + override def category = objsCat } case class CIL_NEWOBJ(method: Symbol) extends Instruction { @@ -705,6 +794,7 @@ trait Opcodes { self: ICodes => override def consumedTypes = method.tpe.paramTypes map toTypeKind override def produced = 1 override def producedTypes = List(toTypeKind(method.tpe.resultType)) + override def category = objsCat } } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 7ca49bd3f4..5ab8a3d751 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -2412,251 +2412,269 @@ abstract class GenASM extends SubComponent with BytecodeWriters { } } - instr match { - case THIS(_) => jmethod.visitVarInsn(Opcodes.ALOAD, 0) - - case CONSTANT(const) => genConstant(jmethod, const) - - case LOAD_ARRAY_ITEM(kind) => jcode.aload(kind) - - case LOAD_LOCAL(local) => jcode.load(indexOf(local), local.kind) - - case lf @ LOAD_FIELD(field, isStatic) => - var owner = javaName(lf.hostClass) - debuglog("LOAD_FIELD with owner: " + owner + " flags: " + Flags.flagsToString(field.owner.flags)) - val fieldJName = javaName(field) - val fieldDescr = descriptor(field) - val opc = if (isStatic) Opcodes.GETSTATIC else Opcodes.GETFIELD - jmethod.visitFieldInsn(opc, owner, fieldJName, fieldDescr) - - case LOAD_MODULE(module) => - // assert(module.isModule, "Expected module: " + module) - debuglog("generating LOAD_MODULE for: " + module + " flags: " + Flags.flagsToString(module.flags)); - if (clasz.symbol == module.moduleClass && jMethodName != nme.readResolve.toString) { - jmethod.visitVarInsn(Opcodes.ALOAD, 0) - } else { - jmethod.visitFieldInsn( - Opcodes.GETSTATIC, - javaName(module) /* + "$" */ , - strMODULE_INSTANCE_FIELD, - descriptor(module) - ) - } + (instr.category: @scala.annotation.switch) match { + + case icodes.localsCat => (instr: @unchecked) match { + case THIS(_) => jmethod.visitVarInsn(Opcodes.ALOAD, 0) + case LOAD_LOCAL(local) => jcode.load(indexOf(local), local.kind) + case STORE_LOCAL(local) => jcode.store(indexOf(local), local.kind) + case STORE_THIS(_) => + // this only works for impl classes because the self parameter comes first + // in the method signature. If that changes, this code has to be revisited. + jmethod.visitVarInsn(Opcodes.ASTORE, 0) + + case SCOPE_ENTER(lv) => + // locals removed by closelim (via CopyPropagation) may have left behind SCOPE_ENTER, SCOPE_EXIT that are to be ignored + val relevant = (!lv.sym.isSynthetic && m.locals.contains(lv)) + if(relevant) { // TODO check: does GenICode emit SCOPE_ENTER, SCOPE_EXIT for synthetic vars? + // this label will have DEBUG bit set in its flags (ie ASM ignores it for dataflow purposes) + // similarly, these labels aren't tracked in the `labels` map. + val start = new asm.Label + jmethod.visitLabel(start) + scoping.pushScope(lv, start) + } + + case SCOPE_EXIT(lv) => + val relevant = (!lv.sym.isSynthetic && m.locals.contains(lv)) + if(relevant) { + // this label will have DEBUG bit set in its flags (ie ASM ignores it for dataflow purposes) + // similarly, these labels aren't tracked in the `labels` map. + val end = new asm.Label + jmethod.visitLabel(end) + scoping.popScope(lv, end) + } + } - case STORE_ARRAY_ITEM(kind) => jcode.astore(kind) + case icodes.stackCat => (instr: @unchecked) match { - case STORE_LOCAL(local) => jcode.store(indexOf(local), local.kind) + case LOAD_MODULE(module) => + // assert(module.isModule, "Expected module: " + module) + debuglog("generating LOAD_MODULE for: " + module + " flags: " + Flags.flagsToString(module.flags)); + if (clasz.symbol == module.moduleClass && jMethodName != nme.readResolve.toString) { + jmethod.visitVarInsn(Opcodes.ALOAD, 0) + } else { + jmethod.visitFieldInsn( + Opcodes.GETSTATIC, + javaName(module) /* + "$" */ , + strMODULE_INSTANCE_FIELD, + descriptor(module) + ) + } - case STORE_THIS(_) => - // this only works for impl classes because the self parameter comes first - // in the method signature. If that changes, this code has to be revisited. - jmethod.visitVarInsn(Opcodes.ASTORE, 0) + case DROP(kind) => emit(if(kind.isWideType) Opcodes.POP2 else Opcodes.POP) - case STORE_FIELD(field, isStatic) => - val owner = javaName(field.owner) - val fieldJName = javaName(field) - val fieldDescr = descriptor(field) - val opc = if (isStatic) Opcodes.PUTSTATIC else Opcodes.PUTFIELD - jmethod.visitFieldInsn(opc, owner, fieldJName, fieldDescr) + case DUP(kind) => emit(if(kind.isWideType) Opcodes.DUP2 else Opcodes.DUP) - case CALL_PRIMITIVE(primitive) => genPrimitive(primitive, instr.pos) + case LOAD_EXCEPTION(_) => () + } - /** Special handling to access native Array.clone() */ - case call @ CALL_METHOD(definitions.Array_clone, Dynamic) => - val target: String = javaType(call.targetTypeKind).getInternalName - jcode.invokevirtual(target, "clone", mdesc_arrayClone) + case icodes.constCat => genConstant(jmethod, instr.asInstanceOf[CONSTANT].constant) - case call @ CALL_METHOD(method, style) => genCallMethod(call) + case icodes.arilogCat => genPrimitive(instr.asInstanceOf[CALL_PRIMITIVE].primitive, instr.pos) - case BOX(kind) => - val MethodNameAndType(mname, mdesc) = jBoxTo(kind) - jcode.invokestatic(BoxesRunTime, mname, mdesc) + case icodes.castsCat => (instr: @unchecked) match { - case UNBOX(kind) => - val MethodNameAndType(mname, mdesc) = jUnboxTo(kind) - jcode.invokestatic(BoxesRunTime, mname, mdesc) + case IS_INSTANCE(tpe) => + val jtyp: asm.Type = + tpe match { + case REFERENCE(cls) => asm.Type.getObjectType(javaName(cls)) + case ARRAY(elem) => javaArrayType(javaType(elem)) + case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe) + } + jmethod.visitTypeInsn(Opcodes.INSTANCEOF, jtyp.getInternalName) - case NEW(REFERENCE(cls)) => - val className = javaName(cls) - jmethod.visitTypeInsn(Opcodes.NEW, className) + case CHECK_CAST(tpe) => + tpe match { - case CREATE_ARRAY(elem, 1) => jcode newarray elem + case REFERENCE(cls) => + if (cls != ObjectClass) { // No need to checkcast for Objects + jmethod.visitTypeInsn(Opcodes.CHECKCAST, javaName(cls)) + } - case CREATE_ARRAY(elem, dims) => - jmethod.visitMultiANewArrayInsn(descriptor(ArrayN(elem, dims)), dims) + case ARRAY(elem) => + val iname = javaArrayType(javaType(elem)).getInternalName + jmethod.visitTypeInsn(Opcodes.CHECKCAST, iname) - case IS_INSTANCE(tpe) => - val jtyp: asm.Type = - tpe match { - case REFERENCE(cls) => asm.Type.getObjectType(javaName(cls)) - case ARRAY(elem) => javaArrayType(javaType(elem)) case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe) } - jmethod.visitTypeInsn(Opcodes.INSTANCEOF, jtyp.getInternalName) - case CHECK_CAST(tpe) => - tpe match { + } - case REFERENCE(cls) => - if (cls != ObjectClass) { // No need to checkcast for Objects - jmethod.visitTypeInsn(Opcodes.CHECKCAST, javaName(cls)) - } + case icodes.objsCat => (instr: @unchecked) match { - case ARRAY(elem) => - val iname = javaArrayType(javaType(elem)).getInternalName - jmethod.visitTypeInsn(Opcodes.CHECKCAST, iname) + case BOX(kind) => + val MethodNameAndType(mname, mdesc) = jBoxTo(kind) + jcode.invokestatic(BoxesRunTime, mname, mdesc) - case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe) - } + case UNBOX(kind) => + val MethodNameAndType(mname, mdesc) = jUnboxTo(kind) + jcode.invokestatic(BoxesRunTime, mname, mdesc) - case sw @ SWITCH(tagss, branches) => - assert(branches.length == tagss.length + 1, sw) - val flatSize = sw.flatTagsCount - val flatKeys = new Array[Int](flatSize) - val flatBranches = new Array[asm.Label](flatSize) - - var restTagss = tagss - var restBranches = branches - var k = 0 // ranges over flatKeys and flatBranches - while(restTagss.nonEmpty) { - val currLabel = labels(restBranches.head) - for(cTag <- restTagss.head) { - flatKeys(k) = cTag; - flatBranches(k) = currLabel - k += 1 - } - restTagss = restTagss.tail - restBranches = restBranches.tail - } - val defaultLabel = labels(restBranches.head) - assert(restBranches.tail.isEmpty) - debuglog("Emitting SWITCH:\ntags: " + tagss + "\nbranches: " + branches) - jcode.emitSWITCH(flatKeys, flatBranches, defaultLabel, MIN_SWITCH_DENSITY) - - case JUMP(whereto) => - if (nextBlock != whereto) { - jcode goTo labels(whereto) - } + case NEW(REFERENCE(cls)) => + val className = javaName(cls) + jmethod.visitTypeInsn(Opcodes.NEW, className) - case CJUMP(success, failure, cond, kind) => - if(kind.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT - if (nextBlock == success) { - jcode.emitIF_ICMP(cond.negate, labels(failure)) - // .. and fall through to success label - } else { - jcode.emitIF_ICMP(cond, labels(success)) - if (nextBlock != failure) { jcode goTo labels(failure) } - } - } else if(kind.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_) - if (nextBlock == success) { - jcode.emitIF_ACMP(cond.negate, labels(failure)) - // .. and fall through to success label - } else { - jcode.emitIF_ACMP(cond, labels(success)) - if (nextBlock != failure) { jcode goTo labels(failure) } - } - } else { - (kind: @unchecked) match { - case LONG => emit(Opcodes.LCMP) - case FLOAT => - if (cond == LT || cond == LE) emit(Opcodes.FCMPG) - else emit(Opcodes.FCMPL) - case DOUBLE => - if (cond == LT || cond == LE) emit(Opcodes.DCMPG) - else emit(Opcodes.DCMPL) - } - if (nextBlock == success) { - jcode.emitIF(cond.negate, labels(failure)) - // .. and fall through to success label - } else { - jcode.emitIF(cond, labels(success)) - if (nextBlock != failure) { jcode goTo labels(failure) } - } - } + case MONITOR_ENTER() => emit(Opcodes.MONITORENTER) + case MONITOR_EXIT() => emit(Opcodes.MONITOREXIT) + } - case CZJUMP(success, failure, cond, kind) => - if(kind.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT - if (nextBlock == success) { - jcode.emitIF(cond.negate, labels(failure)) - } else { - jcode.emitIF(cond, labels(success)) - if (nextBlock != failure) { jcode goTo labels(failure) } - } - } else if(kind.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_) - val Success = success - val Failure = failure - // @unchecked because references aren't compared with GT, GE, LT, LE. - ((cond, nextBlock) : @unchecked) match { - case (EQ, Success) => jcode emitIFNONNULL labels(failure) - case (NE, Failure) => jcode emitIFNONNULL labels(success) - case (EQ, Failure) => jcode emitIFNULL labels(success) - case (NE, Success) => jcode emitIFNULL labels(failure) - case (EQ, _) => - jcode emitIFNULL labels(success) - jcode goTo labels(failure) - case (NE, _) => - jcode emitIFNONNULL labels(success) - jcode goTo labels(failure) - } - } else { - (kind: @unchecked) match { - case LONG => - emit(Opcodes.LCONST_0) - emit(Opcodes.LCMP) - case FLOAT => - emit(Opcodes.FCONST_0) - if (cond == LT || cond == LE) emit(Opcodes.FCMPG) - else emit(Opcodes.FCMPL) - case DOUBLE => - emit(Opcodes.DCONST_0) - if (cond == LT || cond == LE) emit(Opcodes.DCMPG) - else emit(Opcodes.DCMPL) - } - if (nextBlock == success) { - jcode.emitIF(cond.negate, labels(failure)) - } else { - jcode.emitIF(cond, labels(success)) - if (nextBlock != failure) { jcode goTo labels(failure) } - } - } + case icodes.fldsCat => (instr: @unchecked) match { - case RETURN(kind) => jcode emitRETURN kind + case lf @ LOAD_FIELD(field, isStatic) => + var owner = javaName(lf.hostClass) + debuglog("LOAD_FIELD with owner: " + owner + " flags: " + Flags.flagsToString(field.owner.flags)) + val fieldJName = javaName(field) + val fieldDescr = descriptor(field) + val opc = if (isStatic) Opcodes.GETSTATIC else Opcodes.GETFIELD + jmethod.visitFieldInsn(opc, owner, fieldJName, fieldDescr) - case THROW(_) => emit(Opcodes.ATHROW) + case STORE_FIELD(field, isStatic) => + val owner = javaName(field.owner) + val fieldJName = javaName(field) + val fieldDescr = descriptor(field) + val opc = if (isStatic) Opcodes.PUTSTATIC else Opcodes.PUTFIELD + jmethod.visitFieldInsn(opc, owner, fieldJName, fieldDescr) - case DROP(kind) => - emit(if(kind.isWideType) Opcodes.POP2 else Opcodes.POP) + } - case DUP(kind) => - emit(if(kind.isWideType) Opcodes.DUP2 else Opcodes.DUP) + case icodes.mthdsCat => (instr: @unchecked) match { - case MONITOR_ENTER() => emit(Opcodes.MONITORENTER) + /** Special handling to access native Array.clone() */ + case call @ CALL_METHOD(definitions.Array_clone, Dynamic) => + val target: String = javaType(call.targetTypeKind).getInternalName + jcode.invokevirtual(target, "clone", mdesc_arrayClone) - case MONITOR_EXIT() => emit(Opcodes.MONITOREXIT) + case call @ CALL_METHOD(method, style) => genCallMethod(call) - case SCOPE_ENTER(lv) => - // locals removed by closelim (via CopyPropagation) may have left behind SCOPE_ENTER, SCOPE_EXIT that are to be ignored - val relevant = (!lv.sym.isSynthetic && m.locals.contains(lv)) - if(relevant) { // TODO check: does GenICode emit SCOPE_ENTER, SCOPE_EXIT for synthetic vars? - // this label will have DEBUG bit set in its flags (ie ASM ignores it for dataflow purposes) - // similarly, these labels aren't tracked in the `labels` map. - val start = new asm.Label - jmethod.visitLabel(start) - scoping.pushScope(lv, start) - } + } - case SCOPE_EXIT(lv) => - val relevant = (!lv.sym.isSynthetic && m.locals.contains(lv)) - if(relevant) { - // this label will have DEBUG bit set in its flags (ie ASM ignores it for dataflow purposes) - // similarly, these labels aren't tracked in the `labels` map. - val end = new asm.Label - jmethod.visitLabel(end) - scoping.popScope(lv, end) - } + case icodes.arraysCat => (instr: @unchecked) match { + case LOAD_ARRAY_ITEM(kind) => jcode.aload(kind) + case STORE_ARRAY_ITEM(kind) => jcode.astore(kind) + case CREATE_ARRAY(elem, 1) => jcode newarray elem + case CREATE_ARRAY(elem, dims) => jmethod.visitMultiANewArrayInsn(descriptor(ArrayN(elem, dims)), dims) + } + + case icodes.jumpsCat => (instr: @unchecked) match { + + case sw @ SWITCH(tagss, branches) => + assert(branches.length == tagss.length + 1, sw) + val flatSize = sw.flatTagsCount + val flatKeys = new Array[Int](flatSize) + val flatBranches = new Array[asm.Label](flatSize) + + var restTagss = tagss + var restBranches = branches + var k = 0 // ranges over flatKeys and flatBranches + while(restTagss.nonEmpty) { + val currLabel = labels(restBranches.head) + for(cTag <- restTagss.head) { + flatKeys(k) = cTag; + flatBranches(k) = currLabel + k += 1 + } + restTagss = restTagss.tail + restBranches = restBranches.tail + } + val defaultLabel = labels(restBranches.head) + assert(restBranches.tail.isEmpty) + debuglog("Emitting SWITCH:\ntags: " + tagss + "\nbranches: " + branches) + jcode.emitSWITCH(flatKeys, flatBranches, defaultLabel, MIN_SWITCH_DENSITY) + + case JUMP(whereto) => + if (nextBlock != whereto) { + jcode goTo labels(whereto) + } + + case CJUMP(success, failure, cond, kind) => + if(kind.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT + if (nextBlock == success) { + jcode.emitIF_ICMP(cond.negate, labels(failure)) + // .. and fall through to success label + } else { + jcode.emitIF_ICMP(cond, labels(success)) + if (nextBlock != failure) { jcode goTo labels(failure) } + } + } else if(kind.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_) + if (nextBlock == success) { + jcode.emitIF_ACMP(cond.negate, labels(failure)) + // .. and fall through to success label + } else { + jcode.emitIF_ACMP(cond, labels(success)) + if (nextBlock != failure) { jcode goTo labels(failure) } + } + } else { + (kind: @unchecked) match { + case LONG => emit(Opcodes.LCMP) + case FLOAT => + if (cond == LT || cond == LE) emit(Opcodes.FCMPG) + else emit(Opcodes.FCMPL) + case DOUBLE => + if (cond == LT || cond == LE) emit(Opcodes.DCMPG) + else emit(Opcodes.DCMPL) + } + if (nextBlock == success) { + jcode.emitIF(cond.negate, labels(failure)) + // .. and fall through to success label + } else { + jcode.emitIF(cond, labels(success)) + if (nextBlock != failure) { jcode goTo labels(failure) } + } + } + + case CZJUMP(success, failure, cond, kind) => + if(kind.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT + if (nextBlock == success) { + jcode.emitIF(cond.negate, labels(failure)) + } else { + jcode.emitIF(cond, labels(success)) + if (nextBlock != failure) { jcode goTo labels(failure) } + } + } else if(kind.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_) + val Success = success + val Failure = failure + // @unchecked because references aren't compared with GT, GE, LT, LE. + ((cond, nextBlock) : @unchecked) match { + case (EQ, Success) => jcode emitIFNONNULL labels(failure) + case (NE, Failure) => jcode emitIFNONNULL labels(success) + case (EQ, Failure) => jcode emitIFNULL labels(success) + case (NE, Success) => jcode emitIFNULL labels(failure) + case (EQ, _) => + jcode emitIFNULL labels(success) + jcode goTo labels(failure) + case (NE, _) => + jcode emitIFNONNULL labels(success) + jcode goTo labels(failure) + } + } else { + (kind: @unchecked) match { + case LONG => + emit(Opcodes.LCONST_0) + emit(Opcodes.LCMP) + case FLOAT => + emit(Opcodes.FCONST_0) + if (cond == LT || cond == LE) emit(Opcodes.FCMPG) + else emit(Opcodes.FCMPL) + case DOUBLE => + emit(Opcodes.DCONST_0) + if (cond == LT || cond == LE) emit(Opcodes.DCMPG) + else emit(Opcodes.DCMPL) + } + if (nextBlock == success) { + jcode.emitIF(cond.negate, labels(failure)) + } else { + jcode.emitIF(cond, labels(success)) + if (nextBlock != failure) { jcode goTo labels(failure) } + } + } + + } + + case icodes.retCat => (instr: @unchecked) match { + case RETURN(kind) => jcode emitRETURN kind + case THROW(_) => emit(Opcodes.ATHROW) + } - case LOAD_EXCEPTION(_) => - () } } -- cgit v1.2.3