Skip to content

JIT: remove single-def restriction from escape analysis type refinement #113808

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

Conversation

AndyAyersMS
Copy link
Member

Instead, model all assignments to unescaped locals in the connection graph, and deduce the new type via graph analysis.

Update the INDEX_ADDR expansion to handle cases where the array is stack allocated (interior pointers are now TYP_I_IMPL, not TYP_BYREF).

Instead, model all assignments to unescaped locals in the connection graph,
and deduce the new type via graph analysis.

Update the INDEX_ADDR expansion to handle cases where the array is stack
allocated (interior pointers are now TYP_I_IMPL, not TYP_BYREF).
@Copilot Copilot AI review requested due to automatic review settings March 23, 2025 17:51
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot wasn't able to review any files in this pull request.

Files not reviewed (5)
  • src/coreclr/jit/gentree.cpp: Language not supported
  • src/coreclr/jit/gentree.h: Language not supported
  • src/coreclr/jit/morph.cpp: Language not supported
  • src/coreclr/jit/objectalloc.cpp: Language not supported
  • src/coreclr/jit/objectalloc.h: Language not supported

@ghost ghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Mar 23, 2025
@AndyAyersMS
Copy link
Member Author

AndyAyersMS commented Mar 23, 2025

@dotnet/jit-contrib PTAL

This is mainly an enabling change for field-wise escape analysis but has some diffs of its own.

Code diffs best viewed with whitespace suppressed, as I refactored a large method and indentation levels changed.

LclVarDsc* lclVarDsc = comp->lvaGetDesc(lclNum);

if ((lclVarDsc->lvSingleDef == 1) && !comp->opts.IsOSR())
if (!comp->opts.IsOSR())
Copy link
Member Author

Choose a reason for hiding this comment

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

This is the heart of the change; the rest is there to ensure the deduction we make here is sound.

We now need to know "everything" that can be assigned to each local.

Comment on lines +625 to +650
if (tree->OperIsLocalStore())
{
GenTreeLclVarCommon* const lclTree = tree->AsLclVarCommon();
unsigned const lclNum = lclTree->GetLclNum();
if (m_allocator->IsTrackedLocal(lclNum) && !m_allocator->CanLclVarEscape(lclNum))
{
if ((lclTree->gtFlags & GTF_VAR_CONNECTED) == 0)
{
// This store was not modelled in the connection graph.
//
// If the stored value was was not a stack-viable allocation or null,
// add an edge to unknown source. This will ensure this local does
// not get retyped as TYP_I_IMPL.
//
GenTree* const data = lclTree->Data();
if (!data->IsIntegralConst(0) && (m_allocator->AllocationKind(data) == OAT_NONE))
{
// Add a connection to the unknown source.
//
JITDUMP("V%02u value unknown at [%06u]\n", lclNum, m_compiler->dspTreeID(tree));
m_allocator->AddConnGraphEdge(lclNum, m_allocator->m_unknownSourceLocalNum);
}
}
}
tree->gtFlags &= ~GTF_VAR_CONNECTED;
}
Copy link
Member

Choose a reason for hiding this comment

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

Just curious -- why is some handling needed here, and some handling in the other place? Why isn't all this handling done from the same walk to avoid carrying this state between them?

Copy link
Member Author

Choose a reason for hiding this comment

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

In preorder, we walk each tree from any lcl var leaf up towards the root, but we don't always reach the root if we can see that the particular leaf escapes or doesn't escape at some intermediate node.

So at store, we don't know if we have built a model for what gets stored until we've finished visiting all the descendant nodes.

Comment on lines 672 to 674
// Parameters have unknown initial values.
//
if (lclDsc->lvIsParam)
Copy link
Member

Choose a reason for hiding this comment

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

Do OSR locals need modelling here?

Copy link
Member Author

Choose a reason for hiding this comment

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

We currently will only retype to TYP_BYREF in OSR methods, so we're excluding the OSR locals (and more) during the analysis.

We could model things more accurately now by giving each OSR local an unknown correction and removing the broader analysis exclusion. I'll push an update for this.

Comment on lines 826 to 827
const unsigned rhsLclNum = IndexToLocal(rhsLclIndex);
isStackPointing = DoesLclVarPointToStack(rhsLclNum);
Copy link
Member

Choose a reason for hiding this comment

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

It seems like we do a lot of work to translate back and forth between "locals" and "indices", which feels a little bit confusing and unnecessary to me. Particularly the fact that "pseudo locals" also are considered "locals" and not just another type of abstract thing that has an index. Do they need to have "local" status?

Copy link
Member

@jakobbotsch jakobbotsch Mar 24, 2025

Choose a reason for hiding this comment

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

Can the entire loop here be done with BitVecOps::IsSubset(m_ConnGraphAdjacencyMatrix[lclIndex], m_definitelyStackPointingPointers)?

Copy link
Member Author

@AndyAyersMS AndyAyersMS Mar 24, 2025

Choose a reason for hiding this comment

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

Yeah, seems like we can avoid looping.

As for lclnum/index shuffling: I could possibly represent the abstract things with only indices and introduce a second set of index-based helpers to avoid some of the back and forth, but I'm not sure that would be an improvement.

Compiler::fgWalkResult PostOrderVisit(GenTree** use, GenTree* user)
{
GenTree* const tree = *use;
if (tree->OperIsLocalStore())
Copy link
Member

Choose a reason for hiding this comment

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

Why is this part done in post order?

Copy link
Member Author

Choose a reason for hiding this comment

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

Same answer as above: #113808 (comment)

We need to know that all the descendant lcl var nodes have already tried to trace their paths up to this node.

Copy link
Member

Choose a reason for hiding this comment

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

Ah ok, CanLclVarEscapeViaParentStack is called from this visitor in the preorder... Now things make more sense to me.

Copy link
Member Author

Choose a reason for hiding this comment

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

I suppose I could move the current preorder processing to postorder too... will leave as-is for now.

Copy link
Member

@jakobbotsch jakobbotsch Mar 25, 2025

Choose a reason for hiding this comment

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

Yeah... Or even more radically this visitor could work similarly to local morph, tracking semantic information about operands on its own stack and escaping them from the parent instead of the child.

Copy link
Member

@jakobbotsch jakobbotsch left a comment

Choose a reason for hiding this comment

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

LGTM with a question

@AndyAyersMS
Copy link
Member Author

Latest diffs

@AndyAyersMS
Copy link
Member Author

/ba-g Failures are azurelinux timeouts

@AndyAyersMS AndyAyersMS merged commit ac5f930 into dotnet:main Mar 25, 2025
106 of 108 checks passed
@github-actions github-actions bot locked and limited conversation to collaborators Apr 25, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants