275275import java .util .Set ;
276276import java .util .concurrent .locks .Lock ;
277277import java .util .concurrent .locks .ReentrantLock ;
278- import java .util .function .Supplier ;
279278
280279import com .oracle .truffle .api .Assumption ;
281280import com .oracle .truffle .api .CompilerAsserts ;
324323import com .oracle .truffle .espresso .classfile .constantpool .MethodRefConstant ;
325324import com .oracle .truffle .espresso .classfile .constantpool .MethodTypeConstant ;
326325import com .oracle .truffle .espresso .classfile .constantpool .PoolConstant ;
326+ import com .oracle .truffle .espresso .classfile .constantpool .Resolvable ;
327327import com .oracle .truffle .espresso .classfile .constantpool .StringConstant ;
328328import com .oracle .truffle .espresso .classfile .descriptors .Signatures ;
329329import com .oracle .truffle .espresso .classfile .descriptors .Symbol ;
330330import com .oracle .truffle .espresso .classfile .descriptors .Symbol .Type ;
331331import com .oracle .truffle .espresso .classfile .perf .DebugCounter ;
332- import com .oracle .truffle .espresso .constantpool .CallSiteLink ;
333332import com .oracle .truffle .espresso .constantpool .Resolution ;
334333import com .oracle .truffle .espresso .constantpool .ResolvedDynamicConstant ;
335334import com .oracle .truffle .espresso .constantpool .ResolvedWithInvokerClassMethodRefConstant ;
383382import com .oracle .truffle .espresso .runtime .GuestAllocator ;
384383import com .oracle .truffle .espresso .runtime .staticobject .StaticObject ;
385384import com .oracle .truffle .espresso .shared .resolver .CallKind ;
385+ import com .oracle .truffle .espresso .shared .resolver .CallSiteType ;
386386import com .oracle .truffle .espresso .shared .resolver .FieldAccessType ;
387387import com .oracle .truffle .espresso .shared .resolver .ResolvedCall ;
388388import com .oracle .truffle .espresso .substitutions .Target_java_lang_invoke_MethodHandleNatives .SiteTypes ;
@@ -1752,25 +1752,26 @@ private StaticObject newReferenceArray(Klass componentType, int length) {
17521752 private BaseQuickNode getBaseQuickNode (int curBCI , int top , int statementIndex , BaseQuickNode quickNode ) {
17531753 // block while class redefinition is ongoing
17541754 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 ]) {
17591767 // another thread beat us
1760- return nodes [readCPI ( curBCI ) ];
1768+ return nodes [cpi ];
17611769 } 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 ;
17711773 }
17721774 });
1773- return result ;
17741775 }
17751776
17761777 private Object getReturnValueAsObject (VirtualFrame frame , int top ) {
@@ -2179,20 +2180,52 @@ private BaseQuickNode injectQuick(int curBCI, BaseQuickNode quick, int opcode) {
21792180 return quick ;
21802181 }
21812182
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 );
21832207 return atomic (() -> {
21842208 if (bs .currentVolatileBC (curBCI ) == QUICK ) {
21852209 return nodes [readCPI (curBCI )];
21862210 } else {
2187- return injectQuick (curBCI , newQuickNode .get (), QUICK );
2211+ return injectQuick (curBCI , newQuickNode .get (resolved ), QUICK );
21882212 }
21892213 });
21902214 }
21912215
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+
21922225 private int quickenCheckCast (VirtualFrame frame , int top , int curBCI , int opcode ) {
21932226 CompilerAsserts .neverPartOfCompilation ();
21942227 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 ));
21962229 quick .execute (frame , false );
21972230 assert Bytecodes .stackEffectOf (opcode ) == 0 ;
21982231 return 0 ; // Bytecodes.stackEffectOf(opcode);
@@ -2201,7 +2234,7 @@ private int quickenCheckCast(VirtualFrame frame, int top, int curBCI, int opcode
22012234 private int quickenInstanceOf (VirtualFrame frame , int top , int curBCI , int opcode ) {
22022235 CompilerAsserts .neverPartOfCompilation ();
22032236 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 ));
22052238 quick .execute (frame , false );
22062239 assert Bytecodes .stackEffectOf (opcode ) == 0 ;
22072240 return 0 ; // Bytecodes.stackEffectOf(opcode);
@@ -2227,24 +2260,19 @@ private InvokeQuickNode quickenInvoke(int top, int curBCI, int opcode, int state
22272260 QUICKENED_INVOKES .inc ();
22282261 CompilerDirectives .transferToInterpreterAndInvalidate ();
22292262 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 ));
22372265 return quick ;
22382266 }
22392267
22402268 /**
22412269 * Revert speculative quickening e.g. revert inlined fields accessors to a normal invoke.
22422270 * INVOKEVIRTUAL -> QUICK (InlinedGetter/SetterNode) -> QUICK (InvokeVirtualNode)
22432271 */
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 ) {
22452273 CompilerAsserts .neverPartOfCompilation ();
22462274 assert Bytecodes .isInvoke (opcode );
2247- BaseQuickNode invoke = generifyInlinedMethodNode (top , opcode , curBCI , statementIndex , resolutionSeed );
2275+ BaseQuickNode invoke = generifyInlinedMethodNode (top , opcode , curBCI , statementIndex );
22482276 // Perform the call outside of the lock.
22492277 return invoke .execute (frame , false );
22502278 }
@@ -2279,8 +2307,9 @@ private BaseQuickNode replaceQuickAt(int opcode, int curBCI, BaseQuickNode old,
22792307 * Reverts Bytecode-level method inlining at the current bci, in case instrumentation starts
22802308 * happening on this node.
22812309 */
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 ) {
22832311 CompilerAsserts .neverPartOfCompilation ();
2312+ ResolvedInvoke resolvedInvoke = getResolvedInvoke (opcode , readOriginalCPI (curBCI ));
22842313 return atomic (() -> {
22852314 assert bs .currentBC (curBCI ) == QUICK ;
22862315 char nodeIndex = readCPI (curBCI );
@@ -2290,7 +2319,7 @@ public BaseQuickNode generifyInlinedMethodNode(int top, int opcode, int curBCI,
22902319 // Might be racy, as read is not volatile, but redoing the work should be OK.
22912320 return currentQuick ;
22922321 }
2293- BaseQuickNode invoke = dispatchQuickened (top , curBCI , readOriginalCPI ( curBCI ), opcode , statementIndex , resolutionSeed , false );
2322+ BaseQuickNode invoke = dispatchQuickened (top , curBCI , opcode , statementIndex , resolvedInvoke , false );
22942323 nodes [nodeIndex ] = currentQuick .replace (invoke );
22952324 return invoke ;
22962325 });
@@ -2396,12 +2425,8 @@ private int quickenArrayStore(final VirtualFrame frame, int top, int curBCI, int
23962425
23972426 // endregion quickenForeign
23982427
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 ();
24052430 Method resolved = resolvedCall .getResolvedMethod ();
24062431 CallKind callKind = resolvedCall .getCallKind ();
24072432
@@ -2417,13 +2442,7 @@ private InvokeQuickNode dispatchQuickened(int top, int curBCI, char cpi, int opc
24172442 }
24182443
24192444 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 );
24272446 } else {
24282447 // @formatter:off
24292448 return switch (callKind ) {
@@ -2453,39 +2472,10 @@ private RuntimeException throwBoundary(ObjectKlass exceptionKlass, String messag
24532472
24542473 private int quickenInvokeDynamic (final VirtualFrame frame , int top , int curBCI , int opcode ) {
24552474 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 ));
24892479 return quick .execute (frame , false ) - Bytecodes .stackEffectOf (opcode );
24902480 }
24912481
@@ -2499,17 +2489,6 @@ public Klass resolveType(int opcode, char cpi) {
24992489 return getConstantPool ().resolvedKlassAt (getDeclaringKlass (), cpi );
25002490 }
25012491
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-
25132492 private Field resolveField (int opcode , char cpi ) {
25142493 assert opcode == GETFIELD || opcode == GETSTATIC || opcode == PUTFIELD || opcode == PUTSTATIC ;
25152494 Field field = getConstantPool ().resolvedFieldAt (getMethod ().getDeclaringKlass (), cpi );
@@ -2521,6 +2500,34 @@ private Field resolveField(int opcode, char cpi) {
25212500 return field ;
25222501 }
25232502
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+
25242531 // endregion Class/Method/Field resolution
25252532
25262533 // region Instance/array allocation
0 commit comments