[gamecode] Add a test for call and return

It currently fails for two reasons:
 - call's mode selection is incorrect (never updated from when there was
   only the one call instruction and the mode was encoded in operand c)
 - return should automatically restore the stack pointer to the value it
   had on entry to the function, thus allowing local values stored on
   the stack to be safely returned.
This commit is contained in:
Bill Currie 2022-01-17 14:26:12 +09:00
parent 56d8bdee82
commit b3909dbe4c
4 changed files with 120 additions and 1 deletions

View file

@ -1,6 +1,7 @@
libs_gamecode_tests = \
libs/gamecode/test/test-branch \
libs/gamecode/test/test-bitops \
libs/gamecode/test/test-callret \
libs/gamecode/test/test-conv0 \
libs/gamecode/test/test-conv1 \
libs/gamecode/test/test-conv2 \
@ -48,6 +49,11 @@ libs_gamecode_test_test_bitops_SOURCES= \
libs_gamecode_test_test_bitops_LDADD= $(test_gamecode_libs)
libs_gamecode_test_test_bitops_DEPENDENCIES=$(test_gamecode_libs)
libs_gamecode_test_test_callret_SOURCES= \
libs/gamecode/test/test-callret.c
libs_gamecode_test_test_callret_LDADD= $(test_gamecode_libs)
libs_gamecode_test_test_callret_DEPENDENCIES=$(test_gamecode_libs)
libs_gamecode_test_test_conv0_SOURCES= \
libs/gamecode/test/test-conv0.c
libs_gamecode_test_test_conv0_LDADD= $(test_gamecode_libs)

View file

@ -21,6 +21,9 @@ static int verbose = 0;
#define num_statements(statements) \
(sizeof (statements) / sizeof (statements[0]))
#define num_functions(functions) \
(sizeof (functions) / sizeof (functions[0]))
#define BASE(b, base) (((base) & 3) << OP_##b##_SHIFT)
#define OP(a, b, c, op) ((op) | BASE(A, a) | BASE(B, b) | BASE(C, c))
@ -46,4 +49,6 @@ typedef struct {
pr_uint_t think;
pr_uint_t nextthink;
pr_uint_t frame;
bfunction_t *functions;
pr_uint_t num_functions;
} test_t;

View file

@ -88,7 +88,16 @@ setup_test (test_t *test)
test_pr.debug_data = &test_pr;
test_pr.pr_trace = 1;
test_pr.pr_trace_depth = -1;
test_pr.function_table = test_functions;
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;
@ -170,6 +179,9 @@ run_test (test_t *test)
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);

View file

@ -0,0 +1,96 @@
#include "head.c"
#include "QF/mathlib.h"
#define sq(x) ((float)(x)*(float)(x))
#define pi_6 0x3f060a92 // pi/6
#define r3_2 0x3f5db3d7 // sqrt(3)/2
#define f1_2 0x3f000000 // 1/2
#define f1 0x3f800000 // 1
#define f2 0x40000000 // 2
#define shx 0x3f0c4020 // sinh(pi/6)
#define chx 0x3f91f354 // cosh(pi/6)
#define STK (32 * 4) // stack ptr just after globals
static pr_ivec4_t float_callret_init[32] = {
{ 0, pi_6, 2, 0},
{ f1, f2, 0, 0},
};
static pr_ivec4_t float_callret_expect[32] = {
// constants
{ 0, pi_6, 2, 0},
{ f1, f2, 0, 0},
// result
{ r3_2, f1_2, chx, shx },
};
static dstatement_t float_callret_statements[] = {
{ OP_STORE_A_1, 7, 0, STK }, // save stack pointer for check
{ OP_PUSH_A_1, 1, 0, 0 },
{ OP_CALL_B, 2, 0, 8 },
{ OP_LEA_C, STK, 4, STK }, // discard param
{ OP_SUB_I_1, 7, STK, 7 }, // check stack restored
{ OP_BREAK },
// cos_sin_cosh_sinh:
// calculate cos(x), sin(x), cosh(x) and sinh(x) simultaneously
{ OP_WITH, 2, 0, 1 }, // put params into reg 1
{ OP_LEA_C, STK, -24, STK }, // reserve 24 words on the stack
{ OP_WITH, 2, 0, 2 }, // put locals into reg 2
#define x 0 // in parameters float
#define xn 0 // in locals vec4
#define x2 4 // in locals vec4
#define ac 8 // in locals vec4
#define fa 12 // in locals vec4
#define fi 16 // in locals vec4
#define c 20 // in locals int
{ OP(2, 0, 1, OP_STORE_A_1), xn+1,0, x }, // init xn to [1, x, 0, 0]
{ OP(2, 0, 0, OP_STORE_A_1), xn, 0, 4 },
{ OP(2, 0, 2, OP_SWIZZLE_F), xn, 0x0044, xn }, // xn -> [1, x, 1, x]
{ OP(1, 1, 2, OP_MUL_F_1), x, x, x2 }, // x2 -> [x*x, 0, 0, 0]
{ OP(2, 0, 2, OP_SWIZZLE_F), x2, 0x0300, x2},//x2 -> [-x*x, -x*x, x*x, x*x]
{ OP(2, 0, 0, OP_STORE_A_1), fa, 0, 4 }, // init factorial
{ OP(2, 0, 0, OP_STORE_A_1), fa+1,0, 5 },
{ OP(2, 0, 2, OP_SWIZZLE_F), fa, 0x0044, fa }, // fa -> [1, 2, 1, 2]
{ OP(0, 0, 2, OP_SWIZZLE_F), 4, 0x0000, fi }, // init fi -> [1, 1, 1, 1]
{ OP(2, 2, 2, OP_SUB_F_4), ac, ac, ac }, // init acc (output) to 0
{ OP(0, 0, 2, OP_LEA_A), 25, 0, c }, // init count
// loop:
{ OP(2, 2, 2, OP_ADD_F_4), ac, xn, ac }, // acc += xn
{ OP(2, 2, 2, OP_MUL_F_4), xn, x2, xn }, // xn *= x2
{ OP(2, 2, 2, OP_DIV_F_4), xn, fa, xn }, // xn /= f
{ OP(2, 2, 2, OP_ADD_F_4), fa, fi, fa }, // f += inc
{ OP(2, 2, 2, OP_DIV_F_4), xn, fa, xn }, // xn /= f
{ OP(2, 2, 2, OP_ADD_F_4), fa, fi, fa }, // f += inc
{ OP(2, 0, 2, OP_LEA_C), c, -1, c }, // dec count
{ OP(0, 0, 2, OP_IFA), -7, 0, c }, // count > 0
{ OP(2, 0, 0, OP_RETURN), ac, 0, 4 },
};
#undef x
#undef xn
#undef x2
#undef ac
#undef fa
#undef fi
#undef c
static bfunction_t callret_functions[] = {
{ .first_statement = 6 },
};
test_t tests[] = {
{
.desc = "callret",
.num_globals = num_globals(float_callret_init,float_callret_expect),
.num_statements = num_statements (float_callret_statements),
.statements = float_callret_statements,
.init_globals = (pr_int_t *) float_callret_init,
.expect_globals = (pr_int_t *) float_callret_expect,
.functions = callret_functions,
.num_functions = num_functions(callret_functions),
.stack_size = 128,
},
};
#include "main.c"