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

Improvements for Relocatable Compilations #2919

Merged
merged 17 commits into from
Oct 9, 2018

Conversation

dsouzai
Copy link
Contributor

@dsouzai dsouzai commented Sep 18, 2018

Depends on eclipse-omr/omr#2978

This PR introduces changes in OpenJ9 required to enable optimizations that are currently implicitly disabled for relocatable compilations. It does so by introducing the Symbol Validation Manager infrastructure. The fundamental idea for the Symbol Validation Manager was thought up by @jdmpapin ; the current implementation is a collaboration between myself and @jdmpapin .

Issue tracking all the PRs: #2921

@dsouzai dsouzai changed the title Aot improvements part2 Improvements for Relocatable Compilations Part 2 Sep 18, 2018
@dsouzai dsouzai changed the title Improvements for Relocatable Compilations Part 2 WIP: Improvements for Relocatable Compilations Part 2 Sep 18, 2018
@dsouzai dsouzai force-pushed the AOTImprovementsPart2 branch 11 times, most recently from 22698e9 to b697ab8 Compare September 25, 2018 03:05
@dsouzai dsouzai force-pushed the AOTImprovementsPart2 branch 4 times, most recently from 983ab5b to 44c3f5b Compare September 28, 2018 14:49
@dsouzai dsouzai changed the title WIP: Improvements for Relocatable Compilations Part 2 Improvements for Relocatable Compilations Sep 28, 2018
@dsouzai dsouzai force-pushed the AOTImprovementsPart2 branch from 44c3f5b to 665effa Compare September 28, 2018 15:54
TR::VPNonNullObject::create(this), NULL, NULL,
TR::VPObjectLocation::create(this, TR::VPObjectLocation::JavaLangClassObject)),
classChildGlobal);
if (knot)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change does not match the commit message. This seems to be a change to check for the existence of a known object table whereas the commit message is about counts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ugh, I guess I must've missed this when squashing changes. I'll fix it soon.

@@ -8524,6 +8541,12 @@ TR_J9SharedCacheVM::isClassVisible(TR_OpaqueClassBlock * sourceClass, TR_OpaqueC
TR_J9VMBase::isClassVisible(sourceClass, destClass);
}

bool
Copy link
Contributor

@vijaysun-omr vijaysun-omr Sep 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some inherent reason this optimization to skip frames cannot be done under AOT or is it just something you are not handling at this point in time ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh you're viewing this commit by commit; in 5e6be87f7e9f4847742cbb2ebc8b8442b669caa4 (Add AOT Validations) is where I actually add the necessary validations to make sure this works under AOT

if (guard->getSymbolReference()->getSymbol()->getMethodSymbol()->isVirtual())
flags = inlinedMethodIsVirtual;
}
// Setup flags field with type of method that needs to be validated at relocation time
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this get impacted in any way by nest mates in Java11 ? i.e. can we get the "type of method" reliably the way we are getting it here when that feature is in play ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdmpapin do you know?

Copy link
Contributor

@jdmpapin jdmpapin Sep 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The symbol on a call generated for a private invokevirtual or invokeinterface claims to be special, although the constant pool entry isn't a J9RAMSpecialMethodRef. If we think that we had an invokespecial and interpret the constant pool entry accordingly, it might happen to work for private invokevirtual, since its entry is the same shape, but if it does we probably shouldn't rely on it. It definitely won't work for private invokeinterface.

However, the only use of inlinedMethodIsSpecial that I can spot is just preventing TR_RelocationRecordInlinedMethod::validateClassesSame() from checking whether virtualMethodIsOverridden(). Is it used anywhere else?

While we're on the topic of nestmates, IIRC code gen has been changed to stop forcing unresolved dispatch for direct calls, which I think should work for calls to nestmates' private methods that have not been inlined.

TR_OpaqueClassBlock *thisClass = guard->getThisClass();
uint16_t methodID = symValManager->getIDFromSymbol(static_cast<void *>(method));
uint16_t receiverClassID = symValManager->getIDFromSymbol(static_cast<void *>(thisClass));

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just checking why 16 bits for the ID is enough for every compiled method (with inlining) ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These IDs are per compilation; ID=1 for the compilation of method A.foo() is not going to match ID=1 for the compilation of method B.bar(). Thus, @jdmpapin and I figure that 64k unique symbols should be sufficient for any compilation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

