This is an archive of the discontinued LLVM Phabricator instance.

[SCEV] Properly guard reasoning about infinite loops being UB on mustprogress
ClosedPublic

Authored by reames on Jun 7 2021, 11:41 AM.

Details

Summary

Noticed via code inspection. We changed the semantics of the IR when we added mustprogress, and we appear to have not updated this location.

Diff Detail

Event Timeline

reames created this revision.Jun 7 2021, 11:41 AM
reames requested review of this revision.Jun 7 2021, 11:41 AM
Herald added a project: Restricted Project. · View Herald TranscriptJun 7 2021, 11:41 AM
nikic accepted this revision.Jun 7 2021, 12:07 PM

LGTM

This revision is now accepted and ready to land.Jun 7 2021, 12:07 PM
This revision was landed with ongoing or failed builds.Jun 7 2021, 2:48 PM
This revision was automatically updated to reflect the committed changes.
bjope added subscribers: uabelho, bjope.EditedJun 10 2021, 9:31 AM

Hi @reames,

We've seen some regressions after this patch, with SCEV not being able to calculate the backedge taken counts for a loop.

Although I guess the idea here is to make the code more restrictive in some way (so maybe that should be expected).

Anyway, I tried to make a small reproducer here, to visualize the problem:
https://godbolt.org/#z:OYLghAFBqd5TKALEBjA9gEwKYFFMCWALugE4A0BIEAZugHZEDKqAhgDbYgCMALOXUYBVAM7YACgA8QAcgAMM8gCse5dq3qgA%2BjtTkxnVEQINq2epgDC6dgFcAtvRABmAKzlzmADIF62AHIOAEbYpDzc5AAO6CLEJvTWdo4u7tGxxgw%2BfoH2IWHcEQbYRvFMRKykRIkOTm762IYZ9GUVRFkBwaHh%2BuWV1cl1Ir1tvh25XQUAlProtqSoXDIA9EsA1AC0qwAqSAQiqwDuvpjoB6tIrPtESNirlusis/O3GDir2JKs9pGcAHQApHIAIIrDarf4AJgh1m%2BBE4pHBEOc3EhENWe1WRFIGhEdFI9l8wEx6FWXi8ADUALLogBKgJBa02O1YRFWAEkaedLqsQuZVvYNLYOOwAJ6rDCRAjYTDExEQx5zBaI5wQ1H00FMm70Q63WxiVaEGg0ULmVkAN1CsQY%2B3QNFW6EirJIqzEtwIdqYllw5PFGlWkVI0oIRkxNx5rFQAGtpcBsOtytHtRhbIwRADgaDVqsAOrEJD2x2rbgQ35yUs61ax1nsdAO8WzVOrPE89DXVY1h0icg82yso5th2shiY0gpyMVhiiyvYVnEdVrLPNhi3W2h24dyJp%2Bn0muaJu1iC7olA/6uABCp4AIt2j6t6JNwQB2C/ArO3yTg5yX1Zyf7OF9AouZCrIeDBEkon7fr%2B/6rBBf6WHef5noiF4QmeSiTPSWZAQioF7gQkGwUh6Kfgh9DEQRkIXl%2Bd6Ya%2B2FZh%2BVF/t%2BJ7ngQV5IVhqyBkQczapIXHAv8j6XjI0zsLIriKE48iKOgsgIQqzzKtwihELICiTNMNysDgYQQNMkYgBCAAcvyPhCvDOM4ABschyLZpm2c4ACc1lqLIvCKPYLglrwvBIq5LncDZtm8I55CyQo5AKTIigiCAcjkBpcnTHAsCIBAKAYLC8KUNQuWSvCaCoOomhaFiY4CHCRCWtQQSyGp5BBL4FQik1ii5fYpoAPL0KKnXkDgAqaJwmmKIQgYlBaiUyM1HzFL2izNb4dWSXJagEEE2KkCK1g4ENWIEL583iQIDDMGw43FhdwhiFIE3KKo5XaLoW1BIlkDTIO8RzesvXcBs2AEkQ6zsOwZr2BswAxUUJSmBAnj9LUyWeO0OR5CArgRGkcQMCjLjJXjTQY50YQ4/UjSlMMhOFA0xRNC0lRk%2BM%2BQ9K0dMcyzoyYxM0zKQsqgHBoRC9Y6bL0HQnUSVJMlPXFkjOes4Xiq9IFVfQkYPhA%2BDEMBkLIt2MLFaEqkPpY6kTdp5C6fp1BGdjyUbd55C%2BbZj6/K4riPvZdnOS5rjuVFCuyAlSUpdb5AZcgaBYHghAkBQVC0JdLAcFwt2CEQogSNIm0qBCagaKAENQ1TjPxGYFiE84aMWKzWOUyT8S18TMT4/QjddJT8NM7TNg1ETFcI80wzdxThQD0kqPcyM2Tk9j3AC08QsyMRlJYLYnBst%2BLGrGAyysJEkRLB8Xw/NgvyoIf9KC9gWg0HC5hfLc%2B%2Boksx%2Bn%2Bf3x/KgarAl6FWA0LIOCsBFLMVk78oRxl8nGSIEJHxyD8n5dYCDHzcBQSqNBiCIQgHCvg3g6wCAEPCusGgplkHFlMusegpkeC2RQWQpg1CAFAiATOEcBBL6EVRErWyWgyFjnoKceg4NfC2EkOsYA9BbBsO3DBAAYimUeqx2FYkSneW0gZbgiOmnMfUIiRAinoKgLRKYjgWB4tgPSk4xS2AOOUIInB6Q4Cfn4A0jwtA1muuiVWAABOg6AIAkN4AAKi0WwR0cxbiBlsQNMUkJXDJT8bwREOMHw%2BI4FoFM9BX6YC0HpTACJDZyCfABLMSTnCEWDN8F0wBZyqySREH83EdroiBlUm8rAQjsHSfwdsPSGjpMfNuYEvAQAMSmdMmZsy5lzOIgGaU%2BxoGuCLuk1y3EkmuEIpEXYqTwTnnSdwFJSTXKHOvIc5CpyIRrIuVs25tldn7NCVc45j5uxnIuZ8o5Ny7mcXou09QvTjmqmEsCR8kz5nQphbCz8yElmYBWTRM5nzblgsAuk0yzyCKvNPNctF/zXCXPxe8wl5yAWYt4gcpJpkxlAlclCuFzKWXwv9IGJFvDbnFgeTjMp%2B8il3gcXeEQZw8W3PcEWXlBQamoDqdgAAjjS7lfyUSAoRAQTp3KWnAuGUkj5gyQVJIGWAMAZd7C/A3AfMAzh6XFiZayx1CyYKIuRXvW5AzmkYsqdy6p%2B89m4qaUc5pplCXFm%2BW87ZhK0mUp9TjGNNEA00uDdyg1XqI2kqSbZaN9z6LNJ2fvKsDMeqMEdBq%2BgQQGyctCd2UJESkkpPFTjW1ebuVPP3jWPSqTa3hXrdyyVHACAyNWKG61RAgisFYNa2y0rHyEUFfQUVyqcbZuObwaV2KBWYBlIusVTTfVhulRSmitTIgunYI0mNB7Wnqo6cc1y3SjXcvWbqvpqLrXmstbWM9pqTnblEvSlkx0gjLRWUiflKLnxaJoDorR%2BjSCGJiCYsxIiLHHGsfEqcDinGcDlDB1%2BaD0BrVCGqL8qIRF%2BFRHKAkYjODAA4OsC0RgyDrCOJga4pHLyol/FCOUIj4zYhPoSdYAoONQhYqiKq2AqOoiGBGSMaDSCtkZixkDRpQgPAIAAL2k%2BJsjUI6W8ckxUKs6xUCRDkXprjUJ%2BEq3XUZqEHCwbGhZDEkQnG1RoVQErcgVEaCSBEBQKi9h7CSF82hEQYhwtnki9gIuVElajIcxCPifgzMWY81CWMfhSDBhkwB8FQJTWfvsFvP4NB1DADddakSZ5TUpN/SJLjwJiuQwtQQHAjBCKmtq6a1UBWTwtbABB78PXnwEBVEWbsPWoQHFQBcUgWhYg6dRLWyb66Bu/u62AWrE20QGpmxCbDPSXFQjW2iFEm2wBon3mNurO2oRlRLqsC0CH4hFmcKWcs2UiBEE3CAFYwA8y2CCNfdA9gljmsh21xT6AlCM1%2BED1kQQQi2RoDjIIlk0euUwIFWyrhMA0ACnIR8j47Kk4hNupyEZsemUwuJq7fqaKECGL4EMd3TXOGm2AE1YBXBNfpKahNo2dvPkO5%2Bjcvx7B6j%2B0p4AgZItqiuwWmiHOHsQgl9%2B34KYlMQ1%2BCzk7un%2BvNaK2AdtqvRf3YO2AA1rzfxXbnbdy34uwKretaOu3Auhubot719X4P6DcOU11%2BbFQ3emofQc%2B3JuI/bb96iJgJ0eGWCWJYZiaFthniBCeBnMewAnMIiz4wpjWRq5Odz3n/PRLnQ2tJEOm04pkipOyTk99lQQitmlHSNj7aGXIMZZJnkZCu2ivJMO%2BgI6pS0rLGQHe3b4M9t7X2ch/a2UDsHUfsVx9T/Eo7VwtlfiBVMj7H2wVXCmUQfvofzh5YN%2B31HGOj%2Bcrg9NsnQqL/n5hEHJrSMNUL31QgEak2lajyT2iGm6j6gSSehGhLnGk2imkrlmiekWlQGWiGmIw2hinYG2l2n2njiemOlOjUmmGznThuiLmzlzkegLhMmLk0BAHNQ%2Bi%2Bj71%2BmtFkHWBfmcTjD8AODQXsBYjKXWA0A4BFB0w2BEGugqA4LNEHiaASgZlHmrisEHmSAiHRl5kXlxg7iaC5hbkyA0LZlUD7hpk5hUKcHpmpgYGZnnjGCxinlMJniMPHgMLsJXkVC4AhBnzr03zikHA1lHC1h1j1iTnb2Ng/3hHbwfCb2pA5E72n37z8l%2BBOVcjsl4FcAim4FcGcDJ34Bdh8idnrxijinDmSh33SiymfzylCAKggCKk/xAEHGLAcj/zqgQwaiehAPanAPBxLTFigPgJBlgMWBigQJmmwDmhihQLQLOkoEYAaCemwJ2nagOmGPUlyyIPOlIOukzgoMuioPzhihUC51egYLayYPgB%2BkdD%2BnYM4M4FoWwF4MiH4K/EEOENFDEIeEkNIGkNkPiHkMsKcCRhrjMNUHUIXkMK0PSFbhBMhM7gniMIUP7gcKHgsMrisOcPBLsLni5jkx5kxP5hmHcJMi8NvyKNkD8OLC%2BzKV1kTgNiRHWRNk/2VGcCiIpBiJpDiN31th7y6D7zyPn0Hx8PH0SlKOtkdmsi9gwUCkChX14G4Cx2zQ2hv0KLH3ikji7yHznyFLVLKOmFeytCcF4CAA%3D%3D

