1 /* Functional tests of the htm __TM_... macros.  */
2 
3 /* { dg-do run } */
4 /* { dg-require-effective-target htm } */
5 /* { dg-options "-O3 -march=zEC12 -mzarch" } */
6 
7 /* ---------------------------- included header files ---------------------- */
8 
9 #include <stdio.h>
10 #include <string.h>
11 #include <inttypes.h>
12 #include <htmxlintrin.h>
13 
14 /* ---------------------------- local definitions -------------------------- */
15 
16 #define DEFAULT_MAX_REPETITIONS 7
17 #define DEFAULT_REQUIRED_QUORUM 4
18 #define DEFAULT_ABORT_ADDRESS (0x12345678u)
19 
20 /* ---------------------------- local macros ------------------------------- */
21 
22 #define TEST_DF_REP(name) \
23   { #name, name, DEFAULT_MAX_REPETITIONS, DEFAULT_REQUIRED_QUORUM }
24 #define TEST_NO_REP(name) { #name, name, 1, 1 }
25 
26 /* ---------------------------- local types -------------------------------- */
27 
28 typedef int (*test_func_t)(void);
29 
30 typedef struct
31 {
32   const char *name;
33   test_func_t test_func;
34   int max_repetitions;
35   int required_quorum;
36 } test_table_entry_t;
37 
38 typedef enum
39 {
40   ABORT_T_SYSTEM = 0,
41   ABORT_T_USER = 1,
42 } abort_user_t;
43 
44 typedef enum
45 {
46   ABORT_T_NONE = 0,
47   ABORT_T_ILLEGAL,
48   ABORT_T_FOOTPRINT_EXCEEDED,
49   ABORT_T_NESTED_TOO_DEEP,
50   ABORT_T_CONFLICT,
51 
52   ABORT_T_INVALID_ABORT_CODE
53 } abort_t;
54 
55 /* ---------------------------- local variables ---------------------------- */
56 
57 __attribute__ ((aligned(256))) static struct __htm_tdb local_tdb256;
58 static struct __htm_tdb local_tdb;
59 
60 static abort_t const abort_classes[] =
61 {
62   ABORT_T_INVALID_ABORT_CODE,
63   ABORT_T_NONE,
64   ABORT_T_NONE,
65   ABORT_T_NONE,
66 
67   ABORT_T_ILLEGAL,
68   ABORT_T_NONE,
69   ABORT_T_NONE,
70   ABORT_T_FOOTPRINT_EXCEEDED,
71 
72   ABORT_T_FOOTPRINT_EXCEEDED,
73   ABORT_T_CONFLICT,
74   ABORT_T_CONFLICT,
75   ABORT_T_ILLEGAL,
76 
77   ABORT_T_NONE,
78   ABORT_T_NESTED_TOO_DEEP,
79   ABORT_T_NONE,
80   ABORT_T_NONE,
81 
82   ABORT_T_NONE
83 };
84 
85 static size_t num_abort_classes = sizeof(abort_classes) / sizeof(abort_t);
86 
87 /* ---------------------------- exported variables (globals) --------------- */
88 
89 int global_int = 0;
90 uint64_t global_u64 = 0;
91 float global_float_1 = 1.0;
92 float global_float_2 = 2.5;
93 float global_float_3 = 0.0;
94 __attribute__ ((aligned(256))) struct
95 {
96   volatile uint64_t c1;
97   volatile uint64_t c2;
98   volatile uint64_t c3;
99 } counters = { 0, 0, 0 };
100 
101 /* ---------------------------- local helper functions --------------------- */
102 
dump_tdb(struct __htm_tdb * tdb)103 static void dump_tdb(struct __htm_tdb *tdb)
104 {
105   unsigned char *p;
106   int i;
107   int j;
108 
109   p = (unsigned char *)tdb;
110   for (i = 0; i < 16; i++)
111     {
112       fprintf(stderr, "0x%02x  ", i * 16);
113       for (j = 0; j < 16; j++)
114 	{
115 	  fprintf(stderr, "%02x", (int)p[i * 16 + j]);
116 	  if (j < 15)
117 	    {
118 	      fprintf(stderr, " ");
119 	    }
120 	  if (j == 7)
121 	    {
122 	      fprintf(stderr, " ");
123 	    }
124 	}
125       fprintf(stderr, "\n");
126     }
127 
128   return;
129 }
130 
make_fake_tdb(struct __htm_tdb * tdb)131 static void make_fake_tdb(struct __htm_tdb *tdb)
132 {
133   memset(tdb, 0, sizeof(*tdb));
134   tdb->format = 1;
135   tdb->nesting_depth = 1;
136   tdb->atia = DEFAULT_ABORT_ADDRESS;
137   tdb->abort_code = 11;
138 
139   return;
140 }
141 
check_abort_code_in_tdb(struct __htm_tdb * tdb,uint64_t abort_code)142 static int check_abort_code_in_tdb(struct __htm_tdb *tdb, uint64_t abort_code)
143 {
144   long expect_rc;
145   long rc;
146 
147   if (abort_code != 0)
148     {
149       long addr;
150 
151       addr = __TM_failure_address(&local_tdb);
152       if (addr != DEFAULT_ABORT_ADDRESS)
153 	{
154 	  return 11;
155 	}
156     }
157   {
158     long long tdb_abort_code;
159 
160     tdb_abort_code = __TM_failure_code(tdb);
161     if ((uint64_t)tdb_abort_code != abort_code)
162       {
163 	fprintf(
164 		stderr, "tm_ac %" PRIu64 ", ac %" PRIu64
165 		", tdb_ac %" PRIu64 "\n",
166 		(uint64_t)tdb_abort_code, abort_code,
167 		(uint64_t)tdb->abort_code);
168 	return 10;
169       }
170   }
171   expect_rc = (abort_code >= 256) ? 1 : 0;
172   rc = __TM_is_user_abort(tdb);
173   if (rc != expect_rc)
174     {
175       fprintf(stderr, "rc %ld, expect_rc %ld\n", rc, expect_rc);
176       return 1;
177     }
178   {
179     unsigned char code;
180 
181     code = 0xffu;
182     rc = __TM_is_named_user_abort(tdb, &code);
183     if (rc != expect_rc)
184       {
185 	fprintf(
186 		stderr, "rc %ld, expect_rc %ld\n", rc,
187 		expect_rc);
188 	return 2;
189       }
190     if (expect_rc == 1 && code != abort_code - 256)
191       {
192 	return 3;
193       }
194   }
195   if (abort_code > (uint64_t)num_abort_classes)
196     {
197       abort_code = (uint64_t)num_abort_classes;
198     }
199   expect_rc = (abort_classes[abort_code] == ABORT_T_ILLEGAL) ? 1 : 0;
200   rc = __TM_is_illegal(tdb);
201   if (rc != expect_rc)
202     {
203       dump_tdb(tdb);
204       fprintf(stderr, "rc %ld, expect_rc %ld\n", rc, expect_rc);
205       return 4;
206     }
207   expect_rc =
208     (abort_classes[abort_code] == ABORT_T_FOOTPRINT_EXCEEDED) ?
209     1 : 0;
210   rc = __TM_is_footprint_exceeded(tdb);
211   if (rc != expect_rc)
212     {
213       dump_tdb(tdb);
214       fprintf(stderr, "rc %ld, expect_rc %ld\n", rc, expect_rc);
215       return 5;
216     }
217   expect_rc =
218     (abort_classes[abort_code] == ABORT_T_NESTED_TOO_DEEP) ? 1 : 0;
219   rc = __TM_is_nested_too_deep(tdb);
220   if (rc != expect_rc)
221     {
222       dump_tdb(tdb);
223       fprintf(stderr, "rc %ld, expect_rc %ld\n", rc, expect_rc);
224       return 6;
225     }
226   expect_rc = (abort_classes[abort_code] == ABORT_T_CONFLICT) ? 1 : 0;
227   rc = __TM_is_conflict(tdb);
228   if (rc != expect_rc)
229     {
230       dump_tdb(tdb);
231       fprintf(stderr, "rc %ld, expect_rc %ld\n", rc, expect_rc);
232       return 7;
233     }
234 
235   return 0;
236 }
237 
238 /* ---------------------------- local test functions ----------------------- */
239 
240 /* Not a test; make sure that the involved global cachelines are reserved for
241  * writing.  */
init_cache(void)242 static int init_cache(void)
243 {
244   make_fake_tdb(&local_tdb);
245   make_fake_tdb(&local_tdb256);
246   global_int = 0;
247   global_u64 = 0;
248   global_float_1 = 1.0;
249   global_float_2 = 2.5;
250   global_float_3 = 0.0;
251   counters.c1 = 0;
252   counters.c2 = 0;
253   counters.c3 = 0;
254 
255   return 0;
256 }
257 
test_abort_classification(void)258 static int test_abort_classification(void)
259 {
260   int i;
261 
262   make_fake_tdb(&local_tdb);
263   for (i = 0; i <= 256; i++)
264     {
265       int rc;
266 
267       local_tdb.abort_code = (uint64_t)i;
268       rc = check_abort_code_in_tdb(&local_tdb, (uint64_t)i);
269       if (rc != 0)
270 	{
271 	  return 100 * i + rc;
272 	}
273     }
274 
275   return 0;
276 }
277 
test_cc_classification(void)278 static int test_cc_classification(void)
279 {
280   long rc;
281 
282   rc = __TM_is_failure_persistent(0);
283   if (rc != 0)
284     {
285       return 1;
286     }
287   rc = __TM_is_failure_persistent(1);
288   if (rc != 0)
289     {
290       return 2;
291     }
292   rc = __TM_is_failure_persistent(2);
293   if (rc != 0)
294     {
295       return 3;
296     }
297   rc = __TM_is_failure_persistent(3);
298   if (rc != 1)
299     {
300       return 4;
301     }
302 
303   return 0;
304 }
305 
test_tbegin_ntstg_tend(void)306 static int test_tbegin_ntstg_tend(void)
307 {
308   long rc;
309 
310   counters.c1 = 0;
311   counters.c2 = 0;
312   if ((rc = __TM_simple_begin()) == 0)
313     {
314       __TM_non_transactional_store((uint64_t *)&counters.c1, 1);
315       counters.c2 = 2;
316       rc = __TM_end();
317       if (rc != 0)
318 	{
319 	  return 100 * rc + 5;
320 	}
321       if (counters.c1 != 1)
322 	{
323 	  return 100 * counters.c1 + 2;
324 	}
325       if (counters.c2 != 2)
326 	{
327 	  return 100 * counters.c2 + 3;
328 	}
329     }
330   else
331     {
332       return 100 * rc + 4;
333     }
334 
335   return 0;
336 }
337 
test_tbegin_ntstg_tabort(void)338 static int test_tbegin_ntstg_tabort(void)
339 {
340   register float f;
341 
342   counters.c1 = 0;
343   counters.c2 = 0;
344   f = 0;
345   if (__TM_simple_begin() == 0)
346     {
347       __TM_non_transactional_store((uint64_t *)&counters.c1, 1);
348       counters.c2 = 2;
349       f = 1;
350       __TM_named_abort(0);
351       return 1;
352     }
353   if (counters.c1 != 1)
354     {
355       return 100 * counters.c1 + 2;
356     }
357   if (counters.c2 != 0)
358     {
359       return 100 * counters.c2 + 3;
360     }
361   if (f != 0)
362     {
363       return 100 * f + 4;
364     }
365 
366   return 0;
367 }
368 
test_tbegin_aborts(void)369 static int test_tbegin_aborts(void)
370 {
371   float f;
372   long rc;
373 
374   f = 77;
375   if ((rc = __TM_simple_begin()) == 0)
376     {
377       f = 88;
378       __TM_abort();
379       return 2;
380     }
381   else if (rc != 2)
382     {
383       return 3;
384     }
385   if (f != 77)
386     {
387       return 4;
388     }
389   f = 66;
390   if ((rc = __TM_simple_begin()) == 0)
391     {
392       f = 99;
393       __TM_named_abort(3);
394       return 5;
395     }
396   else if (rc != 3)
397     {
398       return 100 * rc + 6;
399     }
400   if (f != 66)
401     {
402       return 100 * f + 7;
403     }
404   if ((rc = __TM_simple_begin()) == 0)
405     {
406       global_float_3 = global_float_1 + global_float_2;
407       rc = __TM_end();
408       if (rc != 0)
409 	{
410 	  return 100 * rc + 8;
411 	}
412     }
413   else
414     {
415       return 100 * rc + 9;
416     }
417   if (global_float_3 != global_float_1 + global_float_2)
418     {
419       return 100 * rc + 10;
420     }
421 
422   return 0;
423 }
424 
test_tbegin_tdb(void)425 static int test_tbegin_tdb(void)
426 {
427   long rc;
428 
429   local_tdb.format = 0;
430   if ((rc = __TM_begin(&local_tdb)) == 0)
431     {
432       rc = __TM_end();
433       if (rc != 0)
434 	{
435 	  return 100 * rc + 1;
436 	}
437       if (local_tdb.format != 0)
438 	{
439 	  dump_tdb(&local_tdb);
440 	  return 100 * local_tdb.format + 2;
441 	}
442     }
443   else
444     {
445       return 100 * rc + 3;
446     }
447   local_tdb.format = 0;
448   if ((rc = __TM_begin(&local_tdb)) == 0)
449     {
450       __TM_named_abort(1);
451       return 4;
452     }
453   else
454     {
455       if (rc != 3)
456 	{
457 	  return 100 * rc + 5;
458 	}
459       if (local_tdb.format != 1)
460 	{
461 	  dump_tdb(&local_tdb);
462 	  return 100 * local_tdb.format + 6;
463 	}
464     }
465   local_tdb256.format = 0;
466   if ((rc = __TM_begin(&local_tdb256)) == 0)
467     {
468       rc = __TM_end();
469       if (rc != 0)
470 	{
471 	  return 1100 * rc + 1;
472 	}
473       if (local_tdb256.format != 0)
474 	{
475 	  dump_tdb(&local_tdb256);
476 	  return 1100 * local_tdb256.format + 2;
477 	}
478     }
479   else
480     {
481       return 1100 * rc + 3;
482     }
483 #if 1 /*!!!does not work*/
484   local_tdb256.format = 0;
485   if ((rc = __TM_begin(&local_tdb256)) == 0)
486     {
487       __TM_named_abort(1);
488       return 2004;
489     }
490   else
491     {
492       if (rc != 3)
493 	{
494 	  return 2100 * rc + 5;
495 	}
496       if (local_tdb256.format != 1)
497 	{
498 	  dump_tdb(&local_tdb256);
499 	  return 2100 * local_tdb256.format + 6;
500 	}
501     }
502 #endif
503 
504   return 0;
505 }
506 
test_etnd(void)507 static int test_etnd(void)
508 {
509   long rc;
510 
511   {
512     long nd;
513 
514     make_fake_tdb(&local_tdb);
515     local_tdb.nesting_depth = 0;
516     nd = __TM_nesting_depth(&local_tdb);
517     if (nd != 0)
518       {
519 	return 1;
520       }
521     local_tdb.nesting_depth = 7;
522     nd = __TM_nesting_depth(&local_tdb);
523     if (nd != 7)
524       {
525 	return 7;
526       }
527     local_tdb.format = 0;
528     nd = __TM_nesting_depth(&local_tdb);
529     if (nd != 0)
530       {
531 	return 2;
532       }
533   }
534   counters.c1 = 0;
535   counters.c1 = 0;
536   counters.c2 = 0;
537   counters.c3 = 0;
538   if ((rc = __TM_simple_begin()) == 0)
539     {
540       counters.c1 = __TM_nesting_depth(0);
541       if (__TM_simple_begin() == 0)
542 	{
543 	  counters.c2 = __TM_nesting_depth(0);
544 	  if (__TM_simple_begin() == 0)
545 	    {
546 	      counters.c3 = __TM_nesting_depth(0);
547 	      __TM_end();
548 	    }
549 	  __TM_end();
550 	}
551       __TM_end();
552     }
553   else
554     {
555       return 100 * rc + 1;
556     }
557   if (counters.c1 != 1)
558     {
559       return 100 * counters.c1 + 2;
560     }
561   if (counters.c2 != 2)
562     {
563       return 100 * counters.c2 + 3;
564     }
565   if (counters.c3 != 3)
566     {
567       return 100 * counters.c3 + 4;
568     }
569 
570   return 0;
571 }
572 
573 /* ---------------------------- local testing framework functions ---------- */
574 
run_one_test(const test_table_entry_t * test_entry)575 static int run_one_test(const test_table_entry_t *test_entry)
576 {
577   int do_print_passes;
578   int succeeded;
579   int rc;
580   int i;
581 
582   do_print_passes = (
583 		     test_entry->required_quorum != 1 ||
584 		     test_entry->max_repetitions != 1);
585   printf("RRR RUN  %s\n", test_entry->name);
586   if (do_print_passes == 1)
587     {
588       printf(
589 	     "         (requires %d successful out of %d runs)\n",
590 	     test_entry->required_quorum,
591 	     test_entry->max_repetitions);
592     }
593   succeeded = 0;
594   rc = 0;
595   for (rc = 0, i = 0; i < test_entry->max_repetitions; i++)
596     {
597       if (do_print_passes == 1)
598 	{
599 	  if (i == 0)
600 	    {
601 	      printf("        ");
602 	    }
603 	  else
604 	    {
605 	      printf(",");
606 	    }
607 	}
608       rc = test_entry->test_func();
609       if (rc == 0)
610 	{
611 	  if (do_print_passes == 1)
612 	    {
613 	      printf(" success");
614 	    }
615 	  succeeded++;
616 	  if (succeeded >= test_entry->required_quorum)
617 	    {
618 	      break;
619 	    }
620 	}
621       else
622 	{
623 	  printf(" failed (rc = %d)", rc);
624 	}
625     }
626   if (do_print_passes == 1 || rc != 0)
627     {
628       printf("\n");
629     }
630   if (succeeded >= test_entry->required_quorum)
631     {
632       printf("+++ OK   %s\n", test_entry->name);
633 
634       return 0;
635     }
636   else
637     {
638       printf("--- FAIL %s\n", test_entry->name);
639 
640       return (rc != 0) ? rc : -1;
641     }
642 }
643 
run_all_tests(const test_table_entry_t * test_table)644 static int run_all_tests(const test_table_entry_t *test_table)
645 {
646   const test_table_entry_t *test;
647   int rc;
648 
649   for (
650        rc = 0, test = &test_table[0];
651        test->test_func != NULL && rc == 0; test++)
652     {
653       rc = run_one_test(test);
654     }
655 
656   return rc;
657 }
658 
659 /* ---------------------------- interface functions ------------------------ */
660 
main(void)661 int main(void)
662 {
663   const test_table_entry_t test_table[] = {
664     TEST_NO_REP(init_cache),
665     TEST_NO_REP(test_abort_classification),
666     TEST_NO_REP(test_cc_classification),
667     TEST_DF_REP(test_tbegin_ntstg_tend),
668     TEST_DF_REP(test_tbegin_ntstg_tabort),
669     TEST_DF_REP(test_tbegin_aborts),
670     TEST_DF_REP(test_tbegin_tdb),
671     TEST_DF_REP(test_etnd),
672     { (void *)0, 0, 0 }
673   };
674 
675   {
676     int rc;
677 
678     rc = run_all_tests(test_table);
679 
680     return rc;
681   }
682 }
683