8000 Enable the IR checker post optimizer with RT longs · scala-js/scala-js@c527a00 · GitHub
[go: up one dir, main page]

Skip to content

Commit c527a00

Browse files
committed
Enable the IR checker post optimizer with RT longs
The two commits preceding this commit are not strictly necessary. However, I felt they significantly help understanding of what the optimizer is doing.
1 parent a3fb06f commit c527a00

File tree

4 files changed

+47
-45
lines changed

4 files changed

+47
-45
lines changed

linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,6 @@ final class Refiner(config: CommonPhaseConfig, checkIR: Boolean) {
3434
private val analyzer =
3535
new Analyzer(config, initial = false, checkIR = checkIR, failOnError = true, irLoader)
3636

37-
/* TODO: Remove this and replace with `checkIR` once the optimizer generates
38-
* well-typed IR with runtime longs.
39-
*/
40-
private val shouldRunIRChecker = {
41-
val optimizerUsesRuntimeLong =
42-
!config.coreSpec.esFeatures.allowBigIntsForLongs &&
43-
!config.coreSpec.targetIsWebAssembly
44-
checkIR && !optimizerUsesRuntimeLong
45-
}
46-
4737
def refine(classDefs: Seq[(ClassDef, Version)],
4838
moduleInitializers: List[ModuleInitializer],
4939
symbolRequirements: SymbolRequirement, logger: Logger)(
@@ -77,7 +67,7 @@ final class Refiner(config: CommonPhaseConfig, checkIR: Boolean) {
7767
linkedTopLevelExports.flatten.toList, moduleInitializers, globalInfo)
7868
}
7969

80-
if (shouldRunIRChecker) {
70+
if (checkIR) {
8171
logger.time("Refiner: Check IR") {
8272
val errorCount = IRChecker.check(result, logger, postOptimizer = true)
8373
if (errorCount != 0) {

linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala

Lines changed: 18 additions & 25 deletions
8000
Original file line numberDiff line numberDiff line change
@@ -272,27 +272,8 @@ private[optimizer] abstract class OptimizerCore(
272272

273273
private val isSubclassFun = isSubclass _
274274

275-
private def isSubtype(lhs: Type, rhs: Type): Boolean = {
276-
assert(lhs != VoidType)
277-
assert(rhs != VoidType)
278-
279-
Types.isSubtype(lhs, rhs)(isSubclassFun) || {
280-
(lhs, rhs) match {
281-
case (LongType, ClassType(LongImpl.RuntimeLongClass, _)) =>
282-
true
283-
case (ClassType(BoxedLongClass, lhsNullable),
284-
ClassType(LongImpl.RuntimeLongClass, rhsNullable)) =>
285-
rhsNullable || !lhsNullable
286-
287-
case (ClassType(LongImpl.RuntimeLongClass, lhsNullable),
288-
ClassType(BoxedLongClass, rhsNullable)) =>
289-
rhsNullable || !lhsNullable
290-
291-
case _ =>
292-
false
293-
}
294-
}
295-
}
275+
private def isSubtype(lhs: Type, rhs: Type): Boolean =
276+
Types.isSubtype(lhs, rhs)(isSubclassFun)
296277

297278
/** Transforms a statement.
298279
*
@@ -781,10 +762,22 @@ private[optimizer] abstract class OptimizerCore(
781762
def addCaptureParam(newName: LocalName): LocalDef = {
782763
val newOriginalName = originalNameForFresh(paramName, originalName, newName)
783764

765+
val captureTpe = {
766+
/* Do not refine the capture type for longs:
767+
* The pretransform might be a stack allocated RuntimeLong.
768+
* We cannot (trivially) capture it in stack allocated form.
769+
* Therefore, we keep the primitive type and let finishTransformExpr
770+
* allocate a RuntimeLong.
771+
* TODO: Improve this and allocate two capture params for lo/hi?
772+
*/
773+
if (useRuntimeLong && paramDef.ptpe == LongType) RefinedType(LongType)
774+
else tcaptureValue.tpe
775+
}
776+
784777
val replacement = ReplaceWithVarRef(newName, newSimpleState(Unused))
785-
val localDef = LocalDef(tcaptureValue.tpe, mutable, replacement)
778+
val localDef = LocalDef(captureTpe, mutable, replacement)
786779
val localIdent = LocalIdent(newName)(ident.pos)
787-
val newParamDef = ParamDef(localIdent, newOriginalName, tcaptureValue.tpe.base, mutable)(paramDef.pos)
780+
val newParamDef = ParamDef(localIdent, newOriginalName, captureTpe.base, mutable)(paramDef.pos)
788781

789782
/* Note that the binding will never create a fresh name for a
790783
* ReplaceWithVarRef. So this will not put our name alignment at risk.
@@ -6452,8 +6445,8 @@ private[optimizer] object OptimizerCore {
64526445
private def createNewLong(lo: Tree, hi: Tree)(
64536446
implicit pos: Position): Tree = {
64546447

6455-
New(LongImpl.RuntimeLongClass, MethodIdent(LongImpl.initFromParts),
6456-
List(lo, hi))
6448+
makeCast(New(LongImpl.RuntimeLongClass, MethodIdent(LongImpl.initFromParts),
6449+
List(lo, hi)), LongType)
64576450
}
64586451

64596452
/** Tests whether `x + y` is valid without falling out of range. */

linker/shared/src/test/scala/org/scalajs/linker/IRCheckerTest.scala

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -420,18 +420,10 @@ object IRCheckerTest {
420420
moduleInitializers: List[ModuleInitializer],
421421
logger: Logger, postOptimizer: Boolean)(
422422
implicit ec: ExecutionContext): Future[Unit] = {
423-
val baseConfig = StandardConfig()
423+
val config = StandardConfig()
424424
.withCheckIR(true)
425425
.withOptimizer(false)
426426

427-
val config = {
428-
/* Disable RuntimeLongs to workaround the Refiner disabling IRChecks in this case.
429-
* TODO: Remove once we run IRChecks post optimizer all the time.
430-
*/
431-
if (postOptimizer) baseConfig.withESFeatures(_.withAllowBigIntsForLongs(true))
432-
else baseConfig
433-
}
434-
435427
val noSymbolRequirements = SymbolRequirement
436428
.factory("IRCheckerTest")
437429
.none()

linker/shared/src/test/scala/org/scalajs/linker/OptimizerTest.scala

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,33 @@ class OptimizerTest {
483483
}
484484
}
485485

486+
@Test
487+
def testLongCaptures(): AsyncResult = await {
488+
val calc = m("calc", Nil, LongRef)
489+
490+
val classDefs = Seq(
491+
classDef(
492+
MainTestClassName,
493+
kind = ClassKind.Class,
494+
superClass = Some(ObjectClass),
495+
methods = List(
496+
// @noinline static def calc(): Long = 1L
497+
MethodDef(EMF.withNamespace(PublicStatic), calc, NON, Nil,
498+
LongType, Some(LongLiteral(1)))(EOH.withNoinline(true), UNV),
499+
mainMethodDef(Block(
500+
VarDef("x", NON, LongType, mutable = false,
501+
ApplyStatic(EAF, MainTestClassName, calc, Nil)(LongType)),
502+
consoleLog(Closure(true, List(paramDef("y", LongType)), Nil, None,
503+
VarRef("y")(LongType), List(VarRef("x")(LongType))))
504+
))
505+
)
506+
)
507+
)
508+
509+
// Check it doesn't fail IRChecking.
510+
linkToModuleSet(classDefs, MainTestModuleInitializers)
511+
}
512+
486513
private def commonClassDefsForFieldRemovalTests(classInline: Boolean,
487514
witnessMutable: Boolean): Seq[ClassDef] = {
488515
val methodName = m("method", Nil, I)

0 commit comments

Comments
 (0)
0