683 lines
12 KiB
C
683 lines
12 KiB
C
/* 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 <stdio.h>
|
|
#include <string.h>
|
|
#include <inttypes.h>
|
|
#include <htmxlintrin.h>
|
|
|
|
/* ---------------------------- local definitions -------------------------- */
|
|
|
|
#define DEFAULT_MAX_REPETITIONS 7
|
|
#define DEFAULT_REQUIRED_QUORUM 4
|
|
#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;
|
|
}
|
|
}
|