64k should be enough (distinct symbols) for any one (AOT compilation) 😅

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay the reason I asked was that it seemed like an ID gets assigned every time we compile time query something that is of relevance from a validation perspective. While the number of distinct symbols in a compilation may be well under 64k, it seemed to me that the number of IDs were more dependent on how many times we queried and I was wondering if in a large method with many passes of different optimizations we might get close to 64k queries. Is there some simple safety valve possible anyway to detect and fail the AOT compilation if the ID goes above 64k ?

Copy link
Contributor Author

@dsouzai dsouzai Sep 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While the number of distinct symbols in a compilation may be well under 64k, it seemed to me that the number of IDs were more dependent on how many times we queried

That's not true, if we queried for class B from the constant pool of class C at cpIndex 5, then regardless of how many times times we ran that query, once a class has been assigned an ID, it is unique to that ID; there is a 1-1 mapping between an ID and a J9Class. Additionally, there is only record per unique query made. So, as in the example above, even if the query was made 10 times, there would only be one validation record stored, since we only need to check once whether class B can be retrieved from the constant pool of class C at cpIndex 5. However, if we retrieved class B from the constant pool of class C at cpIndices 3, 4, and 5, then we'd need three validation records. but the validation records would look something like this (assuming class B <--> ID 2 and class C <--> ID 1):

ClassFromCP,ID=2,BeholderID=1,cpIndex=3
ClassFromCP,ID=2,BeholderID=1,cpIndex=4
ClassFromCP,ID=2,BeholderID=1,cpIndex=5

All that said, I do have a check to ensure that we don't overflow the IDs just in case: 9a11e2f#diff-3e7f7c3bf56a075860c3736280f74b05R108

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay thanks for the clarification and the check for overflow.

