blob: ff43be9fe736e27024e406ab852ee960411c4e33 [file] [log] [blame]
/* Functional tests of the htm __builtin_... 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 <stdint.h>
#include <htmintrin.h>
/* ---------------------------- local definitions -------------------------- */
#define DEFAULT_MAX_REPETITIONS 7
#define DEFAULT_REQUIRED_QUORUM 4
#define NUM_WARMUP_RUNS 2
/* ---------------------------- 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;
/* ---------------------------- local variables ---------------------------- */
__attribute__ ((aligned(256))) static struct __htm_tdb local_tdb256;
static struct __htm_tdb local_tdb;
static int do_dump_tdb = 0;
/* ---------------------------- exported variables (globals) --------------- */
__attribute__ ((aligned(256))) struct
{
float float_1;
float float_2;
float float_3;
} global = { 1.0, 2.5, 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;
if (do_dump_tdb == 0)
{
return;
}
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;
}
/* ---------------------------- local test functions ----------------------- */
/* Check values of the constants defined in htmintrin.h. */
static int test_constants (void)
{
if (_HTM_TBEGIN_STARTED != 0)
{
return 100 * _HTM_TBEGIN_STARTED + 1;
}
if (_HTM_TBEGIN_INDETERMINATE != 1)
{
return 100 * _HTM_TBEGIN_INDETERMINATE + 2;
}
if (_HTM_TBEGIN_TRANSIENT != 2)
{
return 100 * _HTM_TBEGIN_TRANSIENT + 3;
}
if (_HTM_TBEGIN_PERSISTENT != 3)
{
return 100 * _HTM_TBEGIN_PERSISTENT + 4;
}
return 0;
}
static int test_tbegin_ntstg_tend (void)
{
int rc;
counters.c1 = 0;
counters.c2 = 0;
if ((rc = __builtin_tbegin ((void *)0)) == 0)
{
__builtin_non_tx_store ((uint64_t *)&counters.c1, 1);
counters.c2 = 2;
rc = __builtin_tend ();
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)
{
float f;
counters.c1 = 0;
counters.c2 = 0;
f = 0;
if (__builtin_tbegin ((void *)0) == 0)
{
__builtin_non_tx_store ((uint64_t *)&counters.c1, 1);
counters.c2 = 2;
f = 1;
__builtin_tabort (256);
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_nofloat (void)
{
int rc;
counters.c1 = 0;
counters.c2 = 0;
if ((rc = __builtin_tbegin_nofloat ((void *)0)) == 0)
{
__builtin_non_tx_store ((uint64_t *)&counters.c1, 1);
counters.c2 = 2;
rc = __builtin_tend ();
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_retry (void)
{
int rc;
counters.c1 = 0;
counters.c2 = 0;
counters.c3 = 0;
if ((rc = __builtin_tbegin_retry ((void *)0, 5)) == 0)
{
int do_abort;
do_abort = (counters.c1 == 0) ? 1 : 0;
__builtin_non_tx_store (
(uint64_t *)&counters.c1, counters.c1 + 1);
if (do_abort == 1)
{
__builtin_tabort (256);
}
counters.c2 = counters.c2 + 10;
__builtin_non_tx_store ((uint64_t *)&counters.c3, 3);
rc = __builtin_tend ();
if (rc != 0)
{
return 100 * rc + 5;
}
if (counters.c1 != 2)
{
return 100 * counters.c1 + 2;
}
if (counters.c2 != 10)
{
return 100 * counters.c2 + 3;
}
if (counters.c3 != 3)
{
return 100 * counters.c3 + 6;
}
}
else
{
return 100 * rc + 4;
}
return 0;
}
static int test_tbegin_retry_nofloat (void)
{
int rc;
counters.c1 = 0;
counters.c2 = 0;
counters.c3 = 0;
if ((rc = __builtin_tbegin_retry_nofloat ((void *)0, 5)) == 0)
{
int do_abort;
do_abort = (counters.c1 == 0) ? 1 : 0;
__builtin_non_tx_store (
(uint64_t *)&counters.c1, counters.c1 + 1);
if (do_abort == 1)
{
__builtin_tabort (256);
}
counters.c2 = counters.c2 + 10;
__builtin_non_tx_store ((uint64_t *)&counters.c3, 3);
rc = __builtin_tend ();
if (rc != 0)
{
return 100 * rc + 5;
}
if (counters.c1 != 2)
{
return 100 * counters.c1 + 2;
}
if (counters.c2 != 10)
{
return 100 * counters.c2 + 3;
}
if (counters.c3 != 3)
{
return 100 * counters.c3 + 6;
}
}
else
{
return 100 * rc + 4;
}
return 0;
}
static int test_tbegin_aborts (void)
{
float f;
int rc;
f = 77;
if ((rc = __builtin_tbegin ((void *)0)) == 0)
{
f = 88;
__builtin_tabort (256);
return 2;
}
else if (rc != 2)
{
return 3;
}
if (f != 77)
{
return 4;
}
f = 66;
if ((rc = __builtin_tbegin ((void *)0)) == 0)
{
f = 99;
__builtin_tabort (257);
return 5;
}
else if (rc != 3)
{
return 100 * rc + 6;
}
if (f != 66)
{
return 100 * f + 7;
}
if ((rc = __builtin_tbegin ((void *)0)) == 0)
{
global.float_3 = global.float_1 + global.float_2;
rc = __builtin_tend ();
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 __attribute__((noinline)) void indirect_abort(int abort_code)
{
__builtin_tabort (abort_code);
return;
}
static int test_tbegin_indirect_aborts (void)
{
float f;
int rc;
f = 77;
if ((rc = __builtin_tbegin ((void *)0)) == 0)
{
f = 88;
indirect_abort(256);
return 2;
}
else if (rc != 2)
{
return 100 * rc + 3;
}
if (f != 77)
{
return 100 * rc + 4;
}
f = 66;
if ((rc = __builtin_tbegin ((void *)0)) == 0)
{
f = 99;
indirect_abort(257);
return 5;
}
else if (rc != 3)
{
return 100 * rc + 6;
}
if (f != 66)
{
return 100 * f + 7;
}
return 0;
}
static int test_tbegin_nofloat_aborts (void)
{
int rc;
if ((rc = __builtin_tbegin_nofloat ((void *)0)) == 0)
{
__builtin_tabort (256);
return 2;
}
if ((rc = __builtin_tbegin_nofloat ((void *)0)) == 0)
{
__builtin_tabort (257);
return 1005;
}
else if (rc != 3)
{
return 1000 * rc + 6;
}
return 0;
}
static int test_tbegin_nofloat_indirect_aborts (void)
{
int rc;
if ((rc = __builtin_tbegin_nofloat ((void *)0)) == 0)
{
indirect_abort (256);
return 2;
}
if ((rc = __builtin_tbegin_nofloat ((void *)0)) == 0)
{
indirect_abort (257);
return 1005;
}
else if (rc != 3)
{
return 1000 * rc + 6;
}
return 0;
}
static
int _test_tbegin_retry_aborts (int retries, uint64_t abort_code)
{
int rc;
counters.c1 = 0;
if ((rc = __builtin_tbegin_retry ((void *)0, retries)) == 0)
{
__builtin_non_tx_store ((uint64_t *)&counters.c1, counters.c1 + 1);
__builtin_tabort (abort_code);
return 2;
}
else
{
if ((abort_code & 1) == 0)
{
if (rc != 2)
{
return 100 * rc + 2003;
}
else if (counters.c1 != (uint64_t)retries + 1)
{
return 1000 * counters.c1 + 100 * retries + 4;
}
}
else
{
if (rc != 3)
{
return 100 * rc + 3005;
}
else if (counters.c1 != 1)
{
return 1000 * counters.c1 + 100 * retries + 6;
}
}
}
return 0;
}
static int test_tbegin_retry_aborts (void)
{
int rc;
int retries;
for (retries = 1; retries <= 3; retries++)
{
rc = _test_tbegin_retry_aborts (retries, 256);
if (rc != 0)
{
return 10000 + rc;
}
}
for (retries = 1; retries <= 3; retries++)
{
rc = _test_tbegin_retry_aborts (retries, 257);
if (rc != 0)
{
return 20000 + rc;
}
}
if ((rc = __builtin_tbegin_retry ((void *)0, 5)) == 0)
{
global.float_3 = global.float_1 + global.float_2;
rc = __builtin_tend ();
if (rc != 0)
{
return 30000 + 100 * rc + 6;
}
}
else
{
return 30000 + 100 * rc + 7;
}
return 0;
}
static int _test_tbegin_retry_nofloat_aborts (int retries, uint64_t abort_code)
{
int rc;
counters.c1 = 0;
if ((rc = __builtin_tbegin_retry_nofloat ((void *)0, retries)) == 0)
{
__builtin_non_tx_store ((uint64_t *)&counters.c1, counters.c1 + 1);
__builtin_tabort (abort_code);
return 2;
}
else
{
if ((abort_code & 1) == 0)
{
if (rc != 2)
{
return 100 * rc + 2003;
}
else if (counters.c1 != (uint64_t)retries + 1)
{
return 1000 * counters.c1 + 100 * retries + 4;
}
}
else
{
if (rc != 3)
{
return 100 * rc + 3005;
}
else if (counters.c1 != 1)
{
return 1000 * counters.c1 + 100 * retries + 6;
}
}
}
return 0;
}
static int test_tbegin_retry_nofloat_aborts (void)
{
int rc;
int retries;
for (retries = 1; retries <= 3; retries++)
{
rc = _test_tbegin_retry_nofloat_aborts (retries, 256);
if (rc != 0)
{
return 10 * retries + rc;
}
}
for (retries = 1; retries <= 3; retries++)
{
rc = _test_tbegin_retry_nofloat_aborts (retries, 257);
if (rc != 0)
{
return 10000 + 10 * retries + rc;
}
}
return 0;
}
static int test_tbegin_tdb (void)
{
int rc;
local_tdb.format = 0;
if ((rc = __builtin_tbegin (&local_tdb)) == 0)
{
rc = __builtin_tend ();
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 = __builtin_tbegin (&local_tdb)) == 0)
{
__builtin_tabort (257);
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 = __builtin_tbegin (&local_tdb256)) == 0)
{
rc = __builtin_tend ();
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;
}
local_tdb256.format = 0;
if ((rc = __builtin_tbegin (&local_tdb256)) == 0)
{
__builtin_tabort (257);
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;
}
}
return 0;
}
static int test_tbegin_nofloat_tdb (void)
{
int rc;
local_tdb.format = 0;
if ((rc = __builtin_tbegin_nofloat (&local_tdb)) == 0)
{
rc = __builtin_tend ();
if (rc != 0)
{
return 100 * rc + 1;
}
if (local_tdb.format != 0)
{
dump_tdb (&local_tdb);
return 100 * local_tdb.format + 2;
}
}
else
{
return 3;
}
local_tdb.format = 0;
if ((rc = __builtin_tbegin_nofloat (&local_tdb)) == 0)
{
__builtin_tabort (257);
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 = __builtin_tbegin_nofloat (&local_tdb256)) == 0)
{
rc = __builtin_tend ();
if (rc != 0)
{
return 1100 * rc + 1;
}
if (local_tdb256.format != 0)
{
dump_tdb (&local_tdb256);
return 1100 * local_tdb256.format + 2;
}
}
else
{
return 1003;
}
local_tdb256.format = 0;
if ((rc = __builtin_tbegin_nofloat (&local_tdb256)) == 0)
{
__builtin_tabort (257);
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;
}
}
return 0;
}
static int test_tbegin_retry_tdb (void)
{
int rc;
local_tdb256.format = 0;
if ((rc = __builtin_tbegin_retry (&local_tdb256, 2)) == 0)
{
rc = __builtin_tend ();
if (rc != 0)
{
return 1100 * rc + 1;
}
if (local_tdb256.format != 0)
{
dump_tdb (&local_tdb256);
return 1100 * local_tdb256.format + 2;
}
}
else
{
return 1003;
}
local_tdb256.format = 0;
if ((rc = __builtin_tbegin_retry (&local_tdb256, 2)) == 0)
{
__builtin_tabort (257);
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;
}
}
return 0;
}
static int test_tbegin_retry_nofloat_tdb (void)
{
int rc;
local_tdb.format = 0;
if ((rc = __builtin_tbegin_retry_nofloat (&local_tdb, 2)) == 0)
{
rc = __builtin_tend ();
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 = __builtin_tbegin_retry_nofloat (&local_tdb, 2)) == 0)
{
__builtin_tabort (257);
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 = __builtin_tbegin_retry_nofloat (&local_tdb256, 2)) == 0)
{
rc = __builtin_tend ();
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;
}
local_tdb256.format = 0;
if ((rc = __builtin_tbegin_retry_nofloat (&local_tdb256, 2)) == 0)
{
__builtin_tabort (257);
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;
}
}
return 0;
}
static int test_etnd (void)
{
int rc;
counters.c1 = 0;
counters.c2 = 0;
counters.c3 = 0;
if ((rc = __builtin_tbegin ((void *)0)) == 0)
{
counters.c1 = __builtin_tx_nesting_depth ();
if (__builtin_tbegin ((void *)0) == 0)
{
counters.c2 = __builtin_tx_nesting_depth ();
if (__builtin_tbegin ((void *)0) == 0)
{
counters.c3 = __builtin_tx_nesting_depth ();
__builtin_tend ();
}
__builtin_tend ();
}
__builtin_tend ();
}
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;
}
static int test_tbeginc (void)
{
int rc;
counters.c1 = 0;
__builtin_tbeginc ();
counters.c1 = 1;
rc = __builtin_tend ();
if (rc != 0)
{
return 10000 * rc + 1;
}
if (counters.c1 != 1)
{
return 100000 * counters.c1 + 3;
}
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;
/* Warmup run to get all necessary data and instruction pages into the page
* tables. */
{
int run;
do_dump_tdb = 0;
for (run = 0; run < NUM_WARMUP_RUNS; run++)
{
test_entry->test_func ();
}
do_dump_tdb = 1;
}
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 (test_constants),
TEST_DF_REP (test_tbegin_ntstg_tend),
TEST_DF_REP (test_tbegin_ntstg_tabort),
TEST_DF_REP (test_tbegin_nofloat),
TEST_NO_REP (test_tbegin_retry),
TEST_NO_REP (test_tbegin_retry_nofloat),
TEST_DF_REP (test_tbegin_aborts),
TEST_DF_REP (test_tbegin_indirect_aborts),
TEST_DF_REP (test_tbegin_nofloat_aborts),
TEST_DF_REP (test_tbegin_nofloat_indirect_aborts),
TEST_NO_REP (test_tbegin_retry_aborts),
TEST_NO_REP (test_tbegin_retry_nofloat_aborts),
TEST_DF_REP (test_tbegin_tdb),
TEST_DF_REP (test_tbegin_nofloat_tdb),
TEST_NO_REP (test_tbegin_retry_tdb),
TEST_NO_REP (test_tbegin_retry_nofloat_tdb),
TEST_DF_REP (test_etnd),
TEST_DF_REP (test_tbeginc),
{ (void *)0, 0, 0 }
};
{
int rc;
rc = run_all_tests (test_table);
return rc;
}
}