8000 By default, only run DCE on methods with an ATHROW. by retronym · Pull Request #6044 · scala/scala · GitHub
[go: up one dir, main page]

Skip to content

By default, only run DCE on methods with an ATHROW. #6044

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
Prev Previous commit
Next Next commit
Simplify backend pipeline, move code to context without Global
Removes the three work queues in the backend.

Splits up the backend in two main components
  - CodeGen, which has a Global
  - PostProcessor, which has a BTypes (but no Global)

CodeGen generates asm.ClassNodes and stores them in postProcessor.generatedClasses.
The code generator is invoketd through BCodePhase.apply.

The postProcessor then runs the optimizer, computes the InnerClass table and
adds the lambdaDeserialize method if necessary. It finally serializes the
classes into a byte array and writes them to disk.

The implementation of classfile writing still depends on Global. It is passed
in as an argument to the postProcessor. A later commit will move it to a context
without Global and make it thread-safe.
  • Loading branch information
lrytz committed Jul 28, 2017
commit dd1abd99046d94fefc6ad244d84d41eeef61cadb
76 changes: 2 additions & 74 deletions src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
8000 8000
Original file line number Diff line number Diff line change
Expand Up @@ -239,57 +239,8 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
}


/*
* must-single-thread
*/
def getFileForClassfile(base: AbstractFile, clsName: String, suffix: String): AbstractFile = {
getFile(base, clsName, suffix)
}

/*
* must-single-thread
*/
def getOutFolder(csym: Symbol, cName: String, cunit: CompilationUnit): _root_.scala.tools.nsc.io.AbstractFile =
_root_.scala.util.Try {
outputDirectory(csym)
}.recover {
case ex: Throwable =>
reporter.error(cunit.body.pos, s"Couldn't create file for class $cName\n${ex.getMessage}")
null
}.get

var pickledBytes = 0 // statistics

// -----------------------------------------------------------------------------------------
// finding the least upper bound in agreement with the bytecode verifier (given two internal names handed by ASM)
// Background:
// http://gallium.inria.fr/~xleroy/publi/bytecode-verification-JAR.pdf
// http://comments.gmane.org/gmane.comp.java.vm.languages/2293
// https://github.com/scala/bug/issues/3872
// -----------------------------------------------------------------------------------------

/* An `asm.ClassWriter` that uses `jvmWiseLUB()`
* The internal name of the least common ancestor of the types given by inameA and inameB.
* It's what ASM needs to know in order to compute stack map frames, http://asm.ow2.org/doc/developer-guide.html#controlflow
*/
final class CClassWriter(flags: Int) extends asm.ClassWriter(flags) {

/**
* This method is used by asm when computing stack map frames. It is thread-safe: it depends
* only on the BTypes component, which does not depend on global.
* TODO @lry move to a different place where no global is in scope, on bTypes.
*/
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 lub = a.jvmWiseLUB(b).get
val lubName = lub.internalName
assert(lubName != "scala/Any")
lubName // ASM caches the answer during the lifetime of a ClassWriter. We outlive that. Not sure whether caching on our side would improve things.
}
}

/*
* must-single-thread
*/
Expand Down Expand Up @@ -406,29 +357,6 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
case AnnotationInfo(_, _, (_, LiteralAnnotArg(const)) :: Nil) => const.longValue
}

/*
* Populates the InnerClasses JVM attribute with `refedInnerClasses`. See also the doc on inner
* classes in BTypes.scala.
*
* `refedInnerClasses` may contain duplicates, need not contain the enclosing inner classes of
* each inner class it lists (those are looked up and included).
*
* This method serializes in the InnerClasses JVM attribute in an appropriate order, not
* necessarily that given by `refedInnerClasses`.
*
* can-multi-thread
*/
final def addInnerClasses(jclass: asm.ClassVisitor, refedInnerClasses: List[ClassBType]) {
val allNestedClasses = refedInnerClasses.flatMap(_.enclosingNestedClassesChain.get).distinct

// sorting ensures nested classes are listed after their enclosing class thus satisfying the Eclipse Java compiler
for (nestedClass <- allNestedClasses.sortBy(_.internalName.toString)) {
// Extract the innerClassEntry - we know it exists, enclosingNestedClassesChain only returns nested classes.
val Some(e) = nestedClass.innerClassAttributeEntry.get
jclass.visitInnerClass(e.name, e.outerName, e.innerName, e.flags)
}
}

