-
Notifications
You must be signed in to change notification settings - Fork 10.8k
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
[MachinePipeliner] Fix store-store dependences (#72508) #72575
Conversation
The pipeliner needs to mark store-store order dependences as loop carried dependences. Otherwise, the stores may be scheduled further apart than the MII. The order dependences implies that the first instance of the dependent store is scheduled before the second instance of the source store instruction.
// Only chain dependences between a load and store can be loop carried. | ||
// Dependences between stores are loop carried to ensure that the dependent | ||
// store is not scheduled after the source store on the next iteration. | ||
if (Dep.isNormalMemory() && DI->mayStore() && SI->mayStore()) |
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 two stores do not alias, Might there be a dependency of Order/Output between them?
If the answer is yes, then maybe we are too conservative.
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.
The isNormalMemory function checks MayAliasMem or MustAliasMem. Is a different check more precise?
bool isNormalMemory() const {
return getKind() == Order && (Contents.OrdKind == MayAliasMem
|| Contents.OrdKind == MustAliasMem);
}
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.
Seems enough for me, thanks
I don't fully understand, but it appears that adding edges for loop carried dependencies must be done in
The second store is scheduled illegally after the first store in the next iteration:
|
Hi @ytmukai, thanks for providing an example. The case you provide is an interesting one, but an additional case to consider. The original case already has an order dependence, so we need to make sure that the second store isn't scheduled too far way from the first (such that the overlap when the final pipelined scheduled is formed). I also don't fully understand why the order dependence is needed since I would have thought that the first store is dead. I just assumed it's a valid case and it made sense to make sure that the stores aren't scheduled more than MII cycles apart. |
I believe that a dependence recognized by There should be cases where there are loop independent dependencies but no loop carried ones. In those cases, this modification would be excessive. Here's an example of each case.
for (int i=0; i<n; i++) {
/* S0 */ a[i] = 1;
/* S1 */ int tmp = a[x]; // To prevent optimization
/* S2 */ a[i] = tmp;
} There is a dependence from S0 to S2 but not from S2 to S0. Therefore, S2 can be scheduled after S0 at the next iteration. This modification may prevent it.
for (int i=0; i<n; i++) {
/* S0 */ a[i] = 1;
/* S1 */ int tmp = a[x]; // To prevent optimization
/* S2 */ a[i+1] = tmp;
} There is a dependence from S2 to S0 but not from S0 to S2. Therefore, S2 must be scheduled before S0 at the next iteration. The dependence is not recognized by
for (int i=0; i<n; i++) {
// a and idx0/idx1 are disjoint
/* S0 */ a[idx0[i]] = 1;
/* S1 */ a[idx1[i]] = 2;
} There is a loop independent dependence from S0 to S1 and a loop carried dependence from S1 to S0. (The loop carried dependence from S0 to S1 need not be considered since there is an loop independent one.) The former is recognized by |
The first case is most similar to the one in the bug report (though, without the load), #72508
Perhaps the bug report is missing some details about why the second store cannot be scheduled in a later stage. Edit: a comment left #72508 shows that the ticket is for the third case - a loop independent dependence that needs a loop carried dependence to be added. |
Previously we handled the case when there is a store-load loop independence dependence. This change handles all memory-memory dependences. Unless it can be proven otherwise, the function assumes that the dependence is loop carried.
I updated the code to treat stores like loads in isLoopCarriedDep. This handles the case when there is a loop independent dependence between two memory operations. The function conservatively assumes there is a loop carried dependence unless it can be proven otherwise. |
Thanks for the updates. Unfortunately,
Do you have any thoughts on how it should be modified? I think the following two modifications are needed.
|
That's a good question. I think the near term solution is to change addLoopCarriedDependences as you describe to fix the correctness issue. Longer term is to create a graph with the dependences. I think that type of change will take some non-trivial effort. |
This LGTM and fixes the original issue I had in #72508 |
Hi @ytmukai, are you ok with merging this. Your previous comment raises a concern about getIncrementValue(). I think the two changes are independent - this change is for a correctness issue and the implementation of getIncrementValue is to improve performance. Thoughts? |
@bcahoon Sorry for the late reply. I agree with your descriptions. |
Hi @bcahoon, my colleague @kasuga-fj has posted a proposal about this issue below. Could you please give us your opinion? |
The pipeliner needs to mark store-store order dependences as
loop carried dependences. Otherwise, the stores may be scheduled
further apart than the MII. The order dependences implies that
the first instance of the dependent store is scheduled before the
second instance of the source store instruction.