Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c23e077
Remove unnecessary required... forwarders from DottyBackendInterface
SolalPirelli Mar 24, 2026
f9a3fa0
Remove unnecessary trait and clinit detection abstractions
SolalPirelli Mar 24, 2026
ba7acd7
Move java... extensions to Denotation, they are used outside of the j…
SolalPirelli Mar 24, 2026
1cd6ece
Rename remainder of DottyBackendInterface to SymbolUtils
SolalPirelli Mar 24, 2026
55ac342
Remove unnecessary Primitives wrapper
SolalPirelli Mar 24, 2026
4885510
DottyPrimitives -> ScalaPrimitives, move next to ScalaPrimitiveOps
SolalPirelli Mar 24, 2026
0a4e2b1
Move DottyBytecodeTest since it's used beyond JVM, use new wildcard i…
SolalPirelli Mar 25, 2026
daa89c3
Move min/max target versions to where they're used, avoid a jvm refer…
SolalPirelli Mar 25, 2026
b97c0c8
Encapsulate and rename AsmUtils to TraceUtils
SolalPirelli Mar 25, 2026
9758834
Inline some trivial BackendReporting helpers
SolalPirelli Mar 25, 2026
1582f62
Remove unneeded BackendReporting abstraction
SolalPirelli Mar 25, 2026
3655242
Add note for the future
SolalPirelli Mar 25, 2026
32d3f20
Grrrr
SolalPirelli Mar 25, 2026
04add0e
PR feedback: bring back methodSignature helper, but move it to Backen…
SolalPirelli Mar 27, 2026
1cbb039
PR feedback: Make TestOp an enum
SolalPirelli Mar 27, 2026
643b40f
PR feedback: make TraceUtils methods public
SolalPirelli Mar 27, 2026
9447c47
Remove accidental dupes of BackendUtils and InlinerHeuristics
SolalPirelli Mar 31, 2026
c3823a6
Remove unnecessary uses of Context in PPFA
SolalPirelli Mar 31, 2026
34672b7
Remove Context from opt/, InlineInfoLoader no longer compiles, prep f…
SolalPirelli Mar 31, 2026
46cadf6
Compute InlineInfo eagerly again to not need Context in opt/
SolalPirelli Mar 31, 2026
98676de
Remove duplicate cache
SolalPirelli Mar 31, 2026
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
@@ -1,16 +1,15 @@
package dotty.tools
package backend.jvm

import dotc.ast.Trees.Select
import dotc.ast.tpd.*
import dotc.core.*
import Contexts.*
import Names.TermName, StdNames.*
import Types.{JavaArrayType, UnspecifiedErrorType, Type}
import Symbols.{Symbol, NoSymbol}
import Decorators.em
import dotc.report
import dotc.util.ReadOnlyMap
package dotty.tools.backend

import dotty.tools.dotc.core.Contexts.*
import dotty.tools.dotc.core.Decorators.em
import dotty.tools.dotc.core.Names.TermName
import dotty.tools.dotc.core.StdNames.*
import dotty.tools.dotc.core.Types.{JavaArrayType, Type, UnspecifiedErrorType}
import dotty.tools.dotc.core.Symbols.{MutableSymbolMap, NoSymbol, Symbol, defn}
import dotty.tools.dotc.report
import dotty.tools.dotc.util.ReadOnlyMap
import dotty.tools.dotc.ast.Trees.Select
import dotty.tools.dotc.ast.tpd.*

import scala.annotation.threadUnsafe

