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

Fold VarHandle held in static final fields with fear #2885

Merged
merged 2 commits into from
Jan 11, 2019

Conversation

liqunl
Copy link
Contributor

@liqunl liqunl commented Sep 17, 2018

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

@liqunl liqunl force-pushed the folding branch 2 times, most recently from 6e15125 to e5941b3 Compare September 17, 2018 16:03
@liqunl liqunl changed the title WIP: Fold static final fields with OSR guards WIP: Fold static final fields with fear Dec 7, 2018
@liqunl
Copy link
Contributor Author

liqunl commented Jan 2, 2019

@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());
Copy link
Contributor

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)
Copy link
Contributor

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.

Copy link
Contributor Author

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);
Copy link
Contributor

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.

Copy link
Contributor Author

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());
Copy link
Contributor

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.

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 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) ||
Copy link
Contributor

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.

Copy link
Contributor Author

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)
Copy link
Contributor

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?

Copy link
Contributor Author

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"
Copy link
Contributor

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");
Copy link
Contributor

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.

Copy link
Contributor Author

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)
Copy link
Contributor

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.

Copy link
Contributor Author

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))
Copy link
Contributor

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...

Copy link
Contributor Author

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);
Copy link
Contributor

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

@liqunl liqunl force-pushed the folding branch 5 times, most recently from e04d659 to d3b1c91 Compare January 4, 2019 20:06
@@ -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,
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 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.

Copy link
Contributor Author

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,
Copy link
Contributor

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()
Copy link
Contributor

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);
Copy link
Contributor

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

@liqunl liqunl force-pushed the folding branch 2 times, most recently from fd32f17 to 40085bf Compare January 7, 2019 16:18

bool isOSRRelatedBlock(TR::Block *block)
{
return block->isOSRCatchBlock() || block->isOSRCodeBlock() || containsPrepareForOSR(block);
Copy link
Contributor

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?

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 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; }
Copy link
Contributor

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?

Copy link
Contributor Author

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 ?
Copy link
Contributor

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.

@liqunl
Copy link
Contributor Author

liqunl commented Jan 8, 2019

@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,
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 need something to check about if the OSR infrastructure has been removed already since this operation doesn't make sense in that world right?

Copy link
Contributor Author

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()
Copy link
Contributor

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.

@andrewcraik
Copy link
Contributor

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.

@andrewcraik
Copy link
Contributor

Jenkins test sanity xlinux,win,plinux jdk8,jdk11

@liqunl liqunl changed the title WIP: Fold static final fields with fear WIP: Fold VarHandle held in static final fields with fear Jan 9, 2019
@liqunl liqunl changed the title WIP: Fold VarHandle held in static final fields with fear Fold VarHandle held in static final fields with fear Jan 9, 2019

if (!comp()->supportsInduceOSR()
|| !comp()->isOSRTransitionTarget(TR::postExecutionOSR)
|| !comp()->getOSRMode() == TR::voluntaryOSR)
Copy link
Contributor

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();
Copy link
Contributor

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

Copy link
Contributor Author

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())
Copy link
Contributor

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;
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

@jdmpapin jdmpapin Jan 10, 2019

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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());
Copy link
Contributor

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?

@jdmpapin
Copy link
Contributor

I suppose VP catches these things before inlining as part of methodHandleInvokeInliningGroup?

@liqunl
Copy link
Contributor Author

liqunl commented Jan 10, 2019

I suppose VP catches these things before inlining as part of methodHandleInvokeInliningGroup?

Yes.

@liqunl
Copy link
Contributor Author

liqunl commented Jan 10, 2019

@jdmpapin Made some changes in a separate commit, will squash it before merge. Please review again.

Liqun Liu added 2 commits January 10, 2019 16:12
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>
@andrewcraik
Copy link
Contributor

Jenkins test sanity xlinux,win,plinux jdk8,jdk11

Copy link
Contributor

@andrewcraik andrewcraik left a comment

Choose a reason for hiding this comment

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

LGTM

@andrewcraik andrewcraik merged commit ffbffce into eclipse-openj9:master Jan 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants