Skip to content

Commit 949c704

Browse files
authored
Merge pull request #14890 from dotty-staging/codegen-destinations
Use explicit destinations in codegen to avoid uselessly jumping around.
2 parents 400427d + 4a2889f commit 949c704

File tree

4 files changed

+504
-136
lines changed

4 files changed

+504
-136
lines changed

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

Lines changed: 126 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -113,16 +113,6 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
113113
}
114114
}
115115

116-
def genThrow(expr: Tree): Unit = {
117-
val thrownKind = tpeTK(expr)
118-
// `throw null` is valid although scala.Null (as defined in src/libray-aux) isn't a subtype of Throwable.
119-
// Similarly for scala.Nothing (again, as defined in src/libray-aux).
120-
assert(thrownKind.isNullType || thrownKind.isNothingType || thrownKind.asClassBType.isSubtypeOf(ThrowableReference))
121-
genLoad(expr, thrownKind)
122-
lineNumber(expr)
123-
emit(asm.Opcodes.ATHROW) // ICode enters here into enterIgnoreMode, we'll rely instead on DCE at ClassNode level.
124-
}
125-
126116
/* Generate code for primitive arithmetic operations. */
127117
def genArithmeticOp(tree: Tree, code: Int): BType = tree match{
128118
case Apply(fun @ DesugaredSelect(larg, _), args) =>
@@ -211,7 +201,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
211201
generatedType
212202
}
213203

214-
def genLoadIf(tree: If, expectedType: BType): BType = tree match{
204+
def genLoadIfTo(tree: If, expectedType: BType, dest: LoadDestination): BType = tree match{
215205
case If(condp, thenp, elsep) =>
216206

217207
val success = new asm.Label
@@ -221,25 +211,37 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
221211
case Literal(value) if value.tag == UnitTag => false
222212
case _ => true
223213
})
224-
val postIf = if (hasElse) new asm.Label else failure
225214

226215
genCond(condp, success, failure, targetIfNoJump = success)
227216
markProgramPoint(success)
228217

229-
val thenKind = tpeTK(thenp)
230-
val elseKind = if (!hasElse) UNIT else tpeTK(elsep)
231-
def hasUnitBranch = (thenKind == UNIT || elseKind == UNIT) && expectedType == UNIT
232-
val resKind = if (hasUnitBranch) UNIT else tpeTK(tree)
233-
234-
genLoad(thenp, resKind)
235-
if (hasElse) { bc goTo postIf }
236-
markProgramPoint(failure)
237-
if (hasElse) {
238-
genLoad(elsep, resKind)
239-
markProgramPoint(postIf)
240-
}
241-
242-
resKind
218+
if dest == LoadDestination.FallThrough then
219+
if hasElse then
220+
val thenKind = tpeTK(thenp)
221+
val elseKind = tpeTK(elsep)
222+
def hasUnitBranch = (thenKind == UNIT || elseKind == UNIT) && expectedType == UNIT
223+
val resKind = if (hasUnitBranch) UNIT else tpeTK(tree)
224+
225+
val postIf = new asm.Label
226+
genLoadTo(thenp, resKind, LoadDestination.Jump(postIf))
227+
markProgramPoint(failure)
228+
genLoadTo(elsep, resKind, LoadDestination.FallThrough)
229+
markProgramPoint(postIf)
230+
resKind
231+
else
232+
genLoad(thenp, UNIT)
233+
markProgramPoint(failure)
234+
UNIT
235+
end if
236+
else
237+
genLoadTo(thenp, expectedType, dest)
238+
markProgramPoint(failure)
239+
if hasElse then
240+
genLoadTo(elsep, expectedType, dest)
241+
else
242+
genAdaptAndSendToDest(UNIT, expectedType, dest)
243+
expectedType
244+
end if
243245
}
244246

245247
def genPrimitiveOp(tree: Apply, expectedType: BType): BType = (tree: @unchecked) match {
@@ -285,8 +287,13 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
285287
}
286288

