summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/backend/jvm
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools/nsc/backend/jvm')
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala75
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala74
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala24
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala17
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala24
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala85
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala24
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala24
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala33
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala35
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzer.scala16
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala67
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala9
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala38
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala8
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/CopyProp.scala14
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/InlineInfoAttribute.scala13
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala81
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala88
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala37
20 files changed, 438 insertions, 348 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
index 5d152ef0e8..55fe47bde6 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
@@ -11,10 +11,10 @@ package jvm
import scala.annotation.switch
import scala.reflect.internal.Flags
-
import scala.tools.asm
import GenBCode._
import BackendReporting._
+import scala.tools.asm.Opcodes
import scala.tools.asm.tree.MethodInsnNode
import scala.tools.nsc.backend.jvm.BCodeHelpers.{InvokeStyle, TestOp}
@@ -637,7 +637,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
val nativeKind = tpeTK(expr)
genLoad(expr, nativeKind)
val MethodNameAndType(mname, methodType) = srBoxesRuntimeBoxToMethods(nativeKind)
- bc.invokestatic(srBoxesRunTimeRef.internalName, mname, methodType.descriptor, app.pos)
+ bc.invokestatic(srBoxesRunTimeRef.internalName, mname, methodType.descriptor, itf = false, app.pos)
generatedType = boxResultType(fun.symbol)
case Apply(fun, List(expr)) if currentRun.runDefinitions.isUnbox(fun.symbol) =>
@@ -645,7 +645,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
val boxType = unboxResultType(fun.symbol)
generatedType = boxType
val MethodNameAndType(mname, methodType) = srBoxesRuntimeUnboxToMethods(boxType)
- bc.invokestatic(srBoxesRunTimeRef.internalName, mname, methodType.descriptor, app.pos)
+ bc.invokestatic(srBoxesRunTimeRef.internalName, mname, methodType.descriptor, itf = false, app.pos)
case app @ Apply(fun, args) =>
val sym = fun.symbol
@@ -1058,31 +1058,40 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
}
receiverClass.info // ensure types the type is up to date; erasure may add lateINTERFACE to traits
- val receiverName = internalName(receiverClass)
-
- // super calls are only allowed to direct parents
- if (style.isSuper && receiverClass.isTraitOrInterface && !cnode.interfaces.contains(receiverName)) {
- thisBType.info.get.inlineInfo.lateInterfaces += receiverName
- cnode.interfaces.add(receiverName)
- }
+ val receiverBType = classBTypeFromSymbol(receiverClass)
+ val receiverName = receiverBType.internalName
def needsInterfaceCall(sym: Symbol) = {
sym.isTraitOrInterface ||
sym.isJavaDefined && sym.isNonBottomSubClass(definitions.ClassfileAnnotationClass)
}
- val jname = method.javaSimpleName.toString
- val bmType = methodBTypeFromSymbol(method)
- val mdescr = bmType.descriptor
+ val jname = method.javaSimpleName.toString
+ val bmType = methodBTypeFromSymbol(method)
+ val mdescr = bmType.descriptor
+ val isInterface = receiverBType.isInterface.get
import InvokeStyle._
- style match {
- case Static => bc.invokestatic (receiverName, jname, mdescr, pos)
- case Special => bc.invokespecial (receiverName, jname, mdescr, pos)
- case Virtual =>
- if (needsInterfaceCall(receiverClass)) bc.invokeinterface(receiverName, jname, mdescr, pos)
- else bc.invokevirtual (receiverName, jname, mdescr, pos)
- case Super => bc.invokespecial (receiverName, jname, mdescr, pos)
+ if (style == Super) {
+ assert(receiverClass == methodOwner, s"for super call, expecting $receiverClass == $methodOwner")
+ if (receiverClass.isTrait && !receiverClass.isJavaDefined) {
+ val staticDesc = MethodBType(typeToBType(method.owner.info) :: bmType.argumentTypes, bmType.returnType).descriptor
+ val staticName = traitImplMethodName(method).toString
+ bc.invokestatic(receiverName, staticName, staticDesc, isInterface, pos)
+ } else {
+ if (receiverClass.isTraitOrInterface) {
+ // An earlier check in Mixin reports an error in this case, so it doesn't reach the backend
+ assert(cnode.interfaces.contains(receiverName), s"cannot invokespecial $receiverName.$jname, the interface is not a direct parent.")
+ }
+ bc.invokespecial(receiverName, jname, mdescr, isInterface, pos)
+ }
+ } else {
+ val opc = style match {
+ case Static => Opcodes.INVOKESTATIC
+ case Special => Opcodes.INVOKESPECIAL
+ case Virtual => if (isInterface) Opcodes.INVOKEINTERFACE else Opcodes.INVOKEVIRTUAL
+ }
+ bc.emitInvoke(opc, receiverName, jname, mdescr, isInterface, pos)
}
bmType.returnType
@@ -1110,22 +1119,19 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
}
/* Emit code to compare the two top-most stack values using the 'op' operator. */
- private def genCJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType, targetIfNoJump: asm.Label) {
- if (targetIfNoJump == success) genCJUMP(failure, success, op.negate, tk, targetIfNoJump)
+ private def genCJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType, targetIfNoJump: asm.Label, negated: Boolean = false) {
+ if (targetIfNoJump == success) genCJUMP(failure, success, op.negate, tk, targetIfNoJump, negated = !negated)
else {
if (tk.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
bc.emitIF_ICMP(op, success)
} else if (tk.isRef) { // REFERENCE(_) | ARRAY(_)
bc.emitIF_ACMP(op, success)
} else {
+ def useCmpG = if (negated) op == TestOp.GT || op == TestOp.GE else op == TestOp.LT || op == TestOp.LE
(tk: @unchecked) match {
case LONG => emit(asm.Opcodes.LCMP)
- case FLOAT =>
- if (op == TestOp.LT || op == TestOp.LE) emit(asm.Opcodes.FCMPG)
- else emit(asm.Opcodes.FCMPL)
- case DOUBLE =>
- if (op == TestOp.LT || op == TestOp.LE) emit(asm.Opcodes.DCMPG)
- else emit(asm.Opcodes.DCMPL)
+ case FLOAT => emit(if (useCmpG) asm.Opcodes.FCMPG else asm.Opcodes.FCMPL)
+ case DOUBLE => emit(if (useCmpG) asm.Opcodes.DCMPG else asm.Opcodes.DCMPL)
}
bc.emitIF(op, success)
}
@@ -1134,8 +1140,8 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
}
/* Emits code to compare (and consume) stack-top and zero using the 'op' operator */
- private def genCZJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType, targetIfNoJump: asm.Label) {
- if (targetIfNoJump == success) genCZJUMP(failure, success, op.negate, tk, targetIfNoJump)
+ private def genCZJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType, targetIfNoJump: asm.Label, negated: Boolean = false) {
+ if (targetIfNoJump == success) genCZJUMP(failure, success, op.negate, tk, targetIfNoJump, negated = !negated)
else {
if (tk.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
bc.emitIF(op, success)
@@ -1145,18 +1151,17 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
case TestOp.NE => bc emitIFNONNULL success
}
} else {
+ def useCmpG = if (negated) op == TestOp.GT || op == TestOp.GE else op == TestOp.LT || op == TestOp.LE
(tk: @unchecked) match {
case LONG =>
emit(asm.Opcodes.LCONST_0)
emit(asm.Opcodes.LCMP)
case FLOAT =>
emit(asm.Opcodes.FCONST_0)
- if (op == TestOp.LT || op == TestOp.LE) emit(asm.Opcodes.FCMPG)
- else emit(asm.Opcodes.FCMPL)
+ emit(if (useCmpG) asm.Opcodes.FCMPG else asm.Opcodes.FCMPL)
case DOUBLE =>
emit(asm.Opcodes.DCONST_0)
- if (op == TestOp.LT || op == TestOp.LE) emit(asm.Opcodes.DCMPG)
- else emit(asm.Opcodes.DCMPL)
+ emit(if (useCmpG) asm.Opcodes.DCMPG else asm.Opcodes.DCMPL)
}
bc.emitIF(op, success)
}
@@ -1171,8 +1176,8 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
case scalaPrimitives.NE => TestOp.NE
case scalaPrimitives.LT => TestOp.LT
case scalaPrimitives.LE => TestOp.LE
- case scalaPrimitives.GE => TestOp.GE
case scalaPrimitives.GT => TestOp.GT
+ case scalaPrimitives.GE => TestOp.GE
}
/** Some useful equality helpers. */
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
index a32c21795d..df3c2cb3d5 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
@@ -11,6 +11,7 @@ import scala.tools.asm
import scala.tools.nsc.io.AbstractFile
import GenBCode._
import BackendReporting._
+import scala.reflect.internal.Flags
/*
* Traits encapsulating functionality to convert Scala AST Trees into ASM ClassNodes.
@@ -49,6 +50,14 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
}
}
+ def needsStaticImplMethod(sym: Symbol) = sym.hasAttachment[global.mixer.NeedStaticImpl.type]
+
+ final def traitImplMethodName(sym: Symbol): Name = {
+ val name = sym.javaSimpleName
+ if (sym.isMixinConstructor) name
+ else name.append(nme.NAME_JOIN_STRING)
+ }
+
/**
* True if `classSym` is an anonymous class or a local class. I.e., false if `classSym` is a
* member class. This method is used to decide if we should emit an EnclosingMethod attribute.
@@ -76,7 +85,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
val origOwner = sym.originalOwner
// phase travel necessary: after flatten, the name includes the name of outer classes.
// if some outer name contains $anon, a non-anon class is considered anon.
- if (delambdafyInline() && sym.rawowner.isAnonymousFunction) {
+ if (delambdafyInline() && exitingPickler(sym.rawowner.isAnonymousFunction)) {
// SI-9105: special handling for anonymous functions under delambdafy:inline.
//
// class C { def t = () => { def f { class Z } } }
@@ -230,58 +239,6 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
sym.isErroneous
}
- /**
- * Build the [[InlineInfo]] for a class symbol.
- */
- def buildInlineInfoFromClassSymbol(classSym: Symbol, classSymToInternalName: Symbol => InternalName, methodSymToDescriptor: Symbol => String): InlineInfo = {
- val isEffectivelyFinal = classSym.isEffectivelyFinal
-
- val sam = {
- if (classSym.isEffectivelyFinal) None
- else {
- // Phase travel necessary. For example, nullary methods (getter of an abstract val) get an
- // empty parameter list in later phases and would therefore be picked as SAM.
- val samSym = exitingPickler(definitions.samOf(classSym.tpe))
- if (samSym == NoSymbol) None
- else Some(samSym.javaSimpleName.toString + methodSymToDescriptor(samSym))
- }
- }
-
- var warning = Option.empty[ClassSymbolInfoFailureSI9111]
-
- // Primitive methods cannot be inlined, so there's no point in building a MethodInlineInfo. Also, some
- // primitive methods (e.g., `isInstanceOf`) have non-erased types, which confuses [[typeToBType]].
- val methodInlineInfos = classSym.info.decls.iterator.filter(m => m.isMethod && !scalaPrimitives.isPrimitive(m)).flatMap({
- case methodSym =>
- if (completeSilentlyAndCheckErroneous(methodSym)) {
- // Happens due to SI-9111. Just don't provide any MethodInlineInfo for that method, we don't need fail the compiler.
- if (!classSym.isJavaDefined) devWarning("SI-9111 should only be possible for Java classes")
- warning = Some(ClassSymbolInfoFailureSI9111(classSym.fullName))
- None
- } else {
- val name = methodSym.javaSimpleName.toString // same as in genDefDef
- val signature = name + methodSymToDescriptor(methodSym)
-
- // In `trait T { object O }`, `oSym.isEffectivelyFinalOrNotOverridden` is true, but the
- // method is abstract in bytecode, `defDef.rhs.isEmpty`. Abstract methods are excluded
- // so they are not marked final in the InlineInfo attribute.
- //
- // However, due to https://github.com/scala/scala-dev/issues/126, this currently does not
- // work, the abstract accessor for O will be marked effectivelyFinal.
- val effectivelyFinal = methodSym.isEffectivelyFinalOrNotOverridden && !methodSym.isDeferred
-
- val info = MethodInlineInfo(
- effectivelyFinal = effectivelyFinal,
- annotatedInline = methodSym.hasAnnotation(ScalaInlineClass),
- annotatedNoInline = methodSym.hasAnnotation(ScalaNoInlineClass)
- )
- Some((signature, info))
- }
- }).toMap
-
- InlineInfo(isEffectivelyFinal, sam, methodInlineInfos, warning)
- }
-
/*
* must-single-thread
*/
@@ -568,15 +525,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
/**
* The class internal name for a given class symbol.
*/
- final def internalName(sym: Symbol): String = {
- // For each java class, the scala compiler creates a class and a module (thus a module class).
- // If the `sym` is a java module class, we use the java class instead. This ensures that the
- // ClassBType is created from the main class (instead of the module class).
- // The two symbols have the same name, so the resulting internalName is the same.
- // Phase travel (exitingPickler) required for SI-6613 - linkedCoC is only reliable in early phases (nesting)
- val classSym = if (sym.isJavaDefined && sym.isModuleClass) exitingPickler(sym.linkedClassOfClass) else sym
- classBTypeFromSymbol(classSym).internalName
- }
+ final def internalName(sym: Symbol): String = classBTypeFromSymbol(sym).internalName
} // end of trait BCInnerClassGen
trait BCAnnotGen extends BCInnerClassGen {
@@ -1336,6 +1285,7 @@ object BCodeHelpers {
}
object TestOp {
+ // the order here / op numbers are important to get the correct result when calling opcodeIF
val EQ = new TestOp(0)
val NE = new TestOp(1)
val LT = new TestOp(2)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
index 0a95bc5e39..e3d45a9b3e 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
@@ -190,6 +190,7 @@ abstract class BCodeIdiomatic extends SubComponent {
JavaStringBuilderClassName,
INSTANCE_CONSTRUCTOR_NAME,
"()V",
+ itf = false,
pos
)
}
@@ -373,29 +374,26 @@ abstract class BCodeIdiomatic extends SubComponent {
final def rem(tk: BType) { emitPrimitive(JCodeMethodN.remOpcodes, tk) } // can-multi-thread
// can-multi-thread
- final def invokespecial(owner: String, name: String, desc: String, pos: Position) {
- addInvoke(Opcodes.INVOKESPECIAL, owner, name, desc, false, pos)
+ final def invokespecial(owner: String, name: String, desc: String, itf: Boolean, pos: Position): Unit = {
+ emitInvoke(Opcodes.INVOKESPECIAL, owner, name, desc, itf, pos)
}
// can-multi-thread
- final def invokestatic(owner: String, name: String, desc: String, pos: Position) {
- addInvoke(Opcodes.INVOKESTATIC, owner, name, desc, false, pos)
+ final def invokestatic(owner: String, name: String, desc: String, itf: Boolean, pos: Position): Unit = {
+ emitInvoke(Opcodes.INVOKESTATIC, owner, name, desc, itf, pos)
}
// can-multi-thread
- final def invokeinterface(owner: String, name: String, desc: String, pos: Position) {
- addInvoke(Opcodes.INVOKEINTERFACE, owner, name, desc, true, pos)
+ final def invokeinterface(owner: String, name: String, desc: String, pos: Position): Unit = {
+ emitInvoke(Opcodes.INVOKEINTERFACE, owner, name, desc, itf = true, pos)
}
// can-multi-thread
- final def invokevirtual(owner: String, name: String, desc: String, pos: Position) {
- addInvoke(Opcodes.INVOKEVIRTUAL, owner, name, desc, false, pos)
+ final def invokevirtual(owner: String, name: String, desc: String, pos: Position): Unit = {
+ emitInvoke(Opcodes.INVOKEVIRTUAL, owner, name, desc, itf = false, pos)
}
- private def addInvoke(opcode: Int, owner: String, name: String, desc: String, itf: Boolean, pos: Position) = {
+ def emitInvoke(opcode: Int, owner: String, name: String, desc: String, itf: Boolean, pos: Position): Unit = {
val node = new MethodInsnNode(opcode, owner, name, desc, itf)
jmethod.instructions.add(node)
- if (settings.YoptInlinerEnabled) callsitePositions(node) = pos
- }
- final def invokedynamic(owner: String, name: String, desc: String) {
- jmethod.visitMethodInsn(Opcodes.INVOKEDYNAMIC, owner, name, desc)
+ if (settings.optInlinerEnabled) callsitePositions(node) = pos
}
// can-multi-thread
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
index bddc41e5c6..1bff8519ec 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
@@ -488,7 +488,22 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
case ValDef(mods, name, tpt, rhs) => () // fields are added in `genPlainClass()`, via `addClassFields()`
- case dd : DefDef => genDefDef(dd)
+ case dd : DefDef =>
+ val sym = dd.symbol
+ if (needsStaticImplMethod(sym)) {
+ val staticDefDef = global.gen.mkStatic(dd, traitImplMethodName(sym), _.cloneSymbol)
+ val forwarderDefDef = {
+ val forwarderBody = Apply(global.gen.mkAttributedRef(staticDefDef.symbol), This(sym.owner).setType(sym.owner.typeConstructor) :: dd.vparamss.head.map(p => global.gen.mkAttributedIdent(p.symbol))).setType(sym.info.resultType)
+ // we don't want to the optimizer to inline the static method into the forwarder. Instead,
+ // the backend has a special case to transitively inline into a callsite of the forwarder
+ // when the forwarder itself is inlined.
+ forwarderBody.updateAttachment(NoInlineCallsiteAttachment)
+ deriveDefDef(dd)(_ => global.atPos(dd.pos)(forwarderBody))
+ }
+ genDefDef(staticDefDef)
+ if (!sym.isMixinConstructor)
+ genDefDef(forwarderDefDef)
+ } else genDefDef(dd)
case Template(_, _, body) => body foreach gen
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
index 2637d21050..7b2686e7a9 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
@@ -225,8 +225,7 @@ abstract class BTypes {
val inlineInfo = inlineInfoFromClassfile(classNode)
- val classfileInterfaces: List[ClassBType] = classNode.interfaces.asScala.map(classBTypeFromParsedClassfile)(collection.breakOut)
- val interfaces = classfileInterfaces.filterNot(i => inlineInfo.lateInterfaces.contains(i.internalName))
+ val interfaces: List[ClassBType] = classNode.interfaces.asScala.map(classBTypeFromParsedClassfile)(collection.breakOut)
classBType.info = Right(ClassInfo(superClass, interfaces, flags, nestedClasses, nestedInfo, inlineInfo))
classBType
@@ -271,7 +270,7 @@ abstract class BTypes {
// The InlineInfo is built from the classfile (not from the symbol) for all classes that are NOT
// being compiled. For those classes, the info is only needed if the inliner is enabled, othewise
// we can save the memory.
- if (!compilerSettings.YoptInlinerEnabled) BTypes.EmptyInlineInfo
+ if (!compilerSettings.optInlinerEnabled) BTypes.EmptyInlineInfo
else fromClassfileAttribute getOrElse fromClassfileWithoutAttribute
}
@@ -1147,25 +1146,6 @@ object BTypes {
sam: Option[String],
methodInfos: Map[String, MethodInlineInfo],
warning: Option[ClassInlineInfoWarning]) {
- /**
- * A super call (invokespecial) to a default method T.m is only allowed if the interface T is
- * a direct parent of the class. Super calls are introduced for example in Mixin when generating
- * forwarder methods:
- *
- * trait T { override def clone(): Object = "hi" }
- * trait U extends T
- * class C extends U
- *
- * The class C gets a forwarder that invokes T.clone(). During code generation the interface T
- * is added as direct parent to class C. Note that T is not a (direct) parent in the frontend
- * type of class C.
- *
- * All interfaces that are added to a class during code generation are added to this buffer and
- * stored in the InlineInfo classfile attribute. This ensures that the ClassBTypes for a
- * specific class is the same no matter if it's constructed from a Symbol or from a classfile.
- * This is tested in BTypesFromClassfileTest.
- */
- val lateInterfaces: ListBuffer[InternalName] = ListBuffer.empty
}
val EmptyInlineInfo = InlineInfo(false, None, Map.empty, None)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
index d10b6c8dba..1a4590e7d1 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
@@ -97,11 +97,19 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
* for the Nothing / Null. If used for example as a parameter type, we use the runtime classes
* in the classfile method signature.
*/
- final def classBTypeFromSymbol(classSym: Symbol): ClassBType = {
+ final def classBTypeFromSymbol(sym: Symbol): ClassBType = {
+ // For each java class, the scala compiler creates a class and a module (thus a module class).
+ // If the `sym` is a java module class, we use the java class instead. This ensures that the
+ // ClassBType is created from the main class (instead of the module class).
+ // The two symbols have the same name, so the resulting internalName is the same.
+ // Phase travel (exitingPickler) required for SI-6613 - linkedCoC is only reliable in early phases (nesting)
+ val classSym = if (sym.isJavaDefined && sym.isModuleClass) exitingPickler(sym.linkedClassOfClass) else sym
+
assert(classSym != NoSymbol, "Cannot create ClassBType from NoSymbol")
assert(classSym.isClass, s"Cannot create ClassBType from non-class symbol $classSym")
assertClassNotArrayNotPrimitive(classSym)
assert(!primitiveTypeToBType.contains(classSym) || isCompilingPrimitive, s"Cannot create ClassBType for primitive class symbol $classSym")
+
if (classSym == NothingClass) srNothingRef
else if (classSym == NullClass) srNullRef
else {
@@ -234,12 +242,13 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
val allParents = classParents ++ classSym.annotations.flatMap(newParentForAnnotation)
+ val minimizedParents = if (classSym.isJavaDefined) allParents else erasure.minimizeParents(allParents)
// We keep the superClass when computing minimizeParents to eliminate more interfaces.
// Example: T can be eliminated from D
// trait T
// class C extends T
// class D extends C with T
- val interfaces = erasure.minimizeParents(allParents) match {
+ val interfaces = minimizedParents match {
case superClass :: ifs if !isInterfaceOrTrait(superClass.typeSymbol) =>
ifs
case ifs =>
@@ -508,13 +517,13 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
* classfile attribute.
*/
private def buildInlineInfo(classSym: Symbol, internalName: InternalName): InlineInfo = {
- def buildFromSymbol = buildInlineInfoFromClassSymbol(classSym, classBTypeFromSymbol(_).internalName, methodBTypeFromSymbol(_).descriptor)
+ def buildFromSymbol = buildInlineInfoFromClassSymbol(classSym)
// phase travel required, see implementation of `compiles`. for nested classes, it checks if the
// enclosingTopLevelClass is being compiled. after flatten, all classes are considered top-level,
// so `compiles` would return `false`.
if (exitingPickler(currentRun.compiles(classSym))) buildFromSymbol // InlineInfo required for classes being compiled, we have to create the classfile attribute
- else if (!compilerSettings.YoptInlinerEnabled) BTypes.EmptyInlineInfo // For other classes, we need the InlineInfo only inf the inliner is enabled.
+ else if (!compilerSettings.optInlinerEnabled) BTypes.EmptyInlineInfo // For other classes, we need the InlineInfo only inf the inliner is enabled.
else {
// For classes not being compiled, the InlineInfo is read from the classfile attribute. This
// fixes an issue with mixed-in methods: the mixin phase enters mixin methods only to class
@@ -530,6 +539,74 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
}
/**
+ * Build the [[InlineInfo]] for a class symbol.
+ */
+ def buildInlineInfoFromClassSymbol(classSym: Symbol): InlineInfo = {
+ val isEffectivelyFinal = classSym.isEffectivelyFinal
+
+ val sam = {
+ if (classSym.isEffectivelyFinal) None
+ else {
+ // Phase travel necessary. For example, nullary methods (getter of an abstract val) get an
+ // empty parameter list in later phases and would therefore be picked as SAM.
+ val samSym = exitingPickler(definitions.samOf(classSym.tpe))
+ if (samSym == NoSymbol) None
+ else Some(samSym.javaSimpleName.toString + methodBTypeFromSymbol(samSym).descriptor)
+ }
+ }
+
+ var warning = Option.empty[ClassSymbolInfoFailureSI9111]
+
+ // Primitive methods cannot be inlined, so there's no point in building a MethodInlineInfo. Also, some
+ // primitive methods (e.g., `isInstanceOf`) have non-erased types, which confuses [[typeToBType]].
+ val methodInlineInfos = classSym.info.decls.iterator.filter(m => m.isMethod && !scalaPrimitives.isPrimitive(m)).flatMap({
+ case methodSym =>
+ if (completeSilentlyAndCheckErroneous(methodSym)) {
+ // Happens due to SI-9111. Just don't provide any MethodInlineInfo for that method, we don't need fail the compiler.
+ if (!classSym.isJavaDefined) devWarning("SI-9111 should only be possible for Java classes")
+ warning = Some(ClassSymbolInfoFailureSI9111(classSym.fullName))
+ Nil
+ } else {
+ val name = methodSym.javaSimpleName.toString // same as in genDefDef
+ val signature = name + methodBTypeFromSymbol(methodSym).descriptor
+
+ // In `trait T { object O }`, `oSym.isEffectivelyFinalOrNotOverridden` is true, but the
+ // method is abstract in bytecode, `defDef.rhs.isEmpty`. Abstract methods are excluded
+ // so they are not marked final in the InlineInfo attribute.
+ //
+ // However, due to https://github.com/scala/scala-dev/issues/126, this currently does not
+ // work, the abstract accessor for O will be marked effectivelyFinal.
+ val effectivelyFinal = methodSym.isEffectivelyFinalOrNotOverridden && !methodSym.isDeferred
+
+ val info = MethodInlineInfo(
+ effectivelyFinal = effectivelyFinal,
+ annotatedInline = methodSym.hasAnnotation(ScalaInlineClass),
+ annotatedNoInline = methodSym.hasAnnotation(ScalaNoInlineClass))
+
+ if (needsStaticImplMethod(methodSym)) {
+ val staticName = traitImplMethodName(methodSym).toString
+ val selfParam = methodSym.newSyntheticValueParam(methodSym.owner.typeConstructor, nme.SELF)
+ val staticMethodType = methodSym.info match {
+ case mt @ MethodType(params, res) => copyMethodType(mt, selfParam :: params, res)
+ }
+ val staticMethodSignature = staticName + methodBTypeFromMethodType(staticMethodType, isConstructor = false)
+ val staticMethodInfo = MethodInlineInfo(
+ effectivelyFinal = true,
+ annotatedInline = info.annotatedInline,
+ annotatedNoInline = info.annotatedNoInline)
+ if (methodSym.isMixinConstructor)
+ List((staticMethodSignature, staticMethodInfo))
+ else
+ List((signature, info), (staticMethodSignature, staticMethodInfo))
+ } else
+ List((signature, info))
+ }
+ }).toMap
+
+ InlineInfo(isEffectivelyFinal, sam, methodInlineInfos, warning)
+ }
+
+ /**
* For top-level objects without a companion class, the compiler generates a mirror class with
* static forwarders (Java compat). There's no symbol for the mirror class, but we still need a
* ClassBType (its info.nestedClasses will hold the InnerClass entries, see comment in BTypes).
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala b/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala
index fd558587e9..7b640ac54f 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala
@@ -108,15 +108,15 @@ object BackendReporting {
def emitWarning(settings: ScalaSettings): Boolean = this match {
case ClassNotFound(_, javaDefined) =>
- if (javaDefined) settings.YoptWarningNoInlineMixed
- else settings.YoptWarningNoInlineMissingBytecode
+ if (javaDefined) settings.optWarningNoInlineMixed
+ else settings.optWarningNoInlineMissingBytecode
case m @ MethodNotFound(_, _, _, missing) =>
if (m.isArrayMethod) false
- else settings.YoptWarningNoInlineMissingBytecode || missing.exists(_.emitWarning(settings))
+ else settings.optWarningNoInlineMissingBytecode || missing.exists(_.emitWarning(settings))
case FieldNotFound(_, _, _, missing) =>
- settings.YoptWarningNoInlineMissingBytecode || missing.exists(_.emitWarning(settings))
+ settings.optWarningNoInlineMissingBytecode || missing.exists(_.emitWarning(settings))
}
}
@@ -137,7 +137,7 @@ object BackendReporting {
def emitWarning(settings: ScalaSettings): Boolean = this match {
case NoClassBTypeInfoMissingBytecode(cause) => cause.emitWarning(settings)
- case NoClassBTypeInfoClassSymbolInfoFailedSI9111(_) => settings.YoptWarningNoInlineMissingBytecode
+ case NoClassBTypeInfoClassSymbolInfoFailedSI9111(_) => settings.optWarningNoInlineMissingBytecode
}
}
@@ -170,7 +170,7 @@ object BackendReporting {
case MethodInlineInfoIncomplete(_, _, _, cause) => cause.emitWarning(settings)
case MethodInlineInfoMissing(_, _, _, Some(cause)) => cause.emitWarning(settings)
- case MethodInlineInfoMissing(_, _, _, None) => settings.YoptWarningNoInlineMissingBytecode
+ case MethodInlineInfoMissing(_, _, _, None) => settings.optWarningNoInlineMissingBytecode
case MethodInlineInfoError(_, _, _, cause) => cause.emitWarning(settings)
}
@@ -216,7 +216,7 @@ object BackendReporting {
def emitWarning(settings: ScalaSettings): Boolean = this match {
case _: IllegalAccessInstruction | _: MethodWithHandlerCalledOnNonEmptyStack | _: SynchronizedMethod | _: StrictfpMismatch | _: ResultingMethodTooLarge =>
- settings.YoptWarnings.contains(settings.YoptWarningsChoices.anyInlineFailed)
+ settings.optWarnings.contains(settings.optWarningsChoices.anyInlineFailed)
case IllegalAccessCheckFailed(_, _, _, _, _, cause) =>
cause.emitWarning(settings)
@@ -238,7 +238,7 @@ object BackendReporting {
// but at the place where it's created (in findIllegalAccess) we don't have the necessary data (calleeName, calleeDescriptor).
case object UnknownInvokeDynamicInstruction extends OptimizerWarning {
override def toString = "The callee contains an InvokeDynamic instruction with an unknown bootstrap method (not a LambdaMetaFactory)."
- def emitWarning(settings: ScalaSettings): Boolean = settings.YoptWarnings.contains(settings.YoptWarningsChoices.anyInlineFailed)
+ def emitWarning(settings: ScalaSettings): Boolean = settings.optWarnings.contains(settings.optWarningsChoices.anyInlineFailed)
}
/**
@@ -250,7 +250,7 @@ object BackendReporting {
override def emitWarning(settings: ScalaSettings): Boolean = this match {
case RewriteClosureAccessCheckFailed(_, cause) => cause.emitWarning(settings)
- case RewriteClosureIllegalAccess(_, _) => settings.YoptWarnings.contains(settings.YoptWarningsChoices.anyInlineFailed)
+ case RewriteClosureIllegalAccess(_, _) => settings.optWarnings.contains(settings.optWarningsChoices.anyInlineFailed)
}
override def toString: String = this match {
@@ -282,10 +282,10 @@ object BackendReporting {
}
def emitWarning(settings: ScalaSettings): Boolean = this match {
- case NoInlineInfoAttribute(_) => settings.YoptWarningNoInlineMissingScalaInlineInfoAttr
+ case NoInlineInfoAttribute(_) => settings.optWarningNoInlineMissingScalaInlineInfoAttr
case ClassNotFoundWhenBuildingInlineInfoFromSymbol(cause) => cause.emitWarning(settings)
- case ClassSymbolInfoFailureSI9111(_) => settings.YoptWarningNoInlineMissingBytecode
- case UnknownScalaInlineInfoVersion(_, _) => settings.YoptWarningNoInlineMissingScalaInlineInfoAttr
+ case ClassSymbolInfoFailureSI9111(_) => settings.optWarningNoInlineMissingBytecode
+ case UnknownScalaInlineInfoVersion(_, _) => settings.optWarningNoInlineMissingScalaInlineInfoAttr
}
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala
index 1feca56923..d65380aa1f 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala
@@ -217,26 +217,6 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) {
nonOverloadedConstructors(tupleClassSymbols)
}
- // enumeration of specialized classes is temporary, while we still use the java-defined JFunctionN.
- // once we switch to ordinary FunctionN, we can use specializedSubclasses just like for tuples.
- private def specializedJFunctionSymbols(base: String): Seq[Symbol] = {
- def primitives = Seq("B", "S", "I", "J", "C", "F", "D", "Z", "V")
- def ijfd = Iterator("I", "J", "F", "D")
- def ijfdzv = Iterator("I", "J", "F", "D", "Z", "V")
- def ijd = Iterator("I", "J", "D")
- val classNames = {
- primitives.map(base + "0$mc" + _ + "$sp") // Function0
- } ++ {
- // return type specializations appear first in the name string (alphabetical sorting)
- for (r <- ijfdzv; a <- ijfd) yield base + "1$mc" + r + a + "$sp" // Function1
- } ++ {
- for (r <- ijfdzv; a <- ijd; b <- ijd) yield base + "2$mc" + r + a + b + "$sp" // Function2
- }
- classNames map getRequiredClass
- }
-
- lazy val functionRefs: Set[InternalName] = (FunctionClass.seq ++ specializedJFunctionSymbols("scala.runtime.java8.JFunction")).map(classBTypeFromSymbol(_).internalName).toSet
-
lazy val typeOfArrayOp: Map[Int, BType] = {
import scalaPrimitives._
Map(
@@ -342,8 +322,6 @@ trait CoreBTypesProxyGlobalIndependent[BTS <: BTypes] {
def srRefConstructors : Map[InternalName, MethodNameAndType]
def tupleClassConstructors : Map[InternalName, MethodNameAndType]
- def functionRefs: Set[InternalName]
-
def lambdaMetaFactoryBootstrapHandle : asm.Handle
def lambdaDeserializeBootstrapHandle : asm.Handle
}
@@ -410,8 +388,6 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes:
def srRefConstructors : Map[InternalName, MethodNameAndType] = _coreBTypes.srRefConstructors
def tupleClassConstructors : Map[InternalName, MethodNameAndType] = _coreBTypes.tupleClassConstructors
- def functionRefs: Set[InternalName] = _coreBTypes.functionRefs
-
def srSymbolLiteral : ClassBType = _coreBTypes.srSymbolLiteral
def srStructuralCallSite : ClassBType = _coreBTypes.srStructuralCallSite
def srLambdaDeserialize : ClassBType = _coreBTypes.srLambdaDeserialize
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
index 3520d57599..584b11d4ed 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
@@ -77,15 +77,16 @@ abstract class GenBCode extends BCodeSyncAndTry {
/* ---------------- q2 ---------------- */
- case class Item2(arrivalPos: Int,
- mirror: asm.tree.ClassNode,
- plain: asm.tree.ClassNode,
- bean: asm.tree.ClassNode,
- outFolder: scala.tools.nsc.io.AbstractFile) {
+ case class Item2(arrivalPos: Int,
+ mirror: asm.tree.ClassNode,
+ plain: asm.tree.ClassNode,
+ bean: asm.tree.ClassNode,
+ sourceFilePath: String,
+ outFolder: scala.tools.nsc.io.AbstractFile) {
def isPoison = { arrivalPos == Int.MaxValue }
}
- private val poison2 = Item2(Int.MaxValue, null, null, null, null)
+ private val poison2 = Item2(Int.MaxValue, null, null, null, null, null)
private val q2 = new _root_.java.util.LinkedList[Item2]
/* ---------------- q3 ---------------- */
@@ -205,6 +206,7 @@ abstract class GenBCode extends BCodeSyncAndTry {
val item2 =
Item2(arrivalPos,
mirrorC, plainC, beanC,
+ cunit.source.file.canonicalPath,
outF)
q2 add item2 // at the very end of this method so that no Worker2 thread starts mutating before we're done.
@@ -225,19 +227,20 @@ abstract class GenBCode extends BCodeSyncAndTry {
// add classes to the bytecode repo before building the call graph: the latter needs to
// look up classes and methods in the code repo.
- if (settings.YoptAddToBytecodeRepository) q2.asScala foreach {
- case Item2(_, mirror, plain, bean, _) =>
- if (mirror != null) byteCodeRepository.add(mirror, ByteCodeRepository.CompilationUnit)
- if (plain != null) byteCodeRepository.add(plain, ByteCodeRepository.CompilationUnit)
- if (bean != null) byteCodeRepository.add(bean, ByteCodeRepository.CompilationUnit)
+ if (settings.optAddToBytecodeRepository) q2.asScala foreach {
+ case Item2(_, mirror, plain, bean, sourceFilePath, _) =>
+ val someSourceFilePath = Some(sourceFilePath)
+ if (mirror != null) byteCodeRepository.add(mirror, someSourceFilePath)
+ if (plain != null) byteCodeRepository.add(plain, someSourceFilePath)
+ if (bean != null) byteCodeRepository.add(bean, someSourceFilePath)
}
- if (settings.YoptBuildCallGraph) q2.asScala foreach { item =>
+ if (settings.optBuildCallGraph) q2.asScala foreach { item =>
// skip call graph for mirror / bean: wd don't inline into tem, and they are not used in the plain class
if (item.plain != null) callGraph.addClass(item.plain)
}
- if (settings.YoptInlinerEnabled)
+ if (settings.optInlinerEnabled)
bTypes.inliner.runInliner()
- if (settings.YoptClosureInvocations)
+ if (settings.optClosureInvocations)
closureOptimizer.rewriteClosureApplyInvocations()
}
@@ -286,7 +289,7 @@ abstract class GenBCode extends BCodeSyncAndTry {
cw.toByteArray
}
- val Item2(arrivalPos, mirror, plain, bean, outFolder) = item
+ val Item2(arrivalPos, mirror, plain, bean, _, outFolder) = item
val mirrorC = if (mirror == null) null else SubItem3(mirror.name, getByteArray(mirror))
val plainC = SubItem3(plain.name, getByteArray(plain))
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala
index f94642389d..83615abc31 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala
@@ -2,17 +2,18 @@ package scala.tools.nsc
package backend.jvm
package analysis
+import java.lang.invoke.LambdaMetafactory
+
import scala.annotation.switch
-import scala.tools.asm.{Handle, Type}
+import scala.collection.JavaConverters._
+import scala.collection.mutable
import scala.tools.asm.Opcodes._
import scala.tools.asm.tree._
-import scala.tools.asm.tree.analysis.{Frame, BasicInterpreter, Analyzer, Value}
-import GenBCode._
+import scala.tools.asm.tree.analysis._
+import scala.tools.asm.{Handle, Type}
import scala.tools.nsc.backend.jvm.BTypes._
+import scala.tools.nsc.backend.jvm.GenBCode._
import scala.tools.nsc.backend.jvm.opt.BytecodeUtils._
-import java.lang.invoke.LambdaMetafactory
-import scala.collection.mutable
-import scala.collection.JavaConverters._
/**
* This component hosts tools and utilities used in the backend that require access to a `BTypes`
@@ -32,7 +33,12 @@ class BackendUtils[BT <: BTypes](val btypes: BT) {
*/
class AsmAnalyzer[V <: Value](methodNode: MethodNode, classInternalName: InternalName, val analyzer: Analyzer[V] = new Analyzer(new BasicInterpreter)) {
computeMaxLocalsMaxStack(methodNode)
- analyzer.analyze(classInternalName, methodNode)
+ try {
+ analyzer.analyze(classInternalName, methodNode)
+ } catch {
+ case ae: AnalyzerException =>
+ throw new AnalyzerException(null, "While processing " + classInternalName + "." + methodNode.name, ae)
+ }
def frameAt(instruction: AbstractInsnNode): Frame[V] = analyzer.frameAt(instruction, methodNode)
}
@@ -98,7 +104,7 @@ class BackendUtils[BT <: BTypes](val btypes: BT) {
* a boolean indicating if the instruction list contains an instantiation of a serializable SAM
* type.
*/
- def cloneInstructions(methodNode: MethodNode, labelMap: Map[LabelNode, LabelNode]): (InsnList, Map[AbstractInsnNode, AbstractInsnNode], Boolean) = {
+ def cloneInstructions(methodNode: MethodNode, labelMap: Map[LabelNode, LabelNode], keepLineNumbers: Boolean): (InsnList, Map[AbstractInsnNode, AbstractInsnNode], Boolean) = {
val javaLabelMap = labelMap.asJava
val result = new InsnList
var map = Map.empty[AbstractInsnNode, AbstractInsnNode]
@@ -112,18 +118,19 @@ class BackendUtils[BT <: BTypes](val btypes: BT) {
}
case _ =>
}
- val cloned = ins.clone(javaLabelMap)
- result add cloned
- map += ((ins, cloned))
+ if (keepLineNumbers || !ins.isInstanceOf[LineNumberNode]) {
+ val cloned = ins.clone(javaLabelMap)
+ result add cloned
+ map += ((ins, cloned))
+ }
}
(result, map, hasSerializableClosureInstantiation)
}
def getBoxedUnit: FieldInsnNode = new FieldInsnNode(GETSTATIC, srBoxedUnitRef.internalName, "UNIT", srBoxedUnitRef.descriptor)
- private val anonfunAdaptedName = """.*\$anonfun\$\d+\$adapted""".r
+ private val anonfunAdaptedName = """.*\$anonfun\$.*\$\d+\$adapted""".r
def hasAdaptedImplMethod(closureInit: ClosureInstantiation): Boolean = {
- isBuiltinFunctionType(Type.getReturnType(closureInit.lambdaMetaFactoryCall.indy.desc).getInternalName) &&
anonfunAdaptedName.pattern.matcher(closureInit.lambdaMetaFactoryCall.implMethod.getName).matches
}
@@ -248,8 +255,6 @@ class BackendUtils[BT <: BTypes](val btypes: BT) {
}
}
- def isBuiltinFunctionType(internalName: InternalName): Boolean = functionRefs(internalName)
-
/**
* Visit the class node and collect all referenced nested classes.
*/
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzer.scala b/src/compiler/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzer.scala
index 30e73f8ac2..01afd0d2ef 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzer.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzer.scala
@@ -5,8 +5,8 @@ package analysis
import java.util
import scala.annotation.switch
-import scala.tools.asm.{Type, Opcodes}
-import scala.tools.asm.tree.{MethodInsnNode, LdcInsnNode, AbstractInsnNode}
+import scala.tools.asm.{Opcodes, Type}
+import scala.tools.asm.tree.{AbstractInsnNode, LdcInsnNode, MethodInsnNode, MethodNode}
import scala.tools.asm.tree.analysis._
import scala.tools.nsc.backend.jvm.opt.BytecodeUtils
import BytecodeUtils._
@@ -63,7 +63,7 @@ object NullnessValue {
def unknown(insn: AbstractInsnNode) = if (BytecodeUtils.instructionResultSize(insn) == 2) UnknownValue2 else UnknownValue1
}
-final class NullnessInterpreter(bTypes: BTypes) extends Interpreter[NullnessValue](Opcodes.ASM5) {
+final class NullnessInterpreter(bTypes: BTypes, method: MethodNode) extends Interpreter[NullnessValue](Opcodes.ASM5) {
def newValue(tp: Type): NullnessValue = {
// ASM loves giving semantics to null. The behavior here is the same as in SourceInterpreter,
// which is provided by the framework.
@@ -80,7 +80,13 @@ final class NullnessInterpreter(bTypes: BTypes) extends Interpreter[NullnessValu
override def newParameterValue(isInstanceMethod: Boolean, local: Int, tp: Type): NullnessValue = {
// For instance methods, the `this` parameter is known to be not null.
- if (isInstanceMethod && local == 0) NotNullValue
+ val isThis = local == 0 && (isInstanceMethod || {
+ method.parameters != null && !method.parameters.isEmpty && {
+ val p = method.parameters.get(0)
+ (p.access & Opcodes.ACC_SYNTHETIC) != 0 && p.name == "$this"
+ }
+ })
+ if (isThis) NotNullValue
else super.newParameterValue(isInstanceMethod, local, tp)
}
@@ -197,7 +203,7 @@ class NullnessFrame(nLocals: Int, nStack: Int) extends AliasingFrame[NullnessVal
* This class is required to override the `newFrame` methods, which makes makes sure the analyzer
* uses NullnessFrames.
*/
-class NullnessAnalyzer(bTypes: BTypes) extends Analyzer[NullnessValue](new NullnessInterpreter(bTypes)) {
+class NullnessAnalyzer(bTypes: BTypes, method: MethodNode) extends Analyzer[NullnessValue](new NullnessInterpreter(bTypes, method)) {
override def newFrame(nLocals: Int, nStack: Int): NullnessFrame = new NullnessFrame(nLocals, nStack)
override def newFrame(src: Frame[_ <: NullnessValue]): NullnessFrame = new NullnessFrame(src)
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala
index 16590ec75c..78acd72dba 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala
@@ -15,7 +15,6 @@ import scala.tools.asm.Attribute
import scala.tools.nsc.backend.jvm.BackendReporting._
import scala.tools.nsc.util.ClassPath
import BytecodeUtils._
-import ByteCodeRepository._
import BTypes.InternalName
import java.util.concurrent.atomic.AtomicLong
@@ -29,9 +28,10 @@ class ByteCodeRepository[BT <: BTypes](val classPath: ClassPath, val btypes: BT)
import btypes._
/**
- * ClassNodes for classes being compiled in the current compilation run.
+ * Contains ClassNodes and the canonical path of the source file path of classes being compiled in
+ * the current compilation run.
*/
- val compilingClasses: concurrent.Map[InternalName, ClassNode] = recordPerRunCache(concurrent.TrieMap.empty)
+ val compilingClasses: concurrent.Map[InternalName, (ClassNode, String)] = recordPerRunCache(concurrent.TrieMap.empty)
/**
* Cache for parsed ClassNodes.
@@ -67,20 +67,35 @@ class ByteCodeRepository[BT <: BTypes](val classPath: ClassPath, val btypes: BT)
}
}
- def add(classNode: ClassNode, source: Source) = {
- if (source == CompilationUnit) compilingClasses(classNode.name) = classNode
- else parsedClasses(classNode.name) = Right((classNode, lruCounter.incrementAndGet()))
+ def add(classNode: ClassNode, sourceFilePath: Option[String]) = sourceFilePath match {
+ case Some(path) if path != "<no file>" => compilingClasses(classNode.name) = (classNode, path)
+ case _ => parsedClasses(classNode.name) = Right((classNode, lruCounter.incrementAndGet()))
+ }
+
+ private def parsedClassNode(internalName: InternalName): Either[ClassNotFound, ClassNode] = {
+ val r = parsedClasses.get(internalName) match {
+ case Some(l @ Left(_)) => l
+ case Some(r @ Right((classNode, _))) =>
+ parsedClasses(internalName) = Right((classNode, lruCounter.incrementAndGet()))
+ r
+ case None =>
+ limitCacheSize()
+ val res = parseClass(internalName).map((_, lruCounter.incrementAndGet()))
+ parsedClasses(internalName) = res
+ res
+ }
+ r.map(_._1)
}
/**
- * The class node and source for an internal name. If the class node is not yet available, it is
- * parsed from the classfile on the compile classpath.
+ * The class node and source file path (if the class is being compiled) for an internal name. If
+ * the class node is not yet available, it is parsed from the classfile on the compile classpath.
*/
- def classNodeAndSource(internalName: InternalName): Either[ClassNotFound, (ClassNode, Source)] = {
- classNode(internalName) map (n => {
- val source = if (compilingClasses contains internalName) CompilationUnit else Classfile
- (n, source)
- })
+ def classNodeAndSourceFilePath(internalName: InternalName): Either[ClassNotFound, (ClassNode, Option[String])] = {
+ compilingClasses.get(internalName) match {
+ case Some((c, p)) => Right((c, Some(p)))
+ case _ => parsedClassNode(internalName).map((_, None))
+ }
}
/**
@@ -88,19 +103,9 @@ class ByteCodeRepository[BT <: BTypes](val classPath: ClassPath, val btypes: BT)
* the classfile on the compile classpath.
*/
def classNode(internalName: InternalName): Either[ClassNotFound, ClassNode] = {
- compilingClasses.get(internalName).map(Right(_)) getOrElse {
- val r = parsedClasses.get(internalName) match {
- case Some(l @ Left(_)) => l
- case Some(r @ Right((classNode, _))) =>
- parsedClasses(internalName) = Right((classNode, lruCounter.incrementAndGet()))
- r
- case None =>
- limitCacheSize()
- val res = parseClass(internalName).map((_, lruCounter.incrementAndGet()))
- parsedClasses(internalName) = res
- res
- }
- r.map(_._1)
+ compilingClasses.get(internalName) match {
+ case Some((c, _)) => Right(c)
+ case None => parsedClassNode(internalName)
}
}
@@ -289,13 +294,3 @@ class ByteCodeRepository[BT <: BTypes](val classPath: ClassPath, val btypes: BT)
}
}
}
-
-object ByteCodeRepository {
- /**
- * The source of a ClassNode in the ByteCodeRepository. Can be either [[CompilationUnit]] if the
- * class is being compiled or [[Classfile]] if the class was parsed from the compilation classpath.
- */
- sealed trait Source
- object CompilationUnit extends Source
- object Classfile extends Source
-}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala
index 63906d80e5..e21c46dbe9 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala
@@ -93,6 +93,15 @@ object BytecodeUtils {
op == INVOKESPECIAL || op == INVOKESTATIC
}
+ def isVirtualCall(instruction: AbstractInsnNode): Boolean = {
+ val op = instruction.getOpcode
+ op == INVOKEVIRTUAL || op == INVOKEINTERFACE
+ }
+
+ def isCall(instruction: AbstractInsnNode): Boolean = {
+ isNonVirtualCall(instruction) || isVirtualCall(instruction)
+ }
+
def isExecutable(instruction: AbstractInsnNode): Boolean = instruction.getOpcode >= 0
def isConstructor(methodNode: MethodNode): Boolean = {
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala
index d241acf7b1..5248183337 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala
@@ -9,14 +9,13 @@ package opt
import scala.collection.immutable.IntMap
import scala.reflect.internal.util.{NoPosition, Position}
-import scala.tools.asm.{Opcodes, Type, Handle}
+import scala.tools.asm.{Handle, Opcodes, Type}
import scala.tools.asm.tree._
import scala.collection.{concurrent, mutable}
import scala.collection.JavaConverters._
-import scala.tools.nsc.backend.jvm.BTypes.InternalName
+import scala.tools.nsc.backend.jvm.BTypes.{InternalName, MethodInlineInfo}
import scala.tools.nsc.backend.jvm.BackendReporting._
import scala.tools.nsc.backend.jvm.analysis._
-import ByteCodeRepository.{Source, CompilationUnit}
import BytecodeUtils._
class CallGraph[BT <: BTypes](val btypes: BT) {
@@ -68,6 +67,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
}
def containsCallsite(callsite: Callsite): Boolean = callsites(callsite.callsiteMethod) contains callsite.callsiteInstruction
+ def findCallSite(method: MethodNode, call: MethodInsnNode): Option[Callsite] = callsites.getOrElse(method, Map.empty).get(call)
def removeClosureInstantiation(indy: InvokeDynamicInsnNode, methodNode: MethodNode): Option[ClosureInstantiation] = {
val methodClosureInits = closureInstantiations(methodNode)
@@ -102,8 +102,8 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
// It is also used to get the stack height at the call site.
val analyzer = {
- if (compilerSettings.YoptNullnessTracking && AsmAnalyzer.sizeOKForNullness(methodNode)) {
- Some(new AsmAnalyzer(methodNode, definingClass.internalName, new NullnessAnalyzer(btypes)))
+ if (compilerSettings.optNullnessTracking && AsmAnalyzer.sizeOKForNullness(methodNode)) {
+ Some(new AsmAnalyzer(methodNode, definingClass.internalName, new NullnessAnalyzer(btypes, methodNode)))
} else if (AsmAnalyzer.sizeOKForBasicValue(methodNode)) {
Some(new AsmAnalyzer(methodNode, definingClass.internalName))
} else None
@@ -128,17 +128,17 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
methodNode.instructions.iterator.asScala foreach {
case call: MethodInsnNode if a.frameAt(call) != null => // skips over unreachable code
val callee: Either[OptimizerWarning, Callee] = for {
- (method, declarationClass) <- byteCodeRepository.methodNode(call.owner, call.name, call.desc): Either[OptimizerWarning, (MethodNode, InternalName)]
- (declarationClassNode, source) <- byteCodeRepository.classNodeAndSource(declarationClass): Either[OptimizerWarning, (ClassNode, Source)]
+ (method, declarationClass) <- byteCodeRepository.methodNode(call.owner, call.name, call.desc): Either[OptimizerWarning, (MethodNode, InternalName)]
+ (declarationClassNode, calleeSourceFilePath) <- byteCodeRepository.classNodeAndSourceFilePath(declarationClass): Either[OptimizerWarning, (ClassNode, Option[String])]
} yield {
val declarationClassBType = classBTypeFromClassNode(declarationClassNode)
- val info = analyzeCallsite(method, declarationClassBType, call, source)
+ val info = analyzeCallsite(method, declarationClassBType, call, calleeSourceFilePath)
import info._
Callee(
callee = method,
calleeDeclarationClass = declarationClassBType,
safeToInline = safeToInline,
- canInlineFromSource = canInlineFromSource,
+ sourceFilePath = sourceFilePath,
annotatedInline = annotatedInline,
annotatedNoInline = annotatedNoInline,
samParamTypes = info.samParamTypes,
@@ -256,7 +256,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
/**
* Just a named tuple used as return type of `analyzeCallsite`.
*/
- private case class CallsiteInfo(safeToInline: Boolean, canInlineFromSource: Boolean,
+ private case class CallsiteInfo(safeToInline: Boolean, sourceFilePath: Option[String],
annotatedInline: Boolean, annotatedNoInline: Boolean,
samParamTypes: IntMap[ClassBType],
warning: Option[CalleeInfoWarning])
@@ -264,7 +264,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
/**
* Analyze a callsite and gather meta-data that can be used for inlining decisions.
*/
- private def analyzeCallsite(calleeMethodNode: MethodNode, calleeDeclarationClassBType: ClassBType, call: MethodInsnNode, calleeSource: Source): CallsiteInfo = {
+ private def analyzeCallsite(calleeMethodNode: MethodNode, calleeDeclarationClassBType: ClassBType, call: MethodInsnNode, calleeSourceFilePath: Option[String]): CallsiteInfo = {
val methodSignature = calleeMethodNode.name + calleeMethodNode.desc
try {
@@ -273,8 +273,6 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
// callee, we only check there for the methodInlineInfo, we should find it there.
calleeDeclarationClassBType.info.orThrow.inlineInfo.methodInfos.get(methodSignature) match {
case Some(methodInlineInfo) =>
- val canInlineFromSource = compilerSettings.YoptInlineGlobal || calleeSource == CompilationUnit
-
val isAbstract = BytecodeUtils.isAbstractMethod(calleeMethodNode)
val receiverType = classBTypeFromParsedClassfile(call.owner)
@@ -308,13 +306,13 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
// static impl method first (safeToRewrite).
CallsiteInfo(
safeToInline =
- canInlineFromSource &&
+ inlinerHeuristics.canInlineFromSource(calleeSourceFilePath) &&
isStaticallyResolved && // (1)
!isAbstract &&
!BytecodeUtils.isConstructor(calleeMethodNode) &&
!BytecodeUtils.isNativeMethod(calleeMethodNode) &&
!BytecodeUtils.hasCallerSensitiveAnnotation(calleeMethodNode),
- canInlineFromSource = canInlineFromSource,
+ sourceFilePath = calleeSourceFilePath,
annotatedInline = methodInlineInfo.annotatedInline,
annotatedNoInline = methodInlineInfo.annotatedNoInline,
samParamTypes = samParamTypes(calleeMethodNode, receiverType),
@@ -322,12 +320,12 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
case None =>
val warning = MethodInlineInfoMissing(calleeDeclarationClassBType.internalName, calleeMethodNode.name, calleeMethodNode.desc, calleeDeclarationClassBType.info.orThrow.inlineInfo.warning)
- CallsiteInfo(false, false, false, false, IntMap.empty, Some(warning))
+ CallsiteInfo(false, None, false, false, IntMap.empty, Some(warning))
}
} catch {
case Invalid(noInfo: NoClassBTypeInfo) =>
val warning = MethodInlineInfoError(calleeDeclarationClassBType.internalName, calleeMethodNode.name, calleeMethodNode.desc, noInfo)
- CallsiteInfo(false, false, false, false, IntMap.empty, Some(warning))
+ CallsiteInfo(false, None, false, false, IntMap.empty, Some(warning))
}
}
@@ -359,7 +357,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
"Invocation of" +
s" ${callee.map(_.calleeDeclarationClass.internalName).getOrElse("?")}.${callsiteInstruction.name + callsiteInstruction.desc}" +
s"@${callsiteMethod.instructions.indexOf(callsiteInstruction)}" +
- s" in ${callsiteClass.internalName}.${callsiteMethod.name}"
+ s" in ${callsiteClass.internalName}.${callsiteMethod.name}${callsiteMethod.desc}"
}
final case class ClonedCallsite(callsite: Callsite, clonedWhenInlining: Callsite)
@@ -389,7 +387,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
* gathering the information about this callee.
*/
final case class Callee(callee: MethodNode, calleeDeclarationClass: btypes.ClassBType,
- safeToInline: Boolean, canInlineFromSource: Boolean,
+ safeToInline: Boolean, sourceFilePath: Option[String],
annotatedInline: Boolean, annotatedNoInline: Boolean,
samParamTypes: IntMap[btypes.ClassBType],
calleeInfoWarning: Option[CalleeInfoWarning]) {
@@ -432,7 +430,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
def unapply(insn: AbstractInsnNode): Option[(InvokeDynamicInsnNode, Type, Handle, Type)] = insn match {
case indy: InvokeDynamicInsnNode if indy.bsm == metafactoryHandle || indy.bsm == altMetafactoryHandle =>
indy.bsmArgs match {
- case Array(samMethodType: Type, implMethod: Handle, instantiatedMethodType: Type, xs@_*) => // xs binding because IntelliJ gets confused about _@_*
+ case Array(samMethodType: Type, implMethod: Handle, instantiatedMethodType: Type, _@_*) =>
// LambdaMetaFactory performs a number of automatic adaptations when invoking the lambda
// implementation method (casting, boxing, unboxing, and primitive widening, see Javadoc).
//
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala
index 93dc40f318..081830d61d 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala
@@ -17,7 +17,6 @@ import scala.tools.nsc.backend.jvm.BTypes.InternalName
import BytecodeUtils._
import BackendReporting._
import Opcodes._
-import scala.tools.nsc.backend.jvm.opt.ByteCodeRepository.CompilationUnit
import scala.collection.JavaConverters._
class ClosureOptimizer[BT <: BTypes](val btypes: BT) {
@@ -354,16 +353,15 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) {
// the method node is needed for building the call graph entry
val bodyMethod = byteCodeRepository.methodNode(lambdaBodyHandle.getOwner, lambdaBodyHandle.getName, lambdaBodyHandle.getDesc)
- def bodyMethodIsBeingCompiled = byteCodeRepository.classNodeAndSource(lambdaBodyHandle.getOwner).map(_._2 == CompilationUnit).getOrElse(false)
+ val sourceFilePath = byteCodeRepository.compilingClasses.get(lambdaBodyHandle.getOwner).map(_._2)
val callee = bodyMethod.map({
case (bodyMethodNode, bodyMethodDeclClass) =>
val bodyDeclClassType = classBTypeFromParsedClassfile(bodyMethodDeclClass)
- val canInlineFromSource = compilerSettings.YoptInlineGlobal || bodyMethodIsBeingCompiled
Callee(
callee = bodyMethodNode,
calleeDeclarationClass = bodyDeclClassType,
- safeToInline = canInlineFromSource,
- canInlineFromSource = canInlineFromSource,
+ safeToInline = inlinerHeuristics.canInlineFromSource(sourceFilePath),
+ sourceFilePath = sourceFilePath,
annotatedInline = false,
annotatedNoInline = false,
samParamTypes = callGraph.samParamTypes(bodyMethodNode, bodyDeclClassType),
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/CopyProp.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/CopyProp.scala
index 4163d62df7..b05669ce89 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/CopyProp.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/CopyProp.scala
@@ -295,18 +295,12 @@ class CopyProp[BT <: BTypes](val btypes: BT) {
}
/**
- * Eliminate the closure value produced by `indy`. If the SAM type is known to construct
- * without side-effects (e.g. scala/FunctionN), the `indy` and its inputs
- * are eliminated, otherwise a POP is inserted.
+ * Eliminate LMF `indy` and its inputs.
*/
def handleClosureInst(indy: InvokeDynamicInsnNode): Unit = {
- if (isBuiltinFunctionType(Type.getReturnType(indy.desc).getInternalName)) {
- toRemove += indy
- callGraph.removeClosureInstantiation(indy, method)
- handleInputs(indy, Type.getArgumentTypes(indy.desc).length)
- } else {
- toInsertAfter(indy) = getPop(1)
- }
+ toRemove += indy
+ callGraph.removeClosureInstantiation(indy, method)
+ handleInputs(indy, Type.getArgumentTypes(indy.desc).length)
}
def runQueue(): Unit = while (queue.nonEmpty) {
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/InlineInfoAttribute.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/InlineInfoAttribute.scala
index 79d26b0b4e..5ce7072c60 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/InlineInfoAttribute.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/InlineInfoAttribute.scala
@@ -51,7 +51,6 @@ case class InlineInfoAttribute(inlineInfo: InlineInfo) extends Attribute(InlineI
if (inlineInfo.isEffectivelyFinal) flags |= 1
// flags |= 2 // no longer written
if (inlineInfo.sam.isDefined) flags |= 4
- if (inlineInfo.lateInterfaces.nonEmpty) flags |= 8
result.putByte(flags)
for (samNameDesc <- inlineInfo.sam) {
@@ -79,9 +78,6 @@ case class InlineInfoAttribute(inlineInfo: InlineInfo) extends Attribute(InlineI
result.putByte(inlineInfo)
}
- result.putShort(inlineInfo.lateInterfaces.length)
- for (i <- inlineInfo.lateInterfaces) result.putShort(cw.newUTF8(i))
-
result
}
@@ -105,7 +101,6 @@ case class InlineInfoAttribute(inlineInfo: InlineInfo) extends Attribute(InlineI
val isFinal = (flags & 1) != 0
val hasSelf = (flags & 2) != 0
val hasSam = (flags & 4) != 0
- val hasLateInterfaces = (flags & 8) != 0
if (hasSelf) nextUTF8() // no longer used
@@ -128,13 +123,7 @@ case class InlineInfoAttribute(inlineInfo: InlineInfo) extends Attribute(InlineI
(name + desc, MethodInlineInfo(isFinal, isInline, isNoInline))
}).toMap
- val lateInterfaces = if (!hasLateInterfaces) Nil else {
- val numLateInterfaces = nextShort()
- (0 until numLateInterfaces).map(_ => nextUTF8())
- }
-
val info = InlineInfo(isFinal, sam, infos, None)
- info.lateInterfaces ++= lateInterfaces
InlineInfoAttribute(info)
} else {
val msg = UnknownScalaInlineInfoVersion(cr.getClassName, version)
@@ -161,8 +150,6 @@ object InlineInfoAttribute {
* [u2] name (reference)
* [u2] descriptor (reference)
* [u1] isFinal (<< 0), traitMethodWithStaticImplementation (<< 1), hasInlineAnnotation (<< 2), hasNoInlineAnnotation (<< 3)
- * [u2]? numLateInterfaces
- * [u2] lateInterface (reference)
*/
final val VERSION: Byte = 1
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
index f35eaa45e9..9c5a1a9f98 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
@@ -25,6 +25,9 @@ class Inliner[BT <: BTypes](val btypes: BT) {
import inlinerHeuristics._
import backendUtils._
+ case class InlineLog(request: InlineRequest, sizeBefore: Int, sizeAfter: Int, sizeInlined: Int, warning: Option[CannotInlineWarning])
+ var inlineLog: List[InlineLog] = Nil
+
def runInliner(): Unit = {
for (request <- collectAndOrderInlineRequests) {
val Right(callee) = request.callsite.callee // collectAndOrderInlineRequests returns callsites with a known callee
@@ -35,13 +38,36 @@ class Inliner[BT <: BTypes](val btypes: BT) {
val warnings = inline(request)
for (warning <- warnings) {
- if ((callee.annotatedInline && btypes.compilerSettings.YoptWarningEmitAtInlineFailed) || warning.emitWarning(compilerSettings)) {
+ if ((callee.annotatedInline && btypes.compilerSettings.optWarningEmitAtInlineFailed) || warning.emitWarning(compilerSettings)) {
val annotWarn = if (callee.annotatedInline) " is annotated @inline but" else ""
val msg = s"${BackendReporting.methodSignature(callee.calleeDeclarationClass.internalName, callee.callee)}$annotWarn could not be inlined:\n$warning"
backendReporting.inlinerWarning(request.callsite.callsitePosition, msg)
}
}
}
+
+ if (compilerSettings.YoptLogInline.isSetByUser) {
+ val methodPrefix = { val p = compilerSettings.YoptLogInline.value; if (p == "_") "" else p }
+ val byCallsiteMethod = inlineLog.groupBy(_.request.callsite.callsiteMethod).toList.sortBy(_._2.head.request.callsite.callsiteClass.internalName)
+ for ((m, mLogs) <- byCallsiteMethod) {
+ val initialSize = mLogs.minBy(_.sizeBefore).sizeBefore
+ val firstLog = mLogs.head
+ val methodName = s"${firstLog.request.callsite.callsiteClass.internalName}.${m.name}"
+ if (methodName.startsWith(methodPrefix)) {
+ println(s"Inlining into $methodName (initially $initialSize instructions, ultimately ${m.instructions.size}):")
+ val byCallee = mLogs.groupBy(_.request.callsite.callee.get).toList.sortBy(_._2.length).reverse
+ for ((c, cLogs) <- byCallee) {
+ val first = cLogs.head
+ if (first.warning.isEmpty) {
+ val num = if (cLogs.tail.isEmpty) "" else s" ${cLogs.length} times"
+ println(s" - Inlined ${c.calleeDeclarationClass.internalName}.${c.callee.name} (${first.sizeInlined} instructions)$num: ${first.request.reason}")
+ } else
+ println(s" - Failed to inline ${c.calleeDeclarationClass.internalName}.${c.callee.name} (${first.request.reason}): ${first.warning.get}")
+ }
+ println()
+ }
+ }
+ }
}
/**
@@ -80,6 +106,8 @@ class Inliner[BT <: BTypes](val btypes: BT) {
val elided = mutable.Set.empty[InlineRequest]
def nonElidedRequests(methodNode: MethodNode): Set[InlineRequest] = requestsByMethod(methodNode) diff elided
+ def allCallees(r: InlineRequest): Set[MethodNode] = r.post.flatMap(allCallees).toSet + r.callsite.callee.get.callee
+
/**
* Break cycles in the inline request graph by removing callsites.
*
@@ -88,20 +116,20 @@ class Inliner[BT <: BTypes](val btypes: BT) {
*/
def breakInlineCycles: List[InlineRequest] = {
// is there a path of inline requests from start to goal?
- def isReachable(start: MethodNode, goal: MethodNode): Boolean = {
- @tailrec def reachableImpl(check: List[MethodNode], visited: Set[MethodNode]): Boolean = check match {
- case x :: xs =>
+ def isReachable(start: Set[MethodNode], goal: MethodNode): Boolean = {
+ @tailrec def reachableImpl(check: Set[MethodNode], visited: Set[MethodNode]): Boolean = {
+ if (check.isEmpty) false
+ else {
+ val x = check.head
if (x == goal) true
- else if (visited(x)) reachableImpl(xs, visited)
+ else if (visited(x)) reachableImpl(check - x, visited)
else {
- val callees = nonElidedRequests(x).map(_.callsite.callee.get.callee)
- reachableImpl(xs ::: callees.toList, visited + x)
+ val callees = nonElidedRequests(x).flatMap(allCallees)
+ reachableImpl(check - x ++ callees, visited + x)
}
-
- case Nil =>
- false
+ }
}
- reachableImpl(List(start), Set.empty)
+ reachableImpl(start, Set.empty)
}
val result = new mutable.ListBuffer[InlineRequest]()
@@ -110,7 +138,7 @@ class Inliner[BT <: BTypes](val btypes: BT) {
java.util.Arrays.sort(requests, callsiteOrdering)
for (r <- requests) {
// is there a chain of inlining requests that would inline the callsite method into the callee?
- if (isReachable(r.callsite.callee.get.callee, r.callsite.callsiteMethod))
+ if (isReachable(allCallees(r), r.callsite.callsiteMethod))
elided += r
else
result += r
@@ -124,8 +152,8 @@ class Inliner[BT <: BTypes](val btypes: BT) {
if (requests.isEmpty) Nil
else {
val (leaves, others) = requests.partition(r => {
- val inlineRequestsForCallee = nonElidedRequests(r.callsite.callee.get.callee)
- inlineRequestsForCallee.forall(visited)
+ val inlineRequestsForCallees = allCallees(r).flatMap(nonElidedRequests)
+ inlineRequestsForCallees.forall(visited)
})
assert(leaves.nonEmpty, requests)
leaves ::: leavesFirst(others, visited ++ leaves)
@@ -184,7 +212,7 @@ class Inliner[BT <: BTypes](val btypes: BT) {
def impl(post: InlineRequest, at: Callsite): List[InlineRequest] = {
post.callsite.inlinedClones.find(_.clonedWhenInlining == at) match {
case Some(clonedCallsite) =>
- List(InlineRequest(clonedCallsite.callsite, post.post))
+ List(InlineRequest(clonedCallsite.callsite, post.post, post.reason))
case None =>
post.post.flatMap(impl(_, post.callsite)).flatMap(impl(_, at))
}
@@ -199,9 +227,17 @@ class Inliner[BT <: BTypes](val btypes: BT) {
* @return An inliner warning for each callsite that could not be inlined.
*/
def inline(request: InlineRequest): List[CannotInlineWarning] = canInlineBody(request.callsite) match {
- case Some(w) => List(w)
+ case Some(w) =>
+ if (compilerSettings.YoptLogInline.isSetByUser) {
+ val size = request.callsite.callsiteMethod.instructions.size
+ inlineLog ::= InlineLog(request, size, size, 0, Some(w))
+ }
+ List(w)
case None =>
+ val sizeBefore = request.callsite.callsiteMethod.instructions.size
inlineCallsite(request.callsite)
+ if (compilerSettings.YoptLogInline.isSetByUser)
+ inlineLog ::= InlineLog(request, sizeBefore, request.callsite.callsiteMethod.instructions.size, request.callsite.callee.get.callee.instructions.size, None)
val postRequests = request.post.flatMap(adaptPostRequestForMainCallsite(_, request.callsite))
postRequests flatMap inline
}
@@ -219,7 +255,7 @@ class Inliner[BT <: BTypes](val btypes: BT) {
def inlineCallsite(callsite: Callsite): Unit = {
import callsite.{callsiteClass, callsiteMethod, callsiteInstruction, receiverKnownNotNull, callsiteStackHeight}
val Right(callsiteCallee) = callsite.callee
- import callsiteCallee.{callee, calleeDeclarationClass}
+ import callsiteCallee.{callee, calleeDeclarationClass, sourceFilePath}
// Inlining requires the callee not to have unreachable code, the analyzer used below should not
// return any `null` frames. Note that inlining a method can create unreachable code. Example:
@@ -234,11 +270,14 @@ class Inliner[BT <: BTypes](val btypes: BT) {
// New labels for the cloned instructions
val labelsMap = cloneLabels(callee)
- val (clonedInstructions, instructionMap, hasSerializableClosureInstantiation) = cloneInstructions(callee, labelsMap)
- val keepLineNumbers = callsiteClass == calleeDeclarationClass
- if (!keepLineNumbers) {
- removeLineNumberNodes(clonedInstructions)
+ val sameSourceFile = sourceFilePath match {
+ case Some(calleeSource) => byteCodeRepository.compilingClasses.get(callsiteClass.internalName) match {
+ case Some((_, `calleeSource`)) => true
+ case _ => false
+ }
+ case _ => false
}
+ val (clonedInstructions, instructionMap, hasSerializableClosureInstantiation) = cloneInstructions(callee, labelsMap, keepLineNumbers = sameSourceFile)
// local vars in the callee are shifted by the number of locals at the callsite
val localVarShift = callsiteMethod.maxLocals
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala
index 6aaf9734d3..79e74f3eb7 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala
@@ -7,21 +7,23 @@ package scala.tools.nsc
package backend.jvm
package opt
-import scala.tools.asm.tree.MethodNode
-import scala.tools.nsc.backend.jvm.BTypes.InternalName
import scala.collection.JavaConverters._
+import scala.tools.asm.Opcodes
+import scala.tools.asm.tree.{MethodInsnNode, MethodNode}
+import scala.tools.nsc.backend.jvm.BTypes.InternalName
import scala.tools.nsc.backend.jvm.BackendReporting.OptimizerWarning
class InlinerHeuristics[BT <: BTypes](val bTypes: BT) {
import bTypes._
- import inliner._
import callGraph._
- case class InlineRequest(callsite: Callsite, post: List[InlineRequest]) {
+ case class InlineRequest(callsite: Callsite, post: List[InlineRequest], reason: String) {
// invariant: all post inline requests denote callsites in the callee of the main callsite
for (pr <- post) assert(pr.callsite.callsiteMethod == callsite.callee.get.callee, s"Callsite method mismatch: main $callsite - post ${pr.callsite}")
}
+ def canInlineFromSource(sourceFilePath: Option[String]) = compilerSettings.optInlineGlobal || sourceFilePath.isDefined
+
/**
* Select callsites from the call graph that should be inlined, grouped by the containing method.
* Cyclic inlining requests are allowed, the inliner will eliminate requests to break cycles.
@@ -32,27 +34,27 @@ class InlinerHeuristics[BT <: BTypes](val bTypes: BT) {
// classpath. In order to get only the callsites being compiled, we start at the map of
// compilingClasses in the byteCodeRepository.
val compilingMethods = for {
- classNode <- byteCodeRepository.compilingClasses.valuesIterator
- methodNode <- classNode.methods.iterator.asScala
+ (classNode, _) <- byteCodeRepository.compilingClasses.valuesIterator
+ methodNode <- classNode.methods.iterator.asScala
} yield methodNode
compilingMethods.map(methodNode => {
var requests = Set.empty[InlineRequest]
callGraph.callsites(methodNode).valuesIterator foreach {
- case callsite @ Callsite(_, _, _, Right(Callee(callee, calleeDeclClass, safeToInline, canInlineFromSource, calleeAnnotatedInline, _, _, callsiteWarning)), _, _, _, pos, _, _) =>
- inlineRequest(callsite) match {
+ case callsite @ Callsite(_, _, _, Right(Callee(callee, calleeDeclClass, safeToInline, sourceFilePath, calleeAnnotatedInline, _, _, callsiteWarning)), _, _, _, pos, _, _) =>
+ inlineRequest(callsite, requests) match {
case Some(Right(req)) => requests += req
case Some(Left(w)) =>
- if ((calleeAnnotatedInline && bTypes.compilerSettings.YoptWarningEmitAtInlineFailed) || w.emitWarning(compilerSettings)) {
+ if ((calleeAnnotatedInline && bTypes.compilerSettings.optWarningEmitAtInlineFailed) || w.emitWarning(compilerSettings)) {
val annotWarn = if (calleeAnnotatedInline) " is annotated @inline but" else ""
val msg = s"${BackendReporting.methodSignature(calleeDeclClass.internalName, callee)}$annotWarn could not be inlined:\n$w"
backendReporting.inlinerWarning(callsite.callsitePosition, msg)
}
case None =>
- if (canInlineFromSource && calleeAnnotatedInline && !callsite.annotatedNoInline && bTypes.compilerSettings.YoptWarningEmitAtInlineFailed) {
+ if (canInlineFromSource(sourceFilePath) && calleeAnnotatedInline && !callsite.annotatedNoInline && bTypes.compilerSettings.optWarningEmitAtInlineFailed) {
// if the callsite is annotated @inline, we report an inline warning even if the underlying
- // reason is, for example, mixed compilation (which has a separate -Yopt-warning flag).
+ // reason is, for example, mixed compilation (which has a separate -opt-warning flag).
def initMsg = s"${BackendReporting.methodSignature(calleeDeclClass.internalName, callee)} is annotated @inline but cannot be inlined"
def warnMsg = callsiteWarning.map(" Possible reason:\n" + _).getOrElse("")
if (!safeToInline)
@@ -87,20 +89,49 @@ class InlinerHeuristics[BT <: BTypes](val bTypes: BT) {
* InlineRequest for the original callsite? new subclass of OptimizerWarning.
* `Some(Right)` if the callsite should be and can be inlined
*/
- def inlineRequest(callsite: Callsite): Option[Either[OptimizerWarning, InlineRequest]] = {
+ def inlineRequest(callsite: Callsite, selectedRequestsForCallee: Set[InlineRequest]): Option[Either[OptimizerWarning, InlineRequest]] = {
val callee = callsite.callee.get
- def requestIfCanInline(callsite: Callsite): Either[OptimizerWarning, InlineRequest] = inliner.earlyCanInlineCheck(callsite) match {
+ def requestIfCanInline(callsite: Callsite, reason: String): Either[OptimizerWarning, InlineRequest] = inliner.earlyCanInlineCheck(callsite) match {
case Some(w) => Left(w)
- case None => Right(InlineRequest(callsite, Nil))
+ case None =>
+ val callee = callsite.callee.get
+ val postInlineRequest: List[InlineRequest] = callee.calleeDeclarationClass.isInterface match {
+ case Right(true) =>
+ // Treat the pair of trait interface method and static method as one for the purposes of inlining:
+ // if we inline invokeinterface, invoke the invokestatic, too.
+ val calls = callee.callee.instructions.iterator().asScala.filter(BytecodeUtils.isCall).take(2).toList
+ calls match {
+ case List(x: MethodInsnNode) if x.getOpcode == Opcodes.INVOKESTATIC && x.name == (callee.callee.name + "$") =>
+ callGraph.addIfMissing(callee.callee, callee.calleeDeclarationClass)
+ val maybeNodeToCallsite1 = callGraph.findCallSite(callee.callee, x)
+ maybeNodeToCallsite1.toList.flatMap(x => requestIfCanInline(x, reason).right.toOption)
+ case _ =>
+ Nil
+
+ }
+ case _ => Nil
+ }
+
+ Right(InlineRequest(callsite, postInlineRequest, reason))
+
}
compilerSettings.YoptInlineHeuristics.value match {
case "everything" =>
- if (callee.safeToInline) Some(requestIfCanInline(callsite))
+ if (callee.safeToInline) {
+ val reason = if (compilerSettings.YoptLogInline.isSetByUser) "the inline strategy is \"everything\"" else null
+ Some(requestIfCanInline(callsite, reason))
+ }
else None
case "at-inline-annotated" =>
- if (callee.safeToInline && callee.annotatedInline) Some(requestIfCanInline(callsite))
+ if (callee.safeToInline && callee.annotatedInline) {
+ val reason = if (compilerSettings.YoptLogInline.isSetByUser) {
+ val what = if (callee.safeToInline) "callee" else "callsite"
+ s"the $what is annotated `@inline`"
+ } else null
+ Some(requestIfCanInline(callsite, reason))
+ }
else None
case "default" =>
@@ -108,7 +139,30 @@ class InlinerHeuristics[BT <: BTypes](val bTypes: BT) {
def shouldInlineHO = callee.samParamTypes.nonEmpty && (callee.samParamTypes exists {
case (index, _) => callsite.argInfos.contains(index)
})
- if (callee.annotatedInline || callsite.annotatedInline || shouldInlineHO) Some(requestIfCanInline(callsite))
+ if (callee.annotatedInline || callsite.annotatedInline || shouldInlineHO) {
+ val reason = if (compilerSettings.YoptLogInline.isSetByUser) {
+ if (callee.annotatedInline || callsite.annotatedInline) {
+ val what = if (callee.safeToInline) "callee" else "callsite"
+ s"the $what is annotated `@inline`"
+ } else {
+ val paramNames = Option(callee.callee.parameters).map(_.asScala.map(_.name).toVector)
+ def param(i: Int) = {
+ def syn = s"<param $i>"
+ paramNames.fold(syn)(v => v.applyOrElse(i, (_: Int) => syn))
+ }
+ def samInfo(i: Int, sam: String, arg: String) = s"the argument for parameter (${param(i)}: $sam) is a $arg"
+ val argInfos = for ((i, sam) <- callee.samParamTypes; info <- callsite.argInfos.get(i)) yield {
+ val argKind = info match {
+ case FunctionLiteral => "function literal"
+ case ForwardedParam(_) => "parameter of the callsite method"
+ }
+ samInfo(i, sam.internalName.split('/').last, argKind)
+ }
+ s"the callee is a higher-order method, ${argInfos.mkString(", ")}"
+ }
+ } else null
+ Some(requestIfCanInline(callsite, reason))
+ }
else None
} else None
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala
index 4e1349257e..447ee209b5 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala
@@ -191,7 +191,7 @@ class LocalOpt[BT <: BTypes](val btypes: BT) {
* @return `true` if unreachable code was eliminated in some method, `false` otherwise.
*/
def methodOptimizations(clazz: ClassNode): Boolean = {
- !compilerSettings.YoptNone && clazz.methods.asScala.foldLeft(false) {
+ !compilerSettings.optNone && clazz.methods.asScala.foldLeft(false) {
case (changed, method) => methodOptimizations(method, clazz.name) || changed
}
}
@@ -231,7 +231,8 @@ class LocalOpt[BT <: BTypes](val btypes: BT) {
// for local variables in dead blocks. Maybe that's a bug in the ASM framework.
var currentTrace: String = null
- val doTrace = compilerSettings.YoptTrace.isSetByUser && compilerSettings.YoptTrace.value == ownerClassName + "." + method.name
+ val methodPrefix = {val p = compilerSettings.YoptTrace.value; if (p == "_") "" else p }
+ val doTrace = compilerSettings.YoptTrace.isSetByUser && s"$ownerClassName.${method.name}".startsWith(methodPrefix)
def traceIfChanged(optName: String): Unit = if (doTrace) {
val after = AsmUtils.textify(method)
if (currentTrace != after) {
@@ -261,46 +262,46 @@ class LocalOpt[BT <: BTypes](val btypes: BT) {
traceIfChanged("beforeMethodOpt")
// NULLNESS OPTIMIZATIONS
- val runNullness = compilerSettings.YoptNullnessTracking && requestNullness
+ val runNullness = compilerSettings.optNullnessTracking && requestNullness
val nullnessOptChanged = runNullness && nullnessOptimizations(method, ownerClassName)
traceIfChanged("nullness")
// UNREACHABLE CODE
// Both AliasingAnalyzer (used in copyProp) and ProdConsAnalyzer (used in eliminateStaleStores,
// boxUnboxElimination) require not having unreachable instructions (null frames).
- val runDCE = (compilerSettings.YoptUnreachableCode && (requestDCE || nullnessOptChanged)) ||
- compilerSettings.YoptBoxUnbox ||
- compilerSettings.YoptCopyPropagation
+ val runDCE = (compilerSettings.optUnreachableCode && (requestDCE || nullnessOptChanged)) ||
+ compilerSettings.optBoxUnbox ||
+ compilerSettings.optCopyPropagation
val (codeRemoved, liveLabels) = if (runDCE) removeUnreachableCodeImpl(method, ownerClassName) else (false, Set.empty[LabelNode])
traceIfChanged("dce")
// BOX-UNBOX
- val runBoxUnbox = compilerSettings.YoptBoxUnbox && (requestBoxUnbox || nullnessOptChanged)
+ val runBoxUnbox = compilerSettings.optBoxUnbox && (requestBoxUnbox || nullnessOptChanged)
val boxUnboxChanged = runBoxUnbox && boxUnboxElimination(method, ownerClassName)
traceIfChanged("boxUnbox")
// COPY PROPAGATION
- val runCopyProp = compilerSettings.YoptCopyPropagation && (firstIteration || boxUnboxChanged)
+ val runCopyProp = compilerSettings.optCopyPropagation && (firstIteration || boxUnboxChanged)
val copyPropChanged = runCopyProp && copyPropagation(method, ownerClassName)
traceIfChanged("copyProp")
// STALE STORES
- val runStaleStores = compilerSettings.YoptCopyPropagation && (requestStaleStores || nullnessOptChanged || codeRemoved || boxUnboxChanged || copyPropChanged)
+ val runStaleStores = compilerSettings.optCopyPropagation && (requestStaleStores || nullnessOptChanged || codeRemoved || boxUnboxChanged || copyPropChanged)
val storesRemoved = runStaleStores && eliminateStaleStores(method, ownerClassName)
traceIfChanged("staleStores")
// REDUNDANT CASTS
- val runRedundantCasts = compilerSettings.YoptRedundantCasts && (firstIteration || boxUnboxChanged)
+ val runRedundantCasts = compilerSettings.optRedundantCasts && (firstIteration || boxUnboxChanged)
val castRemoved = runRedundantCasts && eliminateRedundantCasts(method, ownerClassName)
traceIfChanged("redundantCasts")
// PUSH-POP
- val runPushPop = compilerSettings.YoptCopyPropagation && (requestPushPop || firstIteration || storesRemoved || castRemoved)
+ val runPushPop = compilerSettings.optCopyPropagation && (requestPushPop || firstIteration || storesRemoved || castRemoved)
val pushPopRemoved = runPushPop && eliminatePushPop(method, ownerClassName)
traceIfChanged("pushPop")
// STORE-LOAD PAIRS
- val runStoreLoad = compilerSettings.YoptCopyPropagation && (requestStoreLoad || boxUnboxChanged || copyPropChanged || pushPopRemoved)
+ val runStoreLoad = compilerSettings.optCopyPropagation && (requestStoreLoad || boxUnboxChanged || copyPropChanged || pushPopRemoved)
val storeLoadRemoved = runStoreLoad && eliminateStoreLoad(method)
traceIfChanged("storeLoadPairs")
@@ -312,7 +313,7 @@ class LocalOpt[BT <: BTypes](val btypes: BT) {
// SIMPLIFY JUMPS
// almost all of the above optimizations enable simplifying more jumps, so we just run it in every iteration
- val runSimplifyJumps = compilerSettings.YoptSimplifyJumps
+ val runSimplifyJumps = compilerSettings.optSimplifyJumps
val jumpsChanged = runSimplifyJumps && simplifyJumps(method)
traceIfChanged("simplifyJumps")
@@ -358,21 +359,21 @@ class LocalOpt[BT <: BTypes](val btypes: BT) {
requestPushPop = true,
requestStoreLoad = true,
firstIteration = true)
- if (compilerSettings.YoptUnreachableCode) unreachableCodeEliminated += method
+ if (compilerSettings.optUnreachableCode) unreachableCodeEliminated += method
r
} else (false, false)
// (*) Removing stale local variable descriptors is required for correctness, see comment in `methodOptimizations`
val localsRemoved =
- if (compilerSettings.YoptCompactLocals) compactLocalVariables(method) // also removes unused
+ if (compilerSettings.optCompactLocals) compactLocalVariables(method) // also removes unused
else if (requireEliminateUnusedLocals) removeUnusedLocalVariableNodes(method)() // (*)
else false
traceIfChanged("localVariables")
- val lineNumbersRemoved = if (compilerSettings.YoptUnreachableCode) removeEmptyLineNumbers(method) else false
+ val lineNumbersRemoved = if (compilerSettings.optUnreachableCode) removeEmptyLineNumbers(method) else false
traceIfChanged("lineNumbers")
- val labelsRemoved = if (compilerSettings.YoptUnreachableCode) removeEmptyLabelNodes(method) else false
+ val labelsRemoved = if (compilerSettings.optUnreachableCode) removeEmptyLabelNodes(method) else false
traceIfChanged("labels")
// assert that local variable annotations are empty (we don't emit them) - otherwise we'd have
@@ -396,7 +397,7 @@ class LocalOpt[BT <: BTypes](val btypes: BT) {
*/
def nullnessOptimizations(method: MethodNode, ownerClassName: InternalName): Boolean = {
AsmAnalyzer.sizeOKForNullness(method) && {
- lazy val nullnessAnalyzer = new AsmAnalyzer(method, ownerClassName, new NullnessAnalyzer(btypes))
+ lazy val nullnessAnalyzer = new AsmAnalyzer(method, ownerClassName, new NullnessAnalyzer(btypes, method))
// When running nullness optimizations the method may still have unreachable code. Analyzer
// frames of unreachable instructions are `null`.