-
Notifications
You must be signed in to change notification settings - Fork 738
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
Fold VarHandle held in static final fields with fear #2885
Conversation
6e15125
to
e5941b3
Compare
@andrewcraik Can I have a review? Will write the PR description later. |
{ | ||
TR_ASSERT(start->getEnclosingBlock() == end->getEnclosingBlock(), "Does not support range across blocks"); | ||
|
||
traceMsg(comp, "prohibit final field folding over n%dn - n%dn \n", start->getNode()->getGlobalIndex(), end->getNode()->getGlobalIndex()); |
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.
this should be dumpOptDetails
|
||
if (comp->getOption(TR_EnableOSR) && | ||
comp->isOSRTransitionTarget(TR::postExecutionOSR) && | ||
comp->getOSRMode() == TR::voluntaryOSR) |
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.
prohibition only works in certain modes - we should probably FATAL_ASSERT if it isn't supported or have some kind of return code. We shouldn't just silently fail.
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.
Added an assertion and StringPeepHoles now checks the mode before prohibition.
{ | ||
prohibitGuardedStaticFinalFieldFoldingOnNodeAndChildren(comp, tt->getNode(), &visited); | ||
tt = tt->getNextTreeTop(); | ||
} while (tt != ttAfterEnd); |
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.
code style - while on next line.
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.
Fixed.
node->getOpCode().isLoadVarDirect() && | ||
node->isLoadOfStaticFinalField()) | ||
{ | ||
traceMsg(comp, "prohibit folding on n%dn\n", node->getGlobalIndex()); |
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.
There should be a some kind of trace flag or something so this doesn't fill the log up.
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.
This is where we set the flag, so I updated the code to use dumpOptDetails here.
static bool skipFinalFieldFoldingInBlock(TR::Block* block) | ||
{ | ||
if (block->isCold() || | ||
TR_FearPointAnalysis::shouldSkipBlock(block) || |
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'm not a fan of this coupling - it isn't obvious why FearPointAnalysis should be connected to VP. The common concept probably belongs in its own header or something with a more sensible name.
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.
Renamed the function to isOSRRelatedBlock and move it to OSRUtils.hpp.
@@ -1175,3 +1176,163 @@ J9::ValuePropagation::getParmValues() | |||
|
|||
TR_ASSERT(parmIterator->atEnd() && parmIndex == numParms, "Bad signature for owning method"); | |||
} | |||
|
|||
static bool isTakenSideOfAVirtualGuard(TR::Block* block) |
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.
This seems misleading - what about an OSR guard or a virtual guard which is implemented using OSR... Why not just check if the block's predecessors to see if the end in a branch that is a virtual guard?
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.
Changed to check the last tree of the block's predecessor.
@@ -43,6 +43,7 @@ | |||
#include "env/VMAccessCriticalSection.hpp" // for VMAccessCriticalSection | |||
#include "runtime/RuntimeAssumptions.hpp" | |||
#include "env/J9JitMemory.hpp" | |||
#include "optimizer/FearPointAnalysis.hpp" |
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.
really not a fan of this - see below we need a more sensible header to hold the common query if there isn't already a suitable OSR header.
@@ -1400,7 +1400,7 @@ J9::SymbolReferenceTable::findOrCreateStaticSymbol(TR::ResolvedMethodSymbol * ow | |||
TR_OpaqueClassBlock *declaringClass = owningMethod->getDeclaringClassFromFieldOrStatic(comp(), cpIndex); | |||
if (declaringClass && fej9->isClassInitialized(declaringClass)) | |||
{ | |||
static const char *dontFoldVarHandle = feGetEnv("TR_DontFoldVarHandle"); | |||
static const char *foldVarHandleWithoutGuard = feGetEnv("TR_FoldVarHandleWithoutGuard"); |
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 would prefer we delivered the code and then flipped the sense of this so we only deal with one thing at a time to make narrowing things down easier.
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.
Change in this file will be in a different PR.
if (fieldNode->getByteCodeInfo().doNotProfile() || | ||
skipFinalFieldFoldingInBlock(tree->getEnclosingBlock()) || | ||
!safeToAddFearPointIn(comp(), fieldNode->getByteCodeInfo().getCallerIndex()) || | ||
TR::TransformUtil::canFoldStaticFinalField(comp(), fieldNode) != TR_maybe) |
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 think this would read better with the ||s a the start of the lines to make the condition relationships more obvious since this is quite complicated.
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.
Fixed.
|
||
if (isStaticFinalFieldWorthFolding(comp(), declaringClass, fieldSignature, fieldSigLength)) | ||
{ | ||
if (TR::TransformUtil::foldStaticFinalFieldAssumingProtection(comp(), fieldNode)) |
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.
A perform transformation would seem to be a good idea here...
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.
foldStaticFinalFieldAssumingProtection
does a perform transformation.
ttNode->getFirstChild()->isOSRFearPointHelperCall()) | ||
{ | ||
if (trace()) | ||
traceMsg(comp(), "Remove osrFearPointHelper call n%dn %p\n", ttNode->getGlobalIndex(), ttNode); |
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.
probably should be a dumpOptDetails
e04d659
to
d3b1c91
Compare
@@ -1916,30 +1916,29 @@ J9::TransformUtil::createDiamondForCall(TR::Optimization* opt, TR::TreeTop *call | |||
void J9::TransformUtil::removePotentialOSRPointHelperCalls(TR::Compilation* comp, TR::TreeTop* start, TR::TreeTop* end) | |||
{ | |||
TR_ASSERT(start->getEnclosingBlock() == end->getEnclosingBlock(), "Does not support range across blocks"); | |||
TR_ASSERT(comp->supportsInduceOSR() && comp->isOSRTransitionTarget(TR::postExecutionOSR) && comp->getOSRMode() == TR::voluntaryOSR, |
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.
do we also need to check that the OSR infrastructure hasn't been removed? it doesn't make sense to do this after that point I don't think since we have made a lot of complex decisions.
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.
comp->supportsInduceOSR()
does the check.
void J9::TransformUtil::prohibitGuardedStaticFinalFieldFoldingOverRange(TR::Compilation* comp, TR::TreeTop* start, TR::TreeTop* end) | ||
{ | ||
TR_ASSERT(start->getEnclosingBlock() == end->getEnclosingBlock(), "Does not support range across blocks"); | ||
TR_ASSERT(comp->supportsInduceOSR() && comp->isOSRTransitionTarget(TR::postExecutionOSR) && comp->getOSRMode() == TR::voluntaryOSR, |
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.
isn't it more correct to say we only do static final field folding in specific modes? the prohibition should work in all modes where we fold?
return false; | ||
} | ||
|
||
if (fieldNode->getByteCodeInfo().doNotProfile() |
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.
hmm - I'm not sure I like this use of doNotProfile - whether a static final field read is foldable is not related to whether the field read node came from the original program representation so this would seem an overload of the meaning of the flag likely to confuses other opts
|
||
TR::TransformUtil::removePotentialOSRPointHelperCalls(comp(), startTree, endTree); | ||
TR::TransformUtil::prohibitOSROverRange(comp(), startTree, endTree); | ||
TR::TransformUtil::prohibitGuardedStaticFinalFieldFoldingOverRange(comp(), startTree, endTree); |
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 don't think this is necessary - we can fold finals if we can protect the OSR points
fd32f17
to
40085bf
Compare
|
||
bool isOSRRelatedBlock(TR::Block *block) | ||
{ | ||
return block->isOSRCatchBlock() || block->isOSRCodeBlock() || containsPrepareForOSR(block); |
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.
what blocks contain a prepare that is not a catch or code block?
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 was just copying shouldSkipBlock
in FearAnalysis. Didn't know that prepareForOSR is in OSR code block. I thought OSR code block is the one with the induce.
@@ -313,6 +313,10 @@ class OMR_EXTENSIBLE Compilation : public OMR::CompilationConnector | |||
|
|||
TR::SymbolValidationManager *getSymbolValidationManager() { return _symbolValidationManager; } | |||
|
|||
// Flag to record if any optimization has prohibited OSR over a range of trees | |||
void setOSRProhibitedOverRangeOfTrees() { _osrProhibitedOverRangeOfTrees = true; } | |||
bool isOSRProhibitedOverRangeOfTrees() { return _osrProhibitedOverRangeOfTrees; } |
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.
perhaps hasOSRProhibitions would be a better, shorter name?
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.
hasOSRProhibitions is used on OMR method symbol to record OSR prohibition on bytecodes. I used a different name to not to cause confusion.
// Due to VirtualGuardHeadMerger, the taken side of a virtual guard may have more than one predecessors, | ||
// each containing a virtual guard that branches to the taken side. It's sufficient to look at only | ||
// one predecessor. | ||
TR::Node* predLastRealNode = block->getPredecessors().size() > 0 ? |
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.
VGHM or other opts may also chain the cold side of guards together - see VirtualGuardTailSplitter - it is not sufficient to look at only one.
@andrewcraik Can you review again? |
@@ -1961,24 +1960,24 @@ void J9::TransformUtil::removePotentialOSRPointHelperCalls(TR::Compilation* comp | |||
void J9::TransformUtil::prohibitOSROverRange(TR::Compilation* comp, TR::TreeTop* start, TR::TreeTop* end) | |||
{ | |||
TR_ASSERT(start->getEnclosingBlock() == end->getEnclosingBlock(), "Does not support range across blocks"); | |||
TR_ASSERT(comp->supportsInduceOSR() && comp->isOSRTransitionTarget(TR::postExecutionOSR) && comp->getOSRMode() == TR::voluntaryOSR, |
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.
Do we need something to check about if the OSR infrastructure has been removed already since this operation doesn't make sense in that world right?
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.
comp->supportsInduceOSR()
does the check.
} | ||
|
||
|
||
static TR_HCRGuardAnalysis* runHCRGuardAnalysisIfNecessary() |
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.
This might be better named runHCRGuardAnalysisIfPossible
since we would like it, but we don't have to do it which I think necessary suggests.
Ok so I think this new revision where VP is checking the safety of folding with an optional, but not currently supported hook for running HCRGuard analysis seems like a good design now where we don't rely on node flags and we will be conservatively correct. Given the complexity I'd appreciate a second opinion from @jdmpapin but I'm going to start sanity. If there are additional checks added to asserts please do it as a separate commit that can be squashed so we can avoid having to do a full sanity by inspecting the change prior to the squash. |
Jenkins test sanity xlinux,win,plinux jdk8,jdk11 |
|
||
if (!comp()->supportsInduceOSR() | ||
|| !comp()->isOSRTransitionTarget(TR::postExecutionOSR) | ||
|| !comp()->getOSRMode() == TR::voluntaryOSR) |
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.
Do you mean comp()->getOSRMode() != TR::voluntaryOSR
? I think this will actually work as-is at the moment, but only because of the (implicit) numeric values assigned in OSRMode
.
enum OSRMode
{
voluntaryOSR, // 0
involuntaryOSR // 1
};
So
(!comp()->getOSRMode()) == TR::voluntaryOSR
iff (!comp()->getOSRMode()) == 0
iff comp()->getOSRMode() != 0
iff comp()->getOSRMode() != TR::voluntaryOSR
tt = tt->getPrevTreeTop(); | ||
} | ||
|
||
TR_HCRGuardAnalysis* guardAnalysis = runHCRGuardAnalysisIfNecessary(); |
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 don't think I see the point of this "hook" - there's no abstraction boundary here. That said, it doesn't actively harm anything. The code checking the results is just dead for now
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.
We haven't done any compile time measured so don't know when we can afford the analysis now. It'll be updated once we done the performance measurement.
} | ||
|
||
TR_HCRGuardAnalysis* guardAnalysis = runHCRGuardAnalysisIfNecessary(); | ||
if (guardAnalysis && guardAnalysis->_blockAnalysisInfo[block->getNumber()]->isEmpty()) |
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.
It seems to me that we could get a definite answer (either TR_yes
or TR_no
) whenever there's a guardAnalysis
. Is there some reason this produces TR_maybe
instead of TR_no
? Although at the moment I suppose the result is only compared with TR_yes
so it won't make a difference.
{ | ||
traceMsg(comp(), "Not safe to add fear point because caller frame %d cannot OSR\n", callerIndex); | ||
} | ||
return TR_no; |
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 we were to insert a fear point here, the points at which OSR would be required to protect it wouldn't necessarily be in the same inlined site. Does this early return just allow for the following early return when !isOSRProhibitedOverRangeOfTrees()
, or is there some other reason it's needed?
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.
No. That's something I didn't think of when changing the code. It was needed in the old version when we never want to run HCR guard analysis. Thanks for pointing this out.
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.
Come to think of it, the !isOSRProhibitedOverRangeOfTrees()
seems to be broken even though we've checked for cannotAttemptOSRDuring()
. Suppose we can attempt OSR at the given tree, but the fear flows out from the beginning of the inlined body and into a different inlined body where we can't, and furthermore suppose that we did no transformations in string peepholes (or similar). Even without running a full "HCR guard analysis" (now a bit of a misnomer), the local analysis below would conservatively prevent folding, if it were allowed to run. But instead we'll return early with TR_yes
edit: The local analysis would only be sure to prevent folding in this case so long as we haven't combined blocks from different methods, which we might do in tree simplification during methodHandleInvokeInliningGroup
. Though for most inlined methods their blocks will be isolated within the diamond for their guards, and most methods have at least an HCR guard that can't be eliminated until OSR guard insertion runs.
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.
potentialOSRPointHelper
will prevent a fear flowing to an inlined body that cannot attempt OSR.
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.
Ah, right! Thanks, that makes sense 👍
// | ||
cleanUpPotentialOSRPointHelperCalls(); | ||
} | ||
|
||
if (trace()) | ||
{ | ||
comp()->dumpMethodTrees("Trees after redundant potentialOSRPointHelper call removal", comp()->getMethodSymbol()); |
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.
Should this tracing move along with the logic to remove potential OSR point helper calls?
I suppose VP catches these things before inlining as part of |
Yes. |
@jdmpapin Made some changes in a separate commit, will squash it before merge. Please review again. |
Recording the OSR prohibition can help determine if an OSR related optimization is safe without running HCRGuardAnalysis. Signed-off-by: Liqun Liu <liqunl@ca.ibm.com>
Fold static final fields that hold VarHandle object with OSR guard. The folding occurs in VP and when OSR infrastructure is still available. Signed-off-by: Liqun Liu <liqunl@ca.ibm.com>
Jenkins test sanity xlinux,win,plinux jdk8,jdk11 |
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
Fold static final fields that hold VarHandle object with OSR guard. The
folding occurs in VP and when OSR infrastructure is still available.
Signed-off-by: Liqun Liu liqunl@ca.ibm.com