aboutsummaryrefslogblamecommitdiff
path: root/compiler/src/dotty/tools/dotc/core/Periods.scala
blob: 892a1f59a5c45a79b93a5b8d8c4e42902152a4e2 (plain) (tree)
1
2
3
4
5
6
7
8
9
10


                             
                                     





                                                    
                                                          


                                     
                                   

                                   
                               
 
                                     
                                                    
                               

                                       
                                                     
                          


                                                                               

                                                                            


                       

                                                                                              

                
                                 
   



                
                                                                 


                                    


                                     

                                                                                                                                      
     
                                              
 
                                             
                                                
 
                                                            
                                                            
 
                                        
                              
                                       
 

                                                       
 

                                                                              
                                                 
                                           
















                                                                     



                                                                    
                                                      




                                              
                                          







                                                  
 
                                                     




                                                  
                                                                                


                 
 
                                                                          
                                                   
                                                           
     
 
                                                                     
                                                                     
                                                                               
     
 
                                                                 
                                
                              
     
   
 
                                   
 
                                                              
 

                                                      


                                                                     
                            

                                                                            
 



                                                                
 
                                                                
                          
                                             
                                          
 
package dotty.tools.dotc.core

import Contexts._
import dotty.tools.dotc.util.DotClass

/** Periods are the central "clock" of the compiler.
 *  A period consists of a run id and a phase id.
 *  run ids represent compiler runs
 *  phase ids represent compiler phases
 */
abstract class Periods extends DotClass { self: Context =>
  import Periods._

  /** The current phase identifier */
  def phaseId: Int = period.phaseId

  /** The current run identifier */
  def runId: Int = period.runId

  /** Execute `op` at given period */
  def atPeriod[T](pd: Period)(op: Context => T): T =
    op(ctx.fresh.setPeriod(pd))

  /** Execute `op` at given phase id */
  def atPhase[T](pid: PhaseId)(op: Context => T): T =
    op(ctx.withPhase(pid))

  /** The period containing the current period where denotations do not change.
   *  We compute this by taking as first phase the first phase less or equal to
   *  the current phase that has the same "nextTransformerId". As last phase
   *  we take the next transformer id following the current phase.
   */
  def stablePeriod = {
    var first = phaseId
    val nxTrans = ctx.base.nextDenotTransformerId(first)
    while (first - 1 > NoPhaseId && (ctx.base.nextDenotTransformerId(first - 1) == nxTrans)) {
      first -= 1
    }
    Period(runId, first, nxTrans)
  }
}

object Periods {

  /** A period is a contiguous sequence of phase ids in some run.
   *  It is coded as follows:
   *
   *     sign, always 0        1 bit
   *     runid                17 bits
   *     last phase id:        7 bits
   *     #phases before last:  7 bits
   *
   *     // Dmitry: sign == 0 isn't actually always true, in some cases phaseId == -1 is used for shifts, that easily creates code < 0
   */
  class Period(val code: Int) extends AnyVal {

    /** The run identifier of this period. */
    def runId: RunId = code >>> (PhaseWidth * 2)

    /** The phase identifier of this single-phase period. */
    def phaseId: PhaseId = (code >>> PhaseWidth) & PhaseMask

    /** The last phase of this period */
    def lastPhaseId: PhaseId =
      (code >>> PhaseWidth) & PhaseMask

    /** The first phase of this period */
    def firstPhaseId = lastPhaseId - (code & PhaseMask)

    def containsPhaseId(id: PhaseId) = firstPhaseId <= id && id <= lastPhaseId

    /** Does this period contain given period? */
    def contains(that: Period): Boolean = {
      // Let    this = (r1, l1, d1), that = (r2, l2, d2)
      // where  r = runid, l = last phase, d = duration - 1
      // Then seen as intervals:
      //
      //  this = r1 / (l1 - d1) .. l1
      //  that = r2 / (l2 - d2) .. l2
      //
      // Let's compute:
      //
      //  lastDiff = X * 2^5 + (l1 - l2) mod 2^5
      //             where X >= 0, X == 0 iff r1 == r2 & l1 - l2 >= 0
      //  result = lastDiff + d2 <= d1
      //  We have:
      //      lastDiff + d2 <= d1
      //  iff X == 0 && l1 - l2 >= 0 && l1 - l2 + d2 <= d1
      //  iff r1 == r2 & l1 >= l2 && l1 - d1 <= l2 - d2
      //  q.e.d
      val lastDiff = (code - that.code) >>> PhaseWidth
      lastDiff + (that.code & PhaseMask ) <= (this.code & PhaseMask)
    }

    /** Does this period overlap with given period? */
    def overlaps(that: Period): Boolean =
      this.runId == that.runId &&
      this.firstPhaseId <= that.lastPhaseId &&
      that.firstPhaseId <= this.lastPhaseId

    /** The intersection of two periods */
    def & (that: Period): Period =
      if (this overlaps that)
        Period(
          this.runId,
          this.firstPhaseId max that.firstPhaseId,
          this.lastPhaseId min that.lastPhaseId)
      else
        Nowhere

    /** The smallest period containing two periods */
    def | (that: Period): Period =
      Period(this.runId,
          this.firstPhaseId min that.firstPhaseId,
          this.lastPhaseId max that.lastPhaseId)

    override def toString = s"Period($firstPhaseId..$lastPhaseId, run = $runId)"
  }

  object Period {

    /** The single-phase period consisting of given run id and phase id */
    def apply(rid: RunId, pid: PhaseId): Period = {
      new Period(((rid << PhaseWidth) | pid) << PhaseWidth)
    }

    /** The period consisting of given run id, and lo/hi phase ids */
    def apply(rid: RunId, loPid: PhaseId, hiPid: PhaseId): Period = {
      new Period(((rid << PhaseWidth) | hiPid) << PhaseWidth | (hiPid - loPid))
    }

    /** The interval consisting of all periods of given run id */
    def allInRun(rid: RunId) = {
      apply(rid, 0, PhaseMask)
    }
  }

  final val Nowhere = new Period(0)

  final val InitialPeriod = Period(InitialRunId, FirstPhaseId)

  final val InvalidPeriod = Period(NoRunId, NoPhaseId)

  /** An ordinal number for compiler runs. First run has number 1. */
  type RunId = Int
  final val NoRunId = 0
  final val InitialRunId = 1
  final val RunWidth = java.lang.Integer.SIZE - PhaseWidth * 2 - 1/* sign */
  final val MaxPossibleRunId = (1 << RunWidth) - 1

  /** An ordinal number for phases. First phase has number 1. */
  type PhaseId = Int
  final val NoPhaseId = 0
  final val FirstPhaseId = 1

  /** The number of bits needed to encode a phase identifier. */
  final val PhaseWidth = 7
  final val PhaseMask = (1 << PhaseWidth) - 1
  final val MaxPossiblePhaseId = PhaseMask
}