This is an archive of the discontinued LLVM Phabricator instance.

[Attributor] Loop Abstract Attribute
Needs ReviewPublic

Authored by uenoku on Jul 25 2019, 11:50 AM.

Details

Summary

This patch would add Loop Abstract Attribute which analyses a termination of Loop in Attributor Framework.
This attribute will determine whether the loop satisfies each of these properties.

  • always-endless

It means that a loop will terminate finally. This property would be used to improve willreturn attribute deduction.

  • never-endless

It means that a loop will never terminate. This property would be used to improve noreturn attribute deduction.

Currently, this patch only contains the prototype of them so a lot of things including tests will be added later.

Diff Detail

Event Timeline

uenoku created this revision.Jul 25 2019, 11:50 AM
Herald added a project: Restricted Project. · View Herald TranscriptJul 25 2019, 11:50 AM

I think we also want to look at:

  • loop exit conditions to show that they are trivial/never fulfilled
  • instructions in the loop to show that endless loops would cause UB eventually
  • instructions in the loop to show that endless loops would cause UB eventually

I couldn't come up with how this can be used for the deduction. For example, alloca in the loop would cause UB but what information this gives us? I think endless loops would itself be a very strong thing.

Please note that endless loops in itself is not UB in IR.

Please note that endless loops in itself is not UB in IR.

I see. Thanks.

Please note that endless loops in itself is not UB in IR.

Correct.

One way to generate UB is through accesses or control involving poison.
What I wanted us to do is derive "not endless" for the following kinds of loops:

int s = ...
while (s++) ...;                     // overflow -> poison value -> poison in control 
while (s + NonZero != N) ...;        // overflow -> poison value -> poison in control
while (s * NonOneNonZero != N) ...;  // overflow -> poison value -> poison in control
...

while (...)
  access A[s++]        // overflow -> poison value -> poison access address 

unsigned u = ...;
while (...)
  access A[u++]        // Assuming NULL is not a valid pointer && "alignment of %ptr" modulo "sizeof(*ptr)" == 0

while (...)
  access A[u++]        // not strictly UB, depending on the types, but reasonable to exploit anyway (I think), potentially under a flag)

(I use signed/unsigned as an alternative to nsw/nuw in the GEP/add.)
We have helpers to determine poison and poison propagation (I think). For now, I'd be fine with the easy cases
for (int i = ...; i cmp ...; i++) { ... } and `for (int i = ...; ...; i++) { ... A[i] ... }
(Must-be-executed is can/should potentially be used here as well.)

For example, alloca in the loop would cause UB but what information this gives us

Arguably, it would not be UB even if every implementation has limited memory. The problem is, I think, that a failed allocation alone is not UB.
Even if we have accesses on that allocation (which I doubt we find in the wild anyway), we would be in the same case as the last example above, not actually UB.

Maybe, to simplify the first version, due the following "cannot be infinite" check: if the loop doesn't make forward progress, thus it is nosync, it has to be ending eventually.