This is an archive of the discontinued LLVM Phabricator instance.

[DAGCombine] Remove the getNegatibleCost to avoid the out of sync with getNegatedExpression
ClosedPublic

Authored by steven.zhang on Apr 2 2020, 9:33 AM.

Details

Summary

We have the getNegatibleCost/getNegatedExpression to evaluate the cost and negate the expression. However, during negating the expression, the cost might change as we are changing the DAG, and then, hit the assertion if we negated the wrong expression as the cost is not trustful anymore.

We are struggling to fix such issues on D75501, D70975, D76638, D76439 etc and I believe there are more potential issues here, as we make assumption that, the cost won't change during negating the expression.

This patch is target to remove the getNegatibleCost to avoid the out of sync with getNegatedExpression, and check the cost during negating the expression. It also reduce the duplicated code between getNegatibleCost and getNegatedExpression. And fix the crash for the test in D76638

The side effect is that, we have to create some nodes which will be removed in the next iteration of DAGCombine soon. And it will change the combine worklist as we always push the operands of the unused node into worklist when it is deleted. But it shouldn't have bad impact as we are designing to not to rely on the combine order.

Diff Detail

Event Timeline

steven.zhang created this revision.Apr 2 2020, 9:33 AM
Herald added a project: Restricted Project. · View Herald TranscriptApr 2 2020, 9:33 AM
RKSimon added inline comments.Apr 2 2020, 9:57 AM
llvm/include/llvm/CodeGen/TargetLowering.h
3538

You might be better off keeping the NegatibleCost enum and using it as a threshold value instead.

Can you split whatever change affects the PPC tests into a separate patch, if it's not too tricky?

llvm/include/llvm/CodeGen/TargetLowering.h
3538

Maybe expand a little what "profitable" means.

3582

"if it has"?

steven.zhang marked an inline comment as done.Apr 2 2020, 6:31 PM

Can you split whatever change affects the PPC tests into a separate patch, if it's not too tricky?

Yes, I am trying to do it, and that is the main reason why this revision is in WIP status. It seems that we have different combine order for that case that result in different combination result, though they are semantics the same. I will take a deep look at this.

llvm/include/llvm/CodeGen/TargetLowering.h
3538

Good suggestion.

Keep the NegatibleCost and change its enum value to make it consistent with its semantics. CostX < CostY means the cost of negated X is cheaper than Y.

steven.zhang retitled this revision from [WIP] Remove the getNegatibleCost to [DAGCombine] Remove the getNegatibleCost to avoid the out of sync with getNegatedExpression.Apr 3 2020, 12:44 AM
steven.zhang edited the summary of this revision. (Show Details)
steven.zhang added reviewers: jsji, Restricted Project.

Can you split whatever change affects the PPC tests into a separate patch, if it's not too tricky?

Yes, I am trying to do it, and that is the main reason why this revision is in WIP status. It seems that we have different combine order for that case that result in different combination result, though they are semantics the same. I will take a deep look at this.

I cannot as it is tricky... We are creating some nodes during negate the expression, and these nodes will be deleted by recursivelyDeleteUnusedNodes() when we are trying another run of the combine. But their operands will be push into the worklist and keep it if their use is not empty. That means, we might change the order of the instructions to combine. For this case, the order matters the instruction selection, so, we see the changes.

steven.zhang edited the summary of this revision. (Show Details)Apr 3 2020, 12:57 AM
steven.zhang edited the summary of this revision. (Show Details)Apr 3 2020, 1:00 AM
qiucf added a subscriber: qiucf.Apr 7 2020, 1:31 AM

This patch overall looks like good refactoring, which resolves the 'inconsistency' and redundant logic between two methods.

Test changes make sense here since we have no way to tell combiner that 'these nodes are totally useless'. If we really want to keep tests unchanged, we need to add some hint to getNode or worklist handling logic.

llvm/include/llvm/CodeGen/TargetLowering.h
261

This change can be a parent commit since it's not related to this refactoring.

llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
5734

If I understand correctly, we can make sure only internal bug in this method would hit the assert? Since we shouldn't get non-null NegX but expensive CostX.

I just mean: we used to have many asserts in this function, because we need to ensure it's 'aligned' with return value of getNegatibleCost. But now we removed that function and use null SDValue to represent Expensive results, so we don't need those asserts any more.

Only asserts like here to catch internal bugs are okay.

steven.zhang marked an inline comment as done.Apr 7 2020, 2:34 AM
steven.zhang added inline comments.
llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
5734

Thank you for the review. Yes, as the getNegatedExpression could be override by target, we need to make sure that they didn't return non-null value with expensive cost, as we make assumption that, if the return value is null, it means expensive negating.

Gentle ping ...

The existing code is buggy, and there's general agreement that this is a good way forward, so please make whatever NFC changes (like switching the enum values) are possible as preliminary patches. Also, make small edits that were already suggested (improve code comments, add assert message string). That will reduce risk and make the final review easier.

llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
5734

Add an assert string for this and other assert lines (if they are going to remain):
http://llvm.org/docs/CodingStandards.html#assert-liberally