One thing that might cause problems here is that it seems like getting the MustProgress attribute on a Function is quite hard. It is basically inferred by having WillReturn on the Function, and afaict the WillReturn attribute is never inferred if the Functions contain loops.

In the godbolt example above the Function has no MustProgress attribute, however the loop has a MustProgress attribute. So maybe loopIsFiniteByAssumption should check if the loop has MustProgress as well?

That wouldn't however help for the regressions we see downstream, because we do not get any MustProgress attributes on the loops either in that case. No idea why really, but I figure those depend on the C-standard in some way. But aren't the loops in my example finite? So why is MustProgress attributes important here?

nikic added a comment.Jun 10 2021, 9:47 AM

One thing that might cause problems here is that it seems like getting the MustProgress attribute on a Function is quite hard. It is basically inferred by having WillReturn on the Function, and afaict the WillReturn attribute is never inferred if the Functions contain loops.

In the godbolt example above the Function has no MustProgress attribute, however the loop has a MustProgress attribute. So maybe loopIsFiniteByAssumption should check if the loop has MustProgress as well?

Yes, it should be checking hasMustProgress(L) as well.

That wouldn't however help for the regressions we see downstream, because we do not get any MustProgress attributes on the loops either in that case. No idea why really, but I figure those depend on the C-standard in some way. But aren't the loops in my example finite? So why is MustProgress attributes important here?

