| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals | |
| ; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT | |
| ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC | |
| ; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-annotate-decl-cs -pass-remarks=attributor -disable-output < %s 2>&1 | FileCheck %s --check-prefixes=REMARKS,REMARKS-TUNIT --implicit-check-not=remark | |
| ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -pass-remarks=attributor -disable-output < %s 2>&1 | FileCheck %s --check-prefixes=REMARKS,REMARKS-CGSCC --implicit-check-not=remark | |
| 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() { | |
| ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; CHECK-LABEL: define {{[^@]+}}@load_wholly_unreachable | |
| ; CHECK-SAME: () #[[ATTR0:[0-9]+]] { | |
| ; CHECK-NEXT: unreachable | |
| ; | |
| ; REMARKS: remark: {{.*}}: Memory access through a pointer known to be null is undefined behavior; replacing with 'unreachable'. | |
| %a = load i32, ptr null | |
| ret void | |
| } | |
| define void @load_undef_pointer() { | |
| ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; CHECK-LABEL: define {{[^@]+}}@load_undef_pointer | |
| ; CHECK-SAME: () #[[ATTR0]] { | |
| ; CHECK-NEXT: unreachable | |
| ; | |
| ; REMARKS: remark: {{.*}}: Memory access through a pointer known to be undef is undefined behavior; replacing with 'unreachable'. | |
| %a = load i32, ptr undef | |
| ret void | |
| } | |
| define void @load_wholly_unreachable_volatile() { | |
| ; CHECK: Function Attrs: nofree norecurse nosync nounwind memory(none) | |
| ; CHECK-LABEL: define {{[^@]+}}@load_wholly_unreachable_volatile | |
| ; CHECK-SAME: () #[[ATTR1:[0-9]+]] { | |
| ; CHECK-NEXT: [[A:%.*]] = load volatile i32, ptr null, align 4 | |
| ; CHECK-NEXT: ret void | |
| ; | |
| %a = load volatile i32, ptr null | |
| ret void | |
| } | |
| define void @loads_wholly_unreachable() { | |
| ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; CHECK-LABEL: define {{[^@]+}}@loads_wholly_unreachable | |
| ; CHECK-SAME: () #[[ATTR0]] { | |
| ; CHECK-NEXT: unreachable | |
| ; | |
| ; REMARKS: remark: {{.*}}: Memory access through a pointer known to be null is undefined behavior; replacing with 'unreachable'. | |
| ; REMARKS: remark: {{.*}}: Memory access through a pointer known to be null is undefined behavior; replacing with 'unreachable'. | |
| %a = load i32, ptr null | |
| %b = load i32, ptr null | |
| ret void | |
| } | |
| define void @load_single_bb_unreachable(i1 %cond) { | |
| ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; CHECK-LABEL: define {{[^@]+}}@load_single_bb_unreachable | |
| ; CHECK-SAME: (i1 noundef [[COND:%.*]]) #[[ATTR0]] { | |
| ; CHECK-NEXT: br i1 [[COND]], label [[T:%.*]], label [[E:%.*]] | |
| ; CHECK: t: | |
| ; CHECK-NEXT: unreachable | |
| ; CHECK: e: | |
| ; CHECK-NEXT: ret void | |
| ; | |
| ; REMARKS: remark: {{.*}}: Memory access through a pointer known to be null is undefined behavior; replacing with 'unreachable'. | |
| br i1 %cond, label %t, label %e | |
| t: | |
| %b = load i32, ptr null | |
| br label %e | |
| e: | |
| ret void | |
| } | |
| ; Note that while the load is removed (because it's unused), the block | |
| ; is not changed to unreachable | |
| define void @load_null_pointer_is_defined() null_pointer_is_valid { | |
| ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(none) | |
| ; CHECK-LABEL: define {{[^@]+}}@load_null_pointer_is_defined | |
| ; CHECK-SAME: () #[[ATTR2:[0-9]+]] { | |
| ; CHECK-NEXT: ret void | |
| ; | |
| %a = load i32, ptr null | |
| ret void | |
| } | |
| define internal ptr @ret_null() { | |
| ; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@ret_null | |
| ; CGSCC-SAME: () #[[ATTR0]] { | |
| ; CGSCC-NEXT: ret ptr null | |
| ; | |
| ret ptr null | |
| } | |
| define void @load_null_propagated() { | |
| ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@load_null_propagated | |
| ; TUNIT-SAME: () #[[ATTR0]] { | |
| ; TUNIT-NEXT: unreachable | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@load_null_propagated | |
| ; CGSCC-SAME: () #[[ATTR3:[0-9]+]] { | |
| ; CGSCC-NEXT: ret void | |
| ; | |
| ; REMARKS-TUNIT: remark: {{.*}}: Memory access through a pointer known to be call is undefined behavior; replacing with 'unreachable'. | |
| %ptr = call ptr @ret_null() | |
| %a = load i32, ptr %ptr | |
| ret void | |
| } | |
| ; -- Store tests -- | |
| define void @store_wholly_unreachable() { | |
| ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; CHECK-LABEL: define {{[^@]+}}@store_wholly_unreachable | |
| ; CHECK-SAME: () #[[ATTR0]] { | |
| ; CHECK-NEXT: unreachable | |
| ; | |
| ; REMARKS: remark: {{.*}}: Memory access through a pointer known to be null is undefined behavior; replacing with 'unreachable'. | |
| store i32 5, ptr null | |
| ret void | |
| } | |
| define void @store_wholly_unreachable_volatile() { | |
| ; CHECK: Function Attrs: nofree norecurse nosync nounwind memory(none) | |
| ; CHECK-LABEL: define {{[^@]+}}@store_wholly_unreachable_volatile | |
| ; CHECK-SAME: () #[[ATTR1]] { | |
| ; CHECK-NEXT: store volatile i32 5, ptr null, align 4 | |
| ; CHECK-NEXT: ret void | |
| ; | |
| store volatile i32 5, ptr null | |
| ret void | |
| } | |
| define void @store_single_bb_unreachable(i1 %cond) { | |
| ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; CHECK-LABEL: define {{[^@]+}}@store_single_bb_unreachable | |
| ; CHECK-SAME: (i1 noundef [[COND:%.*]]) #[[ATTR0]] { | |
| ; CHECK-NEXT: br i1 [[COND]], label [[T:%.*]], label [[E:%.*]] | |
| ; CHECK: t: | |
| ; CHECK-NEXT: unreachable | |
| ; CHECK: e: | |
| ; CHECK-NEXT: ret void | |
| ; | |
| ; REMARKS: remark: {{.*}}: Memory access through a pointer known to be null is undefined behavior; replacing with 'unreachable'. | |
| br i1 %cond, label %t, label %e | |
| t: | |
| store i32 5, ptr null | |
| br label %e | |
| e: | |
| ret void | |
| } | |
| define void @store_null_pointer_is_defined() null_pointer_is_valid { | |
| ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(write) | |
| ; TUNIT-LABEL: define {{[^@]+}}@store_null_pointer_is_defined | |
| ; TUNIT-SAME: () #[[ATTR3:[0-9]+]] { | |
| ; TUNIT-NEXT: store i32 5, ptr null, align 4 | |
| ; TUNIT-NEXT: ret void | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(write) | |
| ; CGSCC-LABEL: define {{[^@]+}}@store_null_pointer_is_defined | |
| ; CGSCC-SAME: () #[[ATTR4:[0-9]+]] { | |
| ; CGSCC-NEXT: store i32 5, ptr null, align 4 | |
| ; CGSCC-NEXT: ret void | |
| ; | |
| store i32 5, ptr null | |
| ret void | |
| } | |
| define void @store_null_propagated() { | |
| ; ATTRIBUTOR-LABEL: @store_null_propagated( | |
| ; ATTRIBUTOR-NEXT: unreachable | |
| ; | |
| ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@store_null_propagated | |
| ; TUNIT-SAME: () #[[ATTR0]] { | |
| ; TUNIT-NEXT: unreachable | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(write) | |
| ; CGSCC-LABEL: define {{[^@]+}}@store_null_propagated | |
| ; CGSCC-SAME: () #[[ATTR5:[0-9]+]] { | |
| ; CGSCC-NEXT: [[PTR:%.*]] = call noalias align 4294967296 ptr @ret_null() #[[ATTR11:[0-9]+]] | |
| ; CGSCC-NEXT: ret void | |
| ; | |
| ; REMARKS-TUNIT: remark: {{.*}}: Memory access through a pointer known to be call is undefined behavior; replacing with 'unreachable'. | |
| %ptr = call ptr @ret_null() | |
| store i32 5, ptr %ptr | |
| ret void | |
| } | |
| ; -- AtomicRMW tests -- | |
| define void @atomicrmw_wholly_unreachable() { | |
| ; TUNIT: Function Attrs: mustprogress norecurse nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@atomicrmw_wholly_unreachable | |
| ; TUNIT-SAME: () #[[ATTR4:[0-9]+]] { | |
| ; TUNIT-NEXT: unreachable | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress norecurse nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@atomicrmw_wholly_unreachable | |
| ; CGSCC-SAME: () #[[ATTR6:[0-9]+]] { | |
| ; CGSCC-NEXT: unreachable | |
| ; | |
| ; REMARKS: remark: {{.*}}: Memory access through a pointer known to be null is undefined behavior; replacing with 'unreachable'. | |
| %a = atomicrmw add ptr null, i32 1 acquire | |
| ret void | |
| } | |
| define void @atomicrmw_single_bb_unreachable(i1 %cond) { | |
| ; TUNIT: Function Attrs: mustprogress norecurse nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@atomicrmw_single_bb_unreachable | |
| ; TUNIT-SAME: (i1 noundef [[COND:%.*]]) #[[ATTR4]] { | |
| ; TUNIT-NEXT: br i1 [[COND]], label [[T:%.*]], label [[E:%.*]] | |
| ; TUNIT: t: | |
| ; TUNIT-NEXT: unreachable | |
| ; TUNIT: e: | |
| ; TUNIT-NEXT: ret void | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress norecurse nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@atomicrmw_single_bb_unreachable | |
| ; CGSCC-SAME: (i1 noundef [[COND:%.*]]) #[[ATTR6]] { | |
| ; CGSCC-NEXT: br i1 [[COND]], label [[T:%.*]], label [[E:%.*]] | |
| ; CGSCC: t: | |
| ; CGSCC-NEXT: unreachable | |
| ; CGSCC: e: | |
| ; CGSCC-NEXT: ret void | |
| ; | |
| ; REMARKS: remark: {{.*}}: Memory access through a pointer known to be null is undefined behavior; replacing with 'unreachable'. | |
| br i1 %cond, label %t, label %e | |
| t: | |
| %a = atomicrmw add ptr null, i32 1 acquire | |
| br label %e | |
| e: | |
| ret void | |
| } | |
| define void @atomicrmw_null_pointer_is_defined() null_pointer_is_valid { | |
| ; TUNIT: Function Attrs: mustprogress norecurse nounwind null_pointer_is_valid willreturn | |
| ; TUNIT-LABEL: define {{[^@]+}}@atomicrmw_null_pointer_is_defined | |
| ; TUNIT-SAME: () #[[ATTR5:[0-9]+]] { | |
| ; TUNIT-NEXT: [[A:%.*]] = atomicrmw add ptr null, i32 1 acquire, align 4 | |
| ; TUNIT-NEXT: ret void | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress norecurse nounwind null_pointer_is_valid willreturn | |
| ; CGSCC-LABEL: define {{[^@]+}}@atomicrmw_null_pointer_is_defined | |
| ; CGSCC-SAME: () #[[ATTR7:[0-9]+]] { | |
| ; CGSCC-NEXT: [[A:%.*]] = atomicrmw add ptr null, i32 1 acquire, align 4 | |
| ; CGSCC-NEXT: ret void | |
| ; | |
| %a = atomicrmw add ptr null, i32 1 acquire | |
| ret void | |
| } | |
| define void @atomicrmw_null_propagated() { | |
| ; ATTRIBUTOR-LABEL: @atomicrmw_null_propagated( | |
| ; ATTRIBUTOR-NEXT: unreachable | |
| ; | |
| ; TUNIT: Function Attrs: mustprogress norecurse nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@atomicrmw_null_propagated | |
| ; TUNIT-SAME: () #[[ATTR4]] { | |
| ; TUNIT-NEXT: unreachable | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nounwind willreturn | |
| ; CGSCC-LABEL: define {{[^@]+}}@atomicrmw_null_propagated | |
| ; CGSCC-SAME: () #[[ATTR8:[0-9]+]] { | |
| ; CGSCC-NEXT: [[PTR:%.*]] = call noalias ptr @ret_null() #[[ATTR12:[0-9]+]] | |
| ; CGSCC-NEXT: [[A:%.*]] = atomicrmw add ptr [[PTR]], i32 1 acquire, align 4 | |
| ; CGSCC-NEXT: ret void | |
| ; | |
| ; REMARKS-TUNIT: remark: {{.*}}: Memory access through a pointer known to be call is undefined behavior; replacing with 'unreachable'. | |
| %ptr = call ptr @ret_null() | |
| %a = atomicrmw add ptr %ptr, i32 1 acquire | |
| ret void | |
| } | |
| ; -- AtomicCmpXchg tests -- | |
| define void @atomiccmpxchg_wholly_unreachable() { | |
| ; TUNIT: Function Attrs: mustprogress norecurse nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@atomiccmpxchg_wholly_unreachable | |
| ; TUNIT-SAME: () #[[ATTR4]] { | |
| ; TUNIT-NEXT: unreachable | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress norecurse nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@atomiccmpxchg_wholly_unreachable | |
| ; CGSCC-SAME: () #[[ATTR6]] { | |
| ; CGSCC-NEXT: unreachable | |
| ; | |
| ; REMARKS: remark: {{.*}}: Memory access through a pointer known to be null is undefined behavior; replacing with 'unreachable'. | |
| %a = cmpxchg ptr null, i32 2, i32 3 acq_rel monotonic | |
| ret void | |
| } | |
| define void @atomiccmpxchg_single_bb_unreachable(i1 %cond) { | |
| ; TUNIT: Function Attrs: mustprogress norecurse nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@atomiccmpxchg_single_bb_unreachable | |
| ; TUNIT-SAME: (i1 noundef [[COND:%.*]]) #[[ATTR4]] { | |
| ; TUNIT-NEXT: br i1 [[COND]], label [[T:%.*]], label [[E:%.*]] | |
| ; TUNIT: t: | |
| ; TUNIT-NEXT: unreachable | |
| ; TUNIT: e: | |
| ; TUNIT-NEXT: ret void | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress norecurse nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@atomiccmpxchg_single_bb_unreachable | |
| ; CGSCC-SAME: (i1 noundef [[COND:%.*]]) #[[ATTR6]] { | |
| ; CGSCC-NEXT: br i1 [[COND]], label [[T:%.*]], label [[E:%.*]] | |
| ; CGSCC: t: | |
| ; CGSCC-NEXT: unreachable | |
| ; CGSCC: e: | |
| ; CGSCC-NEXT: ret void | |
| ; | |
| ; REMARKS: remark: {{.*}}: Memory access through a pointer known to be null is undefined behavior; replacing with 'unreachable'. | |
| br i1 %cond, label %t, label %e | |
| t: | |
| %a = cmpxchg ptr 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 { | |
| ; TUNIT: Function Attrs: mustprogress norecurse nounwind null_pointer_is_valid willreturn | |
| ; TUNIT-LABEL: define {{[^@]+}}@atomiccmpxchg_null_pointer_is_defined | |
| ; TUNIT-SAME: () #[[ATTR5]] { | |
| ; TUNIT-NEXT: [[A:%.*]] = cmpxchg ptr null, i32 2, i32 3 acq_rel monotonic, align 4 | |
| ; TUNIT-NEXT: ret void | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress norecurse nounwind null_pointer_is_valid willreturn | |
| ; CGSCC-LABEL: define {{[^@]+}}@atomiccmpxchg_null_pointer_is_defined | |
| ; CGSCC-SAME: () #[[ATTR7]] { | |
| ; CGSCC-NEXT: [[A:%.*]] = cmpxchg ptr null, i32 2, i32 3 acq_rel monotonic, align 4 | |
| ; CGSCC-NEXT: ret void | |
| ; | |
| %a = cmpxchg ptr null, i32 2, i32 3 acq_rel monotonic | |
| ret void | |
| } | |
| define void @atomiccmpxchg_null_propagated() { | |
| ; ATTRIBUTOR-LABEL: @atomiccmpxchg_null_propagated( | |
| ; ATTRIBUTOR-NEXT: unreachable | |
| ; | |
| ; TUNIT: Function Attrs: mustprogress norecurse nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@atomiccmpxchg_null_propagated | |
| ; TUNIT-SAME: () #[[ATTR4]] { | |
| ; TUNIT-NEXT: unreachable | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nounwind willreturn | |
| ; CGSCC-LABEL: define {{[^@]+}}@atomiccmpxchg_null_propagated | |
| ; CGSCC-SAME: () #[[ATTR8]] { | |
| ; CGSCC-NEXT: [[PTR:%.*]] = call noalias ptr @ret_null() #[[ATTR12]] | |
| ; CGSCC-NEXT: [[A:%.*]] = cmpxchg ptr [[PTR]], i32 2, i32 3 acq_rel monotonic, align 4 | |
| ; CGSCC-NEXT: ret void | |
| ; | |
| ; REMARKS-TUNIT: remark: {{.*}}: Memory access through a pointer known to be call is undefined behavior; replacing with 'unreachable'. | |
| %ptr = call ptr @ret_null() | |
| %a = cmpxchg ptr %ptr, i32 2, i32 3 acq_rel monotonic | |
| ret void | |
| } | |
| ; -- Conditional branching tests -- | |
| ; Note: The unreachable on %t and %e is _not_ from AAUndefinedBehavior | |
| define i32 @cond_br_on_undef() { | |
| ; TUNIT: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@cond_br_on_undef | |
| ; TUNIT-SAME: () #[[ATTR6:[0-9]+]] { | |
| ; TUNIT-NEXT: unreachable | |
| ; TUNIT: t: | |
| ; TUNIT-NEXT: unreachable | |
| ; TUNIT: e: | |
| ; TUNIT-NEXT: unreachable | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@cond_br_on_undef | |
| ; CGSCC-SAME: () #[[ATTR9:[0-9]+]] { | |
| ; CGSCC-NEXT: unreachable | |
| ; CGSCC: t: | |
| ; CGSCC-NEXT: unreachable | |
| ; CGSCC: e: | |
| ; CGSCC-NEXT: unreachable | |
| ; | |
| ; REMARKS: remark: {{.*}}: Branch condition known to be undef is undefined behavior; replacing with 'unreachable'. | |
| br i1 undef, label %t, label %e | |
| t: | |
| ret i32 1 | |
| e: | |
| ret i32 2 | |
| } | |
| ; More complicated branching | |
| ; Valid branch - verify that this is not converted | |
| ; to unreachable. | |
| define void @cond_br_on_undef2(i1 %cond) { | |
| ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; CHECK-LABEL: define {{[^@]+}}@cond_br_on_undef2 | |
| ; CHECK-SAME: (i1 noundef [[COND:%.*]]) #[[ATTR0]] { | |
| ; CHECK-NEXT: br i1 [[COND]], label [[T1:%.*]], label [[E1:%.*]] | |
| ; CHECK: t1: | |
| ; CHECK-NEXT: unreachable | |
| ; CHECK: t2: | |
| ; CHECK-NEXT: unreachable | |
| ; CHECK: e2: | |
| ; CHECK-NEXT: unreachable | |
| ; CHECK: e1: | |
| ; CHECK-NEXT: ret void | |
| ; | |
| ; REMARKS: remark: {{.*}}: Branch condition known to be undef is undefined behavior; replacing with '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() { | |
| ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; CHECK-LABEL: define {{[^@]+}}@ret_undef | |
| ; CHECK-SAME: () #[[ATTR0]] { | |
| ; CHECK-NEXT: ret i1 undef | |
| ; | |
| ret i1 undef | |
| } | |
| define void @cond_br_on_undef_interproc() { | |
| ; TUNIT: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@cond_br_on_undef_interproc | |
| ; TUNIT-SAME: () #[[ATTR6]] { | |
| ; TUNIT-NEXT: unreachable | |
| ; TUNIT: t: | |
| ; TUNIT-NEXT: unreachable | |
| ; TUNIT: e: | |
| ; TUNIT-NEXT: unreachable | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@cond_br_on_undef_interproc | |
| ; CGSCC-SAME: () #[[ATTR3]] { | |
| ; CGSCC-NEXT: [[COND:%.*]] = call i1 @ret_undef() #[[ATTR11]] | |
| ; CGSCC-NEXT: br i1 [[COND]], label [[T:%.*]], label [[E:%.*]] | |
| ; CGSCC: t: | |
| ; CGSCC-NEXT: ret void | |
| ; CGSCC: e: | |
| ; CGSCC-NEXT: ret void | |
| ; | |
| ; REMARKS-TUNIT: remark: {{.*}}: Branch condition known to be call is undefined behavior; replacing with 'unreachable'. | |
| %cond = call i1 @ret_undef() | |
| br i1 %cond, label %t, label %e | |
| t: | |
| ret void | |
| e: | |
| ret void | |
| } | |
| define i1 @ret_undef2() { | |
| ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; CHECK-LABEL: define {{[^@]+}}@ret_undef2 | |
| ; CHECK-SAME: () #[[ATTR0]] { | |
| ; CHECK-NEXT: br i1 true, label [[T:%.*]], label [[E:%.*]] | |
| ; CHECK: t: | |
| ; CHECK-NEXT: ret i1 undef | |
| ; CHECK: e: | |
| ; CHECK-NEXT: unreachable | |
| ; | |
| 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() { | |
| ; TUNIT: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@cond_br_on_undef_interproc2 | |
| ; TUNIT-SAME: () #[[ATTR6]] { | |
| ; TUNIT-NEXT: unreachable | |
| ; TUNIT: t: | |
| ; TUNIT-NEXT: unreachable | |
| ; TUNIT: e: | |
| ; TUNIT-NEXT: unreachable | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@cond_br_on_undef_interproc2 | |
| ; CGSCC-SAME: () #[[ATTR3]] { | |
| ; CGSCC-NEXT: [[COND:%.*]] = call i1 @ret_undef2() #[[ATTR11]] | |
| ; CGSCC-NEXT: br i1 [[COND]], label [[T:%.*]], label [[E:%.*]] | |
| ; CGSCC: t: | |
| ; CGSCC-NEXT: ret void | |
| ; CGSCC: e: | |
| ; CGSCC-NEXT: ret void | |
| ; | |
| ; REMARKS-TUNIT: remark: {{.*}}: Branch condition known to be call is undefined behavior; replacing with '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. | |
| define i32 @cond_br_on_undef3() { | |
| ; TUNIT: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@cond_br_on_undef3 | |
| ; TUNIT-SAME: () #[[ATTR6]] { | |
| ; TUNIT-NEXT: unreachable | |
| ; TUNIT: t: | |
| ; TUNIT-NEXT: unreachable | |
| ; TUNIT: e: | |
| ; TUNIT-NEXT: unreachable | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@cond_br_on_undef3 | |
| ; CGSCC-SAME: () #[[ATTR9]] { | |
| ; CGSCC-NEXT: unreachable | |
| ; CGSCC: t: | |
| ; CGSCC-NEXT: unreachable | |
| ; CGSCC: e: | |
| ; CGSCC-NEXT: unreachable | |
| ; | |
| ; REMARKS: remark: {{.*}}: Branch condition known to be icmp is undefined behavior; replacing with 'unreachable'. | |
| %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() { | |
| ; TUNIT: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@cond_br_on_undef_uninit | |
| ; TUNIT-SAME: () #[[ATTR6]] { | |
| ; TUNIT-NEXT: unreachable | |
| ; TUNIT: t: | |
| ; TUNIT-NEXT: unreachable | |
| ; TUNIT: e: | |
| ; TUNIT-NEXT: unreachable | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@cond_br_on_undef_uninit | |
| ; CGSCC-SAME: () #[[ATTR9]] { | |
| ; CGSCC-NEXT: unreachable | |
| ; CGSCC: t: | |
| ; CGSCC-NEXT: unreachable | |
| ; CGSCC: e: | |
| ; CGSCC-NEXT: unreachable | |
| ; | |
| %alloc = alloca i1 | |
| %cond = load i1, ptr %alloc | |
| br i1 %cond, label %t, label %e | |
| t: | |
| ret i32 1 | |
| e: | |
| ret i32 2 | |
| } | |
| ; Note that the `load` has UB (so it will be changed to unreachable) | |
| ; and the branch is a terminator that can be constant-folded. | |
| ; We want to test that doing both won't cause a segfault. | |
| ; MODULE-NOT: @callee( | |
| define internal i32 @callee(i1 %C, ptr %A) { | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@callee | |
| ; CGSCC-SAME: () #[[ATTR0]] { | |
| ; CGSCC-NEXT: entry: | |
| ; CGSCC-NEXT: unreachable | |
| ; CGSCC: T: | |
| ; CGSCC-NEXT: unreachable | |
| ; CGSCC: F: | |
| ; CGSCC-NEXT: ret i32 1 | |
| ; | |
| ; REMARKS: remark: {{.*}}: Memory access through a pointer known to be null is undefined behavior; replacing with 'unreachable'. | |
| entry: | |
| %A.0 = load i32, ptr null | |
| br i1 %C, label %T, label %F | |
| T: | |
| ret i32 %A.0 | |
| F: | |
| ret i32 1 | |
| } | |
| define i32 @foo() { | |
| ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@foo | |
| ; TUNIT-SAME: () #[[ATTR0]] { | |
| ; TUNIT-NEXT: ret i32 1 | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@foo | |
| ; CGSCC-SAME: () #[[ATTR3]] { | |
| ; CGSCC-NEXT: [[X:%.*]] = call noundef i32 @callee() #[[ATTR11]] | |
| ; CGSCC-NEXT: ret i32 [[X]] | |
| ; | |
| %X = call i32 @callee(i1 false, ptr null) | |
| ret i32 %X | |
| } | |
| ; Tests for nonnull noundef attribute violation. | |
| ; | |
| ; Tests for argument position | |
| define void @arg_nonnull_1(ptr nonnull %a) { | |
| ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) | |
| ; TUNIT-LABEL: define {{[^@]+}}@arg_nonnull_1 | |
| ; TUNIT-SAME: (ptr nofree noundef nonnull writeonly align 4 captures(none) dereferenceable(4) [[A:%.*]]) #[[ATTR7:[0-9]+]] { | |
| ; TUNIT-NEXT: store i32 0, ptr [[A]], align 4 | |
| ; TUNIT-NEXT: ret void | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) | |
| ; CGSCC-LABEL: define {{[^@]+}}@arg_nonnull_1 | |
| ; CGSCC-SAME: (ptr nofree noundef nonnull writeonly align 4 captures(none) dereferenceable(4) [[A:%.*]]) #[[ATTR10:[0-9]+]] { | |
| ; CGSCC-NEXT: store i32 0, ptr [[A]], align 4 | |
| ; CGSCC-NEXT: ret void | |
| ; | |
| store i32 0, ptr %a | |
| ret void | |
| } | |
| define void @arg_nonnull_1_noundef_1(ptr nonnull noundef %a) { | |
| ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) | |
| ; TUNIT-LABEL: define {{[^@]+}}@arg_nonnull_1_noundef_1 | |
| ; TUNIT-SAME: (ptr nofree noundef nonnull writeonly align 4 captures(none) dereferenceable(4) [[A:%.*]]) #[[ATTR7]] { | |
| ; TUNIT-NEXT: store i32 0, ptr [[A]], align 4 | |
| ; TUNIT-NEXT: ret void | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) | |
| ; CGSCC-LABEL: define {{[^@]+}}@arg_nonnull_1_noundef_1 | |
| ; CGSCC-SAME: (ptr nofree noundef nonnull writeonly align 4 captures(none) dereferenceable(4) [[A:%.*]]) #[[ATTR10]] { | |
| ; CGSCC-NEXT: store i32 0, ptr [[A]], align 4 | |
| ; CGSCC-NEXT: ret void | |
| ; | |
| store i32 0, ptr %a | |
| ret void | |
| } | |
| define void @arg_nonnull_12(ptr nonnull %a, ptr nonnull %b, ptr %c) { | |
| ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) | |
| ; TUNIT-LABEL: define {{[^@]+}}@arg_nonnull_12 | |
| ; TUNIT-SAME: (ptr nofree nonnull writeonly captures(none) [[A:%.*]], ptr nofree nonnull writeonly captures(none) [[B:%.*]], ptr nofree writeonly [[C:%.*]]) #[[ATTR7]] { | |
| ; TUNIT-NEXT: [[D:%.*]] = icmp eq ptr [[C]], null | |
| ; TUNIT-NEXT: br i1 [[D]], label [[T:%.*]], label [[F:%.*]] | |
| ; TUNIT: t: | |
| ; TUNIT-NEXT: store i32 0, ptr [[A]], align 4 | |
| ; TUNIT-NEXT: br label [[RET:%.*]] | |
| ; TUNIT: f: | |
| ; TUNIT-NEXT: store i32 1, ptr [[B]], align 4 | |
| ; TUNIT-NEXT: br label [[RET]] | |
| ; TUNIT: ret: | |
| ; TUNIT-NEXT: ret void | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) | |
| ; CGSCC-LABEL: define {{[^@]+}}@arg_nonnull_12 | |
| ; CGSCC-SAME: (ptr nofree nonnull writeonly captures(none) [[A:%.*]], ptr nofree nonnull writeonly captures(none) [[B:%.*]], ptr nofree writeonly [[C:%.*]]) #[[ATTR10]] { | |
| ; CGSCC-NEXT: [[D:%.*]] = icmp eq ptr [[C]], null | |
| ; CGSCC-NEXT: br i1 [[D]], label [[T:%.*]], label [[F:%.*]] | |
| ; CGSCC: t: | |
| ; CGSCC-NEXT: store i32 0, ptr [[A]], align 4 | |
| ; CGSCC-NEXT: br label [[RET:%.*]] | |
| ; CGSCC: f: | |
| ; CGSCC-NEXT: store i32 1, ptr [[B]], align 4 | |
| ; CGSCC-NEXT: br label [[RET]] | |
| ; CGSCC: ret: | |
| ; CGSCC-NEXT: ret void | |
| ; | |
| %d = icmp eq ptr %c, null | |
| br i1 %d, label %t, label %f | |
| t: | |
| store i32 0, ptr %a | |
| br label %ret | |
| f: | |
| store i32 1, ptr %b | |
| br label %ret | |
| ret: | |
| ret void | |
| } | |
| define void @arg_nonnull_12_noundef_2(ptr nonnull %a, ptr noundef nonnull %b, ptr %c) { | |
| ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) | |
| ; TUNIT-LABEL: define {{[^@]+}}@arg_nonnull_12_noundef_2 | |
| ; TUNIT-SAME: (ptr nofree nonnull writeonly captures(none) [[A:%.*]], ptr nofree noundef nonnull writeonly captures(none) [[B:%.*]], ptr nofree writeonly [[C:%.*]]) #[[ATTR7]] { | |
| ; TUNIT-NEXT: [[D:%.*]] = icmp eq ptr [[C]], null | |
| ; TUNIT-NEXT: br i1 [[D]], label [[T:%.*]], label [[F:%.*]] | |
| ; TUNIT: t: | |
| ; TUNIT-NEXT: store i32 0, ptr [[A]], align 4 | |
| ; TUNIT-NEXT: br label [[RET:%.*]] | |
| ; TUNIT: f: | |
| ; TUNIT-NEXT: store i32 1, ptr [[B]], align 4 | |
| ; TUNIT-NEXT: br label [[RET]] | |
| ; TUNIT: ret: | |
| ; TUNIT-NEXT: ret void | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) | |
| ; CGSCC-LABEL: define {{[^@]+}}@arg_nonnull_12_noundef_2 | |
| ; CGSCC-SAME: (ptr nofree nonnull writeonly captures(none) [[A:%.*]], ptr nofree noundef nonnull writeonly captures(none) [[B:%.*]], ptr nofree writeonly [[C:%.*]]) #[[ATTR10]] { | |
| ; CGSCC-NEXT: [[D:%.*]] = icmp eq ptr [[C]], null | |
| ; CGSCC-NEXT: br i1 [[D]], label [[T:%.*]], label [[F:%.*]] | |
| ; CGSCC: t: | |
| ; CGSCC-NEXT: store i32 0, ptr [[A]], align 4 | |
| ; CGSCC-NEXT: br label [[RET:%.*]] | |
| ; CGSCC: f: | |
| ; CGSCC-NEXT: store i32 1, ptr [[B]], align 4 | |
| ; CGSCC-NEXT: br label [[RET]] | |
| ; CGSCC: ret: | |
| ; CGSCC-NEXT: ret void | |
| ; | |
| %d = icmp eq ptr %c, null | |
| br i1 %d, label %t, label %f | |
| t: | |
| store i32 0, ptr %a | |
| br label %ret | |
| f: | |
| store i32 1, ptr %b | |
| br label %ret | |
| ret: | |
| ret void | |
| } | |
| ; Pass null directly to argument with nonnull attribute | |
| define void @arg_nonnull_violation1_1() { | |
| ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@arg_nonnull_violation1_1 | |
| ; TUNIT-SAME: () #[[ATTR0]] { | |
| ; TUNIT-NEXT: unreachable | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@arg_nonnull_violation1_1 | |
| ; CGSCC-SAME: () #[[ATTR3]] { | |
| ; CGSCC-NEXT: unreachable | |
| ; | |
| ; REMARKS: remark: {{.*}}: Argument 0 passed to parameter of arg_nonnull_1 known to be null is undefined behavior; replacing with 'unreachable'. | |
| call void @arg_nonnull_1(ptr null) | |
| ret void | |
| } | |
| define void @arg_nonnull_violation1_2() { | |
| ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@arg_nonnull_violation1_2 | |
| ; TUNIT-SAME: () #[[ATTR0]] { | |
| ; TUNIT-NEXT: unreachable | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@arg_nonnull_violation1_2 | |
| ; CGSCC-SAME: () #[[ATTR3]] { | |
| ; CGSCC-NEXT: unreachable | |
| ; | |
| ; REMARKS: remark: {{.*}}: Argument 0 passed to parameter of arg_nonnull_1_noundef_1 known to be null is undefined behavior; replacing with 'unreachable'. | |
| call void @arg_nonnull_1_noundef_1(ptr null) | |
| ret void | |
| } | |
| ; A case that depends on value simplification | |
| define void @arg_nonnull_violation2_1(i1 %c) { | |
| ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@arg_nonnull_violation2_1 | |
| ; TUNIT-SAME: (i1 [[C:%.*]]) #[[ATTR0]] { | |
| ; TUNIT-NEXT: unreachable | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@arg_nonnull_violation2_1 | |
| ; CGSCC-SAME: (i1 [[C:%.*]]) #[[ATTR3]] { | |
| ; CGSCC-NEXT: unreachable | |
| ; | |
| ; REMARKS: remark: {{.*}}: Argument 0 passed to parameter of arg_nonnull_1 known to be null is undefined behavior; replacing with 'unreachable'. | |
| %mustnull = select i1 %c, ptr null, ptr null | |
| call void @arg_nonnull_1(ptr %mustnull) | |
| ret void | |
| } | |
| define void @arg_nonnull_violation2_2(i1 %c) { | |
| ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@arg_nonnull_violation2_2 | |
| ; TUNIT-SAME: (i1 [[C:%.*]]) #[[ATTR0]] { | |
| ; TUNIT-NEXT: unreachable | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@arg_nonnull_violation2_2 | |
| ; CGSCC-SAME: (i1 [[C:%.*]]) #[[ATTR3]] { | |
| ; CGSCC-NEXT: unreachable | |
| ; | |
| ; REMARKS: remark: {{.*}}: Argument 0 passed to parameter of arg_nonnull_1_noundef_1 known to be null is undefined behavior; replacing with 'unreachable'. | |
| %mustnull = select i1 %c, ptr null, ptr null | |
| call void @arg_nonnull_1_noundef_1(ptr %mustnull) | |
| ret void | |
| } | |
| ; Cases for single and multiple violation at a callsite | |
| define void @arg_nonnull_violation3_1(i1 %c) { | |
| ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@arg_nonnull_violation3_1 | |
| ; TUNIT-SAME: (i1 noundef [[C:%.*]]) #[[ATTR0]] { | |
| ; TUNIT-NEXT: [[PTR:%.*]] = alloca i32, align 4 | |
| ; TUNIT-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] | |
| ; TUNIT: t: | |
| ; TUNIT-NEXT: call void @arg_nonnull_12(ptr nofree noundef nonnull writeonly align 4 captures(none) dereferenceable(4) [[PTR]], ptr nofree noundef nonnull writeonly align 4 captures(none) dereferenceable(4) [[PTR]], ptr nofree noundef nonnull writeonly align 4 dereferenceable(4) [[PTR]]) #[[ATTR8:[0-9]+]] | |
| ; TUNIT-NEXT: call void @arg_nonnull_12(ptr nofree noundef nonnull writeonly align 4 captures(none) dereferenceable(4) [[PTR]], ptr nofree noundef nonnull writeonly align 4 captures(none) dereferenceable(4) [[PTR]], ptr nofree noundef writeonly null) #[[ATTR8]] | |
| ; TUNIT-NEXT: unreachable | |
| ; TUNIT: f: | |
| ; TUNIT-NEXT: unreachable | |
| ; TUNIT: ret: | |
| ; TUNIT-NEXT: ret void | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@arg_nonnull_violation3_1 | |
| ; CGSCC-SAME: (i1 noundef [[C:%.*]]) #[[ATTR3]] { | |
| ; CGSCC-NEXT: [[PTR:%.*]] = alloca i32, align 4 | |
| ; CGSCC-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] | |
| ; CGSCC: t: | |
| ; CGSCC-NEXT: call void @arg_nonnull_12(ptr nofree noundef nonnull writeonly align 4 captures(none) dereferenceable(4) [[PTR]], ptr nofree noundef nonnull writeonly align 4 captures(none) dereferenceable(4) [[PTR]], ptr nofree noundef nonnull writeonly align 4 dereferenceable(4) [[PTR]]) #[[ATTR13:[0-9]+]] | |
| ; CGSCC-NEXT: call void @arg_nonnull_12(ptr nofree noundef nonnull writeonly align 4 captures(none) dereferenceable(4) [[PTR]], ptr nofree noundef nonnull writeonly align 4 captures(none) dereferenceable(4) [[PTR]], ptr nofree noundef writeonly null) #[[ATTR13]] | |
| ; CGSCC-NEXT: unreachable | |
| ; CGSCC: f: | |
| ; CGSCC-NEXT: unreachable | |
| ; CGSCC: ret: | |
| ; CGSCC-NEXT: ret void | |
| ; | |
| ; REMARKS: remark: {{.*}}: Argument 1 passed to parameter of arg_nonnull_12 known to be null is undefined behavior; replacing with 'unreachable'. | |
| ; REMARKS: remark: {{.*}}: Argument 1 passed to parameter of arg_nonnull_12 known to be null is undefined behavior; replacing with 'unreachable'. | |
| ; REMARKS: remark: {{.*}}: Argument 0 passed to parameter of arg_nonnull_12 known to be null is undefined behavior; replacing with 'unreachable'. | |
| ; REMARKS: remark: {{.*}}: Argument 0 passed to parameter of arg_nonnull_12 known to be null is undefined behavior; replacing with 'unreachable'. | |
| ; REMARKS: remark: {{.*}}: Argument 0 passed to parameter of arg_nonnull_12 known to be null is undefined behavior; replacing with 'unreachable'. | |
| ; REMARKS: remark: {{.*}}: Argument 0 passed to parameter of arg_nonnull_12 known to be null is undefined behavior; replacing with 'unreachable'. | |
| %ptr = alloca i32 | |
| br i1 %c, label %t, label %f | |
| t: | |
| call void @arg_nonnull_12(ptr %ptr, ptr %ptr, ptr %ptr) | |
| call void @arg_nonnull_12(ptr %ptr, ptr %ptr, ptr null) | |
| call void @arg_nonnull_12(ptr %ptr, ptr null, ptr %ptr) | |
| call void @arg_nonnull_12(ptr %ptr, ptr null, ptr null) | |
| br label %ret | |
| f: | |
| call void @arg_nonnull_12(ptr null, ptr %ptr, ptr %ptr) | |
| call void @arg_nonnull_12(ptr null, ptr %ptr, ptr null) | |
| call void @arg_nonnull_12(ptr null, ptr null, ptr %ptr) | |
| call void @arg_nonnull_12(ptr null, ptr null, ptr null) | |
| br label %ret | |
| ret: | |
| ret void | |
| } | |
| define void @arg_nonnull_violation3_2(i1 %c) { | |
| ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@arg_nonnull_violation3_2 | |
| ; TUNIT-SAME: (i1 noundef [[C:%.*]]) #[[ATTR0]] { | |
| ; TUNIT-NEXT: [[PTR:%.*]] = alloca i32, align 4 | |
| ; TUNIT-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] | |
| ; TUNIT: t: | |
| ; TUNIT-NEXT: call void @arg_nonnull_12_noundef_2(ptr nofree noundef nonnull writeonly align 4 captures(none) dereferenceable(4) [[PTR]], ptr nofree noundef nonnull writeonly align 4 captures(none) dereferenceable(4) [[PTR]], ptr nofree noundef nonnull writeonly align 4 dereferenceable(4) [[PTR]]) #[[ATTR8]] | |
| ; TUNIT-NEXT: call void @arg_nonnull_12_noundef_2(ptr nofree noundef nonnull writeonly align 4 captures(none) dereferenceable(4) [[PTR]], ptr nofree noundef nonnull writeonly align 4 captures(none) dereferenceable(4) [[PTR]], ptr nofree noundef writeonly null) #[[ATTR8]] | |
| ; TUNIT-NEXT: unreachable | |
| ; TUNIT: f: | |
| ; TUNIT-NEXT: unreachable | |
| ; TUNIT: ret: | |
| ; TUNIT-NEXT: ret void | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@arg_nonnull_violation3_2 | |
| ; CGSCC-SAME: (i1 noundef [[C:%.*]]) #[[ATTR3]] { | |
| ; CGSCC-NEXT: [[PTR:%.*]] = alloca i32, align 4 | |
| ; CGSCC-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] | |
| ; CGSCC: t: | |
| ; CGSCC-NEXT: call void @arg_nonnull_12_noundef_2(ptr nofree noundef nonnull writeonly align 4 captures(none) dereferenceable(4) [[PTR]], ptr nofree noundef nonnull writeonly align 4 captures(none) dereferenceable(4) [[PTR]], ptr nofree noundef nonnull writeonly align 4 dereferenceable(4) [[PTR]]) #[[ATTR13]] | |
| ; CGSCC-NEXT: call void @arg_nonnull_12_noundef_2(ptr nofree noundef nonnull writeonly align 4 captures(none) dereferenceable(4) [[PTR]], ptr nofree noundef nonnull writeonly align 4 captures(none) dereferenceable(4) [[PTR]], ptr nofree noundef writeonly null) #[[ATTR13]] | |
| ; CGSCC-NEXT: unreachable | |
| ; CGSCC: f: | |
| ; CGSCC-NEXT: unreachable | |
| ; CGSCC: ret: | |
| ; CGSCC-NEXT: ret void | |
| ; | |
| ; REMARKS: remark: {{.*}}: Argument 1 passed to parameter of arg_nonnull_12_noundef_2 known to be null is undefined behavior; replacing with 'unreachable'. | |
| ; REMARKS: remark: {{.*}}: Argument 1 passed to parameter of arg_nonnull_12_noundef_2 known to be null is undefined behavior; replacing with 'unreachable'. | |
| ; REMARKS: remark: {{.*}}: Argument 0 passed to parameter of arg_nonnull_12_noundef_2 known to be null is undefined behavior; replacing with 'unreachable'. | |
| ; REMARKS: remark: {{.*}}: Argument 0 passed to parameter of arg_nonnull_12_noundef_2 known to be null is undefined behavior; replacing with 'unreachable'. | |
| ; REMARKS: remark: {{.*}}: Argument 0 passed to parameter of arg_nonnull_12_noundef_2 known to be null is undefined behavior; replacing with 'unreachable'. | |
| ; REMARKS: remark: {{.*}}: Argument 0 passed to parameter of arg_nonnull_12_noundef_2 known to be null is undefined behavior; replacing with 'unreachable'. | |
| %ptr = alloca i32 | |
| br i1 %c, label %t, label %f | |
| t: | |
| call void @arg_nonnull_12_noundef_2(ptr %ptr, ptr %ptr, ptr %ptr) | |
| call void @arg_nonnull_12_noundef_2(ptr %ptr, ptr %ptr, ptr null) | |
| call void @arg_nonnull_12_noundef_2(ptr %ptr, ptr null, ptr %ptr) | |
| call void @arg_nonnull_12_noundef_2(ptr %ptr, ptr null, ptr null) | |
| br label %ret | |
| f: | |
| call void @arg_nonnull_12_noundef_2(ptr null, ptr %ptr, ptr %ptr) | |
| call void @arg_nonnull_12_noundef_2(ptr null, ptr %ptr, ptr null) | |
| call void @arg_nonnull_12_noundef_2(ptr null, ptr null, ptr %ptr) | |
| call void @arg_nonnull_12_noundef_2(ptr null, ptr null, ptr null) | |
| br label %ret | |
| ret: | |
| ret void | |
| } | |
| ; Tests for returned position | |
| define nonnull ptr @returned_nonnnull(i32 %c) { | |
| ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; CHECK-LABEL: define {{[^@]+}}@returned_nonnnull | |
| ; CHECK-SAME: (i32 noundef [[C:%.*]]) #[[ATTR0]] { | |
| ; CHECK-NEXT: switch i32 [[C]], label [[ONDEFAULT:%.*]] [ | |
| ; CHECK-NEXT: i32 0, label [[ONZERO:%.*]] | |
| ; CHECK-NEXT: i32 1, label [[ONONE:%.*]] | |
| ; CHECK-NEXT: ] | |
| ; CHECK: onzero: | |
| ; CHECK-NEXT: [[PTR:%.*]] = alloca i32, align 4 | |
| ; CHECK-NEXT: ret ptr [[PTR]] | |
| ; CHECK: onone: | |
| ; CHECK-NEXT: ret ptr null | |
| ; CHECK: ondefault: | |
| ; CHECK-NEXT: ret ptr undef | |
| ; | |
| switch i32 %c, label %ondefault [ i32 0, label %onzero | |
| i32 1, label %onone ] | |
| onzero: | |
| %ptr = alloca i32 | |
| ret ptr %ptr | |
| onone: | |
| ret ptr null | |
| ondefault: | |
| ret ptr undef | |
| } | |
| define noundef ptr @returned_noundef(i32 %c) { | |
| ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; CHECK-LABEL: define {{[^@]+}}@returned_noundef | |
| ; CHECK-SAME: (i32 noundef [[C:%.*]]) #[[ATTR0]] { | |
| ; CHECK-NEXT: switch i32 [[C]], label [[ONDEFAULT:%.*]] [ | |
| ; CHECK-NEXT: i32 0, label [[ONZERO:%.*]] | |
| ; CHECK-NEXT: i32 1, label [[ONONE:%.*]] | |
| ; CHECK-NEXT: ] | |
| ; CHECK: onzero: | |
| ; CHECK-NEXT: [[PTR:%.*]] = alloca i32, align 4 | |
| ; CHECK-NEXT: ret ptr [[PTR]] | |
| ; CHECK: onone: | |
| ; CHECK-NEXT: ret ptr null | |
| ; CHECK: ondefault: | |
| ; CHECK-NEXT: unreachable | |
| ; | |
| ; REMARKS: remark: {{.*}}: Value returned known to be undef is undefined behavior; replacing with 'unreachable'. | |
| switch i32 %c, label %ondefault [ i32 0, label %onzero | |
| i32 1, label %onone ] | |
| onzero: | |
| %ptr = alloca i32 | |
| ret ptr %ptr | |
| onone: | |
| ret ptr null | |
| ondefault: | |
| ret ptr undef | |
| } | |
| define nonnull noundef ptr @returned_nonnnull_noundef(i32 %c) { | |
| ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; CHECK-LABEL: define {{[^@]+}}@returned_nonnnull_noundef | |
| ; CHECK-SAME: (i32 noundef [[C:%.*]]) #[[ATTR0]] { | |
| ; CHECK-NEXT: switch i32 [[C]], label [[ONDEFAULT:%.*]] [ | |
| ; CHECK-NEXT: i32 0, label [[ONZERO:%.*]] | |
| ; CHECK-NEXT: i32 1, label [[ONONE:%.*]] | |
| ; CHECK-NEXT: ] | |
| ; CHECK: onzero: | |
| ; CHECK-NEXT: [[PTR:%.*]] = alloca i32, align 4 | |
| ; CHECK-NEXT: ret ptr [[PTR]] | |
| ; CHECK: onone: | |
| ; CHECK-NEXT: unreachable | |
| ; CHECK: ondefault: | |
| ; CHECK-NEXT: unreachable | |
| ; | |
| ; REMARKS: remark: {{.*}}: Value returned known to be null is undefined behavior; replacing with 'unreachable'. | |
| ; REMARKS: remark: {{.*}}: Value returned known to be undef is undefined behavior; replacing with 'unreachable'. | |
| switch i32 %c, label %ondefault [ i32 0, label %onzero | |
| i32 1, label %onone ] | |
| onzero: | |
| %ptr = alloca i32 | |
| ret ptr %ptr | |
| onone: | |
| ret ptr null | |
| ondefault: | |
| ret ptr undef | |
| } | |
| define noundef i32 @returned_nonnnull_noundef_int() { | |
| ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; CHECK-LABEL: define {{[^@]+}}@returned_nonnnull_noundef_int | |
| ; CHECK-SAME: () #[[ATTR0]] { | |
| ; CHECK-NEXT: ret i32 0 | |
| ; | |
| ret i32 0 | |
| } | |
| declare void @callee_int_arg(i32) | |
| define void @callsite_noundef_1() { | |
| ; CHECK-LABEL: define {{[^@]+}}@callsite_noundef_1() { | |
| ; CHECK-NEXT: call void @callee_int_arg(i32 noundef 0) | |
| ; CHECK-NEXT: ret void | |
| ; | |
| call void @callee_int_arg(i32 noundef 0) | |
| ret void | |
| } | |
| declare void @callee_ptr_arg(ptr) | |
| define void @callsite_noundef_2() { | |
| ; CHECK-LABEL: define {{[^@]+}}@callsite_noundef_2() { | |
| ; CHECK-NEXT: unreachable | |
| ; | |
| ; REMARKS: remark: {{.*}}: Argument 0 passed to parameter of callee_ptr_arg known to be undef is undefined behavior; replacing with 'unreachable'. | |
| call void @callee_ptr_arg(ptr noundef undef) | |
| ret void | |
| } | |
| define i32 @argument_noundef1(i32 noundef %c) { | |
| ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; CHECK-LABEL: define {{[^@]+}}@argument_noundef1 | |
| ; CHECK-SAME: (i32 noundef returned [[C:%.*]]) #[[ATTR0]] { | |
| ; CHECK-NEXT: ret i32 [[C]] | |
| ; | |
| ret i32 %c | |
| } | |
| define i32 @violate_noundef_nonpointer() { | |
| ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@violate_noundef_nonpointer | |
| ; TUNIT-SAME: () #[[ATTR0]] { | |
| ; TUNIT-NEXT: ret i32 undef | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@violate_noundef_nonpointer | |
| ; CGSCC-SAME: () #[[ATTR3]] { | |
| ; CGSCC-NEXT: unreachable | |
| ; | |
| ; REMARKS-CGSCC: remark: {{.*}}: Argument 0 passed to parameter of argument_noundef1 known to be undef is undefined behavior; replacing with 'unreachable'. | |
| %ret = call i32 @argument_noundef1(i32 undef) | |
| ret i32 %ret | |
| } | |
| define ptr @argument_noundef2(ptr noundef %c) { | |
| ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; CHECK-LABEL: define {{[^@]+}}@argument_noundef2 | |
| ; CHECK-SAME: (ptr nofree noundef readnone returned "no-capture-maybe-returned" [[C:%.*]]) #[[ATTR0]] { | |
| ; CHECK-NEXT: ret ptr [[C]] | |
| ; | |
| ret ptr %c | |
| } | |
| define ptr @violate_noundef_pointer() { | |
| ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@violate_noundef_pointer | |
| ; TUNIT-SAME: () #[[ATTR0]] { | |
| ; TUNIT-NEXT: ret ptr undef | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@violate_noundef_pointer | |
| ; CGSCC-SAME: () #[[ATTR3]] { | |
| ; CGSCC-NEXT: ret ptr undef | |
| ; | |
| %ret = call ptr @argument_noundef2(ptr undef) | |
| ret ptr %ret | |
| } | |
| define internal noundef i32 @assumed_undef_is_ok(i1 %c, i32 %arg) { | |
| ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@assumed_undef_is_ok | |
| ; CGSCC-SAME: (i1 noundef [[C:%.*]]) #[[ATTR3]] { | |
| ; CGSCC-NEXT: br i1 [[C]], label [[REC:%.*]], label [[RET:%.*]] | |
| ; CGSCC: rec: | |
| ; CGSCC-NEXT: br label [[RET]] | |
| ; CGSCC: ret: | |
| ; CGSCC-NEXT: ret i32 0 | |
| ; | |
| %stack = alloca i32 | |
| store i32 %arg, ptr %stack | |
| br i1 %c, label %rec, label %ret | |
| rec: | |
| %call = call i32 @assumed_undef_is_ok(i1 false, i32 0) | |
| store i32 %call, ptr %stack | |
| br label %ret | |
| ret: | |
| %l = load i32, ptr %stack | |
| ret i32 %l | |
| } | |
| define noundef i32 @assumed_undef_is_ok_caller(i1 %c) { | |
| ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) | |
| ; TUNIT-LABEL: define {{[^@]+}}@assumed_undef_is_ok_caller | |
| ; TUNIT-SAME: (i1 [[C:%.*]]) #[[ATTR0]] { | |
| ; TUNIT-NEXT: ret i32 0 | |
| ; | |
| ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) | |
| ; CGSCC-LABEL: define {{[^@]+}}@assumed_undef_is_ok_caller | |
| ; CGSCC-SAME: (i1 noundef [[C:%.*]]) #[[ATTR3]] { | |
| ; CGSCC-NEXT: [[CALL:%.*]] = call i32 @assumed_undef_is_ok(i1 noundef [[C]]) #[[ATTR11]] | |
| ; CGSCC-NEXT: ret i32 [[CALL]] | |
| ; | |
| %call = call i32 @assumed_undef_is_ok(i1 %c, i32 undef) | |
| ret i32 %call | |
| } | |
| ;. | |
| ; TUNIT: attributes #[[ATTR0]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) } | |
| ; TUNIT: attributes #[[ATTR1]] = { nofree norecurse nosync nounwind memory(none) } | |
| ; TUNIT: attributes #[[ATTR2]] = { mustprogress nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(none) } | |
| ; TUNIT: attributes #[[ATTR3]] = { mustprogress nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(write) } | |
| ; TUNIT: attributes #[[ATTR4]] = { mustprogress norecurse nounwind willreturn memory(none) } | |
| ; TUNIT: attributes #[[ATTR5]] = { mustprogress norecurse nounwind null_pointer_is_valid willreturn } | |
| ; TUNIT: attributes #[[ATTR6]] = { mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(none) } | |
| ; TUNIT: attributes #[[ATTR7]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) } | |
| ; TUNIT: attributes #[[ATTR8]] = { nofree nosync nounwind willreturn memory(write) } | |
| ;. | |
| ; CGSCC: attributes #[[ATTR0]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) } | |
| ; CGSCC: attributes #[[ATTR1]] = { nofree norecurse nosync nounwind memory(none) } | |
| ; CGSCC: attributes #[[ATTR2]] = { mustprogress nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(none) } | |
| ; CGSCC: attributes #[[ATTR3]] = { mustprogress nofree nosync nounwind willreturn memory(none) } | |
| ; CGSCC: attributes #[[ATTR4]] = { mustprogress nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(write) } | |
| ; CGSCC: attributes #[[ATTR5]] = { mustprogress nofree nosync nounwind willreturn memory(write) } | |
| ; CGSCC: attributes #[[ATTR6]] = { mustprogress norecurse nounwind willreturn memory(none) } | |
| ; CGSCC: attributes #[[ATTR7]] = { mustprogress norecurse nounwind null_pointer_is_valid willreturn } | |
| ; CGSCC: attributes #[[ATTR8]] = { mustprogress nounwind willreturn } | |
| ; CGSCC: attributes #[[ATTR9]] = { mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(none) } | |
| ; CGSCC: attributes #[[ATTR10]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) } | |
| ; CGSCC: attributes #[[ATTR11]] = { nofree nosync willreturn } | |
| ; CGSCC: attributes #[[ATTR12]] = { willreturn } | |
| ; CGSCC: attributes #[[ATTR13]] = { nofree nounwind willreturn memory(write) } | |
| ;. |