[llvm-dev] Potentially unsafe loop optimization
Richard Kenner via llvm-dev
llvm-dev at lists.llvm.org
Wed Feb 17 15:57:39 PST 2021
I'm seeing a case where an add instruction with "nuw" is moved to what
I think is an unsafe place and I'm wondering if there really is a bug
here. This comes from the small Ada code:
PROCEDURE C26006A IS
procedure C_abort with Import, Convention => C,
External_Name => "abort";
S1 : STRING (1..3) := "A 1";
S2 : STRING (1..3) := "A 2";
BEGIN
FOR C IN CHARACTER'FIRST .. CHARACTER'LAST LOOP
S1 (2) := C;
S2 (2) := C;
if S1 = S2 then
C_Abort;
end if;
END LOOP;
END C26006A;
and the issue is the loop, which is from (interpreting the i8 as unsigned)
0 to 255 (-1).
The loop end test looks like:
loop.cond.iter: ; preds = %8
%6 = load i8, i8* %c, align 1, !tbaa !5
%loop.iter.cond = icmp eq i8 %6, -1
br i1 %loop.iter.cond, label %loop.exit, label %loop.iter
loop.iter: ; preds = %loop.cond.iter
%next.loop.var = add nuw i8 %6, 1
store i8 %next.loop.var, i8* %c, align 1, !tbaa !5
br label %loop.cond
This looks correct. We do the add after the conditional branch, so
we'll never see it overflow.
But when we optimize with -O2 on this, we get:
loop.cond.iter: ; preds = %loop.cond, %3
%loop.iter.cond = icmp eq i8 %c.0, -1
%next.loop.var = add nuw i8 %c.0, 1
br i1 %loop.iter.cond, label %loop.exit, label %loop.cond
But now the add is done unconditionally, whatever the result of the
comparison. That means that in the case where the icmp is true, we
execute an undefined instruction. When we generate code from this, the
code generator notices that, eliminates the test, and makes this an
an conditional jump back, with the relevant code being:
.LBB0_3: # %loop.cond.iter
# in Loop: Header=BB0_1 Depth=1
incb %bl
.LBB0_1: # %loop.cond
# =>This Inner Loop Header: Depth=1
movb %bl, 17(%rsp)
movb %bl, 1(%rsp)
movzwl (%rsp), %eax
xorw 16(%rsp), %ax
movzbl 2(%rsp), %ecx
xorb 18(%rsp), %cl
movzbl %cl, %ecx
orw %ax, %cx
jne .LBB0_3
# %bb.2: # in Loop: Header=BB0_1 Depth=1
callq abort
jmp .LBB0_3
I think the initial IR is correct, but the loop optimization here is
unsafe: if it wants to promote the add instruction, it needs to remove
any "nsw" or "nuw". Does that sound correct?
The full .ll is below:
; ModuleID = 'c26006a.adb'
source_filename = "c26006a.adb"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16
:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
define void @_ada_c26006a() {
entry:
%s1 = alloca { { i32, i32 }, [3 x i8] }, align 4
%s2 = alloca { { i32, i32 }, [3 x i8] }, align 4
%c = alloca i8, align 1
%0 = bitcast { { i32, i32 }, [3 x i8] }* %s1 to [3 x i8]*
store [3 x i8] c"A 1", [3 x i8]* %0, align 4, !tbaa !0
%1 = bitcast { { i32, i32 }, [3 x i8] }* %s2 to [3 x i8]*
store [3 x i8] c"A 2", [3 x i8]* %1, align 4, !tbaa !0
store i8 0, i8* %c, align 1, !tbaa !5
br i1 true, label %loop.cond, label %loop.exit
loop.cond: ; preds = %loop.iter, %entry
%2 = load i8, i8* %c, align 1, !tbaa !5
%3 = getelementptr inbounds [3 x i8], [3 x i8]* %0, i64 0, i32 1
store i8 %2, i8* %3, align 1, !tbaa !8
%4 = load i8, i8* %c, align 1, !tbaa !5
%5 = getelementptr inbounds [3 x i8], [3 x i8]* %1, i64 0, i32 1
store i8 %4, i8* %5, align 1, !tbaa !8
br i1 false, label %rhs.has.0.dim, label %normal.tests
loop.iter: ; preds = %loop.cond.iter
%next.loop.var = add nuw i8 %6, 1
store i8 %next.loop.var, i8* %c, align 1, !tbaa !5
br label %loop.cond
loop.exit: ; preds = %loop.cond.iter, %entry
ret void
loop.cond.iter: ; preds = %8
%6 = load i8, i8* %c, align 1, !tbaa !5
%loop.iter.cond = icmp eq i8 %6, -1
br i1 %loop.iter.cond, label %loop.exit, label %loop.iter
7: ; preds = %9, %rhs.has.0.dim
call void @abort()
br label %8
8: ; preds = %7, %9, %normal.tests
br label %loop.cond.iter
rhs.has.0.dim: ; preds = %loop.cond
br i1 false, label %7, label %normal.tests
normal.tests: ; preds = %rhs.has.0.dim, %loop.cond
br i1 true, label %9, label %8
9: ; preds = %normal.tests
%10 = bitcast [3 x i8]* %1 to i8*
%11 = bitcast [3 x i8]* %0 to i8*
%12 = call i32 @memcmp(i8* align 4 %10, i8* align 4 %11, i64 3)
%13 = icmp eq i32 %12, 0
br i1 %13, label %7, label %8
}
; Function Attrs: nounwind
declare dso_local i32 @memcmp(i8* nocapture nonnull readonly, i8* nocapture nonnull readonly, i64) #0
declare void @abort()
attributes #0 = { nounwind }
!0 = !{!1, !1, i64 0, i64 3}
!1 = !{!2, i64 3, !"c26006a__T2b#TN#AD", !3, i64 0, i64 1, !3, i64 1, i64 1, !3, i64 2, i64 1}
!2 = !{!"Ada Root"}
!3 = !{!4, i64 1, !"character#T0"}
!4 = !{!2, i64 1, !"character#TN"}
!5 = !{!6, !6, i64 0, i64 1}
!6 = !{!7, i64 1, !"T4b#T4"}
!7 = !{!4, i64 1, !"T4b#TN"}
!8 = !{!3, !3, i64 0, i64 1}
More information about the llvm-dev
mailing list