Expand All @@ -31,7 +30,7 @@ import scala.annotation.threadUnsafe
*
* Inspired from the `scalac` compiler.
*/
class DottyPrimitives(ictx: Context) {
class ScalaPrimitives(ictx: Context) {
import dotty.tools.backend.ScalaPrimitivesOps.*

@threadUnsafe private lazy val primitives: ReadOnlyMap[Symbol, Int] = init
Expand All @@ -53,7 +52,6 @@ class DottyPrimitives(ictx: Context) {
def getPrimitive(app: Apply, tpe: Type): Int = {
given Context = ictx
val fun = app.fun.symbol
val defn = ctx.definitions
val code = app.fun match {
case Select(_, nme.primitive.arrayLength) =>
LENGTH
Expand Down Expand Up @@ -122,9 +120,7 @@ class DottyPrimitives(ictx: Context) {
private def init: ReadOnlyMap[Symbol, Int] = {

given Context = ictx

import Symbols.defn
val primitives = Symbols.MutableSymbolMap[Int](512)
val primitives = MutableSymbolMap[Int](512)

/** Add a primitive operation to the map */
def addPrimitive(s: Symbol, code: Int): Unit = {
Expand Down Expand Up @@ -407,7 +403,7 @@ class DottyPrimitives(ictx: Context) {
|| (fun.symbol == NoSymbol // the only trees that do not have a symbol assigned are array.{update,select,length,clone}}
&& {
fun match
case Select(_, StdNames.nme.clone_) => false // but array.clone is NOT a primitive op.
case Select(_, nme.clone_) => false // but array.clone is NOT a primitive op.
case _ => true
})
}
4 changes: 1 addition & 3 deletions compiler/src/dotty/tools/backend/ScalaPrimitivesOps.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package dotty.tools
package backend

object ScalaPrimitivesOps extends ScalaPrimitivesOps

class ScalaPrimitivesOps {
object ScalaPrimitivesOps {
// Arithmetic unary operations
inline val POS = 1 // +x
inline val NEG = 2 // -x
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/backend/jvm/BCodeAsmCommon.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import dotty.tools.dotc.core.Flags.*
import dotty.tools.dotc.core.Symbols.*
import dotty.tools.dotc.report

import DottyBackendInterface.symExtensions
import SymbolUtils.given

/**
* Code shared between GenBCode and GenASM that depends on types defined in
Expand Down
46 changes: 21 additions & 25 deletions compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import dotty.tools.dotc.report
import dotty.tools.dotc.ast.Trees.SyntheticUnit
import dotty.tools.dotc.ast.Positioned
import tpd.*
import DottyBackendInterface.symExtensions
import SymbolUtils.given
import dotty.tools.dotc.util.SrcPos

/*
Expand All @@ -34,15 +34,13 @@ import dotty.tools.dotc.util.SrcPos
* @version 1.0
*
*/
trait BCodeBodyBuilder(val primitives: DottyPrimitives)(using ctx: Context) extends BCodeSkelBuilder {
trait BCodeBodyBuilder(val primitives: ScalaPrimitives)(using ctx: Context) extends BCodeSkelBuilder {

/*
* Functionality to build the body of ASM MethodNode, except for `synchronized` and `try` expressions.
*/
abstract class PlainBodyBuilder(cunit: CompilationUnit) extends PlainSkelBuilder(cunit) {

import Primitives.TestOp

private object DesugaredSelect {
private val desugared = new java.util.IdentityHashMap[Type, tpd.Select]

Expand Down Expand Up @@ -1516,8 +1514,7 @@ trait BCodeBodyBuilder(val primitives: DottyPrimitives)(using ctx: Context) exte
} else if (tk.isRef) { // REFERENCE(_) | ARRAY(_)
bc.emitIF_ACMP(op, success)
} else {
import Primitives.*
def useCmpG = if (negated) op == GT || op == GE else op == LT || op == LE
def useCmpG = if (negated) op == TestOp.GT || op == TestOp.GE else op == TestOp.LT || op == TestOp.LE
(tk: @unchecked) match {
case LONG => emit(asm.Opcodes.LCMP)
case FLOAT => emit(if (useCmpG) asm.Opcodes.FCMPG else asm.Opcodes.FCMPL)
Expand All @@ -1531,18 +1528,17 @@ trait BCodeBodyBuilder(val primitives: DottyPrimitives)(using ctx: Context) exte

/* Emits code to compare (and consume) stack-top and zero using the 'op' operator */
private def genCZJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType, targetIfNoJump: asm.Label, negated: Boolean = false): Unit = {
import Primitives.*
if (targetIfNoJump == success) genCZJUMP(failure, success, op.negate(), tk, targetIfNoJump, negated = !negated)
else {
if (tk.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
bc.emitIF(op, success)
} else if (tk.isRef) { // REFERENCE(_) | ARRAY(_)
(op: @unchecked) match { // references are only compared with EQ and NE
case EQ => bc.emitIFNULL( success)
case NE => bc.emitIFNONNULL(success)
case TestOp.EQ => bc.emitIFNULL( success)
case TestOp.NE => bc.emitIFNONNULL(success)
}
} else {
def useCmpG = if (negated) op == GT || op == GE else op == LT || op == LE
def useCmpG = if (negated) op == TestOp.GT || op == TestOp.GE else op == TestOp.LT || op == TestOp.LE
(tk: @unchecked) match {
case LONG =>
emit(asm.Opcodes.LCONST_0)
Expand All @@ -1561,14 +1557,14 @@ trait BCodeBodyBuilder(val primitives: DottyPrimitives)(using ctx: Context) exte
}

def testOpForPrimitive(primitiveCode: Int) = (primitiveCode: @switch) match {
case ScalaPrimitivesOps.ID => Primitives.EQ
case ScalaPrimitivesOps.NI => Primitives.NE
case ScalaPrimitivesOps.EQ => Primitives.EQ
case ScalaPrimitivesOps.NE => Primitives.NE
case ScalaPrimitivesOps.LT => Primitives.LT
case ScalaPrimitivesOps.LE => Primitives.LE
case ScalaPrimitivesOps.GT => Primitives.GT
case ScalaPrimitivesOps.GE => Primitives.GE
case ScalaPrimitivesOps.ID => TestOp.EQ
case ScalaPrimitivesOps.NI => TestOp.NE
case ScalaPrimitivesOps.EQ => TestOp.EQ
case ScalaPrimitivesOps.NE => TestOp.NE
case ScalaPrimitivesOps.LT => TestOp.LT
case ScalaPrimitivesOps.LE => TestOp.LE
case ScalaPrimitivesOps.GT => TestOp.GT
case ScalaPrimitivesOps.GE => TestOp.GE
}

/*
Expand Down Expand Up @@ -1601,7 +1597,7 @@ trait BCodeBodyBuilder(val primitives: DottyPrimitives)(using ctx: Context) exte

def loadAndTestBoolean() = {
genLoad(tree, BOOL)
genCZJUMP(success, failure, Primitives.NE, BOOL, targetIfNoJump)
genCZJUMP(success, failure, TestOp.NE, BOOL, targetIfNoJump)
}

lineNumber(tree)
Expand Down Expand Up @@ -1713,33 +1709,33 @@ trait BCodeBodyBuilder(val primitives: DottyPrimitives)(using ctx: Context) exte
genLoad(r, ts.ObjectRef)
stack.pop()
genCallMethod(equalsMethod, InvokeStyle.Static)
genCZJUMP(success, failure, Primitives.NE, BOOL, targetIfNoJump)
genCZJUMP(success, failure, TestOp.NE, BOOL, targetIfNoJump)
}
else {
if (isNull(l)) {
// null == expr -> expr eq null
genLoad(r, ts.ObjectRef)
genCZJUMP(success, failure, Primitives.EQ, ts.ObjectRef, targetIfNoJump)
genCZJUMP(success, failure, TestOp.EQ, ts.ObjectRef, targetIfNoJump)
} else if (isNull(r)) {
// expr == null -> expr eq null
genLoad(l, ts.ObjectRef)
genCZJUMP(success, failure, Primitives.EQ, ts.ObjectRef, targetIfNoJump)
genCZJUMP(success, failure, TestOp.EQ, ts.ObjectRef, targetIfNoJump)
} else if (isNonNullExpr(l)) {
// SI-7852 Avoid null check if L is statically non-null.
genLoad(l, ts.ObjectRef)
stack.push(ts.ObjectRef)
genLoad(r, ts.ObjectRef)
stack.pop()
genCallMethod(defn.Any_equals, InvokeStyle.Virtual)
genCZJUMP(success, failure, Primitives.NE, BOOL, targetIfNoJump)
genCZJUMP(success, failure, TestOp.NE, BOOL, targetIfNoJump)
} else {
// l == r -> Objects.equals(l, r)
genLoad(l, ts.ObjectRef)
stack.push(ts.ObjectRef)
genLoad(r, ts.ObjectRef)
stack.pop()
genCallMethod(defn.Objects_equals, InvokeStyle.Static)
genCZJUMP(success, failure, Primitives.NE, BOOL, targetIfNoJump)
genCZJUMP(success, failure, TestOp.NE, BOOL, targetIfNoJump)
}
}
}
Expand Down Expand Up @@ -1854,7 +1850,7 @@ trait BCodeBodyBuilder(val primitives: DottyPrimitives)(using ctx: Context) exte
* create for Java-defined classes as well as for Java annotations
* which we represent as classes.
*/
private def isEmittedInterface(sym: Symbol): Boolean = sym.isInterface ||
private def isEmittedInterface(sym: Symbol): Boolean = sym.is(Trait) ||
sym.is(JavaDefined) && (toDenot(sym).isAnnotation || sym.is(ModuleClass) && (sym.companionClass.is(PureInterface)) || sym.companionClass.is(Trait))


Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import dotty.tools.dotc.core.NameKinds.ExpandedName
import dotty.tools.dotc.core.Signature
import dotty.tools.dotc.core.StdNames.*
import dotty.tools.dotc.core.NameKinds
import dotty.tools.dotc.core.Symbols.{requiredClass => _, *}
import dotty.tools.dotc.core.Symbols.*
import dotty.tools.dotc.core.Types
import dotty.tools.dotc.core.Types.*
import dotty.tools.dotc.core.TypeErasure
Expand All @@ -36,7 +36,7 @@ import dotty.tools.io.AbstractFile
import dotty.tools.dotc.report

import tpd.*
import DottyBackendInterface.{*, given}
import SymbolUtils.given

/*
* Encapsulates functionality to convert Scala AST Trees into ASM ClassNodes.
Expand Down
5 changes: 2 additions & 3 deletions compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import scala.language.unsafeNulls

import scala.tools.asm
import scala.annotation.switch
import Primitives.{NE, EQ, TestOp}
import scala.tools.asm.tree.MethodInsnNode
import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.report
Expand Down Expand Up @@ -372,8 +371,8 @@ trait BCodeIdiomatic(using Context) {
final def emitIF_ICMP(cond: TestOp, label: asm.Label): Unit = { jmethod.visitJumpInsn(cond.opcodeIFICMP(), label) }
// can-multi-thread
final def emitIF_ACMP(cond: TestOp, label: asm.Label): Unit = {
assert((cond == EQ) || (cond == NE), cond)
val opc = if (cond == EQ) Opcodes.IF_ACMPEQ else Opcodes.IF_ACMPNE
assert((cond == TestOp.EQ) || (cond == TestOp.NE), cond)
val opc = if (cond == TestOp.EQ) Opcodes.IF_ACMPEQ else Opcodes.IF_ACMPNE
jmethod.visitJumpInsn(opc, label)
}
// can-multi-thread
Expand Down
26 changes: 12 additions & 14 deletions compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ import dotty.tools.dotc.core.Flags.*
import dotty.tools.dotc.core.StdNames.*
import dotty.tools.dotc.core.NameKinds.*
import dotty.tools.dotc.core.Names.TermName
import dotty.tools.dotc.core.Symbols.{requiredClass => _, *}
import dotty.tools.dotc.core.Symbols.*
import dotty.tools.dotc.core.Types.*
import dotty.tools.dotc.core.Contexts.*
import dotty.tools.dotc.util.Spans.*
import dotty.tools.dotc.report

import DottyBackendInterface.{symExtensions, *}
import SymbolUtils.given
import dotty.tools.dotc.core.NameOps.isStaticConstructorName
import tpd.*

/*
Expand Down Expand Up @@ -181,7 +181,7 @@ trait BCodeSkelBuilder(using ctx: Context) extends BCodeHelpers {
val cd = if (isCZStaticModule) {
// Move statements from the primary constructor following the superclass constructor call to
// a newly synthesised tree representing the "<clinit>", which also assigns the MODULE$ field.
// Because the assigments to both the module instance fields, and the fields of the module itself
// Because the assignments to both the module instance fields, and the fields of the module itself
// are in the <clinit>, these fields can be static + final.

// Should we do this transformation earlier, say in Constructors? Or would that just cause
Expand Down Expand Up @@ -211,7 +211,7 @@ trait BCodeSkelBuilder(using ctx: Context) extends BCodeHelpers {
f.setFlag(JavaStatic)
}

val (clinits, body) = impl.body.partition(stat => stat.isInstanceOf[DefDef] && stat.symbol.isStaticConstructor)
val (clinits, body) = impl.body.partition(stat => stat.isInstanceOf[DefDef] && stat.symbol.name.isStaticConstructorName)

val (uptoSuperStats, remainingConstrStats) = splitAtSuper(impl.constr.rhs.asInstanceOf[Block].stats)
val clInitSymbol: TermSymbol =
Expand Down Expand Up @@ -267,7 +267,7 @@ trait BCodeSkelBuilder(using ctx: Context) extends BCodeHelpers {
cpy.TypeDef(cd0)(rhs = impl2)
} else cd0

val hasStaticCtor = isCZStaticModule || cd.symbol.info.decls.exists(_.isStaticConstructor)
val hasStaticCtor = isCZStaticModule || cd.symbol.info.decls.exists(_.name.isStaticConstructorName)
if (!hasStaticCtor && isCZParcelable) fabricateStaticInitAndroid()

val optSerial: Option[Long] =
Expand All @@ -290,8 +290,7 @@ trait BCodeSkelBuilder(using ctx: Context) extends BCodeHelpers {
// This needs to wait until now since it uses `superCallTargets` which is populating while emitting the class body
initJClass(cnode)

if (AsmUtils.traceClassEnabled && cnode.name.contains(AsmUtils.traceClassPattern))
AsmUtils.traceClass(cnode)
TraceUtils.traceClassIfRequested(cnode)

assert(cd.symbol == claszSymbol, "Someone messed up BCodePhase.claszSymbol during genPlainClass().")

Expand Down Expand Up @@ -427,7 +426,7 @@ trait BCodeSkelBuilder(using ctx: Context) extends BCodeHelpers {
val javagensig = getGenericSignature(f, claszSymbol)
val flags = javaFieldFlags(f)

assert(!f.isStaticMember || !claszSymbol.isInterface || !f.is(Mutable),
assert(!f.isStaticMember || !claszSymbol.is(Trait) || !f.is(Mutable),
s"interface $claszSymbol cannot have non-final static field $f")

val jfield = new asm.tree.FieldNode(
Expand Down Expand Up @@ -712,7 +711,7 @@ trait BCodeSkelBuilder(using ctx: Context) extends BCodeHelpers {
*/
val sym = dd.symbol
val needsStaticImplMethod =
claszSymbol.isInterface && !dd.rhs.isEmpty && !sym.isPrivate && !sym.isStaticMember
claszSymbol.is(Trait) && !dd.rhs.isEmpty && !sym.isPrivate && !sym.isStaticMember
if needsStaticImplMethod then
if sym.name == nme.TRAIT_CONSTRUCTOR then
genTraitConstructorDefDef(dd)
Expand Down Expand Up @@ -845,7 +844,7 @@ trait BCodeSkelBuilder(using ctx: Context) extends BCodeHelpers {
methSymbol = dd.symbol
jMethodName = methSymbol.javaSimpleName
returnType = ts.asmMethodType(methSymbol).returnType
isMethSymStaticCtor = methSymbol.isStaticConstructor
isMethSymStaticCtor = methSymbol.name.isStaticConstructorName

resetMethodBookkeeping(dd)

Expand All @@ -868,7 +867,7 @@ trait BCodeSkelBuilder(using ctx: Context) extends BCodeHelpers {
}

val isNative = methSymbol.hasAnnotation(NativeAttr)
val isAbstractMethod = (methSymbol.is(Deferred) || (methSymbol.owner.isInterface && ((methSymbol.is(Deferred)) || methSymbol.isClassConstructor)))
val isAbstractMethod = (methSymbol.is(Deferred) || (methSymbol.owner.is(Trait) && ((methSymbol.is(Deferred)) || methSymbol.isClassConstructor)))
val flags =
import GenBCodeOps.addFlagIf
BCodeUtils.javaFlags(methSymbol)
Expand Down Expand Up @@ -953,8 +952,7 @@ trait BCodeSkelBuilder(using ctx: Context) extends BCodeHelpers {
// The only non-instruction nodes to be found are LabelNode and LineNumberNode.
}

if (AsmUtils.traceMethodEnabled && mnode.name.contains(AsmUtils.traceMethodPattern))
AsmUtils.traceMethod(mnode)
TraceUtils.traceMethodIfRequested(mnode)

mnode = null
} // end of method genDefDef()
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/backend/jvm/BCodeUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ object BCodeUtils {
* and they would fail verification after lifted.
*/
final def javaFlags(sym: Symbol)(using Context): Int = {
import DottyBackendInterface.symExtensions
import SymbolUtils.given

// Classes are always emitted as public. This matches the behavior of Scala 2
// and is necessary for object deserialization to work properly, otherwise
Expand All @@ -497,7 +497,7 @@ object BCodeUtils {
0 .addFlagIf(privateFlag, ACC_PRIVATE)
.addFlagIf(!privateFlag, ACC_PUBLIC)
.addFlagIf(sym.is(Deferred) || sym.isOneOf(AbstractOrTrait), ACC_ABSTRACT)
.addFlagIf(sym.isInterface, ACC_INTERFACE)
.addFlagIf(sym.is(Trait), ACC_INTERFACE)
.addFlagIf(finalFlag
// Primitives are "abstract final" to prohibit instantiation
// without having to provide any implementations, but that is an
Expand All @@ -509,7 +509,7 @@ object BCodeUtils {
.addFlagIf(sym.isStaticMember && !sym.isClass, ACC_STATIC)
.addFlagIf(sym.is(Bridge), ACC_BRIDGE | ACC_SYNTHETIC)
.addFlagIf(sym.is(Artifact), ACC_SYNTHETIC)
.addFlagIf(sym.isClass && !sym.isInterface, ACC_SUPER)
.addFlagIf(sym.isClass && !sym.is(Trait), ACC_SUPER)
.addFlagIf(sym.isAllOf(JavaEnum), ACC_ENUM)
.addFlagIf(sym.is(JavaVarargs), ACC_VARARGS)
.addFlagIf(sym.is(Synchronized), ACC_SYNCHRONIZED)
Expand Down
Loading
Loading