Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hwtest for PPC flags with dependent conditional branches #28

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cputest/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
add_hwtest(cputest fctiwz fctiwz.cpp)
add_hwtest(cputest flags_bc flags_bc.cpp)
add_hwtest(cputest frsp frsp.cpp)
add_hwtest(cputest load load.cpp)
add_hwtest(cputest reciprocal reciprocal.cpp)
Expand Down
333 changes: 333 additions & 0 deletions cputest/flags_bc.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,333 @@
#include <gctypes.h>
#include <wiiuse/wpad.h>
#include "hwtests.h"

#define ARRAY_LENGTH(array) (sizeof((array))/sizeof((array)[0]))

#define TEST_LOOP(f, tests) \
do { \
START_TEST(); \
for (size_t i = 0; i < ARRAY_LENGTH(tests); i++) \
{ \
f(tests[i]); \
} \
END_TEST(); \
} while(0)

#define TEST_ASM(asm_code) \
asm \
( \
"li %0, 0;" \
asm_code \
"b unmet_%=;" \
"met_%=:" \
"li %0, 1;" \
"unmet_%=:" \
: "=&r"(met) \
: "r"(a), "r"(b) \
: "cc", "r0" \
);

#define TEST_LT(inst, inst_code) \
TEST_ASM(inst_code "; blt met_%=;") \
DO_TEST(met == test.lt, inst " Fail: %08x %08x: LT == %d", a, b, met);

#define TEST_NLT(inst, inst_code) \
TEST_ASM(inst_code "; bge met_%=;") \
DO_TEST(met != test.lt, inst " Fail: %08x %08x: !LT == %d", a, b, met);

#define TEST_GT(inst, inst_code) \
TEST_ASM(inst_code "; bgt met_%=;") \
DO_TEST(met == test.gt, inst " Fail: %08x %08x: GT == %d", a, b, met);

#define TEST_NGT(inst, inst_code) \
TEST_ASM(inst_code "; ble met_%=;") \
DO_TEST(met != test.gt, inst " Fail: %08x %08x: !GT == %d", a, b, met);

#define TEST_EQ(inst, inst_code) \
TEST_ASM(inst_code "; beq met_%=;") \
DO_TEST(met == test.eq, inst " Fail: %08x %08x: EQ == %d", a, b, met);

#define TEST_NEQ(inst, inst_code) \
TEST_ASM(inst_code "; bne met_%=;") \
DO_TEST(met != test.eq, inst " Fail: %08x %08x: !EQ == %d", a, b, met);

// mcrxr cr0 twice to clear SO/OV bits in XER and CR0
#define TEST_SO(inst, inst_code) \
TEST_ASM("mcrxr cr0; mcrxr cr0; " inst_code "; bc 12, 3, met_%=;") \
DO_TEST(met == test.so, inst " Fail: %08x %08x: SO == %d", a, b, met);

#define TEST_NSO(inst, inst_code) \
TEST_ASM("mcrxr cr0; mcrxr cr0; " inst_code "; bc 4, 3, met_%=;") \
DO_TEST(met != test.so, inst " Fail: %08x %08x: !SO == %d", a, b, met);

#define TEST_FLAGS(inst, inst_code) \
TEST_LT(inst, inst_code) \
TEST_NLT(inst, inst_code) \
TEST_GT(inst, inst_code) \
TEST_NGT(inst, inst_code) \
TEST_EQ(inst, inst_code) \
TEST_NEQ(inst, inst_code)

// Additionally tests SO (Summary Overflow)
#define TEST_FLAGS_O(inst, inst_code) \
TEST_FLAGS(inst, inst_code) \
TEST_SO(inst, inst_code) \
TEST_NSO(inst, inst_code)


// Duplicate everything with minor modifications, just for addic
#define TEST_ASM_ADDIC(asm_code) \
asm \
( \
"li %0, 0;" \
asm_code \
"b unmet_%=;" \
"met_%=:" \
"li %0, 1;" \
"unmet_%=:" \
: "=&r"(met) \
: "r"(a) \
: "cc", "r0" \
);

#define TEST_LT_ADDIC(inst, inst_code, input) \
TEST_ASM_ADDIC(inst_code "; blt met_%=;") \
DO_TEST(met == test->lt, inst " Fail: %08x %s: LT == %d", a, input, met);