287289
/* Generate code for trees that produce values on the stack */
288-
def genLoad(tree: Tree, expectedType: BType): Unit = {
290+
def genLoad(tree: Tree, expectedType: BType): Unit =
291+
genLoadTo(tree, expectedType, LoadDestination.FallThrough)
292+
293+
/* Generate code for trees that produce values, sent to a given `LoadDestination`. */
294+
def genLoadTo(tree: Tree, expectedType: BType, dest: LoadDestination): Unit =
289295
var generatedType = expectedType
296+
var generatedDest = LoadDestination.FallThrough
290297

291298
lineNumber(tree)
292299

@@ -307,24 +314,29 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
307314
generatedType = UNIT
308315

309316
case t @ If(_, _, _) =>
310-
generatedType = genLoadIf(t, expectedType)
317+
generatedType = genLoadIfTo(t, expectedType, dest)
318+
generatedDest = dest
311319

312320
case t @ Labeled(_, _) =>
313-
generatedType = genLabeled(t)
321+
generatedType = genLabeledTo(t, expectedType, dest)
322+
generatedDest = dest
314323

315324
case r: Return =>
316325
genReturn(r)
317-
generatedType = expectedType
326+
generatedDest = LoadDestination.Return
318327

319328
case t @ WhileDo(_, _) =>
320-
generatedType = genWhileDo(t, expectedType)
329+
generatedDest = genWhileDo(t)
330+
generatedType = UNIT
321331

322332
case t @ Try(_, _, _) =>
323333
generatedType = genLoadTry(t)
324334

325335
case t: Apply if t.fun.symbol eq defn.throwMethod =>
326-
genThrow(t.args.head)
327-
generatedType = expectedType
336+
val thrownExpr = t.args.head
337+
val thrownKind = tpeTK(thrownExpr)
338+
genLoadTo(thrownExpr, thrownKind, LoadDestination.Throw)
339+
generatedDest = LoadDestination.Throw
328340

329341
case New(tpt) =>
330342
abort(s"Unexpected New(${tpt.tpe.showSummary()}/$tpt) reached GenBCode.\n" +
@@ -425,12 +437,18 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
425437

426438
case blck @ Block(stats, expr) =>
427439
if(stats.isEmpty)
428-
genLoad(expr, expectedType)
429-
else genBlock(blck, expectedType)
440+
genLoadTo(expr, expectedType, dest)
441+
else
442+
genBlockTo(blck, expectedType, dest)
443+
generatedDest = dest
430444

431-
case Typed(Super(_, _), _) => genLoad(tpd.This(claszSymbol.asClass), expectedType)
445+
case Typed(Super(_, _), _) =>
446+
genLoadTo(tpd.This(claszSymbol.asClass), expectedType, dest)
447+
generatedDest = dest
432448

433-
case Typed(expr, _) => genLoad(expr, expectedType)
449+
case Typed(expr, _) =>
450+
genLoadTo(expr, expectedType, dest)
451+
generatedDest = dest
434452

435453
case Assign(_, _) =>
436454
generatedType = UNIT
@@ -440,7 +458,8 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
440458
generatedType = genArrayValue(av)
441459

442460
case mtch @ Match(_, _) =>
443-
generatedType = genMatch(mtch)
461+
generatedType = genMatchTo(mtch, expectedType, dest)
462+
generatedDest = dest
444463

445464
case tpd.EmptyTree => if (expectedType != UNIT) { emitZeroOf(expectedType) }
446465

@@ -451,12 +470,29 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
451470
case _ => abort(s"Unexpected tree in genLoad: $tree/${tree.getClass} at: ${tree.span}")
452471
}
453472

454-
// emit conversion
455-
if (generatedType != expectedType) {
473+
// emit conversion and send to the right destination
474+
if generatedDest == LoadDestination.FallThrough then
475+
genAdaptAndSendToDest(generatedType, expectedType, dest)
476+
end genLoadTo
477+
478+
def genAdaptAndSendToDest(generatedType: BType, expectedType: BType, dest: LoadDestination): Unit =
479+
if generatedType != expectedType then
456480
adapt(generatedType, expectedType)
457-
}
458481

459-
} // end of GenBCode.genLoad()
482+
dest match
483+
case LoadDestination.FallThrough =>
484+
()
485+
case LoadDestination.Jump(label) =>
486+
bc goTo label
487+
case LoadDestination.Return =>
488+
bc emitRETURN returnType
489+
case LoadDestination.Throw =>
490+
val thrownType = expectedType
491+
// `throw null` is valid although scala.Null (as defined in src/libray-aux) isn't a subtype of Throwable.
492+
// Similarly for scala.Nothing (again, as defined in src/libray-aux).
493+
assert(thrownType.isNullType || thrownType.isNothingType || thrownType.asClassBType.isSubtypeOf(ThrowableReference))
494+
emit(asm.Opcodes.ATHROW)
495+
end genAdaptAndSendToDest
460496

