1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals 2; 3; Test that the stpncpy library call simplifier works correctly. 4; 5; RUN: opt < %s -data-layout="E" -passes=instcombine -S | FileCheck %s --check-prefixes=ANY,BE 6; RUN: opt < %s -data-layout="e" -passes=instcombine -S | FileCheck %s --check-prefixes=ANY,LE 7 8declare ptr @stpncpy(ptr, ptr, i64) 9 10declare void @sink(ptr, ptr) 11 12@a4 = constant [4 x i8] c"1234" 13@s4 = constant [5 x i8] c"1234\00" 14 15 16; The following are generated by the stpncpy -> memcpy transformation 17; (trading space for speed). 18@str = private constant [4 x i8] c"4\00\00\00" 19@str.1 = private constant [10 x i8] c"4\00\00\00\00\00\00\00\00\00" 20@str.2 = private constant [10 x i8] c"1234\00\00\00\00\00\00" 21@str.3 = private unnamed_addr constant [4 x i8] c"4\00\00\00", align 1 22@str.4 = private unnamed_addr constant [10 x i8] c"4\00\00\00\00\00\00\00\00\00", align 1 23@str.5 = private unnamed_addr constant [10 x i8] c"1234\00\00\00\00\00\00", align 1 24 25; Verify that the generated constants have the expected contents. 26 27; Verify that exactly overlapping stpncpy(D, D, N) calls are transformed 28; to D + strnlen(D, N) or, equivalently, D + (*D != '\0'), when N < 2. 29 30;. 31; ANY: @a4 = constant [4 x i8] c"1234" 32; ANY: @s4 = constant [5 x i8] c"1234\00" 33; ANY: @str = private constant [4 x i8] c"4\00\00\00" 34; ANY: @str.1 = private constant [10 x i8] c"4\00\00\00\00\00\00\00\00\00" 35; ANY: @str.2 = private constant [10 x i8] c"1234\00\00\00\00\00\00" 36; ANY: @str.3 = private unnamed_addr constant [4 x i8] c"4\00\00\00", align 1 37; ANY: @str.4 = private unnamed_addr constant [10 x i8] c"4\00\00\00\00\00\00\00\00\00", align 1 38; ANY: @str.5 = private unnamed_addr constant [10 x i8] c"1234\00\00\00\00\00\00", align 1 39; ANY: @str.6 = private unnamed_addr constant [3 x i8] c"4\00\00", align 1 40; ANY: @str.7 = private unnamed_addr constant [9 x i8] c"4\00\00\00\00\00\00\00\00", align 1 41; ANY: @str.8 = private unnamed_addr constant [9 x i8] c"1234\00\00\00\00\00", align 1 42; ANY: @str.9 = private unnamed_addr constant [9 x i8] c"1234\00\00\00\00\00", align 1 43;. 44define void @fold_stpncpy_overlap(ptr %dst, i64 %n) { 45; ANY-LABEL: @fold_stpncpy_overlap( 46; ANY-NEXT: call void @sink(ptr [[DST:%.*]], ptr [[DST]]) 47; ANY-NEXT: [[STXNCPY_CHAR0:%.*]] = load i8, ptr [[DST]], align 1 48; ANY-NEXT: [[STPNCPY_CHAR0CMP:%.*]] = icmp ne i8 [[STXNCPY_CHAR0]], 0 49; ANY-NEXT: [[STPNCPY_SEL_IDX:%.*]] = zext i1 [[STPNCPY_CHAR0CMP]] to i64 50; ANY-NEXT: [[STPNCPY_SEL:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 [[STPNCPY_SEL_IDX]] 51; ANY-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[STPNCPY_SEL]]) 52; ANY-NEXT: ret void 53; 54; Fold stpncpy(D, D, 0) to just D. 55 %es_0 = call ptr @stpncpy(ptr %dst, ptr %dst, i64 0) 56 call void @sink(ptr %dst, ptr %es_0) 57 58; Fold stpncpy(D, D, 1) to D + (*D != '\0'). 59 %es_1 = call ptr @stpncpy(ptr %dst, ptr %dst, i64 1) 60 call void @sink(ptr %dst, ptr %es_1) 61 62 ret void 63} 64 65 66; Verify that exactly overlapping stpncpy(D, D, N) calls are left alone 67; when N >= 2. Such calls are strictly undefined and while simplifying 68; them to the expected result is possible there is little to gain from it. 69 70define void @call_stpncpy_overlap(ptr %dst, i64 %n) { 71; ANY-LABEL: @call_stpncpy_overlap( 72; ANY-NEXT: [[ES_2:%.*]] = call ptr @stpncpy(ptr noundef nonnull dereferenceable(1) [[DST:%.*]], ptr noundef nonnull dereferenceable(1) [[DST]], i64 2) 73; ANY-NEXT: call void @sink(ptr nonnull [[DST]], ptr [[ES_2]]) 74; ANY-NEXT: [[ES_3:%.*]] = call ptr @stpncpy(ptr noundef nonnull dereferenceable(1) [[DST]], ptr noundef nonnull dereferenceable(1) [[DST]], i64 3) 75; ANY-NEXT: call void @sink(ptr nonnull [[DST]], ptr [[ES_3]]) 76; ANY-NEXT: [[ES_N:%.*]] = call ptr @stpncpy(ptr nonnull [[DST]], ptr nonnull [[DST]], i64 [[N:%.*]]) 77; ANY-NEXT: call void @sink(ptr nonnull [[DST]], ptr [[ES_N]]) 78; ANY-NEXT: ret void 79; 80; Do not transform stpncpy(D, D, 2). 81 %es_2 = call ptr @stpncpy(ptr %dst, ptr %dst, i64 2) 82 call void @sink(ptr %dst, ptr %es_2) 83 84; Do not transform stpncpy(D, D, 3). 85 %es_3 = call ptr @stpncpy(ptr %dst, ptr %dst, i64 3) 86 call void @sink(ptr %dst, ptr %es_3) 87 88; Do not transform stpncpy(D, D, N). 89 %es_n = call ptr @stpncpy(ptr %dst, ptr %dst, i64 %n) 90 call void @sink(ptr %dst, ptr %es_n) 91 92 ret void 93} 94 95 96; Verify that stpncpy(D, "", N) calls are transformed to memset(D, 0, N). 97 98define void @fold_stpncpy_s0(ptr %dst, i64 %n) { 99; ANY-LABEL: @fold_stpncpy_s0( 100; ANY-NEXT: call void @sink(ptr [[DST:%.*]], ptr [[DST]]) 101; ANY-NEXT: store i8 0, ptr [[DST]], align 1 102; ANY-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[DST]]) 103; ANY-NEXT: store i16 0, ptr [[DST]], align 1 104; ANY-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[DST]]) 105; ANY-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(9) [[DST]], i8 0, i64 9, i1 false) 106; ANY-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[DST]]) 107; ANY-NEXT: call void @llvm.memset.p0.i64(ptr nonnull align 1 [[DST]], i8 0, i64 [[N:%.*]], i1 false) 108; ANY-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[DST]]) 109; ANY-NEXT: ret void 110; 111 %ps0 = getelementptr [5 x i8], ptr @s4, i32 0, i32 4 112 113; Fold stpncpy(D, "", 0) to just D. 114 %es0_0 = call ptr @stpncpy(ptr %dst, ptr %ps0, i64 0) 115 call void @sink(ptr %dst, ptr %es0_0) 116 117; Transform stpncpy(D, "", 1) to *D = '\0, D. 118 %es0_1 = call ptr @stpncpy(ptr %dst, ptr %ps0, i64 1) 119 call void @sink(ptr %dst, ptr %es0_1) 120 121; Transform stpncpy(D, "", 2) to memset(D, 0, 2), D. 122 %es0_2 = call ptr @stpncpy(ptr %dst, ptr %ps0, i64 2) 123 call void @sink(ptr %dst, ptr %es0_2) 124 125; Transform stpncpy(D, "", 9) to memset(D, 0, 9), D. 126 %es0_9 = call ptr @stpncpy(ptr %dst, ptr %ps0, i64 9) 127 call void @sink(ptr %dst, ptr %es0_9) 128 129; Transform stpncpy(D, "", n) to memset(D, 0, n), D. 130 %es0_n = call ptr @stpncpy(ptr %dst, ptr %ps0, i64 %n) 131 call void @sink(ptr %dst, ptr %es0_n) 132 133 ret void 134} 135 136 137; Verify that stpncpy(D, "4", N) calls are transformed to the equivalent 138; of strncpy(D, "4", N) and the result folded to D + (N != 0). 139 140define void @fold_stpncpy_s1(ptr %dst) { 141; BE-LABEL: @fold_stpncpy_s1( 142; BE-NEXT: call void @sink(ptr [[DST:%.*]], ptr [[DST]]) 143; BE-NEXT: store i8 52, ptr [[DST]], align 1 144; BE-NEXT: [[STPNCPY_END:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 1 145; BE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[STPNCPY_END]]) 146; BE-NEXT: store i16 13312, ptr [[DST]], align 1 147; BE-NEXT: [[ENDPTR:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 1 148; BE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR]]) 149; BE-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 1 dereferenceable(3) [[DST]], ptr noundef nonnull align 1 dereferenceable(3) @str.6, i64 3, i1 false) 150; BE-NEXT: [[ENDPTR1:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 1 151; BE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR1]]) 152; BE-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 1 dereferenceable(9) [[DST]], ptr noundef nonnull align 1 dereferenceable(9) @str.7, i64 9, i1 false) 153; BE-NEXT: [[ENDPTR2:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 1 154; BE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR2]]) 155; BE-NEXT: ret void 156; 157; LE-LABEL: @fold_stpncpy_s1( 158; LE-NEXT: call void @sink(ptr [[DST:%.*]], ptr [[DST]]) 159; LE-NEXT: store i8 52, ptr [[DST]], align 1 160; LE-NEXT: [[STPNCPY_END:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 1 161; LE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[STPNCPY_END]]) 162; LE-NEXT: store i16 52, ptr [[DST]], align 1 163; LE-NEXT: [[ENDPTR:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 1 164; LE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR]]) 165; LE-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 1 dereferenceable(3) [[DST]], ptr noundef nonnull align 1 dereferenceable(3) @str.6, i64 3, i1 false) 166; LE-NEXT: [[ENDPTR1:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 1 167; LE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR1]]) 168; LE-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 1 dereferenceable(9) [[DST]], ptr noundef nonnull align 1 dereferenceable(9) @str.7, i64 9, i1 false) 169; LE-NEXT: [[ENDPTR2:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 1 170; LE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR2]]) 171; LE-NEXT: ret void 172; 173 %ps1 = getelementptr [5 x i8], ptr @s4, i32 0, i32 3 174 175; Fold stpncpy(D, "4", 0) to just D. 176 %es1_0 = call ptr @stpncpy(ptr %dst, ptr %ps1, i64 0) 177 call void @sink(ptr %dst, ptr %es1_0) 178 179; Transform stpncpy(D, "4", 1) to *D = '4', D + 1. 180 %es1_1 = call ptr @stpncpy(ptr %dst, ptr %ps1, i64 1) 181 call void @sink(ptr %dst, ptr %es1_1) 182 183; Transform stpncpy(D, "4", 2) to strncpy(D, "4", 2) + 1. 184 %es1_2 = call ptr @stpncpy(ptr %dst, ptr %ps1, i64 2) 185 call void @sink(ptr %dst, ptr %es1_2) 186 187; Transform stpncpy(D, "4", 3) to strncpy(D, "4", 3) + 1, which is then 188; transformed to memcpy(D, "4", 2), D[2] = '\0', D + 1. 189 %es1_3 = call ptr @stpncpy(ptr %dst, ptr %ps1, i64 3) 190 call void @sink(ptr %dst, ptr %es1_3) 191 192; Transform stpncpy(D, "4", 9) to strncpy(D, "4", 9) + 1. 193 %es1_9 = call ptr @stpncpy(ptr %dst, ptr %ps1, i64 9) 194 call void @sink(ptr %dst, ptr %es1_9) 195 196 ret void 197} 198 199 200; Verify that stpncpy(D, "1234", N) calls are transformed to the equivalent 201; of strncpy(D, "1234", N) and the result folded to D + min(4, N). 202 203define void @fold_stpncpy_s4(ptr %dst, i64 %n) { 204; BE-LABEL: @fold_stpncpy_s4( 205; BE-NEXT: call void @sink(ptr [[DST:%.*]], ptr [[DST]]) 206; BE-NEXT: store i8 49, ptr [[DST]], align 1 207; BE-NEXT: [[STPNCPY_END:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 1 208; BE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[STPNCPY_END]]) 209; BE-NEXT: store i16 12594, ptr [[DST]], align 1 210; BE-NEXT: [[ENDPTR:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 2 211; BE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR]]) 212; BE-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 1 dereferenceable(3) [[DST]], ptr noundef nonnull align 1 dereferenceable(5) @s4, i64 3, i1 false) 213; BE-NEXT: [[ENDPTR1:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 3 214; BE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR1]]) 215; BE-NEXT: store i32 825373492, ptr [[DST]], align 1 216; BE-NEXT: [[ENDPTR2:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 4 217; BE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR2]]) 218; BE-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 1 dereferenceable(9) [[DST]], ptr noundef nonnull align 1 dereferenceable(9) @str.8, i64 9, i1 false) 219; BE-NEXT: [[ENDPTR3:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 4 220; BE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR3]]) 221; BE-NEXT: ret void 222; 223; LE-LABEL: @fold_stpncpy_s4( 224; LE-NEXT: call void @sink(ptr [[DST:%.*]], ptr [[DST]]) 225; LE-NEXT: store i8 49, ptr [[DST]], align 1 226; LE-NEXT: [[STPNCPY_END:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 1 227; LE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[STPNCPY_END]]) 228; LE-NEXT: store i16 12849, ptr [[DST]], align 1 229; LE-NEXT: [[ENDPTR:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 2 230; LE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR]]) 231; LE-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 1 dereferenceable(3) [[DST]], ptr noundef nonnull align 1 dereferenceable(5) @s4, i64 3, i1 false) 232; LE-NEXT: [[ENDPTR1:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 3 233; LE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR1]]) 234; LE-NEXT: store i32 875770417, ptr [[DST]], align 1 235; LE-NEXT: [[ENDPTR2:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 4 236; LE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR2]]) 237; LE-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 1 dereferenceable(9) [[DST]], ptr noundef nonnull align 1 dereferenceable(9) @str.8, i64 9, i1 false) 238; LE-NEXT: [[ENDPTR3:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 4 239; LE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR3]]) 240; LE-NEXT: ret void 241; 242 243; Fold stpncpy(D, "1234", 0) to just D. 244 %es4_0 = call ptr @stpncpy(ptr %dst, ptr @s4, i64 0) 245 call void @sink(ptr %dst, ptr %es4_0) 246 247; Transform stpncpy(D, "1234", 1) to *D = '4', D + 1. 248 %es4_1 = call ptr @stpncpy(ptr %dst, ptr @s4, i64 1) 249 call void @sink(ptr %dst, ptr %es4_1) 250 251; Transform stpncpy(D, "1234", 2) to strncpy(D, "1234", 2) + 2. 252 %es4_2 = call ptr @stpncpy(ptr %dst, ptr @s4, i64 2) 253 call void @sink(ptr %dst, ptr %es4_2) 254 255; Transform stpncpy(D, "1234", 3) to strncpy(D, "1234", 3) + 3 256 %es4_3 = call ptr @stpncpy(ptr %dst, ptr @s4, i64 3) 257 call void @sink(ptr %dst, ptr %es4_3) 258 259; Transform stpncpy(D, "1234", 4) to strncpy(D, "1234", 4) + 4. 260 %es4_4 = call ptr @stpncpy(ptr %dst, ptr @s4, i64 4) 261 call void @sink(ptr %dst, ptr %es4_4) 262 263; Transform stpncpy(D, "1234", 9) to strncpy(D, "1234", 9) + 4. 264 %es4_9 = call ptr @stpncpy(ptr %dst, ptr @s4, i64 9) 265 call void @sink(ptr %dst, ptr %es4_9) 266 267 ret void 268} 269 270 271; Verify that a call to stpncpy(D, A, N) with a constant source larger 272; than one byte is left alone when N is unknown. 273 274define void @call_stpncpy_xx_n(ptr %dst, i64 %n) { 275; ANY-LABEL: @call_stpncpy_xx_n( 276; ANY-NEXT: [[EA1_N:%.*]] = call ptr @stpncpy(ptr [[DST:%.*]], ptr nonnull dereferenceable(2) getelementptr inbounds nuw (i8, ptr @a4, i64 3), i64 [[N:%.*]]) 277; ANY-NEXT: call void @sink(ptr [[DST]], ptr [[EA1_N]]) 278; ANY-NEXT: [[EA4_N:%.*]] = call ptr @stpncpy(ptr [[DST]], ptr nonnull dereferenceable(5) @a4, i64 [[N]]) 279; ANY-NEXT: call void @sink(ptr [[DST]], ptr [[EA4_N]]) 280; ANY-NEXT: [[ES1_N:%.*]] = call ptr @stpncpy(ptr [[DST]], ptr nonnull dereferenceable(2) getelementptr inbounds nuw (i8, ptr @s4, i64 3), i64 [[N]]) 281; ANY-NEXT: call void @sink(ptr [[DST]], ptr [[ES1_N]]) 282; ANY-NEXT: [[ES4_N:%.*]] = call ptr @stpncpy(ptr [[DST]], ptr nonnull dereferenceable(5) @s4, i64 [[N]]) 283; ANY-NEXT: call void @sink(ptr [[DST]], ptr [[ES4_N]]) 284; ANY-NEXT: ret void 285; 286; Do not transform stpncpy(D, A4 + 3, N) when N is unknown. 287 %pa1 = getelementptr [4 x i8], ptr @a4, i32 0, i32 3 288 %ea1_n = call ptr @stpncpy(ptr %dst, ptr %pa1, i64 %n) 289 call void @sink(ptr %dst, ptr %ea1_n) 290 291; Do not transform stpncpy(D, A4, N) when N is unknown. 292 %ea4_n = call ptr @stpncpy(ptr %dst, ptr @a4, i64 %n) 293 call void @sink(ptr %dst, ptr %ea4_n) 294 295; Do not transform stpncpy(D, "4", N) when N is unknown. 296 %ps1 = getelementptr [5 x i8], ptr @s4, i32 0, i32 3 297 %es1_n = call ptr @stpncpy(ptr %dst, ptr %ps1, i64 %n) 298 call void @sink(ptr %dst, ptr %es1_n) 299 300; Likewise, do not transform stpncpy(D, "1234", N) when N is unknown. 301 %es4_n = call ptr @stpncpy(ptr %dst, ptr @s4, i64 %n) 302 call void @sink(ptr %dst, ptr %es4_n) 303 304 ret void 305} 306 307; Verify that stpncpy(D, (char[4]){"1234"}, N) calls with an unterminated 308; source array are transformed to the equivalent strncpy call and the result 309; folded to D + min(4, N). 310 311define void @fold_stpncpy_a4(ptr %dst, i64 %n) { 312; BE-LABEL: @fold_stpncpy_a4( 313; BE-NEXT: call void @sink(ptr [[DST:%.*]], ptr [[DST]]) 314; BE-NEXT: store i8 49, ptr [[DST]], align 1 315; BE-NEXT: [[STPNCPY_END:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 1 316; BE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[STPNCPY_END]]) 317; BE-NEXT: store i16 12594, ptr [[DST]], align 1 318; BE-NEXT: [[ENDPTR:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 2 319; BE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR]]) 320; BE-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 1 dereferenceable(3) [[DST]], ptr noundef nonnull align 1 dereferenceable(5) @a4, i64 3, i1 false) 321; BE-NEXT: [[ENDPTR1:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 3 322; BE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR1]]) 323; BE-NEXT: store i32 825373492, ptr [[DST]], align 1 324; BE-NEXT: [[ENDPTR2:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 4 325; BE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR2]]) 326; BE-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 1 dereferenceable(5) [[DST]], ptr noundef nonnull align 1 dereferenceable(5) @a4, i64 5, i1 false) 327; BE-NEXT: [[ENDPTR3:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 4 328; BE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR3]]) 329; BE-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 1 dereferenceable(9) [[DST]], ptr noundef nonnull align 1 dereferenceable(9) @str.9, i64 9, i1 false) 330; BE-NEXT: [[ENDPTR4:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 4 331; BE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR4]]) 332; BE-NEXT: ret void 333; 334; LE-LABEL: @fold_stpncpy_a4( 335; LE-NEXT: call void @sink(ptr [[DST:%.*]], ptr [[DST]]) 336; LE-NEXT: store i8 49, ptr [[DST]], align 1 337; LE-NEXT: [[STPNCPY_END:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 1 338; LE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[STPNCPY_END]]) 339; LE-NEXT: store i16 12849, ptr [[DST]], align 1 340; LE-NEXT: [[ENDPTR:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 2 341; LE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR]]) 342; LE-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 1 dereferenceable(3) [[DST]], ptr noundef nonnull align 1 dereferenceable(5) @a4, i64 3, i1 false) 343; LE-NEXT: [[ENDPTR1:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 3 344; LE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR1]]) 345; LE-NEXT: store i32 875770417, ptr [[DST]], align 1 346; LE-NEXT: [[ENDPTR2:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 4 347; LE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR2]]) 348; LE-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 1 dereferenceable(5) [[DST]], ptr noundef nonnull align 1 dereferenceable(5) @a4, i64 5, i1 false) 349; LE-NEXT: [[ENDPTR3:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 4 350; LE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR3]]) 351; LE-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 1 dereferenceable(9) [[DST]], ptr noundef nonnull align 1 dereferenceable(9) @str.9, i64 9, i1 false) 352; LE-NEXT: [[ENDPTR4:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 4 353; LE-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[ENDPTR4]]) 354; LE-NEXT: ret void 355; 356 357 358; Fold stpncpy(D, A4, 0) to just D. 359 %ea4_0 = call ptr @stpncpy(ptr %dst, ptr @a4, i64 0) 360 call void @sink(ptr %dst, ptr %ea4_0) 361 362; Transform stpncpy(D, A4, 1) to *D = '4', D + 1. 363 %ea4_1 = call ptr @stpncpy(ptr %dst, ptr @a4, i64 1) 364 call void @sink(ptr %dst, ptr %ea4_1) 365 366; Transform stpncpy(D, A4, 2) to strncpy(D, A4, 2) + 2. 367 %ea4_2 = call ptr @stpncpy(ptr %dst, ptr @a4, i64 2) 368 call void @sink(ptr %dst, ptr %ea4_2) 369 370; Transform stpncpy(D, A4, 3) to strncpy(D, A4, 3) + 3 371 %ea4_3 = call ptr @stpncpy(ptr %dst, ptr @a4, i64 3) 372 call void @sink(ptr %dst, ptr %ea4_3) 373 374; Transform stpncpy(D, A4, 4) to strncpy(D, A4, 4) + 4. 375 %ea4_4 = call ptr @stpncpy(ptr %dst, ptr @a4, i64 4) 376 call void @sink(ptr %dst, ptr %ea4_4) 377 378; Transform stpncpy(D, A4, 5) to strncpy(D, A4, 5) + 4. 379 %ea4_5 = call ptr @stpncpy(ptr %dst, ptr @a4, i64 5) 380 call void @sink(ptr %dst, ptr %ea4_5) 381 382; Transform stpncpy(D, A4, 9) to strncpy(D, A4, 9) + 4. 383 %ea4_9 = call ptr @stpncpy(ptr %dst, ptr @a4, i64 9) 384 call void @sink(ptr %dst, ptr %ea4_9) 385 386 ret void 387} 388 389 390; Verify that stpncpy(D, S, N) calls with N < 2 are transformed to 391; the equivalent of strncpy and either folded to D if N == 0 or to 392; *D ? D + 1 : D otherwise. 393 394define void @fold_stpncpy_s(ptr %dst, ptr %src) { 395; ANY-LABEL: @fold_stpncpy_s( 396; ANY-NEXT: call void @sink(ptr [[DST:%.*]], ptr [[DST]]) 397; ANY-NEXT: [[STXNCPY_CHAR0:%.*]] = load i8, ptr [[SRC:%.*]], align 1 398; ANY-NEXT: store i8 [[STXNCPY_CHAR0]], ptr [[DST]], align 1 399; ANY-NEXT: [[STPNCPY_CHAR0CMP:%.*]] = icmp ne i8 [[STXNCPY_CHAR0]], 0 400; ANY-NEXT: [[STPNCPY_SEL_IDX:%.*]] = zext i1 [[STPNCPY_CHAR0CMP]] to i64 401; ANY-NEXT: [[STPNCPY_SEL:%.*]] = getelementptr inbounds nuw i8, ptr [[DST]], i64 [[STPNCPY_SEL_IDX]] 402; ANY-NEXT: call void @sink(ptr nonnull [[DST]], ptr nonnull [[STPNCPY_SEL]]) 403; ANY-NEXT: ret void 404; 405; Fold stpncpy(D, S, 0) to just D. 406 %es_0 = call ptr @stpncpy(ptr %dst, ptr %src, i64 0) 407 call void @sink(ptr %dst, ptr %es_0) 408 409; Transform stpncpy(D, "", 1) to *D = '\0, D. 410 %es_1 = call ptr @stpncpy(ptr %dst, ptr %src, i64 1) 411 call void @sink(ptr %dst, ptr %es_1) 412 413 ret void 414} 415 416 417; Verify that stpncpy(D, S, N) calls with N >= 2 are not transformed. 418; In theory they could be transformed to the equivalent of the following 419; though it's not clear that it would be a win: 420; P = memccpy(D, S, 0, N) 421; N' = P ? N - (P - D) : 0 422; Q = P ? P : D + N 423; memset(Q, 0, N') 424; Q 425; Also verify that the arguments of the call are annotated with the right 426; attributes. 427 428define void @call_stpncpy_s(ptr %dst, ptr %src, i64 %n) { 429; ANY-LABEL: @call_stpncpy_s( 430; ANY-NEXT: [[ES_2:%.*]] = call ptr @stpncpy(ptr noundef nonnull dereferenceable(1) [[DST:%.*]], ptr noundef nonnull dereferenceable(1) [[SRC:%.*]], i64 2) 431; ANY-NEXT: call void @sink(ptr nonnull [[DST]], ptr [[ES_2]]) 432; ANY-NEXT: [[ES_N:%.*]] = call ptr @stpncpy(ptr nonnull [[DST]], ptr nonnull [[SRC]], i64 [[N:%.*]]) 433; ANY-NEXT: call void @sink(ptr nonnull [[DST]], ptr [[ES_N]]) 434; ANY-NEXT: ret void 435; 436; Do not transform stpncpy(D, S, 2). Both *D and *S must be derefernceable 437; but neither D[1] nor S[1] need be. 438 %es_2 = call ptr @stpncpy(ptr %dst, ptr %src, i64 2) 439 call void @sink(ptr %dst, ptr %es_2) 440 441; Do not transform stpncpy(D, S, N). Both D and S must be nonnull but 442; neither *D nor *S need be dereferenceable. 443; TODO: Both D and S should be annotated nonnull and noundef regardless 444; of the value of N. See https://reviews.llvm.org/D124633. 445 %es_n = call ptr @stpncpy(ptr %dst, ptr %src, i64 %n) 446 call void @sink(ptr %dst, ptr %es_n) 447 448 ret void 449} 450;. 451; BE: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: write) } 452; BE: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } 453;. 454; LE: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: write) } 455; LE: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } 456;. 457