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 ;
@@ -1752,25 +1752,26 @@ private StaticObject newReferenceArray(Klass componentType, int length) {
1752
1752
private BaseQuickNode getBaseQuickNode (int curBCI , int top , int statementIndex , BaseQuickNode quickNode ) {
1753
1753
// block while class redefinition is ongoing
1754
1754
getMethod ().getContext ().getClassRedefinition ().check ();
1755
- BaseQuickNode result = quickNode ;
1756
- result = atomic (() -> {
1757
- // re-check if node was already replaced by another thread
1758
- if (quickNode != nodes [readCPI (curBCI )]) {
1755
+ // re-check if node was already replaced by another thread
1756
+ if (quickNode != nodes [readCPI (curBCI )]) {
1757
+ // another thread beat us
1758
+ return nodes [readCPI (curBCI )];
1759
+ }
1760
+ BytecodeStream original = new BytecodeStream (getMethodVersion ().getCodeAttribute ().getOriginalCode ());
1761
+ char originalCpi = original .readCPI (curBCI );
1762
+ int originalOpcode = original .currentBC (curBCI );
1763
+ ResolvedInvoke resolvedInvoke = reResolvedInvoke (originalOpcode , originalCpi );
1764
+ return atomic (() -> {
1765
+ char cpi = readCPI (curBCI );
1766
+ if (quickNode != nodes [cpi ]) {
1759
1767
// another thread beat us
1760
- return nodes [readCPI ( curBCI ) ];
1768
+ return nodes [cpi ];
1761
1769
} else {
1762
- // other threads might still have beat us but if
1763
- // so, the resolution failed and so will we below
1764
- BytecodeStream original = new BytecodeStream (getMethodVersion ().getCodeAttribute ().getOriginalCode ());
1765
- char cpi = original .readCPI (curBCI );
1766
- int nodeOpcode = original .currentBC (curBCI );
1767
- Method resolutionSeed = resolveMethodNoCache (nodeOpcode , cpi );
1768
- BaseQuickNode toInsert = insert (dispatchQuickened (top , curBCI , cpi , nodeOpcode , statementIndex , resolutionSeed , getMethod ().getContext ().getEspressoEnv ().bytecodeLevelInlining ));
1769
- nodes [readCPI (curBCI )] = toInsert ;
1770
- return toInsert ;
1770
+ BaseQuickNode newNode = insert (dispatchQuickened (top , curBCI , originalOpcode , statementIndex , resolvedInvoke , getMethod ().getContext ().getEspressoEnv ().bytecodeLevelInlining ));
1771
+ nodes [cpi ] = newNode ;
1772
+ return newNode ;
1771
1773
}
1772
1774
});
1773
- return result ;
1774
1775
}
1775
1776
1776
1777
private Object getReturnValueAsObject (VirtualFrame frame , int top ) {
@@ -2179,20 +2180,52 @@ private BaseQuickNode injectQuick(int curBCI, BaseQuickNode quick, int opcode) {
2179
2180
return quick ;
2180
2181
}
2181
2182
2182
- private BaseQuickNode tryPatchQuick (int curBCI , Supplier <BaseQuickNode > newQuickNode ) {
2183
+ @ FunctionalInterface
2184
+ private interface QuickNodeFactory <T > {
2185
+ BaseQuickNode get (T t );
2186
+ }
2187
+
2188
+ @ FunctionalInterface
2189
+ private interface QuickNodeResolver <T > {
2190
+ T get (char cpi );
2191
+ }
2192
+
2193
+ private <T > BaseQuickNode tryPatchQuick (int curBCI , QuickNodeResolver <T > resolver , QuickNodeFactory <T > newQuickNode ) {
2194
+ Object found = atomic (() -> {
2195
+ if (bs .currentVolatileBC (curBCI ) == QUICK ) {
2196
+ return nodes [readCPI (curBCI )];
2197
+ } else {
2198
+ return readCPI (curBCI );
2199
+ }
2200
+ });
2201
+ if (found instanceof BaseQuickNode ) {
2202
+ return (BaseQuickNode ) found ;
2203
+ }
2204
+ char cpi = (char ) found ;
2205
+ // Perform resolution outside the lock: it can call arbitrary guest code.
2206
+ T resolved = resolver .get (cpi );
2183
2207
return atomic (() -> {
2184
2208
if (bs .currentVolatileBC (curBCI ) == QUICK ) {
2185
2209
return nodes [readCPI (curBCI )];
2186
2210
} else {
2187
- return injectQuick (curBCI , newQuickNode .get (), QUICK );
2211
+ return injectQuick (curBCI , newQuickNode .get (resolved ), QUICK );
2188
2212
}
2189
2213
});
2190
2214
}
2191
2215
2216
+ @ FunctionalInterface
2217
+ private interface QuickNodeSupplier {
2218
+ BaseQuickNode get ();
2219
+ }
2220
+
2221
+ private BaseQuickNode tryPatchQuick (int curBCI , QuickNodeSupplier newQuickNode ) {
2222
+ return tryPatchQuick (curBCI , cpi -> null , unused -> newQuickNode .get ());
2223
+ }
2224
+
2192
2225
private int quickenCheckCast (VirtualFrame frame , int top , int curBCI , int opcode ) {
2193
2226
CompilerAsserts .neverPartOfCompilation ();
2194
2227
assert opcode == CHECKCAST ;
2195
- BaseQuickNode quick = tryPatchQuick (curBCI , () -> new CheckCastQuickNode ( resolveType (CHECKCAST , readCPI ( curBCI )) , top , curBCI ));
2228
+ BaseQuickNode quick = tryPatchQuick (curBCI , cpi -> resolveType (CHECKCAST , cpi ), k -> new CheckCastQuickNode ( k , top , curBCI ));
2196
2229
quick .execute (frame , false );
2197
2230
assert Bytecodes .stackEffectOf (opcode ) == 0 ;
2198
2231
return 0 ; // Bytecodes.stackEffectOf(opcode);
@@ -2201,7 +2234,7 @@ private int quickenCheckCast(VirtualFrame frame, int top, int curBCI, int opcode
2201
2234
private int quickenInstanceOf (VirtualFrame frame , int top , int curBCI , int opcode ) {
2202
2235
CompilerAsserts .neverPartOfCompilation ();
2203
2236
assert opcode == INSTANCEOF ;
2204
- BaseQuickNode quick = tryPatchQuick (curBCI , () -> new InstanceOfQuickNode ( resolveType (INSTANCEOF , readCPI ( curBCI )) , top , curBCI ));
2237
+ BaseQuickNode quick = tryPatchQuick (curBCI , cpi -> resolveType (INSTANCEOF , cpi ), k -> new InstanceOfQuickNode ( k , top , curBCI ));
2205
2238
quick .execute (frame , false );
2206
2239
assert Bytecodes .stackEffectOf (opcode ) == 0 ;
2207
2240
return 0 ; // Bytecodes.stackEffectOf(opcode);
@@ -2227,24 +2260,19 @@ private InvokeQuickNode quickenInvoke(int top, int curBCI, int opcode, int state
2227
2260
QUICKENED_INVOKES .inc ();
2228
2261
CompilerDirectives .transferToInterpreterAndInvalidate ();
2229
2262
assert Bytecodes .isInvoke (opcode );
2230
- InvokeQuickNode quick = (InvokeQuickNode ) tryPatchQuick (curBCI , () -> {
2231
- // During resolution of the symbolic reference to the method, any of the exceptions
2232
- // pertaining to method resolution (§5.4.3.3) can be thrown.
2233
- char cpi = readCPI (curBCI );
2234
- Method resolutionSeed = resolveMethod (opcode , cpi );
2235
- return dispatchQuickened (top , curBCI , cpi , opcode , statementIndex , resolutionSeed , getMethod ().getContext ().getEspressoEnv ().bytecodeLevelInlining );
2236
- });
2263
+ InvokeQuickNode quick = (InvokeQuickNode ) tryPatchQuick (curBCI , cpi -> getResolvedInvoke (opcode , cpi ),
2264
+ resolvedInvoke -> dispatchQuickened (top , curBCI , opcode , statementIndex , resolvedInvoke , getMethod ().getContext ().getEspressoEnv ().bytecodeLevelInlining ));
2237
2265
return quick ;
2238
2266
}
2239
2267
2240
2268
/**
2241
2269
* Revert speculative quickening e.g. revert inlined fields accessors to a normal invoke.
2242
2270
* INVOKEVIRTUAL -> QUICK (InlinedGetter/SetterNode) -> QUICK (InvokeVirtualNode)
2243
2271
*/
2244
- public int reQuickenInvoke (VirtualFrame frame , int top , int opcode , int curBCI , int statementIndex , Method resolutionSeed ) {
2272
+ public int reQuickenInvoke (VirtualFrame frame , int top , int opcode , int curBCI , int statementIndex ) {
2245
2273
CompilerAsserts .neverPartOfCompilation ();
2246
2274
assert Bytecodes .isInvoke (opcode );
2247
- BaseQuickNode invoke = generifyInlinedMethodNode (top , opcode , curBCI , statementIndex , resolutionSeed );
2275
+ BaseQuickNode invoke = generifyInlinedMethodNode (top , opcode , curBCI , statementIndex );
2248
2276
// Perform the call outside of the lock.
2249
2277
return invoke .execute (frame , false );
2250
2278
}
@@ -2279,8 +2307,9 @@ private BaseQuickNode replaceQuickAt(int opcode, int curBCI, BaseQuickNode old,
2279
2307
* Reverts Bytecode-level method inlining at the current bci, in case instrumentation starts
2280
2308
* happening on this node.
2281
2309
*/
2282
- public BaseQuickNode generifyInlinedMethodNode (int top , int opcode , int curBCI , int statementIndex , Method resolutionSeed ) {
2310
+ public BaseQuickNode generifyInlinedMethodNode (int top , int opcode , int curBCI , int statementIndex ) {
2283
2311
CompilerAsserts .neverPartOfCompilation ();
2312
+ ResolvedInvoke resolvedInvoke = getResolvedInvoke (opcode , readOriginalCPI (curBCI ));
2284
2313
return atomic (() -> {
2285
2314
assert bs .currentBC (curBCI ) == QUICK ;
2286
2315
char nodeIndex = readCPI (curBCI );
@@ -2290,7 +2319,7 @@ public BaseQuickNode generifyInlinedMethodNode(int top, int opcode, int curBCI,
2290
2319
// Might be racy, as read is not volatile, but redoing the work should be OK.
2291
2320
return currentQuick ;
2292
2321
}
2293
- BaseQuickNode invoke = dispatchQuickened (top , curBCI , readOriginalCPI ( curBCI ), opcode , statementIndex , resolutionSeed , false );
2322
+ BaseQuickNode invoke = dispatchQuickened (top , curBCI , opcode , statementIndex , resolvedInvoke , false );
2294
2323
nodes [nodeIndex ] = currentQuick .replace (invoke );
2295
2324
return invoke ;
2296
2325
});
@@ -2396,12 +2425,8 @@ private int quickenArrayStore(final VirtualFrame frame, int top, int curBCI, int
2396
2425
2397
2426
// endregion quickenForeign
2398
2427
2399
- private InvokeQuickNode dispatchQuickened (int top , int curBCI , char cpi , int opcode , int statementIndex , Method resolutionSeed , boolean allowBytecodeInlining ) {
2400
-
2401
- Klass symbolicRef = Resolution .getResolvedHolderKlass ((MethodRefConstant .Indexes ) getConstantPool ().methodAt (cpi ), getConstantPool (), getDeclaringKlass ());
2402
- ResolvedCall <Klass , Method , Field > resolvedCall = EspressoLinkResolver .resolveCallSite (getContext (),
2403
- getDeclaringKlass (), resolutionSeed , SiteTypes .callSiteFromOpCode (opcode ), symbolicRef );
2404
-
2428
+ private InvokeQuickNode dispatchQuickened (int top , int curBCI , int opcode , int statementIndex , ResolvedInvoke resolvedInvoke , boolean allowBytecodeInlining ) {
2429
+ ResolvedCall <Klass , Method , Field > resolvedCall = resolvedInvoke .resolvedCall ();
2405
2430
Method resolved = resolvedCall .getResolvedMethod ();
2406
2431
CallKind callKind = resolvedCall .getCallKind ();
2407
2432
@@ -2417,13 +2442,7 @@ private InvokeQuickNode dispatchQuickened(int top, int curBCI, char cpi, int opc
2417
2442
}
2418
2443
2419
2444
if (resolved .isPolySignatureIntrinsic ()) {
2420
- MethodHandleInvoker invoker = null ;
2421
- // There might be an invoker if it's an InvokeGeneric
2422
- if (getConstantPool ().resolvedMethodRefAt (getDeclaringKlass (), cpi ) instanceof ResolvedWithInvokerClassMethodRefConstant withInvoker ) {
2423
- invoker = withInvoker .invoker ();
2424
- assert invoker == null || ((opcode == INVOKEVIRTUAL || opcode == INVOKESPECIAL ) && resolved .isInvokeIntrinsic ());
2425
- }
2426
- return new InvokeHandleNode (resolved , invoker , top , curBCI );
2445
+ return new InvokeHandleNode (resolved , resolvedInvoke .invoker (), top , curBCI );
2427
2446
} else {
2428
2447
// @formatter:off
2429
2448
return switch (callKind ) {
@@ -2453,39 +2472,10 @@ private RuntimeException throwBoundary(ObjectKlass exceptionKlass, String messag
2453
2472
2454
2473
private int quickenInvokeDynamic (final VirtualFrame frame , int top , int curBCI , int opcode ) {
2455
2474
CompilerDirectives .transferToInterpreterAndInvalidate ();
2456
- assert (Bytecodes .INVOKEDYNAMIC == opcode );
2457
- RuntimeConstantPool pool = getConstantPool ();
2458
- BaseQuickNode quick = null ;
2459
- int indyIndex = -1 ;
2460
- Lock lock = getLock ();
2461
- try {
2462
- lock .lock ();
2463
- if (bs .currentVolatileBC (curBCI ) == QUICK ) {
2464
- // Check if someone did the job for us. Defer the call until we are out of the lock.
2465
- quick = nodes [readCPI (curBCI )];
2466
- } else {
2467
- // fetch indy under lock.
2468
- indyIndex = readCPI (curBCI );
2469
- }
2470
- } finally {
2471
- lock .unlock ();
2472
- }
2473
- if (quick != null ) {
2474
- // Do invocation outside of the lock.
2475
- return quick .execute (frame , false ) - Bytecodes .stackEffectOf (opcode );
2476
- }
2477
- // Resolution should happen outside of the bytecode patching lock.
2478
- CallSiteLink link = pool .linkInvokeDynamic (getMethod ().getDeclaringKlass (), indyIndex , curBCI , getMethod ());
2479
-
2480
- // re-lock to check if someone did the job for us, since this was a heavy operation.
2481
- quick = atomic (() -> {
2482
- if (bs .currentVolatileBC (curBCI ) == QUICK ) {
2483
- // someone beat us to it, just trust him.
2484
- return nodes [readCPI (curBCI )];
2485
- } else {
2486
- return injectQuick (curBCI , new InvokeDynamicCallSiteNode (link .getMemberName (), link .getUnboxedAppendix (), link .getParsedSignature (), getMethod ().getMeta (), top , curBCI ), QUICK );
2487
- }
2488
- });
2475
+ assert opcode == Bytecodes .INVOKEDYNAMIC ;
2476
+ BaseQuickNode quick = tryPatchQuick (curBCI ,
2477
+ cpi -> getConstantPool ().linkInvokeDynamic (getMethod ().getDeclaringKlass (), cpi , curBCI , getMethod ()),
2478
+ link -> new InvokeDynamicCallSiteNode (link .getMemberName (), link .getUnboxedAppendix (), link .getParsedSignature (), getMethod ().getMeta (), top , curBCI ));
2489
2479
return quick .execute (frame , false ) - Bytecodes .stackEffectOf (opcode );
2490
2480
}
2491
2481
@@ -2499,17 +2489,6 @@ public Klass resolveType(int opcode, char cpi) {
2499
2489
return getConstantPool ().resolvedKlassAt (getDeclaringKlass (), cpi );
2500
2490
}
2501
2491
2502
- public Method resolveMethod (int opcode , char cpi ) {
2503
- assert Bytecodes .isInvoke (opcode );
2504
- return getConstantPool ().resolvedMethodAt (getDeclaringKlass (), cpi );
2505
- }
2506
-
2507
- private Method resolveMethodNoCache (int opcode , char cpi ) {
2508
- CompilerAsserts .neverPartOfCompilation ();
2509
- assert Bytecodes .isInvoke (opcode );
2510
- return getConstantPool ().resolvedMethodAtNoCache (getDeclaringKlass (), cpi );
2511
- }
2512
-
2513
2492
private Field resolveField (int opcode , char cpi ) {
2514
2493
assert opcode == GETFIELD || opcode == GETSTATIC || opcode == PUTFIELD || opcode == PUTSTATIC ;
2515
2494
Field field = getConstantPool ().resolvedFieldAt (getMethod ().getDeclaringKlass (), cpi );
@@ -2521,6 +2500,34 @@ private Field resolveField(int opcode, char cpi) {
2521
2500
return field ;
2522
2501
}
2523
2502
2503
+ private record ResolvedInvoke (ResolvedCall <Klass , Method , Field > resolvedCall , MethodHandleInvoker invoker ) {
2504
+ }
2505
+
2506
+ private ResolvedInvoke reResolvedInvoke (int opcode , char cpi ) {
2507
+ getConstantPool ().resolveMethodAndUpdate (getDeclaringKlass (), cpi );
2508
+ return getResolvedInvoke (opcode , cpi );
2509
+ }
2510
+
2511
+ private ResolvedInvoke getResolvedInvoke (int opcode , char cpi ) {
2512
+ assert !lockIsHeld ();
2513
+ // During resolution of the symbolic reference to the method, any of the exceptions
2514
+ // pertaining to method resolution (§5.4.3.3) can be thrown.
2515
+ MethodRefConstant methodRefConstant = getConstantPool ().resolvedMethodRefAt (getDeclaringKlass (), cpi );
2516
+ Method resolutionSeed = (Method ) ((Resolvable .ResolvedConstant ) methodRefConstant ).value ();
2517
+
2518
+ Klass symbolicRef = Resolution .getResolvedHolderKlass ((MethodRefConstant .Indexes ) getConstantPool ().methodAt (cpi ), getConstantPool (), getDeclaringKlass ());
2519
+ CallSiteType callSiteType = SiteTypes .callSiteFromOpCode (opcode );
2520
+ ResolvedCall <Klass , Method , Field > resolvedCall = EspressoLinkResolver .resolveCallSite (getContext (), getDeclaringKlass (), resolutionSeed , callSiteType , symbolicRef );
2521
+ MethodHandleInvoker invoker = null ;
2522
+ // There might be an invoker if it's an InvokeGeneric
2523
+ if (methodRefConstant instanceof ResolvedWithInvokerClassMethodRefConstant withInvoker ) {
2524
+ invoker = withInvoker .invoker ();
2525
+ assert invoker == null || ((opcode == INVOKEVIRTUAL || opcode == INVOKESPECIAL ) && resolvedCall .getResolvedMethod ().isInvokeIntrinsic ());
2526
+ }
2527
+
2528
+ return new ResolvedInvoke (resolvedCall , invoker );
2529
+ }
2530
+
2524
2531
// endregion Class/Method/Field resolution
2525
2532
2526
2533
// region Instance/array allocation
0 commit comments