summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@gmail.com>2015-10-01 17:34:44 +0200
committerLukas Rytz <lukas.rytz@gmail.com>2015-10-20 11:15:50 +0200
commitb21542b9cee7ecf2f261c9eb9286b7c61087105a (patch)
tree623a3901e3cd9414ad9ea875e2e13227a3635709 /src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala
parentc99e53e8a05fc5d45f8e8a28da68d3977be65bfa (diff)
downloadscala-b21542b9cee7ecf2f261c9eb9286b7c61087105a.tar.gz
scala-b21542b9cee7ecf2f261c9eb9286b7c61087105a.tar.bz2
scala-b21542b9cee7ecf2f261c9eb9286b7c61087105a.zip
Don't create inline requests for callsites that cannot be inlined
When traversing the call graph and collecting inline reqeusts, rule out callsites that we already know cannot be inlined. Note that we cannot perform all necessary checks already at this stage: checks that depend on the callee body (the inlined code) are deferred until the callsite is actually inlined. The reason is that the code may change. Example: @inline final def f = try 1 catch { case _: Throwable => 2 } @inline final def g = f def t = println(g) When collecting inline requests, the body of g invokes the public method f, so g could be inlined into t. However, once f is inlined into g, the body of g contains a try-catch block. Now we cannot inline g into t anymore, because the call stack at the g callsite is non-empty (the stack is cleared when entering a handler).
Diffstat (limited to 'src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala')
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala97
1 files changed, 59 insertions, 38 deletions
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 e559b63c09..d8f12ffb11 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala
@@ -12,6 +12,7 @@ import scala.tools.asm.Type
import scala.tools.asm.tree.{MethodNode, MethodInsnNode}
import scala.tools.nsc.backend.jvm.BTypes.InternalName
import scala.collection.convert.decorateAsScala._
+import scala.tools.nsc.backend.jvm.BackendReporting.OptimizerWarning
class InlinerHeuristics[BT <: BTypes](val bTypes: BT) {
import bTypes._
@@ -38,25 +39,32 @@ class InlinerHeuristics[BT <: BTypes](val bTypes: BT) {
compilingMethods.map(methodNode => {
var requests = Set.empty[InlineRequest]
callGraph.callsites(methodNode).valuesIterator foreach {
- case callsite @ Callsite(_, _, _, Right(Callee(callee, calleeDeclClass, safeToInline, _, annotatedInline, _, _, warning)), _, _, _, pos) =>
- val request = inlineRequest(callsite)
- requests ++= request
- if (request.isEmpty) {
- if (annotatedInline && bTypes.compilerSettings.YoptWarningEmitAtInlineFailed) {
- // 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).
- def initMsg = s"${BackendReporting.methodSignature(calleeDeclClass.internalName, callee)} is annotated @inline but cannot be inlined"
- def warnMsg = warning.map(" Possible reason:\n" + _).getOrElse("")
- if (doRewriteTraitCallsite(callsite))
- backendReporting.inlinerWarning(pos, s"$initMsg: the trait method call could not be rewritten to the static implementation method." + warnMsg)
- else if (!safeToInline)
- backendReporting.inlinerWarning(pos, s"$initMsg: the method is not final and may be overridden." + warnMsg)
- else
- backendReporting.inlinerWarning(pos, s"$initMsg." + warnMsg)
- } else if (warning.isDefined && warning.get.emitWarning(compilerSettings)) {
- // when annotatedInline is false, and there is some warning, the callsite metadata is possibly incomplete.
- backendReporting.inlinerWarning(pos, s"there was a problem determining if method ${callee.name} can be inlined: \n"+ warning.get)
- }
+ case callsite @ Callsite(_, _, _, Right(Callee(callee, calleeDeclClass, safeToInline, _, annotatedInline, _, _, callsiteWarning)), _, _, _, pos) =>
+ inlineRequest(callsite) match {
+ case Some(Right(req)) => requests += req
+ case Some(Left(w)) =>
+ if ((annotatedInline && bTypes.compilerSettings.YoptWarningEmitAtInlineFailed) || w.emitWarning(compilerSettings)) {
+ val annotWarn = if (annotatedInline) " 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 (annotatedInline && bTypes.compilerSettings.YoptWarningEmitAtInlineFailed) {
+ // 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).
+ def initMsg = s"${BackendReporting.methodSignature(calleeDeclClass.internalName, callee)} is annotated @inline but cannot be inlined"
+ def warnMsg = callsiteWarning.map(" Possible reason:\n" + _).getOrElse("")
+ if (doRewriteTraitCallsite(callsite))
+ backendReporting.inlinerWarning(pos, s"$initMsg: the trait method call could not be rewritten to the static implementation method." + warnMsg)
+ else if (!safeToInline)
+ backendReporting.inlinerWarning(pos, s"$initMsg: the method is not final and may be overridden." + warnMsg)
+ else
+ backendReporting.inlinerWarning(pos, s"$initMsg." + warnMsg)
+ } else if (callsiteWarning.isDefined && callsiteWarning.get.emitWarning(compilerSettings)) {
+ // when annotatedInline is false, and there is some warning, the callsite metadata is possibly incomplete.
+ backendReporting.inlinerWarning(pos, s"there was a problem determining if method ${callee.name} can be inlined: \n"+ callsiteWarning.get)
+ }
}
case Callsite(ins, _, _, Left(warning), _, _, _, pos) =>
@@ -73,27 +81,40 @@ class InlinerHeuristics[BT <: BTypes](val bTypes: BT) {
*
* The resulting inline request may contain post-inlining requests of callsites that in turn are
* also selected as individual inlining requests.
+ *
+ * @return `None` if this callsite should not be inlined according to the active heuristic
+ * `Some(Left)` if the callsite cannot be inlined (for example because that would cause
+ * an IllegalAccessError) but should be according to the heuristic
+ * TODO: what if a downstream inline request would cause an IAE and we don't create an
+ * 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[InlineRequest] = compilerSettings.YoptInlineHeuristics.value match {
- case "everything" =>
- if (callsite.callee.get.safeToInline) Some(InlineRequest(callsite, Nil))
- else None
-
- case "at-inline-annotated" =>
- val callee = callsite.callee.get
- if (callee.safeToInline && callee.annotatedInline) Some(InlineRequest(callsite, Nil))
- else None
-
- case "default" =>
- val callee = callsite.callee.get
- if (callee.safeToInline && !callee.annotatedNoInline) {
- val shouldInlineHO = callee.samParamTypes.nonEmpty && (callee.samParamTypes exists {
- case (index, _) => callsite.argInfos.contains(index)
- })
-
- if (shouldInlineHO || callee.annotatedInline) Some(InlineRequest(callsite, Nil))
+ def inlineRequest(callsite: Callsite): Option[Either[OptimizerWarning, InlineRequest]] = {
+ val callee = callsite.callee.get
+ def requestIfCanInline(callsite: Callsite): Either[OptimizerWarning, InlineRequest] = inliner.earlyCanInlineCheck(callsite) match {
+ case Some(w) => Left(w)
+ case None => Right(InlineRequest(callsite, Nil))
+ }
+
+ compilerSettings.YoptInlineHeuristics.value match {
+ case "everything" =>
+ if (callee.safeToInline) Some(requestIfCanInline(callsite))
+ else None
+
+ case "at-inline-annotated" =>
+ if (callee.safeToInline && callee.annotatedInline) Some(requestIfCanInline(callsite))
else None
- } else None
+
+ case "default" =>
+ if (callee.safeToInline && !callee.annotatedNoInline) {
+ val shouldInlineHO = callee.samParamTypes.nonEmpty && (callee.samParamTypes exists {
+ case (index, _) => callsite.argInfos.contains(index)
+ })
+
+ if (shouldInlineHO || callee.annotatedInline) Some(requestIfCanInline(callsite))
+ else None
+ } else None
+ }
}
/*