Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions src/hotspot/share/jvmci/jvmciCompilerToVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1586,16 +1586,18 @@ C2V_VMENTRY_NULL(jobject, iterateFrames, (JNIEnv* env, jobject compilerToVM, job
return nullptr;
C2V_END

C2V_VMENTRY_0(int, resolveInvokeDynamicInPool, (JNIEnv* env, jobject, ARGUMENT_PAIR(cp), jint index))
if (!ConstantPool::is_invokedynamic_index(index)) {
JVMCI_THROW_MSG_0(IllegalStateException, err_msg("not an invokedynamic index %d", index));
C2V_VMENTRY_0(int, invokeDynamicOperandToCPIndex, (JNIEnv* env, jobject, ARGUMENT_PAIR(cp), jint operand, jboolean resolve))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is invokeDynamicOperand the correct name here? From what I recall, the operand to an invokedynamic instruction is a constant pool index which is later rewritten to an indy_index. I think the term operand can be confusing here.

Given the way that this method works, the operand in question is always an encoded indy_index.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've pushed a commit with better naming.

if (!ConstantPool::is_invokedynamic_index(operand)) {
JVMCI_THROW_MSG_0(IllegalStateException, err_msg("not an invokedynamic index %d", operand));
}

constantPoolHandle cp(THREAD, UNPACK_PAIR(ConstantPool, cp));
CallInfo callInfo;
LinkResolver::resolve_invoke(callInfo, Handle(), cp, index, Bytecodes::_invokedynamic, CHECK_0);
int indy_index = cp->decode_invokedynamic_index(index);
cp->cache()->set_dynamic_call(callInfo, indy_index);
int indy_index = cp->decode_invokedynamic_index(operand);
if (resolve) {
LinkResolver::resolve_invoke(callInfo, Handle(), cp, operand, Bytecodes::_invokedynamic, CHECK_0);
cp->cache()->set_dynamic_call(callInfo, indy_index);
}
return cp->resolved_indy_entry_at(indy_index)->constant_pool_index();
C2V_END

Expand Down Expand Up @@ -3120,7 +3122,7 @@ JNINativeMethod CompilerToVM::methods[] = {
{CC "getUncachedStringInPool", CC "(" HS_CONSTANT_POOL2 "I)" JAVACONSTANT, FN_PTR(getUncachedStringInPool)},
{CC "resolveTypeInPool", CC "(" HS_CONSTANT_POOL2 "I)" HS_KLASS, FN_PTR(resolveTypeInPool)},
{CC "resolveFieldInPool", CC "(" HS_CONSTANT_POOL2 "I" HS_METHOD2 "B[I)" HS_KLASS, FN_PTR(resolveFieldInPool)},
{CC "resolveInvokeDynamicInPool", CC "(" HS_CONSTANT_POOL2 "I)I", FN_PTR(resolveInvokeDynamicInPool)},
{CC "invokeDynamicOperandToCPIndex", CC "(" HS_CONSTANT_POOL2 "IZ)I", FN_PTR(invokeDynamicOperandToCPIndex)},
{CC "resolveInvokeHandleInPool", CC "(" HS_CONSTANT_POOL2 "I)V", FN_PTR(resolveInvokeHandleInPool)},
{CC "isResolvedInvokeHandleInPool", CC "(" HS_CONSTANT_POOL2 "I)I", FN_PTR(isResolvedInvokeHandleInPool)},
{CC "resolveMethod", CC "(" HS_KLASS2 HS_METHOD2 HS_KLASS2 ")" HS_METHOD, FN_PTR(resolveMethod)},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,17 +387,18 @@ private native HotSpotResolvedJavaMethodImpl lookupMethodInPool(HotSpotConstantP
long callerMethodPointer);

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

private native int resolveInvokeDynamicInPool(HotSpotConstantPool constantPool, long constantPoolPointer, int cpi);
private native int invokeDynamicOperandToCPIndex(HotSpotConstantPool constantPool, long constantPoolPointer, int operand, boolean resolve);

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

/**
* If {@code cpi} denotes an entry representing a resolved dynamic adapter (see
* {@link #resolveInvokeDynamicInPool} and {@link #resolveInvokeHandleInPool}), return the
* {@link #invokeDynamicOperandToCPIndex} and {@link #resolveInvokeHandleInPool}), return the
* opcode of the instruction for which the resolution was performed ({@code invokedynamic} or
* {@code invokevirtual}), or {@code -1} otherwise.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,7 @@ public int rawIndexToConstantPoolIndex(int rawIndex, int opcode) {
if (opcode != Bytecodes.INVOKEDYNAMIC) {
throw new IllegalArgumentException("expected INVOKEDYNAMIC at " + rawIndex + ", got " + opcode);
}
return compilerToVM().resolveInvokeDynamicInPool(this, rawIndex);
return compilerToVM().invokeDynamicOperandToCPIndex(this, rawIndex, false);
}
if (opcode == Bytecodes.INVOKEDYNAMIC) {
throw new IllegalArgumentException("unexpected INVOKEDYNAMIC at " + rawIndex);
Expand Down Expand Up @@ -862,7 +862,7 @@ public void loadReferencedType(int cpi, int opcode, boolean initialize) {
if (!isInvokedynamicIndex(cpi)) {
throw new IllegalArgumentException("must use invokedynamic index but got " + cpi);
}
index = compilerToVM().resolveInvokeDynamicInPool(this, cpi);
index = compilerToVM().invokeDynamicOperandToCPIndex(this, cpi, true);
break;
}
case Bytecodes.GETSTATIC:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,6 @@ public static HotSpotResolvedJavaMethod lookupMethodInPool(
return CTVM.lookupMethodInPool((HotSpotConstantPool) constantPool, cpi, opcode, null);
}

public static void resolveInvokeDynamicInPool(
ConstantPool constantPool, int cpi) {
CTVM.resolveInvokeDynamicInPool((HotSpotConstantPool) constantPool, cpi);
}

public static void resolveInvokeHandleInPool(
ConstantPool constantPool, int cpi) {
CTVM.resolveInvokeHandleInPool((HotSpotConstantPool) constantPool, cpi);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,15 @@

import java.io.File;
import java.io.IOException;
import java.lang.invoke.CallSite;
import java.lang.invoke.ConstantBootstraps;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.StringConcatFactory;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.util.List;
import java.util.Set;

import org.testng.Assert;
import org.testng.annotations.Test;
Expand Down Expand Up @@ -201,6 +204,14 @@ byte[] generateClassfile() {
concat.visitMaxs(0, 0);
concat.visitEnd();

MethodVisitor shouldNotBeCalled = cw.visitMethod(PUBLIC_STATIC, "shouldNotBeCalled", "()V", null, null);
sig = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;";
handle = new Handle(H_INVOKESTATIC, testClassInternalName, "shouldNotBeCalledBSM", sig, false);
shouldNotBeCalled.visitInvokeDynamicInsn("do_shouldNotBeCalled", "()V", handle);
shouldNotBeCalled.visitInsn(RETURN);
shouldNotBeCalled.visitMaxs(0, 0);
shouldNotBeCalled.visitEnd();

cw.visitEnd();
return cw.toByteArray();
}
Expand Down Expand Up @@ -235,6 +246,37 @@ protected Class<?> findClass(String name) throws ClassNotFoundException {
}
}

/**
* Asserts that {@link ConstantPool#lookupConstant(int, boolean)} with {@code resolve == false}
* returns null for all resolvable constant entries.
*/
private static void assertNoEagerConstantResolution(Class<?> testClass, ConstantPool cp, Method getTagAt) throws Exception {
for (int cpi = 1; cpi < cp.length(); cpi++) {
String tag = String.valueOf(getTagAt.invoke(cp, cpi));
switch (tag) {
case "MethodHandle":
case "MethodType":
case "Dynamic": {
Object con = cp.lookupConstant(cpi, false);
Assert.assertNull(con, "Unexpected eager resolution in " + testClass + " at cpi " + cpi + " (tag: " + tag + ")");
break;
}
}
}
}

/**
* Ensures {@link ConstantPool#lookupBootstrapMethodInvocation} does not invoke the associated bootstrap method.
*/
private static void assertLookupBMIDoesNotInvokeBM(MetaAccessProvider metaAccess, Class<?> testClass) throws Exception {
ResolvedJavaMethod shouldNotBeCalled = metaAccess.lookupJavaMethod(testClass.getDeclaredMethod("shouldNotBeCalled"));
ConstantPool cp = shouldNotBeCalled.getConstantPool();
int cpi = getFirstInvokedynamicOperand(shouldNotBeCalled);
BootstrapMethodInvocation bmi = cp.lookupBootstrapMethodInvocation(cpi, INVOKEDYNAMIC);
Assert.assertEquals(bmi.getName(), "do_shouldNotBeCalled");
Assert.assertEquals(bmi.getMethod().getName(), "shouldNotBeCalledBSM");
}

@SuppressWarnings("try")
@Test
public void test() throws Throwable {
Expand Down Expand Up @@ -263,25 +305,15 @@ public void test() throws Throwable {
Method m = testClass.getDeclaredMethod("run");
ResolvedJavaMethod run = metaAccess.lookupJavaMethod(m);
ConstantPool cp = run.getConstantPool();

assertNoEagerConstantResolution(testClass, cp, getTagAt);
assertLookupBMIDoesNotInvokeBM(metaAccess, testClass);

Object lastConstant = null;
for (int cpi = 1; cpi < cp.length(); cpi++) {
String tag = String.valueOf(getTagAt.invoke(cp, cpi));
switch (tag) {
case "MethodHandle":
case "MethodType":
case "Dynamic": {
Object con = cp.lookupConstant(cpi, false);
Assert.assertNull(con, "Unexpected eager resolution");

con = cp.lookupConstant(cpi, true);
Assert.assertNotNull(con, "Eager resolution failed");

if (tag.equals("Dynamic")) {
lastConstant = cp.lookupConstant(cpi);
Assert.assertEquals(con, lastConstant);
}
break;
}
if (tag.equals("Dynamic")) {
lastConstant = cp.lookupConstant(cpi);
}
}
Assert.assertTrue(lastConstant != null, "No Dynamic entries in constant pool of " + testClass.getName());
Expand Down Expand Up @@ -315,7 +347,12 @@ private void testLookupBootstrapMethodInvocation(CondyType condyType, MetaAccess
Method m = testClass.getDeclaredMethod("concat");
ResolvedJavaMethod concat = metaAccess.lookupJavaMethod(m);
ConstantPool cp = concat.getConstantPool();
Object lastConstant = null;

Set<String> expectedBSMs = Set.of(
"jdk.vm.ci.hotspot.test.TestDynamicConstant.shouldNotBeCalledBSM",
"java.lang.invoke.StringConcatFactory.makeConcatWithConstants"
);

for (int cpi = 1; cpi < cp.length(); cpi++) {
String tag = String.valueOf(getTagAt.invoke(cp, cpi));
BootstrapMethodInvocation bsmi = cp.lookupBootstrapMethodInvocation(cpi, -1);
Expand All @@ -324,7 +361,7 @@ private void testLookupBootstrapMethodInvocation(CondyType condyType, MetaAccess
String bsm = bsmi.getMethod().format("%H.%n");
if (tag.equals("InvokeDynamic")) {
Assert.assertTrue(bsmi.isInvokeDynamic());
Assert.assertEquals(bsm, "java.lang.invoke.StringConcatFactory.makeConcatWithConstants");
Assert.assertTrue(expectedBSMs.contains(bsm), expectedBSMs.toString());
} else {
Assert.assertFalse(bsmi.isInvokeDynamic());
if (condyType == CondyType.CALL_DIRECT_BSM) {
Expand All @@ -338,24 +375,41 @@ private void testLookupBootstrapMethodInvocation(CondyType condyType, MetaAccess
}
}

testLoadReferencedType(concat);
testLoadReferencedType(concat, cp);
}

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

private static int beU1(byte[] data, int bci) {
return data[bci] & 0xff;
}
private static final int LDC2_W = 20;
private static void testLoadReferencedType(ResolvedJavaMethod method) {
// Make sure that loadReferencedType for an invokedynamic call site works.

/**
* Gets the operand of the first invokedynamic in {@code method}. This
* assumes that the bytecode of {@code method} is an INVOKEDYNAMIC instruction,
* possibly preceded by an LDC instruction.
*/
private static int getFirstInvokedynamicOperand(ResolvedJavaMethod method) {
byte[] code = method.getCode();
Assert.assertTrue(code[0] == LDC || code[0] == LDC2_W, "unexpected ldc sequence");
int bci = code[0] == LDC ? 2 : 3;
Assert.assertTrue((code[bci] & 0xff) == INVOKEDYNAMIC, "unexpected bytecode");
int cpi = beS4(code, bci + 1);
method.getConstantPool().loadReferencedType(cpi, INVOKEDYNAMIC, false);
BootstrapMethodInvocation bmi = method.getConstantPool().lookupBootstrapMethodInvocation(cpi, INVOKEDYNAMIC);
Assert.assertEquals(bmi.getName(), "do_concat");
int opcode = beU1(code, 0);
if (opcode == INVOKEDYNAMIC) {
return beS4(code, 1);
}
Assert.assertTrue(opcode == LDC || opcode == LDC2_W, String.valueOf(opcode));
int bci = opcode == LDC ? 2 : 3;
Assert.assertEquals(beU1(code, bci), INVOKEDYNAMIC);
return beS4(code, bci + 1);
}

/**
* Ensures that loadReferencedType for an invokedynamic call site does not throw an exception.
*/
private static void testLoadReferencedType(ResolvedJavaMethod method, ConstantPool cp) {
int cpi = getFirstInvokedynamicOperand(method);
cp.loadReferencedType(cpi, INVOKEDYNAMIC, false);
}

// @formatter:off
Expand Down Expand Up @@ -396,4 +450,9 @@ private static void testLoadReferencedType(ResolvedJavaMethod method) {
public static Object getObject (Object v1, Object v2) { return null; }
public static List<?> getList (List<?> v1, List<?> v2) { return List.of(v1, v2); }
// @formatter:on

// A bootstrap method that should never be called
public static CallSite shouldNotBeCalledBSM(MethodHandles.Lookup caller, String name, MethodType type) throws Exception {
throw new RuntimeException("should not be called");
}
}