Skip to content
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

Technical Discussion regarding AOT Validations for Method Handles #11209

Closed
dsouzai opened this issue Nov 17, 2020 · 6 comments
Closed

Technical Discussion regarding AOT Validations for Method Handles #11209

dsouzai opened this issue Nov 17, 2020 · 6 comments

Comments

@dsouzai
Copy link
Contributor

dsouzai commented Nov 17, 2020

I'm opening this issue to discuss a few technical details regarding AOT support for Method Handles.

After some discussion with @liqunl, @andrewcraik, and @vijaysun-omr, my understanding of how this works is that the bytecodes get generated on the fly, as is the case with lambda forms. However, it is possible that constant pool entry for a ldc bytecode could get patched to hold an arbitrary object. If this generated class is to be stored in the SCC, then the bytecodes in the J9ROMClass itself can't be updated [1]. Therefore, just having the class chain of the generated class would not be sufficient, since the RAM ConstantPool entry would get patched at runtime.

Therefore, what we would need is:

  1. Generate class chain of the generated class
  2. Generate class chain of the class of the arb object patched in the constant pool
  3. Verify that the class of the arb object is named by the generated class

Some point of discussion:

If 1. fails, then we can simply abort the compile, since this is equivalent to trying to compile a method who's ROMClass doesn't exist in the SCC

If 2. fails, then we could abort the compile. However, I was wondering; could we just restrict the compiler's knowledge of the object? That is, can we continue with the compile, but state that the class of the object is just java/lang/Object? Optimization will be limited, but we won't have to abort the compile.

Regarding 3., the reason for this is to ensure that what we think is the class of the object that was patched in the constant pool in the compile run is still visible to the generated class in the load run. By this I mean, if in the compile run an object of Class A was patched in the cp entry, and in the load run an object of Class A' was patched in the cp entry, where A and A' have the same ROMClass hierarchy but diff class loaders such that if you were to do a lookup by name you would get A and not A', then we need a way of checking that [2]. With the SVM, it's sufficient to just store a classByName validation record. Without the SVM, it's harder to see how one might do this as the getClassFromSignature front end query doesn't actually do a visibility check; if the class of the arb object is named somewhere in the bytecodes of the generated class, then perhaps we could find the cpIndex where it is named and do a getClassFromCPIndex validation instead? Actually I just realized; even though getClassFromSignature doesn't do a visibility check, we can always compare the answer returned by getClassFromSignature against the class of the arb object.

A more general discussion point. @liqunl had mentioned the KOT; do we hard code the pointer to the object anywhere in the code, or do we just use it to make optimization decisions? If the former is true, then we'd also need a relocation record to pull out the object point from the constant pool and update appropriate locations.

@liqunl @andrewcraik @vijaysun-omr I'd appreciate your thoughts on this matter.

[1] @hangshao0 is my statement true?
[2] @DanHeidinga is it possible for the cp entry to get patched with an object who's class is not visible to the generated class? Will the class of the arb object be named somewhere in the ROMClass of the generated class?

@hangshao0
Copy link
Contributor

[1] @hangshao0 is my statement true?

Yes.

@liqunl
Copy link
Contributor

liqunl commented Nov 17, 2020

If 2 fails, we can restrict optimizations. The object can't be treated as a known object. But I think for customized LambdaForm, usually the object class is from core JCL, loaded by system classloader. And it should be named in the constant pool if it is to be used later.

Notice that the generated methods or classes are not named in its caller's constant pool. They are from MemberName object stored in a field of MH, or from side table of RAM class with invokedynamic/invokehandle. Are we able to relocate these methods and classes?

Most of the performance benefit comes from inlining and field folding guided by known object. We don't hard code the object reference.

@DanHeidinga
Copy link
Member

DanHeidinga commented Nov 18, 2020

[2] @DanHeidinga is it possible for the cp entry to get patched with an object who's class is not visible to the generated class? Will the class of the arb object be named somewhere in the ROMClass of the generated class?

Yes, there's no guarantee that the patched class will name the patched in object's class. The API being used is Unsafe.defineAnonymousClass which is open to anyone with access to Unsafe so we can't depend on any conventions of usage that aren't enforced by the API

@DanHeidinga
Copy link
Member

A more general discussion point. @liqunl had mentioned the KOT; do we hard code the pointer to the object anywhere in the code, or do we just use it to make optimization decisions? If the former is true, then we'd also need a relocation record to pull out the object point from the constant pool and update appropriate locations.

Often for LambdaForms, the object is a MemberName / MethodHandle. (Correct me if I'm wrong, @babsingh / @fengxue-IS). If the AOT code depends on the KOT to know the target method / field referred to by the patched object, then we need to be able to tell if two MemberName/MHs are equivalent or the compiled code can't be reused.

@dsouzai
Copy link
Contributor Author

dsouzai commented Nov 25, 2020

Notice that the generated methods or classes are not named in its caller's constant pool. They are from MemberName object stored in a field of MH, or from side table of RAM class with invokedynamic/invokehandle. Are we able to relocate these methods and classes?

For AOT, all calls go through the interpreter dispatch snippet. However, we'll still need to materialize the J9Method, so I guess we will need a new relocation record to get the J9Method from the MH or the side table. I guess we'd need something similar if the generated methods are inlined.

If the AOT code depends on the KOT to know the target method / field referred to by the patched object, then we need to be able to tell if two MemberName/MHs are equivalent or the compiled code can't be reused.

I believe this can be addressed by validating the class chain of the class of the patched object followed by a getClassFromSignature query using the name of the class of the patched object and comparing the returned J9Class for equality with the J9Class from the patched object. That said, we would need to ensure the KOT is only used for these patched objects (since other objects in the KOT will require different validations).

@dsouzai
Copy link
Contributor Author

dsouzai commented Aug 12, 2024

Closing this given the plan described in #19993.

@dsouzai dsouzai closed this as completed Aug 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants