isLoopSimplifyForm(): is it a bug or not?

Hi, folks.
I want to check whether this behaviour is a bug or not.

Function “isLoopSimplifyForm()” is used to check whether the loop is in simplified form.
if it returns true, I think it means that the loop will not change if I rerun loop-simplify pass on it.

However, what if it returns true, and then runing loop-simplify pass also change the loop structure (adding new preheaders, etc.).

Is it a bug or accepted behavior ?

The IR eample is pasted below.

define void @f943() {
  br label %for.cond32.i.i

for.cond32.i.i:                                   ; preds = %for.end47.i.i, %entry
  br label %for.body37.i.i

for.cond42.preheader.i.i:                         ; preds = %for.body37.i.i
  br i1 poison, label %for.body44.i.i, label %for.end47.i.i

for.body37.i.i:                                   ; preds = %for.body37.i.i, %for.cond32.i.i
  br i1 poison, label %for.body37.i.i, label %for.cond42.preheader.i.i

for.body44.i.i:                                   ; preds = %for.body44.i.i, %for.cond42.preheader.i.i
  store volatile i8 poison, ptr poison, align 1
  br i1 poison, label %for.body44.i.i, label %for.end47.i.i

for.end47.i.i:                                    ; preds = %for.body44.i.i, %for.cond42.preheader.i.i
  br label %for.cond32.i.i

Simple loop form describes the 3 properties described here: LLVM Loop Terminology (and Canonical Forms) — LLVM 16.0.0git documentation

The -loop-simplify pass may do some additional work, like splitting a single loop into two:

The behavior you describe, i.e. adding another preheader if the loop already has one, should not happen though.

Note that each individual loop is converted to loop-simplify form. for.body44.i.i is not because it has no preheader since its predecessor for.cond42.preheader.i.i has multiple outgoing edges.

Thanks @Meinersbur

For the above piece of IR:

After applying loop-simplify

Two new blockes are created, for.body44.i.i.preheader and for.end47.i.i.loopexit.
Both of them are ensure simplified form of inner loop for.body44.i.i.

I am writing codes like this to check whether the whole loop nests are in simplified form.

for (auto &L : LI) {
    if (!L->isLoopSimplifyForm()) {
      Changed |= simplifyLoop(L, &DT, &LI, &SE, &AC, nullptr,
                              false /* PreserveLCSSA */);

The outter loop for.cond32.i.i(loop header), for.end47.i.i(loop latch) is in the loop simplified form. But the inner loop, for.body44.i.i is not.

However the above code did not check for the inner loop, it only checkes the outter loop. Is it a bug or desired behavior ?

for.body44.i.i is not in simplify form because it had no preheader and no dedicated exit block. So simplifyLoop added them (for.body44.i.i.preheder and for.end.47.i.i.loopexit).

The two loops are NOT nested inside each other. One is executed after the other (where the execution of the second loop is conditional). Both are nested inside a third loop (for.cond32.i.i) which is what for (auto &L : LI) iterates over.

The documentation of simplifyLoop says:

/// Simplify each loop in a loop nest recursively.

so it applies loop simplification to the two nested loops as well. Note that isLoopSimplifyForm() only considers the loop itself and not its nested loops.

Whether this is a bug depends on whether the code using this requires only the outer loop to be simplified form. If it expects all nested loops to be in simplified form then yes, this is a bug. In that case it should have been:

for (auto &L : LI) 
  Changed |= simplifyLoop(L, &DT, &LI, &SE, &AC, nullptr,
                              false /* PreserveLCSSA */);

And let simplifyLoop decide itself whether there is any work to do. Someone might have added the isLoopSimplifyForm() because they are only interested in the outer loop, but simplifyOneLoop is not public API.

1 Like

Thanks for your answer.