Consider your example with n = 1:

long foo(long A[]) {
  long x = 0;
  for (long j = 0; j < 1; ++j)
    for (long i = j; i < 1; i += 1/2)
      x += A[i];
  return x;
}

Note that thus j == 0, and 1/2 == 0:

long foo(long A[]) {
  long x = 0;
  for (long i = 0; i < 1; i += 0)
    x += A[i];
  return x;
}

This is an infinite loop.

bjope added a comment.Jun 10 2021, 9:51 AM

One thing that might cause problems here is that it seems like getting the MustProgress attribute on a Function is quite hard. It is basically inferred by having WillReturn on the Function, and afaict the WillReturn attribute is never inferred if the Functions contain loops.

In the godbolt example above the Function has no MustProgress attribute, however the loop has a MustProgress attribute. So maybe loopIsFiniteByAssumption should check if the loop has MustProgress as well?

Yes, it should be checking hasMustProgress(L) as well.

That wouldn't however help for the regressions we see downstream, because we do not get any MustProgress attributes on the loops either in that case. No idea why really, but I figure those depend on the C-standard in some way. But aren't the loops in my example finite? So why is MustProgress attributes important here?

Consider your example with n = 1:

long foo(long A[]) {
  long x = 0;
  for (long j = 0; j < 1; ++j)
    for (long i = j; i < 1; i += 1/2)
      x += A[i];
  return x;
}

