Skip to content

Commit 0afbf17

Browse files
committed
[LAA,LV] Add tests with early-exit loops and deref assumptions.
Adds additional test coverage for early-exit loops with deref assumptions, as suggested in #128436.
1 parent 48ef55c commit 0afbf17

File tree

2 files changed

+216
-0
lines changed

2 files changed

+216
-0
lines changed

llvm/test/Analysis/LoopAccessAnalysis/early-exit-runtime-checks.ll

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,3 +504,123 @@ e.0:
504504
e.1:
505505
ret i32 1
506506
}
507+
508+
define void @all_exits_dominate_latch_countable_exits_at_most_500_iterations_known_deref_via_assumption(ptr %A, ptr %B) {
509+
; CHECK-LABEL: 'all_exits_dominate_latch_countable_exits_at_most_500_iterations_known_deref_via_assumption'
510+
; CHECK-NEXT: loop.header:
511+
; CHECK-NEXT: Memory dependences are safe with run-time checks
512+
; CHECK-NEXT: Dependences:
513+
; CHECK-NEXT: Run-time memory checks:
514+
; CHECK-NEXT: Check 0:
515+
; CHECK-NEXT: Comparing group GRP0:
516+
; CHECK-NEXT: %gep.B = getelementptr inbounds i32, ptr %B, i64 %iv
517+
; CHECK-NEXT: Against group GRP1:
518+
; CHECK-NEXT: %gep.A = getelementptr inbounds i32, ptr %A, i64 %iv
519+
; CHECK-NEXT: Grouped accesses:
520+
; CHECK-NEXT: Group GRP0:
521+
; CHECK-NEXT: (Low: %B High: inttoptr (i64 -1 to ptr))
522+
; CHECK-NEXT: Member: {%B,+,4}<nuw><%loop.header>
523+
; CHECK-NEXT: Group GRP1:
524+
; CHECK-NEXT: (Low: %A High: inttoptr (i64 -1 to ptr))
525+
; CHECK-NEXT: Member: {%A,+,4}<nuw><%loop.header>
526+
; CHECK-EMPTY:
527+
; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop.
528+
; CHECK-NEXT: SCEV assumptions:
529+
; CHECK-EMPTY:
530+
; CHECK-NEXT: Expressions re-written:
531+
;
532+
entry:
533+
call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %A, i64 2000) ]
534+
call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %B, i64 2000) ]
535+
br label %loop.header
536+
537+
loop.header:
538+
%iv = phi i64 [ 0, %entry ], [ %iv.next, %latch ]
539+
%gep.A = getelementptr inbounds i32, ptr %A, i64 %iv
540+
%gep.B = getelementptr inbounds i32, ptr %B, i64 %iv
541+
%l = load i32, ptr %gep.A, align 4
542+
store i32 0, ptr %gep.B, align 4
543+
%cntable.c.1 = icmp ult i64 %iv, 1000
544+
%iv.next = add nuw nsw i64 %iv, 1
545+
br i1 %cntable.c.1, label %b2, label %e.1
546+
547+
b2:
548+
%uncntable.c.0 = icmp eq i32 %l, 0
549+
br i1 %uncntable.c.0, label %e.2, label %b3
550+
551+
b3:
552+
%cntable.c.2 = icmp eq i64 %iv.next, 500
553+
br i1 %cntable.c.2, label %cleanup4, label %latch
554+
555+
latch:
556+
br label %loop.header
557+
558+
cleanup4:
559+
ret void
560+
561+
e.1:
562+
ret void
563+
564+
e.2:
565+
ret void
566+
}
567+
568+
define void @all_exits_dominate_latch_countable_exits_at_most_500_iterations_deref_via_assumption_too_small(ptr %A, ptr %B) {
569+
; CHECK-LABEL: 'all_exits_dominate_latch_countable_exits_at_most_500_iterations_deref_via_assumption_too_small'
570+
; CHECK-NEXT: loop.header:
571+
; CHECK-NEXT: Memory dependences are safe with run-time checks
572+
; CHECK-NEXT: Dependences:
573+
; CHECK-NEXT: Run-time memory checks:
574+
; CHECK-NEXT: Check 0:
575+
; CHECK-NEXT: Comparing group GRP0:
576+
; CHECK-NEXT: %gep.B = getelementptr inbounds i32, ptr %B, i64 %iv
577+
; CHECK-NEXT: Against group GRP1:
578+
; CHECK-NEXT: %gep.A = getelementptr inbounds i32, ptr %A, i64 %iv
579+
; CHECK-NEXT: Grouped accesses:
580+
; CHECK-NEXT: Group GRP0:
581+
; CHECK-NEXT: (Low: %B High: inttoptr (i64 -1 to ptr))
582+
; CHECK-NEXT: Member: {%B,+,4}<nuw><%loop.header>
583+
; CHECK-NEXT: Group GRP1:
584+
; CHECK-NEXT: (Low: %A High: inttoptr (i64 -1 to ptr))
585+
; CHECK-NEXT: Member: {%A,+,4}<nuw><%loop.header>
586+
; CHECK-EMPTY:
587+
; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop.
588+
; CHECK-NEXT: SCEV assumptions:
589+
; CHECK-EMPTY:
590+
; CHECK-NEXT: Expressions re-written:
591+
;
592+
entry:
593+
call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %A, i64 1999) ]
594+
call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %B, i64 1999) ]
595+
br label %loop.header
596+
597+
loop.header:
598+
%iv = phi i64 [ 0, %entry ], [ %iv.next, %latch ]
599+
%gep.A = getelementptr inbounds i32, ptr %A, i64 %iv
600+
%gep.B = getelementptr inbounds i32, ptr %B, i64 %iv
601+
%l = load i32, ptr %gep.A, align 4
602+
store i32 0, ptr %gep.B, align 4
603+
%cntable.c.1 = icmp ult i64 %iv, 1000
604+
%iv.next = add nuw nsw i64 %iv, 1
605+
br i1 %cntable.c.1, label %b2, label %e.1
606+
607+
b2:
608+
%uncntable.c.0 = icmp eq i32 %l, 0
609+
br i1 %uncntable.c.0, label %e.2, label %b3
610+
611+
b3:
612+
%cntable.c.2 = icmp eq i64 %iv.next, 500
613+
br i1 %cntable.c.2, label %cleanup4, label %latch
614+
615+
latch:
616+
br label %loop.header
617+
618+
cleanup4:
619+
ret void
620+
621+
e.1:
622+
ret void
623+
624+
e.2:
625+
ret void
626+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals none --version 4
2+
; RUN: opt -S < %s -p loop-vectorize -enable-early-exit-vectorization -force-vector-width=4 | FileCheck %s
3+
4+
define i64 @early_exit_alignment_and_deref_known_via_assumption_with_constant_size(ptr noalias %p1, ptr noalias %p2) nofree nosync {
5+
; CHECK-LABEL: define i64 @early_exit_alignment_and_deref_known_via_assumption_with_constant_size(
6+
; CHECK-SAME: ptr noalias [[P1:%.*]], ptr noalias [[P2:%.*]]) #[[ATTR0:[0-9]+]] {
7+
; CHECK-NEXT: entry:
8+
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[P1]], i64 4), "dereferenceable"(ptr [[P1]], i64 1024) ]
9+
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[P2]], i64 4), "dereferenceable"(ptr [[P2]], i64 1024) ]
10+
; CHECK-NEXT: br label [[LOOP:%.*]]
11+
; CHECK: loop:
12+
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ [[INDEX_NEXT:%.*]], [[LOOP_INC:%.*]] ], [ 0, [[ENTRY:%.*]] ]
13+
; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i8, ptr [[P1]], i64 [[INDEX]]
14+
; CHECK-NEXT: [[LD1:%.*]] = load i8, ptr [[ARRAYIDX]], align 1
15+
; CHECK-NEXT: [[ARRAYIDX1:%.*]] = getelementptr inbounds i8, ptr [[P2]], i64 [[INDEX]]
16+
; CHECK-NEXT: [[LD2:%.*]] = load i8, ptr [[ARRAYIDX1]], align 1
17+
; CHECK-NEXT: [[CMP3:%.*]] = icmp eq i8 [[LD1]], [[LD2]]
18+
; CHECK-NEXT: br i1 [[CMP3]], label [[LOOP_INC]], label [[LOOP_END:%.*]]
19+
; CHECK: loop.inc:
20+
; CHECK-NEXT: [[INDEX_NEXT]] = add i64 [[INDEX]], 1
21+
; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i64 [[INDEX_NEXT]], 1024
22+
; CHECK-NEXT: br i1 [[EXITCOND]], label [[LOOP]], label [[LOOP_END]]
23+
; CHECK: loop.end:
24+
; CHECK-NEXT: [[RETVAL:%.*]] = phi i64 [ [[INDEX]], [[LOOP]] ], [ -1, [[LOOP_INC]] ]
25+
; CHECK-NEXT: ret i64 [[RETVAL]]
26+
;
27+
entry:
28+
call void @llvm.assume(i1 true) [ "align"(ptr %p1, i64 4), "dereferenceable"(ptr %p1, i64 1024) ]
29+
call void @llvm.assume(i1 true) [ "align"(ptr %p2, i64 4), "dereferenceable"(ptr %p2, i64 1024) ]
30+
br label %loop
31+
32+
loop:
33+
%index = phi i64 [ %index.next, %loop.inc ], [ 0, %entry ]
34+
%arrayidx = getelementptr inbounds i8, ptr %p1, i64 %index
35+
%ld1 = load i8, ptr %arrayidx, align 1
36+
%arrayidx1 = getelementptr inbounds i8, ptr %p2, i64 %index
37+
%ld2 = load i8, ptr %arrayidx1, align 1
38+
%cmp3 = icmp eq i8 %ld1, %ld2
39+
br i1 %cmp3, label %loop.inc, label %loop.end
40+
41+
loop.inc:
42+
%index.next = add i64 %index, 1
43+
%exitcond = icmp ne i64 %index.next, 1024
44+
br i1 %exitcond, label %loop, label %loop.end
45+
46+
loop.end:
47+
%retval = phi i64 [ %index, %loop ], [ -1, %loop.inc ]
48+
ret i64 %retval
49+
}
50+
51+
define i64 @early_exit_alignment_and_deref_via_assumption_with_constant_size_too_small(ptr noalias %p1, ptr noalias %p2) nofree nosync {
52+
; CHECK-LABEL: define i64 @early_exit_alignment_and_deref_via_assumption_with_constant_size_too_small(
53+
; CHECK-SAME: ptr noalias [[P1:%.*]], ptr noalias [[P2:%.*]]) #[[ATTR0]] {
54+
; CHECK-NEXT: entry:
55+
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[P1]], i64 4), "dereferenceable"(ptr [[P1]], i64 1024) ]
56+
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[P2]], i64 4), "dereferenceable"(ptr [[P2]], i64 1024) ]
57+
; CHECK-NEXT: br label [[LOOP:%.*]]
58+
; CHECK: loop:
59+
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ [[INDEX_NEXT:%.*]], [[LOOP_INC:%.*]] ], [ 0, [[ENTRY:%.*]] ]
60+
; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i8, ptr [[P1]], i64 [[INDEX]]
61+
; CHECK-NEXT: [[LD1:%.*]] = load i8, ptr [[ARRAYIDX]], align 1
62+
; CHECK-NEXT: [[ARRAYIDX1:%.*]] = getelementptr inbounds i8, ptr [[P2]], i64 [[INDEX]]
63+
; CHECK-NEXT: [[LD2:%.*]] = load i8, ptr [[ARRAYIDX1]], align 1
64+
; CHECK-NEXT: [[CMP3:%.*]] = icmp eq i8 [[LD1]], [[LD2]]
65+
; CHECK-NEXT: br i1 [[CMP3]], label [[LOOP_INC]], label [[LOOP_END:%.*]]
66+
; CHECK: loop.inc:
67+
; CHECK-NEXT: [[INDEX_NEXT]] = add i64 [[INDEX]], 1
68+
; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i64 [[INDEX_NEXT]], 1025
69+
; CHECK-NEXT: br i1 [[EXITCOND]], label [[LOOP]], label [[LOOP_END]]
70+
; CHECK: loop.end:
71+
; CHECK-NEXT: [[RETVAL:%.*]] = phi i64 [ [[INDEX]], [[LOOP]] ], [ -1, [[LOOP_INC]] ]
72+
; CHECK-NEXT: ret i64 [[RETVAL]]
73+
;
74+
entry:
75+
call void @llvm.assume(i1 true) [ "align"(ptr %p1, i64 4), "dereferenceable"(ptr %p1, i64 1024) ]
76+
call void @llvm.assume(i1 true) [ "align"(ptr %p2, i64 4), "dereferenceable"(ptr %p2, i64 1024) ]
77+
br label %loop
78+
79+
loop:
80+
%index = phi i64 [ %index.next, %loop.inc ], [ 0, %entry ]
81+
%arrayidx = getelementptr inbounds i8, ptr %p1, i64 %index
82+
%ld1 = load i8, ptr %arrayidx, align 1
83+
%arrayidx1 = getelementptr inbounds i8, ptr %p2, i64 %index
84+
%ld2 = load i8, ptr %arrayidx1, align 1
85+
%cmp3 = icmp eq i8 %ld1, %ld2
86+
br i1 %cmp3, label %loop.inc, label %loop.end
87+
88+
loop.inc:
89+
%index.next = add i64 %index, 1
90+
%exitcond = icmp ne i64 %index.next, 1025
91+
br i1 %exitcond, label %loop, label %loop.end
92+
93+
loop.end:
94+
%retval = phi i64 [ %index, %loop ], [ -1, %loop.inc ]
95+
ret i64 %retval
96+
}

0 commit comments

Comments
 (0)