diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp index e5b62375d2a..9b9e8b46d1c 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp @@ -906,9 +906,9 @@ C2V_VMENTRY_NULL(jobject, lookupKlassInPool, (JNIEnv* env, jobject, ARGUMENT_PAI return JVMCIENV->get_jobject(result); C2V_END -C2V_VMENTRY_NULL(jobject, lookupAppendixInPool, (JNIEnv* env, jobject, ARGUMENT_PAIR(cp), jint index)) +C2V_VMENTRY_NULL(jobject, lookupAppendixInPool, (JNIEnv* env, jobject, ARGUMENT_PAIR(cp), jint which)) constantPoolHandle cp(THREAD, UNPACK_PAIR(ConstantPool, cp)); - oop appendix_oop = ConstantPool::appendix_at_if_loaded(cp, index); + oop appendix_oop = ConstantPool::appendix_at_if_loaded(cp, which); return JVMCIENV->get_jobject(JVMCIENV->get_object_constant(appendix_oop)); C2V_END diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java index ed96bd03597..bb6e8bc32fb 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java @@ -560,14 +560,17 @@ final class CompilerToVM { private native int constantPoolRemapInstructionOperandFromCache(HotSpotConstantPool constantPool, long constantPoolPointer, int cpci); /** - * Gets the appendix object (if any) associated with the entry at index {@code cpi} in - * {@code constantPool}. + * Gets the appendix object (if any) associated with the entry identified by {@code which}. + * + * @param which if negative, is treated as an encoded indy index for INVOKEDYNAMIC; + * Otherwise, it's treated as a constant pool cache index (returned by HotSpotConstantPool::rawIndexToConstantPoolCacheIndex) + * for INVOKE{VIRTUAL,SPECIAL,STATIC,INTERFACE}. */ - HotSpotObjectConstantImpl lookupAppendixInPool(HotSpotConstantPool constantPool, int cpi) { - return lookupAppendixInPool(constantPool, constantPool.getConstantPoolPointer(), cpi); + HotSpotObjectConstantImpl lookupAppendixInPool(HotSpotConstantPool constantPool, int which) { + return lookupAppendixInPool(constantPool, constantPool.getConstantPoolPointer(), which); } - private native HotSpotObjectConstantImpl lookupAppendixInPool(HotSpotConstantPool constantPool, long constantPoolPointer, int cpi); + private native HotSpotObjectConstantImpl lookupAppendixInPool(HotSpotConstantPool constantPool, long constantPoolPointer, int which); /** * Installs the result of a compilation into the code cache. diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotConstantPool.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotConstantPool.java index 2fb33ef7a28..d4bb97f03c4 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotConstantPool.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotConstantPool.java @@ -47,6 +47,19 @@ import jdk.vm.ci.meta.UnresolvedJavaType; /** * Implementation of {@link ConstantPool} for HotSpot. + * + * The following convention is used in the jdk.vm.ci.hotspot package when accessing the ConstantPool with an index: + * + * + * Note that {@code cpci} and {@code which} are used only in the HotSpot-specific implementation. They + * are not used by the public interface in jdk.vm.ci.meta.*. + * After JDK-8301993, all uses of {@code cpci} and {@code which} will be replaced with {@code rawIndex}. */ public final class HotSpotConstantPool implements ConstantPool, MetaspaceHandleObject { @@ -252,25 +265,15 @@ public final class HotSpotConstantPool implements ConstantPool, MetaspaceHandleO * @return constant pool cache index */ private static int rawIndexToConstantPoolCacheIndex(int rawIndex, int opcode) { - int index; - if (opcode == Bytecodes.INVOKEDYNAMIC) { - index = rawIndex; - // See: ConstantPool::is_invokedynamic_index - if (index >= 0) { - throw new IllegalArgumentException("not an invokedynamic constant pool index " + index); - } + if (opcode == Bytecodes.INVOKEINTERFACE || + opcode == Bytecodes.INVOKEVIRTUAL || + opcode == Bytecodes.INVOKESPECIAL || + opcode == Bytecodes.INVOKESTATIC) { + return rawIndex + config().constantPoolCpCacheIndexTag; } else { - if (opcode == Bytecodes.INVOKEINTERFACE || - opcode == Bytecodes.INVOKEVIRTUAL || - opcode == Bytecodes.INVOKESPECIAL || - opcode == Bytecodes.INVOKESTATIC) { - index = rawIndex + config().constantPoolCpCacheIndexTag; - } else { - throw new IllegalArgumentException("unexpected opcode " + opcode); - - } + // Only the above 4 bytecodes use ConstantPoolCacheIndex + throw new IllegalArgumentException("unexpected opcode " + opcode); } - return index; } /** @@ -581,8 +584,8 @@ public final class HotSpotConstantPool implements ConstantPool, MetaspaceHandleO } @Override - public BootstrapMethodInvocation lookupBootstrapMethodInvocation(int rawCpi, int opcode) { - int cpi = opcode == -1 ? rawCpi : rawIndexToConstantPoolIndex(rawCpi, opcode); + public BootstrapMethodInvocation lookupBootstrapMethodInvocation(int index, int opcode) { + int cpi = opcode == -1 ? index : indyIndexConstantPoolIndex(index, opcode); final JvmConstant tag = getTagAt(cpi); switch (tag.name) { case "InvokeDynamic": @@ -685,13 +688,19 @@ public final class HotSpotConstantPool implements ConstantPool, MetaspaceHandleO } @Override - public JavaConstant lookupAppendix(int cpi, int opcode) { + public JavaConstant lookupAppendix(int rawIndex, int opcode) { if (!Bytecodes.isInvoke(opcode)) { - throw new IllegalArgumentException("expected an invoke bytecode at " + cpi + ", got " + opcode); + throw new IllegalArgumentException("expected an invoke bytecode for " + rawIndex + ", got " + opcode); } - final int index = rawIndexToConstantPoolCacheIndex(cpi, opcode); - return compilerToVM().lookupAppendixInPool(this, index); + if (opcode == Bytecodes.INVOKEDYNAMIC) { + if (!isInvokedynamicIndex(rawIndex)) { + throw new IllegalArgumentException("expected a raw index for INVOKEDYNAMIC but got " + rawIndex); + } + return compilerToVM().lookupAppendixInPool(this, rawIndex); + } else { + return compilerToVM().lookupAppendixInPool(this, rawIndexToConstantPoolCacheIndex(rawIndex, opcode)); + } } /** @@ -709,19 +718,19 @@ public final class HotSpotConstantPool implements ConstantPool, MetaspaceHandleO } @Override - public JavaMethod lookupMethod(int cpi, int opcode, ResolvedJavaMethod caller) { - final int index = rawIndexToConstantPoolCacheIndex(cpi, opcode); - final HotSpotResolvedJavaMethod method = compilerToVM().lookupMethodInPool(this, index, (byte) opcode, (HotSpotResolvedJavaMethodImpl) caller); + public JavaMethod lookupMethod(int rawIndex, int opcode, ResolvedJavaMethod caller) { + final int cpci = rawIndexToConstantPoolCacheIndex(rawIndex, opcode); + final HotSpotResolvedJavaMethod method = compilerToVM().lookupMethodInPool(this, cpci, (byte) opcode, (HotSpotResolvedJavaMethodImpl) caller); if (method != null) { return method; } else { // Get the method's name and signature. - String name = getNameOf(index, opcode); - HotSpotSignature signature = new HotSpotSignature(runtime(), getSignatureOf(index, opcode)); + String name = getNameOf(cpci, opcode); + HotSpotSignature signature = new HotSpotSignature(runtime(), getSignatureOf(cpci, opcode)); if (opcode == Bytecodes.INVOKEDYNAMIC) { return new UnresolvedJavaMethod(name, signature, runtime().getMethodHandleClass()); } else { - final int klassIndex = getKlassRefIndexAt(index, opcode); + final int klassIndex = getKlassRefIndexAt(cpci, opcode); final Object type = compilerToVM().lookupKlassInPool(this, klassIndex); return new UnresolvedJavaMethod(name, signature, getJavaType(type)); } @@ -780,7 +789,6 @@ public final class HotSpotConstantPool implements ConstantPool, MetaspaceHandleO @Override public JavaField lookupField(int rawIndex, ResolvedJavaMethod method, int opcode) { - final int cpi = compilerToVM().decodeFieldIndexToCPIndex(this, rawIndex); final int nameAndTypeIndex = getNameAndTypeRefIndexAt(rawIndex, opcode); final int typeIndex = getSignatureRefIndexAt(nameAndTypeIndex); String typeName = lookupUtf8(typeIndex); @@ -813,32 +821,23 @@ public final class HotSpotConstantPool implements ConstantPool, MetaspaceHandleO } /** - * Converts a raw index from the bytecodes to a constant pool index (not a cache index). + * Converts a raw index for the INVOKEDYNAMIC bytecode to a constant pool index. * * @param rawIndex index from the bytecode * - * @param opcode bytecode to convert the index for + * @param opcode bytecode to convert the index for. Must be INVOKEDYNAMIC. * * @return constant pool index */ - public int rawIndexToConstantPoolIndex(int rawIndex, int opcode) { + private int indyIndexConstantPoolIndex(int rawIndex, int opcode) { if (isInvokedynamicIndex(rawIndex)) { if (opcode != Bytecodes.INVOKEDYNAMIC) { throw new IllegalArgumentException("expected INVOKEDYNAMIC at " + rawIndex + ", got " + opcode); } return compilerToVM().decodeIndyIndexToCPIndex(this, rawIndex, false); + } else { + throw new IllegalArgumentException("expected a raw index for INVOKEDYNAMIC but got " + rawIndex); } - if (opcode == Bytecodes.INVOKEDYNAMIC) { - throw new IllegalArgumentException("unexpected INVOKEDYNAMIC at " + rawIndex); - } - if (opcode == Bytecodes.GETSTATIC || - opcode == Bytecodes.PUTSTATIC || - opcode == Bytecodes.GETFIELD || - opcode == Bytecodes.PUTFIELD) { - return compilerToVM().decodeFieldIndexToCPIndex(this, rawIndex); - } - int index = rawIndexToConstantPoolCacheIndex(rawIndex, opcode); - return compilerToVM().constantPoolRemapInstructionOperandFromCache(this, index); } @Override diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ConstantPool.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ConstantPool.java index 44cd1bc0d48..5a53de47f6a 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ConstantPool.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ConstantPool.java @@ -172,19 +172,21 @@ public interface ConstantPool { /** * Gets the details for invoking a bootstrap method associated with the - * {@code CONSTANT_Dynamic_info} or {@code CONSTANT_InvokeDynamic_info} pool entry {@code cpi} + * {@code CONSTANT_Dynamic_info} or {@code CONSTANT_InvokeDynamic_info} pool entry * in the constant pool. * - * @param cpi a constant pool index - * @param opcode the opcode of the instruction that has {@code cpi} as an operand or -1 if - * {@code cpi} was not decoded from an instruction stream - * @return the bootstrap method invocation details or {@code null} if the entry at {@code cpi} + * @param index if {@code opcode} is -1, {@code index} is a constant pool index. Otherwise {@code opcode} + * must be {@code Bytecodes.INVOKEDYNAMIC}, and {@code index} must be the operand of that + * opcode in the bytecode stream (i.e., a {@code rawIndex}). + * @param opcode must be {@code Bytecodes.INVOKEDYNAMIC}, or -1 if + * {@code index} was not decoded from a bytecode stream + * @return the bootstrap method invocation details or {@code null} if the entry specified by {@code index} * is not a {@code CONSTANT_Dynamic_info} or @{code CONSTANT_InvokeDynamic_info} * @throws IllegalArgumentException if the bootstrap method invocation makes use of * {@code java.lang.invoke.BootstrapCallInfo} * @jvms 4.7.23 The {@code BootstrapMethods} Attribute */ - default BootstrapMethodInvocation lookupBootstrapMethodInvocation(int cpi, int opcode) { + default BootstrapMethodInvocation lookupBootstrapMethodInvocation(int index, int opcode) { throw new UnsupportedOperationException(); } @@ -242,10 +244,9 @@ public interface ConstantPool { /** * Looks up the appendix at the specified index. * - * @param cpi the constant pool index - * @param opcode the opcode of the instruction for which the lookup is being performed or - * {@code -1} + * @param rawIndex index in the bytecode stream after the {@code opcode} (could be rewritten for some opcodes) + * @param opcode the opcode of the instruction for which the lookup is being performed * @return the appendix if it exists and is resolved or {@code null} */ - JavaConstant lookupAppendix(int cpi, int opcode); + JavaConstant lookupAppendix(int rawIndex, int opcode); } diff --git a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/TestDynamicConstant.java b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/TestDynamicConstant.java index 05f44fcdb77..c69c4e5a345 100644 --- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/TestDynamicConstant.java +++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/TestDynamicConstant.java @@ -271,8 +271,8 @@ public class TestDynamicConstant implements Opcodes { 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); + int rawIndex = getFirstInvokedynamicOperand(shouldNotBeCalled); + BootstrapMethodInvocation bmi = cp.lookupBootstrapMethodInvocation(rawIndex, INVOKEDYNAMIC); Assert.assertEquals(bmi.getName(), "do_shouldNotBeCalled"); Assert.assertEquals(bmi.getMethod().getName(), "shouldNotBeCalledBSM"); } @@ -408,8 +408,8 @@ public class TestDynamicConstant implements Opcodes { * 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); + int rawIndex = getFirstInvokedynamicOperand(method); + cp.loadReferencedType(rawIndex, INVOKEDYNAMIC, false); } // @formatter:off diff --git a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/ConstantPoolTest.java b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/ConstantPoolTest.java index 1241c33f24b..26fcedb64d4 100644 --- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/ConstantPoolTest.java +++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/ConstantPoolTest.java @@ -37,9 +37,15 @@ */ package jdk.vm.ci.runtime.test; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; + import org.testng.Assert; import org.testng.annotations.Test; +import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaField; import jdk.vm.ci.meta.JavaMethod; import jdk.vm.ci.meta.MetaAccessProvider; @@ -82,9 +88,12 @@ public class ConstantPoolTest { return arr.clone(); } - public static final int ALOAD_0 = 42; // 0x2A - public static final int GETSTATIC = 178; // 0xB2 - public static final int INVOKEVIRTUAL = 182; // 0xB6 + public static final int ICONST_0 = 3; + public static final int ALOAD_0 = 42; + public static final int ALOAD_1 = 43; + public static final int GETSTATIC = 178; + public static final int INVOKEVIRTUAL = 182; + public static final int INVOKEDYNAMIC = 186; public static int beU2(byte[] data, int bci) { return ((data[bci] & 0xff) << 8) | (data[bci + 1] & 0xff); @@ -94,6 +103,10 @@ public class ConstantPoolTest { return data[bci] & 0xff; } + public 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); + } + @Test public void lookupArrayCloneMethodTest() throws Exception { MetaAccessProvider metaAccess = JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess(); @@ -138,4 +151,86 @@ public class ConstantPoolTest { JavaField field = m.getConstantPool().lookupField(rawIndex, m, GETSTATIC); Assert.assertEquals("someStaticField", field.getName(), "Wrong field name; rawIndex = " + rawIndex + ";"); } + + static String concatString1(String a, String b) { + return a + b; + } + + static String concatString2(String a, String b) { + return a + b; + } + + static void invokeHandle(MethodHandle mh) throws Throwable { + mh.invokeExact(0); + } + + static void intFunc(int t) {} + + @Test + public void lookupAppendixTest() throws Throwable { + // We want at least two indy bytecodes -- with a single indy, the rawIndex is -1, + // or 0xffffffff. Even if we load it with the wrong endianness, it will still come + // "correctly" out as -1. + concatString1("aaa", "bbb"); // force the indy to be resolved + concatString2("aaa", "bbb"); // force the indy to be resolved + + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodType mt = MethodType.methodType(void.class, int.class); + MethodHandle mh = lookup.findStatic(ConstantPoolTest.class, "intFunc", mt); + invokeHandle(mh); + + lookupAppendixTest_dynamic("concatString1"); + lookupAppendixTest_dynamic("concatString2"); + lookupAppendixTest_virtual(); + } + + public void lookupAppendixTest_dynamic(String methodName) throws Exception { + MetaAccessProvider metaAccess = JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess(); + ResolvedJavaType type = metaAccess.lookupJavaType(ConstantPoolTest.class); + Signature methodSig = metaAccess.parseMethodDescriptor("(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); + ResolvedJavaMethod m = type.findMethod(methodName, methodSig); + Assert.assertNotNull(m); + + // Expected: + // aload_0; + // aload_1; + // invokedynamic ...StringConcatFactory.makeConcatWithConstants... + byte[] bytecode = m.getCode(); + Assert.assertNotNull(bytecode); + Assert.assertEquals(8, bytecode.length); + Assert.assertEquals(ALOAD_0, beU1(bytecode, 0)); + Assert.assertEquals(ALOAD_1, beU1(bytecode, 1)); + Assert.assertEquals(INVOKEDYNAMIC, beU1(bytecode, 2)); + + // Note: internally HotSpot stores the indy index as a native int32, but m.getCode() byte-swaps all such + // indices so they appear to be big-endian. + int rawIndex = beS4(bytecode, 3); + JavaConstant constant = m.getConstantPool().lookupAppendix(rawIndex, INVOKEDYNAMIC); + Assert.assertTrue(constant.toString().startsWith("Object["), "wrong appendix: " + constant); + } + + public void lookupAppendixTest_virtual() throws Exception { + MetaAccessProvider metaAccess = JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess(); + ResolvedJavaType type = metaAccess.lookupJavaType(ConstantPoolTest.class); + Signature methodSig = metaAccess.parseMethodDescriptor("(Ljava/lang/invoke/MethodHandle;)V"); + ResolvedJavaMethod m = type.findMethod("invokeHandle", methodSig); + Assert.assertNotNull(m); + + // Expected + // aload_0 + // iconst_0 + // invokevirtual #rawIndex // Method java/lang/invoke/MethodHandle.invokeExact:(I)V + byte[] bytecode = m.getCode(); + Assert.assertNotNull(bytecode); + Assert.assertEquals(6, bytecode.length); + Assert.assertEquals(ALOAD_0, beU1(bytecode, 0)); + Assert.assertEquals(ICONST_0, beU1(bytecode, 1)); + Assert.assertEquals(INVOKEVIRTUAL, beU1(bytecode, 2)); + + int rawIndex = beU2(bytecode, 3); + //System.out.println("rawIndex = " + rawIndex); + JavaConstant constant = m.getConstantPool().lookupAppendix(rawIndex, INVOKEVIRTUAL); + //System.out.println("constant = " + constant); + Assert.assertTrue(constant.toString().startsWith("Object["), "wrong appendix: " + constant); + } }