Skip to content

Commit 940a2c1

Browse files
authored
Merge pull request #5748 from dotty-staging/try-optimize-1
Reduce some allocations
2 parents 2d16d9f + 2ee9994 commit 940a2c1

28 files changed

+211
-75
lines changed

compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
477477
// unrelated change.
478478
ctx.base.settings.YnoGenericSig.value
479479
|| sym.is(Flags.Artifact)
480-
|| sym.is(Flags.allOf(Flags.Method, Flags.Lifted))
480+
|| sym.is(Flags.LiftedMethod)
481481
|| sym.is(Flags.Bridge)
482482
)
483483

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ class Compiler {
9797
List(new Constructors, // Collect initialization code in primary constructors
9898
// Note: constructors changes decls in transformTemplate, no InfoTransformers should be added after it
9999
new FunctionalInterfaces, // Rewrites closures to implement @specialized types of Functions.
100+
new Instrumentation, // Count closure allocations under -Yinstrument-closures
100101
new GetClass) :: // Rewrites getClass calls on primitive types.
101102
List(new LinkScala2Impls, // Redirect calls to trait methods defined by Scala 2.x, so that they now go to their implementations
102103
new LambdaLift, // Lifts out nested functions to class scope, storing free variables in environments

compiler/src/dotty/tools/dotc/ast/Positioned.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,12 @@ abstract class Positioned(implicit @constructorOnly src: SourceFile) extends Pro
5151
else {
5252
val newpd: this.type =
5353
if (mySpan.isSynthetic) {
54-
if (!mySpan.exists && span.exists)
54+
if (!mySpan.exists && span.exists) {
5555
envelope(source, span.startPos) // fill in children spans
56+
() // Note: the `()` is there to prevent some inefficient code from being generated.
57+
// Without it we get an allocation of a span here since the result type of the `if`
58+
// is `Any`, the lub of `Span` and `Unit`.
59+
}
5660
this
5761
}
5862
else cloneIn(source)

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -978,7 +978,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
978978
}
979979

980980
implicit class ListOfTreeDecorator(val xs: List[tpd.Tree]) extends AnyVal {
981-
def tpes: List[Type] = xs map (_.tpe)
981+
def tpes: List[Type] = xs match {
982+
case x :: xs1 => x.tpe :: xs1.tpes
983+
case nil => Nil
984+
}
982985
}
983986

984987
/** A trait for loaders that compute trees. Currently implemented just by DottyUnpickler. */

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ class ScalaSettings extends Settings.SettingGroup {
149149

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

152+
val YinstrumentClosures: Setting[Boolean] = BooleanSetting("-Yinstrument-closures", "Add instrumentation code that counts closure creations.")
153+
val YinstrumentAllocations: Setting[Boolean] = BooleanSetting("-Yinstrument-allocations", "Add instrumentation code that counts allocations.")
154+
152155
/** Dottydoc specific settings */
153156
val siteRoot: Setting[String] = StringSetting(
154157
"-siteroot",

compiler/src/dotty/tools/dotc/core/Decorators.scala

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -151,18 +151,21 @@ object Decorators {
151151
* exact meaning of "contains" here.
152152
*/
153153
implicit class PhaseListDecorator(val names: List[String]) extends AnyVal {
154-
def containsPhase(phase: Phase): Boolean = phase match {
155-
case phase: MegaPhase => phase.miniPhases.exists(containsPhase)
156-
case _ =>
157-
names exists { name =>
158-
name == "all" || {
159-
val strippedName = name.stripSuffix("+")
160-
val logNextPhase = name != strippedName
161-
phase.phaseName.startsWith(strippedName) ||
162-
(logNextPhase && phase.prev.phaseName.startsWith(strippedName))
163-
}
154+
def containsPhase(phase: Phase): Boolean =
155+
names.nonEmpty && {
156+
phase match {
157+
case phase: MegaPhase => phase.miniPhases.exists(containsPhase)
158+
case _ =>
159+
names exists { name =>
160+
name == "all" || {
161+
val strippedName = name.stripSuffix("+")
162+
val logNextPhase = name != strippedName
163+
phase.phaseName.startsWith(strippedName) ||
164+
(logNextPhase && phase.prev.phaseName.startsWith(strippedName))
165+
}
166+
}
164167
}
165-
}
168+
}
166169
}
167170

168171
implicit class genericDeco[T](val x: T) extends AnyVal {

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,9 @@ class Definitions {
732732
lazy val ValueOfType: TypeRef = ctx.requiredClassRef("scala.ValueOf")
733733
def ValueOfClass(implicit ctx: Context): ClassSymbol = ValueOfType.symbol.asClass
734734

735+
lazy val StatsModule = ctx.requiredModule("dotty.tools.dotc.util.Stats")
736+
def Stats_doRecord(implicit ctx: Context): TermSymbol = StatsModule.requiredMethod("doRecord")
737+
735738
lazy val XMLTopScopeModuleRef: TermRef = ctx.requiredModuleRef("scala.xml.TopScope")
736739

737740
lazy val TupleTypeRef: TypeRef = ctx.requiredClassRef("scala.Tuple")

compiler/src/dotty/tools/dotc/core/Flags.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,8 @@ object Flags {
701701
/** Labeled protected[this] */
702702
final val ProtectedLocal: FlagConjunction = allOf(Protected, Local)
703703

704+
final val LiftedMethod: FlagConjunction = allOf(Lifted, Method)
705+
704706
/** Java symbol which is `protected` and `static` */
705707
final val StaticProtected: FlagConjunction = allOf(JavaDefined, Protected, JavaStatic)
706708

compiler/src/dotty/tools/dotc/core/Mode.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ case class Mode(val bits: Int) extends AnyVal {
1212

1313
override def toString: String =
1414
(0 until 31).filter(i => (bits & (1 << i)) != 0).map(modeName).mkString("Mode(", ",", ")")
15+
16+
def ==(that: Mode): Boolean = this.bits == that.bits
17+
def !=(that: Mode): Boolean = this.bits != that.bits
1518
}
1619

1720
object Mode {

compiler/src/dotty/tools/dotc/core/Names.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ object Names {
570570
*/
571571
def termName(cs: Array[Char], offset: Int, len: Int): SimpleName = synchronized {
572572
util.Stats.record("termName")
573-
val h = hashValue(cs, offset, len) & (table.size - 1)
573+
val h = hashValue(cs, offset, len) & (table.length - 1)
574574

575575
/** Make sure the capacity of the character array is at least `n` */
576576
def ensureCapacity(n: Int) =

compiler/src/dotty/tools/dotc/core/Periods.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ object Periods {
123123
this.lastPhaseId max that.lastPhaseId)
124124

125125
override def toString: String = s"Period($firstPhaseId..$lastPhaseId, run = $runId)"
126+
127+
def ==(that: Period): Boolean = this.code == that.code
128+
def !=(that: Period): Boolean = this.code != that.code
126129
}
127130

128131
object Period {

compiler/src/dotty/tools/dotc/core/Phases.scala

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,25 @@ object Phases {
7979
* whereas a combined TreeTransformer gets period equal to union of periods of it's TreeTransforms
8080
*/
8181
final def squashPhases(phasess: List[List[Phase]],
82-
phasesToSkip: List[String], stopBeforePhases: List[String], stopAfterPhases: List[String], YCheckAfter: List[String]): List[Phase] = {
82+
phasesToSkip: List[String],
83+
stopBeforePhases: List[String],
84+
stopAfterPhases: List[String],
85+
YCheckAfter: List[String])(implicit ctx: Context): List[Phase] = {
8386
val squashedPhases = ListBuffer[Phase]()
8487
var prevPhases: Set[String] = Set.empty
8588
val YCheckAll = YCheckAfter.contains("all")
8689

8790
var stop = false
91+
92+
def isEnabled(p: Phase): Boolean =
93+
!stop &&
94+
!stopBeforePhases.contains(p.phaseName) &&
95+
!phasesToSkip.contains(p.phaseName) &&
96+
p.isEnabled
97+
8898
val filteredPhases = phasess.map(_.filter { p =>
89-
val pstop = stop
90-
stop = stop | stopBeforePhases.contains(p.phaseName) | stopAfterPhases.contains(p.phaseName)
91-
!(pstop || stopBeforePhases.contains(p.phaseName) || phasesToSkip.contains(p.phaseName))
99+
try isEnabled(p)
100+
finally stop |= stopBeforePhases.contains(p.phaseName) | stopAfterPhases.contains(p.phaseName)
92101
})
93102

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

335+
def isEnabled(implicit ctx: Context): Boolean = true
336+
326337
def exists: Boolean = true
327338

328339
def initContext(ctx: FreshContext): Unit = ()

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1530,11 +1530,16 @@ object SymDenotations {
15301530
if (classParents.isEmpty && !emptyParentsExpected)
15311531
onBehalf.signalProvisional()
15321532
val builder = new BaseDataBuilder
1533-
for (p <- classParents)
1534-
p.classSymbol match {
1535-
case pcls: ClassSymbol => builder.addAll(pcls.baseClasses)
1536-
case _ => assert(isRefinementClass || ctx.mode.is(Mode.Interactive), s"$this has non-class parent: $p")
1537-
}
1533+
def traverse(parents: List[Type]): Unit = parents match {
1534+
case p :: parents1 =>
1535+
p.classSymbol match {
1536+
case pcls: ClassSymbol => builder.addAll(pcls.baseClasses)
1537+
case _ => assert(isRefinementClass || ctx.mode.is(Mode.Interactive), s"$this has non-class parent: $p")
1538+
}
1539+
traverse(parents1)
1540+
case nil =>
1541+
}
1542+
traverse(classParents)
15381543
(classSymbol :: builder.baseClasses, builder.baseClassSet)
15391544
}
15401545

compiler/src/dotty/tools/dotc/core/tasty/CommentPickler.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ package dotty.tools.dotc.core.tasty
33
import dotty.tools.dotc.ast.tpd
44
import dotty.tools.dotc.core.Comments.{Comment, CommentsContext, ContextDocstrings}
55
import dotty.tools.dotc.core.Contexts.Context
6-
import dotty.tools.dotc.core.tasty.TastyBuffer.Addr
6+
import dotty.tools.dotc.core.tasty.TastyBuffer.{Addr, NoAddr}
77

88
import java.nio.charset.Charset
99

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

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

19-
def pickleComment(addrOfTree: Option[Addr], comment: Option[Comment]): Unit = (addrOfTree, comment) match {
20-
case (Some(addr), Some(cmt)) =>
19+
def pickleComment(addr: Addr, comment: Option[Comment]): Unit = comment match {
20+
case Some(cmt) if addr != NoAddr =>
2121
val bytes = cmt.raw.getBytes(Charset.forName("UTF-8"))
2222
val length = bytes.length
2323
buf.writeAddr(addr)

compiler/src/dotty/tools/dotc/core/tasty/PositionPickler.scala

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import TastyBuffer._
1414
import util.Spans._
1515
import TastyFormat.SOURCE
1616

17-
class PositionPickler(pickler: TastyPickler, addrOfTree: untpd.Tree => Option[Addr]) {
17+
class PositionPickler(pickler: TastyPickler, addrOfTree: untpd.Tree => Addr) {
1818
val buf: TastyBuffer = new TastyBuffer(5000)
1919
pickler.newSection("Positions", buf)
2020
import ast.tpd._
@@ -72,29 +72,33 @@ class PositionPickler(pickler: TastyPickler, addrOfTree: untpd.Tree => Option[Ad
7272
def traverse(x: Any, current: SourceFile): Unit = x match {
7373
case x: untpd.Tree =>
7474
if (x.span.exists) {
75-
val sourceChange = x.source != current
76-
val needsPos = x.span.toSynthetic != x.envelope(x.source) || alwaysNeedsPos(x)
77-
addrOfTree(x) match {
78-
case Some(addr)
79-
if needsPos && !pickledIndices.contains(addr.index) || sourceChange =>
80-
// we currently do not share trees when unpickling, so if one path to a tree contains
81-
// a source change while another does not, we have to record the position of the tree twice
82-
// in order not to miss the source change. Test case is t3232a.scala.
83-
//println(i"pickling $x with $span at $addr")
75+
val addr = addrOfTree(x)
76+
if (addr != NoAddr) {
77+
if (x.source != current) {
78+
// we currently do not share trees when unpickling, so if one path to a tree contains
79+
// a source change while another does not, we have to record the position of the tree twice
80+
// in order not to miss the source change. Test case is t3232a.scala.
81+
pickleDeltas(addr.index, x.span)
82+
pickleSource(x.source)
83+
}
84+
else if (!pickledIndices.contains(addr.index) &&
85+
(x.span.toSynthetic != x.envelope(x.source) || alwaysNeedsPos(x)))
8486
pickleDeltas(addr.index, x.span)
85-
if (sourceChange) pickleSource(x.source)
86-
case _ =>
87-
//println(i"no address for $x")
8887
}
8988
}
9089
x match {
91-
case x: untpd.MemberDef @unchecked =>
92-
for (ann <- x.symbol.annotations) traverse(ann.tree, x.source)
90+
case x: untpd.MemberDef @unchecked => traverse(x.symbol.annotations, x.source)
9391
case _ =>
9492
}
95-
traverse(x.productIterator, x.source)
96-
case xs: TraversableOnce[_] =>
97-
xs.foreach(traverse(_, current))
93+
val limit = x.productArity
94+
var n = 0
95+
while (n < limit) {
96+
traverse(x.productElement(n), x.source)
97+
n += 1
98+
}
99+
case y :: ys =>
100+
traverse(y, current)
101+
traverse(ys, current)
98102
case x: Annotation =>
99103
traverse(x.tree, current)
100104
case _ =>

compiler/src/dotty/tools/dotc/core/tasty/TastyBuffer.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ object TastyBuffer {
2020
def + (delta: Int): Addr = Addr(this.index + delta)
2121

2222
def relativeTo(base: Addr): Addr = this - base.index - AddrWidth
23+
24+
def ==(that: Addr): Boolean = this.index == that.index
25+
def !=(that: Addr): Boolean = this.index != that.index
2326
}
2427

2528
val NoAddr: Addr = Addr(-1)

compiler/src/dotty/tools/dotc/core/tasty/TastyPickler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class TastyPickler(val rootCls: ClassSymbol) {
6767
* Note that trees are looked up by reference equality,
6868
* so one can reliably use this function only directly after `pickler`.
6969
*/
70-
var addrOfTree: tpd.Tree => Option[Addr] = (_ => None)
70+
var addrOfTree: tpd.Tree => Addr = (_ => NoAddr)
7171

7272
/**
7373
* Addresses in TASTY file of symbols, stored by pickling.

compiler/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ package core
44
package tasty
55

66
import util.Util.{bestFit, dble}
7-
import TastyBuffer.{Addr, AddrWidth}
7+
import TastyBuffer.{Addr, NoAddr, AddrWidth}
88
import config.Printers.pickling
99
import ast.untpd.Tree
1010

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

28-
def addrOfTree(tree: Tree): Option[Addr] = treeAddrs.get(tree) match {
29-
case null => None
30-
case addr: Addr => Some(addr)
28+
def addrOfTree(tree: Tree): Addr = treeAddrs.get(tree) match {
29+
case null => NoAddr
30+
case addr: Addr => addr
3131
}
3232

3333
private def offset(i: Int): Addr = Addr(offsets(i))

compiler/src/dotty/tools/dotc/reporting/Reporter.scala

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,12 @@ object Reporter {
3131
def doReport(m: MessageContainer)(implicit ctx: Context): Unit = ()
3232
override def report(m: MessageContainer)(implicit ctx: Context): Unit = ()
3333
}
34-
}
3534

35+
type ErrorHandler = (MessageContainer, Context) => Unit
36+
37+
private val defaultIncompleteHandler: ErrorHandler =
38+
(mc, ctx) => ctx.reporter.report(mc)(ctx)
39+
}
3640

3741
trait Reporting { this: Context =>
3842

@@ -138,6 +142,7 @@ trait Reporting { this: Context =>
138142
* error messages.
139143
*/
140144
abstract class Reporter extends interfaces.ReporterResult {
145+
import Reporter._
141146

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

158-
type ErrorHandler = MessageContainer => Context => Unit
159-
private[this] var incompleteHandler: ErrorHandler = d => c => report(d)(c)
163+
private[this] var incompleteHandler: ErrorHandler = defaultIncompleteHandler
164+
160165
def withIncompleteHandler[T](handler: ErrorHandler)(op: => T): T = {
161166
val saved = incompleteHandler
162167
incompleteHandler = handler
@@ -185,15 +190,16 @@ abstract class Reporter extends interfaces.ReporterResult {
185190

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

188-
val unreportedWarnings: mutable.HashMap[String, Int] = new mutable.HashMap[String, Int] {
189-
override def default(key: String) = 0
190-
}
193+
var unreportedWarnings: Map[String, Int] = Map.empty
191194

192195
def report(m: MessageContainer)(implicit ctx: Context): Unit =
193196
if (!isHidden(m)) {
194197
doReport(m)(ctx.addMode(Mode.Printing))
195198
m match {
196-
case m: ConditionalWarning if !m.enablingOption.value => unreportedWarnings(m.enablingOption.name) += 1
199+
case m: ConditionalWarning if !m.enablingOption.value =>
200+
val key = m.enablingOption.name
201+
unreportedWarnings =
202+
unreportedWarnings.updated(key, unreportedWarnings.getOrElse(key, 0) + 1)
197203
case m: Warning => _warningCount += 1
198204
case m: Error =>
199205
errors = m :: errors
@@ -204,7 +210,7 @@ abstract class Reporter extends interfaces.ReporterResult {
204210
}
205211

206212
def incomplete(m: MessageContainer)(implicit ctx: Context): Unit =
207-
incompleteHandler(m)(ctx)
213+
incompleteHandler(m, ctx)
208214

209215
/** Summary of warnings and errors */
210216
def summary: String = {

0 commit comments

Comments
 (0)