Skip to content

Commit 3948ecc

Browse files
authored
Port JVM backend refactor from Scala 2 (#15322)
This PR ports JVM backend refactor from Scala 2 as part of the #14912 thread. It squashes changes based on the PRs: - scala/scala#6012 - scala/scala#6057 The last refactor introducing backend parallelism scala/scala#6124 is left for later.
2 parents a569057 + fa96482 commit 3948ecc

18 files changed

+937
-1135
lines changed

Diff for: compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1297,7 +1297,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
12971297
.toList
12981298

12991299
// `StringConcatFactory` only got added in JDK 9, so use `StringBuilder` for lower
1300-
if (classfileVersion < asm.Opcodes.V9) {
1300+
if (backendUtils.classfileVersion < asm.Opcodes.V9) {
13011301

13021302
// Estimate capacity needed for the string builder
13031303
val approxBuilderSize = concatArguments.view.map {

Diff for: compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala

+6-89
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,19 @@ import dotty.tools.backend.jvm.DottyBackendInterface.symExtensions
4242
* @version 1.0
4343
*
4444
*/
45-
trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
45+
trait BCodeHelpers extends BCodeIdiomatic {
4646
// for some reason singleton types aren't allowed in constructor calls. will need several casts in code to enforce
47-
4847
//import global._
49-
//import bTypes._
50-
//import coreBTypes._
5148
import bTypes._
5249
import tpd._
5350
import coreBTypes._
5451
import int.{_, given}
5552
import DottyBackendInterface._
5653

54+
// We need to access GenBCode phase to get access to post-processor components.
55+
// At this point it should always be initialized already.
56+
protected lazy val backendUtils = genBCodePhase.asInstanceOf[GenBCode].postProcessor.backendUtils
57+
5758
def ScalaATTRName: String = "Scala"
5859
def ScalaSignatureATTRName: String = "ScalaSig"
5960

@@ -64,96 +65,12 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
6465

6566
val bCodeAsmCommon: BCodeAsmCommon[int.type] = new BCodeAsmCommon(int)
6667

67-
/*
68-
* must-single-thread
69-
*/
70-
def getFileForClassfile(base: AbstractFile, clsName: String, suffix: String): AbstractFile = {
71-
getFile(base, clsName, suffix)
72-
}
73-
74-
/*
75-
* must-single-thread
76-
*/
77-
def getOutFolder(csym: Symbol, cName: String): AbstractFile = {
78-
try {
79-
outputDirectory
80-
} catch {
81-
case ex: Throwable =>
82-
report.error(em"Couldn't create file for class $cName\n${ex.getMessage}", ctx.source.atSpan(csym.span))
83-
null
84-
}
85-
}
86-
8768
final def traitSuperAccessorName(sym: Symbol): String = {
8869
val nameString = sym.javaSimpleName.toString
8970
if (sym.name == nme.TRAIT_CONSTRUCTOR) nameString
9071
else nameString + "$"
9172
}
9273

93-
// -----------------------------------------------------------------------------------------
94-
// finding the least upper bound in agreement with the bytecode verifier (given two internal names handed by ASM)
95-
// Background:
96-
// http://gallium.inria.fr/~xleroy/publi/bytecode-verification-JAR.pdf
97-
// http://comments.gmane.org/gmane.comp.java.vm.languages/2293
98-
// https://issues.scala-lang.org/browse/SI-3872
99-
// -----------------------------------------------------------------------------------------
100-
101-
/* An `asm.ClassWriter` that uses `jvmWiseLUB()`
102-
* The internal name of the least common ancestor of the types given by inameA and inameB.
103-
* It's what ASM needs to know in order to compute stack map frames, http://asm.ow2.org/doc/developer-guide.html#controlflow
104-
*/
105-
final class CClassWriter(flags: Int) extends asm.ClassWriter(flags) {
106-
107-
/**
108-
* This method is thread-safe: it depends only on the BTypes component, which does not depend
109-
* on global. TODO @lry move to a different place where no global is in scope, on bTypes.
110-
*/
111-
override def getCommonSuperClass(inameA: String, inameB: String): String = {
112-
val a = classBTypeFromInternalName(inameA)
113-
val b = classBTypeFromInternalName(inameB)
114-
val lub = a.jvmWiseLUB(b)
115-
val lubName = lub.internalName
116-
assert(lubName != "scala/Any")
117-
lubName // ASM caches the answer during the lifetime of a ClassWriter. We outlive that. Not sure whether caching on our side would improve things.
118-
}
119-
}
120-
121-
/*
122-
* must-single-thread
123-
*/
124-
def initBytecodeWriter(): BytecodeWriter = {
125-
(None: Option[AbstractFile] /*getSingleOutput*/) match { // todo: implement
126-
case Some(f) if f.hasExtension("jar") =>
127-
new DirectToJarfileWriter(f.file)
128-
case _ =>
129-
factoryNonJarBytecodeWriter()
130-
}
131-
}
132-
133-
/*
134-
* Populates the InnerClasses JVM attribute with `refedInnerClasses`. See also the doc on inner
135-
* classes in BTypes.scala.
136-
*
137-
* `refedInnerClasses` may contain duplicates, need not contain the enclosing inner classes of
138-
* each inner class it lists (those are looked up and included).
139-
*
140-
* This method serializes in the InnerClasses JVM attribute in an appropriate order,
141-
* not necessarily that given by `refedInnerClasses`.
142-
*
143-
* can-multi-thread
144-
*/
145-
final def addInnerClasses(jclass: asm.ClassVisitor, declaredInnerClasses: List[ClassBType], refedInnerClasses: List[ClassBType]): Unit = {
146-
// sorting ensures nested classes are listed after their enclosing class thus satisfying the Eclipse Java compiler
147-
val allNestedClasses = new mutable.TreeSet[ClassBType]()(Ordering.by(_.internalName))
148-
allNestedClasses ++= declaredInnerClasses
149-
refedInnerClasses.foreach(allNestedClasses ++= _.enclosingNestedClassesChain)
150-
for nestedClass <- allNestedClasses
151-
do {
152-
// Extract the innerClassEntry - we know it exists, enclosingNestedClassesChain only returns nested classes.
153-
val Some(e) = nestedClass.innerClassAttributeEntry: @unchecked
154-
jclass.visitInnerClass(e.name, e.outerName, e.innerName, e.flags)
155-
}
156-
}
15774

15875
/*
15976
* can-multi-thread
@@ -680,7 +597,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
680597

681598
val mirrorClass = new asm.tree.ClassNode
682599
mirrorClass.visit(
683-
classfileVersion,
600+
backendUtils.classfileVersion,
684601
bType.info.flags,
685602
mirrorName,
686603
null /* no java-generic-signature */,

Diff for: compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala

+1-40
Original file line numberDiff line numberDiff line change
@@ -19,52 +19,13 @@ import dotty.tools.dotc.report
1919
*/
2020
trait BCodeIdiomatic {
2121
val int: DottyBackendInterface
22-
final lazy val bTypes = new BTypesFromSymbols[int.type](int)
22+
val bTypes: BTypesFromSymbols[int.type]
2323

2424
import int.{_, given}
2525
import bTypes._
2626
import coreBTypes._
2727

2828

29-
30-
lazy val target =
31-
val releaseValue = Option(ctx.settings.javaOutputVersion.value).filter(_.nonEmpty)
32-
val targetValue = Option(ctx.settings.XuncheckedJavaOutputVersion.value).filter(_.nonEmpty)
33-
val defaultTarget = "8"
34-
(releaseValue, targetValue) match
35-
case (Some(release), None) => release
36-
case (None, Some(target)) => target
37-
case (Some(release), Some(_)) =>
38-
report.warning(s"The value of ${ctx.settings.XuncheckedJavaOutputVersion.name} was overridden by ${ctx.settings.javaOutputVersion.name}")
39-
release
40-
case (None, None) => "8" // least supported version by default
41-
42-
43-
// Keep synchronized with `minTargetVersion` and `maxTargetVersion` in ScalaSettings
44-
lazy val classfileVersion: Int = target match {
45-
case "8" => asm.Opcodes.V1_8
46-
case "9" => asm.Opcodes.V9
47-
case "10" => asm.Opcodes.V10
48-
case "11" => asm.Opcodes.V11
49-
case "12" => asm.Opcodes.V12
50-
case "13" => asm.Opcodes.V13
51-
case "14" => asm.Opcodes.V14
52-
case "15" => asm.Opcodes.V15
53-
case "16" => asm.Opcodes.V16
54-
case "17" => asm.Opcodes.V17
55-
case "18" => asm.Opcodes.V18
56-
case "19" => asm.Opcodes.V19
57-
case "20" => asm.Opcodes.V20
58-
}
59-
60-
lazy val majorVersion: Int = (classfileVersion & 0xFF)
61-
lazy val emitStackMapFrame = (majorVersion >= 50)
62-
63-
val extraProc: Int =
64-
import GenBCodeOps.addFlagIf
65-
asm.ClassWriter.COMPUTE_MAXS
66-
.addFlagIf(emitStackMapFrame, asm.ClassWriter.COMPUTE_FRAMES)
67-
6829
lazy val JavaStringBuilderClassName = jlStringBuilderRef.internalName
6930

7031
val CLASS_CONSTRUCTOR_NAME = "<clinit>"

Diff for: compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ trait BCodeSkelBuilder extends BCodeHelpers {
271271
val flags = javaFlags(claszSymbol)
272272

273273
val thisSignature = getGenericSignature(claszSymbol, claszSymbol.owner)
274-
cnode.visit(classfileVersion, flags,
274+
cnode.visit(backendUtils.classfileVersion, flags,
275275
thisName, thisSignature,
276276
superClass, interfaceNames.toArray)
277277

Diff for: compiler/src/dotty/tools/backend/jvm/BTypes.scala

+13-5
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ import scala.tools.asm
1414
* This representation is immutable and independent of the compiler data structures, hence it can
1515
* be queried by concurrent threads.
1616
*/
17-
abstract class BTypes {
17+
abstract class BTypes { self =>
18+
val frontendAccess: PostProcessorFrontendAccess
19+
import frontendAccess.{frontendSynch}
1820

1921
val int: DottyBackendInterface
2022
import int.given
@@ -37,10 +39,7 @@ abstract class BTypes {
3739
*/
3840
def classBTypeFromInternalName(internalName: String) = classBTypeFromInternalNameMap(internalName)
3941

40-
// Some core BTypes are required here, in class BType, where no Global instance is available.
41-
// The Global is only available in the subclass BTypesFromSymbols. We cannot depend on the actual
42-
// implementation (CoreBTypesProxy) here because it has members that refer to global.Symbol.
43-
val coreBTypes: CoreBTypesProxyGlobalIndependent[this.type]
42+
val coreBTypes: CoreBTypes { val bTypes: self.type}
4443
import coreBTypes._
4544

4645
/**
@@ -862,3 +861,12 @@ abstract class BTypes {
862861
*/
863862
/*final*/ case class MethodNameAndType(name: String, methodType: MethodBType)
864863
}
864+
865+
object BTypes {
866+
/**
867+
* A marker for strings that represent class internal names.
868+
* Ideally the type would be incompatible with String, for example by making it a value class.
869+
* But that would create overhead in a Collection[InternalName].
870+
*/
871+
type InternalName = String
872+
}

Diff for: compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala

+8-35
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,14 @@ import dotty.tools.dotc.core.Symbols._
1414
import dotty.tools.dotc.core.Phases.Phase
1515
import dotty.tools.dotc.transform.SymUtils._
1616
import dotty.tools.dotc.core.StdNames
17+
import dotty.tools.dotc.core.Phases
1718

1819
/**
1920
* This class mainly contains the method classBTypeFromSymbol, which extracts the necessary
2021
* information from a symbol and its type to create the corresponding ClassBType. It requires
2122
* access to the compiler (global parameter).
22-
*
23-
* The mixin CoreBTypes defines core BTypes that are used in the backend. Building these BTypes
24-
* uses classBTypeFromSymbol, hence requires access to the compiler (global).
25-
*
26-
* BTypesFromSymbols extends BTypes because the implementation of BTypes requires access to some
27-
* of the core btypes. They are declared in BTypes as abstract members. Note that BTypes does
28-
* not have access to the compiler instance.
2923
*/
30-
class BTypesFromSymbols[I <: DottyBackendInterface](val int: I) extends BTypes {
24+
class BTypesFromSymbols[I <: DottyBackendInterface](val int: I, val frontendAccess: PostProcessorFrontendAccess) extends BTypes {
3125
import int.{_, given}
3226
import DottyBackendInterface.{symExtensions, _}
3327

@@ -37,39 +31,18 @@ class BTypesFromSymbols[I <: DottyBackendInterface](val int: I) extends BTypes {
3731
val bCodeAsmCommon: BCodeAsmCommon[int.type ] = new BCodeAsmCommon(int)
3832
import bCodeAsmCommon._
3933

40-
// Why the proxy, see documentation of class [[CoreBTypes]].
41-
val coreBTypes: CoreBTypesProxy[this.type] = new CoreBTypesProxy[this.type](this)
42-
import coreBTypes._
43-
44-
final def intializeCoreBTypes(): Unit = {
45-
coreBTypes.setBTypes(new CoreBTypes[this.type](this))
46-
}
47-
48-
private[this] val perRunCaches: Caches = new Caches {
49-
def newAnyRefMap[K <: AnyRef, V](): mutable.AnyRefMap[K, V] = new mutable.AnyRefMap[K, V]()
50-
def newWeakMap[K, V](): mutable.WeakHashMap[K, V] = new mutable.WeakHashMap[K, V]()
51-
def recordCache[T <: Clearable](cache: T): T = cache
52-
def newMap[K, V](): mutable.HashMap[K, V] = new mutable.HashMap[K, V]()
53-
def newSet[K](): mutable.Set[K] = new mutable.HashSet[K]
54-
}
55-
56-
// TODO remove abstraction
57-
private abstract class Caches {
58-
def recordCache[T <: Clearable](cache: T): T
59-
def newWeakMap[K, V](): collection.mutable.WeakHashMap[K, V]
60-
def newMap[K, V](): collection.mutable.HashMap[K, V]
61-
def newSet[K](): collection.mutable.Set[K]
62-
def newAnyRefMap[K <: AnyRef, V](): collection.mutable.AnyRefMap[K, V]
34+
val coreBTypes = new CoreBTypesFromSymbols[I]{
35+
val bTypes: BTypesFromSymbols.this.type = BTypesFromSymbols.this
6336
}
37+
import coreBTypes._
6438

65-
@threadUnsafe protected lazy val classBTypeFromInternalNameMap = {
66-
perRunCaches.recordCache(collection.concurrent.TrieMap.empty[String, ClassBType])
67-
}
39+
@threadUnsafe protected lazy val classBTypeFromInternalNameMap =
40+
collection.concurrent.TrieMap.empty[String, ClassBType]
6841

6942
/**
7043
* Cache for the method classBTypeFromSymbol.
7144
*/
72-
@threadUnsafe private lazy val convertedClasses = perRunCaches.newMap[Symbol, ClassBType]()
45+
@threadUnsafe private lazy val convertedClasses = collection.mutable.HashMap.empty[Symbol, ClassBType]
7346

7447
/**
7548
* The ClassBType for a class symbol `sym`.

0 commit comments

Comments
 (0)