-
Notifications
You must be signed in to change notification settings - Fork 10.5k
EscapeAnalysis: make the use-point analysis more precise #27667
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -376,6 +376,27 @@ updatePointsTo(CGNode *InitialNode, CGNode *pointsTo) { | |
clearWorkListFlags(WorkList); | ||
} | ||
|
||
int EscapeAnalysis::ConnectionGraph::addUsePoint(CGNode *Node, SILNode *User) { | ||
// For escaping nodes we have to make worst case assumptions anyway. So no | ||
// need to store use point information. | ||
if (Node->getEscapeState() >= EscapeState::Global && | ||
// ... except it has defer predecessor edges, which are (potentially) not | ||
// escaping: use-points are propagated in reverse direction along defer | ||
// edges! | ||
!Node->hasDeferPredecessors()) { | ||
return -1; | ||
} | ||
|
||
User = User->getRepresentativeSILNodeInObject(); | ||
int Idx = (int)UsePoints.size(); | ||
assert(UsePoints.count(User) == 0 && "value is already a use-point"); | ||
UsePoints[User] = Idx; | ||
UsePointTable.push_back(User); | ||
assert(UsePoints.size() == UsePointTable.size()); | ||
Node->setUsePointBit(Idx); | ||
return Idx; | ||
} | ||
|
||
void EscapeAnalysis::ConnectionGraph::propagateEscapeStates() { | ||
bool Changed = false; | ||
do { | ||
|
@@ -449,6 +470,10 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() { | |
for (CGNode *Def : Node->defersTo) { | ||
Changed |= Def->mergeUsePoints(Node); | ||
} | ||
for (Predecessor Pred : Node->Preds) { | ||
if (Pred.getInt() == EdgeType::Defer) | ||
Changed |= Pred.getPointer()->mergeUsePoints(Node); | ||
} | ||
atrick marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} while (Changed); | ||
} | ||
|
@@ -1858,23 +1883,28 @@ bool EscapeAnalysis::canEscapeToUsePoint(SILValue V, SILNode *UsePoint, | |
if (ConGraph->isUsePoint(UsePoint, Node)) | ||
return true; | ||
|
||
assert(isPointer(V) && "should not have a node for a non-pointer"); | ||
|
||
// Check if the object "content" can escape to the called function. | ||
// This will catch cases where V is a reference and a pointer to a stored | ||
// property escapes. | ||
// It's also important in case of a pointer assignment, e.g. | ||
// V = V1 | ||
// apply(V1) | ||
// In this case the apply is only a use-point for V1 and V1's content node. | ||
// As V1's content node is the same as V's content node, we also make the | ||
// check for the content node. | ||
CGNode *ContentNode = ConGraph->getContentNode(Node); | ||
if (ContentNode->escapesInsideFunction(false)) | ||
return true; | ||
if (V->getType().hasReferenceSemantics()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess this check is fine for now. I don't think it's robust to assume here that all other pointsTo edges represent physical indirection (which is assumed by the caller of this API) when that fact isn't represented anywhere in the graph. That assumes something about how the graph is constructed and assumes that nothing unforeseen has happened to the graph in the mean time. In debugging escape graphs, I've noticed many scenarios that I never foresaw. Very soon I can commit a change that marks nodes that start a heap object. I'm not sure that will be sufficient to handle whatever case you need optimized. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, checking the new flag what you'll add, will be better and more robust. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The basic problem with this API is that the client has some specification for an "object" that doesn't match any concept in the ConnectionGraph. This is one of the key issues that I was addressing on my working branch before I put it on hold to work on bugs and benchmarks. Can you tell me what test broke with this change so I can see if I'm also breaking it? I wonder if it would be sufficient for you to simply do this check instead: When checking for a reference type. I'm afraid that getUnderlyingObject might look through an address to reference cast. |
||
// In case V is the result of getUnderlyingObject, we also have to check | ||
// the content node. Example: | ||
// | ||
// %a = ref_element %r | ||
// apply %f(%a) | ||
// | ||
// The reference %r does not actually escape to the function call. But in | ||
// case this API is called like | ||
// | ||
// canEscapeToUsePoint(getUnderlyingObject(applyArgument)) | ||
// | ||
// it would yield false, although the projected object address is passed to | ||
// the function. | ||
atrick marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
CGNode *ContentNode = ConGraph->getContentNode(Node); | ||
if (ContentNode->escapesInsideFunction(false)) | ||
return true; | ||
|
||
if (ConGraph->isUsePoint(UsePoint, ContentNode)) | ||
return true; | ||
if (ConGraph->isUsePoint(UsePoint, ContentNode)) | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
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 appears to be fixing an existing correctness issue. Is that right?
Uh oh!
There was an error while loading. Please reload this page.
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, There was not correctness issue.
There is (better: was) no point in adding use-points for escaping nodes because escaping nodes would cause to bail anyway. Only now, when propagating in reverse direction, we have to consider this, because a predecessor might not be escaping even if the node itself is escaping.
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 see what you're saying. But if the direction of the defer edge matters, then I think phi arguments needed defer edges in both directions. Or, we just say that the direction doesn't matter and propagate both escape states and use points in both directions.
If the direction does not matter, then we could probably just merge the nodes in the defer web.