if (comp->getOption(TR_UseSymbolValidationManager))
{
TR_OpaqueClassBlock *clazz = (TR_OpaqueClassBlock *)recordInfo->data5;
uintptrj_t classID = (uintptrj_t)symValManager->getIDFromSymbol(static_cast<void *>(clazz));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this ID comparable in any way to the 16 bit IDs I asked about earlier ? Now we are using uintptrj_t which is why I wanted to check.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is one of those pieces of code that @mstoodle is going to very much dislike. With the symbol validation manager, aot relocations no longer need the cpIndex to find the symbol to validate. However, in order to reuse existing relocation records and the validation work that they already do (and not duplicate code), the new aot infrastructure uses the field that was traditionally used to store the cpIndex to instead store the ID for the symbol. Hence, even though the ID is only 16 bits, it gets stored in a uintptrj_t field. (see https://github.com/eclipse/openj9/pull/2919/files/151e307854fc2ae62b7d3a1a399da9c05644589b..f9c180ad226c0aee6d98e8356979586ea9b9ed61#diff-3f1aba0052032c44a8f47c99b97e30e2R263 for an even more (unfortunately) egregious example of this).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah....yuck confirmed...

@vijaysun-omr
Copy link
Contributor

Question about something you wrote in one of the commit messages:

For example, if the method being compiled is A.foo(), then before the
compilation begins, the compiler creates:

1. Root Class Record for A
2. Method From Class Record for foo()

This will result in A getting ID 1, and foo() getting ID 2. As the
compilation continues, if the compiler gets class B in two different
ways, namely once by name and once via CP, then two other records get
created:

3. Class by Name for B
4. Class from CP for B

What happens if we get class B by name again in some other spot in the compilation ? Will we create a separate record for that or are we going to detect that it is a record/query we have seen before i.e is the 3rd record above going to be the only one stored in that case ?

@@ -449,6 +475,8 @@ TR::SymbolValidationManager::addClassFromITableIndexCPRecord(TR_OpaqueClassBlock
{
if (!clazz)
return false;
if (inHeuristicRegion())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to check for heuristic region here ? I thought you were suggesting that we only specify heuristic regions in parts of the code where we are looking at classes in some controlled way, e.g.iterate over subclasses when looking up a single implementor.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought you were suggesting that we only specify heuristic regions in parts of the code where we are looking at classes in some controlled way, e.g.iterate over subclasses when looking up a single implementor.

Yes you're right. However, for the sake of consistency, all validation records check whether a heuristic region is active. it is also a proactive means of ensuring that future code that guards other heuristic regions don't have to worry about the kind of validations that might fail within that region since the manager guarantees that all validation records will pass within the region.

@dsouzai
Copy link
Contributor Author

dsouzai commented Sep 28, 2018

What happens if we get class B by name again in some other spot in the compilation ? Will we create a separate record for that or are we going to detect that it is a record/query we have seen before i.e is the 3rd record above going to be the only one stored in that case ?

Yes, we would create another record associated with that compilation. The reason is because if we acquired B by name via the parameter list of the method being compiled A.foo(LB;), and then in a different compilation, acquired B also by name via the parameter of the method being compiled C.bar(LB;), then in the load run, if A and C are loaded by different class loaders, it is possible for A and C not not agree on B. Therefore, even if B as seen by A and C have the same ROMClass and the same class chains, there could be two different J9Classs - one to describe B as seen by A, and the other to describe B as seen by C.

Having this seemingly redundant record to describe B by name is to guard against the case where A and C agree on B in the compile run, but disagree on B in the load run.

That said, if we were looking up bootstrap classes by name, then the records are definitely redundant because they are never ambiguous.

@jdmpapin anything to add/correct?

Irwin D'Souza added 14 commits October 8, 2018 16:59
The Symbol Validation Manager is used to remember all the different ways
the compiler acquired a symbol. However, there are regions where this
information isn't necessary for the load run. For example, when asking
for something from the Persistent CH Table, all that matters is the
input to the query and the output; however, the query involves going
through multiple classes before finally returning the answer. If the
compiler were to store valiations for everything that occurs during this
query, it would require consistency between the compile and load run for
more than what is only required for the compilation.

Therefore, this commit adds the means to denote regions that are not
strictly necessary to validate for the load run.

Signed-off-by: Irwin D'Souza <[email protected]>
Some front end queries do not need to add a new validation record under
AOT using the symbol validation manager. However, it does need to verify
that the new symbol has been validated.  This query is used to
encapsulate this logic.

Signed-off-by: Irwin D'Souza <[email protected]>
This commit adds uses of the new Symbol Validation Manager infrastructure
to do AOT validations (under an option). It also introduces the
following two changes:

1. Use resolved interpreter dispatch for AOT

Previously, because almost everything was "unresolved" under AOT, all
calls would use the unresolved dispatch. However, with the new
validations, very few unresolved pointers are only unresolved because of
AOT. Therefore, generating an unresolved dispatch is incorrect.

The solution to this problem is to assume, during an AOT compile, that
all methods are interpreted. Thus, if a method is resolved, the compiler
will generate a resolved interpreter dispatch snippet, which can patch
the caller if/when the method is compiled.

2. Fix ObjectAllocationInlining under AOT

Previously, Object Allocation Inlining under AOT maintained certain
assumtpions about the trees that was only true if the class was
unresolved. This commit fixes those assumptions.

Signed-off-by: Irwin D'Souza
Signed-off-by: Irwin D'Souza <[email protected]>
Signed-off-by: Irwin D'Souza <[email protected]>
Add query for when the compiler asks about whether a method is
interpreted for heuristic reasons, and not for functional correctness
reasons.

Signed-off-by: Irwin D'Souzai <[email protected]>
Signed-off-by: Irwin D'Souza <[email protected]>
Disable the symbol validation manager during startup or if the method is
compiled at cold.

Signed-off-by: Irwin D'Souza <[email protected]>
Replace TR_ASSERT_FATAL with TR_ASSERT and abort the compilation if
there are any inconsistencies with the Symbol Validation Manager.

Signed-off-by: Irwin D'Souza <[email protected]>
Signed-off-by: Irwin D'Souza <[email protected]>
@dsouzai dsouzai force-pushed the AOTImprovementsPart2 branch from e80660f to 3eaaad0 Compare October 8, 2018 21:26
@dsouzai
Copy link
Contributor Author

dsouzai commented Oct 8, 2018

@mpirvu Updated the changes; hopefully there are no more linter issues...

@mpirvu
Copy link
Contributor

mpirvu commented Oct 8, 2018

jenkins test sanity all jdk8

@mpirvu
Copy link
Contributor

mpirvu commented Oct 9, 2018

All checks have passed, so merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
comp:doc comp:jit depends:omr Pull request is dependent on a corresponding change in OMR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants