Files
clang-p2996/llvm/test/Transforms/LoopUnroll/multiple-exits.ll
Philip Reames de2fed6152 [unroll] Keep unrolled iterations with initial iteration
The unrolling code was previously inserting new cloned blocks at the end of the function.  The result of this with typical loop structures is that the new iterations are placed far from the initial iteration.

With unrolling, the general assumption is that the a) the loop is reasonable hot, and b) the first Count-1 copies of the loop are rarely (if ever) loop exiting.  As such, placing Count-1 copies out of line is a fairly poor code placement choice.  We'd much rather fall through into the hot (non-exiting) path.  For code with branch profiles, later layout would fix this, but this may have a positive impact on non-PGO compiled code.

However, the real motivation for this change isn't performance.  Its readability and human understanding.  Having to jump around long distances in an IR file to trace an unrolled loop structure is error prone and tedious.
2021-11-12 11:40:50 -08:00

220 lines
7.2 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -loop-unroll -S < %s | FileCheck %s
declare void @bar()
define void @test1() {
; CHECK-LABEL: @test1(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LATCH:%.*]]
; CHECK: latch:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LATCH_1:%.*]]
; CHECK: latch.1:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LATCH_2:%.*]]
; CHECK: latch.2:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LATCH_3:%.*]]
; CHECK: latch.3:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LATCH_4:%.*]]
; CHECK: latch.4:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LATCH_5:%.*]]
; CHECK: latch.5:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LATCH_6:%.*]]
; CHECK: latch.6:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LATCH_7:%.*]]
; CHECK: latch.7:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LATCH_8:%.*]]
; CHECK: latch.8:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LATCH_9:%.*]]
; CHECK: latch.9:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br i1 false, label [[LATCH_10:%.*]], label [[EXIT:%.*]]
; CHECK: latch.10:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
br label %loop
loop:
%iv = phi i64 [0, %entry], [%iv.next, %latch]
%iv.next = add i64 %iv, 1
call void @bar()
%cmp1 = icmp ult i64 %iv, 10
br i1 %cmp1, label %latch, label %exit
latch:
call void @bar()
%cmp2 = icmp ult i64 %iv, 20
br i1 %cmp2, label %loop, label %exit
exit:
ret void
}
; Fully unroll this loop by 10, but leave the unrolled latch
; tests since we don't know if %N < 10, and break the backedge.
define void @test2(i64 %N) {
; CHECK-LABEL: @test2(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LATCH:%.*]]
; CHECK: latch:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br i1 true, label [[LOOP_1:%.*]], label [[EXIT:%.*]]
; CHECK: loop.1:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LATCH_1:%.*]]
; CHECK: latch.1:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: [[CMP2_1:%.*]] = icmp ule i64 1, [[N:%.*]]
; CHECK-NEXT: br i1 [[CMP2_1]], label [[LOOP_2:%.*]], label [[EXIT]]
; CHECK: loop.2:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LATCH_2:%.*]]
; CHECK: latch.2:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: [[CMP2_2:%.*]] = icmp ule i64 2, [[N]]
; CHECK-NEXT: br i1 [[CMP2_2]], label [[LOOP_3:%.*]], label [[EXIT]]
; CHECK: loop.3:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LATCH_3:%.*]]
; CHECK: latch.3:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: [[CMP2_3:%.*]] = icmp ule i64 3, [[N]]
; CHECK-NEXT: br i1 [[CMP2_3]], label [[LOOP_4:%.*]], label [[EXIT]]
; CHECK: loop.4:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LATCH_4:%.*]]
; CHECK: latch.4:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: [[CMP2_4:%.*]] = icmp ule i64 4, [[N]]
; CHECK-NEXT: br i1 [[CMP2_4]], label [[LOOP_5:%.*]], label [[EXIT]]
; CHECK: loop.5:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LATCH_5:%.*]]
; CHECK: latch.5:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: [[CMP2_5:%.*]] = icmp ule i64 5, [[N]]
; CHECK-NEXT: br i1 [[CMP2_5]], label [[LOOP_6:%.*]], label [[EXIT]]
; CHECK: loop.6:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LATCH_6:%.*]]
; CHECK: latch.6:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: [[CMP2_6:%.*]] = icmp ule i64 6, [[N]]
; CHECK-NEXT: br i1 [[CMP2_6]], label [[LOOP_7:%.*]], label [[EXIT]]
; CHECK: loop.7:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LATCH_7:%.*]]
; CHECK: latch.7:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: [[CMP2_7:%.*]] = icmp ule i64 7, [[N]]
; CHECK-NEXT: br i1 [[CMP2_7]], label [[LOOP_8:%.*]], label [[EXIT]]
; CHECK: loop.8:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LATCH_8:%.*]]
; CHECK: latch.8:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: [[CMP2_8:%.*]] = icmp ule i64 8, [[N]]
; CHECK-NEXT: br i1 [[CMP2_8]], label [[LOOP_9:%.*]], label [[EXIT]]
; CHECK: loop.9:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LATCH_9:%.*]]
; CHECK: latch.9:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: [[CMP2_9:%.*]] = icmp ule i64 9, [[N]]
; CHECK-NEXT: br i1 [[CMP2_9]], label [[LOOP_10:%.*]], label [[EXIT]]
; CHECK: loop.10:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LATCH_10:%.*]]
; CHECK: latch.10:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: [[CMP2_10:%.*]] = icmp ule i64 10, [[N]]
; CHECK-NEXT: br i1 [[CMP2_10]], label [[LOOP_11:%.*]], label [[EXIT]]
; CHECK: loop.11:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br i1 false, label [[LATCH_11:%.*]], label [[EXIT]]
; CHECK: latch.11:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
br label %loop
loop:
%iv = phi i64 [0, %entry], [%iv.next, %latch]
%iv.next = add i64 %iv, 1
call void @bar()
%cmp1 = icmp ule i64 %iv, 10
br i1 %cmp1, label %latch, label %exit
latch:
call void @bar()
%cmp2 = icmp ule i64 %iv, %N
br i1 %cmp2, label %loop, label %exit
exit:
ret void
}
; TODO: We could partially unroll this by 2.
define void @test3(i64 %N, i64 %M) {
; CHECK-LABEL: @test3(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[N_MASKED:%.*]] = and i64 [[N:%.*]], 65520
; CHECK-NEXT: [[M_MASKED:%.*]] = and i64 [[M:%.*]], 65520
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LATCH:%.*]] ]
; CHECK-NEXT: [[IV_NEXT]] = add i64 [[IV]], 1
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: [[CMP1:%.*]] = icmp ule i64 [[IV]], [[N_MASKED]]
; CHECK-NEXT: br i1 [[CMP1]], label [[LATCH]], label [[EXIT:%.*]]
; CHECK: latch:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: [[CMP2:%.*]] = icmp ule i64 [[IV]], [[M_MASKED]]
; CHECK-NEXT: br i1 [[CMP2]], label [[LOOP]], label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
%N.masked = and i64 %N, 65520 ; 0xfff0
%M.masked = and i64 %M, 65520 ; 0xfff0
br label %loop
loop:
%iv = phi i64 [0, %entry], [%iv.next, %latch]
%iv.next = add i64 %iv, 1
call void @bar()
%cmp1 = icmp ule i64 %iv, %N.masked
br i1 %cmp1, label %latch, label %exit
latch:
call void @bar()
%cmp2 = icmp ule i64 %iv, %M.masked
br i1 %cmp2, label %loop, label %exit
exit:
ret void
}