blob: fd0ddb1ebb850f72106db4bdc0dfec807d0e4152 [file] [log] [blame]
; RUN: opt --attributor --attributor-disable=false -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
; Test cases specifically designed for the "undefined behavior" abstract function attribute.
; We want to verify that whenever undefined behavior is assumed, the code becomes unreachable.
; We use FIXME's to indicate problems and missing attributes.
; -- Load tests --
define void @load_wholly_unreachable() {
; ATTRIBUTOR-LABEL: @load_wholly_unreachable(
; ATTRIBUTOR-NEXT: unreachable
;
%a = load i32, i32* null
ret void
}
define void @loads_wholly_unreachable() {
; ATTRIBUTOR-LABEL: @loads_wholly_unreachable(
; ATTRIBUTOR-NEXT: unreachable
;
%a = load i32, i32* null
%b = load i32, i32* null
ret void
}
define void @load_single_bb_unreachable(i1 %cond) {
; ATTRIBUTOR-LABEL: @load_single_bb_unreachable(
; ATTRIBUTOR-NEXT: br i1 [[COND:%.*]], label [[T:%.*]], label [[E:%.*]]
; ATTRIBUTOR: t:
; ATTRIBUTOR-NEXT: unreachable
; ATTRIBUTOR: e:
; ATTRIBUTOR-NEXT: ret void
;
br i1 %cond, label %t, label %e
t:
%b = load i32, i32* null
br label %e
e:
ret void
}
define void @load_null_pointer_is_defined() "null-pointer-is-valid"="true" {
; ATTRIBUTOR-LABEL: @load_null_pointer_is_defined(
; ATTRIBUTOR-NEXT: [[A:%.*]] = load i32, i32* null
; ATTRIBUTOR-NEXT: ret void
;
%a = load i32, i32* null
ret void
}
define internal i32* @ret_null() {
ret i32* null
}
; FIXME: null is propagated but the instruction
; is not changed to unreachable.
define void @load_null_propagated() {
; ATTRIBUTOR-LABEL: @load_null_propagated(
; ATTRIBUTOR-NEXT: [[A:%.*]] = load i32, i32* null
; ATTRIBUTOR-NEXT: ret void
;
%ptr = call i32* @ret_null()
%a = load i32, i32* %ptr
ret void
}
; -- Store tests --
define void @store_wholly_unreachable() {
; ATTRIBUTOR-LABEL: @store_wholly_unreachable(
; ATTRIBUTOR-NEXT: unreachable
;
store i32 5, i32* null
ret void
}
define void @store_single_bb_unreachable(i1 %cond) {
; ATTRIBUTOR-LABEL: @store_single_bb_unreachable(
; ATTRIBUTOR-NEXT: br i1 [[COND:%.*]], label [[T:%.*]], label [[E:%.*]]
; ATTRIBUTOR: t:
; ATTRIBUTOR-NEXT: unreachable
; ATTRIBUTOR: e:
; ATTRIBUTOR-NEXT: ret void
;
br i1 %cond, label %t, label %e
t:
store i32 5, i32* null
br label %e
e:
ret void
}
define void @store_null_pointer_is_defined() "null-pointer-is-valid"="true" {
; ATTRIBUTOR-LABEL: @store_null_pointer_is_defined(
; ATTRIBUTOR-NEXT: store i32 5, i32* null
; ATTRIBUTOR-NEXT: ret void
;
store i32 5, i32* null
ret void
}
; -- AtomicRMW tests --
define void @atomicrmw_wholly_unreachable() {
; ATTRIBUTOR-LABEL: @atomicrmw_wholly_unreachable(
; ATTRIBUTOR-NEXT: unreachable
;
%a = atomicrmw add i32* null, i32 1 acquire
ret void
}
define void @atomicrmw_single_bb_unreachable(i1 %cond) {
; ATTRIBUTOR-LABEL: @atomicrmw_single_bb_unreachable(
; ATTRIBUTOR-NEXT: br i1 [[COND:%.*]], label [[T:%.*]], label [[E:%.*]]
; ATTRIBUTOR: t:
; ATTRIBUTOR-NEXT: unreachable
; ATTRIBUTOR: e:
; ATTRIBUTOR-NEXT: ret void
;
br i1 %cond, label %t, label %e
t:
%a = atomicrmw add i32* null, i32 1 acquire
br label %e
e:
ret void
}
define void @atomicrmw_null_pointer_is_defined() "null-pointer-is-valid"="true" {
; ATTRIBUTOR-LABEL: @atomicrmw_null_pointer_is_defined(
; ATTRIBUTOR-NEXT: [[A:%.*]] = atomicrmw add i32* null, i32 1 acquire
; ATTRIBUTOR-NEXT: ret void
;
%a = atomicrmw add i32* null, i32 1 acquire
ret void
}
; -- AtomicCmpXchg tests --
define void @atomiccmpxchg_wholly_unreachable() {
; ATTRIBUTOR-LABEL: @atomiccmpxchg_wholly_unreachable(
; ATTRIBUTOR-NEXT: unreachable
;
%a = cmpxchg i32* null, i32 2, i32 3 acq_rel monotonic
ret void
}
define void @atomiccmpxchg_single_bb_unreachable(i1 %cond) {
; ATTRIBUTOR-LABEL: @atomiccmpxchg_single_bb_unreachable(
; ATTRIBUTOR-NEXT: br i1 [[COND:%.*]], label [[T:%.*]], label [[E:%.*]]
; ATTRIBUTOR: t:
; ATTRIBUTOR-NEXT: unreachable
; ATTRIBUTOR: e:
; ATTRIBUTOR-NEXT: ret void
;
br i1 %cond, label %t, label %e
t:
%a = cmpxchg i32* null, i32 2, i32 3 acq_rel monotonic
br label %e
e:
ret void
}
define void @atomiccmpxchg_null_pointer_is_defined() "null-pointer-is-valid"="true" {
; ATTRIBUTOR-LABEL: @atomiccmpxchg_null_pointer_is_defined(
; ATTRIBUTOR-NEXT: [[A:%.*]] = cmpxchg i32* null, i32 2, i32 3 acq_rel monotonic
; ATTRIBUTOR-NEXT: ret void
;
%a = cmpxchg i32* null, i32 2, i32 3 acq_rel monotonic
ret void
}
; Note: The unreachable on %t and %e is _not_ from AAUndefinedBehavior
define i32 @cond_br_on_undef() {
; ATTRIBUTOR-LABEL: @cond_br_on_undef(
; ATTRIBUTOR-NEXT: unreachable
; ATTRIBUTOR: t:
; ATTRIBUTOR-NEXT: unreachable
; ATTRIBUTOR: e:
; ATTRIBUTOR-NEXT: unreachable
;
br i1 undef, label %t, label %e
t:
ret i32 1
e:
ret i32 2
}
; More complicated branching
define void @cond_br_on_undef2(i1 %cond) {
; ATTRIBUTOR-LABEL: @cond_br_on_undef2(
; ATTRIBUTOR-NEXT: br i1 [[COND:%.*]], label [[T1:%.*]], label [[E1:%.*]]
; ATTRIBUTOR: t1:
; ATTRIBUTOR-NEXT: unreachable
; ATTRIBUTOR: t2:
; ATTRIBUTOR-NEXT: unreachable
; ATTRIBUTOR: e2:
; ATTRIBUTOR-NEXT: unreachable
; ATTRIBUTOR: e1:
; ATTRIBUTOR-NEXT: ret void
;
; Valid branch - verify that this is not converted
; to unreachable.
br i1 %cond, label %t1, label %e1
t1:
br i1 undef, label %t2, label %e2
t2:
ret void
e2:
ret void
e1:
ret void
}
define i1 @ret_undef() {
ret i1 undef
}
define void @cond_br_on_undef_interproc() {
; ATTRIBUTOR-LABEL: @cond_br_on_undef_interproc(
; ATTRIBUTOR-NEXT: %cond = call i1 @ret_undef()
; ATTRIBUTOR-NEXT: unreachable
; ATTRIBUTOR: t:
; ATTRIBUTOR-NEXT: unreachable
; ATTRIBUTOR: e:
; ATTRIBUTOR-NEXT: unreachable
%cond = call i1 @ret_undef()
br i1 %cond, label %t, label %e
t:
ret void
e:
ret void
}
define i1 @ret_undef2() {
br i1 true, label %t, label %e
t:
ret i1 undef
e:
ret i1 undef
}
; More complicated interproc deduction of undef
define void @cond_br_on_undef_interproc2() {
; ATTRIBUTOR-LABEL: @cond_br_on_undef_interproc2(
; ATTRIBUTOR-NEXT: %cond = call i1 @ret_undef2()
; ATTRIBUTOR-NEXT: unreachable
; ATTRIBUTOR: t:
; ATTRIBUTOR-NEXT: unreachable
; ATTRIBUTOR: e:
; ATTRIBUTOR-NEXT: unreachable
%cond = call i1 @ret_undef2()
br i1 %cond, label %t, label %e
t:
ret void
e:
ret void
}
; Branch on undef that depends on propagation of
; undef of a previous instruction.
; FIXME: Currently it doesn't propagate the undef.
define i32 @cond_br_on_undef3() {
; ATTRIBUTOR-LABEL: @cond_br_on_undef3(
; ATTRIBUTOR-NEXT: %cond = icmp ne i32 1, undef
; ATTRIBUTOR-NEXT: br i1 %cond, label %t, label %e
; ATTRIBUTOR: t:
; ATTRIBUTOR-NEXT: ret i32 1
; ATTRIBUTOR: e:
; ATTRIBUTOR-NEXT: ret i32 2
%cond = icmp ne i32 1, undef
br i1 %cond, label %t, label %e
t:
ret i32 1
e:
ret i32 2
}
; Branch on undef because of uninitialized value.
; FIXME: Currently it doesn't propagate the undef.
define i32 @cond_br_on_undef_uninit() {
; ATTRIBUTOR-LABEL: @cond_br_on_undef_uninit(
; ATTRIBUTOR-NEXT: %alloc = alloca i1
; ATTRIBUTOR-NEXT: %cond = load i1, i1* %alloc
; ATTRIBUTOR-NEXT: br i1 %cond, label %t, label %e
; ATTRIBUTOR: t:
; ATTRIBUTOR-NEXT: ret i32 1
; ATTRIBUTOR: e:
; ATTRIBUTOR-NEXT: ret i32 2
%alloc = alloca i1
%cond = load i1, i1* %alloc
br i1 %cond, label %t, label %e
t:
ret i32 1
e:
ret i32 2
}