[llvm-bugs] [Bug 31652] New: Loop unswtich and GVN interact badly and miscompile

via llvm-bugs llvm-bugs at lists.llvm.org
Mon Jan 16 03:19:41 PST 2017


https://llvm.org/bugs/show_bug.cgi?id=31652

            Bug ID: 31652
           Summary: Loop unswtich and GVN interact badly and miscompile
           Product: libraries
           Version: trunk
          Hardware: All
                OS: All
            Status: NEW
          Keywords: miscompilation
          Severity: normal
          Priority: P
         Component: Scalar Optimizations
          Assignee: unassignedbugs at nondot.org
          Reporter: nunoplopes at sapo.pt
                CC: david.majnemer at gmail.com, davide at freebsd.org,
                    dberlin at dberlin.org, gil.hur at sf.snu.ac.kr,
                    juneyoung.lee at sf.snu.ac.kr, llvm-bugs at lists.llvm.org,
                    regehr at cs.utah.edu, sanjoy at playingwithpointers.com
    Classification: Unclassified

Created attachment 17845
  --> https://llvm.org/bugs/attachment.cgi?id=17845&action=edit
Reduced IR test case

Loop unswitch and GVN when taken together can miscompile code due to
disagreeing semantics of branch on undef.
In our opinion it's loop unswitch that is incorrect.

Test case in C:
static volatile int always_false = 0;

void maybe_init(int *p) {}

int f(int limit0) {
  int maybe_undef_loc;
  maybe_init(&maybe_undef_loc);

  int limit = limit0;
  int total = 0;
  for (int i = 0; i < limit; i++) {
    total++;
    if (always_false) {
      if (maybe_undef_loc != (limit0 + 10)) {
        total++;
      }
    }
    limit = limit0 + 10;
  }
  return total;
}

int printf(const char *, ...);

int main(int argc, char **argv) {
  printf("f(10) = %d\n", f(10));
  return 0;
}


I believe this test case has no UB even at C level. It should print "f(10) =
20".

I've attached a reduced IR test case.


Running LICM, the comparison of maybe_undef_loc gets hoisted:

$ opt -S bug.ll -loop-rotate -licm
...
loop.body.lr.ph:
  %undef_loc = load i32, i32* %maybe_undef_loc, align 4
  %add = add nsw i32 %limit0, 10
  %cmp3 = icmp ne i32 %undef_loc, %add
  %limit2 = add nsw i32 %limit0, 10
  br label %loop.body
...


This is still ok. But then adding loop unswitch:

$ opt -S bug.ll -loop-rotate -licm -loop-unswitch
...
loop.body.lr.ph:
  %undef_loc = load i32, i32* %maybe_undef_loc, align 4
  %add = add nsw i32 %limit0, 10
  %cmp3 = icmp ne i32 %undef_loc, %add
  %limit2 = add nsw i32 %limit0, 10
  br i1 %cmp3, label %loop.body.lr.ph.split.us, label
%loop.body.lr.ph.loop.body.lr.ph.split_crit_edge
...

Loop unswitch introduce a branch on %cmp3, which will be undef once we inline
maybe_init(). Hence loop unswitch assumes branch on undef is a
non-deterministic jump (so not UB).
By running GVN after loop unswitch, we clearly see that GVN has a different
perspective on the semantics of branch on undef. It produces this diff:
-  %limit2 = add nsw i32 %limit0, 10
...
-  %cmp = icmp slt i32 %i2, %limit2
+  %cmp = icmp slt i32 %i2, %undef_loc

I'm not including the context here, but to be able to justify this
transformation, branch on undef has to be UB, not a non-deterministic jump as
assumed by loop unswitch above.

Putting all things together:
$ opt -S bug.ll -loop-rotate -licm -loop-unswitch -gvn | opt -S -O2 | llc -o
x.S && clang x.S -o x && ./x
f(10) = 1

This is wrong. Should be 20, not 1.

This example shows how Loop unswitch and GVN can produce a miscompilation
end-to-end. The bug can be fixed by using the proposed freeze instruction in
loop unswitch.

(Example by Sanjoy)

-- 
You are receiving this mail because:
You are on the CC list for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-bugs/attachments/20170116/52a56899/attachment.html>


More information about the llvm-bugs mailing list