@@ -416,8 +416,12 @@ public final class BytecodeNode extends AbstractInstrumentableBytecodeNode imple
416
416
@ CompilationFinal (dimensions = 1 ) //
417
417
private final int [] stackOverflowErrorInfo ;
418
418
419
+ /**
420
+ * Outer array should be seen and used as a {@code @CompilationFinal volatile} array, while
421
+ * inner arrays can be seen as {@code final} arrays.
422
+ */
419
423
@ CompilationFinal (dimensions = 2 ) //
420
- private int [][] jsrBci = null ;
424
+ private volatile int [][] jsrBci = null ;
421
425
422
426
private final BytecodeStream bs ;
423
427
@@ -1033,31 +1037,81 @@ Object executeBodyFromBCI(VirtualFrame frame, int startBCI, int startTop, int st
1033
1037
continue loop ;
1034
1038
}
1035
1039
case RET : {
1036
- int targetBCI = getLocalReturnAddress (frame , bs .readLocalIndex1 (curBCI ));
1040
+ // Use final local variables to pass in lambdas.
1041
+ final int retOpBci = curBCI ;
1042
+ final int targetBCI = getLocalReturnAddress (frame , bs .readLocalIndex1 (curBCI ));
1037
1043
livenessAnalysis .performPostBCI (frame , curBCI , skipLivenessActions );
1038
- if (jsrBci == null ) {
1044
+
1045
+ // Safely obtain the known targets mappings.
1046
+ int [][] knownTargets = jsrBci ;
1047
+ if (knownTargets == null ) {
1039
1048
CompilerDirectives .transferToInterpreterAndInvalidate ();
1040
- jsrBci = new int [bs .endBCI ()][];
1049
+ atomic (() -> {
1050
+ // Double-checked locking.
1051
+ if (jsrBci == null ) {
1052
+ jsrBci = new int [bs .endBCI ()][];
1053
+ }
1054
+ });
1055
+ knownTargets = jsrBci ;
1041
1056
}
1042
- if (jsrBci [curBCI ] == null ) {
1057
+
1058
+ // Safely obtain the known targets for the current ret operation.
1059
+ int [] knownRets = VolatileArrayAccess .volatileRead (knownTargets , retOpBci );
1060
+ if (knownRets == null ) {
1043
1061
CompilerDirectives .transferToInterpreterAndInvalidate ();
1044
- jsrBci [curBCI ] = new int []{targetBCI };
1062
+ atomic (() -> {
1063
+ if (VolatileArrayAccess .volatileRead (jsrBci , retOpBci ) != null ) {
1064
+ return ;
1065
+ }
1066
+ /*
1067
+ * Be very careful on updating the known target bcis, as if another
1068
+ * thread reads the not fully initialized array, it may consider 0
1069
+ * to be a valid RET target, completely breaking PE.
1070
+ */
1071
+ int [] targets = new int []{targetBCI };
1072
+ // Also serves as a "final publication" barrier for the assignment
1073
+ // above.
1074
+ VolatileArrayAccess .volatileWrite (jsrBci , retOpBci , targets );
1075
+ });
1076
+ knownRets = VolatileArrayAccess .volatileRead (knownTargets , retOpBci );
1045
1077
}
1046
- for (int jsr : jsrBci [curBCI ]) {
1078
+ assert knownRets != null ;
1079
+
1080
+ // Lookup in the known targets to transform the return address to a
1081
+ // constant.
1082
+ for (int jsr : knownRets ) {
1047
1083
if (jsr == targetBCI ) {
1048
1084
CompilerAsserts .partialEvaluationConstant (jsr );
1049
- targetBCI = jsr ;
1050
1085
top += Bytecodes .stackEffectOf (RET );
1051
- nextStatementIndex = beforeJumpChecks (frame , curBCI , targetBCI , top , statementIndex , instrument , loopCount , skipLivenessActions );
1052
- curBCI = targetBCI ;
1086
+ nextStatementIndex = beforeJumpChecks (frame , curBCI , jsr , top , statementIndex , instrument , loopCount , skipLivenessActions );
1087
+ curBCI = jsr ;
1053
1088
continue loop ;
1054
1089
}
1055
1090
}
1091
+
1092
+ // Lookup failed: Add the current target to the known targets.
1056
1093
CompilerDirectives .transferToInterpreterAndInvalidate ();
1057
- jsrBci [curBCI ] = Arrays .copyOf (jsrBci [curBCI ], jsrBci [curBCI ].length + 1 );
1058
- jsrBci [curBCI ][jsrBci [curBCI ].length - 1 ] = targetBCI ;
1094
+ atomic (() -> {
1095
+ int [] currentRets = VolatileArrayAccess .volatileRead (jsrBci , retOpBci );
1096
+ for (int jsr : currentRets ) {
1097
+ if (jsr == targetBCI ) {
1098
+ // target has been added by another thread.
1099
+ return ;
1100
+ }
1101
+ }
1102
+ int [] updatedTargets = Arrays .copyOf (currentRets , currentRets .length + 1 );
1103
+ /*
1104
+ * Be very careful on updating the known target bcis, as if another
1105
+ * thread reads the not fully initialized array, it may consider 0 to be
1106
+ * a valid RET target, completely breaking PE.
1107
+ */
1108
+ updatedTargets [updatedTargets .length - 1 ] = targetBCI ;
1109
+ // Also serves as a "final publication" barrier for the assignment
1110
+ // above.
1111
+ VolatileArrayAccess .volatileWrite (jsrBci , retOpBci , updatedTargets );
1112
+ });
1059
1113
top += Bytecodes .stackEffectOf (RET );
1060
- nextStatementIndex = beforeJumpChecks (frame , curBCI , targetBCI , top , statementIndex , instrument , loopCount , skipLivenessActions );
1114
+ nextStatementIndex = beforeJumpChecks (frame , retOpBci , targetBCI , top , statementIndex , instrument , loopCount , skipLivenessActions );
1061
1115
curBCI = targetBCI ;
1062
1116
continue loop ;
1063
1117
}
@@ -1224,31 +1278,82 @@ Object executeBodyFromBCI(VirtualFrame frame, int startBCI, int startTop, int st
1224
1278
case IINC : setLocalInt (frame , bs .readLocalIndex2 (curBCI ), getLocalInt (frame , bs .readLocalIndex2 (curBCI )) + bs .readIncrement2 (curBCI )); break ;
1225
1279
// @formatter:on
1226
1280
case RET : {
1227
- int targetBCI = getLocalReturnAddress (frame , bs .readLocalIndex2 (curBCI ));
1281
+ // Use final local variables to pass in lambdas.
1282
+ final int retOpBci = curBCI ;
1283
+ final int targetBCI = getLocalReturnAddress (frame , bs .readLocalIndex1 (curBCI ));
1228
1284
livenessAnalysis .performPostBCI (frame , curBCI , skipLivenessActions );
1229
- if (jsrBci == null ) {
1285
+
1286
+ // Safely obtain the known targets mappings.
1287
+ int [][] knownTargets = jsrBci ;
1288
+ if (knownTargets == null ) {
1230
1289
CompilerDirectives .transferToInterpreterAndInvalidate ();
1231
- jsrBci = new int [bs .endBCI ()][];
1290
+ atomic (() -> {
1291
+ // Double-checked locking.
1292
+ if (jsrBci == null ) {
1293
+ jsrBci = new int [bs .endBCI ()][];
1294
+ }
1295
+ });
1296
+ knownTargets = jsrBci ;
1232
1297
}
1233
- if (jsrBci [curBCI ] == null ) {
1298
+
1299
+ // Safely obtain the known targets for the current ret operation.
1300
+ int [] knownRets = VolatileArrayAccess .volatileRead (knownTargets , retOpBci );
1301
+ if (knownRets == null ) {
1234
1302
CompilerDirectives .transferToInterpreterAndInvalidate ();
1235
- jsrBci [curBCI ] = new int []{targetBCI };
1303
+ atomic (() -> {
1304
+ if (VolatileArrayAccess .volatileRead (jsrBci , retOpBci ) != null ) {
1305
+ return ;
1306
+ }
1307
+ /*
1308
+ * Be very careful on updating the known target bcis, as if
1309
+ * another thread reads the not fully initialized array, it
1310
+ * may consider 0 to be a valid RET target, completely
1311
+ * breaking PE.
1312
+ */
1313
+ int [] targets = new int []{targetBCI };
1314
+ // Also serves as a "final publication" barrier for the
1315
+ // assignment above.
1316
+ VolatileArrayAccess .volatileWrite (jsrBci , retOpBci , targets );
1317
+ });
1318
+ knownRets = VolatileArrayAccess .volatileRead (knownTargets , retOpBci );
1236
1319
}
1237
- for (int jsr : jsrBci [curBCI ]) {
1320
+ assert knownRets != null ;
1321
+
1322
+ // Lookup in the known targets to transform the return address to a
1323
+ // constant.
1324
+ for (int jsr : knownRets ) {
1238
1325
if (jsr == targetBCI ) {
1239
1326
CompilerAsserts .partialEvaluationConstant (jsr );
1240
- targetBCI = jsr ;
1241
1327
top += Bytecodes .stackEffectOf (RET );
1242
- nextStatementIndex = beforeJumpChecks (frame , curBCI , targetBCI , top , statementIndex , instrument , loopCount , skipLivenessActions );
1243
- curBCI = targetBCI ;
1328
+ nextStatementIndex = beforeJumpChecks (frame , curBCI , jsr , top , statementIndex , instrument , loopCount , skipLivenessActions );
1329
+ curBCI = jsr ;
1244
1330
continue loop ;
1245
1331
}
1246
1332
}
1333
+
1334
+ // Lookup failed: Add the current target to the known targets.
1247
1335
CompilerDirectives .transferToInterpreterAndInvalidate ();
1248
- jsrBci [curBCI ] = Arrays .copyOf (jsrBci [curBCI ], jsrBci [curBCI ].length + 1 );
1249
- jsrBci [curBCI ][jsrBci [curBCI ].length - 1 ] = targetBCI ;
1336
+ atomic (() -> {
1337
+ int [] currentRets = VolatileArrayAccess .volatileRead (jsrBci , retOpBci );
1338
+ for (int jsr : currentRets ) {
1339
+ if (jsr == targetBCI ) {
1340
+ // target has been added by another thread.
1341
+ return ;
1342
+ }
1343
+ }
1344
+ int [] updatedTargets = Arrays .copyOf (currentRets , currentRets .length + 1 );
1345
+ /*
1346
+ * Be very careful on updating the known target bcis, as if
1347
+ * another thread reads the not fully initialized array, it may
1348
+ * consider 0 to be a valid RET target, completely breaking PE.
1349
+ */
1350
+ updatedTargets [updatedTargets .length - 1 ] = targetBCI ;
1351
+ // Also serves as a "final publication" barrier for the
1352
+ // assignment above.
1353
+ VolatileArrayAccess .volatileWrite (jsrBci , retOpBci , updatedTargets );
1354
+ });
1250
1355
top += Bytecodes .stackEffectOf (RET );
1251
- nextStatementIndex = beforeJumpChecks (frame , curBCI , targetBCI , top , statementIndex , instrument , loopCount , skipLivenessActions );
1356
+ nextStatementIndex = beforeJumpChecks (frame , retOpBci , targetBCI , top , statementIndex , instrument , loopCount , skipLivenessActions );
1252
1357
curBCI = targetBCI ;
1253
1358
continue loop ;
1254
1359
}
0 commit comments