steven.zhang marked an inline comment as done.

Rebase the patch and add the assertion string, necessary comments and the crash test of D76638.

steven.zhang updated this revision to Diff 257646.EditedApr 15 2020, 3:13 AM

Minor NFC update to the patch.
To help the reviewer review this patch easier, I summarize the patch as follows:

  1. All the changes in DAGCombine.cpp is NFC change, which is just some text replacement with new interface.
  2. All the changes in X86 or AMDGPU is also NFC change, which remove the duplicated code of getNegatibleCost and make the getNegatedExpression logic more clean.
  3. Inside the TargetLowering.cpp, I merge all the checkers in getNegatibleCost into getNegatedExpression before doing the negation. Most of them are semantics the same, except the FMA/FADD/FMUL. Old logic will return the cost immediately if the LHS is not expensive, which will make the cost of FMUL A, B, A=neutral, B=cheaper as neutral, and now, it is cheaper. We have to negate them both and decide the best cost.
  4. The change in the PowerPC test is as expected.
spatel added inline comments.Apr 15 2020, 5:49 AM
llvm/include/llvm/CodeGen/TargetLowering.h
3575

Do we need to initialize the output value here (and other parts of the patch)? I think it would be better to just document in the code comment for this function that the Cost value is invalid if no SDValue is returned. Alternatively, we could add an 'Invalid' enum value if that's really a concern.

RKSimon added inline comments.Apr 15 2020, 10:03 AM
llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
12397 ↗(On Diff #257646)

Would it help to add getCheaperNegatedExpression as a helper with the existing methods right away and rebase this on top of that?

steven.zhang marked 2 inline comments as done.Apr 16 2020, 1:33 AM
steven.zhang added inline comments.
llvm/include/llvm/CodeGen/TargetLowering.h
261
3575

It makes sense. I will remove this line and add comments that the cost value is undefined if no SDValue returned.

llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
12397 ↗(On Diff #257646)

Good suggestion. I will do that.

steven.zhang planned changes to this revision.EditedApr 16 2020, 4:54 AM

NFC patch https://reviews.llvm.org/D78291 created and I will rebase this patch.

Split this patch into three parts for easy to review:

  1. D78291 NFC change by adding new helper functions and the changes in DAGCombine.
  2. D78347 Addressing the reason of the change in PowerPC tests.
  3. D77319(this patch) Remove the getNegatibleCost and merge it with getNegatedExpression to fix the out of sync issue.
RKSimon accepted this revision.Apr 25 2020, 6:15 AM

LGTM - this looks ready as well - when you are ready please commit the patches separately

This revision is now accepted and ready to land.Apr 25 2020, 6:15 AM
This revision was automatically updated to reflect the committed changes.
sammccall added a subscriber: sammccall.EditedMay 11 2020, 7:39 AM

We're seeing infloops in compilation after this change, specifically in an XLA test https://github.com/tensorflow/tensorflow/blob/master/tensorflow/compiler/xla/tests/convolution_variants_test.cc#L380-L398

I've managed to extract an IR reproducer (attached). llc -mcpu=skylake-avx512 module_0000.ir-with-opt.ll hangs with llc built after this change and not before.
Will revert and try to reduce it through bugpoint too

Reproducer:

steven.zhang reopened this revision.May 11 2020, 9:54 PM

Fix the infinite loop reported by sammccall.

This revision is now accepted and ready to land.May 11 2020, 9:54 PM
steven.zhang requested review of this revision.May 11 2020, 10:13 PM

The root cause of the infinite loop:
As mentioned before, the new created node will be deleted later in the next iteration of the DAGCombine. And we will push all the nodes it refers into the worklist to do the combine again. So, the circle reference would happen if the referred nodes refer that node later.
Thus, we need to remove the dead node we created immediately to avoid the side effect to DAGCombinne.

   /// This is the helper function to return the newly negated expression only
   /// when the cost is cheaper.
   SDValue getCheaperNegatedExpression(SDValue Op, SelectionDAG &DAG,
                                       bool LegalOps, bool OptForSize,
                                       unsigned Depth = 0) const {
-    if (getNegatibleCost(Op, DAG, LegalOps, OptForSize, Depth) ==
-        NegatibleCost::Cheaper)
-      return negateExpression(Op, DAG, LegalOps, OptForSize, Depth);
+    NegatibleCost Cost = NegatibleCost::Expensive;
+    SDValue Neg =
+        getNegatedExpression(Op, DAG, LegalOps, OptForSize, Cost, Depth);
+    if (Neg && Cost == NegatibleCost::Cheaper)
+      return Neg;
+    // Remove the new created node to avoid the side effect to the DAG.
+    if (Neg && Neg.getNode()->use_empty())
+      DAG.RemoveDeadNode(Neg.getNode());
     return SDValue();

Gentle ping ...

RKSimon accepted this revision.May 19 2020, 8:59 AM

LGTM - cheers

This revision is now accepted and ready to land.May 19 2020, 8:59 AM
This revision was automatically updated to reflect the committed changes.