2 * LIBOIL - Library of Optimized Inner Loops
3 * Copyright (c) 2003,2004 David A. Schleef <ds@schleef.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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.
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>
47 * @short_description: Test and profile function implementations.
50 static void oil_test_init_params (OilTest *test);
51 static void fill_array (void *ptr, OilType type, int pre_n, int stride,
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);
60 * @klass: an OilFunctionClass
62 * Creates a new OilTest for the OilFunctionClass represented by @klass.
64 * Returns: the new OilTest
67 oil_test_new (OilFunctionClass *klass)
73 if (klass == NULL) return NULL;
75 proto = oil_prototype_from_string (klass->prototype);
76 if (proto == NULL) return NULL;
78 test = malloc (sizeof (OilTest));
79 memset (test, 0, sizeof (OilTest));
83 test->impl = klass->reference_impl;
84 test->tolerance = 0.0;
86 for (i=0;i<proto->n_params;i++){
87 if (proto->params[i].parameter_type == OIL_ARG_UNKNOWN) {
90 if (oil_type_is_floating_point(proto->params[i].type)) {
91 test->tolerance = 0.001;
93 memcpy (&test->params[proto->params[i].parameter_type], &proto->params[i],
94 sizeof(OilParameter));
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;
104 test->iterations = 10;
115 * Frees memory associated with @test.
118 oil_test_free (OilTest *test)
123 oil_prototype_free (test->proto);
126 for(i=0;i<OIL_ARG_LAST;i++){
127 if (test->params[i].src_data) {
128 free (test->params[i].src_data);
130 if (test->params[i].ref_data) {
131 free (test->params[i].ref_data);
133 if (test->params[i].test_data) {
134 free (test->params[i].test_data);
144 * @impl: an OilFunctionImpl to set
146 * Sets the current implementation of @test to @impl.
149 oil_test_set_impl (OilTest *test, OilFunctionImpl *impl)
155 * oil_test_set_iterations:
157 * @iterations: the number of iterations
159 * Sets the number of iterations of @test to @iterations.
162 oil_test_set_iterations (OilTest *test, int iterations)
164 test->iterations = iterations;
168 * oil_test_set_test_header:
170 * @p: the OilParameter to change the header for
171 * @test_header: the number of bytes of guard header
173 * Sets the number of bytes of guard header for @p to @test_header.
176 oil_test_set_test_header (OilTest *test, OilParameter *p, int test_header)
178 p->test_header = test_header;
182 * oil_test_set_test_footer:
184 * @p: the OilParameter to change the footer for
185 * @test_footer: the number of bytes of guard footer
187 * Sets the number of bytes of guard footer for @p to @test_footer.
190 oil_test_set_test_footer (OilTest *test, OilParameter *p, int test_footer)
192 p->test_footer = test_footer;
204 oil_test_init (OilTest *test)
210 oil_test_init_params(test);
212 test->params[OIL_ARG_N].value = test->n;
216 if (test->klass->test_func) {
217 test->klass->test_func (test);
222 oil_test_check_function (void * priv)
224 OilTest *test = priv;
227 unsigned long args[MAX_PARAMS];
228 unsigned int pointer_mask;
230 oil_test_init (test);
232 OIL_LOG("calling function %s", test->impl->name);
235 for(i=0;i<test->proto->n_params;i++){
237 j = test->proto->params[i].parameter_type;
238 p = &test->params[j];
241 OIL_LOG(" %s: 0x%08lx (%ld)", oil_arg_type_name (j), p->value, p->value);
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;
253 OIL_ERROR ("not reached");
260 oil_profile_init (&test->prof);
261 for(i=0;i<test->iterations;i++){
264 for(k=0;k<test->proto->n_params;k++){
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);
272 _oil_test_marshal_function (test->impl->func, args, test->proto->n_params,
273 pointer_mask, &test->prof);
276 oil_profile_get_ave_std (&test->prof, &test->profile_ave,
281 * oil_test_check_ref:
284 * Runs the test specified by @test on the reference function of the
285 * class being tested.
288 oil_test_check_ref (OilTest *test)
292 if (test->proto->n_params > MAX_PARAMS) {
293 OIL_ERROR ("function class %s has too many parameters",
297 if (test->klass->reference_impl == NULL) {
298 OIL_ERROR ("function class %s has no reference implementation",
303 test->impl = test->klass->reference_impl;
305 oil_test_check_function (test);
307 for(i=0;i<OIL_ARG_LAST;i++){
308 OilParameter *p = &test->params[i];
311 if (p->direction == 'i' || p->direction == 'd') {
312 memcpy (p->ref_data, p->test_data, p->size);
317 test->tested_ref = 1;
321 check_guard (uint8_t *data, int n, int guard)
325 if (data[i] != guard) return 0;
331 * oil_test_check_impl:
333 * @impl: an OilFunctionImpl
335 * Runs the testing procedure described by @test on the implementation
338 * Returns: 1 if @impl passes the test, 0 if it fails
341 oil_test_check_impl (OilTest *test, OilFunctionImpl *impl)
349 if (test->proto->n_params > MAX_PARAMS) {
350 OIL_ERROR ("function has too many parameters");
354 if (!test->inited || !test->tested_ref) {
355 oil_test_check_ref(test);
359 ret = oil_fault_check_try (oil_test_check_function, test);
361 OIL_ERROR ("illegal instruction in %s", test->impl->name);
362 test->profile_ave = 0;
363 test->profile_std = 0;
370 for(i=0;i<OIL_ARG_LAST;i++){
371 OilParameter *p = &test->params[i];
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,
378 n += p->pre_n * p->post_n;
379 if (!check_guard (p->test_data, p->test_header, p->guard)) {
381 OIL_ERROR("function %s wrote before area for parameter %s",
382 test->impl->name, p->parameter_name);
384 if (!check_guard ((uint8_t *)p->test_data + p->size - p->test_footer,
385 p->test_footer, p->guard)) {
387 OIL_ERROR("function %s wrote after area for parameter %s",
388 test->impl->name, p->parameter_name);
390 if (!check_holes (p->test_data, p->type, p->pre_n, p->stride,
391 p->post_n, p->guard)) {
393 OIL_ERROR("function %s wrote in interstitial area for parameter %s",
394 test->impl->name, p->parameter_name);
399 OIL_DEBUG("sum of absolute differences %g for %d values", x, n);
400 test->sum_abs_diff = x;
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);
421 oil_test_cleanup (OilTest *test)
423 OilParameter *params = test->params;
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);
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);
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);
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);
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);
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);
471 init_parameter (OilTest *test, OilParameter *p, OilParameter *ps)
473 if (!p->type) return;
475 p->pre_n = p->prestride_length;
476 if (p->prestride_var == 1) {
479 if (p->prestride_var == 2) {
484 p->stride = ps->value;
486 p->stride = oil_type_sizeof (p->type) * p->pre_n;
487 ps->value = p->stride;
490 p->post_n = p->poststride_length;
491 if (p->poststride_var == 1) {
492 p->post_n += test->n;
494 if (p->poststride_var == 2) {
495 p->post_n += test->m;
498 p->size = p->stride * p->post_n + p->test_header + p->test_footer;
499 p->guard = oil_rand_u8();
501 if (p->direction == 'i' || p->direction == 's') {
502 if (p->src_data) free (p->src_data);
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);
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);
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);
523 oil_test_init_params (OilTest *test)
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]);
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]);
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]);
550 fill_array (void *data, OilType type, int pre_n, int stride, int post_n)
554 #define FILL(type,func) do {\
555 for(i=0;i<post_n;i++){ \
556 func (OIL_OFFSET(data, i*stride), pre_n); \
562 FILL(int8_t,oil_random_s8);
565 FILL(uint8_t,oil_random_u8);
568 FILL(int16_t,oil_random_s16);
571 FILL(uint16_t,oil_random_u16);
574 FILL(int32_t,oil_random_s32);
577 FILL(uint32_t,oil_random_u32);
580 FILL(int64_t,oil_random_s64);
583 FILL(uint64_t,oil_random_u64);
586 FILL(float,oil_random_f32);
589 FILL(double,oil_random_f64);
592 OIL_ERROR ("should not be reached (type == %d)", type);
599 check_array (void *data, void *ref, OilType type, int pre_n, int stride, int post_n)
603 int s2 = oil_type_sizeof (type);
607 OIL_ERROR ("check array pre_n=%d stride=%d post_n=%d",
608 pre_n, stride, post_n);
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)); \
652 OIL_ERROR ("should not be reached (type == %d)", type);
660 check_holes (void *data, OilType type, int pre_n, int stride, int post_n,
667 chunk_size = pre_n * oil_type_sizeof (type);
668 hole_size = stride - chunk_size;
669 if (hole_size == 0) {
673 for(i=0;i<post_n;i++){
674 if (!check_guard (OIL_OFFSET(data, stride * i + chunk_size),
684 oil_test_get_source_data (OilTest *test, OilArgType arg_type)
688 ptr = test->params[arg_type].src_data;
689 ptr += test->params[arg_type].test_header;
695 oil_test_get_arg_pre_n (OilTest *test, OilArgType arg_type)
697 return test->params[arg_type].pre_n;
701 oil_test_get_arg_post_n (OilTest *test, OilArgType arg_type)
703 return test->params[arg_type].post_n;
707 oil_test_get_arg_stride (OilTest *test, OilArgType arg_type)
709 return test->params[arg_type].stride;
713 oil_test_get_value (OilTest *test, OilArgType arg_type)
715 return test->params[arg_type].value;