1; RUN: opt --attributor --attributor-disable=false -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR 2 3target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 4 5; Test cases specifically designed for the "undefined behavior" abstract function attribute. 6; We want to verify that whenever undefined behavior is assumed, the code becomes unreachable. 7; We use FIXME's to indicate problems and missing attributes. 8 9; -- Load tests -- 10 11define void @load_wholly_unreachable() { 12; ATTRIBUTOR-LABEL: @load_wholly_unreachable( 13; ATTRIBUTOR-NEXT: unreachable 14; 15 %a = load i32, i32* null 16 ret void 17} 18 19define void @loads_wholly_unreachable() { 20; ATTRIBUTOR-LABEL: @loads_wholly_unreachable( 21; ATTRIBUTOR-NEXT: unreachable 22; 23 %a = load i32, i32* null 24 %b = load i32, i32* null 25 ret void 26} 27 28 29define void @load_single_bb_unreachable(i1 %cond) { 30; ATTRIBUTOR-LABEL: @load_single_bb_unreachable( 31; ATTRIBUTOR-NEXT: br i1 [[COND:%.*]], label [[T:%.*]], label [[E:%.*]] 32; ATTRIBUTOR: t: 33; ATTRIBUTOR-NEXT: unreachable 34; ATTRIBUTOR: e: 35; ATTRIBUTOR-NEXT: ret void 36; 37 br i1 %cond, label %t, label %e 38t: 39 %b = load i32, i32* null 40 br label %e 41e: 42 ret void 43} 44 45; Note that while the load is removed (because it's unused), the block 46; is not changed to unreachable 47define void @load_null_pointer_is_defined() "null-pointer-is-valid"="true" { 48; ATTRIBUTOR-LABEL: @load_null_pointer_is_defined( 49; ATTRIBUTOR-NEXT: ret void 50; 51 %a = load i32, i32* null 52 ret void 53} 54 55define internal i32* @ret_null() { 56 ret i32* null 57} 58 59define void @load_null_propagated() { 60; ATTRIBUTOR-LABEL: @load_null_propagated( 61; ATTRIBUTOR-NEXT: unreachable 62; 63 %ptr = call i32* @ret_null() 64 %a = load i32, i32* %ptr 65 ret void 66} 67 68; -- Store tests -- 69 70define void @store_wholly_unreachable() { 71; ATTRIBUTOR-LABEL: @store_wholly_unreachable( 72; ATTRIBUTOR-NEXT: unreachable 73; 74 store i32 5, i32* null 75 ret void 76} 77 78define void @store_single_bb_unreachable(i1 %cond) { 79; ATTRIBUTOR-LABEL: @store_single_bb_unreachable( 80; ATTRIBUTOR-NEXT: br i1 [[COND:%.*]], label [[T:%.*]], label [[E:%.*]] 81; ATTRIBUTOR: t: 82; ATTRIBUTOR-NEXT: unreachable 83; ATTRIBUTOR: e: 84; ATTRIBUTOR-NEXT: ret void 85; 86 br i1 %cond, label %t, label %e 87t: 88 store i32 5, i32* null 89 br label %e 90e: 91 ret void 92} 93 94define void @store_null_pointer_is_defined() "null-pointer-is-valid"="true" { 95; ATTRIBUTOR-LABEL: @store_null_pointer_is_defined( 96; ATTRIBUTOR-NEXT: store i32 5, i32* null 97; ATTRIBUTOR-NEXT: ret void 98; 99 store i32 5, i32* null 100 ret void 101} 102 103define void @store_null_propagated() { 104; ATTRIBUTOR-LABEL: @store_null_propagated( 105; ATTRIBUTOR-NEXT: unreachable 106; 107 %ptr = call i32* @ret_null() 108 store i32 5, i32* %ptr 109 ret void 110} 111 112; -- AtomicRMW tests -- 113 114define void @atomicrmw_wholly_unreachable() { 115; ATTRIBUTOR-LABEL: @atomicrmw_wholly_unreachable( 116; ATTRIBUTOR-NEXT: unreachable 117; 118 %a = atomicrmw add i32* null, i32 1 acquire 119 ret void 120} 121 122define void @atomicrmw_single_bb_unreachable(i1 %cond) { 123; ATTRIBUTOR-LABEL: @atomicrmw_single_bb_unreachable( 124; ATTRIBUTOR-NEXT: br i1 [[COND:%.*]], label [[T:%.*]], label [[E:%.*]] 125; ATTRIBUTOR: t: 126; ATTRIBUTOR-NEXT: unreachable 127; ATTRIBUTOR: e: 128; ATTRIBUTOR-NEXT: ret void 129; 130 br i1 %cond, label %t, label %e 131t: 132 %a = atomicrmw add i32* null, i32 1 acquire 133 br label %e 134e: 135 ret void 136} 137 138define void @atomicrmw_null_pointer_is_defined() "null-pointer-is-valid"="true" { 139; ATTRIBUTOR-LABEL: @atomicrmw_null_pointer_is_defined( 140; ATTRIBUTOR-NEXT: [[A:%.*]] = atomicrmw add i32* null, i32 1 acquire 141; ATTRIBUTOR-NEXT: ret void 142; 143 %a = atomicrmw add i32* null, i32 1 acquire 144 ret void 145} 146 147define void @atomicrmw_null_propagated() { 148; ATTRIBUTOR-LABEL: @atomicrmw_null_propagated( 149; ATTRIBUTOR-NEXT: unreachable 150; 151 %ptr = call i32* @ret_null() 152 %a = atomicrmw add i32* %ptr, i32 1 acquire 153 ret void 154} 155 156; -- AtomicCmpXchg tests -- 157 158define void @atomiccmpxchg_wholly_unreachable() { 159; ATTRIBUTOR-LABEL: @atomiccmpxchg_wholly_unreachable( 160; ATTRIBUTOR-NEXT: unreachable 161; 162 %a = cmpxchg i32* null, i32 2, i32 3 acq_rel monotonic 163 ret void 164} 165 166define void @atomiccmpxchg_single_bb_unreachable(i1 %cond) { 167; ATTRIBUTOR-LABEL: @atomiccmpxchg_single_bb_unreachable( 168; ATTRIBUTOR-NEXT: br i1 [[COND:%.*]], label [[T:%.*]], label [[E:%.*]] 169; ATTRIBUTOR: t: 170; ATTRIBUTOR-NEXT: unreachable 171; ATTRIBUTOR: e: 172; ATTRIBUTOR-NEXT: ret void 173; 174 br i1 %cond, label %t, label %e 175t: 176 %a = cmpxchg i32* null, i32 2, i32 3 acq_rel monotonic 177 br label %e 178e: 179 ret void 180} 181 182define void @atomiccmpxchg_null_pointer_is_defined() "null-pointer-is-valid"="true" { 183; ATTRIBUTOR-LABEL: @atomiccmpxchg_null_pointer_is_defined( 184; ATTRIBUTOR-NEXT: [[A:%.*]] = cmpxchg i32* null, i32 2, i32 3 acq_rel monotonic 185; ATTRIBUTOR-NEXT: ret void 186; 187 %a = cmpxchg i32* null, i32 2, i32 3 acq_rel monotonic 188 ret void 189} 190 191define void @atomiccmpxchg_null_propagated() { 192; ATTRIBUTOR-LABEL: @atomiccmpxchg_null_propagated( 193; ATTRIBUTOR-NEXT: unreachable 194; 195 %ptr = call i32* @ret_null() 196 %a = cmpxchg i32* %ptr, i32 2, i32 3 acq_rel monotonic 197 ret void 198} 199 200; -- Conditional branching tests -- 201 202; Note: The unreachable on %t and %e is _not_ from AAUndefinedBehavior 203 204define i32 @cond_br_on_undef() { 205; ATTRIBUTOR-LABEL: @cond_br_on_undef( 206; ATTRIBUTOR-NEXT: unreachable 207; ATTRIBUTOR: t: 208; ATTRIBUTOR-NEXT: unreachable 209; ATTRIBUTOR: e: 210; ATTRIBUTOR-NEXT: unreachable 211; 212 213 br i1 undef, label %t, label %e 214t: 215 ret i32 1 216e: 217 ret i32 2 218} 219 220; More complicated branching 221define void @cond_br_on_undef2(i1 %cond) { 222; ATTRIBUTOR-LABEL: @cond_br_on_undef2( 223; ATTRIBUTOR-NEXT: br i1 [[COND:%.*]], label [[T1:%.*]], label [[E1:%.*]] 224; ATTRIBUTOR: t1: 225; ATTRIBUTOR-NEXT: unreachable 226; ATTRIBUTOR: t2: 227; ATTRIBUTOR-NEXT: unreachable 228; ATTRIBUTOR: e2: 229; ATTRIBUTOR-NEXT: unreachable 230; ATTRIBUTOR: e1: 231; ATTRIBUTOR-NEXT: ret void 232; 233 234 ; Valid branch - verify that this is not converted 235 ; to unreachable. 236 br i1 %cond, label %t1, label %e1 237t1: 238 br i1 undef, label %t2, label %e2 239t2: 240 ret void 241e2: 242 ret void 243e1: 244 ret void 245} 246 247define i1 @ret_undef() { 248 ret i1 undef 249} 250 251define void @cond_br_on_undef_interproc() { 252; ATTRIBUTOR-LABEL: @cond_br_on_undef_interproc( 253; ATTRIBUTOR-NEXT: unreachable 254; ATTRIBUTOR: t: 255; ATTRIBUTOR-NEXT: unreachable 256; ATTRIBUTOR: e: 257; ATTRIBUTOR-NEXT: unreachable 258 259 %cond = call i1 @ret_undef() 260 br i1 %cond, label %t, label %e 261t: 262 ret void 263e: 264 ret void 265} 266 267define i1 @ret_undef2() { 268 br i1 true, label %t, label %e 269t: 270 ret i1 undef 271e: 272 ret i1 undef 273} 274 275; More complicated interproc deduction of undef 276define void @cond_br_on_undef_interproc2() { 277; ATTRIBUTOR-LABEL: @cond_br_on_undef_interproc2( 278; ATTRIBUTOR-NEXT: unreachable 279; ATTRIBUTOR: t: 280; ATTRIBUTOR-NEXT: unreachable 281; ATTRIBUTOR: e: 282; ATTRIBUTOR-NEXT: unreachable 283 %cond = call i1 @ret_undef2() 284 br i1 %cond, label %t, label %e 285t: 286 ret void 287e: 288 ret void 289} 290 291; Branch on undef that depends on propagation of 292; undef of a previous instruction. 293define i32 @cond_br_on_undef3() { 294; ATTRIBUTOR-LABEL: @cond_br_on_undef3( 295; ATTRIBUTOR-NEXT: br label %t 296; ATTRIBUTOR: t: 297; ATTRIBUTOR-NEXT: ret i32 1 298; ATTRIBUTOR: e: 299; ATTRIBUTOR-NEXT: unreachable 300 301 %cond = icmp ne i32 1, undef 302 br i1 %cond, label %t, label %e 303t: 304 ret i32 1 305e: 306 ret i32 2 307} 308 309; Branch on undef because of uninitialized value. 310; FIXME: Currently it doesn't propagate the undef. 311define i32 @cond_br_on_undef_uninit() { 312; ATTRIBUTOR-LABEL: @cond_br_on_undef_uninit( 313; ATTRIBUTOR-NEXT: %alloc = alloca i1 314; ATTRIBUTOR-NEXT: %cond = load i1, i1* %alloc 315; ATTRIBUTOR-NEXT: br i1 %cond, label %t, label %e 316; ATTRIBUTOR: t: 317; ATTRIBUTOR-NEXT: ret i32 1 318; ATTRIBUTOR: e: 319; ATTRIBUTOR-NEXT: ret i32 2 320 321 %alloc = alloca i1 322 %cond = load i1, i1* %alloc 323 br i1 %cond, label %t, label %e 324t: 325 ret i32 1 326e: 327 ret i32 2 328} 329 330; Note that the `load` has UB (so it will be changed to unreachable) 331; and the branch is a terminator that can be constant-folded. 332; We want to test that doing both won't cause a segfault. 333define internal i32 @callee(i1 %C, i32* %A) { 334; ATTRIBUTOR-NOT: @callee( 335; 336entry: 337 %A.0 = load i32, i32* null 338 br i1 %C, label %T, label %F 339 340T: 341 ret i32 %A.0 342 343F: 344 ret i32 1 345} 346 347define i32 @foo() { 348; ATTRIBUTOR-LABEL: @foo() 349; ATTRIBUTOR-NEXT: ret i32 1 350 %X = call i32 @callee(i1 false, i32* null) 351 ret i32 %X 352} 353