Don't drop `assume` intrinsic when it's using `public_type_test ` intrinsic, as it could be used by devirtualization.
331 lines
9.9 KiB
LLVM
331 lines
9.9 KiB
LLVM
; 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 @public_type_test(ptr %x) {
|
|
; CHECK-LABEL: define void @public_type_test(
|
|
; CHECK-SAME: ptr [[X:%.*]]) {
|
|
; CHECK-NEXT: [[TEST:%.*]] = call i1 @llvm.public.type.test(ptr [[X]], metadata !"typeid")
|
|
; CHECK-NEXT: call void @llvm.assume(i1 [[TEST]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%test = call i1 @llvm.public.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
|
|
}
|