[llvm-bugs] [Bug 39282] New: Loop unrolling incorrectly duplicates noalias metadata
via llvm-bugs
llvm-bugs at lists.llvm.org
Sat Oct 13 15:57:40 PDT 2018
https://bugs.llvm.org/show_bug.cgi?id=39282
Bug ID: 39282
Summary: Loop unrolling incorrectly duplicates noalias metadata
Product: libraries
Version: trunk
Hardware: All
OS: All
Status: NEW
Severity: enhancement
Priority: P
Component: Loop Optimizer
Assignee: unassignedbugs at nondot.org
Reporter: nikita.ppv at gmail.com
CC: llvm-bugs at lists.llvm.org
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:
https://github.com/llvm-mirror/llvm/blob/54d4881c352796b18bfe7314662a294754e3a752/lib/Transforms/Utils/InlineFunction.cpp#L801
Something similar is likely necessary in loop unrolling.
Context: This issue has been diagnosed in
https://github.com/rust-lang/rust/issues/54878. Rustc has temporarily disabled
the emission of noalias metadata for mutable references to work around this
problem, see https://github.com/rust-lang/rust/pull/54639.
--
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/20181013/12fff94e/attachment.html>
More information about the llvm-bugs
mailing list