#define TEST_NLT_ADDIC(inst, inst_code, input) \
TEST_ASM_ADDIC(inst_code "; bge met_%=;") \
DO_TEST(met != test->lt, inst " Fail: %08x %s: !LT == %d", a, input, met);

#define TEST_GT_ADDIC(inst, inst_code, input) \
TEST_ASM_ADDIC(inst_code "; bgt met_%=;") \
DO_TEST(met == test->gt, inst " Fail: %08x %s: GT == %d", a, input, met);

#define TEST_NGT_ADDIC(inst, inst_code, input) \
TEST_ASM_ADDIC(inst_code "; ble met_%=;") \
DO_TEST(met != test->gt, inst " Fail: %08x %s: !GT == %d", a, input, met);

#define TEST_EQ_ADDIC(inst, inst_code, input) \
TEST_ASM_ADDIC(inst_code "; beq met_%=;") \
DO_TEST(met == test->eq, inst " Fail: %08x %s: EQ == %d", a, input, met);

#define TEST_NEQ_ADDIC(inst, inst_code, input) \
TEST_ASM_ADDIC(inst_code "; bne met_%=;") \
DO_TEST(met != test->eq, inst " Fail: %08x %s: !EQ == %d", a, input, met);

// mcrxr cr0 twice to clear SO/OV bits in XER and CR0
#define TEST_SO_ADDIC(inst, inst_code, input) \
TEST_ASM_ADDIC("mcrxr cr0; mcrxr cr0; " inst_code "; bc 12, 3, met_%=;") \
DO_TEST(met == test->so, inst " Fail: %08x %s: SO == %d", a, input, met);

#define TEST_NSO_ADDIC(inst, inst_code, input) \
TEST_ASM_ADDIC("mcrxr cr0; mcrxr cr0; " inst_code "; bc 4, 3, met_%=;") \
DO_TEST(met != test->so, inst " Fail: %08x %s: !SO == %d", a, input, met);

#define TEST_FLAGS_ADDIC(inst, inst_code, input) \
TEST_LT_ADDIC(inst, inst_code, input) \
TEST_NLT_ADDIC(inst, inst_code, input) \
TEST_GT_ADDIC(inst, inst_code, input) \
TEST_NGT_ADDIC(inst, inst_code, input) \
TEST_EQ_ADDIC(inst, inst_code, input) \
TEST_NEQ_ADDIC(inst, inst_code, input) \
TEST_SO_ADDIC(inst, inst_code, input) \
TEST_NSO_ADDIC(inst, inst_code, input)

#define TEST_ADDIC(input) \
a = test->a; \
TEST_FLAGS_ADDIC("addic.", "addic. %%r0, %1, " input, input) \
TEST_FLAGS_ADDIC("addic. rx, rx, imm", "addic. %1, %1, " input, input) \

typedef struct
{
u32 a;
u32 b;
bool lt;
bool gt;
bool eq;
bool so;
} flag_test;

flag_test subtraction_tests[] = {
// expectations
// input a input b LT GT EQ SO
{0x00000000, 0x00000000, false, false, true , false},

{0x00000001, 0x00000001, false, false, true , false},
{0x00000008, 0x00000008, false, false, true , false},
{0x00000001, 0x00000008, false, true , false, false},
{0x00000008, 0x00000001, true , false, false, false},

{0x10000000, 0x10000000, false, false, true , false},
{0x80000000, 0x80000000, false, false, true , false},
{0x10000000, 0x80000000, false, true , false, true },
{0x80000000, 0x10000000, true , false, false, true },
{0x80000000, 0x00000000, true , false, false, true },
{0x00000000, 0x80000000, true , false, false, false},
};

flag_test cmp_tests[] = {
// expectations
// input a input b LT GT EQ SO
{0x00000000, 0x00000000, false, false, true , false},

{0x00000001, 0x00000001, false, false, true , false},
{0x00000008, 0x00000008, false, false, true , false},
{0x00000001, 0x00000008, true , false, false, false},
{0x00000008, 0x00000001, false, true , false, false},

{0x10000000, 0x10000000, false, false, true , false},
{0x80000000, 0x80000000, false, false, true , false},
{0x10000000, 0x80000000, false, true , false, false},
{0x80000000, 0x10000000, true , false, false, false},
{0x80000000, 0x00000000, true , false, false, false},
{0x00000000, 0x80000000, false, true , false, false},
};

