diff --git a/src/hotspot/share/interpreter/oopMapCache.cpp b/src/hotspot/share/interpreter/oopMapCache.cpp index 2ce6ce7ca7a4c..17e8475fb482a 100644 --- a/src/hotspot/share/interpreter/oopMapCache.cpp +++ b/src/hotspot/share/interpreter/oopMapCache.cpp @@ -407,6 +407,8 @@ void OopMapCacheEntry::flush() { void InterpreterOopMap::resource_copy(OopMapCacheEntry* from) { assert(_resource_allocate_bit_mask, "Should not resource allocate the _bit_mask"); + assert(from->has_valid_mask(), + "Cannot copy entry with an invalid mask"); set_method(from->method()); set_bci(from->bci()); @@ -612,7 +614,9 @@ void OopMapCache::compute_one_oop_map(const methodHandle& method, int bci, Inter OopMapCacheEntry* tmp = NEW_C_HEAP_OBJ(OopMapCacheEntry, mtClass); tmp->initialize(); tmp->fill(method, bci); - entry->resource_copy(tmp); + if (tmp->has_valid_mask()) { + entry->resource_copy(tmp); + } tmp->flush(); FREE_C_HEAP_OBJ(tmp); } diff --git a/src/hotspot/share/interpreter/oopMapCache.hpp b/src/hotspot/share/interpreter/oopMapCache.hpp index d66ca23feb851..0c2e45036eb56 100644 --- a/src/hotspot/share/interpreter/oopMapCache.hpp +++ b/src/hotspot/share/interpreter/oopMapCache.hpp @@ -84,7 +84,7 @@ class InterpreterOopMap: ResourceObj { private: Method* _method; // the method for which the mask is valid unsigned short _bci; // the bci for which the mask is valid - int _mask_size; // the mask size in bits + int _mask_size; // the mask size in bits (USHRT_MAX if invalid) int _expression_stack_size; // the size of the expression stack in slots protected: @@ -146,6 +146,8 @@ class InterpreterOopMap: ResourceObj { int expression_stack_size() const { return _expression_stack_size; } + // Determines if a valid mask has been computed + bool has_valid_mask() const { return _mask_size != USHRT_MAX; } }; class OopMapCache : public CHeapObj { diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp index 09b3cb26203d1..5affc354ad3fe 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp @@ -35,6 +35,7 @@ #include "compiler/oopMap.hpp" #include "interpreter/bytecodeStream.hpp" #include "interpreter/linkResolver.hpp" +#include "interpreter/oopMapCache.hpp" #include "jfr/jfrEvents.hpp" #include "jvmci/jvmciCodeInstaller.hpp" #include "jvmci/jvmciCompilerToVM.hpp" @@ -2512,7 +2513,7 @@ C2V_VMENTRY_NULL(jlongArray, registerNativeMethods, (JNIEnv* env, jobject, jclas jlongArray info = (jlongArray) JNIHandles::make_local(THREAD, info_oop); runtime->init_JavaVM_info(info, JVMCI_CHECK_0); return info; -} +C2V_END C2V_VMENTRY_PREFIX(jboolean, isCurrentThreadAttached, (JNIEnv* env, jobject c2vm)) if (thread == nullptr || thread->libjvmci_runtime() == nullptr) { @@ -2777,7 +2778,7 @@ C2V_VMENTRY_0(jlong, translate, (JNIEnv* env, jobject, jobject obj_handle, jbool return 0L; } return (jlong) PEER_JVMCIENV->make_global(result).as_jobject(); -} +C2V_END C2V_VMENTRY_NULL(jobject, unhand, (JNIEnv* env, jobject, jlong obj_handle)) requireJVMCINativeLibrary(JVMCI_CHECK_NULL); @@ -2790,13 +2791,13 @@ C2V_VMENTRY_NULL(jobject, unhand, (JNIEnv* env, jobject, jlong obj_handle)) JVMCIENV->destroy_global(global_handle_obj); return result; -} +C2V_END C2V_VMENTRY(void, updateHotSpotNmethod, (JNIEnv* env, jobject, jobject code_handle)) JVMCIObject code = JVMCIENV->wrap(code_handle); // Execute this operation for the side effect of updating the InstalledCode state JVMCIENV->get_nmethod(code); -} +C2V_END C2V_VMENTRY_NULL(jbyteArray, getCode, (JNIEnv* env, jobject, jobject code_handle)) JVMCIObject code = JVMCIENV->wrap(code_handle); @@ -2812,7 +2813,7 @@ C2V_VMENTRY_NULL(jbyteArray, getCode, (JNIEnv* env, jobject, jobject code_handle JVMCIPrimitiveArray result = JVMCIENV->new_byteArray(code_size, JVMCI_CHECK_NULL); JVMCIENV->copy_bytes_from(code_bytes, result, 0, code_size); return JVMCIENV->get_jbyteArray(result); -} +C2V_END C2V_VMENTRY_NULL(jobject, asReflectionExecutable, (JNIEnv* env, jobject, ARGUMENT_PAIR(method))) requireInHotSpot("asReflectionExecutable", JVMCI_CHECK_NULL); @@ -2828,7 +2829,7 @@ C2V_VMENTRY_NULL(jobject, asReflectionExecutable, (JNIEnv* env, jobject, ARGUMEN executable = Reflection::new_method(m, false, CHECK_NULL); } return JNIHandles::make_local(THREAD, executable); -} +C2V_END static InstanceKlass* check_field(Klass* klass, jint index, JVMCI_TRAPS) { if (!klass->is_instance_klass()) { @@ -2850,7 +2851,7 @@ C2V_VMENTRY_NULL(jobject, asReflectionField, (JNIEnv* env, jobject, ARGUMENT_PAI fieldDescriptor fd(iklass, index); oop reflected = Reflection::new_field(&fd, CHECK_NULL); return JNIHandles::make_local(THREAD, reflected); -} +C2V_END static jbyteArray get_encoded_annotation_data(InstanceKlass* holder, AnnotationArray* annotations_array, bool for_class, jint filter_length, jlong filter_klass_pointers, @@ -2964,7 +2965,7 @@ C2V_VMENTRY_NULL(jobjectArray, getFailedSpeculations, (JNIEnv* env, jobject, jlo JVMCIENV->put_object_at(result, result_index++, entry); } return JVMCIENV->get_jobjectArray(result); -} +C2V_END C2V_VMENTRY_0(jlong, getFailedSpeculationsAddress, (JNIEnv* env, jobject, ARGUMENT_PAIR(method))) methodHandle method(THREAD, UNPACK_PAIR(Method, method)); @@ -2975,11 +2976,11 @@ C2V_VMENTRY_0(jlong, getFailedSpeculationsAddress, (JNIEnv* env, jobject, ARGUME method->set_method_data(method_data); } return (jlong) method_data->get_failed_speculations_address(); -} +C2V_END C2V_VMENTRY(void, releaseFailedSpeculations, (JNIEnv* env, jobject, jlong failed_speculations_address)) FailedSpeculation::free_failed_speculations((FailedSpeculation**)(address) failed_speculations_address); -} +C2V_END C2V_VMENTRY_0(jboolean, addFailedSpeculation, (JNIEnv* env, jobject, jlong failed_speculations_address, jbyteArray speculation_obj)) JVMCIPrimitiveArray speculation_handle = JVMCIENV->wrap(speculation_obj); @@ -2987,7 +2988,7 @@ C2V_VMENTRY_0(jboolean, addFailedSpeculation, (JNIEnv* env, jobject, jlong faile char* speculation = NEW_RESOURCE_ARRAY(char, speculation_len); JVMCIENV->copy_bytes_to(speculation_handle, (jbyte*) speculation, 0, speculation_len); return FailedSpeculation::add_failed_speculation(nullptr, (FailedSpeculation**)(address) failed_speculations_address, (address) speculation, speculation_len); -} +C2V_END C2V_VMENTRY(void, callSystemExit, (JNIEnv* env, jobject, jint status)) JavaValue result(T_VOID); @@ -2999,11 +3000,11 @@ C2V_VMENTRY(void, callSystemExit, (JNIEnv* env, jobject, jint status)) vmSymbols::int_void_signature(), &jargs, CHECK); -} +C2V_END C2V_VMENTRY_0(jlong, ticksNow, (JNIEnv* env, jobject)) return CompilerEvent::ticksNow(); -} +C2V_END C2V_VMENTRY_0(jint, registerCompilerPhase, (JNIEnv* env, jobject, jstring jphase_name)) #if INCLUDE_JFR @@ -3013,14 +3014,14 @@ C2V_VMENTRY_0(jint, registerCompilerPhase, (JNIEnv* env, jobject, jstring jphase #else return -1; #endif // !INCLUDE_JFR -} +C2V_END C2V_VMENTRY(void, notifyCompilerPhaseEvent, (JNIEnv* env, jobject, jlong startTime, jint phase, jint compileId, jint level)) EventCompilerPhase event; if (event.should_commit()) { CompilerEvent::PhaseEvent::post(event, startTime, phase, compileId, level); } -} +C2V_END C2V_VMENTRY(void, notifyCompilerInliningEvent, (JNIEnv* env, jobject, jint compileId, ARGUMENT_PAIR(caller), ARGUMENT_PAIR(callee), jboolean succeeded, jstring jmessage, jint bci)) EventCompilerInlining event; @@ -3030,7 +3031,7 @@ C2V_VMENTRY(void, notifyCompilerInliningEvent, (JNIEnv* env, jobject, jint compi JVMCIObject message = JVMCIENV->wrap(jmessage); CompilerEvent::InlineEvent::post(event, compileId, caller, callee, succeeded, JVMCIENV->as_utf8_string(message), bci); } -} +C2V_END C2V_VMENTRY(void, setThreadLocalObject, (JNIEnv* env, jobject, jint id, jobject value)) requireInHotSpot("setThreadLocalObject", JVMCI_CHECK); @@ -3040,7 +3041,7 @@ C2V_VMENTRY(void, setThreadLocalObject, (JNIEnv* env, jobject, jint id, jobject } THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), err_msg("%d is not a valid thread local id", id)); -} +C2V_END C2V_VMENTRY_NULL(jobject, getThreadLocalObject, (JNIEnv* env, jobject, jint id)) requireInHotSpot("getThreadLocalObject", JVMCI_CHECK_NULL); @@ -3049,7 +3050,7 @@ C2V_VMENTRY_NULL(jobject, getThreadLocalObject, (JNIEnv* env, jobject, jint id)) } THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), err_msg("%d is not a valid thread local id", id)); -} +C2V_END C2V_VMENTRY(void, setThreadLocalLong, (JNIEnv* env, jobject, jint id, jlong value)) requireInHotSpot("setThreadLocalLong", JVMCI_CHECK); @@ -3061,7 +3062,7 @@ C2V_VMENTRY(void, setThreadLocalLong, (JNIEnv* env, jobject, jint id, jlong valu THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), err_msg("%d is not a valid thread local id", id)); } -} +C2V_END C2V_VMENTRY_0(jlong, getThreadLocalLong, (JNIEnv* env, jobject, jint id)) requireInHotSpot("getThreadLocalLong", JVMCI_CHECK_0); @@ -3073,7 +3074,49 @@ C2V_VMENTRY_0(jlong, getThreadLocalLong, (JNIEnv* env, jobject, jint id)) THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), err_msg("%d is not a valid thread local id", id)); } -} +C2V_END + +C2V_VMENTRY(void, getOopMapAt, (JNIEnv* env, jobject, ARGUMENT_PAIR(method), + jint bci, jlongArray oop_map_handle)) + methodHandle method(THREAD, UNPACK_PAIR(Method, method)); + if (bci < 0 || bci >= method->code_size()) { + JVMCI_THROW_MSG(IllegalArgumentException, + err_msg("bci %d is out of bounds [0 .. %d)", bci, method->code_size())); + } + InterpreterOopMap mask; + OopMapCache::compute_one_oop_map(method, bci, &mask); + if (!mask.has_valid_mask()) { + JVMCI_THROW_MSG(IllegalArgumentException, err_msg("bci %d is not valid", bci)); + } + if (mask.number_of_entries() == 0) { + return; + } + + int nslots = method->max_locals() + method->max_stack(); + int nwords = ((nslots - 1) / 64) + 1; + JVMCIPrimitiveArray oop_map = JVMCIENV->wrap(oop_map_handle); + int oop_map_len = JVMCIENV->get_length(oop_map); + if (nwords > oop_map_len) { + JVMCI_THROW_MSG(IllegalArgumentException, + err_msg("oop map too short: %d > %d", nwords, oop_map_len)); + } + + jlong* oop_map_buf = NEW_RESOURCE_ARRAY_IN_THREAD_RETURN_NULL(THREAD, jlong, nwords); + if (oop_map_buf == nullptr) { + JVMCI_THROW_MSG(InternalError, err_msg("could not allocate %d longs", nwords)); + } + for (int i = 0; i < nwords; i++) { + oop_map_buf[i] = 0L; + } + + BitMapView oop_map_view = BitMapView((BitMap::bm_word_t*) oop_map_buf, nwords * BitsPerLong); + for (int i = 0; i < nslots; i++) { + if (mask.is_oop(i)) { + oop_map_view.set_bit(i); + } + } + JVMCIENV->copy_longs_from((jlong*)oop_map_buf, oop_map, 0, nwords); +C2V_END #define CC (char*) /*cast a literal from (const char*)*/ #define FN_PTR(f) CAST_FROM_FN_PTR(void*, &(c2v_ ## f)) @@ -3232,6 +3275,7 @@ JNINativeMethod CompilerToVM::methods[] = { {CC "registerCompilerPhase", CC "(" STRING ")I", FN_PTR(registerCompilerPhase)}, {CC "notifyCompilerPhaseEvent", CC "(JIII)V", FN_PTR(notifyCompilerPhaseEvent)}, {CC "notifyCompilerInliningEvent", CC "(I" HS_METHOD2 HS_METHOD2 "ZLjava/lang/String;I)V", FN_PTR(notifyCompilerInliningEvent)}, + {CC "getOopMapAt", CC "(" HS_METHOD2 "I[J)V", FN_PTR(getOopMapAt)}, }; int CompilerToVM::methods_count() { 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 0ca3c731bcf92..98eac1eb5bf2a 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 @@ -1473,4 +1473,13 @@ public void close() { } } } + + /** + * @see HotSpotResolvedJavaMethod#getOopMapAt + */ + void getOopMapAt(HotSpotResolvedJavaMethodImpl method, int bci, long[] oopMap) { + getOopMapAt(method, method.getMethodPointer(), bci, oopMap); + } + + native void getOopMapAt(HotSpotResolvedJavaMethodImpl method, long methodPointer, int bci, long[] oopMap); } diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethod.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethod.java index a7f80cea39a07..0f5c5002e0a2d 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethod.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethod.java @@ -23,6 +23,7 @@ package jdk.vm.ci.hotspot; import java.lang.reflect.Modifier; +import java.util.BitSet; import jdk.vm.ci.meta.JavaMethod; import jdk.vm.ci.meta.ResolvedJavaMethod; @@ -127,4 +128,18 @@ default boolean isDefault() { boolean hasCodeAtLevel(int entryBCI, int level); int methodIdnum(); + + + /** + * Computes which local variables and operand stack slots in {@code method} contain + * live object values at the instruction denoted by {@code bci}. This is the "oop map" + * used by the garbage collector for interpreter frames. + * + * @param bci the index of an instruction in this method's bytecodes + * @return the computed oop map. The first {@link #getMaxLocals} bits are for + * the local variables, the remaining bits are for the stack slots. + * @throws IllegalArgumentException if this method has no bytecode or + * {@code bci} is not the index of a bytecode instruction + */ + BitSet getOopMapAt(int bci); } diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl.java index 046fa1082b9d5..6b9381df1ec7d 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl.java @@ -35,6 +35,7 @@ import java.lang.reflect.Executable; import java.lang.reflect.Modifier; import java.lang.reflect.Type; +import java.util.BitSet; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -767,4 +768,15 @@ private List getAnnotationData0(ResolvedJavaType... filter) { byte[] encoded = compilerToVM().getEncodedExecutableAnnotationData(this, filter); return VMSupport.decodeAnnotations(encoded, AnnotationDataDecoder.INSTANCE); } + + @Override + public BitSet getOopMapAt(int bci) { + if (getCodeSize() == 0) { + throw new IllegalArgumentException("has no bytecode"); + } + int nwords = ((getMaxLocals() + getMaxStackSize() - 1) / 64) + 1; + long[] oopMap = new long[nwords]; + compilerToVM().getOopMapAt(this, bci, oopMap); + return BitSet.valueOf(oopMap); + } } diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaMethod.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaMethod.java index ce927e03ea13b..41eccc9d1c65d 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaMethod.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaMethod.java @@ -28,6 +28,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; +import java.util.BitSet; /** * Represents a resolved Java method. Methods, like fields and types, are resolved through diff --git a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaMethod.java b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaMethod.java index 12bad736adf0e..d82a7dcbc9069 100644 --- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaMethod.java +++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaMethod.java @@ -35,6 +35,10 @@ * @modules jdk.internal.vm.ci/jdk.vm.ci.meta * jdk.internal.vm.ci/jdk.vm.ci.runtime * jdk.internal.vm.ci/jdk.vm.ci.common + * jdk.internal.vm.ci/jdk.vm.ci.hotspot + * java.base/jdk.internal.classfile + * java.base/jdk.internal.classfile.attribute + * java.base/jdk.internal.classfile.constantpool * java.base/jdk.internal.reflect * java.base/jdk.internal.misc * java.base/jdk.internal.vm @@ -50,6 +54,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import java.io.DataInputStream; import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -61,18 +66,35 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import org.junit.Assert; import org.junit.Test; import jdk.internal.vm.test.AnnotationTestInput; +import jdk.internal.classfile.Attributes; +import jdk.internal.classfile.Classfile; +import jdk.internal.classfile.ClassModel; +import jdk.internal.classfile.CodeElement; +import jdk.internal.classfile.MethodModel; +import jdk.internal.classfile.Instruction; +import jdk.internal.classfile.attribute.CodeAttribute; + import jdk.vm.ci.meta.ConstantPool; import jdk.vm.ci.meta.ExceptionHandler; import jdk.vm.ci.meta.ResolvedJavaMethod; @@ -82,6 +104,7 @@ import jdk.vm.ci.runtime.test.TestResolvedJavaMethod.AnnotationDataTest.Annotation2; import jdk.vm.ci.runtime.test.TestResolvedJavaMethod.AnnotationDataTest.Annotation3; import jdk.vm.ci.runtime.test.TestResolvedJavaMethod.AnnotationDataTest.NumbersDE; +import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod; /** * Tests for {@link ResolvedJavaMethod}. @@ -567,6 +590,152 @@ public void getAnnotationDataTest() throws Exception { Assert.assertTrue(numbersDEType.isInitialized()); } + private static ClassModel readClassfile(Class c) throws Exception { + String name = c.getName(); + final int lastDot = name.lastIndexOf('.'); + if (lastDot != -1) { + name = name.substring(lastDot + 1); + } + URI uri = c.getResource(name + ".class").toURI(); + if (uri.getScheme().equals("jar")) { + final String[] parts = uri.toString().split("!"); + if (parts.length == 2) { + try (FileSystem fs = FileSystems.newFileSystem(URI.create(parts[0]), new HashMap<>())) { + return Classfile.of().parse(fs.getPath(parts[1])); + } + } + } + return Classfile.of().parse(Paths.get(uri)); + } + + public static void methodWithManyArgs( + Object o0, int i1, int i2, int i3, int i4, int i5, int i6, int i7, + int i8, int i9, int i10, int i11, int i12, int i13, int i14, int i15, + int i16, int i17, int i18, int i19, int i20, int i21, int i22, int i23, + int i24, int i25, int i26, int i27, int i28, int i29, int i30, int i31, + int i32, int i33, int i34, int i35, int i36, int i37, int i38, int i39, + int i40, int i41, int i42, int i43, int i44, int i45, int i46, int i47, + int i48, int i49, int i50, int i51, int i52, int i53, int i54, int i55, + int i56, int i57, int i58, int i59, int i60, int i61, int i62, int i63, + Object o64, int i65, int i66, int i67, int i68, int i69, int i70, int i71, + int i72, int i73, int i74, int i75, int i76, int i77, int i78, int i79, + int i80, int i81, int i82, int i83, int i84, int i85, int i86, int i87, + int i88, int i89, int i90, int i91, int i92, int i93, int i94, int i95, + int i96, int i97, int i98, int i99, int i100, int i101, int i102, int i103, + int i104, int i105, int i106, int i107, int i108, int i109, int i110, int i111, + int i112, int i113, int i114, int i115, int i116, int i117, int i118, int i119, + int i120, int i121, int i122, int i123, int i124, int i125, int i126, int i127, + Object o128) + { + o0.hashCode(); + o64.hashCode(); + if (o128 != null) { + Object t1 = "tmp val"; + t1.hashCode(); + } else { + int t1 = 42 + i1; + String.valueOf(t1); + } + o128.hashCode(); + } + + private static Map buildMethodMap(ResolvedJavaType type) { + Map methodMap = new HashMap<>(); + for (ResolvedJavaMethod m : type.getDeclaredMethods()) { + if (m.hasBytecodes()) { + String key = m.getName() + ":" + m.getSignature().toMethodDescriptor(); + methodMap.put(key, m); + } + } + for (ResolvedJavaMethod m : type.getDeclaredConstructors()) { + if (m.hasBytecodes()) { + String key = ":" + m.getSignature().toMethodDescriptor(); + methodMap.put(key, m); + } + } + ResolvedJavaMethod clinit = type.getClassInitializer(); + if (clinit != null) { + String key = ":()V"; + methodMap.put(key, clinit); + } + return methodMap; + } + + @Test + public void getOopMapAtTest() throws Exception { + Collection> allClasses = new ArrayList<>(classes); + + // Add this class so that methodWithManyArgs is processed + allClasses.add(getClass()); + + boolean[] processedMethodWithManyArgs = {false}; + + for (Class c : allClasses) { + if (c.isArray() || c.isPrimitive() || c.isHidden()) { + continue; + } + ResolvedJavaType type = metaAccess.lookupJavaType(c); + Map methodMap = buildMethodMap(type); + ClassModel cf = readClassfile(c); + for (MethodModel cm : cf.methods()) { + cm.findAttribute(Attributes.CODE).ifPresent(codeAttr -> { + String key = cm.methodName().stringValue() + ":" + cm.methodType().stringValue(); + HotSpotResolvedJavaMethod m = (HotSpotResolvedJavaMethod) Objects.requireNonNull(methodMap.get(key)); + boolean isMethodWithManyArgs = c == getClass() && m.getName().equals("methodWithManyArgs"); + if (isMethodWithManyArgs) { + processedMethodWithManyArgs[0] = true; + } + int maxSlots = m.getMaxLocals() + m.getMaxStackSize(); + + int bci = 0; + Map expectOopMaps = !isMethodWithManyArgs ? null : Map.of( + "{0, 64, 128}", new int[] {0}, + "{0, 64, 128, 130}", new int[] {0}, + "{0, 64, 128, 129}", new int[] {0}); + for (CodeElement i : codeAttr.elementList()) { + if (i instanceof Instruction ins) { + BitSet oopMap = m.getOopMapAt(bci); + if (isMethodWithManyArgs) { + System.out.printf("methodWithManyArgs@%d [%d]: %s%n", bci, maxSlots, oopMap); + System.out.printf("methodWithManyArgs@%d [%d]: %s%n", bci, maxSlots, ins); + + // Assumes stability of javac output + String where = "methodWithManyArgs@" + bci; + String oopMapString = String.valueOf(oopMap); + int[] count = expectOopMaps.get(oopMapString); + if (count == null) { + throw new AssertionError(where + ": unexpected oop map: " + oopMapString); + } + count[0]++; + } + + // Requesting an oop map at an invalid BCI must throw an exception + if (ins.sizeInBytes() > 1) { + try { + oopMap = m.getOopMapAt(bci + 1); + throw new AssertionError("expected exception for illegal bci %d in %s: %s".formatted(bci + 1, m.format("%H.%n(%p)"), oopMap)); + } catch(IllegalArgumentException e) { + // expected + } + } + bci += ins.sizeInBytes(); + } + } + if (isMethodWithManyArgs) { + for (var e : expectOopMaps.entrySet()) { + if (e.getValue()[0] == 0) { + throw new AssertionError(m.format("%H.%n(%p)") + "did not find expected oop map: " + e.getKey()); + } + System.out.printf("methodWithManyArgs: %s = %d%n", e.getKey(), e.getValue()[0]); + } + } + }); + } + } + + Assert.assertTrue(processedMethodWithManyArgs[0]); + } + private Method findTestMethod(Method apiMethod) { String testName = apiMethod.getName() + "Test"; for (Method m : getClass().getDeclaredMethods()) {