-
Notifications
You must be signed in to change notification settings - Fork 722
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Abort compilations for exceptions during invokedynamic resolution #17625
Abort compilations for exceptions during invokedynamic resolution #17625
Conversation
@0xdaryl Requesting review |
I believe the new VM API being added will need a JITServer override. I will try to implement that as well. |
runtime/compiler/env/VMJ9.h
Outdated
@@ -279,6 +279,16 @@ class TR_J9VMBase : public TR_FrontEnd | |||
virtual bool jitStaticsAreSame(TR_ResolvedMethod *, int32_t, TR_ResolvedMethod *, int32_t); | |||
virtual bool jitFieldsAreSame(TR_ResolvedMethod *, int32_t, TR_ResolvedMethod *, int32_t, int32_t); | |||
|
|||
/** | |||
* \brief Check is object is an array class instance |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First "is" -> "if"
runtime/compiler/env/VMJ9.h
Outdated
* | ||
* \param currentThread the current thread | ||
* \param object the object address | ||
* \return true if object is an array class instance |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can just say ; false otherwise
for brevity and delete the next line.
runtime/compiler/env/VMJ9.h
Outdated
* \return true if object is an array class instance | ||
* \return false if object is not an array class instance | ||
*/ | ||
virtual bool objectIsArray(J9VMThread *currentThread, TR_OpaqueClassBlock *object); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may have chosen this name because it matches the helper you end up calling, but since this is an inquiry API in the FrontEnd would it be better to call it isArrayObject()
instead?
2a8225f
to
058b1e0
Compare
Just confirming.... Is this issue only with the JIT's (mis)understanding of invokedynamic and the fix applies to both the OpenJ9 MH implementation as well as the OpenJDK LambdaForm MH implementations? |
058b1e0
to
9da1856
Compare
No, this fix only applies to the OpenJDK MH implementation. The older OpenJ9 MH implementation has a different mechanism for invokedynamic resolution, and does not place an exception object in the CallSite table entry, and the valid table entries themselves are not arrays. |
@0xdaryl I have addressed your initial review comments. There are some JITServer test failures due to the missing JITServer implementation. I am currently working on that. |
OK, I just expanded the code around the fix and saw the #ifdefs guarding it, which is what I was going to ask you to add. So it is all good. Tag me when you think this PR is ready for another review. |
3426a6d
to
84a38de
Compare
Jenkins test sanity,sanity.openjdk all jdk17 |
Jenkins test sanity,sanity.openjdk all jdk21 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Since a new message has been added, could you please increment the MINOR_NUMBER here
https://github.com/eclipse-openj9/openj9/blob/master/runtime/compiler/net/CommunicationStream.hpp#L121
Also, I was wondering why the parameter for isArrayObject(TR_OpaqueClassBlock *object);
is declared to be as a pointer to a class, when in fact it's an object.
@mpirvu I have updated the MINOR_NUMBER
Thanks for pointing that out, I should not be using TR_OpaqueClassBlock* type for an object. Fixed that as well. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am having second thoughts about the implementation. The code seems to be peeking inside objects without making sure it has VM access. Without VM access in hand, the GC can move the objects while the compiler attempts to inspect them. I made some suggestions on how the code needs to be changed.
Moreover, the JITServer should never receive any object references. Thus, the new frontend method should just fatal assert if executed at the server rather than sending a message at the client.
runtime/compiler/env/j9method.cpp
Outdated
// and appendix objects necessary for constructing a resolved invokedynamic adapter method call. | ||
// The exception object would not be an array type object, and therefore it is sufficient to check if | ||
// the entry obtained from the CallSite table is an array instance to determine if the resolution was successful. | ||
if (!fej9()->isArrayObject(*invokeCacheArray)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am having second thoughts about this one. if *invokeCacheArray
is truly a pointer to an object, what prevents GC from moving that object while we try to access it? We may need to hold VMAccess while executing if (!fej9()->isArrayObject(*invokeCacheArray))
(extend the VMAccessCriticalSection
which starts below).
There is also a comment below that "// this will not work in AOT or JITServer", so probably there is some guarantee that we cannot enter this path for JITServer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if *invokeCacheArray is truly a pointer to an object, what prevents GC from moving that object while we try to access it?
I did not feel it was necessary to ensure VM access for this. My reasoning was, immediately after checking if the corresponding entry in the call site table had a valid object pointer using
openj9/runtime/compiler/env/j9method.cpp
Lines 5915 to 5919 in ab5dfcf
bool | |
TR_ResolvedJ9Method::isUnresolvedCallSiteTableEntry(int32_t callSiteIndex) | |
{ | |
return *(j9object_t*)callSiteTableEntryAddress(callSiteIndex) == NULL; | |
} |
We want to check whether that non-null object pointer cached in the call site table slot was an array or not. If that object pointer at ramClass->callSites[callSiteIndex]
(i.e, *invokeCacheArray
) is not pointing to a valid array object, then we should abort anyway. The slot is populated by an object that the VM's invokedynamic resolution mechanism allocates and (I'm speculating) the GC probably can not move. It's not a regular Java object. Once a call site table slot is populated (only if it was NULL to begin with), it is not modified again. What we do need VM access for however is accessing the individual elements in the invokeCacheArray
, which is why we have the VM access being acquired right after this.
Based on my reasoning, do you still think it is necessary to put the isArrayObject check within the block with VM access acquired?
There is also a comment below that "// this will not work in AOT or JITServer", so probably there is some guarantee that we cannot enter this path for JITServer.
This was true when this code was first written during the initial OpenJDK MethodHandle implementation, but since then JITServer became capable of supporting OpenJDK MHs and I can confirm that this code path is executed in JITServer now based on my testing. I will update the comment here to reflect the current state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Responding to your overall review comment:
Moreover, the JITServer should never receive any object references. Thus, the new frontend method should just fatal assert if executed at the server rather than sending a message at the client.
Wouldn't this mean that JITServer loses support for any method containing invokedynamic
bytecodes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If that array stored in one of the callsites is really special and cannot be moved by GC (maybe because it does not reside in the heap), then you don't need VM access for checking whether it's an array. Interesting though that the two elements of the array are protected by VM access.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fej9()->targetMethodFromMemberName()
has a FATAL_ASSERT in the JITServer implementation.
TR_OpaqueMethodBlock*
TR_J9ServerVM::targetMethodFromMemberName(uintptr_t memberName)
{
TR_ASSERT_FATAL(false, "targetMethodFromMemberName must not be called on JITServer");
return NULL;
}
so if we ever entered that path while compiling at the server, the assert would be triggered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another point: getResolvedDynamicMethod()
has a different implementation for JITServer where the server asks the client to execute the corresponding method and then, with the answers received from the client, the server builds a resolvedMethod. Thus, the server does not need to access Java objects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for pointing those out, I had the wrong understanding of how things were being handled in JITServer mode. I will make the changes as you suggested.
I will also enforce VM access requirement for checking if object is array, so as not to cause confusion in the future for others, as there is no real benefit to doing this without VM access but lots of potential danger if this API was later used to check if heap objects were array types.
I am surprised though that we cache object references in J9Class (J9Class->callSites[]). Either GC cannot move such objects or somehow it updates all these pointers when objects are moved. |
48470a6
to
6866cb6
Compare
@mpirvu I have updated the implementation based on the review comments. I would appreciate another review. |
6866cb6
to
15fcf3e
Compare
15fcf3e
to
b314536
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
jenkins test sanity all jdk20 |
jenkins test sanity plinuxjit,xlinuxjit,zlinuxjit jdk20 |
All tests have passed. |
Invokedynamic call resolution can either result in a valid entry in the CallSite table if successful, or an exception object otherwise. The call site table entry is a two element array containing the MemberName and appendix objects necessary for constructing a resolved invokedynamic call. The exception object would not be an array type object, and therefore it is sufficient to check if the entry obtained from the CallSite table is an array instance to determine if the resolution was successful. This changeset handles the cases when we have an invalid entry in the CallSite table that results in either failed inlining or failed compilation, depending on whether the invokedynamic is part of the inlining candidate or the root method, respectively. Signed-off-by: Nazim Bhuiyan <[email protected]>
isInvokeCacheEntryAnArray is used to check if invokedynamic bytecodes' CallSite table entries are valid invoke cache array objects. If the resolution fails, we end up with an exception object in the corresponding table slot instead of an array object. Signed-off-by: Nazim Bhuiyan <[email protected]>
Signed-off-by: Nazim Bhuiyan <[email protected]>
b314536
to
c67008b
Compare
@0xdaryl I have addressed your comments. The latest force push addressed indentation issues, removed an empty line I accidentally added, and added curly braces for an if statement. As the tests have passed earlier although not visible due to the updated branch, relaunching them should not be necessary for these changes alone. |
Recent changes were mostly whitespace. Previous testing was successful. No need to re-run CI. Merging. |
Invokedynamic call resolution can either result in a valid entry
in the CallSite table if successful, or an exception object
otherwise. The call site table entry is a two element array
containing the MemberName and appendix objects necessary for
constructing a resolved invokedynamic call. The exception object
would not be an array type object, and therefore it is sufficient
to check if the entry obtained from the CallSite table is an array
instance to determine if the resolution was successful.
This changeset handles the cases when we have an invalid entry
in the CallSite table that results in either failed inlining or
failed compilation, depending on whether the invokedynamic is part
of the inlining candidate or the root method, respectively.
Issue: #16965