/*
* Custom attribute (JVMS 4.7.1) "ScalaSig" used as marker only
* i.e., the pickle is contained in a custom annotation, see:
Expand Down Expand Up @@ -1058,7 +986,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
val bType = mirrorClassClassBType(moduleClass)
val mirrorClass = new asm.tree.ClassNode
mirrorClass.visit(
classfileVersion,
backendUtils.classfileVersion,
bType.info.get.flags,
bType.internalName,
null /* no java-generic-signature */,
Expand Down Expand Up @@ -1102,7 +1030,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {

val beanInfoClass = new asm.tree.ClassNode
beanInfoClass.visit(
classfileVersion,
backendUtils.classfileVersion,
beanInfoType.info.get.flags,
beanInfoType.internalName,
null, // no java-generic-signature
Expand Down
12 changes: 0 additions & 12 deletions src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,6 @@ abstract class BCodeIdiomatic {
import bTypes._
import coreBTypes._

val classfileVersion: Int = settings.target.value match {
case "jvm-1.8" => asm.Opcodes.V1_8
}

val majorVersion: Int = (classfileVersion & 0xFF)
val emitStackMapFrame = (majorVersion >= 50)

val extraProc: Int = GenBCode.mkFlags(
asm.ClassWriter.COMPUTE_MAXS,
if (emitStackMapFrame) asm.ClassWriter.COMPUTE_FRAMES else 0
)

lazy val JavaStringBuilderClassName = jlStringBuilderRef.internalName

val EMPTY_STRING_ARRAY = Array.empty[String]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
val flags = javaFlags(claszSymbol)

val thisSignature = getGenericSignature(claszSymbol, claszSymbol.owner)
cnode.visit(classfileVersion, flags,
cnode.visit(backendUtils.classfileVersion, flags,
thisBType.internalName, thisSignature,
superClass, interfaceNames.toArray)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import scala.util.control.ControlThrowable
*/
sealed abstract class BackendReporting {
def inlinerWarning(pos: Position, message: String): Unit

def error(pos: Position, message: String): Unit
}

final class BackendReportingImpl(val global: Global) extends BackendReporting {
Expand All @@ -21,6 +23,8 @@ final class BackendReportingImpl(val global: Global) extends BackendReporting {
def inlinerWarning(pos: Position, message: String): Unit = {
currentRun.reporting.inlinerWarning(pos, message)
}

def error(pos: Position, message: String): Unit = reporter.error(pos, message)
}

/**
Expand Down
86 changes: 85 additions & 1 deletion src/compiler/scala/tools/nsc/backend/jvm/CodeGen.scala
9E88
Original file line number Diff line number Diff line change
@@ -1,10 +1,94 @@
package scala.tools.nsc
package backend.jvm

import scala.tools.asm.tree.ClassNode

abstract class CodeGen[G <: Global](val global: G) {
import global._
val bTypes: BTypesFromSymbols[global.type]

import global._
import bTypes._

private val caseInsensitively = perRunCaches.newMap[String, Symbol]()

private var mirrorCodeGen : CodeGenImpl.JMirrorBuilder = null
private var beanInfoCodeGen : CodeGenImpl.JBeanInfoBuilder = null

def genUnit(unit: CompilationUnit): Unit = {
import genBCode.postProcessor.generatedClasses

def genClassDef(cd: ClassDef): Unit = try {
val sym = cd.symbol
val sourceFile = unit.source.file
generatedClasses += GeneratedClass(genClass(cd, unit), sourceFile, isArtifact = false)
if (bTypes.isTopLevelModuleClass(sym)) {
if (sym.companionClass == NoSymbol)
generatedClasses += GeneratedClass(genMirrorClass(sym, unit), sourceFile, isArtifact = true)
else
log(s"No mirror class for module with linked class: ${sym.fullName}")
}
if (sym hasAnnotation coreBTypes.BeanInfoAttr)
generatedClasses += GeneratedClass(genBeanInfoClass(cd, unit), sourceFile, isArtifact = true)
} catch {
case ex: Throwable =>
ex.printStackTrace()
error(s"Error while emitting ${unit.source}\n${ex.getMessage}")
}

def genClassDefs(tree: Tree): Unit = tree match {
case EmptyTree => ()
case PackageDef(_, stats) => stats foreach genClassDefs
case cd: ClassDef => genClassDef(cd)
}

genClassDefs(unit.body)
}

def genClass(cd: ClassDef, unit: CompilationUnit): ClassNode = {
warnCaseInsensitiveOverwrite(cd)
addSbtIClassShim(cd)
val b = new CodeGenImpl.SyncAndTryBuilder(unit)
b.genPlainClass(cd)
b.cnode
}

def genMirrorClass(classSym: Symbol, unit: CompilationUnit): ClassNode = {
mirrorCodeGen.genMirrorClass(classSym, unit)
}

def genBeanInfoClass(cd: ClassDef, unit: CompilationUnit): ClassNode = {
val sym = cd.symbol
beanInfoCodeGen.genBeanInfoClass(sym, unit, CodeGenImpl.fieldSymbols(sym), CodeGenImpl.methodSymbols(cd))
}

private def warnCaseInsensitiveOverwrite(cd: ClassDef): Unit = {
val sym = cd.symbol
// GenASM checks this before classfiles are emitted, https://github.com/scala/scala/commit/e4d1d930693ac75d8eb64c2c3c69f2fc22bec739
val lowercaseJavaClassName = sym.javaClassName.toLowerCase
caseInsensitively.get(lowercaseJavaClassName) match {
case None =>
caseInsensitively.put(lowercaseJavaClassName, sym)
case Some(dupClassSym) =>
reporter.warning(
sym.pos,
s"Class ${sym.javaClassName} differs only in case from ${dupClassSym.javaClassName}. " +
"Such classes will overwrite one another on case-insensitive filesystems."
)
}
}

private def addSbtIClassShim(cd: ClassDef): Unit = {
// shim for SBT, see https://github.com/sbt/sbt/issues/2076
// TODO put this closer to classfile writing once we have closure elimination
// TODO create a nicer public API to find out the correspondence between sourcefile and ultimate classfiles
currentUnit.icode += new icodes.IClass(cd.symbol)
}

def initialize(): Unit = {
mirrorCodeGen = new CodeGenImpl.JMirrorBuilder()
beanInfoCodeGen = new CodeGenImpl.JBeanInfoBuilder()
}

object CodeGenImpl extends {
val global: CodeGen.this.global.type = CodeGen.this.global
val bTypes: CodeGen.this.bTypes.type = CodeGen.this.bTypes
Expand Down
Loading
0