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