1 | /* Measure strcmp and wcscmp functions. |
2 | Copyright (C) 2013-2022 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #define TEST_MAIN |
20 | #ifdef WIDE |
21 | # define TEST_NAME "wcscmp" |
22 | #else |
23 | # define TEST_NAME "strcmp" |
24 | #endif |
25 | #include "bench-string.h" |
26 | |
27 | #ifdef WIDE |
28 | # define L(str) L##str |
29 | # define SIMPLE_STRCMP simple_wcscmp |
30 | # define CHARBYTESLOG 2 |
31 | # define MIDCHAR 0x7fffffff |
32 | # define LARGECHAR 0xfffffffe |
33 | |
34 | /* Wcscmp uses signed semantics for comparison, not unsigned */ |
35 | /* Avoid using substraction since possible overflow */ |
36 | |
37 | int |
38 | simple_wcscmp (const wchar_t *s1, const wchar_t *s2) |
39 | { |
40 | wchar_t c1, c2; |
41 | do |
42 | { |
43 | c1 = *s1++; |
44 | c2 = *s2++; |
45 | if (c2 == L'\0') |
46 | return c1 - c2; |
47 | } |
48 | while (c1 == c2); |
49 | |
50 | return c1 < c2 ? -1 : 1; |
51 | } |
52 | |
53 | #else |
54 | # include <limits.h> |
55 | |
56 | # define L(str) str |
57 | # define SIMPLE_STRCMP simple_strcmp |
58 | # define CHARBYTESLOG 0 |
59 | # define MIDCHAR 0x7f |
60 | # define LARGECHAR 0xfe |
61 | |
62 | /* Strcmp uses unsigned semantics for comparison. */ |
63 | int |
64 | simple_strcmp (const char *s1, const char *s2) |
65 | { |
66 | int ret; |
67 | |
68 | while ((ret = *(unsigned char *) s1 - *(unsigned char*) s2++) == 0 && *s1++); |
69 | return ret; |
70 | } |
71 | |
72 | #endif |
73 | |
74 | # include "json-lib.h" |
75 | |
76 | typedef int (*proto_t) (const CHAR *, const CHAR *); |
77 | |
78 | IMPL (SIMPLE_STRCMP, 1) |
79 | IMPL (STRCMP, 1) |
80 | |
81 | static void |
82 | do_one_test (json_ctx_t *json_ctx, impl_t *impl, |
83 | const CHAR *s1, const CHAR *s2, |
84 | int exp_result) |
85 | { |
86 | size_t i, iters = INNER_LOOP_ITERS8; |
87 | timing_t start, stop, cur; |
88 | |
89 | TIMING_NOW (start); |
90 | for (i = 0; i < iters; ++i) |
91 | { |
92 | CALL (impl, s1, s2); |
93 | } |
94 | TIMING_NOW (stop); |
95 | |
96 | TIMING_DIFF (cur, start, stop); |
97 | |
98 | json_element_double (ctx: json_ctx, d: (double) cur / (double) iters); |
99 | } |
100 | |
101 | static void |
102 | do_test (json_ctx_t *json_ctx, size_t align1, size_t align2, size_t len, int |
103 | max_char, int exp_result) |
104 | { |
105 | size_t i; |
106 | |
107 | CHAR *s1, *s2; |
108 | |
109 | if (len == 0) |
110 | return; |
111 | |
112 | align1 &= 63; |
113 | if (align1 + (len + 1) * CHARBYTES >= page_size) |
114 | return; |
115 | |
116 | align2 &= 63; |
117 | if (align2 + (len + 1) * CHARBYTES >= page_size) |
118 | return; |
119 | |
120 | /* Put them close to the end of page. */ |
121 | i = align1 + CHARBYTES * (len + 2); |
122 | s1 = (CHAR *) (buf1 + ((page_size - i) / 16 * 16) + align1); |
123 | i = align2 + CHARBYTES * (len + 2); |
124 | s2 = (CHAR *) (buf2 + ((page_size - i) / 16 * 16) + align2); |
125 | |
126 | for (i = 0; i < len; i++) |
127 | s1[i] = s2[i] = 1 + (23 << ((CHARBYTES - 1) * 8)) * i % max_char; |
128 | |
129 | s1[len] = s2[len] = 0; |
130 | s1[len + 1] = 23; |
131 | s2[len + 1] = 24 + exp_result; |
132 | s2[len - 1] -= exp_result; |
133 | |
134 | json_element_object_begin (ctx: json_ctx); |
135 | json_attr_uint (ctx: json_ctx, name: "length" , d: (double) len); |
136 | json_attr_uint (ctx: json_ctx, name: "align1" , d: (double) align1); |
137 | json_attr_uint (ctx: json_ctx, name: "align2" , d: (double) align2); |
138 | json_array_begin (ctx: json_ctx, name: "timings" ); |
139 | |
140 | FOR_EACH_IMPL (impl, 0) |
141 | do_one_test (json_ctx, impl, s1, s2, exp_result); |
142 | |
143 | json_array_end (ctx: json_ctx); |
144 | json_element_object_end (ctx: json_ctx); |
145 | } |
146 | |
147 | static void |
148 | do_one_test_page_boundary (json_ctx_t *json_ctx, CHAR *s1, CHAR *s2, |
149 | size_t align1, size_t align2, size_t len, |
150 | int exp_result) |
151 | { |
152 | json_element_object_begin (ctx: json_ctx); |
153 | json_attr_uint (ctx: json_ctx, name: "length" , d: (double) len); |
154 | json_attr_uint (ctx: json_ctx, name: "align1" , d: (double) align1); |
155 | json_attr_uint (ctx: json_ctx, name: "align2" , d: (double) align2); |
156 | json_array_begin (ctx: json_ctx, name: "timings" ); |
157 | FOR_EACH_IMPL (impl, 0) |
158 | do_one_test (json_ctx, impl, s1, s2, exp_result); |
159 | json_array_end (ctx: json_ctx); |
160 | json_element_object_end (ctx: json_ctx); |
161 | } |
162 | |
163 | static void |
164 | do_test_page_boundary (json_ctx_t *json_ctx) |
165 | { |
166 | /* To trigger bug 25933, we need a size that is equal to the vector |
167 | length times 4. In the case of AVX2 for Intel, we need 32 * 4. We |
168 | make this test generic and run it for all architectures as additional |
169 | boundary testing for such related algorithms. */ |
170 | size_t size = 32 * 4; |
171 | size_t len; |
172 | CHAR *s1 = (CHAR *) (buf1 + (BUF1PAGES - 1) * page_size); |
173 | CHAR *s2 = (CHAR *) (buf2 + (BUF1PAGES - 1) * page_size); |
174 | int exp_result; |
175 | |
176 | memset (s1, 'a', page_size); |
177 | memset (s2, 'a', page_size); |
178 | |
179 | s1[(page_size / CHARBYTES) - 1] = (CHAR) 0; |
180 | s2[(page_size / CHARBYTES) - 1] = (CHAR) 0; |
181 | |
182 | /* Iterate over a size that is just below where we expect the bug to |
183 | trigger up to the size we expect will trigger the bug e.g. [99-128]. |
184 | Likewise iterate the start of two strings between 30 and 31 bytes |
185 | away from the boundary to simulate alignment changes. */ |
186 | for (size_t s = 99; s <= size; s++) |
187 | for (size_t s1a = 30; s1a < 32; s1a++) |
188 | for (size_t s2a = 30; s2a < 32; s2a++) |
189 | { |
190 | size_t align1 = (page_size / CHARBYTES - s) - s1a; |
191 | size_t align2 = (page_size / CHARBYTES - s) - s2a; |
192 | CHAR *s1p = s1 + align1; |
193 | CHAR *s2p = s2 + align2; |
194 | len = (page_size / CHARBYTES) - 1 - align1; |
195 | exp_result = SIMPLE_STRCMP (s1: s1p, s2: s2p); |
196 | do_one_test_page_boundary (json_ctx, s1: s1p, s2: s2p, align1, align2, |
197 | len, exp_result); |
198 | } |
199 | } |
200 | |
201 | int |
202 | test_main (void) |
203 | { |
204 | json_ctx_t json_ctx; |
205 | size_t i; |
206 | |
207 | test_init (); |
208 | |
209 | json_init (ctx: &json_ctx, indent_level: 0, stdout); |
210 | |
211 | json_document_begin (ctx: &json_ctx); |
212 | json_attr_string (ctx: &json_ctx, name: "timing_type" , TIMING_TYPE); |
213 | |
214 | json_attr_object_begin (ctx: &json_ctx, name: "functions" ); |
215 | json_attr_object_begin (ctx: &json_ctx, TEST_NAME); |
216 | json_attr_string (ctx: &json_ctx, name: "bench-variant" , s: "default" ); |
217 | |
218 | json_array_begin (ctx: &json_ctx, name: "ifuncs" ); |
219 | FOR_EACH_IMPL (impl, 0) |
220 | json_element_string (ctx: &json_ctx, s: impl->name); |
221 | json_array_end (ctx: &json_ctx); |
222 | |
223 | json_array_begin (ctx: &json_ctx, name: "results" ); |
224 | |
225 | for (i = 1; i < 32; ++i) |
226 | { |
227 | do_test (json_ctx: &json_ctx, CHARBYTES * i, CHARBYTES * i, len: i, MIDCHAR, exp_result: 0); |
228 | do_test (json_ctx: &json_ctx, CHARBYTES * i, CHARBYTES * i, len: i, MIDCHAR, exp_result: 1); |
229 | do_test (json_ctx: &json_ctx, CHARBYTES * i, CHARBYTES * i, len: i, MIDCHAR, exp_result: -1); |
230 | } |
231 | |
232 | for (i = 1; i < 10 + CHARBYTESLOG; ++i) |
233 | { |
234 | do_test (json_ctx: &json_ctx, align1: 0, align2: 0, len: 2 << i, MIDCHAR, exp_result: 0); |
235 | do_test (json_ctx: &json_ctx, align1: 0, align2: 0, len: 2 << i, LARGECHAR, exp_result: 0); |
236 | do_test (json_ctx: &json_ctx, align1: 0, align2: 0, len: 2 << i, MIDCHAR, exp_result: 1); |
237 | do_test (json_ctx: &json_ctx, align1: 0, align2: 0, len: 2 << i, LARGECHAR, exp_result: 1); |
238 | do_test (json_ctx: &json_ctx, align1: 0, align2: 0, len: 2 << i, MIDCHAR, exp_result: -1); |
239 | do_test (json_ctx: &json_ctx, align1: 0, align2: 0, len: 2 << i, LARGECHAR, exp_result: -1); |
240 | do_test (json_ctx: &json_ctx, align1: 0, CHARBYTES * i, len: 2 << i, MIDCHAR, exp_result: 1); |
241 | do_test (json_ctx: &json_ctx, CHARBYTES * i, CHARBYTES * (i + 1), len: 2 << i, LARGECHAR, exp_result: 1); |
242 | } |
243 | |
244 | for (i = 1; i < 8; ++i) |
245 | { |
246 | do_test (json_ctx: &json_ctx, CHARBYTES * i, align2: 2 * CHARBYTES * i, len: 8 << i, MIDCHAR, exp_result: 0); |
247 | do_test (json_ctx: &json_ctx, align1: 2 * CHARBYTES * i, CHARBYTES * i, len: 8 << i, LARGECHAR, exp_result: 0); |
248 | do_test (json_ctx: &json_ctx, CHARBYTES * i, align2: 2 * CHARBYTES * i, len: 8 << i, MIDCHAR, exp_result: 1); |
249 | do_test (json_ctx: &json_ctx, align1: 2 * CHARBYTES * i, CHARBYTES * i, len: 8 << i, LARGECHAR, exp_result: 1); |
250 | do_test (json_ctx: &json_ctx, CHARBYTES * i, align2: 2 * CHARBYTES * i, len: 8 << i, MIDCHAR, exp_result: -1); |
251 | do_test (json_ctx: &json_ctx, align1: 2 * CHARBYTES * i, CHARBYTES * i, len: 8 << i, LARGECHAR, exp_result: -1); |
252 | } |
253 | |
254 | do_test_page_boundary (json_ctx: &json_ctx); |
255 | |
256 | json_array_end (ctx: &json_ctx); |
257 | json_attr_object_end (ctx: &json_ctx); |
258 | json_attr_object_end (ctx: &json_ctx); |
259 | json_document_end (ctx: &json_ctx); |
260 | |
261 | return ret; |
262 | } |
263 | |
264 | #include <support/test-driver.c> |
265 | |