<html>
    <head>
      <base href="https://bugs.llvm.org/">
    </head>
    <body><table border="1" cellspacing="0" cellpadding="8">
        <tr>
          <th>Bug ID</th>
          <td><a class="bz_bug_link 
          bz_status_NEW "
   title="NEW - Loop unrolling incorrectly duplicates noalias metadata"
   href="https://bugs.llvm.org/show_bug.cgi?id=39282">39282</a>
          </td>
        </tr>

        <tr>
          <th>Summary</th>
          <td>Loop unrolling incorrectly duplicates noalias metadata
          </td>
        </tr>

        <tr>
          <th>Product</th>
          <td>libraries
          </td>
        </tr>

        <tr>
          <th>Version</th>
          <td>trunk
          </td>
        </tr>

        <tr>
          <th>Hardware</th>
          <td>All
          </td>
        </tr>

        <tr>
          <th>OS</th>
          <td>All
          </td>
        </tr>

        <tr>
          <th>Status</th>
          <td>NEW
          </td>
        </tr>

        <tr>
          <th>Severity</th>
          <td>enhancement
          </td>
        </tr>

        <tr>
          <th>Priority</th>
          <td>P
          </td>
        </tr>

        <tr>
          <th>Component</th>
          <td>Loop Optimizer
          </td>
        </tr>

        <tr>
          <th>Assignee</th>
          <td>unassignedbugs@nondot.org
          </td>
        </tr>

        <tr>
          <th>Reporter</th>
          <td>nikita.ppv@gmail.com
          </td>
        </tr>

        <tr>
          <th>CC</th>
          <td>llvm-bugs@lists.llvm.org
          </td>
        </tr></table>
      <p>
        <div>
        <pre>The loop unrolling pass currently duplicates instructions carrying !noalias and
!alias.scope metadata without adjustments. This may lead to miscompilations,
because the aliasing information may not necessarily be valid across multiple
loop iterations.

The following C code demonstrates such a miscompilation:

#include "stdio.h"

void copy(int * restrict to, int * restrict from) {
        *to = *from;
}

void test(int *a, int *b) {
        for (int i = 0; i < 4; i++) {
                copy(&b[i & 1], &a[i & 1]);
        }
}

int main() {
        int ary[] = {0, 1, 2};
        test(&ary[1], &ary[0]);
        printf("%d %d %d\n", ary[0], ary[1], ary[2]);
        return 1;
}

Compiled using clang with -O0 the result is "2 2 2" (which is correct).
Compiled at -O3 the result is "1 2 2".

The following LLVM IR shows approximately the same situation as the above code
post-inlining:

define void @test(i32* %addr1, i32* %addr2) {
start:
    br label %body

body:
    %i = phi i32 [ 0, %start ], [ %i2, %body ]
    %j = and i32 %i, 1
    %addr1i = getelementptr inbounds i32, i32* %addr1, i32 %j
    %addr2i = getelementptr inbounds i32, i32* %addr2, i32 %j

    %x = load i32, i32* %addr1i, !alias.scope !2
    store i32 %x, i32* %addr2i, !noalias !2

    %i2 = add i32 %i, 1
    %cmp = icmp slt i32 %i2, 4
    br i1 %cmp, label %body, label %end

end:
    ret void
}

!0 = !{!0}
!1 = !{!1, !0}
!2 = !{!1}

The aliasing information here is valid within one loop iteration. Running this
through opt -loop-unrolling yields:

define void @test(i32* %addr1, i32* %addr2) {
start:
  br label %body

body:                                             ; preds = %start
  %x = load i32, i32* %addr1, !alias.scope !0
  store i32 %x, i32* %addr2, !noalias !0
  %addr1i.1 = getelementptr inbounds i32, i32* %addr1, i32 1
  %addr2i.1 = getelementptr inbounds i32, i32* %addr2, i32 1
  %x.1 = load i32, i32* %addr1i.1, !alias.scope !0
  store i32 %x.1, i32* %addr2i.1, !noalias !0
  %x.2 = load i32, i32* %addr1, !alias.scope !0
  store i32 %x.2, i32* %addr2, !noalias !0
  %addr1i.3 = getelementptr inbounds i32, i32* %addr1, i32 1
  %addr2i.3 = getelementptr inbounds i32, i32* %addr2, i32 1
  %x.3 = load i32, i32* %addr1i.3, !alias.scope !0
  store i32 %x.3, i32* %addr2i.3, !noalias !0
  ret void
}

!0 = !{!1}
!1 = distinct !{!1, !2}
!2 = distinct !{!2}

The loop was fully unrolled, and instructions carrying !noalias and
!alias.scope metadata were duplicated. With this IR, stores from *different*
iterations no longer alias with loads from *different* iterations, which does
not match the original semantics. A miscompile can then be caused by running
through -scoped-noalias -gvn.

The inlining pass has to deal with a similar issue. It has special code to
duplicate !noalias and !alias.scope metadata:
<a href="https://github.com/llvm-mirror/llvm/blob/54d4881c352796b18bfe7314662a294754e3a752/lib/Transforms/Utils/InlineFunction.cpp#L801">https://github.com/llvm-mirror/llvm/blob/54d4881c352796b18bfe7314662a294754e3a752/lib/Transforms/Utils/InlineFunction.cpp#L801</a>
Something similar is likely necessary in loop unrolling.

Context: This issue has been diagnosed in
<a href="https://github.com/rust-lang/rust/issues/54878">https://github.com/rust-lang/rust/issues/54878</a>. Rustc has temporarily disabled
the emission of noalias metadata for mutable references to work around this
problem, see <a href="https://github.com/rust-lang/rust/pull/54639">https://github.com/rust-lang/rust/pull/54639</a>.</pre>
        </div>
      </p>


      <hr>
      <span>You are receiving this mail because:</span>

      <ul>
          <li>You are on the CC list for the bug.</li>
      </ul>
    </body>
</html>