Implement ROMMethodInfo caching support and stack walker integration#22918
Implement ROMMethodInfo caching support and stack walker integration#22918harship04 wants to merge 6 commits intoeclipse-openj9:masterfrom
Conversation
19d1ee8 to
5f11894
Compare
|
Waiting for the changes we discussed before review (but at a glance, looks good so far). |
24bce23 to
18af487
Compare
|
@gacholio , could you please review the PR |
runtime/vm/swalk.c
Outdated
| MARK_SLOT_AS_OBJECT(walkState, (j9object_t*) &(methodFrame->specialFrameFlags)); | ||
| walkState->method = methodFrame->method; | ||
| if (NULL != walkState->method) { | ||
| initializeBasicROMMethodInfo(walkState, J9_ROM_METHOD_FROM_RAM_METHOD(walkState->method)); |
There was a problem hiding this comment.
I don't think there should be any remaining callers of the basic init code (other than internally in mapcache.cpp) - they should all be replaced with the new ROM method call (other than the one for a bytecoded PC of course).
runtime/stackmap/mapcache.cpp
Outdated
| /* Always compute argument bits */ | ||
| j9localmap_ArgBitsForPC0(romClass, romMethod, newInfo.argbits); | ||
|
|
||
| if (computeStackAndLocals && pc < (UDATA)J9_BYTECODE_SIZE_FROM_ROM_METHOD(romMethod)) { |
There was a problem hiding this comment.
Please bracket the second clause. In fact, why is this necessary? I think we can assume a running PC is withing the bytecode range of its method.
There was a problem hiding this comment.
I removed the pc < (UDATA)J9_BYTECODE_SIZE_FROM_ROM_METHOD(romMethod) check in populateROMMethodInfo, but when I also remove the corresponding condition in getROMMethodInfoForBytecodePC—the one that returns if (pc <= J9SF_MAX_SPECIAL_FRAME_TYPE || pc >= (UDATA)J9_BYTECODE_SIZE_FROM_ROM_METHOD(romMethod)) then the JVM crashes with a segmentation faul
runtime/stackmap/mapcache.cpp
Outdated
| NULL); | ||
| } | ||
|
|
||
| /* Copy metadata */ |
There was a problem hiding this comment.
Should this not be a call to the basic init code?
|
Looks good in general. When computing the maps, you are not checking the size, checking for failure, or setting the bits indicating that the maps have been cached. |
|
The argbits do not seem to be being computed at all. |
495e826 to
b581cfd
Compare
|
The segmentation fault occurred because when pc == 0, the code returned early without initializing walkState->romMethodInfo; this was fixed by calling initializeBasicROMMethodInfo() for special-frame or out-of-bounds PC values. |
|
I added computePendingCountForBytecode() for bytecode-PC frames (argCount + tempCount, plus 1 for synchronized or non-empty methods). J9OSRFrame has pendingStackHeight and numberOfLocals, but J9ROMMethodInfo doesn’t store them. Currently, populateROMMethodInfo() receives these values for OSR frames but doesn’t save them. Should ROMMethodInfo eventually store them for caching, or are they only needed during map computation? |
e5bafb7 to
9ae4274
Compare
|
issue: original code: the method causing the issue will null cp It seems that the code never reached the line that dereferences the constant pool—J9Class *methodClass = UNTAGGED_METHOD_CP(walkState->method)->ramClass—because the frame never passes the checks if (walkState->flags & J9_STACKWALK_ITERATE_O_SLOTS) or if (walkState->argCount). |
|
@gacholio as we discussed, I’ve updated the code . Could you please review it. To safely determine the class loader for caching, I deconstructed the J9_CLASS_FROM_METHOD macro by explicitly retrieving the constant pool using J9_CP_FROM_METHOD(method) and checking if it is NULL. If the constant pool is NULL, the bootstrap loader (vm->systemClassLoader) is used, since such methods are internal VM frames not loaded from class files. |
runtime/stackmap/mapcache.cpp
Outdated
| /* Only compute argbits if method has arguments */ | ||
| if (romMethodInfo->argCount > 0) { | ||
| J9ROMClass *romClass = J9_CLASS_FROM_METHOD(method)->romClass; | ||
| if (romMethodInfo->argCount <= (J9_ARGBITS_CACHE_SLOTS * 32)) { |
There was a problem hiding this comment.
#define J9_STACKMAP_CACHE_SLOTS 2
#define J9_LOCALMAP_CACHE_SLOTS 2
#define J9_ARGBITS_CACHE_SLOTS 2
/* Flag values for J9ROMMethodInfo */
#define J9MAPCACHE_STACKMAP_CACHED 1
#define J9MAPCACHE_LOCALMAP_CACHED 2
#define J9MAPCACHE_ARGBITS_CACHED 4
#define J9MAPCACHE_METHOD_IS_CONSTRUCTOR 8
#define J9MAPCACHE_VALID 128
/* J9ROMMethodInfo must be a multiple of 8 bytes in size */
typedef struct J9ROMMethodInfo {
void *key;
U_32 stackmap[J9_STACKMAP_CACHE_SLOTS];
U_32 localmap[J9_ARGBITS_CACHE_SLOTS];
U_32 argbits[J9_ARGBITS_CACHE_SLOTS];
U_32 modifiers;
32 represents the number of bits in a U_32. Since argbits is stored as an array of U_32, each element can hold 32 argument reference bits.The conditionromMethodInfo->argCount <= (J9_ARGBITS_CACHE_SLOTS * 32)checks whether the number of method arguments can fit in the argbits bitmap used for caching. The argbits field is an array of U_32, and each U_32 contains 32 bits, where each bit represents one argument slot. Since J9_ARGBITS_CACHE_SLOTS is 2, the bitmap can store up to 2 × 32 = 64 argument bits.
There was a problem hiding this comment.
Instead of J9_*_CACHE_SLOTS, perhaps we should have:
/* Cache slot sizes must all be multiples of 64 bits. */
#define J9_STACKMAP_CACHE_BITS 64
#define J9_LOCALMAP_CACHE_BITS 64
#define J9_ARGBITS_CACHE_BITS 64typedef struct J9ROMMethodInfo {
void *key;
U_32 stackmap[J9_STACKMAP_CACHE_BITS / sizeof(U_32)];
U_32 localmap[J9_ARGBITS_CACHE_BITS / sizeof(U_32)];
U_32 argbits[J9_ARGBITS_CACHE_BITS / sizeof(U_32)];Then line 130 (above) becomes:
if (romMethodInfo->argCount <= J9_ARGBITS_CACHE_BITS) {There was a problem hiding this comment.
@keithc-ca ,
Based on my understanding, sizeof(U_32) returns 4 bytes, and sizeof(U_32) * 8 gives 32 bits. If we compute the array size using J9_ARGBITS_CACHE_BITS / sizeof(U_32), it becomes 64 / 4 which is 16.
and if we do this J9_STACKMAP_CACHE_BITS / (sizeof(U_32) * 8) then it will be 64/32 which will be 2 slots
Could you please confirm if this is correct, or if I should keep the original definition using J9_STACKMAP_CACHE_SLOTS 2 instead?
There was a problem hiding this comment.
Sorry, you're right, those arrays should use / 32 instead of / sizeof(U_32):
U_32 stackmap[J9_STACKMAP_CACHE_BITS / 32];
U_32 localmap[J9_ARGBITS_CACHE_BITS / 32];
U_32 argbits[J9_ARGBITS_CACHE_BITS / 32];37d7dad to
7629b32
Compare
d50ed41 to
4129a78
Compare
|
@gacholio, We initially assumed that there was no need to check the PC offset, since the stack walker already determines the frame type and calls the bytecode frame helper. I observed that pcOffset can still be invalid ( very large and outside the bytecode range) because getROMMethodInfoForBytecodeFrame is invoked from This leads to an out-of-bounds bytecodePC being computed and passed into the stackmap logic, which assumes a valid bytecode PC and results in a segmentation fault . |
runtime/oti/j9nonbuilder.h
Outdated
| U_32 stackmap[J9_STACKMAP_CACHE_BITS / 32]; | ||
| U_32 localmap[J9_LOCALMAP_CACHE_BITS / 32]; | ||
| U_32 argbits[J9_ARGBITS_CACHE_BITS / 32]; |
There was a problem hiding this comment.
These fields should use name patterns that are consistent:
U_32 stackMap[J9_STACKMAP_CACHE_BITS / 32];
U_32 localMap[J9_LOCALMAP_CACHE_BITS / 32];
U_32 argBits[J9_ARGBITS_CACHE_BITS / 32];There was a problem hiding this comment.
This has not been addressed.
There was a problem hiding this comment.
This is a work in progress.
There was a problem hiding this comment.
Sorry for the delay, I was on leave.
@keithc-ca, Changes are now updated.
This is the issue - JIT frames are not bytecode frames. This call should be to the ROMMethod entrypoint (JIT frames do not use the computed stack/local maps - they only require the argbits). This lines up with the use of the ROM method as key for the other frames (native method, etc). |
@gacholio I’ve replaced the call with: so that JIT frames now use the ROMMethod entrypoint instead of the bytecode frame helper. |
Implements a unified ROM method caching mechanism for consolidating ROM method metadata. Updates stack walking to use the cached information instead of repeated metadata lookups.