mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-15 01:11:27 +00:00
f18837f195
I always suspected the overflow conversions were UB, but with gcc doing different things on arm, I thought it was about time to abandon those particular tests. What I was not expecting was for the return value of strcmp to be "UB" (in that there's no guarantee of the exact value, just < = > 0). Fortunately, nothing actually relies on the value of the op other than the tests, so modify the test to make the behavior well defined.
254 lines
6.5 KiB
C
254 lines
6.5 KiB
C
#include <setjmp.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "QF/va.h"
|
|
|
|
#include "QF/simd/types.h"
|
|
|
|
#define num_tests (sizeof (tests) / sizeof (tests[0]))
|
|
static int test_enabled[num_tests] = { 0 };
|
|
|
|
#include "getopt.h"
|
|
|
|
#include "QF/cmd.h"
|
|
#include "QF/cvar.h"
|
|
|
|
static bfunction_t test_functions[] = {
|
|
{}, // null function
|
|
{ .first_statement = 0 }
|
|
};
|
|
static dprograms_t test_progs = {
|
|
.version = PROG_VERSION,
|
|
};
|
|
static progs_t test_pr;
|
|
|
|
static jmp_buf jump_buffer;
|
|
|
|
static void
|
|
test_debug_handler (prdebug_t event, void *param, void *data)
|
|
{
|
|
progs_t *pr = data;
|
|
|
|
switch (event) {
|
|
case prd_breakpoint:
|
|
if (verbose > 0) {
|
|
printf ("debug: %s\n", prdebug_names[event]);
|
|
}
|
|
longjmp (jump_buffer, 1);
|
|
case prd_subenter:
|
|
if (verbose > 0) {
|
|
printf ("debug: subenter %d\n", *(pr_func_t *) param);
|
|
}
|
|
case prd_subexit:
|
|
break;
|
|
case prd_trace:
|
|
dstatement_t *st = test_pr.pr_statements + test_pr.pr_xstatement;
|
|
if (verbose > 1) {
|
|
printf ("---\n");
|
|
printf ("debug: trace %05x %04x %04x %04x %04x%s\n",
|
|
test_pr.pr_xstatement, st->op, st->a, st->b, st->c,
|
|
pr->globals.stack ? va (0, " %05x", *pr->globals.stack)
|
|
: "");
|
|
printf (" %04x %04x %04x\n",
|
|
st->a + PR_BASE (pr, st, A),
|
|
st->b + PR_BASE (pr, st, B),
|
|
st->c + PR_BASE (pr, st, C));
|
|
}
|
|
if (verbose > 0) {
|
|
PR_PrintStatement (&test_pr, st, 0);
|
|
}
|
|
if (pr->globals.stack) {
|
|
if (*pr->globals.stack & 3) {
|
|
printf ("stack not aligned: %d\n", *pr->globals.stack);
|
|
longjmp (jump_buffer, 3);
|
|
}
|
|
}
|
|
break;
|
|
case prd_runerror:
|
|
printf ("debug: %s: %s\n", prdebug_names[event], (char *)param);
|
|
longjmp (jump_buffer, 3);
|
|
case prd_watchpoint:
|
|
case prd_begin:
|
|
case prd_terminate:
|
|
case prd_error:
|
|
case prd_none:
|
|
printf ("debug: unexpected:%s %p\n", prdebug_names[event], param);
|
|
longjmp (jump_buffer, 2);
|
|
}
|
|
}
|
|
|
|
static void
|
|
setup_test (test_t *test)
|
|
{
|
|
memset (&test_pr, 0, sizeof (test_pr));
|
|
PR_Init (&test_pr);
|
|
PR_Debug_Init (&test_pr);
|
|
test_pr.progs = &test_progs;
|
|
test_pr.debug_handler = test_debug_handler;
|
|
test_pr.debug_data = &test_pr;
|
|
test_pr.pr_trace = 1;
|
|
test_pr.pr_trace_depth = -1;
|
|
if (test->num_functions && test->functions) {
|
|
test_pr.function_table = calloc ((test->num_functions + 2),
|
|
sizeof (bfunction_t));
|
|
memcpy (test_pr.function_table, test_functions,
|
|
2 * sizeof (bfunction_t));
|
|
memcpy (test_pr.function_table + 2, test->functions,
|
|
test->num_functions * sizeof (bfunction_t));
|
|
} else {
|
|
test_pr.function_table = test_functions;
|
|
}
|
|
|
|
pr_uint_t num_globals = test->num_globals;
|
|
num_globals += test->extra_globals + test->stack_size;
|
|
|
|
test_pr.globals_size = num_globals;
|
|
test_pr.pr_globals = Sys_Alloc (num_globals * sizeof (pr_type_t));
|
|
memcpy (test_pr.pr_globals, test->init_globals,
|
|
test->num_globals * sizeof (pr_type_t));
|
|
memset (test_pr.pr_globals + test->num_globals, 0,
|
|
test->extra_globals * sizeof (pr_type_t));
|
|
if (test->stack_size) {
|
|
pr_ptr_t stack = num_globals - test->stack_size;
|
|
test_pr.stack_bottom = stack + 4;
|
|
test_pr.globals.stack = (pr_ptr_t *) (test_pr.pr_globals + stack);
|
|
*test_pr.globals.stack = num_globals;
|
|
}
|
|
if (test->edict_area) {
|
|
test_pr.pr_edict_area = test_pr.pr_globals + test->edict_area;
|
|
}
|
|
if (test->double_time || test->float_time) {
|
|
test_pr.fields.nextthink = test->nextthink;
|
|
test_pr.fields.frame = test->frame;
|
|
test_pr.fields.think = test->think;
|
|
test_pr.globals.self = (pr_uint_t *) &test_pr.pr_globals[test->self];
|
|
if (test->double_time) {
|
|
test_pr.globals.dtime = (double *)&test_pr.pr_globals[test->dtime];
|
|
*test_pr.globals.dtime = *test->double_time;
|
|
}
|
|
if (test->float_time) {
|
|
test_pr.globals.ftime = (float *) &test_pr.pr_globals[test->ftime];
|
|
*test_pr.globals.ftime = *test->float_time;
|
|
}
|
|
}
|
|
|
|
test_progs.statements.count = test->num_statements + 1;
|
|
test_pr.pr_statements
|
|
= malloc ((test->num_statements + 1) * sizeof (dstatement_t));
|
|
memcpy (test_pr.pr_statements, test->statements,
|
|
(test->num_statements + 1) * sizeof (dstatement_t));
|
|
test_pr.pr_statements[test->num_statements] =
|
|
(dstatement_t) { OP_BREAK, 0, 0, 0 };
|
|
|
|
test_pr.pr_strings = (char *) test->strings;
|
|
test_pr.pr_stringsize = test->string_size;
|
|
}
|
|
|
|
static int
|
|
check_result (test_t *test)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (memcmp (test_pr.pr_globals, test->expect_globals,
|
|
test->num_globals * sizeof (pr_int_t)) == 0) {
|
|
ret = 1;
|
|
printf ("test #%zd: %s: OK\n", test - tests, test->desc);
|
|
} else {
|
|
printf ("test #%zd: %s: words differ\n", test - tests, test->desc);
|
|
for (pr_uint_t i = 0; i < test->num_globals; i += 4) {
|
|
pr_ivec4_t *a = (pr_ivec4_t *) &test->expect_globals[i];
|
|
pr_ivec4_t *b = (pr_ivec4_t *) &test_pr.pr_globals[i];
|
|
if (memcmp (a, b, sizeof (pr_ivec4_t))) {
|
|
printf ("-%4x { %8x, %8x, %8x, %8x }\n", i, VEC4_EXP (*a));
|
|
printf ("+%4x { %8x, %8x, %8x, %8x }\n", i, VEC4_EXP (*b));
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
run_test (test_t *test)
|
|
{
|
|
int jump_ret;
|
|
int ret = 0;
|
|
|
|
setup_test (test);
|
|
|
|
if (!(jump_ret = setjmp (jump_buffer))) {
|
|
PR_ExecuteProgram (&test_pr, 1);
|
|
printf ("returned from progs\n");
|
|
}
|
|
if (jump_ret == 1) {
|
|
ret = check_result (test);
|
|
} else {
|
|
printf ("test #%zd: %s: critical failure\n", test - tests, test->desc);
|
|
}
|
|
|
|
pr_uint_t num_globals = test->num_globals;
|
|
num_globals += test->extra_globals + test->stack_size;
|
|
if (test->num_functions && test->functions) {
|
|
free (test_pr.function_table);
|
|
}
|
|
Sys_Free (test_pr.pr_globals, num_globals * sizeof (pr_type_t));
|
|
|
|
free (test_pr.pr_statements);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
int c;
|
|
size_t i, test;
|
|
int pass = 1;
|
|
|
|
Cmd_Init_Hash ();
|
|
Cvar_Init_Hash ();
|
|
Cmd_Init ();
|
|
Cvar_Init ();
|
|
PR_Init_Cvars ();
|
|
pr_boundscheck = 1;
|
|
|
|
while ((c = getopt (argc, argv, "qvt:")) != EOF) {
|
|
switch (c) {
|
|
case 'q':
|
|
verbose--;
|
|
break;
|
|
case 'v':
|
|
verbose++;
|
|
break;
|
|
case 't':
|
|
test = atoi (optarg);
|
|
if (test < num_tests) {
|
|
test_enabled[test] = 1;
|
|
} else {
|
|
fprintf (stderr, "Bad test number (0 - %zd)\n", num_tests);
|
|
return 1;
|
|
}
|
|
break;
|
|
default:
|
|
fprintf (stderr, "-q (quiet) -v (verbose) and/or -t TEST "
|
|
"(test number)\n");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < num_tests; i++)
|
|
if (test_enabled[i])
|
|
break;
|
|
if (i == num_tests) {
|
|
for (i = 0; i < num_tests; i++)
|
|
test_enabled[i] = 1;
|
|
}
|
|
|
|
for (i = 0; i < num_tests; i++) {
|
|
if (!test_enabled[i])
|
|
continue;
|
|
pass &= run_test (&tests[i]);
|
|
}
|
|
|
|
return !pass;
|
|
}
|