Skip to content

Commit fb0f8f1

Browse files
lrytznicolasstucki
authored andcommitted
Generate leaner code for branches
GenBCode used to generate more bytecode for branching instructions than GenASM. A simple method def f(x: Int, b: Boolean) = if (b) 1 else 2 would generate ILOAD 2 IFNE L1 GOTO L2 L1 ICONST_1 GOTO L3 L2 ICONST_2 L3 IRETURN If the conditional branch is negated (IFEQ) the GOTO is unnecessary. While -Yopt:l:method would clean this up, it's also not too hard to generate the leaner bytecode in the first place. Port of f70c77e
1 parent d90cbcb commit fb0f8f1

File tree

2 files changed

+212
-89
lines changed

2 files changed

+212
-89
lines changed

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

Lines changed: 86 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -205,14 +205,14 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
205205
val hasElse = !elsep.isEmpty
206206
val postIf = if (hasElse) new asm.Label else failure
207207

208-
genCond(condp, success, failure)
208+
genCond(condp, success, failure, targetIfNoJump = success)
209+
markProgramPoint(success)
209210

210211
val thenKind = tpeTK(thenp)
211212
val elseKind = if (!hasElse) UNIT else tpeTK(elsep)
212213
def hasUnitBranch = (thenKind == UNIT || elseKind == UNIT) && expectedType == UNIT
213214
val resKind = if (hasUnitBranch) UNIT else tpeTK(tree)
214215

215-
markProgramPoint(success)
216216
genLoad(thenp, resKind)
217217
if (hasElse) { bc goTo postIf }
218218
markProgramPoint(failure)
@@ -238,14 +238,14 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
238238
else if (isArrayOp(code)) genArrayOp(tree, code, expectedType)
239239
else if (isLogicalOp(code) || isComparisonOp(code)) {
240240
val success, failure, after = new asm.Label
241-
genCond(tree, success, failure)
241+
genCond(tree, success, failure, targetIfNoJump = success)
242242
// success block
243-
markProgramPoint(success)
244-
bc boolconst true
245-
bc goTo after
243+
markProgramPoint(success)
244+
bc boolconst true
245+
bc goTo after
246246
// failure block
247-
markProgramPoint(failure)
248-
bc boolconst false
247+
markProgramPoint(failure)
248+
bc boolconst false
249249
// after
250250
markProgramPoint(after)
251251

@@ -1167,57 +1167,60 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
11671167
}
11681168

