Skip to content

Commit

Permalink
Add -opt:l:inline and -opt-inline-from, deprecate -opt:l:classpath
Browse files Browse the repository at this point in the history
Introduce the optimizer level `-opt:l:inline` and a new setting
`-opt-inline-from` to control what classes we inline from.

`-opt:l:classpath` and `-opt:l:project` continue to work in the same
way, with a deprecation warning.
  • Loading branch information
lrytz committed Jun 27, 2017
1 parent c616fcb commit f340924
Show file tree
Hide file tree
Showing 53 changed files with 128 additions and 86 deletions.
21 changes: 13 additions & 8 deletions project/ScriptCommands.scala
Expand Up @@ -20,7 +20,7 @@ object ScriptCommands {
) ++ (args match {
case Seq(url) => publishTarget(url)
case Nil => Nil
}) ++ noDocs ++ enableOptimizer
}) ++ noDocs ++ enableOptimizerOldFlag
}

/** Set up the environment for `validate/test`.
Expand All @@ -31,7 +31,7 @@ object ScriptCommands {
) ++ (args match {
case Seq(url) => Seq(resolvers in Global += "scala-pr" at url)
case Nil => Nil
}) ++ enableOptimizer
}) ++ enableOptimizerNewFlags
}

/** Set up the environment for building STARR in `validate/bootstrap`. The arguments are:
Expand All @@ -41,7 +41,7 @@ object ScriptCommands {
Seq(
baseVersion in Global := ver,
baseVersionSuffix in Global := "SPLIT"
) ++ publishTarget(url) ++ noDocs ++ enableOptimizer
) ++ publishTarget(url) ++ noDocs ++ enableOptimizerOldFlag
}

/** Set up the environment for building locker in `validate/bootstrap`. The arguments are:
Expand All @@ -52,7 +52,7 @@ object ScriptCommands {
baseVersion in Global := ver,
baseVersionSuffix in Global := "SPLIT",
resolvers in Global += "scala-pr" at url
) ++ publishTarget(url) ++ noDocs ++ enableOptimizer
) ++ publishTarget(url) ++ noDocs ++ enableOptimizerOldFlag
}

/** Set up the environment for building quick in `validate/bootstrap`. The arguments are:
Expand All @@ -64,7 +64,7 @@ object ScriptCommands {
baseVersionSuffix in Global := "SPLIT",
resolvers in Global += "scala-pr" at url,
testOptions in IntegrationTest in LocalProject("test") ++= Seq(Tests.Argument("--show-log"), Tests.Argument("--show-diff"))
) ++ publishTarget(url) ++ enableOptimizer
) ++ publishTarget(url) ++ enableOptimizerNewFlags
}

/** Set up the environment for publishing in `validate/bootstrap`. The arguments are:
Expand All @@ -81,7 +81,7 @@ object ScriptCommands {
publishTo in Global := Some("sonatype-releases" at "https://oss.sonatype.org/service/local/staging/deploy/maven2"),
credentials in Global += Credentials(Path.userHome / ".credentials-sonatype"),
pgpPassphrase in Global := Some(Array.empty)
) ++ enableOptimizer
) ++ enableOptimizerNewFlags
}

private[this] def setup(name: String)(f: Seq[String] => Seq[Setting[_]]) =
Expand All @@ -92,8 +92,13 @@ object ScriptCommands {
logLevel in update in ThisBuild := Level.Warn
)

private[this] val enableOptimizer = Seq(
scalacOptions in Compile in ThisBuild += "-opt:l:classpath"
// TODO: remove this once the STARR accepts the new flags
private[this] val enableOptimizerOldFlag = Seq(
scalacOptions in Compile in ThisBuild ++= Seq("-opt:l:classpath")
)

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

private[this] val noDocs = Seq(
Expand Down
6 changes: 3 additions & 3 deletions src/compiler/scala/tools/nsc/Global.scala
Expand Up @@ -1295,7 +1295,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
unitbuf += unit
compiledFiles += unit.source.file.path
}
private def warnDeprecatedAndConflictingSettings(unit: CompilationUnit) {
private def warnDeprecatedAndConflictingSettings() {
// issue warnings for any usage of deprecated settings
settings.userSetSettings filter (_.isDeprecated) foreach { s =>
currentRun.reporting.deprecationWarning(NoPosition, s.name + " is deprecated: " + s.deprecationMessage.get, "")
Expand Down Expand Up @@ -1396,7 +1396,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
def compileSources(sources: List[SourceFile]) = if (!reporter.hasErrors) {

def checkDeprecations() = {
warnDeprecatedAndConflictingSettings(newCompilationUnit(""))
warnDeprecatedAndConflictingSettings()
reporting.summarizeErrors()
}

Expand All @@ -1418,7 +1418,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
val startTime = currentTime

reporter.reset()
warnDeprecatedAndConflictingSettings(unitbuf.head)
warnDeprecatedAndConflictingSettings()
globalPhase = fromPhase

while (globalPhase.hasNext && !reporter.hasErrors) {
Expand Down
Expand Up @@ -387,7 +387,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
calleeInfoWarning: Option[CalleeInfoWarning]) {
override def toString = s"Callee($calleeDeclarationClass.${callee.name})"

def canInlineFromSource = inlinerHeuristics.canInlineFromSource(sourceFilePath)
def canInlineFromSource = inlinerHeuristics.canInlineFromSource(sourceFilePath, calleeDeclarationClass.internalName)
def isAbstract = isAbstractMethod(callee)
def isSpecialMethod = isConstructor(callee) || isNativeMethod(callee) || hasCallerSensitiveAnnotation(callee)

Expand Down
Expand Up @@ -15,18 +15,23 @@ import scala.tools.asm.Opcodes
import scala.tools.asm.tree.{AbstractInsnNode, MethodInsnNode, MethodNode}
import scala.tools.nsc.backend.jvm.BTypes.InternalName
import scala.tools.nsc.backend.jvm.BackendReporting.{CalleeNotFinal, OptimizerWarning}
import scala.collection.mutable
import scala.tools.nsc.backend.jvm.opt.InlinerHeuristics.InlineSourceMatcher

class InlinerHeuristics[BT <: BTypes](val bTypes: BT) {
import bTypes._
import callGraph._

val inlineSourceMatcher = new InlineSourceMatcher(compilerSettings.optInlineFrom.value)

final 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
def canInlineFromSource(sourceFilePath: Option[String], calleeDeclarationClass: InternalName) = {
compilerSettings.optLClasspath || (compilerSettings.optLProject && sourceFilePath.isDefined) ||
inlineSourceMatcher.allow(calleeDeclarationClass)
}

/**
* Select callsites from the call graph that should be inlined, grouped by the containing method.
Expand Down
68 changes: 50 additions & 18 deletions src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
Expand Up @@ -234,23 +234,36 @@ trait ScalaSettings extends AbsScalaSettings
val boxUnbox = Choice("box-unbox", "Eliminate box-unbox pairs within the same method (also tuples, xRefs, value class instances). Enables unreachable-code.")
val nullnessTracking = Choice("nullness-tracking", "Track nullness / non-nullness of local variables and apply optimizations.")
val closureInvocations = Choice("closure-invocations" , "Rewrite closure invocations to the implementation method.")
val inlineProject = Choice("inline-project", "Inline only methods defined in the files being compiled. Enables unreachable-code.")
val inlineGlobal = Choice("inline-global", "Inline methods from any source, including classfiles on the compile classpath. Enables unreachable-code.")
val inline = Choice("inline", "Inline method invocations according to -Yopt-inline-heuristics and -opt-inlnie-from.")

// note: unlike the other optimizer levels, "l:none" appears up in the `opt.value` set because it's not an expanding option (expandsTo is empty)
val lNone = Choice("l:none", "Disable optimizations. Takes precedence: `-opt:l:none,+box-unbox` / `-opt:l:none -opt:box-unbox` don't enable box-unbox.")
val lNone = Choice("l:none",
"Disable optimizations. Takes precedence: `-opt:l:none,+box-unbox` / `-opt:l:none -opt:box-unbox` don't enable box-unbox.")

private val defaultChoices = List(unreachableCode)
val lDefault = Choice("l:default", "Enable default optimizations: "+ defaultChoices.mkString("", ",", "."), expandsTo = defaultChoices)
val lDefault = Choice(
"l:default",
"Enable default optimizations: " + defaultChoices.mkString("", ",", "."),
expandsTo = defaultChoices)

private val methodChoices = List(unreachableCode, simplifyJumps, compactLocals, copyPropagation, redundantCasts, boxUnbox, nullnessTracking, closureInvocations)
val lMethod = Choice("l:method", "Enable intra-method optimizations: "+ methodChoices.mkString("", ",", "."), expandsTo = methodChoices)

private val projectChoices = List(lMethod, inlineProject)
val lProject = Choice("l:project", "Enable cross-method optimizations within the current project: "+ projectChoices.mkString("", ",", "."), expandsTo = projectChoices)

private val classpathChoices = List(lProject, inlineGlobal)
val lClasspath = Choice("l:classpath", "Enable cross-method optimizations across the entire classpath: "+ classpathChoices.mkString("", ",", "."), expandsTo = classpathChoices)
val lMethod = Choice(
"l:method",
"Enable intra-method optimizations: " + methodChoices.mkString("", ",", "."),
expandsTo = methodChoices)

private val inlineChoices = List(lMethod, inline)
val lInline = Choice("l:inline",
"Enable cross-method optimizations: " + inlineChoices.mkString("", ",", "."),
expandsTo = inlineChoices)

val lProject = Choice(
"l:project",
"[deprecated, use -opt:l:inline, -opt-inlnie-from] Enable cross-method optimizations within the current project.")

val lClasspath = Choice(
"l:classpath",
"[deprecated, use -opt:l:inline, -opt-inlnie-from] Enable cross-method optimizations across the entire classpath.")
}

// We don't use the `default` parameter of `MultiChoiceSetting`: it specifies the default values
Expand All @@ -260,7 +273,11 @@ trait ScalaSettings extends AbsScalaSettings
name = "-opt",
helpArg = "optimization",
descr = "Enable optimizations",
domain = optChoices)
domain = optChoices).withPostSetHook(s => {
import optChoices._
if (!s.value.contains(inline) && (s.value.contains(lProject) || s.value.contains(lClasspath)))
s.enable(lInline)
})

private def optEnabled(choice: optChoices.Choice) = {
!opt.contains(optChoices.lNone) && {
Expand All @@ -278,14 +295,21 @@ trait ScalaSettings extends AbsScalaSettings
def optBoxUnbox = optEnabled(optChoices.boxUnbox)
def optNullnessTracking = optEnabled(optChoices.nullnessTracking)
def optClosureInvocations = optEnabled(optChoices.closureInvocations)
def optInlinerEnabled = optEnabled(optChoices.inline)

def optInlineProject = optEnabled(optChoices.inlineProject)
def optInlineGlobal = optEnabled(optChoices.inlineGlobal)
def optInlinerEnabled = optInlineProject || optInlineGlobal
// deprecated inliner levels
def optLProject = optEnabled(optChoices.lProject)
def optLClasspath = optEnabled(optChoices.lClasspath)

def optBuildCallGraph = optInlinerEnabled || optClosureInvocations
def optAddToBytecodeRepository = optBuildCallGraph || optInlinerEnabled || optClosureInvocations

val optInlineFrom = StringSetting(
"-opt-inline-from",
"patterns",
"Classfile name patterns from which to allow inlining. ** = anything, * = package or class name, ! to exclude. Example: scala.**:!scala.Predef$:corp.*.util.*:corp.**.*Util*",
"")

val YoptInlineHeuristics = ChoiceSetting(
name = "-Yopt-inline-heuristics",
helpArg = "strategy",
Expand Down Expand Up @@ -360,8 +384,11 @@ trait ScalaSettings extends AbsScalaSettings
val future = BooleanSetting("-Xfuture", "Turn on future language features.") enablingIfNotSetByUser futureSettings
val optimise = BooleanSetting("-optimise", "Compiler flag for the optimizer in Scala 2.11")
.withAbbreviation("-optimize")
.withDeprecationMessage("In 2.12, -optimise enables -opt:l:classpath. Check -opt:help for using the Scala 2.12 optimizer.")
.withPostSetHook(_ => opt.tryToSet(List(optChoices.lClasspath.name)))
.withDeprecationMessage("In 2.12, -optimise enables -opt:l:inline -opt-inline-from **. Check -opt:help for using the Scala 2.12 optimizer.")
.withPostSetHook(_ => {
opt.enable(optChoices.lInline)
optInlineFrom.value = "**"
})
val Xexperimental = BooleanSetting("-Xexperimental", "Enable experimental extensions.") enablingIfNotSetByUser experimentalSettings

// Feature extensions
Expand Down Expand Up @@ -405,6 +432,11 @@ trait ScalaSettings extends AbsScalaSettings
}
*/

