Skip to content

[WIP] Emit detailed compiler trace under -Yprofile #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 10 commits into from
Closed
4 changes: 2 additions & 2 deletions project/ScriptCommands.scala
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,11 @@ object ScriptCommands {
Project.setProject(session, newStructure, state)
}

private[this] val enableOptimizer = Seq(
val enableOptimizer = Seq(
scalacOptions in Compile in ThisBuild ++= Seq("-opt:l:inline", "-opt-inline-from:scala/**")
)

private[this] val noDocs = Seq(
val noDocs = Seq(
publishArtifact in (Compile, packageDoc) in ThisBuild := false
)

Expand Down
7 changes: 6 additions & 1 deletion src/compiler/scala/tools/nsc/Global.scala
Original file line number Diff line number Diff line change
Expand Up @@ -438,8 +438,10 @@ class Global(var currentSettings: Settings, reporter0: Reporter)
currentRun.informUnitStarting(this, unit)
val unit0 = currentUnit
currentRun.currentUnit = unit
currentRun.profiler.beforeUnit(phase, unit.source.file)
try apply(unit)
finally {
currentRun.profiler.afterUnit(phase, unit.source.file)
currentRun.currentUnit = unit0
currentRun.advanceUnit()
}
Expand Down Expand Up @@ -1100,6 +1102,9 @@ class Global(var currentSettings: Settings, reporter0: Reporter)

def newJavaUnitParser(unit: CompilationUnit): JavaUnitParser = new JavaUnitParser(unit)

override protected[scala] def currentRunProfilerBeforeCompletion(root: Symbol, associatedFile: AbstractFile): Unit = currentRun.profiler.beforeCompletion(root, associatedFile)
override protected[scala] def currentRunProfilerAfterCompletion(root: Symbol, associatedFile: AbstractFile): Unit = currentRun.profiler.afterCompletion(root, associatedFile)

/** A Run is a single execution of the compiler on a set of units.
*/
class Run extends RunContextApi with RunReporting with RunParsing {
Expand Down Expand Up @@ -1448,7 +1453,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter)
private final val GlobalPhaseName = "global (synthetic)"
protected final val totalCompileTime = statistics.newTimer("#total compile time", GlobalPhaseName)

def compileUnits(units: List[CompilationUnit], fromPhase: Phase): Unit = compileUnitsInternal(units,fromPhase)
def compileUnits(units: List[CompilationUnit], fromPhase: Phase): Unit = compileUnitsInternal(units,fromPhase)
private def compileUnitsInternal(units: List[CompilationUnit], fromPhase: Phase) {
units foreach addUnit
reporter.reset()
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/ast/TreeGen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
})
val selfParam = ValDef(selfParamSym)
val rhs = orig.rhs.substituteThis(newSym.owner, gen.mkAttributedIdent(selfParamSym)) // scala/scala-dev#186 intentionally leaving Ident($this) is unpositioned
.substituteSymbols(origParams, newSym.info.params.drop(1)).changeOwner(origSym -> newSym)
.substituteSymbols(origParams, newSym.info.params.drop(1)).changeOwner(origSym, newSym)
treeCopy.DefDef(orig, orig.mods, orig.name, orig.tparams, (selfParam :: orig.vparamss.head) :: Nil, orig.tpt, rhs).setSymbol(newSym)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -932,7 +932,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
}

def genLoadArguments(args: List[Tree], btpes: List[BType]) {
(args zip btpes) foreach { case (arg, btpe) => genLoad(arg, btpe) }
foreach2(args, btpes) { case (arg, btpe) => genLoad(arg, btpe) }
}

def genLoadModule(tree: Tree): BType = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -789,7 +789,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic {
if (needsAnnotation) {
val c = Constant(definitions.RemoteExceptionClass.tpe)
val arg = Literal(c) setType c.tpe
meth.addAnnotation(appliedType(definitions.ThrowsClass, c.tpe), arg)
meth.addAnnotation(appliedType(definitions.ThrowsClass, c.tpe :: Nil), arg)
}
}

Expand Down
39 changes: 24 additions & 15 deletions src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
package scala.tools.nsc
package backend.jvm