11691169
/* Emit code to compare the two top-most stack values using the 'op' operator. */
1170-
private def genCJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType): Unit = {
1171-
if (tk.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
1172-
bc.emitIF_ICMP(op, success)
1173-
} else if (tk.isRef) { // REFERENCE(_) | ARRAY(_)
1174-
bc.emitIF_ACMP(op, success)
1175-
} else {
1176-
import Primitives._
1177-
(tk: @unchecked) match {
1178-
case LONG => emit(asm.Opcodes.LCMP)
1179-
case FLOAT =>
1180-
if (op == LT || op == LE) emit(asm.Opcodes.FCMPG)
1181-
else emit(asm.Opcodes.FCMPL)
1182-
case DOUBLE =>
1183-
if (op == LT || op == LE) emit(asm.Opcodes.DCMPG)
1184-
else emit(asm.Opcodes.DCMPL)
1170+
private def genCJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType, targetIfNoJump: asm.Label): Unit = {
1171+
if (targetIfNoJump == success) genCJUMP(failure, success, op.negate(), tk, targetIfNoJump)
1172+
else {
1173+
if (tk.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
1174+
bc.emitIF_ICMP(op, success)
1175+
} else if (tk.isRef) { // REFERENCE(_) | ARRAY(_)
1176+
bc.emitIF_ACMP(op, success)
1177+
} else {
1178+
import Primitives._
1179+
(tk: @unchecked) match {
1180+
case LONG => emit(asm.Opcodes.LCMP)
1181+
case FLOAT =>
1182+
if (op == LT || op == LE) emit(asm.Opcodes.FCMPG)
1183+
else emit(asm.Opcodes.FCMPL)
1184+
case DOUBLE =>
1185+
if (op == LT || op == LE) emit(asm.Opcodes.DCMPG)
1186+
else emit(asm.Opcodes.DCMPL)
1187+
}
1188+
bc.emitIF(op, success)
11851189
}
1186-
bc.emitIF(op, success)
1190+
if (targetIfNoJump != failure) bc goTo failure
11871191
}
1188-
bc goTo failure
11891192
}
11901193

11911194
/* Emits code to compare (and consume) stack-top and zero using the 'op' operator */
1192-
private def genCZJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType): Unit = {
1195+
private def genCZJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType, targetIfNoJump: asm.Label): Unit = {
11931196
import Primitives._
1194-
1195-
if (tk.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
1196-
bc.emitIF(op, success)
1197-
} else if (tk.isRef) { // REFERENCE(_) | ARRAY(_)
1198-
// @unchecked because references aren't compared with GT, GE, LT, LE.
1199-
1200-
(op : @unchecked) match {
1201-
case EQ => bc emitIFNULL success
1202-
case NE => bc emitIFNONNULL success
1203-
}
1204-
} else {
1205-
(tk: @unchecked) match {
1206-
case LONG =>
1207-
emit(asm.Opcodes.LCONST_0)
1208-
emit(asm.Opcodes.LCMP)
1209-
case FLOAT =>
1210-
emit(asm.Opcodes.FCONST_0)
1211-
if (op == LT || op == LE) emit(asm.Opcodes.FCMPG)
1212-
else emit(asm.Opcodes.FCMPL)
1213-
case DOUBLE =>
1214-
emit(asm.Opcodes.DCONST_0)
1215-
if (op == LT || op == LE) emit(asm.Opcodes.DCMPG)
1216-
else emit(asm.Opcodes.DCMPL)
1197+
if (targetIfNoJump == success) genCZJUMP(failure, success, op.negate(), tk, targetIfNoJump)
1198+
else {
1199+
if (tk.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
1200+
bc.emitIF(op, success)
1201+
} else if (tk.isRef) { // REFERENCE(_) | ARRAY(_)
1202+
op match { // references are only compared with EQ and NE
1203+
case EQ => bc emitIFNULL success
1204+
case NE => bc emitIFNONNULL success
1205+
}
1206+
} else {
1207+
(tk: @unchecked) match {
1208+
case LONG =>
1209+
emit(asm.Opcodes.LCONST_0)
1210+
emit(asm.Opcodes.LCMP)
1211+
case FLOAT =>
1212+
emit(asm.Opcodes.FCONST_0)
1213+
if (op == LT || op == LE) emit(asm.Opcodes.FCMPG)
1214+
else emit(asm.Opcodes.FCMPL)
1215+
case DOUBLE =>
1216+
emit(asm.Opcodes.DCONST_0)
1217+
if (op == LT || op == LE) emit(asm.Opcodes.DCMPG)
1218+
else emit(asm.Opcodes.DCMPL)
1219+
}
1220+
bc.emitIF(op, success)
12171221
}
1218-
bc.emitIF(op, success)
1222+
if (targetIfNoJump != failure) bc goTo failure
12191223
}
1220-
bc goTo failure
12211224
}
12221225

12231226
val testOpForPrimitive: Array[TestOp] = {
@@ -1232,29 +1235,26 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
12321235
* Generate code for conditional expressions.
12331236
* The jump targets success/failure of the test are `then-target` and `else-target` resp.
12341237
*/
1235-
private def genCond(tree: Tree, success: asm.Label, failure: asm.Label): Unit = {
1238+
private def genCond(tree: Tree, success: asm.Label, failure: asm.Label, targetIfNoJump: asm.Label): Unit = {
12361239

12371240
def genComparisonOp(l: Tree, r: Tree, code: Int): Unit = {
1238-
val op: TestOp = testOpForPrimitive(code - ScalaPrimitivesOps.ID)
1239-
// special-case reference (in)equality test for null (null eq x, x eq null)
1240-
var nonNullSide: Tree = null
1241-
if (ScalaPrimitivesOps.isReferenceEqualityOp(code) &&
1242-
{ nonNullSide = ifOneIsNull(l, r); nonNullSide != null }
1243-
) {
1241+
val op = testOpForPrimitive(code)
1242+
val nonNullSide = if (ScalaPrimitivesOps.isReferenceEqualityOp(code)) ifOneIsNull(l, r) else null
1243+
if (nonNullSide != null) {
1244+
// special-case reference (in)equality test for null (null eq x, x eq null)
12441245
genLoad(nonNullSide, ObjectReference)
1245-
genCZJUMP(success, failure, op, ObjectReference)
1246-
}
1247-
else {
1246+
genCZJUMP(success, failure, op, ObjectReference, targetIfNoJump)
1247+
} else {
12481248
val tk = tpeTK(l).maxType(tpeTK(r))
12491249
genLoad(l, tk)
12501250
genLoad(r, tk)
1251-
genCJUMP(success, failure, op, tk)
1251+
genCJUMP(success, failure, op, tk, targetIfNoJump)
12521252
}
12531253
}
12541254

1255-
def default() = {
1255+
def loadAndTestBoolean() = {
12561256
genLoad(tree, BOOL)
1257-
genCZJUMP(success, failure, Primitives.NE, BOOL)
1257+
genCZJUMP(success, failure, Primitives.NE, BOOL, targetIfNoJump)
12581258
}
12591259

12601260
lineNumber(tree)
@@ -1265,38 +1265,35 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
12651265

12661266
// lhs and rhs of test
12671267
lazy val Select(lhs, _) = fun
1268-
val rhs = if (args.isEmpty) EmptyTree else args.head; // args.isEmpty only for ZNOT
1268+
val rhs = if (args.isEmpty) EmptyTree else args.head // args.isEmpty only for ZNOT
12691269

1270-
def genZandOrZor(and: Boolean): Unit = { // TODO WRONG
1270+
def genZandOrZor(and: Boolean): Unit = {
12711271
// reaching "keepGoing" indicates the rhs should be evaluated too (ie not short-circuited).
12721272
val keepGoing = new asm.Label
12731273

1274-
if (and) genCond(lhs, keepGoing, failure)
1275-
else genCond(lhs, success, keepGoing)
1274+
if (and) genCond(lhs, keepGoing, failure, targetIfNoJump = keepGoing)
1275+
else genCond(lhs, success, keepGoing, targetIfNoJump = keepGoing)
12761276

12771277
markProgramPoint(keepGoing)
1278-
genCond(rhs, success, failure)
1278+
genCond(rhs, success, failure, targetIfNoJump)
12791279
}
12801280

1281-
12821281
primitives.getPrimitive(tree, lhs.tpe) match {
1283-
case ZNOT => genCond(lhs, failure, success)
1282+
case ZNOT => genCond(lhs, failure, success, targetIfNoJump)
12841283
case ZAND => genZandOrZor(and = true)
12851284
case ZOR => genZandOrZor(and = false)
12861285
case code =>
1287-
// TODO !!!!!!!!!! isReferenceType, in the sense of TypeKind? (ie non-array, non-boxed, non-nothing, may be null)
12881286
if (ScalaPrimitivesOps.isUniversalEqualityOp(code) && tpeTK(lhs).isClass) {
1289-
// `lhs` has reference type
1290-
if (code == EQ) genEqEqPrimitive(lhs, rhs, success, failure)
1291-
else genEqEqPrimitive(lhs, rhs, failure, success)
1292-
}
1293-
else if (ScalaPrimitivesOps.isComparisonOp(code))
1287+
// rewrite `==` to null tests and `equals`. not needed for arrays (`equals` is reference equality).
1288+
if (code == EQ) genEqEqPrimitive(lhs, rhs, success, failure, targetIfNoJump)
1289+
else genEqEqPrimitive(lhs, rhs, failure, success, targetIfNoJump)
1290+
} else if (ScalaPrimitivesOps.isComparisonOp(code)) {
12941291
genComparisonOp(lhs, rhs, code)
1295-
else
1296-
default()
1292+
} else
1293+
loadAndTestBoolean()
12971294
}
12981295

1299-
case _ => default()
1296+
case _ => loadAndTestBoolean()
13001297
}
13011298

13021299
} // end of genCond()
@@ -1308,7 +1305,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
13081305
* @param l left-hand-side of the '=='
13091306
* @param r right-hand-side of the '=='
13101307
*/
1311-
def genEqEqPrimitive(l: Tree, r: Tree, success: asm.Label, failure: asm.Label): Unit = {
1308+
def genEqEqPrimitive(l: Tree, r: Tree, success: asm.Label, failure: asm.Label, targetIfNoJump: asm.Label): Unit = {
13121309

13131310
/* True if the equality comparison is between values that require the use of the rich equality
13141311
* comparator (scala.runtime.Comparator.equals). This is the case when either side of the
@@ -1330,26 +1327,27 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
13301327
else externalEqualsNumObject
13311328
} else externalEquals
13321329
}
1330+
13331331
genLoad(l, ObjectReference)
13341332
genLoad(r, ObjectReference)
13351333
genCallMethod(equalsMethod, InvokeStyle.Static)
1336-
genCZJUMP(success, failure, Primitives.NE, BOOL)
1334+
genCZJUMP(success, failure, Primitives.NE, BOOL, targetIfNoJump)
13371335
}
13381336
else {
13391337
if (isNull(l)) {
13401338
// null == expr -> expr eq null
13411339
genLoad(r, ObjectReference)
1342-
genCZJUMP(success, failure, Primitives.EQ, ObjectReference)
1340+
genCZJUMP(success, failure, Primitives.EQ, ObjectReference, targetIfNoJump)
13431341
} else if (isNull(r)) {
13441342
// expr == null -> expr eq null
13451343
genLoad(l, ObjectReference)
1346-
genCZJUMP(success, failure, Primitives.EQ, ObjectReference)
1344+
genCZJUMP(success, failure, Primitives.EQ, ObjectReference, targetIfNoJump)
13471345
} else if (isNonNullExpr(l)) {
13481346
// SI-7852 Avoid null check if L is statically non-null.
13491347
genLoad(l, ObjectReference)
13501348
genLoad(r, ObjectReference)
13511349
genCallMethod(Object_equals, InvokeStyle.Virtual)
1352-
genCZJUMP(success, failure, Primitives.NE, BOOL)
1350+
genCZJUMP(success, failure, Primitives.NE, BOOL, targetIfNoJump)
13531351
} else {
13541352
// l == r -> if (l eq null) r eq null else l.equals(r)
13551353
val eqEqTempLocal = locals.makeLocal(ObjectReference, nme_EQEQ_LOCAL_VAR.mangledString, Object_Type, r.pos)
@@ -1360,17 +1358,17 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
13601358
genLoad(r, ObjectReference)
13611359
locals.store(eqEqTempLocal)
13621360
bc dup ObjectReference
1363-
genCZJUMP(lNull, lNonNull, Primitives.EQ, ObjectReference)
1361+
genCZJUMP(lNull, lNonNull, Primitives.EQ, ObjectReference, targetIfNoJump = lNull)
13641362

13651363
markProgramPoint(lNull)
13661364
bc drop ObjectReference
13671365
locals.load(eqEqTempLocal)
1368-
genCZJUMP(success, failure, Primitives.EQ, ObjectReference)
1366+
genCZJUMP(success, failure, Primitives.EQ, ObjectReference, targetIfNoJump = lNonNull)
13691367

13701368
markProgramPoint(lNonNull)
13711369
locals.load(eqEqTempLocal)
13721370
genCallMethod(Object_equals, InvokeStyle.Virtual)
1373-
genCZJUMP(success, failure, Primitives.NE, BOOL)
1371+
genCZJUMP(success, failure, Primitives.NE, BOOL, targetIfNoJump)
13741372
}
13751373
}
13761374
}

0 commit comments

Comments
 (0)