None
if (opt.value.contains(optChoices.lProject))
Some("-opt:l:project is deprecated, use -opt:l:inline and -opt-inlnie-from")
else if (opt.value.contains(optChoices.lClasspath))
Some("-opt:l:classpath is deprecated, use -opt:l:inline and -opt-inlnie-from")
else
None
}
}
2 changes: 1 addition & 1 deletion test/benchmarks/build.sbt
@@ -1,6 +1,6 @@
scalaHome := Some(file("../../build/pack"))
scalaVersion := "2.12.1-dev"
scalacOptions ++= Seq("-feature", "-opt:l:classpath")
scalacOptions ++= Seq("-feature", "-opt:l:inline", "-opt-inline-from", "**")

lazy val root = (project in file(".")).
enablePlugins(JmhPlugin).
Expand Down
2 changes: 1 addition & 1 deletion test/files/instrumented/inline-in-constructors.flags
@@ -1 +1 @@
-opt:l:classpath
-opt:l:inline -opt-inline-from **
2 changes: 1 addition & 1 deletion test/files/neg/inlineIndyLambdaPrivate.flags
@@ -1 +1 @@
-opt:l:classpath -Yopt-inline-heuristics:everything -opt-warnings:_ -Xfatal-warnings
-opt:l:inline -opt-inline-from ** -Yopt-inline-heuristics:everything -opt-warnings:_ -Xfatal-warnings
2 changes: 1 addition & 1 deletion test/files/neg/inlineMaxSize.flags
@@ -1 +1 @@
-Ydelambdafy:method -opt:l:classpath -opt-warnings -Xfatal-warnings
-Ydelambdafy:method -opt:l:inline -opt-inline-from ** -opt-warnings -Xfatal-warnings
2 changes: 1 addition & 1 deletion test/files/neg/optimiseDeprecated.check
@@ -1,4 +1,4 @@
warning: -optimise is deprecated: In 2.12, -optimise enables -opt:l:classpath. Check -opt:help for using the Scala 2.12 optimizer.
warning: -optimise is deprecated: In 2.12, -optimise enables -opt:l:inline -opt-inline-from **. Check -opt:help for using the Scala 2.12 optimizer.
error: No warnings can be incurred under -Xfatal-warnings.
one warning found
one error found
2 changes: 1 addition & 1 deletion test/files/neg/sealed-final-neg.flags
@@ -1 +1 @@
-Xfatal-warnings -opt:l:project -opt-warnings
-Xfatal-warnings -opt:l:inline -opt-inline-from ** -opt-warnings
2 changes: 1 addition & 1 deletion test/files/pos/inline-access-levels.flags
@@ -1 +1 @@
-opt:l:classpath -Xfatal-warnings -opt-warnings
-opt:l:inline -opt-inline-from ** -Xfatal-warnings -opt-warnings
2 changes: 1 addition & 1 deletion test/files/pos/t3234.flags
@@ -1 +1 @@
-opt:l:project -opt-warnings -Xfatal-warnings
-opt:l:inline -opt-inline-from ** -opt-warnings -Xfatal-warnings
2 changes: 1 addition & 1 deletion test/files/pos/t3420.flags
@@ -1 +1 @@
-opt-warnings -opt:l:classpath -Xfatal-warnings
-opt-warnings -opt:l:inline -opt-inline-from ** -Xfatal-warnings
2 changes: 1 addition & 1 deletion test/files/pos/t4840.flags
@@ -1 +1 @@
-opt:l:classpath
-opt:l:inline -opt-inline-from **
2 changes: 1 addition & 1 deletion test/files/pos/t8410.flags
@@ -1 +1 @@
-opt:l:project -Xfatal-warnings -deprecation:false -opt-warnings:none
-opt:l:inline -opt-inline-from ** -Xfatal-warnings -deprecation:false -opt-warnings:none
2 changes: 1 addition & 1 deletion test/files/pos/t9111-inliner-workaround.flags
@@ -1 +1 @@
-opt:l:classpath
-opt:l:inline -opt-inline-from **
2 changes: 1 addition & 1 deletion test/files/run/bcodeInlinerMixed.flags
@@ -1 +1 @@
-opt:l:classpath
-opt:l:inline -opt-inline-from **
2 changes: 1 addition & 1 deletion test/files/run/classfile-format-51.scala
Expand Up @@ -16,7 +16,7 @@ import Opcodes._
// verify. So the test includes a version check that short-circuits the whole test
// on JDK 6
object Test extends DirectTest {
override def extraSettings: String = "-opt:l:classpath -usejavacp -d " + testOutput.path + " -cp " + testOutput.path
override def extraSettings: String = "-opt:l:inline -opt-inline-from ** -usejavacp -d " + testOutput.path + " -cp " + testOutput.path

def generateClass() {
val invokerClassName = "DynamicInvoker"
Expand Down
2 changes: 1 addition & 1 deletion test/files/run/classfile-format-52.scala
Expand Up @@ -13,7 +13,7 @@ import Opcodes._
// By its nature the test can only work on JDK 8+ because under JDK 7- the
// interface won't verify.
object Test extends DirectTest {
override def extraSettings: String = "-opt:l:classpath -usejavacp -d " + testOutput.path + " -cp " + testOutput.path
override def extraSettings: String = "-opt:l:inline -opt-inline-from ** -usejavacp -d " + testOutput.path + " -cp " + testOutput.path

def generateInterface() {
val interfaceName = "HasDefaultMethod"
Expand Down
2 changes: 1 addition & 1 deletion test/files/run/finalvar.flags
@@ -1 +1 @@
-Yoverride-vars -opt:l:project
-Yoverride-vars -opt:l:inline -opt-inline-from **
2 changes: 1 addition & 1 deletion test/files/run/icode-reader-dead-code.scala
Expand Up @@ -36,7 +36,7 @@ object Test extends DirectTest {

// If inlining fails, the compiler will issue an inliner warning that is not present in the
// check file
compileString(newCompiler("-usejavacp", "-opt:l:classpath"))(bCode)
compileString(newCompiler("-usejavacp", "-opt:l:inline", "-opt-inline-from", "**"))(bCode)
}

def readClass(file: String) = {
Expand Down
2 changes: 1 addition & 1 deletion test/files/run/noInlineUnknownIndy/Test.scala
Expand Up @@ -11,7 +11,7 @@ object Test extends DirectTest {

def compileCode(code: String) = {
val classpath = List(sys.props("partest.lib"), testOutput.path) mkString sys.props("path.separator")
compileString(newCompiler("-cp", classpath, "-d", testOutput.path, "-opt:l:classpath", "-Yopt-inline-heuristics:everything", "-opt-warnings:_"))(code)
compileString(newCompiler("-cp", classpath, "-d", testOutput.path, "-opt:l:inline", "-opt-inline-from", "**", "-Yopt-inline-heuristics:everything", "-opt-warnings:_"))(code)
}

def show(): Unit = {
Expand Down
2 changes: 1 addition & 1 deletion test/files/run/repl-inline.scala
Expand Up @@ -15,7 +15,7 @@ assert(h == "h", h)
def main(args: Array[String]) {
def test(f: Settings => Unit): Unit = {
val settings = new Settings()
settings.processArgumentString("-opt:l:classpath")
settings.processArgumentString("-opt:l:inline -opt-inline-from **")
f(settings)
settings.usejavacp.value = true
val repl = new interpreter.IMain(settings)
Expand Down
2 changes: 1 addition & 1 deletion test/files/run/synchronized.flags
@@ -1 +1 @@
-opt:l:project
-opt:l:inline -opt-inline-from **
2 changes: 1 addition & 1 deletion test/files/run/t2106.flags
@@ -1 +1 @@
-opt-warnings -opt:l:classpath
-opt-warnings -opt:l:inline -opt-inline-from **
2 changes: 1 addition & 1 deletion test/files/run/t3509.flags
@@ -1 +1 @@
-opt:l:classpath
-opt:l:inline -opt-inline-from **
2 changes: 1 addition & 1 deletion test/files/run/t3569.flags
@@ -1 +1 @@
-opt:l:classpath
-opt:l:inline -opt-inline-from **
2 changes: 1 addition & 1 deletion test/files/run/t4285.flags
@@ -1 +1 @@
-opt:l:classpath
-opt:l:inline -opt-inline-from **
2 changes: 1 addition & 1 deletion test/files/run/t4935.flags
@@ -1 +1 @@
-opt:l:classpath
-opt:l:inline -opt-inline-from **
2 changes: 1 addition & 1 deletion test/files/run/t5789.scala
Expand Up @@ -5,7 +5,7 @@ import scala.tools.partest.ReplTest


object Test extends ReplTest {
override def extraSettings = "-opt:l:classpath"
override def extraSettings = "-opt:l:inline -opt-inline-from **"
def code = """
val n = 2
() => n
Expand Down
2 changes: 1 addition & 1 deletion test/files/run/t6102.flags
@@ -1 +1 @@
-opt:l:classpath -Xfatal-warnings
-opt:l:inline -opt-inline-from ** -Xfatal-warnings
2 changes: 1 addition & 1 deletion test/files/run/t6188.flags
@@ -1 +1 @@
-opt:l:classpath
-opt:l:inline -opt-inline-from **
2 changes: 1 addition & 1 deletion test/files/run/t7459b-optimize.flags
@@ -1 +1 @@
-opt:l:classpath
-opt:l:inline -opt-inline-from **

0 comments on commit f340924

Please sign in to comment.