Skip to content

Reduce some allocations #5748

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

Merged
merged 19 commits into from
Jan 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
// unrelated change.
ctx.base.settings.YnoGenericSig.value
|| sym.is(Flags.Artifact)
|| sym.is(Flags.allOf(Flags.Method, Flags.Lifted))
|| sym.is(Flags.LiftedMethod)
|| sym.is(Flags.Bridge)
)

Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class Compiler {
List(new Constructors, // Collect initialization code in primary constructors
// Note: constructors changes decls in transformTemplate, no InfoTransformers should be added after it
new FunctionalInterfaces, // Rewrites closures to implement @specialized types of Functions.
new Instrumentation, // Count closure allocations under -Yinstrument-closures
new GetClass) :: // Rewrites getClass calls on primitive types.
List(new LinkScala2Impls, // Redirect calls to trait methods defined by Scala 2.x, so that they now go to their implementations
new LambdaLift, // Lifts out nested functions to class scope, storing free variables in environments
Expand Down
6 changes: 5 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/Positioned.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,12 @@ abstract class Positioned(implicit @constructorOnly src: SourceFile) extends Pro
else {
val newpd: this.type =
if (mySpan.isSynthetic) {
if (!mySpan.exists && span.exists)
if (!mySpan.exists && span.exists) {
envelope(source, span.startPos) // fill in children spans
() // Note: the `()` is there to prevent some inefficient code from being generated.
// Without it we get an allocation of a span here since the result type of the `if`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this changes whether an allocation happens or not but it does make the code more efficient, I've opened an issue to track this that you could refer to in this comment: #5750

// is `Any`, the lub of `Span` and `Unit`.
}
this
}
else cloneIn(source)
Expand Down
5 changes: 4 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -978,7 +978,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
}

implicit class ListOfTreeDecorator(val xs: List[tpd.Tree]) extends AnyVal {
def tpes: List[Type] = xs map (_.tpe)
def tpes: List[Type] = xs match {
case x :: xs1 => x.tpe :: xs1.tpes
case nil => Nil
}
}

/** A trait for loaders that compute trees. Currently implemented just by DottyUnpickler. */
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ class ScalaSettings extends Settings.SettingGroup {

val YnoDecodeStacktraces: Setting[Boolean] = BooleanSetting("-Yno-decode-stacktraces", "Show raw StackOverflow stacktraces, instead of decoding them into triggering operations.")

val YinstrumentClosures: Setting[Boolean] = BooleanSetting("-Yinstrument-closures", "Add instrumentation code that counts closure creations.")
val YinstrumentAllocations: Setting[Boolean] = BooleanSetting("-Yinstrument-allocations", "Add instrumentation code that counts allocations.")

/** Dottydoc specific settings */
val siteRoot: Setting[String] = StringSetting(
"-siteroot",
Expand Down
25 changes: 14 additions & 11 deletions compiler/src/dotty/tools/dotc/core/Decorators.scala
Original file line number Diff line number Diff line change
Expand Up @@ -151,18 +151,21 @@ object Decorators {
* exact meaning of "contains" here.
*/
implicit class PhaseListDecorator(val names: List[String]) extends AnyVal {
def containsPhase(phase: Phase): Boolean = phase match {
case phase: MegaPhase => phase.miniPhases.exists(containsPhase)
case _ =>
names exists { name =>
name == "all" || {
val strippedName = name.stripSuffix("+")
val logNextPhase = name != strippedName
phase.phaseName.startsWith(strippedName) ||
(logNextPhase && phase.prev.phaseName.startsWith(strippedName))
}
def containsPhase(phase: Phase): Boolean =
names.nonEmpty && {
phase match {
case phase: MegaPhase => phase.miniPhases.exists(containsPhase)
case _ =>
names exists { name =>
name == "all" || {
val strippedName = name.stripSuffix("+")
val logNextPhase = name != strippedName
phase.phaseName.startsWith(strippedName) ||
(logNextPhase && phase.prev.phaseName.startsWith(strippedName))
}
}
}
}
}
}

implicit class genericDeco[T](val x: T) extends AnyVal {
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,9 @@ class Definitions {
lazy val ValueOfType: TypeRef = ctx.requiredClassRef("scala.ValueOf")
def ValueOfClass(implicit ctx: Context): ClassSymbol = ValueOfType.symbol.asClass

lazy val StatsModule = ctx.requiredModule("dotty.tools.dotc.util.Stats")
def Stats_doRecord(implicit ctx: Context): TermSymbol = StatsModule.requiredMethod("doRecord")

lazy val XMLTopScopeModuleRef: TermRef = ctx.requiredModuleRef("scala.xml.TopScope")

lazy val TupleTypeRef: TypeRef = ctx.requiredClassRef("scala.Tuple")
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,8 @@ object Flags {
/** Labeled protected[this] */
final val ProtectedLocal: FlagConjunction = allOf(Protected, Local)

final val LiftedMethod: FlagConjunction = allOf(Lifted, Method)

/** Java symbol which is `protected` and `static` */
final val StaticProtected: FlagConjunction = allOf(JavaDefined, Protected, JavaStatic)

Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Mode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ case class Mode(val bits: Int) extends AnyVal {

override def toString: String =
(0 until 31).filter(i => (bits & (1 << i)) != 0).map(modeName).mkString("Mode(", ",", ")")

def ==(that: Mode): Boolean = this.bits == that.bits
def !=(that: Mode): Boolean = this.bits != that.bits
}

object Mode {
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Names.scala
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ object Names {
*/
def termName(cs: Array[Char], offset: Int, len: Int): SimpleName = synchronized {
util.Stats.record("termName")
val h = hashValue(cs, offset, len) & (table.size - 1)
val h = hashValue(cs, offset, len) & (table.length - 1)

/** Make sure the capacity of the character array is at least `n` */
def ensureCapacity(n: Int) =
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Periods.scala
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ object Periods {
this.lastPhaseId max that.lastPhaseId)

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

def ==(that: Period): Boolean = this.code == that.code
def !=(that: Period): Boolean = this.code != that.code
}

object Period {
Expand Down
19 changes: 15 additions & 4 deletions compiler/src/dotty/tools/dotc/core/Phases.scala
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,25 @@ object Phases {
* whereas a combined TreeTransformer gets period equal to union of periods of it's TreeTransforms
*/
final def squashPhases(phasess: List[List[Phase]],
phasesToSkip: List[String], stopBeforePhases: List[String], stopAfterPhases: List[String], YCheckAfter: List[String]): List[Phase] = {
phasesToSkip: List[String],
stopBeforePhases: List[String],
stopAfterPhases: List[String],
YCheckAfter: List[String])(implicit ctx: Context): List[Phase] = {
val squashedPhases = ListBuffer[Phase]()
var prevPhases: Set[String] = Set.empty
val YCheckAll = YCheckAfter.contains("all")

var stop = false

def isEnabled(p: Phase): Boolean =
!stop &&
!stopBeforePhases.contains(p.phaseName) &&
!phasesToSkip.contains(p.phaseName) &&
p.isEnabled

val filteredPhases = phasess.map(_.filter { p =>
val pstop = stop
stop = stop | stopBeforePhases.contains(p.phaseName) | stopAfterPhases.contains(p.phaseName)
!(pstop || stopBeforePhases.contains(p.phaseName) || phasesToSkip.contains(p.phaseName))
try isEnabled(p)
finally stop |= stopBeforePhases.contains(p.phaseName) | stopAfterPhases.contains(p.phaseName)
})

var i = 0
Expand Down Expand Up @@ -323,6 +332,8 @@ object Phases {
/** Can this transform change the base types of a type? */
def changesBaseTypes: Boolean = changesParents

def isEnabled(implicit ctx: Context): Boolean = true

def exists: Boolean = true

def initContext(ctx: FreshContext): Unit = ()
Expand Down
15 changes: 10 additions & 5 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1530,11 +1530,16 @@ object SymDenotations {
if (classParents.isEmpty && !emptyParentsExpected)
onBehalf.signalProvisional()
val builder = new BaseDataBuilder
for (p <- classParents)
p.classSymbol match {
case pcls: ClassSymbol => builder.addAll(pcls.baseClasses)
case _ => assert(isRefinementClass || ctx.mode.is(Mode.Interactive), s"$this has non-class parent: $p")
}
def traverse(parents: List[Type]): Unit = parents match {
case p :: parents1 =>
p.classSymbol match {
case pcls: ClassSymbol => builder.addAll(pcls.baseClasses)
case _ => assert(isRefinementClass || ctx.mode.is(Mode.Interactive), s"$this has non-class parent: $p")
}
traverse(parents1)
case nil =>
}
traverse(classParents)
(classSymbol :: builder.baseClasses, builder.baseClassSet)
}

Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/core/tasty/CommentPickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ package dotty.tools.dotc.core.tasty
import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.core.Comments.{Comment, CommentsContext, ContextDocstrings}
import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.core.tasty.TastyBuffer.Addr
import dotty.tools.dotc.core.tasty.TastyBuffer.{Addr, NoAddr}

import java.nio.charset.Charset

class CommentPickler(pickler: TastyPickler, addrOfTree: tpd.Tree => Option[Addr])(implicit ctx: Context) {
class CommentPickler(pickler: TastyPickler, addrOfTree: tpd.Tree => Addr)(implicit ctx: Context) {
private[this] val buf = new TastyBuffer(5000)
pickler.newSection("Comments", buf)

Expand All @@ -16,8 +16,8 @@ class CommentPickler(pickler: TastyPickler, addrOfTree: tpd.Tree => Option[Addr]
new Traverser(ctx.docCtx.get).traverse(root)
}

def pickleComment(addrOfTree: Option[Addr], comment: Option[Comment]): Unit = (addrOfTree, comment) match {
case (Some(addr), Some(cmt)) =>
def pickleComment(addr: Addr, comment: Option[Comment]): Unit = comment match {
case Some(cmt) if addr != NoAddr =>
val bytes = cmt.raw.getBytes(Charset.forName("UTF-8"))
val length = bytes.length
buf.writeAddr(addr)
Expand Down
40 changes: 22 additions & 18 deletions compiler/src/dotty/tools/dotc/core/tasty/PositionPickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import TastyBuffer._
import util.Spans._
import TastyFormat.SOURCE

class PositionPickler(pickler: TastyPickler, addrOfTree: untpd.Tree => Option[Addr]) {
class PositionPickler(pickler: TastyPickler, addrOfTree: untpd.Tree => Addr) {
val buf: TastyBuffer = new TastyBuffer(5000)
pickler.newSection("Positions", buf)
import ast.tpd._
Expand Down Expand Up @@ -72,29 +72,33 @@ class PositionPickler(pickler: TastyPickler, addrOfTree: untpd.Tree => Option[Ad
def traverse(x: Any, current: SourceFile): Unit = x match {
case x: untpd.Tree =>
if (x.span.exists) {
val sourceChange = x.source != current
val needsPos = x.span.toSynthetic != x.envelope(x.source) || alwaysNeedsPos(x)
addrOfTree(x) match {
case Some(addr)
if needsPos && !pickledIndices.contains(addr.index) || sourceChange =>
// we currently do not share trees when unpickling, so if one path to a tree contains
// a source change while another does not, we have to record the position of the tree twice
// in order not to miss the source change. Test case is t3232a.scala.
//println(i"pickling $x with $span at $addr")
val addr = addrOfTree(x)
if (addr != NoAddr) {
if (x.source != current) {
// we currently do not share trees when unpickling, so if one path to a tree contains
// a source change while another does not, we have to record the position of the tree twice
// in order not to miss the source change. Test case is t3232a.scala.
pickleDeltas(addr.index, x.span)
pickleSource(x.source)
}
else if (!pickledIndices.contains(addr.index) &&
(x.span.toSynthetic != x.envelope(x.source) || alwaysNeedsPos(x)))
pickleDeltas(addr.index, x.span)
if (sourceChange) pickleSource(x.source)
case _ =>
//println(i"no address for $x")
}
}
x match {
case x: untpd.MemberDef @unchecked =>
for (ann <- x.symbol.annotations) traverse(ann.tree, x.source)
case x: untpd.MemberDef @unchecked => traverse(x.symbol.annotations, x.source)
case _ =>
}
traverse(x.productIterator, x.source)
case xs: TraversableOnce[_] =>
xs.foreach(traverse(_, current))
val limit = x.productArity
var n = 0
while (n < limit) {
traverse(x.productElement(n), x.source)
n += 1
}
case y :: ys =>
traverse(y, current)
traverse(ys, current)
case x: Annotation =>
traverse(x.tree, current)
case _ =>
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/core/tasty/TastyBuffer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ object TastyBuffer {
def + (delta: Int): Addr = Addr(this.index + delta)

def relativeTo(base: Addr): Addr = this - base.index - AddrWidth

def ==(that: Addr): Boolean = this.index == that.index
def !=(that: Addr): Boolean = this.index != that.index
}

val NoAddr: Addr = Addr(-1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class TastyPickler(val rootCls: ClassSymbol) {
* Note that trees are looked up by reference equality,
* so one can reliably use this function only directly after `pickler`.
*/
var addrOfTree: tpd.Tree => Option[Addr] = (_ => None)
var addrOfTree: tpd.Tree => Addr = (_ => NoAddr)

/**
* Addresses in TASTY file of symbols, stored by pickling.
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package core
package tasty

import util.Util.{bestFit, dble}
import TastyBuffer.{Addr, AddrWidth}
import TastyBuffer.{Addr, NoAddr, AddrWidth}
import config.Printers.pickling
import ast.untpd.Tree

Expand All @@ -25,9 +25,9 @@ class TreeBuffer extends TastyBuffer(50000) {
case addr: Addr => addr
}

def addrOfTree(tree: Tree): Option[Addr] = treeAddrs.get(tree) match {
case null => None
case addr: Addr => Some(addr)
def addrOfTree(tree: Tree): Addr = treeAddrs.get(tree) match {
case null => NoAddr
case addr: Addr => addr
}

private def offset(i: Int): Addr = Addr(offsets(i))
Expand Down
22 changes: 14 additions & 8 deletions compiler/src/dotty/tools/dotc/reporting/Reporter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@ object Reporter {
def doReport(m: MessageContainer)(implicit ctx: Context): Unit = ()
override def report(m: MessageContainer)(implicit ctx: Context): Unit = ()
}
}

type ErrorHandler = (MessageContainer, Context) => Unit

private val defaultIncompleteHandler: ErrorHandler =
(mc, ctx) => ctx.reporter.report(mc)(ctx)
}

trait Reporting { this: Context =>

Expand Down Expand Up @@ -138,6 +142,7 @@ trait Reporting { this: Context =>
* error messages.
*/
abstract class Reporter extends interfaces.ReporterResult {
import Reporter._

/** Report a diagnostic */
def doReport(m: MessageContainer)(implicit ctx: Context): Unit
Expand All @@ -155,8 +160,8 @@ abstract class Reporter extends interfaces.ReporterResult {
finally _truncationOK = saved
}

type ErrorHandler = MessageContainer => Context => Unit
private[this] var incompleteHandler: ErrorHandler = d => c => report(d)(c)
private[this] var incompleteHandler: ErrorHandler = defaultIncompleteHandler

def withIncompleteHandler[T](handler: ErrorHandler)(op: => T): T = {
val saved = incompleteHandler
incompleteHandler = handler
Expand Down Expand Up @@ -185,15 +190,16 @@ abstract class Reporter extends interfaces.ReporterResult {

def reportNewFeatureUseSite(featureTrait: Symbol): Unit = reportedFeaturesUseSites += featureTrait

val unreportedWarnings: mutable.HashMap[String, Int] = new mutable.HashMap[String, Int] {
override def default(key: String) = 0
}
var unreportedWarnings: Map[String, Int] = Map.empty

def report(m: MessageContainer)(implicit ctx: Context): Unit =
if (!isHidden(m)) {
doReport(m)(ctx.addMode(Mode.Printing))
m match {
case m: ConditionalWarning if !m.enablingOption.value => unreportedWarnings(m.enablingOption.name) += 1
case m: ConditionalWarning if !m.enablingOption.value =>
val key = m.enablingOption.name
unreportedWarnings =
unreportedWarnings.updated(key, unreportedWarnings.getOrElse(key, 0) + 1)
case m: Warning => _warningCount += 1
case m: Error =>
errors = m :: errors
Expand All @@ -204,7 +210,7 @@ abstract class Reporter extends interfaces.ReporterResult {
}

def incomplete(m: MessageContainer)(implicit ctx: Context): Unit =
incompleteHandler(m)(ctx)
incompleteHandler(m, ctx)

/** Summary of warnings and errors */
def summary: String = {
Expand Down
Loading