Skip to content

Commit

Permalink
documentation about BasicBlock and Inliner
Browse files Browse the repository at this point in the history
  • Loading branch information
magarciaEPFL committed May 8, 2012
1 parent afa60fc commit 76b6fd4
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 16 deletions.
6 changes: 3 additions & 3 deletions src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala
Expand Up @@ -219,8 +219,8 @@ trait BasicBlocks {
///////////////////// Substitutions /////////////////////// ///////////////////// Substitutions ///////////////////////


/** /**
* Replace the instruction at the given position. Used by labels when * Replace the instruction at the given position. Used by labels when they are anchored.
* they are anchored. It retains the position of the previous instruction. * The replacing instruction is given the nsc.util.Position of the instruction it replaces.
*/ */
def replaceInstruction(pos: Int, instr: Instruction): Boolean = { def replaceInstruction(pos: Int, instr: Instruction): Boolean = {
assert(closed, "Instructions can be replaced only after the basic block is closed") assert(closed, "Instructions can be replaced only after the basic block is closed")
Expand All @@ -233,7 +233,7 @@ trait BasicBlocks {
/** /**
* Replace the given instruction with the new one. * Replace the given instruction with the new one.
* Returns `true` if it actually changed something. * Returns `true` if it actually changed something.
* It retains the position of the previous instruction. * The replacing instruction is given the nsc.util.Position of the instruction it replaces.
*/ */
def replaceInstruction(oldInstr: Instruction, newInstr: Instruction): Boolean = { def replaceInstruction(oldInstr: Instruction, newInstr: Instruction): Boolean = {
assert(closed, "Instructions can be replaced only after the basic block is closed") assert(closed, "Instructions can be replaced only after the basic block is closed")
Expand Down
29 changes: 16 additions & 13 deletions src/compiler/scala/tools/nsc/backend/opt/Inliners.scala
Expand Up @@ -9,7 +9,7 @@ package backend.opt


import scala.collection.mutable import scala.collection.mutable
import scala.tools.nsc.symtab._ import scala.tools.nsc.symtab._
import scala.tools.nsc.util.{ NoSourceFile } import scala.tools.nsc.util.NoSourceFile


/** /**
* @author Iulian Dragos * @author Iulian Dragos
Expand Down Expand Up @@ -196,33 +196,35 @@ abstract class Inliners extends SubComponent {
val staleIn = mutable.Set.empty[BasicBlock] val staleIn = mutable.Set.empty[BasicBlock]


/** /**
* A transformation local to the body of the argument. * A transformation local to the body of the IMethod received as argument.
* An linining decision consists in replacing a callsite with the body of the callee. * An linining decision consists in replacing a callsite with the body of the callee.
* Please notice that, because `analyzeMethod()` itself may modify a method body, * Please notice that, because `analyzeMethod()` itself may modify a method body,
* the particular callee bodies that end up being inlined depend on the particular order in which methods are visited * the particular callee bodies that end up being inlined depend on the particular order in which methods are visited
* (no topological ordering over the call-graph is attempted). * (no topological sorting over the call-graph is attempted).
* *
* Making an inlining decision requires type-flow information for both caller and callee. * Making an inlining decision requires type-flow information for both caller and callee.
* Regarding the caller, such information is needed only for basic blocks containing inlining candidates * Regarding the caller, such information is needed only for basic blocks containing inlining candidates
* (and their transitive predecessors). This observation leads to using a custom type-flow analysis (MTFAGrowable) * (and their transitive predecessors). This observation leads to using a custom type-flow analysis (MTFAGrowable)
* that can be re-inited, i.e. that reuses lattice elements (type-flow information) computed in a previous iteration * that can be re-inited, i.e. that reuses lattice elements (type-flow information computed in a previous iteration)
* as starting point for faster convergence in a new iteration. * as starting point for faster convergence in a new iteration.
* *
* The mechanics of inlining are iterative for a given invocation of `analyzeMethod(m)`, * The mechanics of inlining are iterative for a given invocation of `analyzeMethod(m)`,
* thus considering the basic blocks that successful inlining added in a previous iteration: * and are affected by inlinings from previous iterations
* (ie, "heuristic" rules are based on statistics tracked for that purpose):
* *
* (1) before the iterations proper start, so-called preinlining is performed. * (1) before the iterations proper start, so-called preinlining is performed.
* Those callsites whose (receiver, concreteMethod) are both known statically * Those callsites whose (receiver, concreteMethod) are both known statically
* can be analyzed for inlining before computing a type-flow. Details in `preInline()` * can be analyzed for inlining before computing a type-flow. Details in `preInline()`
* *
* (2) the first iteration computes type-flow information for basic blocks containing inlining candidates * (2) the first iteration computes type-flow information for basic blocks containing inlining candidates
* (and their transitive predecessors), so called `relevantBBs`. * (and their transitive predecessors), so called `relevantBBs` basic blocks.
* The ensuing analysis of each candidate (performed by `analyzeInc()`) * The ensuing analysis of each candidate (performed by `analyzeInc()`)
* may result in a CFG isomorphic to that of the callee being inserted where the callsite was * may result in a CFG isomorphic to that of the callee being inserted in place of the callsite
* (i.e. a CALL_METHOD instruction is replaced with a single-entry single-exit CFG, which we call "successful inlining"). * (i.e. a CALL_METHOD instruction is replaced with a single-entry single-exit CFG,
* a situation we call "successful inlining").
* *
* (3) following iterations have their relevant basic blocks updated to focus * (3) following iterations have `relevantBBs` updated to focus on the inlined basic blocks and their successors only.
* on the inlined basic blocks and their successors only. Details in `MTFAGrowable.reinit()` * Details in `MTFAGrowable.reinit()`
* */ * */
def analyzeMethod(m: IMethod): Unit = { def analyzeMethod(m: IMethod): Unit = {
// m.normalize // m.normalize
Expand Down Expand Up @@ -372,7 +374,7 @@ abstract class Inliners extends SubComponent {
* That's why preInline() is invoked twice: any inlinings downplayed by the heuristics during the first round get an opportunity to rank higher during the second. * That's why preInline() is invoked twice: any inlinings downplayed by the heuristics during the first round get an opportunity to rank higher during the second.
* *
* As a whole, both `preInline()` invocations amount to priming the inlining process, * As a whole, both `preInline()` invocations amount to priming the inlining process,
* so that the first TFA run afterwards is able to gain more information as compared to a cold-start. * so that the first TFA that is run afterwards is able to gain more information as compared to a cold-start.
*/ */
val totalPreInlines = { val totalPreInlines = {
val firstRound = preInline(true) val firstRound = preInline(true)
Expand All @@ -388,9 +390,10 @@ abstract class Inliners extends SubComponent {


/* it's important not to inline in unreachable basic blocks. linearizedBlocks() returns only reachable ones. */ /* it's important not to inline in unreachable basic blocks. linearizedBlocks() returns only reachable ones. */
tfa.callerLin = caller.m.linearizedBlocks() tfa.callerLin = caller.m.linearizedBlocks()
/* TODO Do we want to perform inlining in non-finally exception handlers? /* TODO Do we really want to inline inside exception handlers?
* Seems counterproductive (the larger the method the less likely it will be JITed). * Seems counterproductive (the larger the method the less likely it will be JITed).
* The alternative above would be `linearizer.linearizeAt(caller.m, caller.m.startBlock)`. * The alternative would be `linearizer.linearizeAt(caller.m, caller.m.startBlock)`.
* And, we would cut down on TFA iterations, too.
* See also comment on the same topic in TypeFlowAnalysis. */ * See also comment on the same topic in TypeFlowAnalysis. */


tfa.reinit(m, staleOut.toList, splicedBlocks, staleIn) tfa.reinit(m, staleOut.toList, splicedBlocks, staleIn)
Expand Down

0 comments on commit 76b6fd4

Please sign in to comment.