This is an archive of the discontinued LLVM Phabricator instance.

[InstCombine] Prevent icmp transform that can cause inf loop if part of min/max
AbandonedPublic

Authored by tejohnson on Mar 14 2019, 11:42 AM.

Details

Summary

I found an infinite loop due to repeatedly canonicalizing a min/max
cmp/select sequence in canonicalizeMinMaxWithConstant due to a
competing transformation on that icmp due to a dominating cmp.

There is already code in the ICmp folder to try to detect and block
ICmp transformations leading to such infinite loops. Prior fixes along
these lines have extracted and moved the offending ICmp transforms below
the check (e.g. r293345 and r315895). Rather than take that approach, I
moved the detection of the possible min/max sequence earlier, and pass a
flag into foldICmpWithDominatingICmp.

Added a test to minmax-fold.ll that will infinitely loop without this
patch. I also added a test (not subject to the min/max) of this
particular ICmp transform to icmp-dom.ll, since it does not appear that
one exists (when I block that transformation completely, nothing
fails!).

Note that I could not figure out how to trigger the infinite loop with
the NE transformation just below the EQ transformation I am guarding.
Which doesn't mean it can't happen, but guarding that one too resulted
in some test case failures (as we blocked some expected
transformations). In general, I'm concerned that this possibility exists
with other ICmp transformations, and that they are being worked around
one by one as they are encountered in the wild, but I don't have
the expertise here to do a broader assessment/fix.

Diff Detail

Event Timeline

tejohnson created this revision.Mar 14 2019, 11:42 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 14 2019, 11:42 AM
Herald added a subscriber: jdoerfert. · View Herald Transcript

Please check in the test that provides the missing coverage as an NFC preliminary patch.

I agree with all of the comments about the min/max hacks here, and I wish we could untangle this, but it's tough to do without breaking something along the way.

I'd prefer that we IR simplify our way out of this infinite loop instead of looking the other way though. Ie, can we get this in instsimplify using a ConstantRange?
https://rise4fun.com/Alive/dOr

%i2 = icmp sgt i32 %i, 1
%i3 = select i1 %i2, i32 %i, i32 1
%i4 = icmp sgt i32 %i3, 0
=>
%i4 = true
nikic added a comment.Mar 17 2019, 9:48 AM

I'd prefer that we IR simplify our way out of this infinite loop instead of looking the other way though. Ie, can we get this in instsimplify using a ConstantRange?

That should be relatively simple to do, we just need to support constant range calculation for min/max flavor selects in computeConstantRange().

I'd prefer that we IR simplify our way out of this infinite loop instead of looking the other way though. Ie, can we get this in instsimplify using a ConstantRange?

That should be relatively simple to do, we just need to support constant range calculation for min/max flavor selects in computeConstantRange().

One thing I didn't mention is that this opportunity was exposed only by InstCombine's instruction sinking. I captured the code after sinking to create the test case.

Also, is it reasonable to assume prior passes will transform the code to avoid triggering possible infinite loops in later passes like this? Unless you mean move part of this transformation into InstSimplify and out of InstCombine? If that is a better longer term fix for these issues, it would be good if this one can go in for now to block the infinite loop.

I'd prefer that we IR simplify our way out of this infinite loop instead of looking the other way though. Ie, can we get this in instsimplify using a ConstantRange?

That should be relatively simple to do, we just need to support constant range calculation for min/max flavor selects in computeConstantRange().

One thing I didn't mention is that this opportunity was exposed only by InstCombine's instruction sinking. I captured the code after sinking to create the test case.

Also, is it reasonable to assume prior passes will transform the code to avoid triggering possible infinite loops in later passes like this? Unless you mean move part of this transformation into InstSimplify and out of InstCombine?

instsimplify and instcombine have a special relationship: instcombine always calls instsimplify as an analysis on an instruction before trying more general combines (because we can avoid a lot of work if we can kill the instruction first). So that lets us assert that some patterns just don't exist by the time general instcombine is running.

If that is a better longer term fix for these issues, it would be good if this one can go in for now to block the infinite loop.

@nikic - do you have time to work on the instsimplify change? I'm ok if we want to add this patch as an immediate work-around for the hang, but if we need to take that option, let's mark this with a FIXME comment because we know there's a better solution.

@spatel I've created D59506 for the InstSimplify change.

@spatel I've created D59506 for the InstSimplify change.

Thanks! I think we can safely abandon this patch...until we hit the next infinite loop (although that extra regression test in this patch should still be committed).

@spatel I've created D59506 for the InstSimplify change.

Thanks! I think we can safely abandon this patch...until we hit the next infinite loop (although that extra regression test in this patch should still be committed).

Thanks. I will commit the extra regression test independently. Before we abandon this, let me make sure the other patch fixes my original infinite loop, which as I mentioned was exposed by the instruction sinking done during InstCombine. I'll take a look when I'm back in the office tomorrow.

tejohnson abandoned this revision.Mar 19 2019, 8:43 AM

Committed icmp test in r356463.

Also tried my original reproducer with the InstSimplify patch D59506, and thankfully that does fix it. Abandoning this one.