461497
// ---------------- field load and store ----------------
462498

@@ -533,13 +569,23 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
533569
}
534570
}
535571

536-
private def genLabeled(tree: Labeled): BType = tree match {
572+
private def genLabeledTo(tree: Labeled, expectedType: BType, dest: LoadDestination): BType = tree match {
537573
case Labeled(bind, expr) =>
538574

539-
val resKind = tpeTK(tree)
540-
genLoad(expr, resKind)
541-
markProgramPoint(programPoint(bind.symbol))
542-
resKind
575+
val labelSym = bind.symbol
576+
577+
if dest == LoadDestination.FallThrough then
578+
val resKind = tpeTK(tree)
579+
val jumpTarget = new asm.Label
580+
registerJumpDest(labelSym, resKind, LoadDestination.Jump(jumpTarget))
581+
genLoad(expr, resKind)
582+
markProgramPoint(jumpTarget)
583+
resKind
584+
else
585+
registerJumpDest(labelSym, expectedType, dest)
586+
genLoadTo(expr, expectedType, dest)
587+
expectedType
588+
end if
543589
}
544590

545591
private def genReturn(r: Return): Unit = {
@@ -548,17 +594,14 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
548594

549595
if (NoSymbol == fromSym) {
550596
// return from enclosing method
551-
val returnedKind = tpeTK(expr)
552-
genLoad(expr, returnedKind)
553-
adapt(returnedKind, returnType)
554-
val saveReturnValue = (returnType != UNIT)
555-
lineNumber(r)
556-
557597
cleanups match {
558598
case Nil =>
559599
// not an assertion: !shouldEmitCleanup (at least not yet, pendingCleanups() may still have to run, and reset `shouldEmitCleanup`.
560-
bc emitRETURN returnType
600+
genLoadTo(expr, returnType, LoadDestination.Return)
561601
case nextCleanup :: rest =>
602+
genLoad(expr, returnType)
603+
lineNumber(r)
604+
val saveReturnValue = (returnType != UNIT)
562605
if (saveReturnValue) {
563606
// regarding return value, the protocol is: in place of a `return-stmt`, a sequence of `adapt, store, jump` are inserted.
564607
if (earlyReturnVar == null) {
@@ -578,54 +621,39 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
578621
* that cross cleanup boundaries. However, in theory such crossings are valid, so we should take care
579622
* of them.
580623
*/
581-
val resultKind = toTypeKind(fromSym.info)
582-
genLoad(expr, resultKind)
583-
lineNumber(r)
584-
bc goTo programPoint(fromSym)
624+
val (exprExpectedType, exprDest) = findJumpDest(fromSym)
625+
genLoadTo(expr, exprExpectedType, exprDest)
585626
}
586627
} // end of genReturn()
587628

588-
def genWhileDo(tree: WhileDo, expectedType: BType): BType = tree match{
629+
def genWhileDo(tree: WhileDo): LoadDestination = tree match{
589630
case WhileDo(cond, body) =>
590631

591632
val isInfinite = cond == tpd.EmptyTree
592633

634+
val loop = new asm.Label
635+
markProgramPoint(loop)
636+
593637
if isInfinite then
594-
body match
595-
case Labeled(bind, expr) if tpeTK(body) == UNIT =>
596-
// this is the shape of tailrec methods
597-
val loop = programPoint(bind.symbol)
598-
markProgramPoint(loop)
599-
genLoad(expr, UNIT)
600-
bc goTo loop
601-
case _ =>
602-
val loop = new asm.Label
603-
markProgramPoint(loop)
604-
genLoad(body, UNIT)
605-
bc goTo loop
606-
end match
607-
expectedType
638+
val dest = LoadDestination.Jump(loop)
639+
genLoadTo(body, UNIT, dest)
640+
dest
608641
else
609642
body match
610643
case Literal(value) if value.tag == UnitTag =>
611644
// this is the shape of do..while loops
612-
val loop = new asm.Label
613-
markProgramPoint(loop)
614645
val exitLoop = new asm.Label
615646
genCond(cond, loop, exitLoop, targetIfNoJump = exitLoop)
616647
markProgramPoint(exitLoop)
617648
case _ =>
618-
val loop = new asm.Label
619649
val success = new asm.Label
620650
val failure = new asm.Label
621-
markProgramPoint(loop)
622651
genCond(cond, success, failure, targetIfNoJump = success)
623652
markProgramPoint(success)
624-
genLoad(body, UNIT)
625-
bc goTo loop
653+
genLoadTo(body, UNIT, LoadDestination.Jump(loop))
626654
markProgramPoint(failure)
627655
end match
628-
UNIT
656+
LoadDestination.FallThrough
629657
}
630658

631659
def genTypeApply(t: TypeApply): BType = (t: @unchecked) match {
@@ -848,11 +876,16 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
848876
* Int/String values to use as keys, and a code block. The exception is the "default" case
849877
* clause which doesn't list any key (there is exactly one of these per match).
850878
*/
851-
private def genMatch(tree: Match): BType = tree match {
879+
private def genMatchTo(tree: Match, expectedType: BType, dest: LoadDestination): BType = tree match {
852880
case Match(selector, cases) =>
853881
lineNumber(tree)
854-
val generatedType = tpeTK(tree)
855-
val postMatch = new asm.Label
882+
883+
val (generatedType, postMatch, postMatchDest) =
884+
if dest == LoadDestination.FallThrough then
885+
val postMatch = new asm.Label
886+
(tpeTK(tree), postMatch, LoadDestination.Jump(postMatch))
887+
else
888+
(expectedType, null, dest)
856889

857890
// Only two possible selector types exist in `Match` trees at this point: Int and String
858891
if (tpeTK(selector) == INT) {
@@ -902,8 +935,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
902935
for (sb <- switchBlocks.reverse) {
903936
val (caseLabel, caseBody) = sb
904937
markProgramPoint(caseLabel)
905-
genLoad(caseBody, generatedType)
906-
bc goTo postMatch
938+
genLoadTo(caseBody, generatedType, postMatchDest)
907939
}
908940
} else {
909941

@@ -968,13 +1000,14 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
9681000
}
9691001

9701002
// Push the hashCode of the string (or `0` it is `null`) onto the stack and switch on it
971-
genLoadIf(
1003+
genLoadIfTo(
9721004
If(
9731005
tree.selector.select(defn.Any_==).appliedTo(nullLiteral),
9741006
Literal(Constant(0)),
9751007
tree.selector.select(defn.Any_hashCode).appliedToNone
9761008
),
977-
INT
1009+
INT,
1010+
LoadDestination.FallThrough
9781011
)
9791012
bc.emitSWITCH(mkArrayReverse(flatKeys), mkArrayL(targets.reverse), default, MIN_SWITCH_DENSITY)
9801013

@@ -993,8 +1026,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
9931026
val thisCaseMatches = new asm.Label
9941027
genCond(condp, thisCaseMatches, keepGoing, targetIfNoJump = thisCaseMatches)
9951028
markProgramPoint(thisCaseMatches)
996-
genLoad(caseBody, generatedType)
997-
bc goTo postMatch
1029+
genLoadTo(caseBody, generatedType, postMatchDest)
9981030
}
9991031
markProgramPoint(keepGoing)
10001032
}
@@ -1004,22 +1036,22 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
10041036
// emit blocks for common patterns
10051037
for ((caseLabel, caseBody) <- indirectBlocks.reverse) {
10061038
markProgramPoint(caseLabel)
1007-
genLoad(caseBody, generatedType)
1008-
bc goTo postMatch
1039+
genLoadTo(caseBody, generatedType, postMatchDest)
10091040
}
10101041
}
10111042

1012-
markProgramPoint(postMatch)
1043+
if postMatch != null then
1044+
markProgramPoint(postMatch)
10131045
generatedType
10141046
}
10151047

1016-
def genBlock(tree: Block, expectedType: BType) = tree match {
1048+
def genBlockTo(tree: Block, expectedType: BType, dest: LoadDestination): Unit = tree match {
10171049
case Block(stats, expr) =>
10181050

10191051
val savedScope = varsInScope
10201052
varsInScope = Nil
10211053
stats foreach genStat
1022-
genLoad(expr, expectedType)
1054+
genLoadTo(expr, expectedType, dest)
10231055
val end = currProgramPoint()
10241056
if (emitVars) {
10251057
// add entries to LocalVariableTable JVM attribute

0 commit comments

Comments
 (0)