| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 | |
| ; RUN: opt -S -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 < %s | FileCheck %s | |
| declare void @a() | |
| declare void @b() | |
| declare void @c() | |
| declare void @d() | |
| define void @switch_replace_default(i32 %x) { | |
| ; CHECK-LABEL: define void @switch_replace_default( | |
| ; CHECK-SAME: i32 [[X:%.*]]) { | |
| ; CHECK-NEXT: [[MIN:%.*]] = call i32 @llvm.umin.i32(i32 [[X]], i32 3) | |
| ; CHECK-NEXT: switch i32 [[X]], label %[[COMMON_RET:.*]] [ | |
| ; CHECK-NEXT: i32 0, label %[[CASE0:.*]] | |
| ; CHECK-NEXT: i32 1, label %[[CASE1:.*]] | |
| ; CHECK-NEXT: i32 2, label %[[CASE2:.*]] | |
| ; CHECK-NEXT: ], !prof [[PROF0:![0-9]+]] | |
| ; CHECK: [[COMMON_RET]]: | |
| ; CHECK-NEXT: ret void | |
| ; CHECK: [[CASE0]]: | |
| ; CHECK-NEXT: call void @a() | |
| ; CHECK-NEXT: br label %[[COMMON_RET]] | |
| ; CHECK: [[CASE1]]: | |
| ; CHECK-NEXT: call void @b() | |
| ; CHECK-NEXT: br label %[[COMMON_RET]] | |
| ; CHECK: [[CASE2]]: | |
| ; CHECK-NEXT: call void @c() | |
| ; CHECK-NEXT: br label %[[COMMON_RET]] | |
| ; | |
| %min = call i32 @llvm.umin.i32(i32 %x, i32 3) | |
| switch i32 %min, label %unreachable [ | |
| i32 0, label %case0 | |
| i32 1, label %case1 | |
| i32 2, label %case2 | |
| i32 3, label %case3 | |
| ], !prof !0 | |
| case0: | |
| call void @a() | |
| ret void | |
| case1: | |
| call void @b() | |
| ret void | |
| case2: | |
| call void @c() | |
| ret void | |
| case3: | |
| ret void | |
| unreachable: | |
| unreachable | |
| } | |
| define void @switch_replace_default_and_remove_dead_cases(i32 %x) { | |
| ; CHECK-LABEL: define void @switch_replace_default_and_remove_dead_cases( | |
| ; CHECK-SAME: i32 [[X:%.*]]) { | |
| ; CHECK-NEXT: [[MIN:%.*]] = call i32 @llvm.umin.i32(i32 [[X]], i32 3) | |
| ; CHECK-NEXT: switch i32 [[X]], label %[[COMMON_RET:.*]] [ | |
| ; CHECK-NEXT: i32 2, label %[[CASE2:.*]] | |
| ; CHECK-NEXT: i32 1, label %[[CASE1:.*]] | |
| ; CHECK-NEXT: ] | |
| ; CHECK: [[COMMON_RET]]: | |
| ; CHECK-NEXT: ret void | |
| ; CHECK: [[CASE1]]: | |
| ; CHECK-NEXT: call void @b() | |
| ; CHECK-NEXT: br label %[[COMMON_RET]] | |
| ; CHECK: [[CASE2]]: | |
| ; CHECK-NEXT: call void @c() | |
| ; CHECK-NEXT: br label %[[COMMON_RET]] | |
| ; | |
| %min = call i32 @llvm.umin.i32(i32 %x, i32 3) | |
| switch i32 %min, label %unreachable [ | |
| i32 4, label %case4 | |
| i32 1, label %case1 | |
| i32 2, label %case2 | |
| i32 3, label %case3 | |
| ] | |
| case4: | |
| call void @a() | |
| ret void | |
| case1: | |
| call void @b() | |
| ret void | |
| case2: | |
| call void @c() | |
| ret void | |
| case3: | |
| ret void | |
| unreachable: | |
| unreachable | |
| } | |
| define void @switch_replace_default_when_holes(i32 %x) { | |
| ; CHECK-LABEL: define void @switch_replace_default_when_holes( | |
| ; CHECK-SAME: i32 [[X:%.*]]) { | |
| ; CHECK-NEXT: [[MIN:%.*]] = call i32 @llvm.umin.i32(i32 [[X]], i32 3) | |
| ; CHECK-NEXT: switch i32 [[X]], label %[[COMMON_RET:.*]] [ | |
| ; CHECK-NEXT: i32 1, label %[[CASE1:.*]] | |
| ; CHECK-NEXT: i32 2, label %[[CASE2:.*]] | |
| ; CHECK-NEXT: ] | |
| ; CHECK: [[COMMON_RET]]: | |
| ; CHECK-NEXT: ret void | |
| ; CHECK: [[CASE1]]: | |
| ; CHECK-NEXT: call void @b() | |
| ; CHECK-NEXT: br label %[[COMMON_RET]] | |
| ; CHECK: [[CASE2]]: | |
| ; CHECK-NEXT: call void @c() | |
| ; CHECK-NEXT: br label %[[COMMON_RET]] | |
| ; | |
| %min = call i32 @llvm.umin.i32(i32 %x, i32 3) | |
| switch i32 %min, label %unreachable [ | |
| i32 1, label %case1 | |
| i32 2, label %case2 | |
| i32 3, label %case3 | |
| ] | |
| case1: | |
| call void @b() | |
| ret void | |
| case2: | |
| call void @c() | |
| ret void | |
| case3: | |
| ret void | |
| unreachable: | |
| unreachable | |
| } | |
| define void @do_not_switch_replace_default(i32 %x, i32 %y) { | |
| ; CHECK-LABEL: define void @do_not_switch_replace_default( | |
| ; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) { | |
| ; CHECK-NEXT: [[MIN:%.*]] = call i32 @llvm.umin.i32(i32 [[X]], i32 [[Y]]) | |
| ; CHECK-NEXT: switch i32 [[MIN]], label %[[UNREACHABLE:.*]] [ | |
| ; CHECK-NEXT: i32 0, label %[[CASE0:.*]] | |
| ; CHECK-NEXT: i32 1, label %[[CASE1:.*]] | |
| ; CHECK-NEXT: i32 2, label %[[CASE2:.*]] | |
| ; CHECK-NEXT: i32 3, label %[[COMMON_RET:.*]] | |
| ; CHECK-NEXT: ] | |
| ; CHECK: [[COMMON_RET]]: | |
| ; CHECK-NEXT: ret void | |
| ; CHECK: [[CASE0]]: | |
| ; CHECK-NEXT: call void @a() | |
| ; CHECK-NEXT: br label %[[COMMON_RET]] | |
| ; CHECK: [[CASE1]]: | |
| ; CHECK-NEXT: call void @b() | |
| ; CHECK-NEXT: br label %[[COMMON_RET]] | |
| ; CHECK: [[CASE2]]: | |
| ; CHECK-NEXT: call void @c() | |
| ; CHECK-NEXT: br label %[[COMMON_RET]] | |
| ; CHECK: [[UNREACHABLE]]: | |
| ; CHECK-NEXT: unreachable | |
| ; | |
| %min = call i32 @llvm.umin.i32(i32 %x, i32 %y) | |
| switch i32 %min, label %unreachable [ | |
| i32 0, label %case0 | |
| i32 1, label %case1 | |
| i32 2, label %case2 | |
| i32 3, label %case3 | |
| ] | |
| case0: | |
| call void @a() | |
| ret void | |
| case1: | |
| call void @b() | |
| ret void | |
| case2: | |
| call void @c() | |
| ret void | |
| case3: | |
| ret void | |
| unreachable: | |
| unreachable | |
| } | |
| define void @do_not_replace_switch_default_but_remove_dead_cases(i32 %x) { | |
| ; CHECK-LABEL: define void @do_not_replace_switch_default_but_remove_dead_cases( | |
| ; CHECK-SAME: i32 [[X:%.*]]) { | |
| ; CHECK-NEXT: [[MIN:%.*]] = call i32 @llvm.umin.i32(i32 [[X]], i32 3) | |
| ; CHECK-NEXT: switch i32 [[MIN]], label %[[CASE0:.*]] [ | |
| ; CHECK-NEXT: i32 3, label %[[COMMON_RET:.*]] | |
| ; CHECK-NEXT: i32 1, label %[[CASE1:.*]] | |
| ; CHECK-NEXT: i32 2, label %[[CASE2:.*]] | |
| ; CHECK-NEXT: ] | |
| ; CHECK: [[COMMON_RET]]: | |
| ; CHECK-NEXT: ret void | |
| ; CHECK: [[CASE0]]: | |
| ; CHECK-NEXT: call void @a() | |
| ; CHECK-NEXT: br label %[[COMMON_RET]] | |
| ; CHECK: [[CASE1]]: | |
| ; CHECK-NEXT: call void @b() | |
| ; CHECK-NEXT: br label %[[COMMON_RET]] | |
| ; CHECK: [[CASE2]]: | |
| ; CHECK-NEXT: call void @c() | |
| ; CHECK-NEXT: br label %[[COMMON_RET]] | |
| ; | |
| %min = call i32 @llvm.umin.i32(i32 %x, i32 3) | |
| switch i32 %min, label %case0 [ ; default is reachable, therefore simplification not triggered | |
| i32 0, label %case0 | |
| i32 1, label %case1 | |
| i32 2, label %case2 | |
| i32 3, label %case3 | |
| i32 4, label %case4 | |
| ] | |
| case0: | |
| call void @a() | |
| ret void | |
| case1: | |
| call void @b() | |
| ret void | |
| case2: | |
| call void @c() | |
| ret void | |
| case3: | |
| ret void | |
| case4: | |
| call void @d() | |
| ret void | |
| } | |
| define void @switch_remove_dead_cases(i32 %x) { | |
| ; CHECK-LABEL: define void @switch_remove_dead_cases( | |
| ; CHECK-SAME: i32 [[X:%.*]]) { | |
| ; CHECK-NEXT: [[MIN:%.*]] = call i32 @llvm.umin.i32(i32 [[X]], i32 4) | |
| ; CHECK-NEXT: switch i32 [[X]], label %[[COMMON_RET:.*]] [ | |
| ; CHECK-NEXT: i32 2, label %[[CASE_A:.*]] | |
| ; CHECK-NEXT: i32 3, label %[[CASE_B:.*]] | |
| ; CHECK-NEXT: ], !prof [[PROF1:![0-9]+]] | |
| ; CHECK: [[COMMON_RET]]: | |
| ; CHECK-NEXT: ret void | |
| ; CHECK: [[CASE_A]]: | |
| ; CHECK-NEXT: call void @a() | |
| ; CHECK-NEXT: br label %[[COMMON_RET]] | |
| ; CHECK: [[CASE_B]]: | |
| ; CHECK-NEXT: call void @b() | |
| ; CHECK-NEXT: br label %[[COMMON_RET]] | |
| ; | |
| %min = call i32 @llvm.umin.i32(i32 %x, i32 4) | |
| switch i32 %min, label %unreachable [ | |
| i32 2, label %case_a | |
| i32 3, label %case_b | |
| i32 4, label %case_ret | |
| i32 5, label %case_ret | |
| ], !prof !1 | |
| case_a: | |
| call void @a() | |
| ret void | |
| case_b: | |
| call void @b() | |
| ret void | |
| case_ret: | |
| ret void | |
| unreachable: | |
| unreachable | |
| } | |
| !0 = !{!"branch_weights", i32 1, i32 2, i32 3, i32 99, i32 5} | |
| ;. | |
| ; CHECK: [[PROF0]] = !{!"branch_weights", i32 5, i32 2, i32 3, i32 99} | |
| ;. | |
| !1 = !{!"branch_weights", i32 11, i32 12, i32 13, i32 14, i32 15} | |
| ;. | |
| ; CHECK: [[PROF1]] = !{!"branch_weights", i32 14, i32 12, i32 13} | |
| ;. |