| ; 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 |
| } |