/* Functional tests of the htm __TM_... macros. */ /* { dg-do run } */ /* { dg-require-effective-target htm } */ /* { dg-options "-O3 -march=zEC12 -mzarch" } */ /* ---------------------------- included header files ---------------------- */ #include #include #include #include /* ---------------------------- local definitions -------------------------- */ #define DEFAULT_MAX_REPETITIONS 5 #define DEFAULT_REQUIRED_QUORUM ((DEFAULT_MAX_REPETITIONS) - 1) #define DEFAULT_ABORT_ADDRESS (0x12345678u) /* ---------------------------- local macros ------------------------------- */ #define TEST_DF_REP(name) \ { #name, name, DEFAULT_MAX_REPETITIONS, DEFAULT_REQUIRED_QUORUM } #define TEST_NO_REP(name) { #name, name, 1, 1 } /* ---------------------------- local types -------------------------------- */ typedef int (*test_func_t)(void); typedef struct { const char *name; test_func_t test_func; int max_repetitions; int required_quorum; } test_table_entry_t; typedef enum { ABORT_T_SYSTEM = 0, ABORT_T_USER = 1, } abort_user_t; typedef enum { ABORT_T_NONE = 0, ABORT_T_ILLEGAL, ABORT_T_FOOTPRINT_EXCEEDED, ABORT_T_NESTED_TOO_DEEP, ABORT_T_CONFLICT, ABORT_T_INVALID_ABORT_CODE } abort_t; /* ---------------------------- local variables ---------------------------- */ __attribute__ ((aligned(256))) static struct __htm_tdb local_tdb256; static struct __htm_tdb local_tdb; static abort_t const abort_classes[] = { ABORT_T_INVALID_ABORT_CODE, ABORT_T_NONE, ABORT_T_NONE, ABORT_T_NONE, ABORT_T_ILLEGAL, ABORT_T_NONE, ABORT_T_NONE, ABORT_T_FOOTPRINT_EXCEEDED, ABORT_T_FOOTPRINT_EXCEEDED, ABORT_T_CONFLICT, ABORT_T_CONFLICT, ABORT_T_ILLEGAL, ABORT_T_NONE, ABORT_T_NESTED_TOO_DEEP, ABORT_T_NONE, ABORT_T_NONE, ABORT_T_NONE }; static size_t num_abort_classes = sizeof(abort_classes) / sizeof(abort_t); /* ---------------------------- exported variables (globals) --------------- */ int global_int = 0; uint64_t global_u64 = 0; float global_float_1 = 1.0; float global_float_2 = 2.5; float global_float_3 = 0.0; __attribute__ ((aligned(256))) struct { volatile uint64_t c1; volatile uint64_t c2; volatile uint64_t c3; } counters = { 0, 0, 0 }; /* ---------------------------- local helper functions --------------------- */ static void dump_tdb(struct __htm_tdb *tdb) { unsigned char *p; int i; int j; p = (unsigned char *)tdb; for (i = 0; i < 16; i++) { fprintf(stderr, "0x%02x ", i * 16); for (j = 0; j < 16; j++) { fprintf(stderr, "%02x", (int)p[i * 16 + j]); if (j < 15) { fprintf(stderr, " "); } if (j == 7) { fprintf(stderr, " "); } } fprintf(stderr, "\n"); } return; } static void make_fake_tdb(struct __htm_tdb *tdb) { memset(tdb, 0, sizeof(*tdb)); tdb->format = 1; tdb->nesting_depth = 1; tdb->atia = DEFAULT_ABORT_ADDRESS; tdb->abort_code = 11; return; } static int check_abort_code_in_tdb(struct __htm_tdb *tdb, uint64_t abort_code) { long expect_rc; long rc; if (abort_code != 0) { long addr; addr = __TM_failure_address(&local_tdb); if (addr != DEFAULT_ABORT_ADDRESS) { return 11; } } { long long tdb_abort_code; tdb_abort_code = __TM_failure_code(tdb); if ((uint64_t)tdb_abort_code != abort_code) { fprintf( stderr, "tm_ac %" PRIu64 ", ac %" PRIu64 ", tdb_ac %" PRIu64 "\n", (uint64_t)tdb_abort_code, abort_code, (uint64_t)tdb->abort_code); return 10; } } expect_rc = (abort_code >= 256) ? 1 : 0; rc = __TM_is_user_abort(tdb); if (rc != expect_rc) { fprintf(stderr, "rc %ld, expect_rc %ld\n", rc, expect_rc); return 1; } { unsigned char code; code = 0xffu; rc = __TM_is_named_user_abort(tdb, &code); if (rc != expect_rc) { fprintf( stderr, "rc %ld, expect_rc %ld\n", rc, expect_rc); return 2; } if (expect_rc == 1 && code != abort_code - 256) { return 3; } } if (abort_code > (uint64_t)num_abort_classes) { abort_code = (uint64_t)num_abort_classes; } expect_rc = (abort_classes[abort_code] == ABORT_T_ILLEGAL) ? 1 : 0; rc = __TM_is_illegal(tdb); if (rc != expect_rc) { dump_tdb(tdb); fprintf(stderr, "rc %ld, expect_rc %ld\n", rc, expect_rc); return 4; } expect_rc = (abort_classes[abort_code] == ABORT_T_FOOTPRINT_EXCEEDED) ? 1 : 0; rc = __TM_is_footprint_exceeded(tdb); if (rc != expect_rc) { dump_tdb(tdb); fprintf(stderr, "rc %ld, expect_rc %ld\n", rc, expect_rc); return 5; } expect_rc = (abort_classes[abort_code] == ABORT_T_NESTED_TOO_DEEP) ? 1 : 0; rc = __TM_is_nested_too_deep(tdb); if (rc != expect_rc) { dump_tdb(tdb); fprintf(stderr, "rc %ld, expect_rc %ld\n", rc, expect_rc); return 6; } expect_rc = (abort_classes[abort_code] == ABORT_T_CONFLICT) ? 1 : 0; rc = __TM_is_conflict(tdb); if (rc != expect_rc) { dump_tdb(tdb); fprintf(stderr, "rc %ld, expect_rc %ld\n", rc, expect_rc); return 7; } return 0; } /* ---------------------------- local test functions ----------------------- */ /* Not a test; make sure that the involved global cachelines are reserved for * writing. */ static int init_cache(void) { make_fake_tdb(&local_tdb); make_fake_tdb(&local_tdb256); global_int = 0; global_u64 = 0; global_float_1 = 1.0; global_float_2 = 2.5; global_float_3 = 0.0; counters.c1 = 0; counters.c2 = 0; counters.c3 = 0; return 0; } static int test_abort_classification(void) { int i; make_fake_tdb(&local_tdb); for (i = 0; i <= 256; i++) { int rc; local_tdb.abort_code = (uint64_t)i; rc = check_abort_code_in_tdb(&local_tdb, (uint64_t)i); if (rc != 0) { return 100 * i + rc; } } return 0; } static int test_cc_classification(void) { long rc; rc = __TM_is_failure_persistent(0); if (rc != 0) { return 1; } rc = __TM_is_failure_persistent(1); if (rc != 0) { return 2; } rc = __TM_is_failure_persistent(2); if (rc != 0) { return 3; } rc = __TM_is_failure_persistent(3); if (rc != 1) { return 4; } return 0; } static int test_tbegin_ntstg_tend(void) { long rc; counters.c1 = 0; counters.c2 = 0; if ((rc = __TM_simple_begin()) == 0) { __TM_non_transactional_store((uint64_t *)&counters.c1, 1); counters.c2 = 2; rc = __TM_end(); if (rc != 0) { return 100 * rc + 5; } if (counters.c1 != 1) { return 100 * counters.c1 + 2; } if (counters.c2 != 2) { return 100 * counters.c2 + 3; } } else { return 100 * rc + 4; } return 0; } static int test_tbegin_ntstg_tabort(void) { register float f; counters.c1 = 0; counters.c2 = 0; f = 0; if (__TM_simple_begin() == 0) { __TM_non_transactional_store((uint64_t *)&counters.c1, 1); counters.c2 = 2; f = 1; __TM_named_abort(0); return 1; } if (counters.c1 != 1) { return 100 * counters.c1 + 2; } if (counters.c2 != 0) { return 100 * counters.c2 + 3; } if (f != 0) { return 100 * f + 4; } return 0; } static int test_tbegin_aborts(void) { float f; long rc; f = 77; if ((rc = __TM_simple_begin()) == 0) { f = 88; __TM_abort(); return 2; } else if (rc != 2) { return 3; } if (f != 77) { return 4; } f = 66; if ((rc = __TM_simple_begin()) == 0) { f = 99; __TM_named_abort(3); return 5; } else if (rc != 3) { return 100 * rc + 6; } if (f != 66) { return 100 * f + 7; } if ((rc = __TM_simple_begin()) == 0) { global_float_3 = global_float_1 + global_float_2; rc = __TM_end(); if (rc != 0) { return 100 * rc + 8; } } else { return 100 * rc + 9; } if (global_float_3 != global_float_1 + global_float_2) { return 100 * rc + 10; } return 0; } static int test_tbegin_tdb(void) { long rc; local_tdb.format = 0; if ((rc = __TM_begin(&local_tdb)) == 0) { rc = __TM_end(); if (rc != 0) { return 100 * rc + 1; } if (local_tdb.format != 0) { dump_tdb(&local_tdb); return 100 * local_tdb.format + 2; } } else { return 100 * rc + 3; } local_tdb.format = 0; if ((rc = __TM_begin(&local_tdb)) == 0) { __TM_named_abort(1); return 4; } else { if (rc != 3) { return 100 * rc + 5; } if (local_tdb.format != 1) { dump_tdb(&local_tdb); return 100 * local_tdb.format + 6; } } local_tdb256.format = 0; if ((rc = __TM_begin(&local_tdb256)) == 0) { rc = __TM_end(); if (rc != 0) { return 1100 * rc + 1; } if (local_tdb256.format != 0) { dump_tdb(&local_tdb256); return 1100 * local_tdb256.format + 2; } } else { return 1100 * rc + 3; } #if 1 /*!!!does not work*/ local_tdb256.format = 0; if ((rc = __TM_begin(&local_tdb256)) == 0) { __TM_named_abort(1); return 2004; } else { if (rc != 3) { return 2100 * rc + 5; } if (local_tdb256.format != 1) { dump_tdb(&local_tdb256); return 2100 * local_tdb256.format + 6; } } #endif return 0; } static int test_etnd(void) { long rc; { long nd; make_fake_tdb(&local_tdb); local_tdb.nesting_depth = 0; nd = __TM_nesting_depth(&local_tdb); if (nd != 0) { return 1; } local_tdb.nesting_depth = 7; nd = __TM_nesting_depth(&local_tdb); if (nd != 7) { return 7; } local_tdb.format = 0; nd = __TM_nesting_depth(&local_tdb); if (nd != 0) { return 2; } } counters.c1 = 0; counters.c1 = 0; counters.c2 = 0; counters.c3 = 0; if ((rc = __TM_simple_begin()) == 0) { counters.c1 = __TM_nesting_depth(0); if (__TM_simple_begin() == 0) { counters.c2 = __TM_nesting_depth(0); if (__TM_simple_begin() == 0) { counters.c3 = __TM_nesting_depth(0); __TM_end(); } __TM_end(); } __TM_end(); } else { return 100 * rc + 1; } if (counters.c1 != 1) { return 100 * counters.c1 + 2; } if (counters.c2 != 2) { return 100 * counters.c2 + 3; } if (counters.c3 != 3) { return 100 * counters.c3 + 4; } return 0; } /* ---------------------------- local testing framework functions ---------- */ static int run_one_test(const test_table_entry_t *test_entry) { int do_print_passes; int succeeded; int rc; int i; do_print_passes = ( test_entry->required_quorum != 1 || test_entry->max_repetitions != 1); printf("RRR RUN %s\n", test_entry->name); if (do_print_passes == 1) { printf( " (requires %d successful out of %d runs)\n", test_entry->required_quorum, test_entry->max_repetitions); } succeeded = 0; rc = 0; for (rc = 0, i = 0; i < test_entry->max_repetitions; i++) { if (do_print_passes == 1) { if (i == 0) { printf(" "); } else { printf(","); } } rc = test_entry->test_func(); if (rc == 0) { if (do_print_passes == 1) { printf(" success"); } succeeded++; if (succeeded >= test_entry->required_quorum) { break; } } else { printf(" failed (rc = %d)", rc); } } if (do_print_passes == 1 || rc != 0) { printf("\n"); } if (succeeded >= test_entry->required_quorum) { printf("+++ OK %s\n", test_entry->name); return 0; } else { printf("--- FAIL %s\n", test_entry->name); return (rc != 0) ? rc : -1; } } static int run_all_tests(const test_table_entry_t *test_table) { const test_table_entry_t *test; int rc; for ( rc = 0, test = &test_table[0]; test->test_func != NULL && rc == 0; test++) { rc = run_one_test(test); } return rc; } /* ---------------------------- interface functions ------------------------ */ int main(void) { const test_table_entry_t test_table[] = { TEST_NO_REP(init_cache), TEST_NO_REP(test_abort_classification), TEST_NO_REP(test_cc_classification), TEST_DF_REP(test_tbegin_ntstg_tend), TEST_DF_REP(test_tbegin_ntstg_tabort), TEST_DF_REP(test_tbegin_aborts), TEST_DF_REP(test_tbegin_tdb), TEST_DF_REP(test_etnd), { (void *)0, 0, 0 } }; { int rc; rc = run_all_tests(test_table); return rc; } }