1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py 2; RUN: opt < %s -passes=dse -S | FileCheck %s 3 4target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64" 5 6declare noalias ptr @malloc(i64) 7 8declare void @foo() 9declare void @capture(ptr) 10 11; Check that we do not remove the second store, as %m is returned. 12define ptr @test_return_captures_1() { 13; CHECK-LABEL: @test_return_captures_1( 14; CHECK-NEXT: [[M:%.*]] = call ptr @malloc(i64 24) 15; CHECK-NEXT: store i8 1, ptr [[M]], align 1 16; CHECK-NEXT: ret ptr [[M]] 17; 18 %m = call ptr @malloc(i64 24) 19 store i8 0, ptr %m 20 store i8 1, ptr %m 21 ret ptr %m 22} 23 24; Same as @test_return_captures_1, but across BBs. 25define ptr @test_return_captures_2() { 26; CHECK-LABEL: @test_return_captures_2( 27; CHECK-NEXT: [[M:%.*]] = call ptr @malloc(i64 24) 28; CHECK-NEXT: br label [[EXIT:%.*]] 29; CHECK: exit: 30; CHECK-NEXT: store i8 1, ptr [[M]], align 1 31; CHECK-NEXT: ret ptr [[M]] 32; 33 %m = call ptr @malloc(i64 24) 34 store i8 0, ptr %m 35 br label %exit 36 37exit: 38 store i8 1, ptr %m 39 ret ptr %m 40} 41 42 43%S1 = type { ptr } 44 45; We cannot remove the last store to %m, because it escapes by storing it to %E. 46define void @test_malloc_capture_1(ptr %E) { 47; CHECK-LABEL: @test_malloc_capture_1( 48; CHECK-NEXT: [[M:%.*]] = call ptr @malloc(i64 24) 49; CHECK-NEXT: br label [[EXIT:%.*]] 50; CHECK: exit: 51; CHECK-NEXT: store ptr [[M]], ptr [[E:%.*]], align 4 52; CHECK-NEXT: store i8 1, ptr [[M]], align 1 53; CHECK-NEXT: ret void 54; 55 %m = call ptr @malloc(i64 24) 56 br label %exit 57 58exit: 59 store ptr %m, ptr %E 60 store i8 1, ptr %m 61 ret void 62} 63 64; Check we do not eliminate either store. The first one cannot be eliminated, 65; due to the call of @capture. The second one because %m escapes. 66define ptr @test_malloc_capture_2() { 67; CHECK-LABEL: @test_malloc_capture_2( 68; CHECK-NEXT: [[M:%.*]] = call ptr @malloc(i64 24) 69; CHECK-NEXT: store i8 0, ptr [[M]], align 1 70; CHECK-NEXT: call void @capture(ptr [[M]]) 71; CHECK-NEXT: br label [[EXIT:%.*]] 72; CHECK: exit: 73; CHECK-NEXT: store i8 1, ptr [[M]], align 1 74; CHECK-NEXT: ret ptr [[M]] 75; 76 %m = call ptr @malloc(i64 24) 77 store i8 0, ptr %m 78 call void @capture(ptr %m) 79 br label %exit 80 81exit: 82 store i8 1, ptr %m 83 ret ptr %m 84} 85 86; We can remove the first store store i8 0, ptr %m because there are no throwing 87; instructions between the 2 stores and also %m escapes after the killing store. 88define ptr @test_malloc_capture_3() { 89; CHECK-LABEL: @test_malloc_capture_3( 90; CHECK-NEXT: [[M:%.*]] = call ptr @malloc(i64 24) 91; CHECK-NEXT: br label [[EXIT:%.*]] 92; CHECK: exit: 93; CHECK-NEXT: store i8 1, ptr [[M]], align 1 94; CHECK-NEXT: call void @capture(ptr [[M]]) 95; CHECK-NEXT: ret ptr [[M]] 96; 97 %m = call ptr @malloc(i64 24) 98 store i8 0, ptr %m 99 br label %exit 100 101exit: 102 store i8 1, ptr %m 103 call void @capture(ptr %m) 104 ret ptr %m 105} 106 107; TODO: We could remove the first store store i8 0, ptr %m because %m escapes 108; after the killing store. 109define ptr @test_malloc_capture_4() { 110; CHECK-LABEL: @test_malloc_capture_4( 111; CHECK-NEXT: [[M:%.*]] = call ptr @malloc(i64 24) 112; CHECK-NEXT: store i8 0, ptr [[M]], align 1 113; CHECK-NEXT: call void @may_throw_readnone() 114; CHECK-NEXT: br label [[EXIT:%.*]] 115; CHECK: exit: 116; CHECK-NEXT: store i8 1, ptr [[M]], align 1 117; CHECK-NEXT: call void @capture(ptr [[M]]) 118; CHECK-NEXT: ret ptr [[M]] 119; 120 121 %m = call ptr @malloc(i64 24) 122 store i8 0, ptr %m 123 call void @may_throw_readnone() 124 br label %exit 125 126exit: 127 store i8 1, ptr %m 128 call void @capture(ptr %m) 129 ret ptr %m 130} 131 132 133; We cannot remove the first store store i8 0, ptr %m because %m escapes 134; before the killing store and we may throw in between. 135define ptr @test_malloc_capture_5() { 136; CHECK-LABEL: @test_malloc_capture_5( 137; CHECK-NEXT: [[M:%.*]] = call ptr @malloc(i64 24) 138; CHECK-NEXT: call void @capture(ptr [[M]]) 139; CHECK-NEXT: store i8 0, ptr [[M]], align 1 140; CHECK-NEXT: call void @may_throw_readnone() 141; CHECK-NEXT: br label [[EXIT:%.*]] 142; CHECK: exit: 143; CHECK-NEXT: store i8 1, ptr [[M]], align 1 144; CHECK-NEXT: ret ptr [[M]] 145; 146 147 %m = call ptr @malloc(i64 24) 148 call void @capture(ptr %m) 149 store i8 0, ptr %m 150 call void @may_throw_readnone() 151 br label %exit 152 153exit: 154 store i8 1, ptr %m 155 ret ptr %m 156} 157 158 159; TODO: We could remove the first store 'store i8 0, ptr %m' even though there 160; is a throwing instruction between them, because %m escapes after the killing 161; store. 162define ptr @test_malloc_capture_6() { 163; CHECK-LABEL: @test_malloc_capture_6( 164; CHECK-NEXT: [[M:%.*]] = call ptr @malloc(i64 24) 165; CHECK-NEXT: store i8 0, ptr [[M]], align 1 166; CHECK-NEXT: call void @may_throw_readnone() 167; CHECK-NEXT: br label [[EXIT:%.*]] 168; CHECK: exit: 169; CHECK-NEXT: store i8 1, ptr [[M]], align 1 170; CHECK-NEXT: call void @capture(ptr [[M]]) 171; CHECK-NEXT: ret ptr [[M]] 172; 173 174 %m = call ptr @malloc(i64 24) 175 store i8 0, ptr %m 176 call void @may_throw_readnone() 177 br label %exit 178 179exit: 180 store i8 1, ptr %m 181 call void @capture(ptr %m) 182 ret ptr %m 183} 184 185; We *could* remove the first store 'store i8 0, ptr %m' even though there is a 186; throwing instruction between them, because %m escapes after the killing store. 187; But this would require using PointerMayBeCapturedBefore in 188; isInvisibleToCallerBeforeRet, which we currently do not do to limit 189; compile-time, as this appears to hardly ever lead to more stores eliminated 190; in practice. 191define ptr @test_malloc_capture_7() { 192; CHECK-LABEL: @test_malloc_capture_7( 193; CHECK-NEXT: [[M:%.*]] = call ptr @malloc(i64 24) 194; CHECK-NEXT: store i8 0, ptr [[M]], align 1 195; CHECK-NEXT: call void @may_throw() 196; CHECK-NEXT: br label [[EXIT:%.*]] 197; CHECK: exit: 198; CHECK-NEXT: store i8 1, ptr [[M]], align 1 199; CHECK-NEXT: call void @capture(ptr [[M]]) 200; CHECK-NEXT: ret ptr [[M]] 201; 202 203 %m = call ptr @malloc(i64 24) 204 store i8 0, ptr %m 205 call void @may_throw() 206 br label %exit 207 208exit: 209 store i8 1, ptr %m 210 call void @capture(ptr %m) 211 ret ptr %m 212} 213 214; Stores to stack objects can be eliminated if they are not captured inside the function. 215define void @test_alloca_nocapture_1() { 216; CHECK-LABEL: @test_alloca_nocapture_1( 217; CHECK-NEXT: call void @foo() 218; CHECK-NEXT: br label [[EXIT:%.*]] 219; CHECK: exit: 220; CHECK-NEXT: ret void 221; 222 %m = alloca i8 223 store i8 0, ptr %m 224 call void @foo() 225 br label %exit 226 227exit: 228 store i8 1, ptr %m 229 ret void 230} 231 232; Cannot remove first store i8 0, ptr %m, as the call to @capture captures the object. 233define void @test_alloca_capture_1() { 234; CHECK-LABEL: @test_alloca_capture_1( 235; CHECK-NEXT: [[M:%.*]] = alloca i8, align 1 236; CHECK-NEXT: store i8 0, ptr [[M]], align 1 237; CHECK-NEXT: call void @capture(ptr [[M]]) 238; CHECK-NEXT: br label [[EXIT:%.*]] 239; CHECK: exit: 240; CHECK-NEXT: ret void 241; 242 %m = alloca i8 243 store i8 0, ptr %m 244 call void @capture(ptr %m) 245 br label %exit 246 247exit: 248 store i8 1, ptr %m 249 ret void 250} 251 252; We can remove the last store to %m, even though it escapes because the alloca 253; becomes invalid after the function returns. 254define void @test_alloca_capture_2(ptr %E) { 255; CHECK-LABEL: @test_alloca_capture_2( 256; CHECK-NEXT: [[M:%.*]] = alloca i8, align 1 257; CHECK-NEXT: br label [[EXIT:%.*]] 258; CHECK: exit: 259; CHECK-NEXT: store ptr [[M]], ptr [[E:%.*]], align 4 260; CHECK-NEXT: ret void 261; 262 %m = alloca i8 263 br label %exit 264 265exit: 266 store ptr %m, ptr %E 267 store i8 1, ptr %m 268 ret void 269} 270 271; Readnone functions are not modeled in MemorySSA, but could throw. 272; Make sure we do not eliminate the first store 'store i8 2, ptr %call' 273define void @malloc_capture_throw_1() { 274; CHECK-LABEL: @malloc_capture_throw_1( 275; CHECK-NEXT: [[CALL:%.*]] = call ptr @malloc(i64 1) 276; CHECK-NEXT: call void @may_capture(ptr [[CALL]]) 277; CHECK-NEXT: store i8 2, ptr [[CALL]], align 1 278; CHECK-NEXT: call void @may_throw_readnone() 279; CHECK-NEXT: store i8 3, ptr [[CALL]], align 1 280; CHECK-NEXT: ret void 281; 282 %call = call ptr @malloc(i64 1) 283 call void @may_capture(ptr %call) 284 store i8 2, ptr %call, align 1 285 call void @may_throw_readnone() 286 store i8 3, ptr %call, align 1 287 ret void 288} 289 290; Readnone functions are not modeled in MemorySSA, but could throw. 291; Make sure we do not eliminate the first store 'store i8 2, ptr %call' 292define void @malloc_capture_throw_2() { 293; CHECK-LABEL: @malloc_capture_throw_2( 294; CHECK-NEXT: [[CALL:%.*]] = call ptr @malloc(i64 1) 295; CHECK-NEXT: call void @may_capture(ptr [[CALL]]) 296; CHECK-NEXT: store i8 2, ptr [[CALL]], align 1 297; CHECK-NEXT: br label [[BB:%.*]] 298; CHECK: bb: 299; CHECK-NEXT: call void @may_throw_readnone() 300; CHECK-NEXT: store i8 3, ptr [[CALL]], align 1 301; CHECK-NEXT: ret void 302; 303 %call = call ptr @malloc(i64 1) 304 call void @may_capture(ptr %call) 305 store i8 2, ptr %call, align 1 306 br label %bb 307 308bb: 309 call void @may_throw_readnone() 310 store i8 3, ptr %call, align 1 311 ret void 312} 313 314 315declare void @may_capture(ptr) 316declare void @may_throw_readnone() readnone 317declare void @may_throw() 318