/** This test is related to GitHub issue 895
 ** https://github.com/cc65/cc65/issues/895
 **
 ** The OptCmp8 optimization attempted to eliminate an unnecessary
 ** comparison and branch when the operands of the comparison are
 ** known to be constant at compile time.
 **
 ** For 8-bit types it worked well, but for 16-bit types it failed
 ** to generate correct code for some cases. The bug manifest as a
 ** branch on an uninitialized carry flag.
 */

#include "unittest.h"

signed char   sca, scb;
signed int    sia, sib;
signed long   sla, slb;

unsigned char uca, ucb;
unsigned int  uia, uib;
unsigned long ula, ulb;

#define OPTCMP8TEST_SINGLE(num,cmpop,asmprefix,vara,varb,b0,b1,a0,a1,typename,name) \
    typename name ## _ ## num ## (void) { \
        varb = b0; \
        asm( asmprefix ); \
        vara = a0; \
        if (vara cmpop a1) varb = b1; \
        return varb; \
    }

#define OPTCMP8TEST_VERIFY(num,b,desc,printterm,name) \
    ASSERT_AreEqual(name ## _ ## num ##(),b,printterm,"Incorrect optimization of const comparison (" #name "_" #num ": " desc ").");

/* Generates a set of comparison tests for one type and set of test values.
**     name = a name for this test (no spaces)
**     typename = the type used
**     b0 = result if comparison is false
**     b1 = result if comparison is true
**     a0 = a low value to use for the comparison tests (a0 < a1)
**     a1 = a high value to use for the comparison tests (a0 < a1)
**     vara = temporary variable of the type to be examined
**     varb = temporary variable of the type to be examined
**     printterm = printf term to display the variable type
*/
#define OPTCMP8TEST(name,typename,b0,b1,a0,a1,vara,varb,printterm) \
    OPTCMP8TEST_SINGLE(1,<,"clc",vara,varb,b0,b1,a0,a1,typename,name); \
    OPTCMP8TEST_SINGLE(2,<,"sec",vara,varb,b0,b1,a0,a1,typename,name); \
    OPTCMP8TEST_SINGLE(3,<,"clc",vara,varb,b0,b1,a1,a0,typename,name); \
    OPTCMP8TEST_SINGLE(4,<,"sec",vara,varb,b0,b1,a1,a0,typename,name); \
    OPTCMP8TEST_SINGLE(5,>,"clc",vara,varb,b0,b1,a0,a1,typename,name); \
    OPTCMP8TEST_SINGLE(6,>,"sec",vara,varb,b0,b1,a0,a1,typename,name); \
    OPTCMP8TEST_SINGLE(7,>,"clc",vara,varb,b0,b1,a1,a0,typename,name); \
    OPTCMP8TEST_SINGLE(8,>,"sec",vara,varb,b0,b1,a1,a0,typename,name); \
    OPTCMP8TEST_SINGLE(9,<=,"clc",vara,varb,b0,b1,a0,a1,typename,name); \
    OPTCMP8TEST_SINGLE(10,<=,"sec",vara,varb,b0,b1,a0,a1,typename,name); \
    OPTCMP8TEST_SINGLE(11,<=,"clc",vara,varb,b0,b1,a1,a0,typename,name); \
    OPTCMP8TEST_SINGLE(12,<=,"sec",vara,varb,b0,b1,a1,a0,typename,name); \
    OPTCMP8TEST_SINGLE(13,>=,"clc",vara,varb,b0,b1,a0,a1,typename,name); \
    OPTCMP8TEST_SINGLE(14,>=,"sec",vara,varb,b0,b1,a0,a1,typename,name); \
    OPTCMP8TEST_SINGLE(15,>=,"clc",vara,varb,b0,b1,a1,a0,typename,name); \
    OPTCMP8TEST_SINGLE(16,>=,"sec",vara,varb,b0,b1,a1,a0,typename,name); \
    OPTCMP8TEST_SINGLE(17,==,"nop",vara,varb,b0,b1,a0,a1,typename,name); \
    OPTCMP8TEST_SINGLE(18,==,"nop",vara,varb,b0,b1,a1,a1,typename,name); \
    OPTCMP8TEST_SINGLE(19,!=,"nop",vara,varb,b0,b1,a0,a1,typename,name); \
    OPTCMP8TEST_SINGLE(20,!=,"nop",vara,varb,b0,b1,a1,a1,typename,name); \
    void name ## _ ## test(void) { \
        OPTCMP8TEST_VERIFY(1,b1,"low < high, clc",printterm,name); \
        OPTCMP8TEST_VERIFY(2,b1,"low < high, sec",printterm,name); \
        OPTCMP8TEST_VERIFY(3,b0,"high < low, clc",printterm,name); \
        OPTCMP8TEST_VERIFY(4,b0,"high < low, sec",printterm,name); \
        OPTCMP8TEST_VERIFY(5,b0,"low > high, clc",printterm,name); \
        OPTCMP8TEST_VERIFY(6,b0,"low > high, sec",printterm,name); \
        OPTCMP8TEST_VERIFY(7,b1,"high > low, clc",printterm,name); \
        OPTCMP8TEST_VERIFY(8,b1,"high > low, sec",printterm,name); \
        OPTCMP8TEST_VERIFY(9,b1,"low <= high, clc",printterm,name); \
        OPTCMP8TEST_VERIFY(10,b1,"low <= high, sec",printterm,name); \
        OPTCMP8TEST_VERIFY(11,b0,"high <= low, clc",printterm,name); \
        OPTCMP8TEST_VERIFY(12,b0,"high <= low, sec",printterm,name); \
        OPTCMP8TEST_VERIFY(13,b0,"low >= high, clc",printterm,name); \
        OPTCMP8TEST_VERIFY(14,b0,"low >= high, sec",printterm,name); \
        OPTCMP8TEST_VERIFY(15,b1,"high >= low, clc",printterm,name); \
        OPTCMP8TEST_VERIFY(16,b1,"high >= low, sec",printterm,name); \
        OPTCMP8TEST_VERIFY(17,b0,"low == high, nop",printterm,name); \
        OPTCMP8TEST_VERIFY(18,b1,"high == high, nop",printterm,name); \
        OPTCMP8TEST_VERIFY(19,b1,"low != high, nop",printterm,name); \
        OPTCMP8TEST_VERIFY(20,b0,"high != high, nop",printterm,name); \
    }

OPTCMP8TEST(signed_char,signed char,-20,5,60,100,sca,scb,"%d");
OPTCMP8TEST(unsigned_char,unsigned char,20,5,60,100,uca,ucb,"%u");
OPTCMP8TEST(signed_int,signed int,-2000,50,600,1000,sia,sib,"%d");
OPTCMP8TEST(unsigned_int,unsigned int,2000,50,600,1000,uia,uib,"%u");
OPTCMP8TEST(signed_long,signed long,-200000L,5000L,60000L,100000L,sla,slb,"%d");
OPTCMP8TEST(unsigned_long,unsigned long,200000UL,5000UL,60000UL,100000UL,ula,ulb,"%u");

TEST
{
    signed_char_test();
    unsigned_char_test();
    signed_int_test();
    unsigned_int_test();
    signed_long_test();
    unsigned_long_test();
}
ENDTEST
