[llvm] 07142b3 - [SimpleLoopUnswitch] Add tests to check partially invariant unswitch
Jingu Kang via llvm-commits
llvm-commits at lists.llvm.org
Mon Mar 29 05:07:52 PDT 2021
Author: Jingu Kang
Date: 2021-03-29T13:06:32+01:00
New Revision: 07142b304004a4bfe729363ee41d247a2b6d413d
URL: https://github.com/llvm/llvm-project/commit/07142b304004a4bfe729363ee41d247a2b6d413d
DIFF: https://github.com/llvm/llvm-project/commit/07142b304004a4bfe729363ee41d247a2b6d413d.diff
LOG: [SimpleLoopUnswitch] Add tests to check partially invariant unswitch
Differential Revision: https://reviews.llvm.org/D99493
Added:
llvm/test/Transforms/SimpleLoopUnswitch/partial-unswitch.ll
Modified:
Removed:
################################################################################
diff --git a/llvm/test/Transforms/SimpleLoopUnswitch/partial-unswitch.ll b/llvm/test/Transforms/SimpleLoopUnswitch/partial-unswitch.ll
new file mode 100644
index 000000000000..4343efa3221c
--- /dev/null
+++ b/llvm/test/Transforms/SimpleLoopUnswitch/partial-unswitch.ll
@@ -0,0 +1,1102 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -passes='loop-mssa(unswitch<nontrivial>),verify<loops>' -S < %s | FileCheck %s
+
+declare void @clobber()
+
+define i32 @partial_unswitch_true_successor(i32* %ptr, i32 %N) {
+; CHECK-LABEL: @partial_unswitch_true_successor(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[PTR:%.*]], align 4
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[TMP0]], 100
+; CHECK-NEXT: br i1 [[TMP1]], label [[ENTRY_SPLIT_US:%.*]], label [[ENTRY_SPLIT:%.*]]
+; CHECK: entry.split.us:
+; CHECK-NEXT: br label [[LOOP_HEADER_US:%.*]]
+; CHECK: loop.header.us:
+; CHECK-NEXT: [[IV_US:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT_US]] ], [ [[IV_NEXT_US:%.*]], [[LOOP_LATCH_US:%.*]] ]
+; CHECK-NEXT: br label [[NOCLOBBER_US:%.*]]
+; CHECK: noclobber.us:
+; CHECK-NEXT: br label [[LOOP_LATCH_US]]
+; CHECK: loop.latch.us:
+; CHECK-NEXT: [[C_US:%.*]] = icmp ult i32 [[IV_US]], [[N:%.*]]
+; CHECK-NEXT: [[IV_NEXT_US]] = add i32 [[IV_US]], 1
+; CHECK-NEXT: br i1 [[C_US]], label [[LOOP_HEADER_US]], label [[EXIT_SPLIT_US:%.*]]
+; CHECK: exit.split.us:
+; CHECK-NEXT: br label [[EXIT:%.*]]
+; CHECK: entry.split:
+; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
+; CHECK: loop.header:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[LV:%.*]] = load i32, i32* [[PTR]], align 4
+; CHECK-NEXT: [[SC:%.*]] = icmp eq i32 [[LV]], 100
+; CHECK-NEXT: br i1 [[SC]], label [[NOCLOBBER:%.*]], label [[CLOBBER:%.*]]
+; CHECK: noclobber:
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: clobber:
+; CHECK-NEXT: call void @clobber()
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[IV]], [[N]]
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: br i1 [[C]], label [[LOOP_HEADER]], label [[EXIT_SPLIT:%.*]]
+; CHECK: exit.split:
+; CHECK-NEXT: br label [[EXIT]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 10
+;
+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()
+ 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_false_successor(i32* %ptr, i32 %N) {
+; CHECK-LABEL: @partial_unswitch_false_successor(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[PTR:%.*]], align 4
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[TMP0]], 100
+; CHECK-NEXT: br i1 [[TMP1]], label [[ENTRY_SPLIT:%.*]], label [[ENTRY_SPLIT_US:%.*]]
+; CHECK: entry.split.us:
+; CHECK-NEXT: br label [[LOOP_HEADER_US:%.*]]
+; CHECK: loop.header.us:
+; CHECK-NEXT: [[IV_US:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT_US]] ], [ [[IV_NEXT_US:%.*]], [[LOOP_LATCH_US:%.*]] ]
+; CHECK-NEXT: br label [[NOCLOBBER_US:%.*]]
+; CHECK: noclobber.us:
+; CHECK-NEXT: br label [[LOOP_LATCH_US]]
+; CHECK: loop.latch.us:
+; CHECK-NEXT: [[C_US:%.*]] = icmp ult i32 [[IV_US]], [[N:%.*]]
+; CHECK-NEXT: [[IV_NEXT_US]] = add i32 [[IV_US]], 1
+; CHECK-NEXT: br i1 [[C_US]], label [[LOOP_HEADER_US]], label [[EXIT_SPLIT_US:%.*]]
+; CHECK: exit.split.us:
+; CHECK-NEXT: br label [[EXIT:%.*]]
+; CHECK: entry.split:
+; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
+; CHECK: loop.header:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[LV:%.*]] = load i32, i32* [[PTR]], align 4
+; CHECK-NEXT: [[SC:%.*]] = icmp eq i32 [[LV]], 100
+; CHECK-NEXT: br i1 [[SC]], label [[CLOBBER:%.*]], label [[NOCLOBBER:%.*]]
+; CHECK: clobber:
+; CHECK-NEXT: call void @clobber()
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: noclobber:
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[IV]], [[N]]
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: br i1 [[C]], label [[LOOP_HEADER]], label [[EXIT_SPLIT:%.*]]
+; CHECK: exit.split:
+; CHECK-NEXT: br label [[EXIT]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 10
+;
+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 %clobber, label %noclobber
+
+clobber:
+ call void @clobber()
+ br label %loop.latch
+
+noclobber:
+ 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_unswtich_gep_load_icmp(i32** %ptr, i32 %N) {
+; CHECK-LABEL: @partial_unswtich_gep_load_icmp(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32*, i32** [[PTR:%.*]], i32 1
+; CHECK-NEXT: [[TMP1:%.*]] = load i32*, i32** [[TMP0]], align 8
+; CHECK-NEXT: [[TMP2:%.*]] = load i32, i32* [[TMP1]], align 4
+; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 100
+; CHECK-NEXT: br i1 [[TMP3]], label [[ENTRY_SPLIT_US:%.*]], label [[ENTRY_SPLIT:%.*]]
+; CHECK: entry.split.us:
+; CHECK-NEXT: br label [[LOOP_HEADER_US:%.*]]
+; CHECK: loop.header.us:
+; CHECK-NEXT: [[IV_US:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT_US]] ], [ [[IV_NEXT_US:%.*]], [[LOOP_LATCH_US:%.*]] ]
+; CHECK-NEXT: br label [[NOCLOBBER_US:%.*]]
+; CHECK: noclobber.us:
+; CHECK-NEXT: br label [[LOOP_LATCH_US]]
+; CHECK: loop.latch.us:
+; CHECK-NEXT: [[C_US:%.*]] = icmp ult i32 [[IV_US]], [[N:%.*]]
+; CHECK-NEXT: [[IV_NEXT_US]] = add i32 [[IV_US]], 1
+; CHECK-NEXT: br i1 [[C_US]], label [[LOOP_HEADER_US]], label [[EXIT_SPLIT_US:%.*]]
+; CHECK: exit.split.us:
+; CHECK-NEXT: br label [[EXIT:%.*]]
+; CHECK: entry.split:
+; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
+; CHECK: loop.header:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr i32*, i32** [[PTR]], i32 1
+; CHECK-NEXT: [[LV_1:%.*]] = load i32*, i32** [[GEP]], align 8
+; CHECK-NEXT: [[LV:%.*]] = load i32, i32* [[LV_1]], align 4
+; CHECK-NEXT: [[SC:%.*]] = icmp eq i32 [[LV]], 100
+; CHECK-NEXT: br i1 [[SC]], label [[NOCLOBBER:%.*]], label [[CLOBBER:%.*]]
+; CHECK: noclobber:
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: clobber:
+; CHECK-NEXT: call void @clobber()
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[IV]], [[N]]
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: br i1 [[C]], label [[LOOP_HEADER]], label [[EXIT_SPLIT:%.*]]
+; CHECK: exit.split:
+; CHECK-NEXT: br label [[EXIT]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 10
+;
+entry:
+ br label %loop.header
+
+loop.header:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %gep = getelementptr i32*, i32** %ptr, i32 1
+ %lv.1 = load i32*, i32** %gep
+ %lv = load i32, i32* %lv.1
+ %sc = icmp eq i32 %lv, 100
+ br i1 %sc, label %noclobber, label %clobber
+
+noclobber:
+ br label %loop.latch
+
+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_reduction_phi(i32* %ptr, i32 %N) {
+; CHECK-LABEL: @partial_unswitch_reduction_phi(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[PTR:%.*]], align 4
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[TMP0]], 100
+; CHECK-NEXT: br i1 [[TMP1]], label [[ENTRY_SPLIT:%.*]], label [[ENTRY_SPLIT_US:%.*]]
+; CHECK: entry.split.us:
+; CHECK-NEXT: br label [[LOOP_HEADER_US:%.*]]
+; CHECK: loop.header.us:
+; CHECK-NEXT: [[IV_US:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT_US]] ], [ [[IV_NEXT_US:%.*]], [[LOOP_LATCH_US:%.*]] ]
+; CHECK-NEXT: [[RED_US:%.*]] = phi i32 [ 20, [[ENTRY_SPLIT_US]] ], [ [[RED_NEXT_US:%.*]], [[LOOP_LATCH_US]] ]
+; CHECK-NEXT: br label [[NOCLOBBER_US:%.*]]
+; CHECK: noclobber.us:
+; CHECK-NEXT: [[ADD_10_US:%.*]] = add i32 [[RED_US]], 10
+; CHECK-NEXT: br label [[LOOP_LATCH_US]]
+; CHECK: loop.latch.us:
+; CHECK-NEXT: [[RED_NEXT_US]] = phi i32 [ [[ADD_10_US]], [[NOCLOBBER_US]] ]
+; CHECK-NEXT: [[C_US:%.*]] = icmp ult i32 [[IV_US]], [[N:%.*]]
+; CHECK-NEXT: [[IV_NEXT_US]] = add i32 [[IV_US]], 1
+; CHECK-NEXT: br i1 [[C_US]], label [[LOOP_HEADER_US]], label [[EXIT_SPLIT_US:%.*]]
+; CHECK: exit.split.us:
+; CHECK-NEXT: [[RED_NEXT_LCSSA_US:%.*]] = phi i32 [ [[RED_NEXT_US]], [[LOOP_LATCH_US]] ]
+; CHECK-NEXT: br label [[EXIT:%.*]]
+; CHECK: entry.split:
+; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
+; CHECK: loop.header:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[RED:%.*]] = phi i32 [ 20, [[ENTRY_SPLIT]] ], [ [[RED_NEXT:%.*]], [[LOOP_LATCH]] ]
+; CHECK-NEXT: [[LV:%.*]] = load i32, i32* [[PTR]], align 4
+; CHECK-NEXT: [[SC:%.*]] = icmp eq i32 [[LV]], 100
+; CHECK-NEXT: br i1 [[SC]], label [[CLOBBER:%.*]], label [[NOCLOBBER:%.*]]
+; CHECK: clobber:
+; CHECK-NEXT: call void @clobber()
+; CHECK-NEXT: [[ADD_5:%.*]] = add i32 [[RED]], 5
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: noclobber:
+; CHECK-NEXT: [[ADD_10:%.*]] = add i32 [[RED]], 10
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: [[RED_NEXT]] = phi i32 [ [[ADD_5]], [[CLOBBER]] ], [ [[ADD_10]], [[NOCLOBBER]] ]
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[IV]], [[N]]
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: br i1 [[C]], label [[LOOP_HEADER]], label [[EXIT_SPLIT:%.*]]
+; CHECK: exit.split:
+; CHECK-NEXT: [[RED_NEXT_LCSSA:%.*]] = phi i32 [ [[RED_NEXT]], [[LOOP_LATCH]] ]
+; CHECK-NEXT: br label [[EXIT]]
+; CHECK: exit:
+; CHECK-NEXT: [[DOTUS_PHI:%.*]] = phi i32 [ [[RED_NEXT_LCSSA]], [[EXIT_SPLIT]] ], [ [[RED_NEXT_LCSSA_US]], [[EXIT_SPLIT_US]] ]
+; CHECK-NEXT: ret i32 [[DOTUS_PHI]]
+;
+entry:
+ br label %loop.header
+
+loop.header:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %red = phi i32 [ 20, %entry ], [ %red.next, %loop.latch ]
+ %lv = load i32, i32* %ptr
+ %sc = icmp eq i32 %lv, 100
+ br i1 %sc, label %clobber, label %noclobber
+
+clobber:
+ call void @clobber()
+ %add.5 = add i32 %red, 5
+ br label %loop.latch
+
+noclobber:
+ %add.10 = add i32 %red, 10
+ br label %loop.latch
+
+loop.latch:
+ %red.next = phi i32 [ %add.5, %clobber ], [ %add.10, %noclobber ]
+ %c = icmp ult i32 %iv, %N
+ %iv.next = add i32 %iv, 1
+ br i1 %c, label %loop.header, label %exit
+
+exit:
+ %red.next.lcssa = phi i32 [ %red.next, %loop.latch ]
+ ret i32 %red.next.lcssa
+}
+
+; Partial unswitching is possible, because the store in %noclobber does not
+; alias the load of the condition.
+define i32 @partial_unswitch_true_successor_noclobber(i32* noalias %ptr.1, i32* noalias %ptr.2, i32 %N) {
+; CHECK-LABEL: @partial_unswitch_true_successor_noclobber(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[PTR_1:%.*]], align 4
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[TMP0]], 100
+; CHECK-NEXT: br i1 [[TMP1]], label [[ENTRY_SPLIT_US:%.*]], label [[ENTRY_SPLIT:%.*]]
+; CHECK: entry.split.us:
+; CHECK-NEXT: br label [[LOOP_HEADER_US:%.*]]
+; CHECK: loop.header.us:
+; CHECK-NEXT: [[IV_US:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT_US]] ], [ [[IV_NEXT_US:%.*]], [[LOOP_LATCH_US:%.*]] ]
+; CHECK-NEXT: [[LV_US:%.*]] = load i32, i32* [[PTR_1]], align 4
+; CHECK-NEXT: br label [[NOCLOBBER_US:%.*]]
+; CHECK: noclobber.us:
+; CHECK-NEXT: [[GEP_1_US:%.*]] = getelementptr i32, i32* [[PTR_2:%.*]], i32 [[IV_US]]
+; CHECK-NEXT: store i32 [[LV_US]], i32* [[GEP_1_US]], align 4
+; CHECK-NEXT: br label [[LOOP_LATCH_US]]
+; CHECK: loop.latch.us:
+; CHECK-NEXT: [[C_US:%.*]] = icmp ult i32 [[IV_US]], [[N:%.*]]
+; CHECK-NEXT: [[IV_NEXT_US]] = add i32 [[IV_US]], 1
+; CHECK-NEXT: br i1 [[C_US]], label [[LOOP_HEADER_US]], label [[EXIT_SPLIT_US:%.*]]
+; CHECK: exit.split.us:
+; CHECK-NEXT: br label [[EXIT:%.*]]
+; CHECK: entry.split:
+; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
+; CHECK: loop.header:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[LV:%.*]] = load i32, i32* [[PTR_1]], align 4
+; CHECK-NEXT: [[SC:%.*]] = icmp eq i32 [[LV]], 100
+; CHECK-NEXT: br i1 [[SC]], label [[NOCLOBBER:%.*]], label [[CLOBBER:%.*]]
+; CHECK: noclobber:
+; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr i32, i32* [[PTR_2]], i32 [[IV]]
+; CHECK-NEXT: store i32 [[LV]], i32* [[GEP_1]], align 4
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: clobber:
+; CHECK-NEXT: call void @clobber()
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[IV]], [[N]]
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: br i1 [[C]], label [[LOOP_HEADER]], label [[EXIT_SPLIT:%.*]]
+; CHECK: exit.split:
+; CHECK-NEXT: br label [[EXIT]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 10
+;
+entry:
+ br label %loop.header
+
+loop.header:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %lv = load i32, i32* %ptr.1
+ %sc = icmp eq i32 %lv, 100
+ br i1 %sc, label %noclobber, label %clobber
+
+noclobber:
+ %gep.1 = getelementptr i32, i32* %ptr.2, i32 %iv
+ store i32 %lv, i32* %gep.1
+ br label %loop.latch
+
+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 void @no_partial_unswitch_phi_cond(i1 %lc, i32 %N) {
+; CHECK-LABEL: @no_partial_unswitch_phi_cond(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
+; CHECK: loop.header:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[SC:%.*]] = phi i1 [ [[LC:%.*]], [[ENTRY]] ], [ true, [[LOOP_LATCH]] ]
+; CHECK-NEXT: br i1 [[SC]], label [[CLOBBER:%.*]], label [[NOCLOBBER:%.*]]
+; CHECK: clobber:
+; CHECK-NEXT: call void @clobber()
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: noclobber:
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[IV]], [[N:%.*]]
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: br i1 [[C]], label [[LOOP_HEADER]], label [[EXIT:%.*]]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %loop.header
+
+loop.header:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %sc = phi i1 [ %lc, %entry ], [ true, %loop.latch ]
+ br i1 %sc, label %clobber, label %noclobber
+
+clobber:
+ call void @clobber()
+ br label %loop.latch
+
+noclobber:
+ 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 void
+}
+
+define void @no_partial_unswitch_clobber_latch(i32* %ptr, i32 %N) {
+; CHECK-LABEL: @no_partial_unswitch_clobber_latch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
+; CHECK: loop.header:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[LV:%.*]] = load i32, i32* [[PTR:%.*]], align 4
+; CHECK-NEXT: [[SC:%.*]] = icmp eq i32 [[LV]], 100
+; CHECK-NEXT: br i1 [[SC]], label [[NOCLOBBER:%.*]], label [[CLOBBER:%.*]]
+; CHECK: noclobber:
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: clobber:
+; CHECK-NEXT: call void @clobber()
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: call void @clobber()
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[IV]], [[N:%.*]]
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: br i1 [[C]], label [[LOOP_HEADER]], label [[EXIT:%.*]]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+;
+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()
+ br label %loop.latch
+
+loop.latch:
+ call void @clobber()
+ %c = icmp ult i32 %iv, %N
+ %iv.next = add i32 %iv, 1
+ br i1 %c, label %loop.header, label %exit
+
+exit:
+ ret void
+}
+
+define void @no_partial_unswitch_clobber_header(i32* %ptr, i32 %N) {
+; CHECK-LABEL: @no_partial_unswitch_clobber_header(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
+; CHECK: loop.header:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: call void @clobber()
+; CHECK-NEXT: [[LV:%.*]] = load i32, i32* [[PTR:%.*]], align 4
+; CHECK-NEXT: [[SC:%.*]] = icmp eq i32 [[LV]], 100
+; CHECK-NEXT: br i1 [[SC]], label [[NOCLOBBER:%.*]], label [[CLOBBER:%.*]]
+; CHECK: noclobber:
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: clobber:
+; CHECK-NEXT: call void @clobber()
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[IV]], [[N:%.*]]
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: br i1 [[C]], label [[LOOP_HEADER]], label [[EXIT:%.*]]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %loop.header
+
+loop.header:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ call void @clobber()
+ %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()
+ 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 void
+}
+
+define void @no_partial_unswitch_clobber_both(i32* %ptr, i32 %N) {
+; CHECK-LABEL: @no_partial_unswitch_clobber_both(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
+; CHECK: loop.header:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[LV:%.*]] = load i32, i32* [[PTR:%.*]], align 4
+; CHECK-NEXT: [[SC:%.*]] = icmp eq i32 [[LV]], 100
+; CHECK-NEXT: br i1 [[SC]], label [[NOCLOBBER:%.*]], label [[CLOBBER:%.*]]
+; CHECK: noclobber:
+; CHECK-NEXT: call void @clobber()
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: clobber:
+; CHECK-NEXT: call void @clobber()
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[IV]], [[N:%.*]]
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: br i1 [[C]], label [[LOOP_HEADER]], label [[EXIT:%.*]]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+;
+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:
+ call void @clobber()
+ br label %loop.latch
+
+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 void
+}
+
+define i32 @no_partial_unswitch_true_successor_storeclobber(i32* %ptr.1, i32* %ptr.2, i32 %N) {
+; CHECK-LABEL: @no_partial_unswitch_true_successor_storeclobber(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
+; CHECK: loop.header:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[LV:%.*]] = load i32, i32* [[PTR_1:%.*]], align 4
+; CHECK-NEXT: [[SC:%.*]] = icmp eq i32 [[LV]], 100
+; CHECK-NEXT: br i1 [[SC]], label [[NOCLOBBER:%.*]], label [[CLOBBER:%.*]]
+; CHECK: noclobber:
+; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr i32, i32* [[PTR_2:%.*]], i32 [[IV]]
+; CHECK-NEXT: store i32 [[LV]], i32* [[GEP_1]], align 4
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: clobber:
+; CHECK-NEXT: call void @clobber()
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[IV]], [[N:%.*]]
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: br i1 [[C]], label [[LOOP_HEADER]], label [[EXIT:%.*]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 10
+;
+entry:
+ br label %loop.header
+
+loop.header:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %lv = load i32, i32* %ptr.1
+ %sc = icmp eq i32 %lv, 100
+ br i1 %sc, label %noclobber, label %clobber
+
+noclobber:
+ %gep.1 = getelementptr i32, i32* %ptr.2, i32 %iv
+ store i32 %lv, i32* %gep.1
+ br label %loop.latch
+
+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
+}
+
+; Make sure the duplicated instructions are moved to a preheader that always
+; executes when the loop body also executes. Do not check the unswitched code,
+; because it is already checked in the @partial_unswitch_true_successor test
+; case.
+define i32 @partial_unswitch_true_successor_preheader_insertion(i32* %ptr, i32 %N) {
+; CHECK-LABEL: @partial_unswitch_true_successor_preheader_insertion(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[EC:%.*]] = icmp ne i32* [[PTR:%.*]], null
+; CHECK-NEXT: br i1 [[EC]], label [[LOOP_PH:%.*]], label [[EXIT:%.*]]
+; CHECK: loop.ph:
+; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[PTR]], align 4
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[TMP0]], 100
+; CHECK-NEXT: br i1 [[TMP1]], label [[LOOP_PH_SPLIT_US:%.*]], label [[LOOP_PH_SPLIT:%.*]]
+; CHECK: loop.ph.split.us:
+; CHECK-NEXT: br label [[LOOP_HEADER_US:%.*]]
+; CHECK: loop.header.us:
+; CHECK-NEXT: [[IV_US:%.*]] = phi i32 [ 0, [[LOOP_PH_SPLIT_US]] ], [ [[IV_NEXT_US:%.*]], [[LOOP_LATCH_US:%.*]] ]
+; CHECK-NEXT: br label [[NOCLOBBER_US:%.*]]
+; CHECK: noclobber.us:
+; CHECK-NEXT: br label [[LOOP_LATCH_US]]
+; CHECK: loop.latch.us:
+; CHECK-NEXT: [[C_US:%.*]] = icmp ult i32 [[IV_US]], [[N:%.*]]
+; CHECK-NEXT: [[IV_NEXT_US]] = add i32 [[IV_US]], 1
+; CHECK-NEXT: br i1 [[C_US]], label [[LOOP_HEADER_US]], label [[EXIT_LOOPEXIT_SPLIT_US:%.*]]
+; CHECK: exit.loopexit.split.us:
+; CHECK-NEXT: br label [[EXIT_LOOPEXIT:%.*]]
+; CHECK: loop.ph.split:
+; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
+; CHECK: loop.header:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[LOOP_PH_SPLIT]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[LV:%.*]] = load i32, i32* [[PTR]], align 4
+; CHECK-NEXT: [[SC:%.*]] = icmp eq i32 [[LV]], 100
+; CHECK-NEXT: br i1 [[SC]], label [[NOCLOBBER:%.*]], label [[CLOBBER:%.*]]
+; CHECK: noclobber:
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: clobber:
+; CHECK-NEXT: call void @clobber()
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[IV]], [[N]]
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: br i1 [[C]], label [[LOOP_HEADER]], label [[EXIT_LOOPEXIT_SPLIT:%.*]]
+; CHECK: exit.loopexit.split:
+; CHECK-NEXT: br label [[EXIT_LOOPEXIT]]
+; CHECK: exit.loopexit:
+; CHECK-NEXT: br label [[EXIT]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 10
+;
+
+entry:
+ %ec = icmp ne i32* %ptr, null
+ br i1 %ec, label %loop.ph, label %exit
+
+loop.ph:
+ br label %loop.header
+
+loop.header:
+ %iv = phi i32 [ 0, %loop.ph ], [ %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()
+ 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
+}
+
+; Make sure the duplicated instructions are hoisted just before the branch of
+; the preheader. Do not check the unswitched code, because it is already checked
+; in the @partial_unswitch_true_successor test case
+define i32 @partial_unswitch_true_successor_insert_point(i32* %ptr, i32 %N) {
+; CHECK-LABEL: @partial_unswitch_true_successor_insert_point(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: call void @clobber()
+; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[PTR:%.*]], align 4
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[TMP0]], 100
+; CHECK-NEXT: br i1 [[TMP1]], label [[ENTRY_SPLIT_US:%.*]], label [[ENTRY_SPLIT:%.*]]
+; CHECK: entry.split.us:
+; CHECK-NEXT: br label [[LOOP_HEADER_US:%.*]]
+; CHECK: loop.header.us:
+; CHECK-NEXT: [[IV_US:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT_US]] ], [ [[IV_NEXT_US:%.*]], [[LOOP_LATCH_US:%.*]] ]
+; CHECK-NEXT: br label [[NOCLOBBER_US:%.*]]
+; CHECK: noclobber.us:
+; CHECK-NEXT: br label [[LOOP_LATCH_US]]
+; CHECK: loop.latch.us:
+; CHECK-NEXT: [[C_US:%.*]] = icmp ult i32 [[IV_US]], [[N:%.*]]
+; CHECK-NEXT: [[IV_NEXT_US]] = add i32 [[IV_US]], 1
+; CHECK-NEXT: br i1 [[C_US]], label [[LOOP_HEADER_US]], label [[EXIT_SPLIT_US:%.*]]
+; CHECK: exit.split.us:
+; CHECK-NEXT: br label [[EXIT:%.*]]
+; CHECK: entry.split:
+; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
+; CHECK: loop.header:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[LV:%.*]] = load i32, i32* [[PTR]], align 4
+; CHECK-NEXT: [[SC:%.*]] = icmp eq i32 [[LV]], 100
+; CHECK-NEXT: br i1 [[SC]], label [[NOCLOBBER:%.*]], label [[CLOBBER:%.*]]
+; CHECK: noclobber:
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: clobber:
+; CHECK-NEXT: call void @clobber()
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[IV]], [[N]]
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: br i1 [[C]], label [[LOOP_HEADER]], label [[EXIT_SPLIT:%.*]]
+; CHECK: exit.split:
+; CHECK-NEXT: br label [[EXIT]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 10
+;
+entry:
+ call void @clobber()
+ 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()
+ 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
+}
+
+; Make sure invariant instructions in the loop are also hoisted to the preheader.
+; Do not check the unswitched code, because it is already checked in the
+; @partial_unswitch_true_successor test case
+define i32 @partial_unswitch_true_successor_hoist_invariant(i32* %ptr, i32 %N) {
+; CHECK-LABEL: @partial_unswitch_true_successor_hoist_invariant(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, i32* [[PTR:%.*]], i64 1
+; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[TMP0]], align 4
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[TMP1]], 100
+; CHECK-NEXT: br i1 [[TMP2]], label [[ENTRY_SPLIT_US:%.*]], label [[ENTRY_SPLIT:%.*]]
+; CHECK: entry.split.us:
+; CHECK-NEXT: br label [[LOOP_HEADER_US:%.*]]
+; CHECK: loop.header.us:
+; CHECK-NEXT: [[IV_US:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT_US]] ], [ [[IV_NEXT_US:%.*]], [[LOOP_LATCH_US:%.*]] ]
+; CHECK-NEXT: br label [[NOCLOBBER_US:%.*]]
+; CHECK: noclobber.us:
+; CHECK-NEXT: br label [[LOOP_LATCH_US]]
+; CHECK: loop.latch.us:
+; CHECK-NEXT: [[C_US:%.*]] = icmp ult i32 [[IV_US]], [[N:%.*]]
+; CHECK-NEXT: [[IV_NEXT_US]] = add i32 [[IV_US]], 1
+; CHECK-NEXT: br i1 [[C_US]], label [[LOOP_HEADER_US]], label [[EXIT_SPLIT_US:%.*]]
+; CHECK: exit.split.us:
+; CHECK-NEXT: br label [[EXIT:%.*]]
+; CHECK: entry.split:
+; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
+; CHECK: loop.header:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, i32* [[PTR]], i64 1
+; CHECK-NEXT: [[LV:%.*]] = load i32, i32* [[GEP]], align 4
+; CHECK-NEXT: [[SC:%.*]] = icmp eq i32 [[LV]], 100
+; CHECK-NEXT: br i1 [[SC]], label [[NOCLOBBER:%.*]], label [[CLOBBER:%.*]]
+; CHECK: noclobber:
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: clobber:
+; CHECK-NEXT: call void @clobber()
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[IV]], [[N]]
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: br i1 [[C]], label [[LOOP_HEADER]], label [[EXIT_SPLIT:%.*]]
+; CHECK: exit.split:
+; CHECK-NEXT: br label [[EXIT]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 10
+;
+entry:
+ br label %loop.header
+
+loop.header:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %gep = getelementptr i32, i32* %ptr, i64 1
+ %lv = load i32, i32* %gep
+ %sc = icmp eq i32 %lv, 100
+ br i1 %sc, label %noclobber, label %clobber
+
+noclobber:
+ br label %loop.latch
+
+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
+}
+
+; Do not unswitch if the condition depends on an atomic load. Duplicating such
+; loads is not safe.
+define i32 @no_partial_unswitch_atomic_load_unordered(i32* %ptr, i32 %N) {
+; CHECK-LABEL: @no_partial_unswitch_atomic_load_unordered(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
+; CHECK: loop.header:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[LV:%.*]] = load atomic i32, i32* [[PTR:%.*]] unordered, align 4
+; CHECK-NEXT: [[SC:%.*]] = icmp eq i32 [[LV]], 100
+; CHECK-NEXT: br i1 [[SC]], label [[NOCLOBBER:%.*]], label [[CLOBBER:%.*]]
+; CHECK: noclobber:
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: clobber:
+; CHECK-NEXT: call void @clobber()
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[IV]], [[N:%.*]]
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: br i1 [[C]], label [[LOOP_HEADER]], label [[EXIT:%.*]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 10
+;
+entry:
+ br label %loop.header
+
+loop.header:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %lv = load atomic i32, i32* %ptr unordered, align 4
+ %sc = icmp eq i32 %lv, 100
+ br i1 %sc, label %noclobber, label %clobber
+
+noclobber:
+ br label %loop.latch
+
+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
+}
+
+; Do not unswitch if the condition depends on an atomic load. Duplicating such
+; loads is not safe.
+define i32 @no_partial_unswitch_atomic_load_monotonic(i32* %ptr, i32 %N) {
+; CHECK-LABEL: @no_partial_unswitch_atomic_load_monotonic(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
+; CHECK: loop.header:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[LV:%.*]] = load atomic i32, i32* [[PTR:%.*]] monotonic, align 4
+; CHECK-NEXT: [[SC:%.*]] = icmp eq i32 [[LV]], 100
+; CHECK-NEXT: br i1 [[SC]], label [[NOCLOBBER:%.*]], label [[CLOBBER:%.*]]
+; CHECK: noclobber:
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: clobber:
+; CHECK-NEXT: call void @clobber()
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[IV]], [[N:%.*]]
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: br i1 [[C]], label [[LOOP_HEADER]], label [[EXIT:%.*]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 10
+;
+entry:
+ br label %loop.header
+
+loop.header:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %lv = load atomic i32, i32* %ptr monotonic, align 4
+ %sc = icmp eq i32 %lv, 100
+ br i1 %sc, label %noclobber, label %clobber
+
+noclobber:
+ br label %loop.latch
+
+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
+}
+
+
+declare i32 @get_value()
+
+; Do not unswitch if the condition depends on a call, that may clobber memory.
+; Duplicating such a call is not safe.
+define i32 @no_partial_unswitch_cond_call(i32* %ptr, i32 %N) {
+; CHECK-LABEL: @no_partial_unswitch_cond_call(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
+; CHECK: loop.header:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[LV:%.*]] = call i32 @get_value()
+; CHECK-NEXT: [[SC:%.*]] = icmp eq i32 [[LV]], 100
+; CHECK-NEXT: br i1 [[SC]], label [[NOCLOBBER:%.*]], label [[CLOBBER:%.*]]
+; CHECK: noclobber:
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: clobber:
+; CHECK-NEXT: call void @clobber()
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[IV]], [[N:%.*]]
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: br i1 [[C]], label [[LOOP_HEADER]], label [[EXIT:%.*]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 10
+;
+entry:
+ br label %loop.header
+
+loop.header:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %lv = call i32 @get_value()
+ %sc = icmp eq i32 %lv, 100
+ br i1 %sc, label %noclobber, label %clobber
+
+noclobber:
+ br label %loop.latch
+
+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_true_successor_exit(i32* %ptr, i32 %N) {
+; CHECK-LABEL: @no_partial_unswitch_true_successor_exit(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
+; CHECK: loop.header:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[LV:%.*]] = load i32, i32* [[PTR:%.*]], align 4
+; CHECK-NEXT: [[SC:%.*]] = icmp eq i32 [[LV]], 100
+; CHECK-NEXT: br i1 [[SC]], label [[EXIT:%.*]], label [[CLOBBER:%.*]]
+; CHECK: clobber:
+; CHECK-NEXT: call void @clobber()
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[IV]], [[N:%.*]]
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: br i1 [[C]], label [[LOOP_HEADER]], label [[EXIT]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 10
+;
+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 %exit, label %clobber
+
+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_true_same_successor(i32* %ptr, i32 %N) {
+; CHECK-LABEL: @no_partial_unswitch_true_same_successor(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
+; CHECK: loop.header:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[LV:%.*]] = load i32, i32* [[PTR:%.*]], align 4
+; CHECK-NEXT: [[SC:%.*]] = icmp eq i32 [[LV]], 100
+; CHECK-NEXT: br i1 [[SC]], label [[NOCLOBBER:%.*]], label [[NOCLOBBER]]
+; CHECK: noclobber:
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[IV]], [[N:%.*]]
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: br i1 [[C]], label [[LOOP_HEADER]], label [[EXIT:%.*]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 10
+;
+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 %noclobber
+
+noclobber:
+ 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_true_to_latch(i32* %ptr, i32 %N) {
+; CHECK-LABEL: @partial_unswitch_true_to_latch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[PTR:%.*]], align 4
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[TMP0]], 100
+; CHECK-NEXT: br i1 [[TMP1]], label [[ENTRY_SPLIT_US:%.*]], label [[ENTRY_SPLIT:%.*]]
+; CHECK: entry.split.us:
+; CHECK-NEXT: br label [[LOOP_HEADER_US:%.*]]
+; CHECK: loop.header.us:
+; CHECK-NEXT: [[IV_US:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT_US]] ], [ [[IV_NEXT_US:%.*]], [[LOOP_LATCH_US:%.*]] ]
+; CHECK-NEXT: br label [[LOOP_LATCH_US]]
+; CHECK: loop.latch.us:
+; CHECK-NEXT: [[C_US:%.*]] = icmp ult i32 [[IV_US]], [[N:%.*]]
+; CHECK-NEXT: [[IV_NEXT_US]] = add i32 [[IV_US]], 1
+; CHECK-NEXT: br i1 [[C_US]], label [[LOOP_HEADER_US]], label [[EXIT_SPLIT_US:%.*]]
+; CHECK: exit.split.us:
+; CHECK-NEXT: br label [[EXIT:%.*]]
+; CHECK: entry.split:
+; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
+; CHECK: loop.header:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[LV:%.*]] = load i32, i32* [[PTR]], align 4
+; CHECK-NEXT: [[SC:%.*]] = icmp eq i32 [[LV]], 100
+; CHECK-NEXT: br i1 [[SC]], label [[LOOP_LATCH]], label [[CLOBBER:%.*]]
+; CHECK: clobber:
+; CHECK-NEXT: call void @clobber()
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[IV]], [[N]]
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: br i1 [[C]], label [[LOOP_HEADER]], label [[EXIT_SPLIT:%.*]]
+; CHECK: exit.split:
+; CHECK-NEXT: br label [[EXIT]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 10
+;
+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 %loop.latch, label %clobber
+
+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
+}
More information about the llvm-commits
mailing list