@@ -1039,6 +1039,383 @@ class TestBCode extends DottyBytecodeTest {
10391039 }
10401040 }
10411041
1042+ @ Test def patmatControlFlow (): Unit = {
1043+ val source =
1044+ s """ class Foo {
1045+ | def m1(xs: List[Int]): Int = xs match
1046+ | case x :: xr => x
1047+ | case Nil => 20
1048+ |
1049+ | def m2(xs: List[Int]): Int = xs match
1050+ | case (1 | 2) :: xr => 10
1051+ | case x :: xr => x
1052+ | case _ => 20
1053+ |}
1054+ """ .stripMargin
1055+
1056+ checkBCode(source) { dir =>
1057+ val fooClass = loadClassNode(dir.lookupName(" Foo.class" , directory = false ).input)
1058+
1059+ // ---------------
1060+
1061+ val m1Meth = getMethod(fooClass, " m1" )
1062+
1063+ assertSameCode(m1Meth, List (
1064+ VarOp (ALOAD , 1 ),
1065+ VarOp (ASTORE , 2 ),
1066+ VarOp (ALOAD , 2 ),
1067+ TypeOp (INSTANCEOF , " scala/collection/immutable/$colon$colon" ),
1068+ Jump (IFEQ , Label (19 )),
1069+ VarOp (ALOAD , 2 ),
1070+ TypeOp (CHECKCAST , " scala/collection/immutable/$colon$colon" ),
1071+ VarOp (ASTORE , 3 ),
1072+ VarOp (ALOAD , 3 ),
1073+ Invoke (INVOKEVIRTUAL , " scala/collection/immutable/$colon$colon" , " next$access$1" , " ()Lscala/collection/immutable/List;" , false ),
1074+ VarOp (ASTORE , 4 ),
1075+ VarOp (ALOAD , 3 ),
1076+ Invoke (INVOKEVIRTUAL , " scala/collection/immutable/$colon$colon" , " head" , " ()Ljava/lang/Object;" , false ),
1077+ Invoke (INVOKESTATIC , " scala/runtime/BoxesRunTime" , " unboxToInt" , " (Ljava/lang/Object;)I" , false ),
1078+ VarOp (ISTORE , 5 ),
1079+ VarOp (ALOAD , 4 ),
1080+ VarOp (ASTORE , 6 ),
1081+ VarOp (ILOAD , 5 ),
1082+ Jump (GOTO , Label (47 )),
1083+ Label (19 ),
1084+ Field (GETSTATIC , " scala/package$" , " MODULE$" , " Lscala/package$;" ),
1085+ Invoke (INVOKEVIRTUAL , " scala/package$" , " Nil" , " ()Lscala/collection/immutable/Nil$;" , false ),
1086+ VarOp (ALOAD , 2 ),
1087+ VarOp (ASTORE , 7 ),
1088+ Op (DUP ),
1089+ Jump (IFNONNULL , Label (31 )),
1090+ Op (POP ),
1091+ VarOp (ALOAD , 7 ),
1092+ Jump (IFNULL , Label (36 )),
1093+ Jump (GOTO , Label (40 )),
1094+ Label (31 ),
1095+ VarOp (ALOAD , 7 ),
1096+ Invoke (INVOKEVIRTUAL , " java/lang/Object" , " equals" , " (Ljava/lang/Object;)Z" , false ),
1097+ Jump (IFEQ , Label (40 )),
1098+ Label (36 ),
1099+ IntOp (BIPUSH , 20 ),
1100+ Jump (GOTO , Label (47 )),
1101+ Label (40 ),
1102+ TypeOp (NEW , " scala/MatchError" ),
1103+ Op (DUP ),
1104+ VarOp (ALOAD , 2 ),
1105+ Invoke (INVOKESPECIAL , " scala/MatchError" , " <init>" , " (Ljava/lang/Object;)V" , false ),
1106+ Op (ATHROW ),
1107+ Label (47 ),
1108+ Op (IRETURN ),
1109+ ))
1110+
1111+ // ---------------
1112+
1113+ val m2Meth = getMethod(fooClass, " m2" )
1114+
1115+ assertSameCode(m2Meth, List (
1116+ VarOp (ALOAD , 1 ),
1117+ VarOp (ASTORE , 2 ),
1118+ VarOp (ALOAD , 2 ),
1119+ TypeOp (INSTANCEOF , " scala/collection/immutable/$colon$colon" ),
1120+ Jump (IFEQ , Label (42 )),
1121+ VarOp (ALOAD , 2 ),
1122+ TypeOp (CHECKCAST , " scala/collection/immutable/$colon$colon" ),
1123+ VarOp (ASTORE , 3 ),
1124+ VarOp (ALOAD , 3 ),
1125+ Invoke (INVOKEVIRTUAL , " scala/collection/immutable/$colon$colon" , " head" , " ()Ljava/lang/Object;" , false ),
1126+ Invoke (INVOKESTATIC , " scala/runtime/BoxesRunTime" , " unboxToInt" , " (Ljava/lang/Object;)I" , false ),
1127+ VarOp (ISTORE , 4 ),
1128+ VarOp (ALOAD , 3 ),
1129+ Invoke (INVOKEVIRTUAL , " scala/collection/immutable/$colon$colon" , " next$access$1" , " ()Lscala/collection/immutable/List;" , false ),
1130+ VarOp (ASTORE , 5 ),
1131+ Op (ICONST_1 ),
1132+ VarOp (ILOAD , 4 ),
1133+ Jump (IF_ICMPNE , Label (19 )),
1134+ Jump (GOTO , Label (28 )),
1135+ Label (19 ),
1136+ Op (ICONST_2 ),
1137+ VarOp (ILOAD , 4 ),
1138+ Jump (IF_ICMPNE , Label (25 )),
1139+ Jump (GOTO , Label (28 )),
1140+ Label (25 ),
1141+ Jump (GOTO , Label (34 )),
1142+ Label (28 ),
1143+ VarOp (ALOAD , 5 ),
1144+ VarOp (ASTORE , 6 ),
1145+ IntOp (BIPUSH , 10 ),
1146+ Jump (GOTO , Label (46 )),
1147+ Label (34 ),
1148+ VarOp (ILOAD , 4 ),
1149+ VarOp (ISTORE , 7 ),
1150+ VarOp (ALOAD , 5 ),
1151+ VarOp (ASTORE , 8 ),
1152+ VarOp (ILOAD , 7 ),
1153+ Jump (GOTO , Label (46 )),
1154+ Label (42 ),
1155+ IntOp (BIPUSH , 20 ),
1156+ Jump (GOTO , Label (46 )),
1157+ Label (46 ),
1158+ Op (IRETURN ),
1159+ ))
1160+ }
1161+ }
1162+
1163+ @ Test def switchControlFlow (): Unit = {
1164+ val source =
1165+ s """ import scala.annotation.switch
1166+ |
1167+ |class Foo {
1168+ | def m1(x: Int): Int = (x: @switch) match
1169+ | case 1 => 10
1170+ | case 7 => 20
1171+ | case 8 => 30
1172+ | case 9 => 40
1173+ | case _ => x
1174+ |
1175+ | def m2(x: Int): Int = (x: @switch) match
1176+ | case (1 | 2) => 10
1177+ | case 7 => 20
1178+ | case 8 => 30
1179+ | case c if c > 100 => 20
1180+ |}
1181+ """ .stripMargin
1182+
1183+ checkBCode(source) { dir =>
1184+ val fooClass = loadClassNode(dir.lookupName(" Foo.class" , directory = false ).input)
1185+
1186+ // ---------------
1187+
1188+ val m1Meth = getMethod(fooClass, " m1" )
1189+
1190+ assertSameCode(m1Meth, List (
1191+ VarOp (ILOAD , 1 ),
1192+ VarOp (ISTORE , 2 ),
1193+ VarOp (ILOAD , 2 ),
1194+ LookupSwitch (LOOKUPSWITCH , Label (40 ), List (1 , 7 , 8 , 9 ), List (Label (4 ), Label (13 ), Label (22 ), Label (31 ))),
1195+ Label (4 ),
1196+ IntOp (BIPUSH , 10 ),
1197+ Jump (GOTO , Label (52 )),
1198+ Op (NOP ),
1199+ Op (NOP ),
1200+ Op (ATHROW ),
1201+ Label (13 ),
1202+ IntOp (BIPUSH , 20 ),
1203+ Jump (GOTO , Label (52 )),
1204+ Op (NOP ),
1205+ Op (NOP ),
1206+ Op (ATHROW ),
1207+ Label (22 ),
1208+ IntOp (BIPUSH , 30 ),
1209+ Jump (GOTO , Label (52 )),
1210+ Op (NOP ),
1211+ Op (NOP ),
1212+ Op (ATHROW ),
1213+ Label (31 ),
1214+ IntOp (BIPUSH , 40 ),
1215+ Jump (GOTO , Label (52 )),
1216+ Op (NOP ),
1217+ Op (NOP ),
1218+ Op (ATHROW ),
1219+ Label (40 ),
1220+ VarOp (ILOAD , 1 ),
1221+ Jump (GOTO , Label (52 )),
1222+ Op (NOP ),
1223+ Op (NOP ),
1224+ Op (ATHROW ),
1225+ Op (ATHROW ),
1226+ Label (52 ),
1227+ Op (IRETURN ),
1228+ ))
1229+
1230+ // ---------------
1231+
1232+ val m2Meth = getMethod(fooClass, " m2" )
1233+
1234+ assertSameCode(m2Meth, List (
1235+ VarOp (ILOAD , 1 ),
1236+ VarOp (ISTORE , 2 ),
1237+ VarOp (ILOAD , 2 ),
1238+ LookupSwitch (LOOKUPSWITCH , Label (31 ), List (1 , 2 , 7 , 8 ), List (Label (4 ), Label (4 ), Label (13 ), Label (22 ))),
1239+ Label (4 ),
1240+ IntOp (BIPUSH , 10 ),
1241+ Jump (GOTO , Label (56 )),
1242+ Op (NOP ),
1243+ Op (NOP ),
1244+ Op (ATHROW ),
1245+ Label (13 ),
1246+ IntOp (BIPUSH , 20 ),
1247+ Jump (GOTO , Label (56 )),
1248+ Op (NOP ),
1249+ Op (NOP ),
1250+ Op (ATHROW ),
1251+ Label (22 ),
1252+ IntOp (BIPUSH , 30 ),
1253+ Jump (GOTO , Label (56 )),
1254+ Op (NOP ),
1255+ Op (NOP ),
1256+ Op (ATHROW ),
1257+ Label (31 ),
1258+ VarOp (ILOAD , 2 ),
1259+ VarOp (ISTORE , 3 ),
1260+ VarOp (ILOAD , 3 ),
1261+ IntOp (BIPUSH , 100 ),
1262+ Jump (IF_ICMPLE , Label (40 )),
1263+ IntOp (BIPUSH , 20 ),
1264+ Jump (GOTO , Label (56 )),
1265+ Label (40 ),
1266+ TypeOp (NEW , " scala/MatchError" ),
1267+ Op (DUP ),
1268+ VarOp (ILOAD , 2 ),
1269+ Invoke (INVOKESTATIC , " scala/runtime/BoxesRunTime" , " boxToInteger" , " (I)Ljava/lang/Integer;" , false ),
1270+ Invoke (INVOKESPECIAL , " scala/MatchError" , " <init>" , " (Ljava/lang/Object;)V" , false ),
1271+ Op (ATHROW ),
1272+ Op (NOP ),
1273+ Op (NOP ),
1274+ Op (ATHROW ),
1275+ Op (ATHROW ),
1276+ Label (56 ),
1277+ Op (IRETURN ),
1278+ ))
1279+ }
1280+ }
1281+
1282+ @ Test def ifThenElseControlFlow (): Unit = {
1283+ /* This is a test case coming from the Scala.js linker, where in Scala 2 we
1284+ * had to introduce a "useless" `return` to make the bytecode size smaller,
1285+ * measurably increasing performance (!).
1286+ */
1287+
1288+ val source =
1289+ s """ import java.io.Writer
1290+ |
1291+ |final class SourceMapWriter(out: Writer) {
1292+ | private val Base64Map =
1293+ | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
1294+ | "abcdefghijklmnopqrstuvwxyz" +
1295+ | "0123456789+/"
1296+ |
1297+ | private final val VLQBaseShift = 5
1298+ | private final val VLQBase = 1 << VLQBaseShift
1299+ | private final val VLQBaseMask = VLQBase - 1
1300+ | private final val VLQContinuationBit = VLQBase
1301+ |
1302+ | def entryPoint(value: Int): Unit = writeBase64VLQ(value)
1303+ |
1304+ | private def writeBase64VLQ(value0: Int): Unit = {
1305+ | val signExtended = value0 >> 31
1306+ | val value = (((value0 ^ signExtended) - signExtended) << 1) | (signExtended & 1)
1307+ | if (value < 26) {
1308+ | out.write('A' + value) // was `return out...`
1309+ | } else {
1310+ | def writeBase64VLQSlowPath(value0: Int): Unit = {
1311+ | var value = value0
1312+ | while ({
1313+ | // do {
1314+ | var digit = value & VLQBaseMask
1315+ | value = value >>> VLQBaseShift
1316+ | if (value != 0)
1317+ | digit |= VLQContinuationBit
1318+ | out.write(Base64Map.charAt(digit))
1319+ | // } while (
1320+ | value != 0
1321+ | // )
1322+ | }) ()
1323+ | }
1324+ | writeBase64VLQSlowPath(value)
1325+ | }
1326+ | }
1327+ |}
1328+ """ .stripMargin
1329+
1330+ checkBCode(source) { dir =>
1331+ val sourceMapWriterClass = loadClassNode(dir.lookupName(" SourceMapWriter.class" , directory = false ).input)
1332+
1333+ // ---------------
1334+
1335+ val writeBase64VLQMeth = getMethod(sourceMapWriterClass, " writeBase64VLQ" )
1336+
1337+ assertSameCode(writeBase64VLQMeth, List (
1338+ VarOp (ILOAD , 1 ),
1339+ IntOp (BIPUSH , 31 ),
1340+ Op (ISHR ),
1341+ VarOp (ISTORE , 2 ),
1342+ VarOp (ILOAD , 1 ),
1343+ VarOp (ILOAD , 2 ),
1344+ Op (IXOR ),
1345+ VarOp (ILOAD , 2 ),
1346+ Op (ISUB ),
1347+ Op (ICONST_1 ),
1348+ Op (ISHL ),
1349+ VarOp (ILOAD , 2 ),
1350+ Op (ICONST_1 ),
1351+ Op (IAND ),
1352+ Op (IOR ),
1353+ VarOp (ISTORE , 3 ),
1354+ VarOp (ILOAD , 3 ),
1355+ IntOp (BIPUSH , 26 ),
1356+ Jump (IF_ICMPGE , Label (26 )),
1357+ VarOp (ALOAD , 0 ),
1358+ Field (GETFIELD , " SourceMapWriter" , " out" , " Ljava/io/Writer;" ),
1359+ IntOp (BIPUSH , 65 ),
1360+ VarOp (ILOAD , 3 ),
1361+ Op (IADD ),
1362+ Invoke (INVOKEVIRTUAL , " java/io/Writer" , " write" , " (I)V" , false ),
1363+ Jump (GOTO , Label (31 )),
1364+ Label (26 ),
1365+ VarOp (ALOAD , 0 ),
1366+ VarOp (ILOAD , 3 ),
1367+ Invoke (INVOKESPECIAL , " SourceMapWriter" , " writeBase64VLQSlowPath$1" , " (I)V" , false ),
1368+ Label (31 ),
1369+ Op (RETURN ),
1370+ ))
1371+
1372+ // ---------------
1373+
1374+ val writeBase64VLQSlowPathMeth = getMethod(sourceMapWriterClass, " writeBase64VLQSlowPath$1" )
1375+
1376+ assertSameCode(writeBase64VLQSlowPathMeth, List (
1377+ VarOp (ILOAD , 1 ),
1378+ VarOp (ISTORE , 2 ),
1379+ Label (2 ),
1380+ VarOp (ILOAD , 2 ),
1381+ IntOp (BIPUSH , 31 ),
1382+ Op (IAND ),
1383+ VarOp (ISTORE , 3 ),
1384+ VarOp (ILOAD , 2 ),
1385+ Op (ICONST_5 ),
1386+ Op (IUSHR ),
1387+ VarOp (ISTORE , 2 ),
1388+ VarOp (ILOAD , 2 ),
1389+ Op (ICONST_0 ),
1390+ Jump (IF_ICMPEQ , Label (19 )),
1391+ VarOp (ILOAD , 3 ),
1392+ IntOp (BIPUSH , 32 ),
1393+ Op (IOR ),
1394+ VarOp (ISTORE , 3 ),
1395+ Label (19 ),
1396+ VarOp (ALOAD , 0 ),
1397+ Field (GETFIELD , " SourceMapWriter" , " out" , " Ljava/io/Writer;" ),
1398+ Field (GETSTATIC , " scala/Char$" , " MODULE$" , " Lscala/Char$;" ),
1399+ VarOp (ALOAD , 0 ),
1400+ Field (GETFIELD , " SourceMapWriter" , " Base64Map" , " Ljava/lang/String;" ),
1401+ VarOp (ILOAD , 3 ),
1402+ Invoke (INVOKEVIRTUAL , " java/lang/String" , " charAt" , " (I)C" , false ),
1403+ Invoke (INVOKEVIRTUAL , " scala/Char$" , " char2int" , " (C)I" , false ),
1404+ Invoke (INVOKEVIRTUAL , " java/io/Writer" , " write" , " (I)V" , false ),
1405+ VarOp (ILOAD , 2 ),
1406+ Op (ICONST_0 ),
1407+ Jump (IF_ICMPEQ , Label (35 )),
1408+ Op (ICONST_1 ),
1409+ Jump (GOTO , Label (38 )),
1410+ Label (35 ),
1411+ Op (ICONST_0 ),
1412+ Label (38 ),
1413+ Jump (IFNE , Label (2 )),
1414+ Op (RETURN ),
1415+ ))
1416+ }
1417+ }
1418+
10421419 @ Test
10431420 def getClazz : Unit = {
10441421 val source = """
0 commit comments