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

Track methods with AOT bodies with dependencies #20599

Merged
merged 1 commit into from
Nov 19, 2024

Conversation

cjjdespres
Copy link
Contributor

When a method is initialized in jitHookInitializeSendTarget, the SCC will be checked to see if it has a stored AOT body that was compiled with tracked dependencies. If it does, and all of them are satisfied, the method will be given an initial count of 0. If not all of the dependencies are satisfied, the method will be tracked by the TR_DependencyTable until the dependencies are satisfied, at which point its count will be set to zero, triggering an AOT load on its next invocation.

Related: #20529

@cjjdespres cjjdespres requested a review from dsouzai as a code owner November 15, 2024 13:18
@cjjdespres
Copy link
Contributor Author

Attn @mpirvu. This is the warm run side of the dependency tracking.

@mpirvu mpirvu self-assigned this Nov 15, 2024
* entry will be the offset itself (which necessarily has a set low bit), and if the class only needs to be loaded
* it will be the offset with the low bit cleared. *
* The dependencies of an AOT method are encoded as an array of uintptr_t
* values. The first entry is the number of entries after the first, the
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The original documentation said the first entry was the total number of entries, but I actually had the dependencies set up similarly to the well-known classes chain, where the first entry is the number of subsequent entries.

if (isClassLoad)
entry->_loadedClasses.insert(ramClass);
{
checkForSatisfaction(offsetEntry->_waitingLoadMethods);
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 should only be run if there have been no previous loads for this offset

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 mean that the code needs some modification?

auto entry = getOffsetEntry(offset, isClassLoad);
TR_ASSERT(entry || !isClassLoad, "Class %p offset %lu initialized without loading");
auto offsetEntry = getOffsetEntry(offset, isClassLoad);
TR_ASSERT(offsetEntry || !isClassLoad, "Class %p offset %lu initialized without loading", ramClass, offset);
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 isn't accurate - I check that ramClass matches its cached version for loads only, so if this isn't a load, I need to check that ramClass is in offsetEntry->_loadedClasses. If it isn't, then it was intentionally not added when it was loaded.

runtime/compiler/control/HookedByTheJit.cpp Show resolved Hide resolved
runtime/compiler/control/HookedByTheJit.cpp Outdated Show resolved Hide resolved
runtime/compiler/env/DependencyTable.hpp Outdated Show resolved Hide resolved
runtime/compiler/env/DependencyTable.cpp Outdated Show resolved Hide resolved
runtime/compiler/env/DependencyTable.cpp Outdated Show resolved Hide resolved
if (isClassLoad)
entry->_loadedClasses.insert(ramClass);
{
checkForSatisfaction(offsetEntry->_waitingLoadMethods);
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 mean that the code needs some modification?

runtime/compiler/env/DependencyTable.cpp Show resolved Hide resolved
runtime/compiler/env/DependencyTable.cpp Show resolved Hide resolved
runtime/compiler/env/DependencyTable.cpp Outdated Show resolved Hide resolved
@cjjdespres cjjdespres force-pushed the dep-compilations-track branch from a7a9f91 to 9ca5d97 Compare November 18, 2024 13:34
@cjjdespres
Copy link
Contributor Author

Other than those corrections, I also added a fix in buildAOTMethodDependenciesKey() in J9SharedCache.cpp - I realized that the key length should in fact not include the NULL terminator. This matters when looking up the dependencies by key in the warm run.

Copy link
Contributor

@mpirvu mpirvu left a comment

Choose a reason for hiding this comment

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

Can the code handle the situation when two j9classes have the same ROMClass?
Say, I have an AOT method that depends on two ROMClasses and then I have two load operations of two different j9classes but with the same ROMClass, matching the dependency. Would the code wrongly decrement the number of outstanding dependencies to 0 and trigger a compilation?

}

void
TR_AOTDependencyTable::eraseOffsetEntryIfEmpty(const OffsetEntry &entry, uintptr_t offset)
Copy link
Contributor

Choose a reason for hiding this comment

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

Ideally, we would have an iterator as parameter that would capture both the entry and the offset because they come same hashtable entry. Maybe refine later.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it's not the most elegant.

@cjjdespres
Copy link
Contributor Author

For loads, I don't think so. One of the loads would trigger classLoadEvent() via the class load hook, which would keep the _tableMonitor for the duration of the operation. There would be no previous loads, so that method's _remainingDependencies would be decremented by 1. Then the other load would happen and trigger classLoadEvent(). It will see a previous load for that dependency and do nothing. I didn't think there was a gap for a race to occur.

For initializations, there might be a chance of that happening. If two threads try to initialize different classes at the same offset at the same time, the first thread will acquire the _tableMonitor while neither are marked initialized, and decrement the _remainingDependencies of the methods waiting for initialization. If the second thread updates the dependency table before the first class is marked initialized, then maybe we would accidentally decrement a second time.

I'm not sure if two class initializations can get to that state at the same time. If there isn't a convenient way of making sure the second class initialization event is triggered only after the first is marked initialized, perhaps I could transfer the initialized class into a new _initializedClasses set in the entry, or otherwise mark the class as being known to be initialized in the dependency table.

@mpirvu
Copy link
Contributor

mpirvu commented Nov 18, 2024

jenkins compile all jdk17

When a method is initialized in jitHookInitializeSendTarget, the SCC
will be checked to see if it has a stored AOT body that was compiled
with tracked dependencies. If it does, and all of them are satisfied,
the method will be given an initial count of 0. If not all of the
dependencies are satisfied, the method will be tracked by the
TR_DependencyTable until the dependencies are satisfied, at which point
its count will be set to zero, triggering an AOT load on its next
invocation.

Signed-off-by: Christian Despres <[email protected]>
@cjjdespres cjjdespres force-pushed the dep-compilations-track branch from 9ca5d97 to 8f5ae8d Compare November 19, 2024 00:38
@mpirvu
Copy link
Contributor

mpirvu commented Nov 19, 2024

jenkins compile all jdk8

@cjjdespres
Copy link
Contributor Author

The ppc64_aix build failed because methodWillBeCompiled wasn't in the PERSISTENT_COLLECTIONS_UNSUPPORTED declaration of TR_AOTDependencyTable:

16:41:09  /home/jenkins/workspace/Build_JDK17_ppc64_aix_Personal/openj9/runtime/compiler/control/CompilationThread.cpp:8260:24: error: no member named 'methodWillBeCompiled' in 'TR_AOTDependencyTable'
16:41:09        dependencyTable->methodWillBeCompiled(method);

I don't know why aarch64_linux failed. All I see is:

16:40:39  Compiling 4 files for BUILD_JIGSAW_TOOLS
16:40:57  gmake[3]: *** [/home/jenkins/workspace/Build_JDK17_aarch64_linux_Personal/build/linux-aarch64-server-release/buildtools/tools_jigsaw_classes/_the.BUILD_JIGSAW_TOOLS_batch] Error 1
16:40:57  CompileModuleTools.gmk:47: recipe for target '/home/jenkins/workspace/Build_JDK17_aarch64_linux_Personal/build/linux-aarch64-server-release/buildtools/tools_jigsaw_classes/_the.BUILD_JIGSAW_TOOLS_batch' failed
16:40:57  gmake[2]: *** [buildtools-modules] Error 1
16:40:57  make/Main.gmk:95: recipe for target 'buildtools-modules' failed
16:40:57  gmake[2]: *** Waiting for unfinished jobs....

The build was here.

@mpirvu
Copy link
Contributor

mpirvu commented Nov 19, 2024

Windows builds failed because of lack of machines:

All nodes of label ‘[ci.role.build&&hw.arch.x86&&sw.os.windows](https://openj9-jenkins.osuosl.org/label/ci.role.build&&hw.arch.x86&&sw.os.windows/)’ are offline

@mpirvu
Copy link
Contributor

mpirvu commented Nov 19, 2024

jenkins test sanity xlinux jdk23

@mpirvu mpirvu merged commit e6ae080 into eclipse-openj9:master Nov 19, 2024
12 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants