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