Note that thus j == 0, and 1/2 == 0:

long foo(long A[]) {
  long x = 0;
  for (long i = 0; i < 1; i += 0)
    x += A[i];
  return x;
}

This is an infinite loop.

Sorry, I used wrong example in the link. I've changed it to something where I do "i += n" instead of "i += n/2".

Is perhaps the problem that "i += n" might overflow (UB) and thereby we can't guarantee "must progress"?

Sorry, I used wrong example in the link. I've changed it to something where I do "i += n" instead of "i += n/2".

Is perhaps the problem that "i += n" might overflow (UB) and thereby we can't guarantee "must progress"?

For the updated example it should be technically fine, but SCEV is not strong enough to figure out that n != 0 at that point. Possibly performing an additional isLoopEntryGuardedByCond() check for != 0 would help.

JFYI, C and C++ apparently have different rules here. For c++ your example function is mustprogress, for C only the loop is. (Based on a quick skim of checkIfLoopMustProgress clang's CodeGenFunction.h) I have no idea if the C rules could be more aggressive here or not, you'd be best taking that up with a clang developer.

It does look like checking the loop mustprogress flag would be sufficient to address the specific regression seen here. I'll see if that's easy to implement quickly.

For the weakness being discussed, feel free to file a bug. I'm currently working through a collection of missing cases in SCEV's trip count logic and having an example to come back to would help.

The regression for C programs should be addressed in aaaeb4b16. Note that this requires code motion done in b6ee5f2b.

The regression for C programs should be addressed in aaaeb4b16. Note that this requires code motion done in b6ee5f2b.

Thanks a lot for all the follow up on the problem I had noticed!

Seems like clang won't add the mustprogress attribute to loops when using C99, so I think we would need to use -ffinite-loops to get any help from aaaeb4b16.

After examining a bunch of application code I found two cases for which vectorization disappeared when adding this patch, and another case when hwloop analysis failed to derive the trip count by using SCEV.
I will however try D104066 , which looks a bit more promising to avoid the regressions I've seen with C99.

(I'll make sure to write some PR:s if something remains also when having applied D104066.)