Skip to content

Commit 75b0d02

Browse files
committed
avoid bootstrap method invocation by lookupBootstrapMethodInfo
1 parent 54c5404 commit 75b0d02

File tree

5 files changed

+107
-50
lines changed

5 files changed

+107
-50
lines changed

src/hotspot/share/jvmci/jvmciCompilerToVM.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1586,16 +1586,18 @@ C2V_VMENTRY_NULL(jobject, iterateFrames, (JNIEnv* env, jobject compilerToVM, job
15861586
return nullptr;
15871587
C2V_END
15881588

1589-
C2V_VMENTRY_0(int, resolveInvokeDynamicInPool, (JNIEnv* env, jobject, ARGUMENT_PAIR(cp), jint index))
1590-
if (!ConstantPool::is_invokedynamic_index(index)) {
1591-
JVMCI_THROW_MSG_0(IllegalStateException, err_msg("not an invokedynamic index %d", index));
1589+
C2V_VMENTRY_0(int, invokeDynamicOperandToCPIndex, (JNIEnv* env, jobject, ARGUMENT_PAIR(cp), jint operand, jboolean resolve))
1590+
if (!ConstantPool::is_invokedynamic_index(operand)) {
1591+
JVMCI_THROW_MSG_0(IllegalStateException, err_msg("not an invokedynamic index %d", operand));
15921592
}
15931593

15941594
constantPoolHandle cp(THREAD, UNPACK_PAIR(ConstantPool, cp));
15951595
CallInfo callInfo;
1596-
LinkResolver::resolve_invoke(callInfo, Handle(), cp, index, Bytecodes::_invokedynamic, CHECK_0);
1597-
int indy_index = cp->decode_invokedynamic_index(index);
1598-
cp->cache()->set_dynamic_call(callInfo, indy_index);
1596+
int indy_index = cp->decode_invokedynamic_index(operand);
1597+
if (resolve) {
1598+
LinkResolver::resolve_invoke(callInfo, Handle(), cp, operand, Bytecodes::_invokedynamic, CHECK_0);
1599+
cp->cache()->set_dynamic_call(callInfo, indy_index);
1600+
}
15991601
return cp->resolved_indy_entry_at(indy_index)->constant_pool_index();
16001602
C2V_END
16011603

@@ -3120,7 +3122,7 @@ JNINativeMethod CompilerToVM::methods[] = {
31203122
{CC "getUncachedStringInPool", CC "(" HS_CONSTANT_POOL2 "I)" JAVACONSTANT, FN_PTR(getUncachedStringInPool)},
31213123
{CC "resolveTypeInPool", CC "(" HS_CONSTANT_POOL2 "I)" HS_KLASS, FN_PTR(resolveTypeInPool)},
31223124
{CC "resolveFieldInPool", CC "(" HS_CONSTANT_POOL2 "I" HS_METHOD2 "B[I)" HS_KLASS, FN_PTR(resolveFieldInPool)},
3123-
{CC "resolveInvokeDynamicInPool", CC "(" HS_CONSTANT_POOL2 "I)I", FN_PTR(resolveInvokeDynamicInPool)},
3125+
{CC "invokeDynamicOperandToCPIndex", CC "(" HS_CONSTANT_POOL2 "IZ)I", FN_PTR(invokeDynamicOperandToCPIndex)},
31243126
{CC "resolveInvokeHandleInPool", CC "(" HS_CONSTANT_POOL2 "I)V", FN_PTR(resolveInvokeHandleInPool)},
31253127
{CC "isResolvedInvokeHandleInPool", CC "(" HS_CONSTANT_POOL2 "I)I", FN_PTR(isResolvedInvokeHandleInPool)},
31263128
{CC "resolveMethod", CC "(" HS_KLASS2 HS_METHOD2 HS_KLASS2 ")" HS_METHOD, FN_PTR(resolveMethod)},

src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -387,17 +387,18 @@ private native HotSpotResolvedJavaMethodImpl lookupMethodInPool(HotSpotConstantP
387387
long callerMethodPointer);
388388

389389
/**
390-
* Ensures that the type referenced by the specified {@code JVM_CONSTANT_InvokeDynamic} entry at
391-
* index {@code cpi} in {@code constantPool} is loaded and initialized.
390+
* Converts the operand of an invokedynamic instruction in {@code operand}
391+
* to an index directly into {@code constantPool}.
392392
*
393-
* @throws IllegalArgumentException if {@code cpi} is not an invokedynamic index
394-
* @return the invokedynamic index
393+
* @param resolve if {@true}, then resolve the entry (which may call the bootstrap method)
394+
* @throws IllegalArgumentException if {@code operand} is not a valid invokedynamic operand
395+
* @return {@code JVM_CONSTANT_InvokeDynamic} constant pool entry index for the invokedynamic
395396
*/
396-
int resolveInvokeDynamicInPool(HotSpotConstantPool constantPool, int cpi) {
397-
return resolveInvokeDynamicInPool(constantPool, constantPool.getConstantPoolPointer(), cpi);
397+
int invokeDynamicOperandToCPIndex(HotSpotConstantPool constantPool, int operand, boolean resolve) {
398+
return invokeDynamicOperandToCPIndex(constantPool, constantPool.getConstantPoolPointer(), operand, resolve);
398399
}
399400

400-
private native int resolveInvokeDynamicInPool(HotSpotConstantPool constantPool, long constantPoolPointer, int cpi);
401+
private native int invokeDynamicOperandToCPIndex(HotSpotConstantPool constantPool, long constantPoolPointer, int operand, boolean resolve);
401402

402403
/**
403404
* Resolves the details for invoking the bootstrap method associated with the
@@ -440,7 +441,7 @@ void resolveInvokeHandleInPool(HotSpotConstantPool constantPool, int cpi) {
440441

441442
/**
442443
* If {@code cpi} denotes an entry representing a resolved dynamic adapter (see
443-
* {@link #resolveInvokeDynamicInPool} and {@link #resolveInvokeHandleInPool}), return the
444+
* {@link #decodeInvokeDynamicIndex} and {@link #resolveInvokeHandleInPool}), return the
444445
* opcode of the instruction for which the resolution was performed ({@code invokedynamic} or
445446
* {@code invokevirtual}), or {@code -1} otherwise.
446447
*/

src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotConstantPool.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -828,7 +828,7 @@ public int rawIndexToConstantPoolIndex(int rawIndex, int opcode) {
828828
if (opcode != Bytecodes.INVOKEDYNAMIC) {
829829
throw new IllegalArgumentException("expected INVOKEDYNAMIC at " + rawIndex + ", got " + opcode);
830830
}
831-
return compilerToVM().resolveInvokeDynamicInPool(this, rawIndex);
831+
return compilerToVM().invokeDynamicOperandToCPIndex(this, rawIndex, false);
832832
}
833833
if (opcode == Bytecodes.INVOKEDYNAMIC) {
834834
throw new IllegalArgumentException("unexpected INVOKEDYNAMIC at " + rawIndex);
@@ -862,7 +862,7 @@ public void loadReferencedType(int cpi, int opcode, boolean initialize) {
862862
if (!isInvokedynamicIndex(cpi)) {
863863
throw new IllegalArgumentException("must use invokedynamic index but got " + cpi);
864864
}
865-
index = compilerToVM().resolveInvokeDynamicInPool(this, cpi);
865+
index = compilerToVM().invokeDynamicOperandToCPIndex(this, cpi, true);
866866
break;
867867
}
868868
case Bytecodes.GETSTATIC:

test/hotspot/jtreg/compiler/jvmci/common/patches/jdk.internal.vm.ci/jdk/vm/ci/hotspot/CompilerToVMHelper.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,6 @@ public static HotSpotResolvedJavaMethod lookupMethodInPool(
132132
return CTVM.lookupMethodInPool((HotSpotConstantPool) constantPool, cpi, opcode, null);
133133
}
134134

135-
public static void resolveInvokeDynamicInPool(
136-
ConstantPool constantPool, int cpi) {
137-
CTVM.resolveInvokeDynamicInPool((HotSpotConstantPool) constantPool, cpi);
138-
}
139-
140135
public static void resolveInvokeHandleInPool(
141136
ConstantPool constantPool, int cpi) {
142137
CTVM.resolveInvokeHandleInPool((HotSpotConstantPool) constantPool, cpi);

test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/TestDynamicConstant.java

Lines changed: 87 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,15 @@
3838

3939
import java.io.File;
4040
import java.io.IOException;
41+
import java.lang.invoke.CallSite;
4142
import java.lang.invoke.ConstantBootstraps;
4243
import java.lang.invoke.MethodHandles;
44+
import java.lang.invoke.MethodType;
4345
import java.lang.invoke.StringConcatFactory;
4446
import java.lang.reflect.Method;
4547
import java.nio.file.Files;
4648
import java.util.List;
49+
import java.util.Set;
4750

4851
import org.testng.Assert;
4952
import org.testng.annotations.Test;
@@ -201,6 +204,14 @@ byte[] generateClassfile() {
201204
concat.visitMaxs(0, 0);
202205
concat.visitEnd();
203206

207+
MethodVisitor shouldNotBeCalled = cw.visitMethod(PUBLIC_STATIC, "shouldNotBeCalled", "()V", null, null);
208+
sig = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;";
209+
handle = new Handle(H_INVOKESTATIC, testClassInternalName, "shouldNotBeCalledBSM", sig, false);
210+
shouldNotBeCalled.visitInvokeDynamicInsn("do_shouldNotBeCalled", "()V", handle);
211+
shouldNotBeCalled.visitInsn(RETURN);
212+
shouldNotBeCalled.visitMaxs(0, 0);
213+
shouldNotBeCalled.visitEnd();
214+
204215
cw.visitEnd();
205216
return cw.toByteArray();
206217
}
@@ -235,6 +246,37 @@ protected Class<?> findClass(String name) throws ClassNotFoundException {
235246
}
236247
}
237248

249+
/**
250+
* Asserts that {@link ConstantPool#lookupConstant(int, boolean)} with {@code resolve == false}
251+
* returns null for all resolvable constant entries.
252+
*/
253+
private static void assertNoEagerConstantResolution(Class<?> testClass, ConstantPool cp, Method getTagAt) throws Exception {
254+
for (int cpi = 1; cpi < cp.length(); cpi++) {
255+
String tag = String.valueOf(getTagAt.invoke(cp, cpi));
256+
switch (tag) {
257+
case "MethodHandle":
258+
case "MethodType":
259+
case "Dynamic": {
260+
Object con = cp.lookupConstant(cpi, false);
261+
Assert.assertNull(con, "Unexpected eager resolution in " + testClass + " at cpi " + cpi + " (tag: " + tag + ")");
262+
break;
263+
}
264+
}
265+
}
266+
}
267+
268+
/**
269+
* Ensures {@link ConstantPool#lookupBootstrapMethodInvocation} does not invoke the associated bootstrap method.
270+
*/
271+
private static void assertLookupBMIDoesNotInvokeBM(MetaAccessProvider metaAccess, Class<?> testClass) throws Exception {
272+
ResolvedJavaMethod shouldNotBeCalled = metaAccess.lookupJavaMethod(testClass.getDeclaredMethod("shouldNotBeCalled"));
273+
ConstantPool cp = shouldNotBeCalled.getConstantPool();
274+
int cpi = getFirstInvokedynamicOperand(shouldNotBeCalled);
275+
BootstrapMethodInvocation bmi = cp.lookupBootstrapMethodInvocation(cpi, INVOKEDYNAMIC);
276+
Assert.assertEquals(bmi.getName(), "do_shouldNotBeCalled");
277+
Assert.assertEquals(bmi.getMethod().getName(), "shouldNotBeCalledBSM");
278+
}
279+
238280
@SuppressWarnings("try")
239281
@Test
240282
public void test() throws Throwable {
@@ -263,25 +305,15 @@ public void test() throws Throwable {
263305
Method m = testClass.getDeclaredMethod("run");
264306
ResolvedJavaMethod run = metaAccess.lookupJavaMethod(m);
265307
ConstantPool cp = run.getConstantPool();
308+
309+
assertNoEagerConstantResolution(testClass, cp, getTagAt);
310+
assertLookupBMIDoesNotInvokeBM(metaAccess, testClass);
311+
266312
Object lastConstant = null;
267313
for (int cpi = 1; cpi < cp.length(); cpi++) {
268314
String tag = String.valueOf(getTagAt.invoke(cp, cpi));
269-
switch (tag) {
270-
case "MethodHandle":
271-
case "MethodType":
272-
case "Dynamic": {
273-
Object con = cp.lookupConstant(cpi, false);
274-
Assert.assertNull(con, "Unexpected eager resolution");
275-
276-
con = cp.lookupConstant(cpi, true);
277-
Assert.assertNotNull(con, "Eager resolution failed");
278-
279-
if (tag.equals("Dynamic")) {
280-
lastConstant = cp.lookupConstant(cpi);
281-
Assert.assertEquals(con, lastConstant);
282-
}
283-
break;
284-
}
315+
if (tag.equals("Dynamic")) {
316+
lastConstant = cp.lookupConstant(cpi);
285317
}
286318
}
287319
Assert.assertTrue(lastConstant != null, "No Dynamic entries in constant pool of " + testClass.getName());
@@ -315,7 +347,12 @@ private void testLookupBootstrapMethodInvocation(CondyType condyType, MetaAccess
315347
Method m = testClass.getDeclaredMethod("concat");
316348
ResolvedJavaMethod concat = metaAccess.lookupJavaMethod(m);
317349
ConstantPool cp = concat.getConstantPool();
318-
Object lastConstant = null;
350+
351+
Set<String> expectedBSMs = Set.of(
352+
"jdk.vm.ci.hotspot.test.TestDynamicConstant.shouldNotBeCalledBSM",
353+
"java.lang.invoke.StringConcatFactory.makeConcatWithConstants"
354+
);
355+
319356
for (int cpi = 1; cpi < cp.length(); cpi++) {
320357
String tag = String.valueOf(getTagAt.invoke(cp, cpi));
321358
BootstrapMethodInvocation bsmi = cp.lookupBootstrapMethodInvocation(cpi, -1);
@@ -324,7 +361,7 @@ private void testLookupBootstrapMethodInvocation(CondyType condyType, MetaAccess
324361
String bsm = bsmi.getMethod().format("%H.%n");
325362
if (tag.equals("InvokeDynamic")) {
326363
Assert.assertTrue(bsmi.isInvokeDynamic());
327-
Assert.assertEquals(bsm, "java.lang.invoke.StringConcatFactory.makeConcatWithConstants");
364+
Assert.assertTrue(expectedBSMs.contains(bsm), expectedBSMs.toString());
328365
} else {
329366
Assert.assertFalse(bsmi.isInvokeDynamic());
330367
if (condyType == CondyType.CALL_DIRECT_BSM) {
@@ -338,24 +375,41 @@ private void testLookupBootstrapMethodInvocation(CondyType condyType, MetaAccess
338375
}
339376
}
340377

341-
testLoadReferencedType(concat);
378+
testLoadReferencedType(concat, cp);
342379
}
343380

344381
private static int beS4(byte[] data, int bci) {
345382
return (data[bci] << 24) | ((data[bci + 1] & 0xff) << 16) | ((data[bci + 2] & 0xff) << 8) | (data[bci + 3] & 0xff);
346383
}
347384

385+
private static int beU1(byte[] data, int bci) {
386+
return data[bci] & 0xff;
387+
}
348388
private static final int LDC2_W = 20;
349-
private static void testLoadReferencedType(ResolvedJavaMethod method) {
350-
// Make sure that loadReferencedType for an invokedynamic call site works.
389+
390+
/**
391+
* Gets the operand of the first invokedynamic in {@code method}. This
392+
* assumes that the bytecode of {@code method} is an INVOKEDYNAMIC instruction,
393+
* possibly preceded by an LDC instruction.
394+
*/
395+
private static int getFirstInvokedynamicOperand(ResolvedJavaMethod method) {
351396
byte[] code = method.getCode();
352-
Assert.assertTrue(code[0] == LDC || code[0] == LDC2_W, "unexpected ldc sequence");
353-
int bci = code[0] == LDC ? 2 : 3;
354-
Assert.assertTrue((code[bci] & 0xff) == INVOKEDYNAMIC, "unexpected bytecode");
355-
int cpi = beS4(code, bci + 1);
356-
method.getConstantPool().loadReferencedType(cpi, INVOKEDYNAMIC, false);
357-
BootstrapMethodInvocation bmi = method.getConstantPool().lookupBootstrapMethodInvocation(cpi, INVOKEDYNAMIC);
358-
Assert.assertEquals(bmi.getName(), "do_concat");
397+
int opcode = beU1(code, 0);
398+
if (opcode == INVOKEDYNAMIC) {
399+
return beS4(code, 1);
400+
}
401+
Assert.assertTrue(opcode == LDC || opcode == LDC2_W, String.valueOf(opcode));
402+
int bci = opcode == LDC ? 2 : 3;
403+
Assert.assertEquals(beU1(code, bci), INVOKEDYNAMIC);
404+
return beS4(code, bci + 1);
405+
}
406+
407+
/**
408+
* Ensures that loadReferencedType for an invokedynamic call site does not throw an exception.
409+
*/
410+
private static void testLoadReferencedType(ResolvedJavaMethod method, ConstantPool cp) {
411+
int cpi = getFirstInvokedynamicOperand(method);
412+
cp.loadReferencedType(cpi, INVOKEDYNAMIC, false);
359413
}
360414

361415
// @formatter:off
@@ -396,4 +450,9 @@ private static void testLoadReferencedType(ResolvedJavaMethod method) {
396450
public static Object getObject (Object v1, Object v2) { return null; }
397451
public static List<?> getList (List<?> v1, List<?> v2) { return List.of(v1, v2); }
398452
// @formatter:on
453+
454+
// A bootstrap method that should never be called
455+
public static CallSite shouldNotBeCalledBSM(MethodHandles.Lookup caller, String name, MethodType type) throws Exception {
456+
throw new RuntimeException("should not be called");
457+
}
399458
}

0 commit comments

Comments
 (0)