import scala.collection.{concurrent, mutable}
import java.{util => ju}
import scala.collection.concurrent
import scala.tools.asm
import scala.tools.asm.Opcodes
import scala.tools.nsc.backend.jvm.BTypes.{InlineInfo, InternalName}
Expand All @@ -23,7 +24,7 @@ import scala.tools.nsc.backend.jvm.opt._
*/
abstract class BTypes {
val frontendAccess: PostProcessorFrontendAccess
import frontendAccess.{frontendSynch, recordPerRunCache}
import frontendAccess.{frontendSynch, recordPerRunJavaMapCache}

val coreBTypes: CoreBTypes { val bTypes: BTypes.this.type }
import coreBTypes._
Expand All @@ -35,13 +36,15 @@ abstract class BTypes {
* `getCommonSuperClass`. In this method we need to obtain the ClassBType for a given internal
* name. The method assumes that every class type that appears in the bytecode exists in the map
*/
def cachedClassBType(internalName: InternalName): Option[ClassBType] =
// OPT: not returning Option[ClassBType] because the Some allocation shows up as a hotspot
def cachedClassBType(internalName: InternalName): ClassBType =
classBTypeCache.get(internalName)

// Concurrent maps because stack map frames are computed when in the class writer, which
// might run on multiple classes concurrently.
// Note usage should be private to this file, except for tests
val classBTypeCache: concurrent.Map[InternalName, ClassBType] = recordPerRunCache(FlatConcurrentHashMap.empty)
val classBTypeCache: ju.concurrent.ConcurrentHashMap[InternalName, ClassBType] =
recordPerRunJavaMapCache(new ju.concurrent.ConcurrentHashMap[InternalName, ClassBType])

/**
* A BType is either a primitive type, a ClassBType, an ArrayBType of one of these, or a MethodType
Expand Down Expand Up @@ -809,17 +812,23 @@ abstract class BTypes {
def unapply(cr:ClassBType) = Some(cr.internalName)

def apply(internalName: InternalName, fromSymbol: Boolean)(init: (ClassBType) => Either[NoClassBTypeInfo, ClassInfo]) = {
val newRes = if (fromSymbol) new ClassBTypeFromSymbol(internalName) else new ClassBTypeFromClassfile(internalName)
// synchronized s required to ensure proper initialisation if info.
// see comment on def info
newRes.synchronized {
classBTypeCache.putIfAbsent(internalName, newRes) match {
case None =>
newRes._info = init(newRes)
newRes.checkInfoConsistency()
newRes
case Some(old) =>
old
val cached = classBTypeCache.get(internalName)
if (cached ne null) cached
else {
val newRes =
if (fromSymbol) new ClassBTypeFromSymbol(internalName)
else new ClassBTypeFromClassfile(internalName)
// synchronized is required to ensure proper initialisation of info.
// see comment on def info
newRes.synchronized {
classBTypeCache.putIfAbsent(internalName, newRes) match {
case null =>
newRes._info = init(newRes)
newRes.checkInfoConsistency()
newRes
case old =>
old
}
}
}
}
Expand Down
16 changes: 6 additions & 10 deletions src/compiler/scala/tools/nsc/backend/jvm/BTypesFromClassfile.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,10 @@ abstract class BTypesFromClassfile {
* be found in the `byteCodeRepository`, the `info` of the resulting ClassBType is undefined.
*/
def classBTypeFromParsedClassfile(internalName: InternalName): ClassBType = {
cachedClassBType(internalName).getOrElse{
ClassBType(internalName, false){ res:ClassBType =>
byteCodeRepository.classNode(internalName) match {
case Left(msg) => Left(NoClassBTypeInfoMissingBytecode(msg))
case Right(c) => computeClassInfoFromClassNode(c, res)
}
ClassBType(internalName, fromSymbol = false) { res: ClassBType =>
byteCodeRepository.classNode(internalName) match {
case Left(msg) => Left(NoClassBTypeInfoMissingBytecode(msg))
case Right(c) => computeClassInfoFromClassNode(c, res)
}
}
}
Expand All @@ -60,10 +58,8 @@ abstract class BTypesFromClassfile {
* Construct the [[ClassBType]] for a parsed classfile.
*/
def classBTypeFromClassNode(classNode: ClassNode): ClassBType = {
cachedClassBType(classNode.name).getOrElse {
ClassBType(classNode.name, false) { res: ClassBType =>
computeClassInfoFromClassNode(classNode, res)
}
ClassBType(classNode.name, fromSymbol = false) { res: ClassBType =>
computeClassInfoFromClassNode(classNode, res)
}
}

Expand Down
59 changes: 24 additions & 35 deletions src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -93,19 +93,12 @@ abstract class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
else if (classSym == NullClass) srNullRef
else {
val internalName = classSym.javaBinaryNameString
cachedClassBType(internalName) match {
case Some(bType) =>
if (currentRun.compiles(classSym))
assert(bType fromSymbol, s"ClassBType for class being compiled was already created from a classfile: ${classSym.fullName}")
bType
case None =>
// The new ClassBType is added to the map via its apply, before we set its info. This
// allows initializing cyclic dependencies, see the comment on variable ClassBType._info.
ClassBType(internalName, true) { res:ClassBType =>
if (completeSilentlyAndCheckErroneous(classSym))
Left(NoClassBTypeInfoClassSymbolInfoFailedSI9111(classSym.fullName))
else computeClassInfo(classSym, res)
}
// The new ClassBType is added to the map via its apply, before we set its info. This
// allows initializing cyclic dependencies, see the comment on variable ClassBType._info.
ClassBType(internalName, fromSymbol = true) { res:ClassBType =>
if (completeSilentlyAndCheckErroneous(classSym))
Left(NoClassBTypeInfoClassSymbolInfoFailedSI9111(classSym.fullName))
else computeClassInfo(classSym, res)
}
}
}
Expand Down Expand Up @@ -623,33 +616,29 @@ abstract class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
def mirrorClassClassBType(moduleClassSym: Symbol): ClassBType = {
assert(isTopLevelModuleClass(moduleClassSym), s"not a top-level module class: $moduleClassSym")
val internalName = moduleClassSym.javaBinaryNameString.stripSuffix(nme.MODULE_SUFFIX_STRING)
cachedClassBType(internalName).getOrElse {
ClassBType(internalName, true) { c: ClassBType =>
val shouldBeLazy = moduleClassSym.isJavaDefined || !currentRun.compiles(moduleClassSym)
val nested = Lazy.withLockOrEager(shouldBeLazy, exitingPickler(memberClassesForInnerClassTable(moduleClassSym)) map classBTypeFromSymbol)
Right(ClassInfo(
superClass = Some(ObjectRef),
interfaces = Nil,
flags = asm.Opcodes.ACC_SUPER | asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_FINAL,
nestedClasses = nested,
nestedInfo = Lazy.eagerNone,
inlineInfo = EmptyInlineInfo.copy(isEffectivelyFinal = true))) // no method inline infos needed, scala never invokes methods on the mirror class
}
ClassBType(internalName, fromSymbol = true) { c: ClassBType =>
val shouldBeLazy = moduleClassSym.isJavaDefined || !currentRun.compiles(moduleClassSym)
val nested = Lazy.withLockOrEager(shouldBeLazy, exitingPickler(memberClassesForInnerClassTable(moduleClassSym)) map classBTypeFromSymbol)
Right(ClassInfo(
superClass = Some(ObjectRef),
interfaces = Nil,
flags = asm.Opcodes.ACC_SUPER | asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_FINAL,
nestedClasses = nested,
nestedInfo = Lazy.eagerNone,
inlineInfo = EmptyInlineInfo.copy(isEffectivelyFinal = true))) // no method inline infos needed, scala never invokes methods on the mirror class
}
}

def beanInfoClassClassBType(mainClass: Symbol): ClassBType = {
val internalName = mainClass.javaBinaryNameString + "BeanInfo"
cachedClassBType(internalName).getOrElse {
ClassBType(internalName, true) { c: ClassBType =>
Right(ClassInfo(
superClass = Some(sbScalaBeanInfoRef),
interfaces = Nil,
flags = javaFlags(mainClass),
nestedClasses = Lazy.eagerNil,
nestedInfo = Lazy.eagerNone,
inlineInfo = EmptyInlineInfo))
}
ClassBType(internalName, fromSymbol = true) { c: ClassBType =>
Right(ClassInfo(
superClass = Some(sbScalaBeanInfoRef),
interfaces = Nil,
flags = javaFlags(mainClass),
nestedClasses = Lazy.eagerNil,
nestedInfo = Lazy.eagerNone,
inlineInfo = EmptyInlineInfo))
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/compiler/scala/tools/nsc/backend/jvm/PostProcessor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ abstract class PostProcessor extends PerRunInit {
*/
override def getCommonSuperClass(inameA: String, inameB: String): String = {
// All types that appear in a class node need to have their ClassBType cached, see [[cachedClassBType]].
val a = cachedClassBType(inameA).get
val b = cachedClassBType(inameB).get
val a = cachedClassBType(inameA)
val b = cachedClassBType(inameB)
val lub = a.jvmWiseLUB(b).get
val lubName = lub.internalName
assert(lubName != "scala/Any")
Expand Down
Loading