blob: bb9d346ea560a73ff8bb1c324295064814b0a595 [file] [log] [blame]
/* 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;
}
}