| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 |
| ; RUN: opt -S -passes=drop-unnecessary-assumes < %s | FileCheck %s |
| |
| declare void @use(i32 %x) |
| declare i32 @get() |
| |
| define void @basic_dead(i32 %x) { |
| ; CHECK-LABEL: define void @basic_dead( |
| ; CHECK-SAME: i32 [[X:%.*]]) { |
| ; CHECK-NEXT: ret void |
| ; |
| %cond = icmp sge i32 %x, 0 |
| call void @llvm.assume(i1 %cond) |
| ret void |
| } |
| |
| define i32 @basic_live(i32 %x) { |
| ; CHECK-LABEL: define i32 @basic_live( |
| ; CHECK-SAME: i32 [[X:%.*]]) { |
| ; CHECK-NEXT: [[COND:%.*]] = icmp sge i32 [[X]], 0 |
| ; CHECK-NEXT: call void @llvm.assume(i1 [[COND]]) |
| ; CHECK-NEXT: ret i32 [[X]] |
| ; |
| %cond = icmp sge i32 %x, 0 |
| call void @llvm.assume(i1 %cond) |
| ret i32 %x |
| } |
| |
| ; Affected value is not direct operand of the condition. |
| define i32 @complex_live(i32 %x) { |
| ; CHECK-LABEL: define i32 @complex_live( |
| ; CHECK-SAME: i32 [[X:%.*]]) { |
| ; CHECK-NEXT: [[AND:%.*]] = and i32 [[X]], 1 |
| ; CHECK-NEXT: [[COND:%.*]] = icmp ne i32 [[AND]], 0 |
| ; CHECK-NEXT: call void @llvm.assume(i1 [[COND]]) |
| ; CHECK-NEXT: ret i32 [[X]] |
| ; |
| %and = and i32 %x, 1 |
| %cond = icmp ne i32 %and, 0 |
| call void @llvm.assume(i1 %cond) |
| ret i32 %x |
| } |
| |
| ; There are multiple affected values, and not all are one-use. |
| define i32 @multiple_live1(i32 %x, i32 %y) { |
| ; CHECK-LABEL: define i32 @multiple_live1( |
| ; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[X]], [[Y]] |
| ; CHECK-NEXT: call void @llvm.assume(i1 [[COND]]) |
| ; CHECK-NEXT: ret i32 [[X]] |
| ; |
| %cond = icmp eq i32 %x, %y |
| call void @llvm.assume(i1 %cond) |
| ret i32 %x |
| } |
| |
| define i32 @multiple_live2(i32 %x, i32 %y) { |
| ; CHECK-LABEL: define i32 @multiple_live2( |
| ; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[X]], [[Y]] |
| ; CHECK-NEXT: call void @llvm.assume(i1 [[COND]]) |
| ; CHECK-NEXT: ret i32 [[Y]] |
| ; |
| %cond = icmp eq i32 %x, %y |
| call void @llvm.assume(i1 %cond) |
| ret i32 %y |
| } |
| |
| define void @operand_bundle_one_dead(ptr %x) { |
| ; CHECK-LABEL: define void @operand_bundle_one_dead( |
| ; CHECK-SAME: ptr [[X:%.*]]) { |
| ; CHECK-NEXT: ret void |
| ; |
| call void @llvm.assume(i1 true) ["align"(ptr %x, i64 8)] |
| ret void |
| } |
| |
| define ptr @operand_bundle_one_live(ptr %x) { |
| ; CHECK-LABEL: define ptr @operand_bundle_one_live( |
| ; CHECK-SAME: ptr [[X:%.*]]) { |
| ; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[X]], i64 8) ] |
| ; CHECK-NEXT: ret ptr [[X]] |
| ; |
| call void @llvm.assume(i1 true) ["align"(ptr %x, i64 8)] |
| ret ptr %x |
| } |
| |
| define void @operand_bundle_multiple_dead(ptr %x, ptr %y) { |
| ; CHECK-LABEL: define void @operand_bundle_multiple_dead( |
| ; CHECK-SAME: ptr [[X:%.*]], ptr [[Y:%.*]]) { |
| ; CHECK-NEXT: ret void |
| ; |
| call void @llvm.assume(i1 true) ["align"(ptr %x, i64 8), "align"(ptr %y, i64 8)] |
| ret void |
| } |
| |
| define ptr @operand_bundle_one_live_one_dead(ptr %x, ptr %y) { |
| ; CHECK-LABEL: define ptr @operand_bundle_one_live_one_dead( |
| ; CHECK-SAME: ptr [[X:%.*]], ptr [[Y:%.*]]) { |
| ; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[Y]], i64 8) ] |
| ; CHECK-NEXT: ret ptr [[Y]] |
| ; |
| call void @llvm.assume(i1 true) ["align"(ptr %x, i64 8), "align"(ptr %y, i64 8)] |
| ret ptr %y |
| } |
| |
| define i64 @operand_bundle_ignore_unaffected_operands(ptr %x, i64 %align) { |
| ; CHECK-LABEL: define i64 @operand_bundle_ignore_unaffected_operands( |
| ; CHECK-SAME: ptr [[X:%.*]], i64 [[ALIGN:%.*]]) { |
| ; CHECK-NEXT: ret i64 [[ALIGN]] |
| ; |
| call void @llvm.assume(i1 true) ["align"(ptr %x, i64 %align)] |
| ret i64 %align |
| } |
| |
| define void @operand_bundle_remove_dead_insts(ptr %x) { |
| ; CHECK-LABEL: define void @operand_bundle_remove_dead_insts( |
| ; CHECK-SAME: ptr [[X:%.*]]) { |
| ; CHECK-NEXT: ret void |
| ; |
| %gep = getelementptr i8, ptr %x, i64 8 |
| call void @llvm.assume(i1 true) ["align"(ptr %gep, i64 8)] |
| ret void |
| } |
| |
| define void @operand_bundle_no_args() { |
| ; CHECK-LABEL: define void @operand_bundle_no_args() { |
| ; CHECK-NEXT: call void @llvm.assume(i1 true) [ "cold"() ] |
| ; CHECK-NEXT: ret void |
| ; |
| call void @llvm.assume(i1 true) ["cold"()] |
| ret void |
| } |
| |
| ; Can always drop ignore bundles, regardless of uses. |
| define ptr @operand_bundle_ignore(ptr %x) { |
| ; CHECK-LABEL: define ptr @operand_bundle_ignore( |
| ; CHECK-SAME: ptr [[X:%.*]]) { |
| ; CHECK-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[X]]) ] |
| ; CHECK-NEXT: ret ptr [[X]] |
| ; |
| call void @llvm.assume(i1 true) ["ignore"(), "ignore"(ptr %x), "nonnull"(ptr %x)] |
| ret ptr %x |
| } |
| |
| define void @operand_bundle_separate_storage_both_dead(ptr %x, ptr %y) { |
| ; CHECK-LABEL: define void @operand_bundle_separate_storage_both_dead( |
| ; CHECK-SAME: ptr [[X:%.*]], ptr [[Y:%.*]]) { |
| ; CHECK-NEXT: ret void |
| ; |
| call void @llvm.assume(i1 true) ["separate_storage"(ptr %x, ptr %y)] |
| ret void |
| } |
| |
| define ptr @operand_bundle_separate_storage_one_live1(ptr %x, ptr %y) { |
| ; CHECK-LABEL: define ptr @operand_bundle_separate_storage_one_live1( |
| ; CHECK-SAME: ptr [[X:%.*]], ptr [[Y:%.*]]) { |
| ; CHECK-NEXT: call void @llvm.assume(i1 true) [ "separate_storage"(ptr [[X]], ptr [[Y]]) ] |
| ; CHECK-NEXT: ret ptr [[Y]] |
| ; |
| call void @llvm.assume(i1 true) ["separate_storage"(ptr %x, ptr %y)] |
| ret ptr %y |
| } |
| |
| define ptr @operand_bundle_separate_storage_one_live2(ptr %x, ptr %y) { |
| ; CHECK-LABEL: define ptr @operand_bundle_separate_storage_one_live2( |
| ; CHECK-SAME: ptr [[X:%.*]], ptr [[Y:%.*]]) { |
| ; CHECK-NEXT: call void @llvm.assume(i1 true) [ "separate_storage"(ptr [[X]], ptr [[Y]]) ] |
| ; CHECK-NEXT: ret ptr [[X]] |
| ; |
| call void @llvm.assume(i1 true) ["separate_storage"(ptr %x, ptr %y)] |
| ret ptr %x |
| } |
| |
| define void @type_test(ptr %x) { |
| ; CHECK-LABEL: define void @type_test( |
| ; CHECK-SAME: ptr [[X:%.*]]) { |
| ; CHECK-NEXT: [[TEST:%.*]] = call i1 @llvm.type.test(ptr [[X]], metadata !"typeid") |
| ; CHECK-NEXT: call void @llvm.assume(i1 [[TEST]]) |
| ; CHECK-NEXT: ret void |
| ; |
| %test = call i1 @llvm.type.test(ptr %x, metadata !"typeid") |
| call void @llvm.assume(i1 %test) |
| ret void |
| } |
| |
| define void @multiple_dead_conds(i32 %x) { |
| ; CHECK-LABEL: define void @multiple_dead_conds( |
| ; CHECK-SAME: i32 [[X:%.*]]) { |
| ; CHECK-NEXT: ret void |
| ; |
| %cond1 = icmp sge i32 %x, 0 |
| call void @llvm.assume(i1 %cond1) |
| %cond2 = icmp ne i32 %x, 64 |
| call void @llvm.assume(i1 %cond2) |
| ret void |
| } |
| |
| define void @multiple_dead_bundles(ptr %x) { |
| ; CHECK-LABEL: define void @multiple_dead_bundles( |
| ; CHECK-SAME: ptr [[X:%.*]]) { |
| ; CHECK-NEXT: ret void |
| ; |
| call void @llvm.assume(i1 true) ["align"(ptr %x, i64 8), "nonnull"(ptr %x)] |
| ret void |
| } |
| |
| ; The assume is eliminated, but currently leaves behind a dead cycle. |
| define void @dead_cycle(i1 %loop.cond) { |
| ; CHECK-LABEL: define void @dead_cycle( |
| ; CHECK-SAME: i1 [[LOOP_COND:%.*]]) { |
| ; CHECK-NEXT: [[ENTRY:.*]]: |
| ; CHECK-NEXT: br label %[[LOOP:.*]] |
| ; CHECK: [[LOOP]]: |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] |
| ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 |
| ; CHECK-NEXT: br i1 [[LOOP_COND]], label %[[LOOP]], label %[[EXIT:.*]] |
| ; CHECK: [[EXIT]]: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] |
| %cond = icmp ne i32 %iv, 64 |
| call void @llvm.assume(i1 %cond) |
| %iv.next = add i32 %iv, 1 |
| br i1 %loop.cond, label %loop, label %exit |
| |
| exit: |
| ret void |
| } |
| |
| define void @use_in_side_effect(i32 %x) { |
| ; CHECK-LABEL: define void @use_in_side_effect( |
| ; CHECK-SAME: i32 [[X:%.*]]) { |
| ; CHECK-NEXT: [[COND:%.*]] = icmp sge i32 [[X]], 0 |
| ; CHECK-NEXT: call void @llvm.assume(i1 [[COND]]) |
| ; CHECK-NEXT: call void @use(i32 [[X]]) |
| ; CHECK-NEXT: ret void |
| ; |
| %cond = icmp sge i32 %x, 0 |
| call void @llvm.assume(i1 %cond) |
| call void @use(i32 %x) |
| ret void |
| } |
| |
| define void @indirect_use_in_side_effect(i32 %x) { |
| ; CHECK-LABEL: define void @indirect_use_in_side_effect( |
| ; CHECK-SAME: i32 [[X:%.*]]) { |
| ; CHECK-NEXT: [[COND:%.*]] = icmp sge i32 [[X]], 0 |
| ; CHECK-NEXT: call void @llvm.assume(i1 [[COND]]) |
| ; CHECK-NEXT: [[ADD:%.*]] = add i32 [[X]], 1 |
| ; CHECK-NEXT: call void @use(i32 [[ADD]]) |
| ; CHECK-NEXT: ret void |
| ; |
| %cond = icmp sge i32 %x, 0 |
| call void @llvm.assume(i1 %cond) |
| %add = add i32 %x, 1 |
| call void @use(i32 %add) |
| ret void |
| } |
| |
| ; The affected value itself has a side effect, but we can still drop the |
| ; assume. |
| define void @affected_value_has_side_effect() { |
| ; CHECK-LABEL: define void @affected_value_has_side_effect() { |
| ; CHECK-NEXT: [[X:%.*]] = call i32 @get() |
| ; CHECK-NEXT: ret void |
| ; |
| %x = call i32 @get() |
| %cond = icmp sge i32 %x, 0 |
| call void @llvm.assume(i1 %cond) |
| ret void |
| } |
| |
| define i32 @affected_value_has_side_effect_and_is_used() { |
| ; CHECK-LABEL: define i32 @affected_value_has_side_effect_and_is_used() { |
| ; CHECK-NEXT: [[X:%.*]] = call i32 @get() |
| ; CHECK-NEXT: [[COND:%.*]] = icmp sge i32 [[X]], 0 |
| ; CHECK-NEXT: call void @llvm.assume(i1 [[COND]]) |
| ; CHECK-NEXT: ret i32 [[X]] |
| ; |
| %x = call i32 @get() |
| %cond = icmp sge i32 %x, 0 |
| call void @llvm.assume(i1 %cond) |
| ret i32 %x |
| } |
| |
| @g = external global i8 |
| @g2 = external global i8 |
| |
| ; Assumes on globals are currently not supported. |
| define void @assume_on_global() { |
| ; CHECK-LABEL: define void @assume_on_global() { |
| ; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr @g, i64 8) ] |
| ; CHECK-NEXT: ret void |
| ; |
| call void @llvm.assume(i1 true) ["align"(ptr @g, i64 8)] |
| ret void |
| } |
| |
| define void @assume_on_global_used_in_other_func() { |
| ; CHECK-LABEL: define void @assume_on_global_used_in_other_func() { |
| ; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr @g2, i64 8) ] |
| ; CHECK-NEXT: ret void |
| ; |
| call void @llvm.assume(i1 true) ["align"(ptr @g2, i64 8)] |
| ret void |
| } |
| |
| define ptr @other_func() { |
| ; CHECK-LABEL: define ptr @other_func() { |
| ; CHECK-NEXT: ret ptr @g2 |
| ; |
| ret ptr @g2 |
| } |