flag_test addition_tests[] = {
// expectations
// input a input b LT GT EQ SO
{0x00000000, 0x00000000, false, false, true , false},

{0x00000001, 0x00000001, false, true , false, false},

{0x10000000, 0x10000000, false, true , false, false},
{0x80000000, 0x80000000, false, false, true , true },
{0x10000000, 0x80000000, true , false, false, false},
{0x80000000, 0x10000000, true , false, false, false},
{0x70000000, 0x10000000, true , false, false, true },
{0x10000000, 0x70000000, true , false, false, true },
{0x80000000, 0x00000000, true , false, false, false},
{0x00000000, 0x80000000, true , false, false, false},

{0xF7000000, 0x10000000, false, true , false, false},
{0x10000000, 0xF7000000, false, true , false, false},
};

// Input b (imm) is supplied via macro
flag_test addic_tests[] = {
// expectations
// input a input b LT GT EQ SO
{0x00000000, 0x00000000, false, false, true , false},

{0xFFFFFFFF, 0x00000001, false, false, true , false},
{0xFFFF0000, 0x00000001, true , false, false, false},

{0x80000000, 0x0000FFFF, false, true , false, false},

{0x00000001, 0x0000FFFF, false, false, true , false},
};

static void AddTest(const flag_test& test)
{
u32 a = test.a;
u32 b = test.b;
u32 met = 0;

TEST_FLAGS("add.", "add. %%r0, %1, %2")

// Test for rD == rA
TEST_FLAGS("add. rx, rx, ry", "add. %1, %1, %2")

// Test with overflow
TEST_FLAGS_O("addo.", "addo. %%r0, %1, %2")

// Untested code path: immediates
}

static void AddcTest(const flag_test& test)
{
u32 a = test.a;
u32 b = test.b;
u32 met = 0;

TEST_FLAGS("addc.", "addc. %%r0, %1, %2")

// Test for rD == rA
TEST_FLAGS("addc. rx, rx, ry", "addc. %1, %1, %2")

// Test with overflow
TEST_FLAGS_O("addco.", "addco. %%r0, %1, %2")
}

// Does not test carry input!
static void AddeTest(const flag_test& test)
{
u32 a = test.a;
u32 b = test.b;
u32 met = 0;

TEST_FLAGS("adde.", "mcrxr cr0; adde. %%r0, %1, %2")

// Test for rD == rA
TEST_FLAGS("adde. rx, rx, ry", "mcrxr cr0; adde. %1, %1, %2")

// Test with overflow
TEST_FLAGS_O("addeo.", "mcrxr cr0; addeo. %%r0, %1, %2")
}

static void CmpTest(const flag_test& test)
{
u32 a = test.a;
u32 b = test.b;
u32 met = 0;

TEST_FLAGS_O("cmp", "cmp cr0, %1, %2")
}

static void SubfTest(const flag_test& test)
{
u32 a = test.a;
u32 b = test.b;
u32 met = 0;

TEST_FLAGS_O("subfo.", "subfo. %%r0, %1, %2")
}

static void AddicTest()
{
START_TEST();

// Set in macro
u32 a = 0;
u32 met = 0;

flag_test* test = &addic_tests[0];
TEST_ADDIC("0")

test = &addic_tests[1];
TEST_ADDIC("1")

test = &addic_tests[2];
TEST_ADDIC("1")

test = &addic_tests[3];
TEST_ADDIC("-1")

test = &addic_tests[4];
TEST_ADDIC("-1")

END_TEST();
}

int main()
{
network_init();
WPAD_Init();

TEST_LOOP(AddTest, addition_tests);
TEST_LOOP(AddcTest, addition_tests);
TEST_LOOP(AddeTest, addition_tests);
AddicTest();
// addme, addze left out, part of arithXex like adde
// boolX left out
TEST_LOOP(CmpTest, cmp_tests);
// cntlzw, divw, divwu, mulXx left out
TEST_LOOP(SubfTest, subtraction_tests);

network_printf("Shutting down...\n");
network_shutdown();

return 0;
}