xref: /llvm-project/llvm/test/Transforms/InstCombine/stpncpy-1.ll (revision f226cabbb1b9737676536bc4417336bef4808992)
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