Update tizen 2.0 beta source
[profile/ivi/liboil.git] / liboil / liboiltest.c
1 /*
2  * LIBOIL - Library of Optimized Inner Loops
3  * Copyright (c) 2003,2004 David A. Schleef <ds@schleef.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
19  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
24  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  * POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31
32 #include <liboil/liboiltest.h>
33 #include <liboil/liboildebug.h>
34 #include <liboil/liboilrandom.h>
35 #include <liboil/liboilprofile.h>
36 #include <liboil/liboilfault.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <stdio.h>
40 #include <math.h>
41
42 #define MAX_PARAMS 20
43
44 /**
45  * SECTION:liboiltest
46  * @title:OilTest
47  * @short_description: Test and profile function implementations.
48  *
49  */
50 static void oil_test_init_params (OilTest *test);
51 static void fill_array (void *ptr, OilType type, int pre_n, int stride,
52     int post_n);
53 static double check_array (void *data, void *ref, OilType type, int pre_n,
54     int stride, int post_n);
55 static int check_holes (void *data, OilType type, int pre_n,
56     int stride, int post_n, int guard);
57
58 /**
59  * oil_test_new:
60  * @klass: an OilFunctionClass
61  *
62  * Creates a new OilTest for the OilFunctionClass represented by @klass.
63  *
64  * Returns: the new OilTest
65  */
66 OilTest *
67 oil_test_new (OilFunctionClass *klass)
68 {
69   OilTest *test;
70   OilPrototype *proto;
71   int i;
72
73   if (klass == NULL) return NULL;
74
75   proto = oil_prototype_from_string (klass->prototype);
76   if (proto == NULL) return NULL;
77
78   test = malloc (sizeof (OilTest));
79   memset (test, 0, sizeof (OilTest));
80
81   test->klass = klass;
82   test->proto = proto;
83   test->impl = klass->reference_impl;
84   test->tolerance = 0.0;
85
86   for (i=0;i<proto->n_params;i++){
87     if (proto->params[i].parameter_type == OIL_ARG_UNKNOWN) {
88       return NULL;
89     }
90     if (oil_type_is_floating_point(proto->params[i].type)) {
91       test->tolerance = 0.001;
92     }
93     memcpy (&test->params[proto->params[i].parameter_type], &proto->params[i],
94         sizeof(OilParameter));
95   }
96   for (i=0;i<OIL_ARG_LAST;i++){
97     test->params[i].src_data = NULL;
98     test->params[i].ref_data = NULL;
99     test->params[i].test_data = NULL;
100     test->params[i].test_header = OIL_TEST_HEADER;
101     test->params[i].test_footer = OIL_TEST_FOOTER;
102   }
103
104   test->iterations = 10;
105   test->n = 100;
106   test->m = 100;
107
108   return test;
109 }
110
111 /**
112  * oil_test_free:
113  * @test: the OilTest
114  *
115  * Frees memory associated with @test.
116  */
117 void
118 oil_test_free (OilTest *test)
119 {
120   int i;
121
122   if (test->proto) {
123     oil_prototype_free (test->proto);
124   }
125
126   for(i=0;i<OIL_ARG_LAST;i++){
127     if (test->params[i].src_data) {
128       free (test->params[i].src_data);
129     }
130     if (test->params[i].ref_data) {
131       free (test->params[i].ref_data);
132     }
133     if (test->params[i].test_data) {
134       free (test->params[i].test_data);
135     }
136   }
137
138   free(test);
139 }
140
141 /**
142  * oil_test_set_impl:
143  * @test: the OilTest
144  * @impl: an OilFunctionImpl to set
145  *
146  * Sets the current implementation of @test to @impl.
147  */
148 void
149 oil_test_set_impl (OilTest *test, OilFunctionImpl *impl)
150 {
151   test->impl = impl;
152 }
153
154 /**
155  * oil_test_set_iterations:
156  * @test: the OilTest
157  * @iterations: the number of iterations
158  *
159  * Sets the number of iterations of @test to @iterations.
160  */
161 void
162 oil_test_set_iterations (OilTest *test, int iterations)
163 {
164   test->iterations = iterations;
165 }
166
167 /**
168  * oil_test_set_test_header:
169  * @test: the OilTest
170  * @p: the OilParameter to change the header for
171  * @test_header: the number of bytes of guard header
172  *
173  * Sets the number of bytes of guard header for @p to @test_header.
174  */
175 void
176 oil_test_set_test_header (OilTest *test, OilParameter *p, int test_header)
177 {
178   p->test_header = test_header;
179 }
180
181 /**
182  * oil_test_set_test_footer:
183  * @test: the OilTest
184  * @p: the OilParameter to change the footer for
185  * @test_footer: the number of bytes of guard footer
186  *
187  * Sets the number of bytes of guard footer for @p to @test_footer.
188  */
189 void
190 oil_test_set_test_footer (OilTest *test, OilParameter *p, int test_footer)
191 {
192   p->test_footer = test_footer;
193 }
194
195 /**
196  * oil_test_init:
197  * @test: the OilTest
198  *
199  * Intializes @test.
200  * 
201  * FIXME: needs work
202  */
203 void
204 oil_test_init (OilTest *test)
205 {
206   if (test->inited) {
207     return;
208   }
209
210   oil_test_init_params(test);
211
212   test->params[OIL_ARG_N].value = test->n;
213   
214   test->inited = 1;
215
216   if (test->klass->test_func) {
217     test->klass->test_func (test);
218   }
219 }
220
221 static void
222 oil_test_check_function (void * priv)
223 {
224   OilTest *test = priv;
225   int i;
226   int j;
227   unsigned long args[MAX_PARAMS];
228   unsigned int pointer_mask;
229
230   oil_test_init (test);
231
232   OIL_LOG("calling function %s", test->impl->name);
233
234   pointer_mask = 1;
235   for(i=0;i<test->proto->n_params;i++){
236     OilParameter *p;
237     j = test->proto->params[i].parameter_type;
238     p = &test->params[j];
239
240     pointer_mask <<= 1;
241     OIL_LOG("  %s: 0x%08lx (%ld)", oil_arg_type_name (j), p->value, p->value);
242     if (p->is_pointer) {
243       pointer_mask |= 1;
244       if (p->direction == 's') {
245         args[i] = (unsigned long)p->src_data + p->test_header;
246       } else if (p->direction == 'i') {
247         memcpy (p->test_data, p->src_data, p->size);
248         args[i] = (unsigned long)p->test_data + p->test_header;
249       } else if (p->direction == 'd') {
250         memset (p->test_data, p->guard, p->size);
251         args[i] = (unsigned long)p->test_data + p->test_header;
252       } else {
253         OIL_ERROR ("not reached");
254       }
255     } else {
256       args[i] = p->value;
257     }
258   }
259
260   oil_profile_init (&test->prof);
261   for(i=0;i<test->iterations;i++){
262     int k;
263
264     for(k=0;k<test->proto->n_params;k++){
265       OilParameter *p;
266       j = test->proto->params[k].parameter_type;
267       p = &test->params[j];
268       if (p->direction == 'i') {
269         memcpy (p->test_data, p->src_data, p->size);
270       }
271     }
272     _oil_test_marshal_function (test->impl->func, args, test->proto->n_params,
273         pointer_mask, &test->prof);
274   }
275
276   oil_profile_get_ave_std (&test->prof, &test->profile_ave,
277       &test->profile_std);
278 }
279
280 /**
281  * oil_test_check_ref:
282  * @test: the OilTest
283  *
284  * Runs the test specified by @test on the reference function of the
285  * class being tested.
286  */
287 void
288 oil_test_check_ref (OilTest *test)
289 {
290   int i;
291
292   if (test->proto->n_params > MAX_PARAMS) {
293     OIL_ERROR ("function class %s has too many parameters",
294         test->klass->name);
295     return;
296   }
297   if (test->klass->reference_impl == NULL) {
298     OIL_ERROR ("function class %s has no reference implementation",
299         test->klass->name);
300     return;
301   }
302
303   test->impl = test->klass->reference_impl;
304
305   oil_test_check_function (test);
306
307   for(i=0;i<OIL_ARG_LAST;i++){
308     OilParameter *p = &test->params[i];
309
310     if (p->is_pointer) {
311       if (p->direction == 'i' || p->direction == 'd') {
312         memcpy (p->ref_data, p->test_data, p->size);
313       }
314     }
315   }
316
317   test->tested_ref = 1;
318 }
319
320 static int
321 check_guard (uint8_t *data, int n, int guard)
322 {
323   int i;
324   for(i=0;i<n;i++) {
325     if (data[i] != guard) return 0;
326   }
327   return 1;
328 }
329
330 /**
331  * oil_test_check_impl:
332  * @test: the OilTest
333  * @impl: an OilFunctionImpl
334  *
335  * Runs the testing procedure described by @test on the implementation
336  * @impl.
337  *
338  * Returns: 1 if @impl passes the test, 0 if it fails
339  */
340 int
341 oil_test_check_impl (OilTest *test, OilFunctionImpl *impl)
342 {
343   double x;
344   int i;
345   int n;
346   int fail = 0;
347   int ret;
348
349   if (test->proto->n_params > MAX_PARAMS) {
350     OIL_ERROR ("function has too many parameters");
351     return 0;
352   }
353
354   if (!test->inited || !test->tested_ref) {
355     oil_test_check_ref(test);
356   }
357
358   test->impl = impl;
359   ret = oil_fault_check_try (oil_test_check_function, test);
360   if (!ret) {
361     OIL_ERROR ("illegal instruction in %s", test->impl->name);
362     test->profile_ave = 0;
363     test->profile_std = 0;
364
365     return 0;
366   }
367
368   x = 0;
369   n = 0;
370   for(i=0;i<OIL_ARG_LAST;i++){
371     OilParameter *p = &test->params[i];
372
373     if (p->is_pointer) {
374       if (p->direction == 'i' || p->direction == 'd') {
375         x += check_array (p->test_data + p->test_header,
376             p->ref_data + p->test_header, p->type, p->pre_n, p->stride,
377             p->post_n);
378         n += p->pre_n * p->post_n;
379         if (!check_guard (p->test_data, p->test_header, p->guard)) {
380           fail = 1;
381           OIL_ERROR("function %s wrote before area for parameter %s",
382               test->impl->name, p->parameter_name);
383         }
384         if (!check_guard ((uint8_t *)p->test_data + p->size - p->test_footer,
385               p->test_footer, p->guard)) {
386           fail = 1;
387           OIL_ERROR("function %s wrote after area for parameter %s",
388               test->impl->name, p->parameter_name);
389         }
390         if (!check_holes (p->test_data, p->type, p->pre_n, p->stride,
391               p->post_n, p->guard)) {
392           fail = 1;
393           OIL_ERROR("function %s wrote in interstitial area for parameter %s",
394               test->impl->name, p->parameter_name);
395         }
396       }
397     }
398   }
399   OIL_DEBUG("sum of absolute differences %g for %d values", x, n);
400   test->sum_abs_diff = x;
401   test->n_points = n;
402
403   if (x > test->tolerance * n || fail) {
404     OIL_ERROR ("function %s in class %s failed check (%g > %g) || (outside=%d)",
405         test->impl->name, test->klass->name, x, test->tolerance * n, fail);
406     return 0;
407   }
408
409   return 1;
410 }
411
412 /**
413  * oil_test_cleanup
414  * @test: the OilTest
415  *
416  * Cleans up @test.
417  *
418  * FIXME: needs work
419  */
420 void
421 oil_test_cleanup (OilTest *test)
422 {
423   OilParameter *params = test->params;
424
425   /* src1 */
426   if(params[OIL_ARG_SRC1].type) {
427     if (!params[OIL_ARG_SSTR1].type) {
428       params[OIL_ARG_SSTR1].value = oil_type_sizeof (params[OIL_ARG_SRC1].type);
429     }
430   }
431
432   /* src2 */
433   if(params[OIL_ARG_SRC2].type) {
434     if (!params[OIL_ARG_SSTR2].type) {
435       params[OIL_ARG_SSTR2].value = oil_type_sizeof (params[OIL_ARG_SRC2].type);
436     }
437   }
438
439   /* src3 */
440   if(params[OIL_ARG_SRC3].type) {
441     if (!params[OIL_ARG_SSTR3].type) {
442       params[OIL_ARG_SSTR3].value = oil_type_sizeof (params[OIL_ARG_SRC3].type);
443     }
444   }
445
446   /* dest1 */
447   if(params[OIL_ARG_DEST1].type) {
448     if (!params[OIL_ARG_DSTR1].type) {
449       params[OIL_ARG_DSTR1].value = oil_type_sizeof (params[OIL_ARG_DEST1].type);
450     }
451   }
452
453   /* dest2 */
454   if(params[OIL_ARG_DEST2].type) {
455     if (!params[OIL_ARG_DSTR2].type) {
456       params[OIL_ARG_DSTR2].value = oil_type_sizeof (params[OIL_ARG_DEST2].type);
457     }
458   }
459
460   /* dest3 */
461   if(params[OIL_ARG_DEST3].type) {
462     if (!params[OIL_ARG_DSTR3].type) {
463       params[OIL_ARG_DSTR3].value = oil_type_sizeof (params[OIL_ARG_DEST3].type);
464     }
465   }
466
467 }
468
469
470 static void
471 init_parameter (OilTest *test, OilParameter *p, OilParameter *ps)
472 {
473   if (!p->type) return;
474
475   p->pre_n = p->prestride_length;
476   if (p->prestride_var == 1) {
477     p->pre_n += test->n;
478   }
479   if (p->prestride_var == 2) {
480     p->pre_n += test->m;
481   }
482
483   if (ps->value) {
484     p->stride = ps->value;
485   } else {
486     p->stride = oil_type_sizeof (p->type) * p->pre_n;
487     ps->value = p->stride;
488   }
489
490   p->post_n = p->poststride_length;
491   if (p->poststride_var == 1) {
492     p->post_n += test->n;
493   }
494   if (p->poststride_var == 2) {
495     p->post_n += test->m;
496   }
497
498   p->size = p->stride * p->post_n + p->test_header + p->test_footer;
499   p->guard = oil_rand_u8();
500
501   if (p->direction == 'i' || p->direction == 's') {
502     if (p->src_data) free (p->src_data);
503
504     OIL_DEBUG("allocating %d bytes for src_data for %s", p->size, p->parameter_name);
505     p->src_data = malloc (p->size);
506     memset (p->src_data, p->guard, p->size);
507     fill_array (p->src_data + p->test_header, p->type, p->pre_n, p->stride, p->post_n);
508   }
509
510   if (p->direction == 'i' || p->direction == 'd') {
511     if (p->ref_data) free (p->ref_data);
512     p->ref_data = malloc (p->size);
513     memset (p->ref_data, p->guard, p->size);
514     OIL_DEBUG("allocating %d bytes for ref_data and test_data for %s", p->size, p->parameter_name);
515
516     if (p->test_data) free (p->test_data);
517     p->test_data = malloc (p->size);
518     memset (p->test_data, p->guard, p->size);
519   }
520 }
521
522 static void
523 oil_test_init_params (OilTest *test)
524 {
525   init_parameter (test, &test->params[OIL_ARG_DEST1],
526       &test->params[OIL_ARG_DSTR1]);
527   init_parameter (test, &test->params[OIL_ARG_DEST2],
528       &test->params[OIL_ARG_DSTR2]);
529   init_parameter (test, &test->params[OIL_ARG_DEST3],
530       &test->params[OIL_ARG_DSTR3]);
531
532   init_parameter (test, &test->params[OIL_ARG_SRC1],
533       &test->params[OIL_ARG_SSTR1]);
534   init_parameter (test, &test->params[OIL_ARG_SRC2],
535       &test->params[OIL_ARG_SSTR2]);
536   init_parameter (test, &test->params[OIL_ARG_SRC3],
537       &test->params[OIL_ARG_SSTR3]);
538   init_parameter (test, &test->params[OIL_ARG_SRC4],
539       &test->params[OIL_ARG_SSTR4]);
540   init_parameter (test, &test->params[OIL_ARG_SRC5],
541       &test->params[OIL_ARG_SSTR5]);
542
543   init_parameter (test, &test->params[OIL_ARG_INPLACE1],
544       &test->params[OIL_ARG_ISTR1]);
545   init_parameter (test, &test->params[OIL_ARG_INPLACE2],
546       &test->params[OIL_ARG_ISTR2]);
547 }
548
549 static void
550 fill_array (void *data, OilType type, int pre_n, int stride, int post_n)
551 {
552   int i;
553
554 #define FILL(type,func) do {\
555   for(i=0;i<post_n;i++){ \
556     func (OIL_OFFSET(data, i*stride), pre_n); \
557   } \
558 }while(0)
559
560   switch (type) {
561     case OIL_TYPE_s8p:
562       FILL(int8_t,oil_random_s8);
563       break;
564     case OIL_TYPE_u8p:
565       FILL(uint8_t,oil_random_u8);
566       break;
567     case OIL_TYPE_s16p:
568       FILL(int16_t,oil_random_s16);
569       break;
570     case OIL_TYPE_u16p:
571       FILL(uint16_t,oil_random_u16);
572       break;
573     case OIL_TYPE_s32p:
574       FILL(int32_t,oil_random_s32);
575       break;
576     case OIL_TYPE_u32p:
577       FILL(uint32_t,oil_random_u32);
578       break;
579     case OIL_TYPE_s64p:
580       FILL(int64_t,oil_random_s64);
581       break;
582     case OIL_TYPE_u64p:
583       FILL(uint64_t,oil_random_u64);
584       break;
585     case OIL_TYPE_f32p:
586       FILL(float,oil_random_f32);
587       break;
588     case OIL_TYPE_f64p:
589       FILL(double,oil_random_f64);
590       break;
591     default:
592       OIL_ERROR ("should not be reached (type == %d)", type);
593       return;
594       break;
595   }
596 }
597
598 static double
599 check_array (void *data, void *ref, OilType type, int pre_n, int stride, int post_n)
600 {
601   int i;
602   int j;
603   int s2 = oil_type_sizeof (type);
604   double x = 0;
605
606 #if 0
607   OIL_ERROR ("check array pre_n=%d stride=%d post_n=%d",
608       pre_n, stride, post_n);
609 #endif
610
611 #define CHECK(type) do {\
612   for(i=0;i<post_n;i++){ \
613     for(j=0;j<pre_n;j++){ \
614       x += fabs((double)OIL_GET(data, i*stride + j*s2, type) - \
615           (double)OIL_GET(ref, i*stride + j*s2, type)); \
616     } \
617   } \
618 }while(0)
619
620   switch (type) {
621     case OIL_TYPE_s8p:
622       CHECK(int8_t);
623       break;
624     case OIL_TYPE_u8p:
625       CHECK(uint8_t);
626       break;
627     case OIL_TYPE_s16p:
628       CHECK(int16_t);
629       break;
630     case OIL_TYPE_u16p:
631       CHECK(uint16_t);
632       break;
633     case OIL_TYPE_s32p:
634       CHECK(int32_t);
635       break;
636     case OIL_TYPE_u32p:
637       CHECK(uint32_t);
638       break;
639     case OIL_TYPE_s64p:
640       CHECK(int64_t);
641       break;
642     case OIL_TYPE_u64p:
643       CHECK(uint64_t);
644       break;
645     case OIL_TYPE_f32p:
646       CHECK(float);
647       break;
648     case OIL_TYPE_f64p:
649       CHECK(double);
650       break;
651     default:
652       OIL_ERROR ("should not be reached (type == %d)", type);
653       return 1e9;
654       break;
655   }
656   return x;
657 }
658
659 static int
660 check_holes (void *data, OilType type, int pre_n, int stride, int post_n,
661     int guard)
662 {
663   int i;
664   int chunk_size;
665   int hole_size;
666
667   chunk_size = pre_n * oil_type_sizeof (type);
668   hole_size = stride - chunk_size;
669   if (hole_size == 0) {
670     return 1;
671   }
672
673   for(i=0;i<post_n;i++){
674     if (!check_guard (OIL_OFFSET(data, stride * i + chunk_size),
675         hole_size, guard)) {
676       return 0;
677     }
678   }
679
680   return 1;
681 }
682
683 void *
684 oil_test_get_source_data (OilTest *test, OilArgType arg_type)
685 {
686   uint8_t *ptr;
687  
688   ptr = test->params[arg_type].src_data;
689   ptr += test->params[arg_type].test_header;
690
691   return ptr;
692 }
693
694 int
695 oil_test_get_arg_pre_n (OilTest *test, OilArgType arg_type)
696 {
697   return test->params[arg_type].pre_n;
698 }
699
700 int
701 oil_test_get_arg_post_n (OilTest *test, OilArgType arg_type)
702 {
703   return test->params[arg_type].post_n;
704 }
705
706 int
707 oil_test_get_arg_stride (OilTest *test, OilArgType arg_type)
708 {
709   return test->params[arg_type].stride;
710 }
711
712 int
713 oil_test_get_value (OilTest *test, OilArgType arg_type)
714 {
715   return test->params[arg_type].value;
716 }