If we determine that the invariant path through the loop has no effects, we can directly branch to the exit block, instead to unswitching first. Besides avoiding some extra work (unswitching first, then deleting the loop again) this allows to be more aggressive than regular unswitching with respect to cost-modeling. This approach should always be be desirable. This is similar in spirit to D93734, just that it uses the previously added checks for loop-unswitching. I tried to add the required no-op checks from scratch, as we only check a subset of the loop. There is potential to unify the checks with LoopDeletion, at the cost of adding a predicate whether a block should be considered. Reviewed By: jdoerfert Differential Revision: https://reviews.llvm.org/D95468
427 lines
9.9 KiB
LLVM
427 lines
9.9 KiB
LLVM
; RUN: opt -loop-unswitch -loop-unswitch-threshold=10 -verify-dom-info -verify-memoryssa -S -enable-new-pm=0 %s | FileCheck %s
|
|
|
|
declare void @clobber()
|
|
|
|
; Test cases for partial unswitching, where the regular cost-model overestimates
|
|
; the cost of unswitching, because it misses the fact that the unswitched paths
|
|
; are no-ops.
|
|
|
|
|
|
define i32 @no_partial_unswitch_size_too_large_no_mustprogress(i32* %ptr, i32 %N) {
|
|
; CHECK-LABEL: @no_partial_unswitch_size_too_large_no_mustprogress
|
|
; CHECK-LABEL: entry:
|
|
; CHECK-NEXT: br label %loop.header
|
|
;
|
|
entry:
|
|
br label %loop.header
|
|
|
|
loop.header:
|
|
%iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
|
|
%lv = load i32, i32* %ptr
|
|
%sc = icmp eq i32 %lv, 100
|
|
br i1 %sc, label %noclobber, label %clobber
|
|
|
|
noclobber:
|
|
br label %loop.latch
|
|
|
|
clobber:
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
br label %loop.latch
|
|
|
|
loop.latch:
|
|
%c = icmp ult i32 %iv, %N
|
|
%iv.next = add i32 %iv, 1
|
|
br i1 %c, label %loop.header, label %exit
|
|
|
|
exit:
|
|
ret i32 10
|
|
}
|
|
|
|
define i32 @partial_unswitch_shortcut_mustprogress(i32* %ptr, i32 %N) mustprogress {
|
|
; CHECK-LABEL: @partial_unswitch_shortcut_mustprogress
|
|
; CHECK-LABEL: entry:
|
|
; CHECK-NEXT: [[LV:%[0-9]+]] = load i32, i32* %ptr, align 4
|
|
; CHECK-NEXT: [[C:%[0-9]+]] = icmp eq i32 [[LV]], 100
|
|
; CHECK-NEXT: br i1 [[C]], label %[[CRIT_TO_EXIT:[a-z._]+]], label %[[CRIT_TO_HEADER:[a-z._]+]]
|
|
;
|
|
; CHECK: [[CRIT_TO_HEADER]]:
|
|
; CHECK-NEXT: br label %loop.header
|
|
;
|
|
; CHECK: [[CRIT_TO_EXIT]]:
|
|
; CHECK-NEXT: br label %exit
|
|
;
|
|
entry:
|
|
br label %loop.header
|
|
|
|
loop.header:
|
|
%iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
|
|
%lv = load i32, i32* %ptr
|
|
%sc = icmp eq i32 %lv, 100
|
|
br i1 %sc, label %noclobber, label %clobber
|
|
|
|
noclobber:
|
|
br label %loop.latch
|
|
|
|
clobber:
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
br label %loop.latch
|
|
|
|
loop.latch:
|
|
%c = icmp ult i32 %iv, %N
|
|
%iv.next = add i32 %iv, 1
|
|
br i1 %c, label %loop.header, label %exit
|
|
|
|
exit:
|
|
ret i32 10
|
|
}
|
|
|
|
define i32 @partial_unswitch_shortcut_mustprogress_single_exit_on_path(i32* %ptr, i32 %N) mustprogress {
|
|
; CHECK-LABEL: @partial_unswitch_shortcut_mustprogress_single_exit_on_path
|
|
; CHECK-LABEL: entry:
|
|
; CHECK-NEXT: [[LV:%[0-9]+]] = load i32, i32* %ptr, align 4
|
|
; CHECK-NEXT: [[C:%[0-9]+]] = icmp eq i32 [[LV]], 100
|
|
; CHECK-NEXT: br i1 [[C]], label %[[CRIT_TO_EXIT:.+]], label %[[CRIT_TO_HEADER:[a-z._]+]]
|
|
;
|
|
; CHECK: [[CRIT_TO_HEADER]]:
|
|
; CHECK-NEXT: br label %loop.header
|
|
;
|
|
; CHECK: [[CRIT_TO_EXIT]]:
|
|
; CHECK-NEXT: br label %exit
|
|
;
|
|
entry:
|
|
br label %loop.header
|
|
|
|
loop.header:
|
|
%iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
|
|
%lv = load i32, i32* %ptr
|
|
%sc = icmp eq i32 %lv, 100
|
|
br i1 %sc, label %noclobber, label %clobber
|
|
|
|
noclobber:
|
|
%c.1 = icmp ult i32 %iv, 123
|
|
br i1 %c.1, label %loop.latch, label %exit.1
|
|
|
|
clobber:
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
%c = icmp ult i32 %iv, %N
|
|
br i1 %c, label %loop.latch, label %exit.2
|
|
|
|
loop.latch:
|
|
%iv.next = add i32 %iv, 1
|
|
br label %loop.header
|
|
|
|
exit.1:
|
|
ret i32 10
|
|
|
|
exit.2:
|
|
ret i32 10
|
|
}
|
|
|
|
define i32 @no_partial_unswitch_shortcut_mustprogress_no_exit_on_path(i32* %ptr, i32 %N) mustprogress {
|
|
; CHECK-LABEL: @no_partial_unswitch_shortcut_mustprogress_no_exit_on_path
|
|
; CHECK-LABEL: entry:
|
|
; CHECK-NEXT: br label %loop.header
|
|
;
|
|
entry:
|
|
br label %loop.header
|
|
|
|
loop.header:
|
|
%iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
|
|
%lv = load i32, i32* %ptr
|
|
%sc = icmp eq i32 %lv, 100
|
|
br i1 %sc, label %noclobber, label %clobber
|
|
|
|
noclobber:
|
|
br label %loop.latch
|
|
|
|
clobber:
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
%c = icmp ult i32 %iv, %N
|
|
br i1 %c, label %loop.latch, label %exit
|
|
|
|
loop.latch:
|
|
%iv.next = add i32 %iv, 1
|
|
br label %loop.header
|
|
|
|
exit:
|
|
ret i32 10
|
|
}
|
|
|
|
define i32 @no_partial_unswitch_shortcut_mustprogress_exit_value_used(i32* %ptr, i32 %N) mustprogress {
|
|
; CHECK-LABEL: @no_partial_unswitch_shortcut_mustprogress_exit_value_use
|
|
; CHECK-LABEL: entry:
|
|
; CHECK-NEXT: br label %loop.header
|
|
;
|
|
entry:
|
|
br label %loop.header
|
|
|
|
loop.header:
|
|
%iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
|
|
%red = phi i32 [ 0, %entry ], [ %red.next, %loop.latch ]
|
|
%lv = load i32, i32* %ptr
|
|
%sc = icmp eq i32 %lv, 100
|
|
br i1 %sc, label %noclobber, label %clobber
|
|
|
|
noclobber:
|
|
%red.add = add i32 %red, %lv
|
|
br label %loop.latch
|
|
|
|
clobber:
|
|
%red.mul = mul i32 %red, %lv
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
br label %loop.latch
|
|
|
|
loop.latch:
|
|
%red.next = phi i32 [ %red.add, %noclobber ], [ %red.mul, %clobber ]
|
|
%c = icmp ult i32 %iv, %N
|
|
%iv.next = add i32 %iv, 1
|
|
br i1 %c, label %loop.header, label %exit
|
|
|
|
exit:
|
|
ret i32 %red.next
|
|
}
|
|
|
|
define i32 @partial_unswitch_shortcut_multiple_exiting_blocks(i32* %ptr, i32 %N, i1 %ec.1) mustprogress {
|
|
; CHECK-LABEL: @partial_unswitch_shortcut_multiple_exiting_blocks
|
|
; CHECK-LABEL: entry:
|
|
; CHECK-NEXT: [[LV:%[0-9]+]] = load i32, i32* %ptr, align 4
|
|
; CHECK-NEXT: [[C:%[0-9]+]] = icmp eq i32 [[LV]], 100
|
|
; CHECK-NEXT: br i1 [[C]], label %[[CRIT_TO_EXIT:.+]], label %[[CRIT_TO_HEADER:[a-z._]+]]
|
|
;
|
|
; CHECK: [[CRIT_TO_HEADER]]:
|
|
; CHECK-NEXT: br label %loop.header
|
|
;
|
|
; CHECK: [[CRIT_TO_EXIT]]:
|
|
; CHECK-NEXT: br label %exit
|
|
;
|
|
entry:
|
|
br label %loop.header
|
|
|
|
loop.header:
|
|
%iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
|
|
%lv = load i32, i32* %ptr
|
|
%sc = icmp eq i32 %lv, 100
|
|
br i1 %sc, label %noclobber, label %clobber
|
|
|
|
noclobber:
|
|
br i1 %ec.1, label %loop.latch, label %exit
|
|
|
|
clobber:
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
br label %loop.latch
|
|
|
|
loop.latch:
|
|
%c = icmp ult i32 %iv, %N
|
|
%iv.next = add i32 %iv, 1
|
|
br i1 %c, label %loop.header, label %exit
|
|
|
|
exit:
|
|
ret i32 10
|
|
}
|
|
|
|
define i32 @no_partial_unswitch_shortcut_multiple_exit_blocks(i32* %ptr, i32 %N, i1 %ec.1) mustprogress {
|
|
; CHECK-LABEL: @no_partial_unswitch_shortcut_multiple_exit_blocks
|
|
; CHECK-LABEL: entry:
|
|
; CHECK-NEXT: br label %loop.header
|
|
;
|
|
entry:
|
|
br label %loop.header
|
|
|
|
loop.header:
|
|
%iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
|
|
%lv = load i32, i32* %ptr
|
|
%sc = icmp eq i32 %lv, 100
|
|
br i1 %sc, label %noclobber, label %clobber
|
|
|
|
noclobber:
|
|
br i1 %ec.1, label %loop.latch, label %exit.2
|
|
|
|
clobber:
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
br label %loop.latch
|
|
|
|
loop.latch:
|
|
%c = icmp ult i32 %iv, %N
|
|
%iv.next = add i32 %iv, 1
|
|
br i1 %c, label %loop.header, label %exit.1
|
|
|
|
exit.1:
|
|
ret i32 10
|
|
|
|
exit.2:
|
|
ret i32 20
|
|
}
|
|
|
|
define i32 @no_partial_unswitch_shortcut_mustprogress_store(i32* %ptr, i32* noalias %dst, i32 %N) mustprogress {
|
|
; CHECK-LABEL: @no_partial_unswitch_shortcut_mustprogress_store
|
|
; CHECK-LABEL: entry:
|
|
; CHECK-NEXT: br label %loop.header
|
|
;
|
|
entry:
|
|
br label %loop.header
|
|
|
|
loop.header:
|
|
%iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
|
|
%lv = load i32, i32* %ptr
|
|
%sc = icmp eq i32 %lv, 100
|
|
br i1 %sc, label %noclobber, label %clobber
|
|
|
|
noclobber:
|
|
store i32 0, i32* %dst
|
|
br label %loop.latch
|
|
|
|
clobber:
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
br label %loop.latch
|
|
|
|
loop.latch:
|
|
%c = icmp ult i32 %iv, %N
|
|
%iv.next = add i32 %iv, 1
|
|
br i1 %c, label %loop.header, label %exit
|
|
|
|
exit:
|
|
ret i32 10
|
|
}
|
|
|
|
define i32 @no_partial_unswitch_shortcut_mustprogress_store2(i32* %ptr, i32* noalias %dst, i32 %N) mustprogress {
|
|
; CHECK-LABEL: @no_partial_unswitch_shortcut_mustprogress_store
|
|
; CHECK-LABEL: entry:
|
|
; CHECK-NEXT: br label %loop.header
|
|
;
|
|
entry:
|
|
br label %loop.header
|
|
|
|
loop.header:
|
|
%iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
|
|
%lv = load i32, i32* %ptr
|
|
%sc = icmp eq i32 %lv, 100
|
|
br i1 %sc, label %noclobber, label %clobber
|
|
|
|
noclobber:
|
|
br label %loop.latch
|
|
|
|
clobber:
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
br label %loop.latch
|
|
|
|
loop.latch:
|
|
store i32 0, i32* %dst
|
|
%c = icmp ult i32 %iv, %N
|
|
%iv.next = add i32 %iv, 1
|
|
br i1 %c, label %loop.header, label %exit
|
|
|
|
exit:
|
|
ret i32 10
|
|
}
|
|
|
|
define i32 @no_partial_unswitch_shortcut_mustprogress_store3(i32* %ptr, i32* noalias %dst, i32 %N) mustprogress {
|
|
; CHECK-LABEL: @no_partial_unswitch_shortcut_mustprogress_store
|
|
; CHECK-LABEL: entry:
|
|
; CHECK-NEXT: br label %loop.header
|
|
;
|
|
entry:
|
|
br label %loop.header
|
|
|
|
loop.header:
|
|
%iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
|
|
store i32 0, i32* %dst
|
|
%lv = load i32, i32* %ptr
|
|
%sc = icmp eq i32 %lv, 100
|
|
br i1 %sc, label %noclobber, label %clobber
|
|
|
|
noclobber:
|
|
br label %loop.latch
|
|
|
|
clobber:
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
call void @clobber()
|
|
br label %loop.latch
|
|
|
|
loop.latch:
|
|
%c = icmp ult i32 %iv, %N
|
|
%iv.next = add i32 %iv, 1
|
|
br i1 %c, label %loop.header, label %exit
|
|
|
|
exit:
|
|
ret i32 10
|
|
}
|