From b941d6f1e41e3d4f0a65df7020b6c5c450f54c57 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 24 Feb 2024 20:02:13 +0100 Subject: [PATCH] new comparison tests --- .../src/prog8/codegen/cpu6502/IfElseAsmGen.kt | 2 +- compiler/test/comparisons/Makefile | 1 + compiler/test/comparisons/make_cmp_tests.py | 390 ++++++++++++++++++ docs/source/todo.rst | 20 +- 4 files changed, 409 insertions(+), 4 deletions(-) create mode 100644 compiler/test/comparisons/make_cmp_tests.py diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt index ae77f75e7..df945ee1f 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt @@ -755,7 +755,7 @@ _jump jmp ($asmLabel) ora P8ZP_SCRATCH_REG beq + bcc + - jmp $(asmLabel) + jmp ($asmLabel) +""") } else { asmgen.out(""" diff --git a/compiler/test/comparisons/Makefile b/compiler/test/comparisons/Makefile index e657c5f8c..f88c3ae6c 100644 --- a/compiler/test/comparisons/Makefile +++ b/compiler/test/comparisons/Makefile @@ -9,6 +9,7 @@ test: clean generate test_prgs generate: python make_eq_tests.py + python make_cmp_tests.py p8compile -target cx16 -sourcelines *.p8 >/dev/null test_prgs: diff --git a/compiler/test/comparisons/make_cmp_tests.py b/compiler/test/comparisons/make_cmp_tests.py new file mode 100644 index 000000000..1a0d6e831 --- /dev/null +++ b/compiler/test/comparisons/make_cmp_tests.py @@ -0,0 +1,390 @@ +# generates various Prog8 files with a large amount of number comparison tests, +# using various forms of the if statement (because these forms have their own code gen paths) + +import sys + +fail_index = 0 + + +class C: + def __init__(self, short, long, operator, compare): + self.short=short + self.long=long + self.operator=operator + self.compare=compare + + +def header(dt, comparison: C): + print(f""" +%import textio +%import floats +%import test_stack +%zeropage dontuse +%option no_sysinit + +main {{ + ubyte success = 0 + str datatype = "{dt}" + uword @shared comparison + + sub start() {{ + txt.print("\\n{comparison.long} tests for: ") + txt.print(datatype) + txt.nl() + test_stack.test() + txt.print("\\n{comparison.operator}number: ") + test_cmp_number() + txt.print("\\n{comparison.operator}var: ") + test_cmp_var() + txt.print("\\n{comparison.operator}array[]: ") + test_cmp_array() + txt.print("\\n{comparison.operator}expr: ") + test_cmp_expr() + test_stack.test() + }} + + sub verify_success(ubyte expected) {{ + if success==expected {{ + txt.print("ok") + }} else {{ + txt.print(" **failed** ") + txt.print_ub(success) + txt.print(" success, expected ") + txt.print_ub(expected) + }} + }} + + sub fail_byte(uword idx, byte v1, byte v2) {{ + txt.print(" **fail#") + txt.print_uw(idx) + txt.chrout(':') + txt.print_b(v1) + txt.chrout(',') + txt.print_b(v2) + txt.print(" **") + }} + + sub fail_ubyte(uword idx, ubyte v1, ubyte v2) {{ + txt.print(" **fail#") + txt.print_uw(idx) + txt.chrout(':') + txt.print_ub(v1) + txt.chrout(',') + txt.print_ub(v2) + txt.print(" **") + }} + + sub fail_word(uword idx, word v1, word v2) {{ + txt.print(" **fail#") + txt.print_uw(idx) + txt.chrout(':') + txt.print_w(v1) + txt.chrout(',') + txt.print_w(v2) + txt.print(" **") + }} + + sub fail_uword(uword idx, uword v1, uword v2) {{ + txt.print(" **fail#") + txt.print_uw(idx) + txt.chrout(':') + txt.print_uw(v1) + txt.chrout(',') + txt.print_uw(v2) + txt.print(" **") + }} + + sub fail_float(uword idx, float v1, float v2) {{ + txt.print(" **fail#") + txt.print_uw(idx) + txt.chrout(':') + floats.print(v1) + txt.chrout(',') + floats.print(v2) + txt.print(" **") + }} + +""") + + +def tc(value): + if value < 0x8000: + return value + else: + return -(65536 - value) + + +testnumbers = { + "byte": [-100, 0, 100], + "ubyte": [0, 1, 255], + "word": [tc(0xaabb), 0, 0x00aa, 0x7700, 0x7fff], + "uword": [0, 1, 0x7700, 0xffff], + "float": [0.0, 1234.56] +} + + +def make_test_number(datatype, comparison: C): + numbers = testnumbers[datatype] + print(" sub test_cmp_number() {") + print(f""" {datatype} @shared x + success = 0""") + expected = 0 + test_index = 0 + global fail_index + for x in numbers: + print(f" x={x}") + for value in numbers: + result = comparison.compare(x, value) + comp = comparison.operator + test_index += 1 + if result: + expected += 4 # there are 4 test types for every successful combo + true_action1 = "success++" + true_action2 = "success++" + true_action3 = "success++" + true_action4 = "success++" + else: + fail_index += 1 + true_action1 = f"fail_{datatype}({fail_index},x,{value})" + fail_index += 1 + true_action2 = f"fail_{datatype}({fail_index},x,{value})" + fail_index += 1 + true_action3 = f"fail_{datatype}({fail_index},x,{value})" + fail_index += 1 + true_action4 = f"fail_{datatype}({fail_index},x,{value})" + print(f""" ; direct jump + if x{comp}{value} + goto lbl{test_index}a + goto skip{test_index}a +lbl{test_index}a: {true_action1} +skip{test_index}a: + ; indirect jump + cx16.r3 = &lbl{test_index}b + if x{comp}{value} + goto cx16.r3 + goto skip{test_index}b +lbl{test_index}b: {true_action2} +skip{test_index}b: + ; no else + if x{comp}{value} + {true_action3} + + ; with else + if x{comp}{value} + {true_action4} + else + cx16.r0L++ +""") + print(f" verify_success({expected})\n}}") + + +def make_test_var(datatype, comparison: C): + numbers = testnumbers[datatype] + print(" sub test_cmp_var() {") + print(f""" {datatype} @shared x, value + success = 0""") + expected = 0 + test_index = 0 + global fail_index + for x in numbers: + print(f" x={x}") + for value in numbers: + if value == 0: + continue # 0 already tested separately + print(f" value={value}") + result = comparison.compare(x, value) + comp = comparison.operator + test_index += 1 + if result: + expected += 4 # there are 4 test types for every successful combo + true_action1 = "success++" + true_action2 = "success++" + true_action3 = "success++" + true_action4 = "success++" + else: + fail_index += 1 + true_action1 = f"fail_{datatype}({fail_index},x,value)" + fail_index += 1 + true_action2 = f"fail_{datatype}({fail_index},x,value)" + fail_index += 1 + true_action3 = f"fail_{datatype}({fail_index},x,value)" + fail_index += 1 + true_action4 = f"fail_{datatype}({fail_index},x,value)" + print(f""" ; direct jump + if x{comp}value + goto lbl{test_index}a + goto skip{test_index}a +lbl{test_index}a: {true_action1} +skip{test_index}a: + ; indirect jump + cx16.r3 = &lbl{test_index}b + if x{comp}value + goto cx16.r3 + goto skip{test_index}b +lbl{test_index}b: {true_action2} +skip{test_index}b: + ; no else + if x{comp}value + {true_action3} + + ; with else + if x{comp}value + {true_action4} + else + cx16.r0L++ +""") + print(f" verify_success({expected})\n}}") + + +def make_test_array(datatype, comparison: C): + numbers = testnumbers[datatype] + print(" sub test_cmp_array() {") + print(f""" {datatype} @shared x + {datatype}[] values = [0, 0] + success = 0""") + expected = 0 + test_index = 0 + global fail_index + for x in numbers: + print(f" x={x}") + for value in numbers: + if value == 0: + continue # 0 already tested separately + print(f" values[1]={value}") + result = comparison.compare(x, value) + comp = comparison.operator + test_index += 1 + if result: + expected += 4 # there are 4 test types for every successful combo + true_action1 = "success++" + true_action2 = "success++" + true_action3 = "success++" + true_action4 = "success++" + else: + fail_index += 1 + true_action1 = f"fail_{datatype}({fail_index},x,{value})" + fail_index += 1 + true_action2 = f"fail_{datatype}({fail_index},x,{value})" + fail_index += 1 + true_action3 = f"fail_{datatype}({fail_index},x,{value})" + fail_index += 1 + true_action4 = f"fail_{datatype}({fail_index},x,{value})" + print(f""" ; direct jump + if x{comp}values[1] + goto lbl{test_index}a + goto skip{test_index}a +lbl{test_index}a: {true_action1} +skip{test_index}a: + ; indirect jump + cx16.r3 = &lbl{test_index}b + if x{comp}values[1] + goto cx16.r3 + goto skip{test_index}b +lbl{test_index}b: {true_action2} +skip{test_index}b: + ; no else + if x{comp}values[1] + {true_action3} + + ; with else + if x{comp}values[1] + {true_action4} + else + cx16.r0L++ +""") + print(f" verify_success({expected})\n}}") + + +def make_test_expr(datatype, comparison: C): + numbers = testnumbers[datatype] + print(" sub test_cmp_expr() {") + print(f""" {datatype} @shared x + cx16.r4 = 1 + cx16.r5 = 1 + float @shared f4 = 1.0 + float @shared f5 = 1.0 + success = 0""") + expected = 0 + test_index = 0 + global fail_index + for x in numbers: + print(f" x={x}") + for value in numbers: + if value == 0: + continue # 0 already tested separately + if datatype=="byte": + expr = f"cx16.r4sL+{value}-cx16.r5sL" + elif datatype=="ubyte": + expr = f"cx16.r4L+{value}-cx16.r5L" + elif datatype=="word": + expr = f"cx16.r4s+{value}-cx16.r5s" + elif datatype=="uword": + expr = f"cx16.r4+{value}-cx16.r5" + elif datatype=="float": + expr = f"f4+{value}-f5" + result = comparison.compare(x, value) + comp = comparison.operator + test_index += 1 + if result: + expected += 4 # there are 4 test types for every successful combo + true_action1 = "success++" + true_action2 = "success++" + true_action3 = "success++" + true_action4 = "success++" + else: + fail_index += 1 + true_action1 = f"fail_{datatype}({fail_index},x,{value})" + fail_index += 1 + true_action2 = f"fail_{datatype}({fail_index},x,{value})" + fail_index += 1 + true_action3 = f"fail_{datatype}({fail_index},x,{value})" + fail_index += 1 + true_action4 = f"fail_{datatype}({fail_index},x,{value})" + print(f""" ; direct jump + if x{comp}{expr} + goto lbl{test_index}a + goto skip{test_index}a +lbl{test_index}a: {true_action1} +skip{test_index}a: + ; indirect jump + cx16.r3 = &lbl{test_index}b + if x{comp}{expr} + goto cx16.r3 + goto skip{test_index}b +lbl{test_index}b: {true_action2} +skip{test_index}b: + ; no else + if x{comp}{expr} + {true_action3} + + ; with else + if x{comp}{expr} + {true_action4} + else + cx16.r0L++ +""") + print(f" verify_success({expected})\n}}") + + +def generate(datatype, comparison: C): + global fail_index + fail_index = 0 + header(datatype, comparison) + make_test_number(datatype, comparison) + make_test_var(datatype, comparison) + make_test_array(datatype, comparison) + make_test_expr(datatype, comparison) + print("\n}\n") + + +if __name__ == '__main__': + comparisons = [ + C("lt", "less-than", "<", lambda x,y: x < y), + C("lte", "less-equal", "<=", lambda x,y: x <= y), + C("gt", "greater-than", ">", lambda x,y: x > y), + C("gte", "greater-equal", ">=", lambda x,y: x >= y), + ] + for comparison in comparisons: + for dt in ["ubyte", "uword", "byte", "word", "float"]: + sys.stdout = open(f"test_{dt}_{comparison.short}.p8", "wt") + generate(dt, comparison) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 53ad05dd0..2b206cf91 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,10 +1,24 @@ TODO ==== -retest all comparisons in if statements (byte, word, signed and unsigned) + all comparison assignments. Against 0 and something else as 0. -with jump, indirect jump, no else block, and both if+else blocks. +fix IR codegen error: type FLOAT invalid for SGTS + +add tests for comparison that do an assignment rather than an if. + +fix ifelse codegens: +byte >= (all 4 paths) +byte > (all 4 paths) +byte <= (all 4 paths) +byte < (all 4 paths) +uword >= (all 4 paths) +uword > (var and array) +uword <= (all 4 paths) +uword < (var) +word >= (all 4 paths) +word > (var, array) +word <= (var) +word < (var) -fix "wordGreaterValue" improve code size for "wordGreaterValue" et al.