275
275
import java .util .Set ;
276
276
import java .util .concurrent .locks .Lock ;
277
277
import java .util .concurrent .locks .ReentrantLock ;
278
- import java .util .function .Supplier ;
279
278
280
279
import com .oracle .truffle .api .Assumption ;
281
280
import com .oracle .truffle .api .CompilerAsserts ;
324
323
import com .oracle .truffle .espresso .classfile .constantpool .MethodRefConstant ;
325
324
import com .oracle .truffle .espresso .classfile .constantpool .MethodTypeConstant ;
326
325
import com .oracle .truffle .espresso .classfile .constantpool .PoolConstant ;
326
+ import com .oracle .truffle .espresso .classfile .constantpool .Resolvable ;
327
327
import com .oracle .truffle .espresso .classfile .constantpool .StringConstant ;
328
328
import com .oracle .truffle .espresso .classfile .descriptors .Signatures ;
329
329
import com .oracle .truffle .espresso .classfile .descriptors .Symbol ;
330
330
import com .oracle .truffle .espresso .classfile .descriptors .Symbol .Type ;
331
331
import com .oracle .truffle .espresso .classfile .perf .DebugCounter ;
332
- import com .oracle .truffle .espresso .constantpool .CallSiteLink ;
333
332
import com .oracle .truffle .espresso .constantpool .Resolution ;
334
333
import com .oracle .truffle .espresso .constantpool .ResolvedDynamicConstant ;
335
334
import com .oracle .truffle .espresso .constantpool .ResolvedWithInvokerClassMethodRefConstant ;
383
382
import com .oracle .truffle .espresso .runtime .GuestAllocator ;
384
383
import com .oracle .truffle .espresso .runtime .staticobject .StaticObject ;
385
384
import com .oracle .truffle .espresso .shared .resolver .CallKind ;
385
+ import com .oracle .truffle .espresso .shared .resolver .CallSiteType ;
386
386
import com .oracle .truffle .espresso .shared .resolver .FieldAccessType ;
387
387
import com .oracle .truffle .espresso .shared .resolver .ResolvedCall ;
388
388
import com .oracle .truffle .espresso .substitutions .Target_java_lang_invoke_MethodHandleNatives .SiteTypes ;
@@ -1754,25 +1754,26 @@ private StaticObject newReferenceArray(Klass componentType, int length) {
1754
1754
private BaseQuickNode getBaseQuickNode (int curBCI , int top , int statementIndex , BaseQuickNode quickNode ) {
1755
1755
// block while class redefinition is ongoing
1756
1756
getMethod ().getContext ().getClassRedefinition ().check ();
1757
- BaseQuickNode result = quickNode ;
1758
- result = atomic (() -> {
1759
- // re-check if node was already replaced by another thread
1760
- if (quickNode != nodes [readCPI (curBCI )]) {
1757
+ // re-check if node was already replaced by another thread
1758
+ if (quickNode != nodes [readCPI (curBCI )]) {
1759
+ // another thread beat us
1760
+ return nodes [readCPI (curBCI )];
1761
+ }
1762
+ BytecodeStream original = new BytecodeStream (getMethodVersion ().getCodeAttribute ().getOriginalCode ());
1763
+ char originalCpi = original .readCPI (curBCI );
1764
+ int originalOpcode = original .currentBC (curBCI );
1765
+ ResolvedInvoke resolvedInvoke = reResolvedInvoke (originalOpcode , originalCpi );
1766
+ return atomic (() -> {
1767
+ char cpi = readCPI (curBCI );
1768
+ if (quickNode != nodes [cpi ]) {
1761
1769
// another thread beat us
1762
- return nodes [readCPI ( curBCI ) ];
1770
+ return nodes [cpi ];
1763
1771
} else {
1764
- // other threads might still have beat us but if
1765
- // so, the resolution failed and so will we below
1766
- BytecodeStream original = new BytecodeStream (getMethodVersion ().getCodeAttribute ().getOriginalCode ());
1767
- char cpi = original .readCPI (curBCI );
1768
- int nodeOpcode = original .currentBC (curBCI );
1769
- Method resolutionSeed = resolveMethodNoCache (nodeOpcode , cpi );
1770
- BaseQuickNode toInsert = insert (dispatchQuickened (top , curBCI , cpi , nodeOpcode , statementIndex , resolutionSeed , getMethod ().getContext ().getEspressoEnv ().bytecodeLevelInlining ));
1771
- nodes [readCPI (curBCI )] = toInsert ;
1772
- return toInsert ;
1772
+ BaseQuickNode newNode = insert (dispatchQuickened (top , curBCI , originalOpcode , statementIndex , resolvedInvoke , getMethod ().getContext ().getEspressoEnv ().bytecodeLevelInlining ));
1773
+ nodes [cpi ] = newNode ;
1774
+ return newNode ;
1773
1775
}
1774
1776
});
1775
- return result ;
1776
1777
}
1777
1778
1778
1779
private Object getReturnValueAsObject (VirtualFrame frame , int top ) {
@@ -2192,20 +2193,52 @@ private BaseQuickNode injectQuick(int curBCI, BaseQuickNode quick, int opcode) {
2192
2193
return quick ;
2193
2194
}
2194
2195
2195
- private BaseQuickNode tryPatchQuick (int curBCI , Supplier <BaseQuickNode > newQuickNode ) {
2196
+ @ FunctionalInterface
2197
+ private interface QuickNodeFactory <T > {
2198
+ BaseQuickNode get (T t );
2199
+ }
2200
+
2201
+ @ FunctionalInterface
2202
+ private interface QuickNodeResolver <T > {
2203
+ T get (char cpi );
2204
+ }
2205
+
2206
+ private <T > BaseQuickNode tryPatchQuick (int curBCI , QuickNodeResolver <T > resolver , QuickNodeFactory <T > newQuickNode ) {
2207
+ Object found = atomic (() -> {
2208
+ if (bs .currentVolatileBC (curBCI ) == QUICK ) {
2209
+ return nodes [readCPI (curBCI )];
2210
+ } else {
2211
+ return readCPI (curBCI );
2212
+ }
2213
+ });
2214
+ if (found instanceof BaseQuickNode ) {
2215
+ return (BaseQuickNode ) found ;
2216
+ }
2217
+ char cpi = (char ) found ;
2218
+ // Perform resolution outside the lock: it can call arbitrary guest code.
2219
+ T resolved = resolver .get (cpi );
2196
2220
return atomic (() -> {
2197
2221
if (bs .currentVolatileBC (curBCI ) == QUICK ) {
2198
2222
return nodes [readCPI (curBCI )];
2199
2223
} else {
2200
- return injectQuick (curBCI , newQuickNode .get (), QUICK );
2224
+ return injectQuick (curBCI , newQuickNode .get (resolved ), QUICK );
2201
2225
}
2202
2226
});
2203
2227
}
2204
2228
2229
+ @ FunctionalInterface
2230
+ private interface QuickNodeSupplier {
2231
+ BaseQuickNode get ();
2232
+ }
2233
+
2234
+ private BaseQuickNode tryPatchQuick (int curBCI , QuickNodeSupplier newQuickNode ) {
2235
+ return tryPatchQuick (curBCI , cpi -> null , unused -> newQuickNode .get ());
2236
+ }
2237
+
2205
2238
private int quickenCheckCast (VirtualFrame frame , int top , int curBCI , int opcode ) {
2206
2239
CompilerAsserts .neverPartOfCompilation ();
2207
2240
assert opcode == CHECKCAST ;
2208
- BaseQuickNode quick = tryPatchQuick (curBCI , () -> new CheckCastQuickNode ( resolveType (CHECKCAST , readCPI ( curBCI )) , top , curBCI ));
2241
+ BaseQuickNode quick = tryPatchQuick (curBCI , cpi -> resolveType (CHECKCAST , cpi ), k -> new CheckCastQuickNode ( k , top , curBCI ));
2209
2242
quick .execute (frame , false );
2210
2243
assert Bytecodes .stackEffectOf (opcode ) == 0 ;
2211
2244
return 0 ; // Bytecodes.stackEffectOf(opcode);
@@ -2214,7 +2247,7 @@ private int quickenCheckCast(VirtualFrame frame, int top, int curBCI, int opcode
2214
2247
private int quickenInstanceOf (VirtualFrame frame , int top , int curBCI , int opcode ) {
2215
2248
CompilerAsserts .neverPartOfCompilation ();
2216
2249
assert opcode == INSTANCEOF ;
2217
- BaseQuickNode quick = tryPatchQuick (curBCI , () -> new InstanceOfQuickNode ( resolveType (INSTANCEOF , readCPI ( curBCI )) , top , curBCI ));
2250
+ BaseQuickNode quick = tryPatchQuick (curBCI , cpi -> resolveType (INSTANCEOF , cpi ), k -> new InstanceOfQuickNode ( k , top , curBCI ));
2218
2251
quick .execute (frame , false );
2219
2252
assert Bytecodes .stackEffectOf (opcode ) == 0 ;
2220
2253
return 0 ; // Bytecodes.stackEffectOf(opcode);
@@ -2240,24 +2273,19 @@ private InvokeQuickNode quickenInvoke(int top, int curBCI, int opcode, int state
2240
2273
QUICKENED_INVOKES .inc ();
2241
2274
CompilerDirectives .transferToInterpreterAndInvalidate ();
2242
2275
assert Bytecodes .isInvoke (opcode );
2243
- InvokeQuickNode quick = (InvokeQuickNode ) tryPatchQuick (curBCI , () -> {
2244
- // During resolution of the symbolic reference to the method, any of the exceptions
2245
- // pertaining to method resolution (§5.4.3.3) can be thrown.
2246
- char cpi = readCPI (curBCI );
2247
- Method resolutionSeed = resolveMethod (opcode , cpi );
2248
- return dispatchQuickened (top , curBCI , cpi , opcode , statementIndex , resolutionSeed , getMethod ().getContext ().getEspressoEnv ().bytecodeLevelInlining );
2249
- });
2276
+ InvokeQuickNode quick = (InvokeQuickNode ) tryPatchQuick (curBCI , cpi -> getResolvedInvoke (opcode , cpi ),
2277
+ resolvedInvoke -> dispatchQuickened (top , curBCI , opcode , statementIndex , resolvedInvoke , getMethod ().getContext ().getEspressoEnv ().bytecodeLevelInlining ));
2250
2278
return quick ;
2251
2279
}
2252
2280
2253
2281
/**
2254
2282
* Revert speculative quickening e.g. revert inlined fields accessors to a normal invoke.
2255
2283
* INVOKEVIRTUAL -> QUICK (InlinedGetter/SetterNode) -> QUICK (InvokeVirtualNode)
2256
2284
*/
2257
- public int reQuickenInvoke (VirtualFrame frame , int top , int opcode , int curBCI , int statementIndex , Method resolutionSeed ) {
2285
+ public int reQuickenInvoke (VirtualFrame frame , int top , int opcode , int curBCI , int statementIndex ) {
2258
2286
CompilerAsserts .neverPartOfCompilation ();
2259
2287
assert Bytecodes .isInvoke (opcode );
2260
- BaseQuickNode invoke = generifyInlinedMethodNode (top , opcode , curBCI , statementIndex , resolutionSeed );
2288
+ BaseQuickNode invoke = generifyInlinedMethodNode (top , opcode , curBCI , statementIndex );
2261
2289
// Perform the call outside of the lock.
2262
2290
return invoke .execute (frame , false );
2263
2291
}
@@ -2292,8 +2320,9 @@ private BaseQuickNode replaceQuickAt(int opcode, int curBCI, BaseQuickNode old,
2292
2320
* Reverts Bytecode-level method inlining at the current bci, in case instrumentation starts
2293
2321
* happening on this node.
2294
2322
*/
2295
- public BaseQuickNode generifyInlinedMethodNode (int top , int opcode , int curBCI , int statementIndex , Method resolutionSeed ) {
2323
+ public BaseQuickNode generifyInlinedMethodNode (int top , int opcode , int curBCI , int statementIndex ) {
2296
2324
CompilerAsserts .neverPartOfCompilation ();
2325
+ ResolvedInvoke resolvedInvoke = getResolvedInvoke (opcode , readOriginalCPI (curBCI ));
2297
2326
return atomic (() -> {
2298
2327
assert bs .currentBC (curBCI ) == QUICK ;
2299
2328
char nodeIndex = readCPI (curBCI );
@@ -2303,7 +2332,7 @@ public BaseQuickNode generifyInlinedMethodNode(int top, int opcode, int curBCI,
2303
2332
// Might be racy, as read is not volatile, but redoing the work should be OK.
2304
2333
return currentQuick ;
2305
2334
}
2306
- BaseQuickNode invoke = dispatchQuickened (top , curBCI , readOriginalCPI ( curBCI ), opcode , statementIndex , resolutionSeed , false );
2335
+ BaseQuickNode invoke = dispatchQuickened (top , curBCI , opcode , statementIndex , resolvedInvoke , false );
2307
2336
nodes [nodeIndex ] = currentQuick .replace (invoke );
2308
2337
return invoke ;
2309
2338
});
@@ -2409,12 +2438,8 @@ private int quickenArrayStore(final VirtualFrame frame, int top, int curBCI, int
2409
2438
2410
2439
// endregion quickenForeign
2411
2440
2412
- private InvokeQuickNode dispatchQuickened (int top , int curBCI , char cpi , int opcode , int statementIndex , Method resolutionSeed , boolean allowBytecodeInlining ) {
2413
-
2414
- Klass symbolicRef = Resolution .getResolvedHolderKlass ((MethodRefConstant .Indexes ) getConstantPool ().methodAt (cpi ), getConstantPool (), getDeclaringKlass ());
2415
- ResolvedCall <Klass , Method , Field > resolvedCall = EspressoLinkResolver .resolveCallSite (getContext (),
2416
- getDeclaringKlass (), resolutionSeed , SiteTypes .callSiteFromOpCode (opcode ), symbolicRef );
2417
-
2441
+ private InvokeQuickNode dispatchQuickened (int top , int curBCI , int opcode , int statementIndex , ResolvedInvoke resolvedInvoke , boolean allowBytecodeInlining ) {
2442
+ ResolvedCall <Klass , Method , Field > resolvedCall = resolvedInvoke .resolvedCall ();
2418
2443
Method resolved = resolvedCall .getResolvedMethod ();
2419
2444
CallKind callKind = resolvedCall .getCallKind ();
2420
2445
@@ -2430,13 +2455,7 @@ private InvokeQuickNode dispatchQuickened(int top, int curBCI, char cpi, int opc
2430
2455
}
2431
2456
2432
2457
if (resolved .isPolySignatureIntrinsic ()) {
2433
- MethodHandleInvoker invoker = null ;
2434
- // There might be an invoker if it's an InvokeGeneric
2435
- if (getConstantPool ().resolvedMethodRefAt (getDeclaringKlass (), cpi ) instanceof ResolvedWithInvokerClassMethodRefConstant withInvoker ) {
2436
- invoker = withInvoker .invoker ();
2437
- assert invoker == null || ((opcode == INVOKEVIRTUAL || opcode == INVOKESPECIAL ) && resolved .isInvokeIntrinsic ());
2438
- }
2439
- return new InvokeHandleNode (resolved , invoker , top , curBCI );
2458
+ return new InvokeHandleNode (resolved , resolvedInvoke .invoker (), top , curBCI );
2440
2459
} else {
2441
2460
// @formatter:off
2442
2461
return switch (callKind ) {
@@ -2466,39 +2485,10 @@ private RuntimeException throwBoundary(ObjectKlass exceptionKlass, String messag
2466
2485
2467
2486
private int quickenInvokeDynamic (final VirtualFrame frame , int top , int curBCI , int opcode ) {
2468
2487
CompilerDirectives .transferToInterpreterAndInvalidate ();
2469
- assert (Bytecodes .INVOKEDYNAMIC == opcode );
2470
- RuntimeConstantPool pool = getConstantPool ();
2471
- BaseQuickNode quick = null ;
2472
- int indyIndex = -1 ;
2473
- Lock lock = getLock ();
2474
- try {
2475
- lock .lock ();
2476
- if (bs .currentVolatileBC (curBCI ) == QUICK ) {
2477
- // Check if someone did the job for us. Defer the call until we are out of the lock.
2478
- quick = nodes [readCPI (curBCI )];
2479
- } else {
2480
- // fetch indy under lock.
2481
- indyIndex = readCPI (curBCI );
2482
- }
2483
- } finally {
2484
- lock .unlock ();
2485
- }
2486
- if (quick != null ) {
2487
- // Do invocation outside of the lock.
2488
- return quick .execute (frame , false ) - Bytecodes .stackEffectOf (opcode );
2489
- }
2490
- // Resolution should happen outside of the bytecode patching lock.
2491
- CallSiteLink link = pool .linkInvokeDynamic (getMethod ().getDeclaringKlass (), indyIndex , curBCI , getMethod ());
2492
-
2493
- // re-lock to check if someone did the job for us, since this was a heavy operation.
2494
- quick = atomic (() -> {
2495
- if (bs .currentVolatileBC (curBCI ) == QUICK ) {
2496
- // someone beat us to it, just trust him.
2497
- return nodes [readCPI (curBCI )];
2498
- } else {
2499
- return injectQuick (curBCI , new InvokeDynamicCallSiteNode (link .getMemberName (), link .getUnboxedAppendix (), link .getParsedSignature (), getMethod ().getMeta (), top , curBCI ), QUICK );
2500
- }
2501
- });
2488
+ assert opcode == Bytecodes .INVOKEDYNAMIC ;
2489
+ BaseQuickNode quick = tryPatchQuick (curBCI ,
2490
+ cpi -> getConstantPool ().linkInvokeDynamic (getMethod ().getDeclaringKlass (), cpi , curBCI , getMethod ()),
2491
+ link -> new InvokeDynamicCallSiteNode (link .getMemberName (), link .getUnboxedAppendix (), link .getParsedSignature (), getMethod ().getMeta (), top , curBCI ));
2502
2492
return quick .execute (frame , false ) - Bytecodes .stackEffectOf (opcode );
2503
2493
}
2504
2494
@@ -2512,17 +2502,6 @@ public Klass resolveType(int opcode, char cpi) {
2512
2502
return getConstantPool ().resolvedKlassAt (getDeclaringKlass (), cpi );
2513
2503
}
2514
2504
2515
- public Method resolveMethod (int opcode , char cpi ) {
2516
- assert Bytecodes .isInvoke (opcode );
2517
- return getConstantPool ().resolvedMethodAt (getDeclaringKlass (), cpi );
2518
- }
2519
-
2520
- private Method resolveMethodNoCache (int opcode , char cpi ) {
2521
- CompilerAsserts .neverPartOfCompilation ();
2522
- assert Bytecodes .isInvoke (opcode );
2523
- return getConstantPool ().resolvedMethodAtNoCache (getDeclaringKlass (), cpi );
2524
- }
2525
-
2526
2505
private Field resolveField (int opcode , char cpi ) {
2527
2506
assert opcode == GETFIELD || opcode == GETSTATIC || opcode == PUTFIELD || opcode == PUTSTATIC ;
2528
2507
Field field = getConstantPool ().resolvedFieldAt (getMethod ().getDeclaringKlass (), cpi );
@@ -2534,6 +2513,34 @@ private Field resolveField(int opcode, char cpi) {
2534
2513
return field ;
2535
2514
}
2536
2515
2516
+ private record ResolvedInvoke (ResolvedCall <Klass , Method , Field > resolvedCall , MethodHandleInvoker invoker ) {
2517
+ }
2518
+
2519
+ private ResolvedInvoke reResolvedInvoke (int opcode , char cpi ) {
2520
+ getConstantPool ().resolveMethodAndUpdate (getDeclaringKlass (), cpi );
2521
+ return getResolvedInvoke (opcode , cpi );
2522
+ }
2523
+
2524
+ private ResolvedInvoke getResolvedInvoke (int opcode , char cpi ) {
2525
+ assert !lockIsHeld ();
2526
+ // During resolution of the symbolic reference to the method, any of the exceptions
2527
+ // pertaining to method resolution (§5.4.3.3) can be thrown.
2528
+ MethodRefConstant methodRefConstant = getConstantPool ().resolvedMethodRefAt (getDeclaringKlass (), cpi );
2529
+ Method resolutionSeed = (Method ) ((Resolvable .ResolvedConstant ) methodRefConstant ).value ();
2530
+
2531
+ Klass symbolicRef = Resolution .getResolvedHolderKlass ((MethodRefConstant .Indexes ) getConstantPool ().methodAt (cpi ), getConstantPool (), getDeclaringKlass ());
2532
+ CallSiteType callSiteType = SiteTypes .callSiteFromOpCode (opcode );
2533
+ ResolvedCall <Klass , Method , Field > resolvedCall = EspressoLinkResolver .resolveCallSite (getContext (), getDeclaringKlass (), resolutionSeed , callSiteType , symbolicRef );
2534
+ MethodHandleInvoker invoker = null ;
2535
+ // There might be an invoker if it's an InvokeGeneric
2536
+ if (methodRefConstant instanceof ResolvedWithInvokerClassMethodRefConstant withInvoker ) {
2537
+ invoker = withInvoker .invoker ();
2538
+ assert invoker == null || ((opcode == INVOKEVIRTUAL || opcode == INVOKESPECIAL ) && resolvedCall .getResolvedMethod ().isInvokeIntrinsic ());
2539
+ }
2540
+
2541
+ return new ResolvedInvoke (resolvedCall , invoker );
2542
+ }
2543
+
2537
2544
// endregion Class/Method/Field resolution
2538
2545
2539
2546
// region Instance/array allocation
0 commit comments