Skip to content

Avoid allocations in more places #9690

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 6 commits into from
Sep 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ object Trees {
val point = span.point
if (rawMods.is(Synthetic) || name.toTermName == nme.ERROR) Span(point)
else {
val realName = name.stripModuleClassSuffix.lastPart.toString
val realName = name.stripModuleClassSuffix.lastPart
Span(point, point + realName.length, point)
}
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1172,8 +1172,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {

// convert a numeric with a toXXX method
def primitiveConversion(tree: Tree, numericCls: Symbol)(using Context): Tree = {
val mname = ("to" + numericCls.name).toTermName
val conversion = tree.tpe member mname
val mname = "to".concat(numericCls.name)
val conversion = tree.tpe member(mname)
if (conversion.symbol.exists)
tree.select(conversion.symbol.termRef).ensureApplied
else if (tree.tpe.widen isRef numericCls)
Expand Down
7 changes: 6 additions & 1 deletion compiler/src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -918,7 +918,12 @@ object Contexts {
private[Contexts] val comparers = new mutable.ArrayBuffer[TypeComparer]
private[Contexts] var comparersInUse: Int = 0

private[dotc] var nameCharBuffer = new Array[Char](256)
private var charArray = new Array[Char](256)

def sharedCharArray(len: Int): Array[Char] =
while len > charArray.length do
charArray = new Array[Char](charArray.length * 2)
charArray

def reset(): Unit = {
for ((_, set) <- uniqueSets) set.clear()
Expand Down
19 changes: 14 additions & 5 deletions compiler/src/dotty/tools/dotc/core/Decorators.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,25 @@ object Decorators {
* This avoids some allocation relative to `termName(s)`
*/
def sliceToTermName(start: Int, end: Int)(using Context): SimpleName =
val base = ctx.base
val len = end - start
while len > base.nameCharBuffer.length do
base.nameCharBuffer = new Array[Char](base.nameCharBuffer.length * 2)
s.getChars(start, end, base.nameCharBuffer, 0)
termName(base.nameCharBuffer, 0, len)
val chars = ctx.base.sharedCharArray(len)
s.getChars(start, end, chars, 0)
termName(chars, 0, len)

def sliceToTypeName(start: Int, end: Int)(using Context): TypeName =
sliceToTermName(start, end).toTypeName

def concat(name: Name)(using Context): SimpleName = name match
case name: SimpleName =>
val len = s.length + name.length
var chars = ctx.base.sharedCharArray(len)
s.getChars(0, s.length, chars, 0)
if name.length != 0 then name.getChars(0, name.length, chars, s.length)
termName(chars, 0, len)
case name: TypeName => s.concat(name.toTermName)
case _ => termName(s.concat(name.toString))
end extension

/** Implements a findSymbol method on iterators of Symbols that
* works like find but avoids Option, replacing None with NoSymbol.
*/
Expand Down
7 changes: 5 additions & 2 deletions compiler/src/dotty/tools/dotc/core/NameKinds.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import NameOps._
import StdNames._
import NameTags._
import Contexts._
import Decorators._
import collection.mutable

import scala.annotation.internal.sharable
Expand Down Expand Up @@ -208,6 +209,8 @@ object NameKinds {
extends NumberedNameKind(UNIQUE, s"Unique $separator") {
override def definesNewName: Boolean = true

val separatorName = separator.toTermName

def mkString(underlying: TermName, info: ThisInfo): String = {
val safePrefix = str.sanitize(underlying.toString) + separator
safePrefix + info.num
Expand All @@ -226,10 +229,10 @@ object NameKinds {

/** An extractor for unique names of arbitrary kind */
object AnyUniqueName {
def unapply(name: DerivedName): Option[(TermName, String, Int)] = name match {
def unapply(name: DerivedName): Option[(TermName, TermName, Int)] = name match {
case DerivedName(qual, info: NumberedInfo) =>
info.kind match {
case unique: UniqueNameKind => Some((qual, unique.separator, info.num))
case unique: UniqueNameKind => Some((qual, unique.separatorName, info.num))
case _ => None
}
case _ => None
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/core/NameOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import scala.internal.Chars
import Chars.isOperatorPart
import Definitions._
import nme._
import Decorators.concat

object NameOps {

Expand Down Expand Up @@ -138,7 +139,7 @@ object NameOps {
case _ => false

/** Add an `extension_` in front of this name */
def toExtensionName = termName("extension_" ++ name.toString)
def toExtensionName(using Context): SimpleName = "extension_".concat(name)

/** Drop `extension_` in front of this name, if it has this prefix */
def dropExtension = name match
Expand Down
7 changes: 7 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Names.scala
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,13 @@ object Names {
def head: Char = apply(0)
def last: Char = apply(length - 1)

/** Copy character slice (from until end) to character array starting at `dstStart`.
* @pre Destination must have enough space to hold all characters of this name.
*/
def getChars(from: Int, end: Int, dst: Array[Char], dstStart: Int): Unit =
assert(0 <= from && from <= end && end <= length)
Array.copy(chrs, start + from, dst, dstStart, end - from)

override def asSimpleName: SimpleName = this
override def toSimpleName: SimpleName = this
override final def mangle: SimpleName = encode
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/tasty/NameBuffer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class NameBuffer extends TastyBuffer(10000) {
case AnyQualifiedName(prefix, name) =>
nameIndex(prefix); nameIndex(name)
case AnyUniqueName(original, separator, num) =>
nameIndex(separator.toTermName)
nameIndex(separator)
if (!original.isEmpty) nameIndex(original)
case DerivedName(original, _) =>
nameIndex(original)
Expand Down Expand Up @@ -82,7 +82,7 @@ class NameBuffer extends TastyBuffer(10000) {
withLength { writeNameRef(prefix); writeNameRef(name) }
case AnyUniqueName(original, separator, num) =>
withLength {
writeNameRef(separator.toTermName)
writeNameRef(separator)
writeNat(num)
if (!original.isEmpty) writeNameRef(original)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ class PositionPickler(
lastIndex = index
lastSpan = span

pickledIndices += index
pickledIndices.addOne(index)
// Note `+=` boxes since it is a generic @inline function in `SetOps`
// that forwards to the specialized `addOne` in `BitSet`. Since the
// current backend does not implement `@inline` we are missing the
// specialization.
}

def pickleSource(source: SourceFile): Unit = {
Expand Down
54 changes: 9 additions & 45 deletions compiler/src/dotty/tools/dotc/parsing/Scanners.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import core.StdNames._, core.Comments._
import util.SourceFile
import java.lang.Character.isDigit
import scala.internal.Chars._
import util.SourcePosition
import util.{SourcePosition, CharBuffer}
import util.Spans.Span
import config.Config
import config.Printers.lexical
Expand All @@ -21,38 +21,6 @@ import config.Feature.migrateTo3
import config.SourceVersion._
import reporting.Message

object Cbufs {
import java.lang.StringBuilder

private final val TargetCapacity = 256

opaque type Cbuf = StringBuilder
object Cbuf:
def apply(): Cbuf = new StringBuilder(TargetCapacity)

extension (buf: Cbuf):
def clear(): Unit = {
if buf.capacity() > TargetCapacity then
buf.setLength(TargetCapacity)
buf.trimToSize()
end if
buf.setLength(0)
}
def toCharArray: Array[Char] = {
val n = buf.length()
val res = new Array[Char](n)
buf.getChars(0, n, res, 0)
res
}
def append(c: Char): buf.type = { buf.append(c) ; buf }
def isEmpty: Boolean = buf.length() == 0
def length: Int = buf.length()
def last: Char = buf.charAt(buf.length() - 1)
end extension
}

import Cbufs._

object Scanners {

/** Offset into source character array */
Expand Down Expand Up @@ -142,22 +110,16 @@ object Scanners {

/** A character buffer for literals
*/
protected val litBuf = Cbuf()
protected val litBuf = CharBuffer()

/** append Unicode character to "litBuf" buffer
*/
protected def putChar(c: Char): Unit = litBuf.append(c)

/** Return buffer contents and clear */
def flushBuf(buf: Cbuf): String = {
val str = buf.toString
buf.clear()
str
}

/** Clear buffer and set name and token */
def finishNamed(idtoken: Token = IDENTIFIER, target: TokenData = this): Unit = {
target.name = termName(flushBuf(litBuf))
target.name = termName(litBuf.chars, 0, litBuf.length)
litBuf.clear()
target.token = idtoken
if (idtoken == IDENTIFIER)
target.token = toToken(target.name)
Expand All @@ -168,7 +130,8 @@ object Scanners {

/** Clear buffer and set string */
def setStrVal(): Unit =
strVal = flushBuf(litBuf)
strVal = litBuf.toString
litBuf.clear()

@inline def isNumberSeparator(c: Char): Boolean = c == '_'

Expand Down Expand Up @@ -241,7 +204,7 @@ object Scanners {
def getDocComment(pos: Int): Option[Comment] = docstringMap.get(pos)

/** A buffer for comments */
private val commentBuf = Cbuf()
private val commentBuf = CharBuffer()

private def handleMigration(keyword: Token): Token =
if keyword == ERASED && !ctx.settings.YerasedTerms.value then IDENTIFIER
Expand Down Expand Up @@ -888,7 +851,8 @@ object Scanners {
def finishComment(): Boolean = {
if (keepComments) {
val pos = Span(start, charOffset - 1, start)
val comment = Comment(pos, flushBuf(commentBuf))
val comment = Comment(pos, commentBuf.toString)
commentBuf.clear()
commentPosBuf += pos

if (comment.isDocComment)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/ElimOpaque.scala
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class ElimOpaque extends MiniPhase with DenotTransformer {
if sym == defn.Any_== || sym == defn.Any_!= then
tree match
case Apply(Select(receiver, name: TermName), args)
if atPhase(thisPhase)(receiver.tpe.widen.typeSymbol.isOpaqueAlias) =>
if atPhase(thisPhase)(receiver.tpe.widen.dealias.typeSymbol.isOpaqueAlias) =>
applyOverloaded(receiver, name, args, Nil, defn.BooleanType)
case _ =>
tree
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Deriving.scala
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ trait Deriving {
* @param reportErrors Report an error if an instance with the same name exists already
*/
private def addDerivedInstance(clsName: Name, info: Type, pos: SrcPos): Unit = {
val instanceName = s"derived$$$clsName".toTermName
val instanceName = "derived$".concat(clsName)
if (ctx.denotNamed(instanceName).exists)
report.error(i"duplicate type class derivation for $clsName", pos)
else
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ object ProtoTypes {
extends CachedProxyType with ProtoType with ValueTypeOrProto {

private var myExtensionName: TermName = null
def extensionName: TermName =
def extensionName(using Context): TermName =
if myExtensionName == null then myExtensionName = name.toExtensionName
myExtensionName

Expand Down
28 changes: 28 additions & 0 deletions compiler/src/dotty/tools/dotc/util/CharBuffer.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package dotty.tools
package dotc
package util

/** A character buffer that exposes the internal array for reading.
* That way we can avoid copying when converting to names.
*/
class CharBuffer(initialSize: Int = 1024):
private var cs: Array[Char] = new Array[Char](initialSize)
private var len: Int = 0

def append(ch: Char): Unit =
if len == cs.length then
val cs1 = new Array[Char](len * 2)
Array.copy(cs, 0, cs1, 0, len)
cs = cs1
cs(len) = ch
len += 1

def chars = cs
def length = len
def isEmpty: Boolean = len == 0
def last: Char = cs(len - 1)
def clear(): Unit = len = 0

override def toString = String(cs, 0, len)