1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals --version 2 2; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT 3; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC 4 5%struct.Foo = type { i32, i32, i8 } 6 7@.str = private unnamed_addr constant [17 x i8] c"The value is %d\0A\00", align 1 8 9;. 10; CHECK: @.str = private unnamed_addr constant [17 x i8] c"The value is %d\0A\00", align 1 11;. 12define dso_local void @positive_alloca_1(i32 noundef %val) #0 { 13; CHECK-LABEL: define dso_local void @positive_alloca_1 14; CHECK-SAME: (i32 noundef [[VAL:%.*]]) { 15; CHECK-NEXT: entry: 16; CHECK-NEXT: [[VAL_ADDR1:%.*]] = alloca i8, i32 4, align 4 17; CHECK-NEXT: [[F2:%.*]] = alloca i8, i32 4, align 4 18; CHECK-NEXT: store i32 [[VAL]], ptr [[VAL_ADDR1]], align 4 19; CHECK-NEXT: store i32 10, ptr [[F2]], align 4 20; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[F2]], align 4 21; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP0]], 1 22; CHECK-NEXT: store i32 [[ADD]], ptr [[F2]], align 4 23; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[F2]], align 4 24; CHECK-NEXT: [[ADD3:%.*]] = add nsw i32 [[TMP1]], [[VAL]] 25; CHECK-NEXT: [[CALL:%.*]] = call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(17) @.str, i32 noundef [[ADD3]]) 26; CHECK-NEXT: ret void 27; 28entry: 29 %val.addr = alloca i64, align 4 30 %f = alloca %struct.Foo, align 4 31 store i32 %val, ptr %val.addr, align 4 32 %field1 = getelementptr inbounds %struct.Foo, ptr %f, i32 0, i32 0 33 store i32 10, ptr %field1, align 4 34 %field11 = getelementptr inbounds %struct.Foo, ptr %f, i32 0, i32 0 35 %0 = load i32, ptr %field11, align 4 36 %add = add nsw i32 %0, 1 37 store i32 %add, ptr %field11, align 4 38 %field12 = getelementptr inbounds %struct.Foo, ptr %f, i32 0, i32 0 39 %1 = load i32, ptr %field12, align 4 40 %2 = load i32, ptr %val.addr, align 4 41 %add3 = add nsw i32 %1, %2 42 %call = call i32 (ptr, ...) @printf(ptr noundef @.str, i32 noundef %add3) 43 ret void 44} 45 46; TODO: change malloc like call 47; Function Attrs: noinline nounwind uwtable 48define dso_local void @positive_malloc_1(ptr noundef %val) #0 { 49; CHECK-LABEL: define dso_local void @positive_malloc_1 50; CHECK-SAME: (ptr nofree noundef readonly captures(none) [[VAL:%.*]]) { 51; CHECK-NEXT: entry: 52; CHECK-NEXT: [[VAL_ADDR:%.*]] = alloca ptr, align 8 53; CHECK-NEXT: [[F:%.*]] = alloca ptr, align 8 54; CHECK-NEXT: store ptr [[VAL]], ptr [[VAL_ADDR]], align 8 55; CHECK-NEXT: [[CALL:%.*]] = call noalias ptr @malloc(i64 noundef 12) 56; CHECK-NEXT: store ptr [[CALL]], ptr [[F]], align 8 57; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[VAL]], align 4 58; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP0]], 10 59; CHECK-NEXT: store i32 [[ADD]], ptr [[CALL]], align 4 60; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[CALL]], align 4 61; CHECK-NEXT: [[CALL2:%.*]] = call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(17) @.str, i32 noundef [[TMP1]]) 62; CHECK-NEXT: ret void 63; 64entry: 65 %val.addr = alloca ptr, align 8 66 %f = alloca ptr, align 8 67 store ptr %val, ptr %val.addr, align 8 68 %call = call noalias ptr @malloc(i64 noundef 12) #3 69 store ptr %call, ptr %f, align 8 70 %0 = load ptr, ptr %val.addr, align 8 71 %1 = load i32, ptr %0, align 4 72 %add = add nsw i32 %1, 10 73 %2 = load ptr, ptr %f, align 8 74 %a = getelementptr inbounds %struct.Foo, ptr %2, i32 0, i32 0 75 store i32 %add, ptr %a, align 4 76 %3 = load ptr, ptr %f, align 8 77 %a1 = getelementptr inbounds %struct.Foo, ptr %3, i32 0, i32 0 78 %4 = load i32, ptr %a1, align 4 79 %call2 = call i32 (ptr, ...) @printf(ptr noundef @.str, i32 noundef %4) 80 ret void 81} 82 83; TODO: change malloc like call 84; Function Attrs: noinline nounwind uwtable 85define dso_local void @positive_malloc_2(ptr noundef %val) #0 { 86; CHECK-LABEL: define dso_local void @positive_malloc_2 87; CHECK-SAME: (ptr nofree noundef readonly captures(none) [[VAL:%.*]]) { 88; CHECK-NEXT: entry: 89; CHECK-NEXT: [[VAL_ADDR:%.*]] = alloca ptr, align 8 90; CHECK-NEXT: [[F:%.*]] = alloca ptr, align 8 91; CHECK-NEXT: store ptr [[VAL]], ptr [[VAL_ADDR]], align 8 92; CHECK-NEXT: [[CALL:%.*]] = call noalias ptr @malloc(i64 noundef 60) 93; CHECK-NEXT: store ptr [[CALL]], ptr [[F]], align 8 94; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[VAL]], align 4 95; CHECK-NEXT: store i32 [[TMP0]], ptr [[CALL]], align 4 96; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[CALL]], align 4 97; CHECK-NEXT: [[CALL2:%.*]] = call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(17) @.str, i32 noundef [[TMP1]]) 98; CHECK-NEXT: ret void 99; 100entry: 101 %val.addr = alloca ptr, align 8 102 %x = alloca i32, align 4 103 %f = alloca ptr, align 8 104 store ptr %val, ptr %val.addr, align 8 105 store i32 15, ptr %x, align 4 106 %0 = load i32, ptr %x, align 4 107 %conv = sext i32 %0 to i64 108 %mul = mul i64 4, %conv 109 %call = call noalias ptr @malloc(i64 noundef %mul) 110 store ptr %call, ptr %f, align 8 111 %1 = load ptr, ptr %val.addr, align 8 112 %2 = load i32, ptr %1, align 4 113 %3 = load ptr, ptr %f, align 8 114 %arrayidx = getelementptr inbounds i32, ptr %3, i64 0 115 store i32 %2, ptr %arrayidx, align 4 116 %4 = load ptr, ptr %f, align 8 117 %arrayidx1 = getelementptr inbounds i32, ptr %4, i64 0 118 %5 = load i32, ptr %arrayidx1, align 4 119 %call2 = call i32 (ptr, ...) @printf(ptr noundef @.str, i32 noundef %5) 120 ret void 121} 122 123; Function Attrs: noinline nounwind uwtable 124define dso_local ptr @negative_test_escaping_pointer(i32 noundef %val) #0 { 125; CHECK-LABEL: define dso_local ptr @negative_test_escaping_pointer 126; CHECK-SAME: (i32 noundef [[VAL:%.*]]) { 127; CHECK-NEXT: entry: 128; CHECK-NEXT: [[VAL_ADDR:%.*]] = alloca i32, align 4 129; CHECK-NEXT: [[F:%.*]] = alloca ptr, align 8 130; CHECK-NEXT: store i32 [[VAL]], ptr [[VAL_ADDR]], align 4 131; CHECK-NEXT: [[CALL:%.*]] = call noalias ptr @malloc(i64 noundef 16) 132; CHECK-NEXT: store ptr [[CALL]], ptr [[F]], align 8 133; CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[F]], align 8 134; CHECK-NEXT: store i32 2, ptr [[TMP0]], align 8 135; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 10, [[VAL]] 136; CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[F]], align 8 137; CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 8 138; CHECK-NEXT: [[ADD2:%.*]] = add nsw i32 [[TMP2]], [[ADD]] 139; CHECK-NEXT: store i32 [[ADD2]], ptr [[TMP1]], align 8 140; CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[F]], align 8 141; CHECK-NEXT: ret ptr [[TMP3]] 142; 143entry: 144 %val.addr = alloca i32, align 4 145 %f = alloca ptr, align 8 146 store i32 %val, ptr %val.addr, align 4 147 %call = call noalias ptr @malloc(i64 noundef 16) #2 148 store ptr %call, ptr %f, align 8 149 %0 = load ptr, ptr %f, align 8 150 %field1 = getelementptr inbounds %struct.Foo, ptr %0, i32 0, i32 0 151 store i32 2, ptr %field1, align 8 152 %1 = load i32, ptr %val.addr, align 4 153 %add = add nsw i32 10, %1 154 %2 = load ptr, ptr %f, align 8 155 %field11 = getelementptr inbounds %struct.Foo, ptr %2, i32 0, i32 0 156 %3 = load i32, ptr %field11, align 8 157 %add2 = add nsw i32 %3, %add 158 store i32 %add2, ptr %field11, align 8 159 %4 = load ptr, ptr %f, align 8 160 ret ptr %4 161} 162 163 164;TODO: The allocation can be reduced here. 165;However, the offsets (load/store etc.) Need to be changed. 166; Function Attrs: noinline nounwind uwtable 167define dso_local { i64, ptr } @positive_test_not_a_single_start_offset(i32 noundef %val) #0 { 168; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) 169; CHECK-LABEL: define dso_local { i64, ptr } @positive_test_not_a_single_start_offset 170; CHECK-SAME: (i32 noundef [[VAL:%.*]]) #[[ATTR0:[0-9]+]] { 171; CHECK-NEXT: entry: 172; CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 173; CHECK-NEXT: [[VAL_ADDR:%.*]] = alloca i32, align 4 174; CHECK-NEXT: store i32 [[VAL]], ptr [[VAL_ADDR]], align 4 175; CHECK-NEXT: store i32 2, ptr [[RETVAL]], align 8 176; CHECK-NEXT: [[FIELD3:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[RETVAL]], i32 0, i32 2 177; CHECK-NEXT: store ptr [[VAL_ADDR]], ptr [[FIELD3]], align 8 178; CHECK-NEXT: [[TMP0:%.*]] = load { i64, ptr }, ptr [[RETVAL]], align 8 179; CHECK-NEXT: ret { i64, ptr } [[TMP0]] 180; 181entry: 182 %retval = alloca %struct.Foo, align 8 183 %val.addr = alloca i32, align 4 184 store i32 %val, ptr %val.addr, align 4 185 %field1 = getelementptr inbounds %struct.Foo, ptr %retval, i32 0, i32 0 186 store i32 2, ptr %field1, align 8 187 %field3 = getelementptr inbounds %struct.Foo, ptr %retval, i32 0, i32 2 188 store ptr %val.addr, ptr %field3, align 8 189 %0 = load { i64, ptr }, ptr %retval, align 8 190 ret { i64, ptr } %0 191} 192 193; Function Attrs: noinline nounwind uwtable 194define dso_local void @positive_test_reduce_array_allocation_1() { 195; CHECK-LABEL: define dso_local void @positive_test_reduce_array_allocation_1() { 196; CHECK-NEXT: entry: 197; CHECK-NEXT: [[ARRAY1:%.*]] = alloca i8, i32 4, align 8 198; CHECK-NEXT: store i32 0, ptr [[ARRAY1]], align 8 199; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARRAY1]], align 8 200; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[TMP0]], 2 201; CHECK-NEXT: store i32 [[TMP1]], ptr [[ARRAY1]], align 8 202; CHECK-NEXT: [[TMP2:%.*]] = add i32 1, 2 203; CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[ARRAY1]], align 8 204; CHECK-NEXT: [[TMP4:%.*]] = add i32 [[TMP2]], [[TMP3]] 205; CHECK-NEXT: store i32 [[TMP4]], ptr [[ARRAY1]], align 8 206; CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[ARRAY1]], align 8 207; CHECK-NEXT: [[CALL:%.*]] = call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(17) @.str, i32 noundef [[TMP5]]) 208; CHECK-NEXT: ret void 209; 210entry: 211 %array = alloca ptr, i32 10 212 store i32 0, ptr %array 213 %0 = load i32, ptr %array 214 %1 = add i32 %0, 2 215 store i32 %1, ptr %array 216 %2 = add i32 1, 2 217 %3 = load i32, ptr %array 218 %4 = add i32 %2, %3 219 store i32 %4, ptr %array 220 %5 = load i32, ptr %array 221 %call = call i32 (ptr, ...) @printf(ptr noundef @.str, i32 noundef %5) 222 ret void 223} 224 225 226; Function Attrs: noinline nounwind uwtable 227; TODO: Here the array size is not known at compile time. 228; However the array does not escape and is only partially used. 229; Should the optimization reduce the allocation size regardless? Based on AAPointerInfo. 230define dso_local void @baz(ptr noundef %val, i32 noundef %arrayLength) #0 { 231; CHECK-LABEL: define dso_local void @baz 232; CHECK-SAME: (ptr nofree noundef readonly captures(none) [[VAL:%.*]], i32 noundef [[ARRAYLENGTH:%.*]]) { 233; CHECK-NEXT: entry: 234; CHECK-NEXT: [[VAL_ADDR:%.*]] = alloca ptr, align 8 235; CHECK-NEXT: [[ARRAYLENGTH_ADDR:%.*]] = alloca i32, align 4 236; CHECK-NEXT: [[F:%.*]] = alloca ptr, align 8 237; CHECK-NEXT: store ptr [[VAL]], ptr [[VAL_ADDR]], align 8 238; CHECK-NEXT: store i32 [[ARRAYLENGTH]], ptr [[ARRAYLENGTH_ADDR]], align 4 239; CHECK-NEXT: [[CONV:%.*]] = sext i32 [[ARRAYLENGTH]] to i64 240; CHECK-NEXT: [[MUL:%.*]] = mul i64 4, [[CONV]] 241; CHECK-NEXT: [[CALL:%.*]] = call noalias ptr @malloc(i64 noundef [[MUL]]) 242; CHECK-NEXT: store ptr [[CALL]], ptr [[F]], align 8 243; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[VAL]], align 4 244; CHECK-NEXT: store i32 [[TMP0]], ptr [[CALL]], align 4 245; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[CALL]], align 4 246; CHECK-NEXT: [[CALL2:%.*]] = call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(17) @.str, i32 noundef [[TMP1]]) 247; CHECK-NEXT: ret void 248; 249entry: 250 %val.addr = alloca ptr, align 8 251 %arrayLength.addr = alloca i32, align 4 252 %f = alloca ptr, align 8 253 store ptr %val, ptr %val.addr, align 8 254 store i32 %arrayLength, ptr %arrayLength.addr, align 4 255 %0 = load i32, ptr %arrayLength.addr, align 4 256 %conv = sext i32 %0 to i64 257 %mul = mul i64 4, %conv 258 %call = call noalias ptr @malloc(i64 noundef %mul) #3 259 store ptr %call, ptr %f, align 8 260 %1 = load ptr, ptr %val.addr, align 8 261 %2 = load i32, ptr %1, align 4 262 %3 = load ptr, ptr %f, align 8 263 %arrayidx = getelementptr inbounds i32, ptr %3, i64 0 264 store i32 %2, ptr %arrayidx, align 4 265 %4 = load ptr, ptr %f, align 8 266 %arrayidx1 = getelementptr inbounds i32, ptr %4, i64 0 267 %5 = load i32, ptr %arrayidx1, align 4 268 %call2 = call i32 (ptr, ...) @printf(ptr noundef @.str, i32 noundef %5) 269 ret void 270} 271 272;TODO: Here since only even indexes of the array are part of the output 273;We can reduce the allocation by half and make an array that's accessed contiguously 274; Function Attrs: noinline nounwind uwtable 275define dso_local void @positive_test_reduce_array_allocation_2() #0 { 276; CHECK-LABEL: define dso_local void @positive_test_reduce_array_allocation_2() { 277; CHECK-NEXT: entry: 278; CHECK-NEXT: [[ARRAY:%.*]] = alloca ptr, align 8 279; CHECK-NEXT: [[I:%.*]] = alloca i32, align 4 280; CHECK-NEXT: [[CALL:%.*]] = call noalias ptr @malloc(i64 noundef 40000) 281; CHECK-NEXT: store ptr [[CALL]], ptr [[ARRAY]], align 8 282; CHECK-NEXT: store i32 0, ptr [[I]], align 4 283; CHECK-NEXT: br label [[FOR_COND:%.*]] 284; CHECK: for.cond: 285; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[I]], align 4 286; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[TMP0]], 10000 287; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]] 288; CHECK: for.body: 289; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[I]], align 4 290; CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[I]], align 4 291; CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP2]] to i64 292; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 [[IDXPROM]] 293; CHECK-NEXT: store i32 [[TMP1]], ptr [[ARRAYIDX]], align 4 294; CHECK-NEXT: br label [[FOR_INC:%.*]] 295; CHECK: for.inc: 296; CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[I]], align 4 297; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP3]], 2 298; CHECK-NEXT: store i32 [[ADD]], ptr [[I]], align 4 299; CHECK-NEXT: br label [[FOR_COND]] 300; CHECK: for.end: 301; CHECK-NEXT: store i32 0, ptr [[I]], align 4 302; CHECK-NEXT: br label [[FOR_COND1:%.*]] 303; CHECK: for.cond1: 304; CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[I]], align 4 305; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i32 [[TMP4]], 10000 306; CHECK-NEXT: br i1 [[CMP2]], label [[FOR_BODY3:%.*]], label [[FOR_END9:%.*]] 307; CHECK: for.body3: 308; CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[I]], align 4 309; CHECK-NEXT: [[IDXPROM4:%.*]] = sext i32 [[TMP5]] to i64 310; CHECK-NEXT: [[ARRAYIDX5:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 [[IDXPROM4]] 311; CHECK-NEXT: [[TMP6:%.*]] = load i32, ptr [[ARRAYIDX5]], align 4 312; CHECK-NEXT: [[ADD6:%.*]] = add nsw i32 [[TMP6]], 1 313; CHECK-NEXT: store i32 [[ADD6]], ptr [[ARRAYIDX5]], align 4 314; CHECK-NEXT: br label [[FOR_INC7:%.*]] 315; CHECK: for.inc7: 316; CHECK-NEXT: [[TMP7:%.*]] = load i32, ptr [[I]], align 4 317; CHECK-NEXT: [[ADD8:%.*]] = add nsw i32 [[TMP7]], 2 318; CHECK-NEXT: store i32 [[ADD8]], ptr [[I]], align 4 319; CHECK-NEXT: br label [[FOR_COND1]] 320; CHECK: for.end9: 321; CHECK-NEXT: store i32 0, ptr [[I]], align 4 322; CHECK-NEXT: br label [[FOR_COND10:%.*]] 323; CHECK: for.cond10: 324; CHECK-NEXT: [[TMP8:%.*]] = load i32, ptr [[I]], align 4 325; CHECK-NEXT: [[CMP11:%.*]] = icmp slt i32 [[TMP8]], 10000 326; CHECK-NEXT: br i1 [[CMP11]], label [[FOR_BODY12:%.*]], label [[FOR_END18:%.*]] 327; CHECK: for.body12: 328; CHECK-NEXT: [[TMP9:%.*]] = load i32, ptr [[I]], align 4 329; CHECK-NEXT: [[IDXPROM13:%.*]] = sext i32 [[TMP9]] to i64 330; CHECK-NEXT: [[ARRAYIDX14:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 [[IDXPROM13]] 331; CHECK-NEXT: [[TMP10:%.*]] = load i32, ptr [[ARRAYIDX14]], align 4 332; CHECK-NEXT: [[CALL15:%.*]] = call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(17) @.str, i32 noundef [[TMP10]]) 333; CHECK-NEXT: br label [[FOR_INC16:%.*]] 334; CHECK: for.inc16: 335; CHECK-NEXT: [[TMP11:%.*]] = load i32, ptr [[I]], align 4 336; CHECK-NEXT: [[ADD17:%.*]] = add nsw i32 [[TMP11]], 2 337; CHECK-NEXT: store i32 [[ADD17]], ptr [[I]], align 4 338; CHECK-NEXT: br label [[FOR_COND10]] 339; CHECK: for.end18: 340; CHECK-NEXT: ret void 341; 342entry: 343 %array = alloca ptr, align 8 344 %i = alloca i32, align 4 345 %call = call noalias ptr @malloc(i64 noundef 40000) #3 346 store ptr %call, ptr %array, align 8 347 store i32 0, ptr %i, align 4 348 br label %for.cond 349 350for.cond: 351 %0 = load i32, ptr %i, align 4 352 %cmp = icmp slt i32 %0, 10000 353 br i1 %cmp, label %for.body, label %for.end 354 355for.body: 356 %1 = load i32, ptr %i, align 4 357 %2 = load ptr, ptr %array, align 8 358 %3 = load i32, ptr %i, align 4 359 %idxprom = sext i32 %3 to i64 360 %arrayidx = getelementptr inbounds i32, ptr %2, i64 %idxprom 361 store i32 %1, ptr %arrayidx, align 4 362 br label %for.inc 363 364for.inc: 365 %4 = load i32, ptr %i, align 4 366 %add = add nsw i32 %4, 2 367 store i32 %add, ptr %i, align 4 368 br label %for.cond 369 370for.end: 371 store i32 0, ptr %i, align 4 372 br label %for.cond1 373 374for.cond1: 375 %5 = load i32, ptr %i, align 4 376 %cmp2 = icmp slt i32 %5, 10000 377 br i1 %cmp2, label %for.body3, label %for.end9 378 379for.body3: 380 %6 = load ptr, ptr %array, align 8 381 %7 = load i32, ptr %i, align 4 382 %idxprom4 = sext i32 %7 to i64 383 %arrayidx5 = getelementptr inbounds i32, ptr %6, i64 %idxprom4 384 %8 = load i32, ptr %arrayidx5, align 4 385 %add6 = add nsw i32 %8, 1 386 store i32 %add6, ptr %arrayidx5, align 4 387 br label %for.inc7 388 389for.inc7: 390 %9 = load i32, ptr %i, align 4 391 %add8 = add nsw i32 %9, 2 392 store i32 %add8, ptr %i, align 4 393 br label %for.cond1 394 395for.end9: 396 store i32 0, ptr %i, align 4 397 br label %for.cond10 398 399for.cond10: 400 %10 = load i32, ptr %i, align 4 401 %cmp11 = icmp slt i32 %10, 10000 402 br i1 %cmp11, label %for.body12, label %for.end18 403 404for.body12: 405 %11 = load ptr, ptr %array, align 8 406 %12 = load i32, ptr %i, align 4 407 %idxprom13 = sext i32 %12 to i64 408 %arrayidx14 = getelementptr inbounds i32, ptr %11, i64 %idxprom13 409 %13 = load i32, ptr %arrayidx14, align 4 410 %call15 = call i32 (ptr, ...) @printf(ptr noundef @.str, i32 noundef %13) 411 br label %for.inc16 412 413for.inc16: 414 %14 = load i32, ptr %i, align 4 415 %add17 = add nsw i32 %14, 2 416 store i32 %add17, ptr %i, align 4 417 br label %for.cond10 418 419for.end18: 420 ret void 421} 422 423 424define dso_local void @pthread_test(){ 425; TUNIT-LABEL: define dso_local void @pthread_test() { 426; TUNIT-NEXT: [[ARG1:%.*]] = alloca i8, align 8 427; TUNIT-NEXT: [[THREAD:%.*]] = alloca i64, align 8 428; TUNIT-NEXT: [[CALL1:%.*]] = call i32 @pthread_create(ptr noundef nonnull align 8 dereferenceable(8) [[THREAD]], ptr noundef align 4294967296 null, ptr noundef nonnull @pthread_allocation_should_remain_same, ptr noundef nonnull align 8 dereferenceable(1) [[ARG1]]) 429; TUNIT-NEXT: [[F1:%.*]] = alloca i8, i32 4, align 4 430; TUNIT-NEXT: [[CALL2:%.*]] = call i32 @pthread_create(ptr noundef nonnull align 8 dereferenceable(8) [[THREAD]], ptr noundef align 4294967296 null, ptr noundef nonnull @pthread_allocation_should_be_reduced, ptr noalias nofree nonnull readnone align 4 captures(none) dereferenceable(12) undef) 431; TUNIT-NEXT: [[F2:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 432; TUNIT-NEXT: [[CALL3:%.*]] = call i32 @pthread_create(ptr noundef nonnull align 8 dereferenceable(8) [[THREAD]], ptr noundef align 4294967296 null, ptr noundef nonnull @pthread_check_captured_pointer, ptr noundef nonnull align 4 dereferenceable(12) [[F2]]) 433; TUNIT-NEXT: ret void 434; 435; CGSCC-LABEL: define dso_local void @pthread_test() { 436; CGSCC-NEXT: [[ARG1:%.*]] = alloca i8, align 8 437; CGSCC-NEXT: [[THREAD:%.*]] = alloca i64, align 8 438; CGSCC-NEXT: [[CALL1:%.*]] = call i32 @pthread_create(ptr noundef nonnull align 8 dereferenceable(8) [[THREAD]], ptr noundef align 4294967296 null, ptr noundef nonnull @pthread_allocation_should_remain_same, ptr noundef nonnull align 8 dereferenceable(1) [[ARG1]]) 439; CGSCC-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 440; CGSCC-NEXT: [[CALL2:%.*]] = call i32 @pthread_create(ptr noundef nonnull align 8 dereferenceable(8) [[THREAD]], ptr noundef align 4294967296 null, ptr noundef nonnull @pthread_allocation_should_be_reduced, ptr noalias nofree noundef nonnull readonly align 4 captures(none) dereferenceable(12) [[F]]) 441; CGSCC-NEXT: [[F2:%.*]] = alloca [[STRUCT_FOO]], align 4 442; CGSCC-NEXT: [[CALL3:%.*]] = call i32 @pthread_create(ptr noundef nonnull align 8 dereferenceable(8) [[THREAD]], ptr noundef align 4294967296 null, ptr noundef nonnull @pthread_check_captured_pointer, ptr noundef nonnull align 4 dereferenceable(12) [[F2]]) 443; CGSCC-NEXT: ret void 444; 445 %arg1 = alloca i8, align 8 446 %thread = alloca i64, align 8 447 %call1 = call i32 @pthread_create(ptr nonnull %thread, ptr null, ptr nonnull @pthread_allocation_should_remain_same, ptr %arg1) 448 %f = alloca %struct.Foo, align 4 449 %call2 = call i32 @pthread_create(ptr nonnull %thread, ptr null, ptr nonnull @pthread_allocation_should_be_reduced, ptr %f) 450 %f2 = alloca %struct.Foo, align 4 451 %call3 = call i32 @pthread_create(ptr nonnull %thread, ptr null, ptr nonnull @pthread_check_captured_pointer, ptr %f2) 452 ret void 453} 454 455define internal ptr @pthread_allocation_should_remain_same(ptr %arg) { 456; CHECK-LABEL: define internal noundef nonnull align 8 dereferenceable(1) ptr @pthread_allocation_should_remain_same 457; CHECK-SAME: (ptr noundef nonnull returned align 8 dereferenceable(1) [[ARG:%.*]]) { 458; CHECK-NEXT: entry: 459; CHECK-NEXT: [[CALL:%.*]] = call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(17) @.str, ptr noundef nonnull align 8 dereferenceable(1) [[ARG]]) 460; CHECK-NEXT: ret ptr [[ARG]] 461; 462entry: 463 %call = call i32 (ptr, ...) @printf(ptr noundef @.str, ptr noundef %arg) 464 ret ptr %arg 465} 466 467define internal void @pthread_allocation_should_be_reduced(ptr %arg) { 468; 469; TUNIT-LABEL: define internal void @pthread_allocation_should_be_reduced 470; TUNIT-SAME: (ptr noalias nofree nonnull readnone align 4 captures(none) dereferenceable(12) [[ARG:%.*]]) { 471; TUNIT-NEXT: entry: 472; TUNIT-NEXT: [[CALL:%.*]] = call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(17) @.str, i32 undef) 473; TUNIT-NEXT: ret void 474; 475; CGSCC-LABEL: define internal void @pthread_allocation_should_be_reduced 476; CGSCC-SAME: (ptr noalias nofree noundef nonnull readonly align 4 captures(none) dereferenceable(12) [[ARG:%.*]]) { 477; CGSCC-NEXT: entry: 478; CGSCC-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARG]], align 4 479; CGSCC-NEXT: [[CALL:%.*]] = call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(17) @.str, i32 noundef [[TMP0]]) 480; CGSCC-NEXT: ret void 481; 482entry: 483 %field1 = getelementptr inbounds %struct.Foo, ptr %arg, i32 0, i32 0 484 %0 = load i32, ptr %field1, align 4 485 %call = call i32 (ptr, ...) @printf(ptr noundef @.str, i32 noundef %0) 486 ret void 487} 488 489define internal void @pthread_check_captured_pointer(ptr %arg){ 490; CHECK-LABEL: define internal void @pthread_check_captured_pointer 491; CHECK-SAME: (ptr noundef nonnull align 4 dereferenceable(12) [[ARG:%.*]]) { 492; CHECK-NEXT: entry: 493; CHECK-NEXT: call void @external_call(ptr noundef nonnull align 4 dereferenceable(12) [[ARG]]) 494; CHECK-NEXT: ret void 495; 496entry: 497 %field1 = getelementptr inbounds %struct.Foo, ptr %arg, i32 0, i32 0 498 call void @external_call(ptr %field1) 499 ret void 500} 501 502 503declare external void @external_call(ptr) 504 505declare !callback !0 dso_local i32 @pthread_create(ptr, ptr, ptr, ptr) 506!1 = !{i64 2, i64 3, i1 false} 507!0 = !{!1} 508 509declare i32 @printf(ptr noundef, ...) #1 510 511; Function Attrs: nounwind allocsize(0) 512declare noalias ptr @malloc(i64 noundef) #1 513;. 514; TUNIT: attributes #[[ATTR0]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) } 515;. 516; CGSCC: attributes #[[ATTR0]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) } 517;. 518; TUNIT: [[META0:![0-9]+]] = !{[[META1:![0-9]+]]} 519; TUNIT: [[META1]] = !{i64 2, i64 3, i1 false} 520;. 521; CGSCC: [[META0:![0-9]+]] = !{[[META1:![0-9]+]]} 522; CGSCC: [[META1]] = !{i64 2, i64 3, i1 false} 523;. 524