diff options
author | Miguel Garcia <miguelalfredo.garcia@epfl.ch> | 2012-07-01 14:14:05 +0200 |
---|---|---|
committer | Miguel Garcia <miguelalfredo.garcia@epfl.ch> | 2012-07-01 18:09:03 +0200 |
commit | abbabb787ee9488ffa873a40f5fb28580cb05742 (patch) | |
tree | 432acd73b64df99eaa7dc35be6d65a8324956026 /src | |
parent | 5d12fa4b791e73d5c99a0e145d28cbaba12823d2 (diff) | |
download | scala-abbabb787ee9488ffa873a40f5fb28580cb05742.tar.gz scala-abbabb787ee9488ffa873a40f5fb28580cb05742.tar.bz2 scala-abbabb787ee9488ffa873a40f5fb28580cb05742.zip |
GenASM gets a 10% performance boost via switch
Diffstat (limited to 'src')
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala | 152 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala | 446 |
2 files changed, 353 insertions, 245 deletions
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(_) =>
- ()
}
}
|