Skip to content

Commit 75c1f23

Browse files
committed
WiP Use explicit destinations in codegen to avoid uselessly jumping around.
1 parent 73df472 commit 75c1f23

File tree

4 files changed

+165
-139
lines changed

4 files changed

+165
-139
lines changed

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

Lines changed: 124 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,8 +201,8 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
211201
generatedType
212202
}
213203

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

217207
val success = new asm.Label
218208
val failure = 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 of GenBCode.genLoad()
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) // ICode enters here into enterIgnoreMode, we'll rely instead on DCE at ClassNode level.
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 {
537-
case Labeled(bind, expr) =>
572+
private def genLabeledTo(tree: Labeled, expectedType: BType, dest: LoadDestination): BType = {
573+
val Labeled(bind, expr) = tree
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,15 @@ 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)
556597

557598
cleanups match {
558599
case Nil =>
559600
// not an assertion: !shouldEmitCleanup (at least not yet, pendingCleanups() may still have to run, and reset `shouldEmitCleanup`.
560-
bc emitRETURN returnType
601+
genLoadTo(expr, returnType, LoadDestination.Return)
561602
case nextCleanup :: rest =>
603+
genLoad(expr, returnType)
604+
lineNumber(r)
605+
val saveReturnValue = (returnType != UNIT)
562606
if (saveReturnValue) {
563607
// regarding return value, the protocol is: in place of a `return-stmt`, a sequence of `adapt, store, jump` are inserted.
564608
if (earlyReturnVar == null) {
@@ -578,54 +622,39 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
578622
* that cross cleanup boundaries. However, in theory such crossings are valid, so we should take care
579623
* of them.
580624
*/
581-
val resultKind = toTypeKind(fromSym.info)
582-
genLoad(expr, resultKind)
583-
lineNumber(r)
584-
bc goTo programPoint(fromSym)
625+
val (exprExpectedType, exprDest) = findJumpDest(fromSym)
626+
genLoadTo(expr, exprExpectedType, exprDest)
585627
}
586628
} // end of genReturn()
587629

588-
def genWhileDo(tree: WhileDo, expectedType: BType): BType = tree match{
630+
def genWhileDo(tree: WhileDo): LoadDestination = tree match{
589631
case WhileDo(cond, body) =>
590632

591633
val isInfinite = cond == tpd.EmptyTree
592634

635+
val loop = new asm.Label
636+
markProgramPoint(loop)
637+
593638
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
639+
val dest = LoadDestination.Jump(loop)
640+
genLoadTo(body, UNIT, dest)
641+
dest
608642
else
609643
body match
610644
case Literal(value) if value.tag == UnitTag =>
611645
// this is the shape of do..while loops
612-
val loop = new asm.Label
613-
markProgramPoint(loop)
614646
val exitLoop = new asm.Label
615647
genCond(cond, loop, exitLoop, targetIfNoJump = exitLoop)
616648
markProgramPoint(exitLoop)
617649
case _ =>
618-
val loop = new asm.Label
619650
val success = new asm.Label
620651
val failure = new asm.Label
621-
markProgramPoint(loop)
622652
genCond(cond, success, failure, targetIfNoJump = success)
623653
markProgramPoint(success)
624-
genLoad(body, UNIT)
625-
bc goTo loop
654+
genLoadTo(body, UNIT, LoadDestination.Jump(loop))
626655
markProgramPoint(failure)
627656
end match
628-
UNIT
657+
LoadDestination.FallThrough
629658
}
630659

631660
def genTypeApply(t: TypeApply): BType = (t: @unchecked) match {
@@ -848,11 +877,13 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
848877
* Int/String values to use as keys, and a code block. The exception is the "default" case
849878
* clause which doesn't list any key (there is exactly one of these per match).
850879
*/
851-
private def genMatch(tree: Match): BType = tree match {
880+
private def genMatchTo(tree: Match, expectedType: BType, dest: LoadDestination): BType = tree match {
852881
case Match(selector, cases) =>
853882
lineNumber(tree)
854-
val generatedType = tpeTK(tree)
883+
884+
val generatedType = if dest == LoadDestination.FallThrough then tpeTK(tree) else expectedType
855885
val postMatch = new asm.Label
886+
val postMatchDest = if dest == LoadDestination.FallThrough then LoadDestination.Jump(postMatch) else dest
856887

857888
// Only two possible selector types exist in `Match` trees at this point: Int and String
858889
if (tpeTK(selector) == INT) {
@@ -902,8 +933,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
902933
for (sb <- switchBlocks.reverse) {
903934
val (caseLabel, caseBody) = sb
904935
markProgramPoint(caseLabel)
905-
genLoad(caseBody, generatedType)
906-
bc goTo postMatch
936+
genLoadTo(caseBody, generatedType, postMatchDest)
907937
}
908938
} else {
909939

@@ -968,13 +998,14 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
968998
}
969999

9701000
// Push the hashCode of the string (or `0` it is `null`) onto the stack and switch on it
971-
genLoadIf(
1001+
genLoadIfTo(
9721002
If(
9731003
tree.selector.select(defn.Any_==).appliedTo(nullLiteral),
9741004
Literal(Constant(0)),
9751005
tree.selector.select(defn.Any_hashCode).appliedToNone
9761006
),
977-
INT
1007+
INT,
1008+
LoadDestination.FallThrough
9781009
)
9791010
bc.emitSWITCH(mkArrayReverse(flatKeys), mkArrayL(targets.reverse), default, MIN_SWITCH_DENSITY)
9801011

@@ -993,8 +1024,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
9931024
val thisCaseMatches = new asm.Label
9941025
genCond(condp, thisCaseMatches, keepGoing, targetIfNoJump = thisCaseMatches)
9951026
markProgramPoint(thisCaseMatches)
996-
genLoad(caseBody, generatedType)
997-
bc goTo postMatch
1027+
genLoadTo(caseBody, generatedType, postMatchDest)
9981028
}
9991029
markProgramPoint(keepGoing)
10001030
}
@@ -1004,22 +1034,22 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
10041034
// emit blocks for common patterns
10051035
for ((caseLabel, caseBody) <- indirectBlocks.reverse) {
10061036
markProgramPoint(caseLabel)
1007-
genLoad(caseBody, generatedType)
1008-
bc goTo postMatch
1037+
genLoadTo(caseBody, generatedType, postMatchDest)
10091038
}
10101039
}
10111040

1012-
markProgramPoint(postMatch)
1041+
if dest == LoadDestination.FallThrough then
1042+
markProgramPoint(postMatch)
10131043
generatedType
10141044
}
10151045

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

10191049
val savedScope = varsInScope
10201050
varsInScope = Nil
10211051
stats foreach genStat
1022-
genLoad(expr, expectedType)
1052+
genLoadTo(expr, expectedType, dest)
10231053
val end = currProgramPoint()
10241054
if (emitVars) {
10251055
// add entries to LocalVariableTable JVM attribute

0 commit comments

Comments
 (0)