1
1
/*
2
- * Copyright (c) 2023, 2023 , Oracle and/or its affiliates. All rights reserved.
2
+ * Copyright (c) 2023, 2025 , Oracle and/or its affiliates. All rights reserved.
3
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
4
*
5
5
* This code is free software; you can redistribute it and/or modify it
24
24
*/
25
25
package com .oracle .svm .interpreter ;
26
26
27
+ import com .oracle .graal .pointsto .constraints .UnsupportedFeatureException ;
27
28
import static com .oracle .svm .hosted .pltgot .GOTEntryAllocator .GOT_NO_ENTRY ;
28
29
import static com .oracle .svm .interpreter .metadata .Bytecodes .INVOKEDYNAMIC ;
30
+ import static com .oracle .svm .interpreter .metadata .Bytecodes .INVOKEINTERFACE ;
31
+ import static com .oracle .svm .interpreter .metadata .Bytecodes .INVOKESPECIAL ;
32
+ import static com .oracle .svm .interpreter .metadata .Bytecodes .INVOKESTATIC ;
33
+ import static com .oracle .svm .interpreter .metadata .Bytecodes .INVOKEVIRTUAL ;
29
34
import static com .oracle .svm .interpreter .metadata .InterpreterResolvedJavaMethod .EST_NO_ENTRY ;
30
35
import static com .oracle .svm .interpreter .metadata .InterpreterResolvedJavaMethod .VTBL_NO_ENTRY ;
31
36
import static com .oracle .svm .interpreter .metadata .InterpreterResolvedJavaMethod .VTBL_ONE_IMPL ;
35
40
import java .lang .reflect .Constructor ;
36
41
import java .lang .reflect .Field ;
37
42
import java .lang .reflect .Method ;
43
+ import java .lang .reflect .Modifier ;
38
44
import java .nio .charset .StandardCharsets ;
39
45
import java .nio .file .FileSystems ;
40
46
import java .nio .file .Files ;
72
78
import com .oracle .svm .core .hub .DynamicHub ;
73
79
import com .oracle .svm .core .meta .MethodPointer ;
74
80
import com .oracle .svm .core .option .HostedOptionValues ;
81
+ import com .oracle .svm .core .util .UserError ;
75
82
import com .oracle .svm .core .util .VMError ;
76
83
import com .oracle .svm .graal .hosted .DeoptimizationFeature ;
77
84
import com .oracle .svm .hosted .FeatureImpl ;
113
120
import jdk .graal .compiler .nodes .graphbuilderconf .InvocationPlugins ;
114
121
import jdk .graal .compiler .options .OptionValues ;
115
122
import jdk .graal .compiler .phases .util .Providers ;
123
+ import jdk .vm .ci .meta .ConstantPool ;
116
124
import jdk .vm .ci .meta .JavaConstant ;
117
125
import jdk .vm .ci .meta .JavaKind ;
118
126
import jdk .vm .ci .meta .JavaMethod ;
@@ -257,9 +265,18 @@ static boolean isReachable(AnalysisMethod m) {
257
265
return m .isReachable () || m .isDirectRootMethod () || m .isVirtualRootMethod ();
258
266
}
259
267
268
+ private static boolean isInvokeSpecial (AnalysisMethod method ) {
269
+ boolean invokeSpecial = method .isConstructor ();
270
+ if (!invokeSpecial ) {
271
+ invokeSpecial = Modifier .isPrivate (method .getModifiers ());
272
+ }
273
+ return invokeSpecial ;
274
+ }
275
+
260
276
@ Override
261
277
public void duringAnalysis (DuringAnalysisAccess access ) {
262
278
FeatureImpl .DuringAnalysisAccessImpl accessImpl = (FeatureImpl .DuringAnalysisAccessImpl ) access ;
279
+ MetaAccessProvider metaAccessProvider = accessImpl .getMetaAccess ();
263
280
264
281
boolean addedIndyHelper = false ;
265
282
for (AnalysisMethod m : accessImpl .getUniverse ().getMethods ()) {
@@ -308,12 +325,12 @@ public void duringAnalysis(DuringAnalysisAccess access) {
308
325
methodsProcessedDuringAnalysis .add (method );
309
326
if (method .wrapped instanceof SubstitutionMethod subMethod && subMethod .isUserSubstitution ()) {
310
327
if (subMethod .getOriginal ().isNative ()) {
311
- accessImpl .registerAsRoot (method , false , "compiled entry point of substitution needed for interpreter" );
328
+ accessImpl .registerAsRoot (method , isInvokeSpecial ( method ) , "compiled entry point of substitution needed for interpreter" );
312
329
continue ;
313
330
}
314
331
}
315
332
byte [] code = method .getCode ();
316
- if (code == null ) {
333
+ if (code == null || ! InterpreterFeature . callableByInterpreter ( method , metaAccessProvider ) ) {
317
334
continue ;
318
335
}
319
336
AnalysisType declaringClass = method .getDeclaringClass ();
@@ -329,39 +346,87 @@ public void duringAnalysis(DuringAnalysisAccess access) {
329
346
accessImpl .registerAsUsed (declaringClass , "interpreter needs dynamic hub at runtime for this class" );
330
347
access .requireAnalysisIteration ();
331
348
}
349
+ InvocationPlugin invocationPlugin = accessImpl .getBigBang ().getProviders (method ).getGraphBuilderPlugins ().getInvocationPlugins ().lookupInvocation (method ,
350
+ accessImpl .getBigBang ().getOptions ());
351
+ if (invocationPlugin != null ) {
352
+ // There's an invocation plugin for this method.
353
+ continue ;
354
+ }
355
+ boolean analyzeCallees = true ;
332
356
for (int bci = 0 ; bci < BytecodeStream .endBCI (code ); bci = BytecodeStream .nextBCI (code , bci )) {
333
357
int bytecode = BytecodeStream .currentBC (code , bci );
334
358
if (bytecode == INVOKEDYNAMIC ) {
335
359
int targetMethodCPI = BytecodeStream .readCPI4 (code , bci );
336
- JavaMethod targetMethod = method .getConstantPool ().lookupMethod (targetMethodCPI , bytecode );
337
- /*
338
- * SVM optimizes away javac's INVOKDYNAMIC-based String concatenation e.g.
339
- * MH.makeConcatWithConstants(...) . The CP method entry remains unresolved.
340
- *
341
- * Only reachable call sites should have its method and appendix included in
342
- * the image, for now, ALL INVOKEDYNAMIC call sites of reachable methods are
343
- * included.
344
- */
345
- if (targetMethod instanceof UnresolvedJavaMethod ) {
346
- method .getConstantPool ().loadReferencedType (targetMethodCPI , bytecode );
347
- targetMethod = method .getConstantPool ().lookupMethod (targetMethodCPI , bytecode );
348
- }
349
- if (targetMethod instanceof AnalysisMethod analysisMethod ) {
350
- accessImpl .registerAsRoot (analysisMethod , true , "forced for indy support in interpreter" );
351
- InterpreterUtil .log ("[during analysis] force %s mark as reachable" , targetMethod );
360
+ AnalysisMethod analysisMethod = getAnalysisMethodAt (method .getConstantPool (), targetMethodCPI , bytecode );
361
+ if (analysisMethod != null ) {
362
+ accessImpl .registerAsRoot (analysisMethod , false , "forced for indy support in interpreter" );
363
+ InterpreterUtil .log ("[during analysis] force %s mark as reachable" , analysisMethod );
352
364
}
353
365
354
366
JavaConstant appendixConstant = method .getConstantPool ().lookupAppendix (targetMethodCPI , bytecode );
355
367
if (appendixConstant instanceof ImageHeapConstant imageHeapConstant ) {
356
368
supportImpl .ensureConstantIsInImageHeap (snippetReflection , imageHeapConstant );
357
369
}
358
370
}
371
+ if (analyzeCallees ) {
372
+ switch (bytecode ) {
373
+ /* GR-53540: Handle invokedyanmic too */
374
+ case INVOKESPECIAL , INVOKESTATIC , INVOKEVIRTUAL , INVOKEINTERFACE -> {
375
+ int originalCPI = BytecodeStream .readCPI (code , bci );
376
+
377
+ try {
378
+ AnalysisMethod calleeMethod = getAnalysisMethodAt (method .getConstantPool (), originalCPI , bytecode );
379
+ if (calleeMethod == null || !calleeMethod .isReachable ()) {
380
+ continue ;
381
+ }
382
+
383
+ if (!InterpreterFeature .callableByInterpreter (calleeMethod , metaAccessProvider )) {
384
+ InterpreterUtil .log ("[process invokes] cannot execute %s due to call-site (%s) @ bci=%s is not callable by interpreter%n" , method .getName (), bci , calleeMethod );
385
+ if (method .getAnalyzedGraph () == null ) {
386
+ accessImpl .registerAsRoot (method , isInvokeSpecial (method ), "method handle for interpreter" );
387
+ accessImpl .registerAsUsed (method .getDeclaringClass ().getJavaClass ());
388
+ access .requireAnalysisIteration ();
389
+ }
390
+ if (method .reachableInCurrentLayer ()) {
391
+ SubstrateCompilationDirectives .singleton ().registerForcedCompilation (method );
392
+ }
393
+ analyzeCallees = false ;
394
+ break ;
395
+ }
396
+ } catch (UnsupportedFeatureException | UserError .UserException e ) {
397
+ InterpreterUtil .log ("[process invokes] lookup in method %s failed due to:" , method );
398
+ InterpreterUtil .log (e );
399
+ // ignore, call will fail at run-time if reached
400
+ }
401
+ }
402
+ }
403
+ }
359
404
}
360
405
}
361
406
}
362
407
supportImpl .trimForcedReferencesInImageHeap ();
363
408
}
364
409
410
+ private static AnalysisMethod getAnalysisMethodAt (ConstantPool constantPool , int targetMethodCPI , int bytecode ) {
411
+ JavaMethod targetMethod = constantPool .lookupMethod (targetMethodCPI , bytecode );
412
+ /*
413
+ * SVM optimizes away javac's INVOKDYNAMIC-based String concatenation e.g.
414
+ * MH.makeConcatWithConstants(...) . The CP method entry remains unresolved.
415
+ *
416
+ * Only reachable call sites should have its method and appendix included in the image, for
417
+ * now, ALL INVOKEDYNAMIC call sites of reachable methods are included.
418
+ */
419
+ if (targetMethod instanceof UnresolvedJavaMethod ) {
420
+ constantPool .loadReferencedType (targetMethodCPI , bytecode );
421
+ targetMethod = constantPool .lookupMethod (targetMethodCPI , bytecode );
422
+ }
423
+ if (targetMethod instanceof AnalysisMethod analysisMethod ) {
424
+ return analysisMethod ;
425
+ } else {
426
+ return null ;
427
+ }
428
+ }
429
+
365
430
@ Override
366
431
public void afterAnalysis (AfterAnalysisAccess access ) {
367
432
VMError .guarantee (InterpreterToVM .wordJavaKind () == JavaKind .Long ||
@@ -407,7 +472,7 @@ public void beforeCompilation(BeforeCompilationAccess access) {
407
472
for (HostedMethod hMethod : hUniverse .getMethods ()) {
408
473
AnalysisMethod aMethod = hMethod .getWrapped ();
409
474
if (isReachable (aMethod )) {
410
- boolean needsMethodBody = InterpreterFeature .executableByInterpreter (aMethod );
475
+ boolean needsMethodBody = InterpreterFeature .executableByInterpreter (aMethod ) && InterpreterFeature . callableByInterpreter ( hMethod , hMetaAccess ) ;
411
476
// Test if the methods needs to be compiled for execution in the interpreter:
412
477
if (aMethod .getAnalyzedGraph () != null && //
413
478
(aMethod .wrapped instanceof SubstitutionMethod subMethod && subMethod .isUserSubstitution () ||
0 commit comments