-
Notifications
You must be signed in to change notification settings - Fork 771
Initialize temporary used to test whether heapification is needed #22783
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
Initialize temporary used to test whether heapification is needed #22783
Conversation
@vijaysun-omr, may I ask you to review this pull request? The problem can be observed with a test like the following:
Compiled with the following options, Escape Analysis stack allocates the instance of
In the IL that's generated by Escape Analysis, Entry into method following Escape Analysis
Stack allocation code
Cold block heapification code
The fix ensures @jdmpapin, FYI. Thank you for discovering this problem! |
I thought live variables for GC would track uninitialized reference local variable use (load) and zero initialize such local variables in the prologue. Is this not happening here OR did we feel that we should handle it explicitly in the IL trees via a store anyway ? |
I thought so too, but I recently looked for that zero-initialization in another case where I saw an uninitialized auto (which led me to open #22754), and I didn't find it there, though somehow I did always seem to get an NPE from the interpreter, which suggests that at least with my setup it was always zero anyway. Maybe I just missed where it happens. My contention though is that even if we have that kind of a safety net, we should never load from an auto that has not been (explicitly) defined. I'm not sure that we should require that fact to be syntactically "obvious," e.g. demonstrable by a standard liveness analysis, but at least dynamically when we load at runtime, I think the auto should have to have been defined by that point. It's just harder to analyze code that might implicitly use some default value from the entry, and I don't think there's any strong reason to generate IL that does so, or to transform IL in a way that makes it do so. If we want the auto to have a particular value on entry to the method, then it's very straightforward to simply generate a store. As an example of the difficulty in analysis, soon I should be contributing a new opt pass to OMR that reasons flow-insensitively about the possible values of autos, and it does this simply by considering all definitions. If we're allowed to load from an uninitialized (or... not explicitly initialized) auto, then any use of an auto might be doing that, and my new pass would need to do a dataflow analysis just to rule it out. (It was an assertion in this new pass that led to the discovery that the temp created by EA could be used uninitialized.) Another example is that if an auto has exactly one definition, then (IMO) we should be able to reason that all uses see the value from that definition. This might be implicitly assumed in some parts of the compiler - at least, I don't know that it isn't, and I doubt that anybody else does either. Finally, liveness for GC only takes / would take care of address-typed autos, since that's all we have to worry about for GC purposes. But I think we shouldn't load from uninitialized autos of other types either, and IMO it would be inconsistent to say that autos must be defined before use, but the address-typed ones are OK because we happen to have a GC interaction. |
I do think a case could be made that the load should be allowed but that the value must be ultimately irrelevant (in some sufficiently precise way). I believe that would still allow for the types of analysis that I've argued for, but it would require us to introduce a notion something like LLVM's poison. |
I'll think about the arguments that Devin is making about how things ought to be, but I wanted to pop out the place that I believe the initialization would get driven by :
sets the property on autos and that gets consulted in the code generator. |
I agree with all of Devin's reasons, especially the ones about complicating analyses (including the one he's about to add) and consistency. I don't believe there was a deep reasoning behind not having the store in this particular case. It was just something that was going to be implicitly handled by the codegen anyway, but it should be (mostly) equivalent from a performance standpoint to expose the store in the IL trees and it has several other benefits. I will review this change with that understanding |
Jenkins test sanity all jdk21 |
@hzongaro I wonder if the commit message and PR message needs to be changed to omit talking about uninitialized value being seen by GC, and mentioning the motivation being more of a cleanup as discussed for the above reasons. |
To elaborate a little bit, openj9/runtime/compiler/codegen/CodeGenGC.cpp Line 288 in 5d6eadb
It calls openj9/runtime/compiler/codegen/CodeGenGC.cpp Line 502 in 5d6eadb
Then the number of slots to be initialized is checked in
I was able to find the zero-initialization in a log from Henry's test case with |
Yes, I will adjust that. |
The heapifyForColdBlocks method of Escape Analysis creates a local temporary that it uses to hold a reference to a stack allocated object at the point of the stack allocation. If the object might need to be heapified, it generates IL that tests whether that temporary still holds a reference to the stack allocated object, and if it does, it performs the heapification and updates the temporary. However, the temporary was not explicitly initialized on entry to the method. During Code Generation J9::CodeGenerator::createStackAtlas will ensure that any autos that might contain references on entry to the method are zero initialized. Despite that, it's better practice to ensure that such initialization is explicitly performed explicitly in the IL, as it allows, for instance, other analyses to rely on all the definitions seen in the IL for a particular auto as enumerating the possible values the auto can hold at particular points without having to consider the possibilty that the auto might hold some as yet to be determined default value that it will acquire during Code Generation. In the spirit of upholding that better practice, this changes heapifyForColdBlocks to generate IL at method entry that zero initializes the temporary variable. Signed-off-by: Henry Zongaro <[email protected]>
0ec1cf2
to
fa0f72d
Compare
Done! |
Jenkins test sanity all jdk21 |
Thanks for the commit message change. Could you please also edit the opening comment in this PR that still refers to the original problem description ? |
Sorry for missing that! Done. |
Jenkins test sanity zlinux jdk21 |
I am going to merge this since tests have passed and this change is fine on its own. However, I was wondering if we could have a similar issue with zero initialization happening in the code generator for some other auto that "may" point to the stack allocated object, and is part of the series of tests we lay down before heapification happens, i.e. to update all the autos that are in fact pointing at the stack allocated object to point instead at the heapified object. I thought of this potentially being an issue because we don't check for whether a store "covers" the cold escape point or not, before adding a test for whether the auto is pointing at the stack allocated object. |
The
heapifyForColdBlocks
method of Escape Analysis creates a local temporary that it uses to hold a reference to a stack allocated object at the point of the stack allocation. If the object might need to be heapified, it generates IL that tests whether that temporary still holds a reference to the stack allocated object, and if it does, it performs the heapification and updates the temporary. However, the temporary was not being initialized on entry to the method.Fixed this by generating IL that zero initializes the temporary variable at method entry.
During Code Generation
J9::CodeGenerator::createStackAtlas
will ensure that any autos that might contain references on entry to the method are zero initialized. Despite that, it's better practice to ensure that such initialization is explicitly performed explicitly in the IL, as it allows, for instance, other analyses to rely on all the definitions seen in the IL for a particular auto as enumerating the possible values the auto can hold at particular points without having to consider the possibility that the auto might hold some as yet to be determined default value that it will acquire during Code Generation.In the spirit of upholding that better practice, this changes
heapifyForColdBlocks
to generate IL at method entry that zero initializes the temporary variable.