1 /**************************************************************************
2 *
3 * Copyright 2009 VMware, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28
29 /**
30 * @file
31 * Unit tests for type conversion.
32 *
33 * @author Jose Fonseca <[email protected]>
34 */
35
36
37 #include "util/u_pointer.h"
38 #include "gallivm/lp_bld_init.h"
39 #include "gallivm/lp_bld_type.h"
40 #include "gallivm/lp_bld_const.h"
41 #include "gallivm/lp_bld_conv.h"
42 #include "gallivm/lp_bld_debug.h"
43 #include "lp_test.h"
44
45
46 typedef void (*conv_test_ptr_t)(const void *src, const void *dst);
47
48
49 void
write_tsv_header(FILE * fp)50 write_tsv_header(FILE *fp)
51 {
52 fprintf(fp,
53 "result\t"
54 "cycles_per_channel\t"
55 "src_type\t"
56 "dst_type\n");
57
58 fflush(fp);
59 }
60
61
62 static void
write_tsv_row(FILE * fp,struct lp_type src_type,struct lp_type dst_type,double cycles,bool success)63 write_tsv_row(FILE *fp,
64 struct lp_type src_type,
65 struct lp_type dst_type,
66 double cycles,
67 bool success)
68 {
69 fprintf(fp, "%s\t", success ? "pass" : "fail");
70
71 fprintf(fp, "%.1f\t", cycles / MAX2(src_type.length, dst_type.length));
72
73 dump_type(fp, src_type);
74 fprintf(fp, "\t");
75
76 dump_type(fp, dst_type);
77 fprintf(fp, "\n");
78
79 fflush(fp);
80 }
81
82
83 static void
dump_conv_types(FILE * fp,struct lp_type src_type,struct lp_type dst_type)84 dump_conv_types(FILE *fp,
85 struct lp_type src_type,
86 struct lp_type dst_type)
87 {
88 fprintf(fp, "src_type=");
89 dump_type(fp, src_type);
90
91 fprintf(fp, " dst_type=");
92 dump_type(fp, dst_type);
93
94 fprintf(fp, " ...\n");
95 fflush(fp);
96 }
97
98
99 static LLVMValueRef
add_conv_test(struct gallivm_state * gallivm,struct lp_type src_type,unsigned num_srcs,struct lp_type dst_type,unsigned num_dsts)100 add_conv_test(struct gallivm_state *gallivm,
101 struct lp_type src_type, unsigned num_srcs,
102 struct lp_type dst_type, unsigned num_dsts)
103 {
104 LLVMModuleRef module = gallivm->module;
105 LLVMContextRef context = gallivm->context;
106 LLVMBuilderRef builder = gallivm->builder;
107 LLVMTypeRef args[2];
108 LLVMValueRef func;
109 LLVMValueRef src_ptr;
110 LLVMValueRef dst_ptr;
111 LLVMBasicBlockRef block;
112 LLVMValueRef src[LP_MAX_VECTOR_LENGTH];
113 LLVMValueRef dst[LP_MAX_VECTOR_LENGTH];
114 unsigned i;
115 LLVMTypeRef src_vec_type = lp_build_vec_type(gallivm, src_type);
116 LLVMTypeRef dst_vec_type = lp_build_vec_type(gallivm, dst_type);
117
118 args[0] = LLVMPointerType(src_vec_type, 0);
119 args[1] = LLVMPointerType(dst_vec_type, 0);
120
121 func = LLVMAddFunction(module, "test",
122 LLVMFunctionType(LLVMVoidTypeInContext(context),
123 args, 2, 0));
124 LLVMSetFunctionCallConv(func, LLVMCCallConv);
125 src_ptr = LLVMGetParam(func, 0);
126 dst_ptr = LLVMGetParam(func, 1);
127
128 block = LLVMAppendBasicBlockInContext(context, func, "entry");
129 LLVMPositionBuilderAtEnd(builder, block);
130
131 for (i = 0; i < num_srcs; ++i) {
132 LLVMValueRef index = LLVMConstInt(LLVMInt32TypeInContext(context), i, 0);
133 LLVMValueRef ptr = LLVMBuildGEP2(builder, src_vec_type, src_ptr, &index, 1, "");
134 src[i] = LLVMBuildLoad2(builder, src_vec_type, ptr, "");
135 }
136
137 lp_build_conv(gallivm, src_type, dst_type, src, num_srcs, dst, num_dsts);
138
139 for (i = 0; i < num_dsts; ++i) {
140 LLVMValueRef index = LLVMConstInt(LLVMInt32TypeInContext(context), i, 0);
141 LLVMValueRef ptr = LLVMBuildGEP2(builder, dst_vec_type, dst_ptr, &index, 1, "");
142 LLVMBuildStore(builder, dst[i], ptr);
143 }
144
145 LLVMBuildRetVoid(builder);
146
147 gallivm_verify_function(gallivm, func);
148
149 return func;
150 }
151
152
153 UTIL_ALIGN_STACK
154 static bool
test_one(unsigned verbose,FILE * fp,struct lp_type src_type,struct lp_type dst_type)155 test_one(unsigned verbose,
156 FILE *fp,
157 struct lp_type src_type,
158 struct lp_type dst_type)
159 {
160 lp_context_ref context;
161 struct gallivm_state *gallivm;
162 LLVMValueRef func = NULL;
163 conv_test_ptr_t conv_test_ptr;
164 bool success;
165 const unsigned n = LP_TEST_NUM_SAMPLES;
166 int64_t cycles[LP_TEST_NUM_SAMPLES];
167 double cycles_avg = 0.0;
168 unsigned num_srcs;
169 unsigned num_dsts;
170 double eps;
171 unsigned i, j;
172
173 if ((src_type.width >= dst_type.width && src_type.length > dst_type.length) ||
174 (src_type.width <= dst_type.width && src_type.length < dst_type.length)) {
175 return true;
176 }
177
178 /* Known failures
179 * - fixed point 32 -> float 32
180 * - float 32 -> signed normalized integer 32
181 */
182 if ((src_type.floating && !dst_type.floating && dst_type.sign && dst_type.norm && src_type.width == dst_type.width) ||
183 (!src_type.floating && dst_type.floating && src_type.fixed && src_type.width == dst_type.width)) {
184 return true;
185 }
186
187 /* Known failures
188 * - fixed point 32 -> float 32
189 * - float 32 -> signed normalized integer 32
190 */
191 if ((src_type.floating && !dst_type.floating && dst_type.sign && dst_type.norm && src_type.width == dst_type.width) ||
192 (!src_type.floating && dst_type.floating && src_type.fixed && src_type.width == dst_type.width)) {
193 return true;
194 }
195
196 if (verbose >= 1)
197 dump_conv_types(stderr, src_type, dst_type);
198
199 if (src_type.length > dst_type.length) {
200 num_srcs = 1;
201 num_dsts = src_type.length/dst_type.length;
202 }
203 else if (src_type.length < dst_type.length) {
204 num_dsts = 1;
205 num_srcs = dst_type.length/src_type.length;
206 }
207 else {
208 num_dsts = 1;
209 num_srcs = 1;
210 }
211
212 /* We must not loose or gain channels. Only precision */
213 assert(src_type.length * num_srcs == dst_type.length * num_dsts);
214
215 eps = MAX2(lp_const_eps(src_type), lp_const_eps(dst_type));
216 if (dst_type.norm && dst_type.sign && src_type.sign && !src_type.floating) {
217 /*
218 * This is quite inaccurate due to shift being used.
219 * I don't think it's possible to hit such conversions with
220 * llvmpipe though.
221 */
222 eps *= 2;
223 }
224
225 lp_context_create(&context);
226 gallivm = gallivm_create("test_module", &context, NULL);
227
228 func = add_conv_test(gallivm, src_type, num_srcs, dst_type, num_dsts);
229
230 gallivm_compile_module(gallivm);
231
232 conv_test_ptr = (conv_test_ptr_t)gallivm_jit_function(gallivm, func, "test");
233
234 gallivm_free_ir(gallivm);
235
236 success = true;
237 for (i = 0; i < n && success; ++i) {
238 unsigned src_stride = src_type.length*src_type.width/8;
239 unsigned dst_stride = dst_type.length*dst_type.width/8;
240 alignas(LP_MIN_VECTOR_ALIGN) uint8_t src[LP_MAX_VECTOR_LENGTH*LP_MAX_VECTOR_LENGTH];
241 alignas(LP_MIN_VECTOR_ALIGN) uint8_t dst[LP_MAX_VECTOR_LENGTH*LP_MAX_VECTOR_LENGTH];
242 double fref[LP_MAX_VECTOR_LENGTH*LP_MAX_VECTOR_LENGTH];
243 uint8_t ref[LP_MAX_VECTOR_LENGTH*LP_MAX_VECTOR_LENGTH];
244 int64_t start_counter = 0;
245 int64_t end_counter = 0;
246
247 for (j = 0; j < num_srcs; ++j) {
248 random_vec(src_type, src + j*src_stride);
249 read_vec(src_type, src + j*src_stride, fref + j*src_type.length);
250 }
251
252 for (j = 0; j < num_dsts; ++j) {
253 write_vec(dst_type, ref + j*dst_stride, fref + j*dst_type.length);
254 }
255
256 start_counter = rdtsc();
257 conv_test_ptr(src, dst);
258 end_counter = rdtsc();
259
260 cycles[i] = end_counter - start_counter;
261
262 for (j = 0; j < num_dsts; ++j) {
263 if (!compare_vec_with_eps(dst_type, dst + j*dst_stride, ref + j*dst_stride, eps))
264 success = false;
265 }
266
267 if (!success || verbose >= 3) {
268 if (verbose < 1)
269 dump_conv_types(stderr, src_type, dst_type);
270 if (success) {
271 fprintf(stderr, "PASS\n");
272 }
273 else {
274 fprintf(stderr, "MISMATCH\n");
275 }
276
277 for (j = 0; j < num_srcs; ++j) {
278 fprintf(stderr, " Src%u: ", j);
279 dump_vec(stderr, src_type, src + j*src_stride);
280 fprintf(stderr, "\n");
281 }
282
283 #if 1
284 fprintf(stderr, " Ref: ");
285 for (j = 0; j < src_type.length*num_srcs; ++j)
286 fprintf(stderr, " %f", fref[j]);
287 fprintf(stderr, "\n");
288 #endif
289
290 for (j = 0; j < num_dsts; ++j) {
291 fprintf(stderr, " Dst%u: ", j);
292 dump_vec(stderr, dst_type, dst + j*dst_stride);
293 fprintf(stderr, "\n");
294
295 fprintf(stderr, " Ref%u: ", j);
296 dump_vec(stderr, dst_type, ref + j*dst_stride);
297 fprintf(stderr, "\n");
298 }
299 }
300 }
301
302 /*
303 * Unfortunately the output of cycle counter is not very reliable as it comes
304 * -- sometimes we get outliers (due IRQs perhaps?) which are
305 * better removed to avoid random or biased data.
306 */
307 {
308 double sum = 0.0, sum2 = 0.0;
309 double avg, std;
310 unsigned m;
311
312 for (i = 0; i < n; ++i) {
313 sum += cycles[i];
314 sum2 += cycles[i]*cycles[i];
315 }
316
317 avg = sum/n;
318 std = sqrtf((sum2 - n*avg*avg)/n);
319
320 m = 0;
321 sum = 0.0;
322 for (i = 0; i < n; ++i) {
323 if (fabs(cycles[i] - avg) <= 4.0*std) {
324 sum += cycles[i];
325 ++m;
326 }
327 }
328
329 cycles_avg = sum/m;
330
331 }
332
333 if (fp)
334 write_tsv_row(fp, src_type, dst_type, cycles_avg, success);
335
336 gallivm_destroy(gallivm);
337 lp_context_destroy(&context);
338
339 return success;
340 }
341
342
343 const struct lp_type conv_types[] = {
344 /* float, fixed, sign, norm, width, len */
345
346 /* Float */
347 { true, false, true, true, 32, 4 },
348 { true, false, true, false, 32, 4 },
349 { true, false, false, true, 32, 4 },
350 { true, false, false, false, 32, 4 },
351
352 { true, false, true, true, 32, 8 },
353 { true, false, true, false, 32, 8 },
354 { true, false, false, true, 32, 8 },
355 { true, false, false, false, 32, 8 },
356
357 /* Fixed */
358 { false, true, true, true, 32, 4 },
359 { false, true, true, false, 32, 4 },
360 { false, true, false, true, 32, 4 },
361 { false, true, false, false, 32, 4 },
362
363 { false, true, true, true, 32, 8 },
364 { false, true, true, false, 32, 8 },
365 { false, true, false, true, 32, 8 },
366 { false, true, false, false, 32, 8 },
367
368 /* Integer */
369 { false, false, true, true, 32, 4 },
370 { false, false, true, false, 32, 4 },
371 { false, false, false, true, 32, 4 },
372 { false, false, false, false, 32, 4 },
373
374 { false, false, true, true, 32, 8 },
375 { false, false, true, false, 32, 8 },
376 { false, false, false, true, 32, 8 },
377 { false, false, false, false, 32, 8 },
378
379 { false, false, true, true, 16, 8 },
380 { false, false, true, false, 16, 8 },
381 { false, false, false, true, 16, 8 },
382 { false, false, false, false, 16, 8 },
383
384 { false, false, true, true, 8, 16 },
385 { false, false, true, false, 8, 16 },
386 { false, false, false, true, 8, 16 },
387 { false, false, false, false, 8, 16 },
388
389 { false, false, true, true, 8, 4 },
390 { false, false, true, false, 8, 4 },
391 { false, false, false, true, 8, 4 },
392 { false, false, false, false, 8, 4 },
393
394 { false, false, false, true, 8, 8 },
395 };
396
397
398 const unsigned num_types = ARRAY_SIZE(conv_types);
399
400
401 bool
test_all(unsigned verbose,FILE * fp)402 test_all(unsigned verbose, FILE *fp)
403 {
404 const struct lp_type *src_type;
405 const struct lp_type *dst_type;
406 bool success = true;
407 int error_count = 0;
408
409 for (src_type = conv_types; src_type < &conv_types[num_types]; ++src_type) {
410 for (dst_type = conv_types; dst_type < &conv_types[num_types]; ++dst_type) {
411
412 if (src_type == dst_type)
413 continue;
414
415 if (!test_one(verbose, fp, *src_type, *dst_type)){
416 success = false;
417 ++error_count;
418 }
419 }
420 }
421
422 fprintf(stderr, "%d failures\n", error_count);
423
424 return success;
425 }
426
427
428 bool
test_some(unsigned verbose,FILE * fp,unsigned long n)429 test_some(unsigned verbose, FILE *fp,
430 unsigned long n)
431 {
432 const struct lp_type *src_type;
433 const struct lp_type *dst_type;
434 unsigned long i;
435 bool success = true;
436
437 for (i = 0; i < n; ++i) {
438 src_type = &conv_types[rand() % num_types];
439
440 do {
441 dst_type = &conv_types[rand() % num_types];
442 } while (src_type == dst_type || src_type->norm != dst_type->norm);
443
444 if (!test_one(verbose, fp, *src_type, *dst_type))
445 success = false;
446 }
447
448 return success;
449 }
450
451
452 bool
test_single(unsigned verbose,FILE * fp)453 test_single(unsigned verbose, FILE *fp)
454 {
455 /* float, fixed, sign, norm, width, len */
456 struct lp_type f32x4_type =
457 { true, false, true, true, 32, 4 };
458 struct lp_type ub8x4_type =
459 { false, false, false, true, 8, 16 };
460
461 bool success;
462
463 success = test_one(verbose, fp, f32x4_type, ub8x4_type);
464
465 return success;
466 }
467