/* Test CSE - make sure CSE does not use stale data */ #include /* Need to locate a variable at an absolute address, but without */ /* the linker's help. Pick an address and address space that is */ /* likely free based on cpu and compile model. If ABSADDR is */ /* left undefined, the tests that needs it will be skipped. */ #if defined(__SDCC) # if defined(__SDCC_mcs51) # if defined(__SDCC_MODEL_LARGE) # define ABSADDRSPACE __xdata # define ABSADDR 0x8000 # else # define ABSADDRSPACE __data # define ABSADDR 0x70 # endif # endif # if defined(__SDCC_ds390) # define ABSADDRSPACE __xdata # define ABSADDR 0x8000 # endif # if defined(__SDCC_s08) # define ABSADDRSPACE __xdata # define ABSADDR 0x7f00 # endif # if defined(__SDCC_z80) || defined(__SDCC_z80n) # define ABSADDRSPACE __xdata # define ABSADDR 0x7f00 # endif #endif typedef struct { unsigned char x1; unsigned char x2; unsigned char *p; } packet_t; unsigned char gx; unsigned char *px; packet_t gpacket; #ifdef ABSADDR # define apacket1 (*((ABSADDRSPACE packet_t *)ABSADDR)) ABSADDRSPACE packet_t __at ABSADDR apacket2; #endif unsigned char a1,a2,a3,a4,a5; void setgx(unsigned char x) { gx = x; } unsigned char passthrough(unsigned char i) { return i; } void zeroAndKeepAddress(unsigned char *p) { px = p; *p = 0; } void keepAddressFromPacket(packet_t *pp) { px = pp->p; } void incIndirect(void) { (*px)++; } packet_t * getaddrgpacket(void) { return &gpacket; } void incx1(packet_t *pp) { pp->x1++; } void incx2(packet_t *pp) { pp->x2++; } void incgpx1(void) { gpacket.x1++; } /* Be sneaky and define this later so that the addrtaken flag of gx is not */ /* set until after the test functions are compiled. Not important now, but */ /* this should remove the temptation to misuse reliance on addrtaken later. */ unsigned char * getaddrgx(void); /* The ASSERT macro, since it branches based on a condition, will break up a */ /* extended basic block. So to test for CSE problems within a single EBBlock */ /* we will save intermediate results in global variables a1..a5 and do all */ /* of the ASSERTs at the end of the function. For some tests it may make */ /* more sense to use local variables, but I'm intentionally trying to keep */ /* register pressure low. */ void test_FuncCall1(void) { unsigned char lx; gx = 1; setgx(2); /* Cannot CSE global variable assignment across function call */ a1 = gx; /* ASSERT(gx == 2) */ lx = gx; setgx(3); /* Cannot CSE global variable read across function call */ a2 = gx; /* ASSERT(gx == 3) */ a3 = lx; /* ASSERT(lx == 2) */ lx = gx<<1; a4 = lx; /* ASSERT(lx == 6) */ setgx(1); lx = gx<<1; /* Cannot CSE expression based on global variable across function call */ a5 = lx; /* ASSERT(lx == 2) */ ASSERT(a1 == 2); ASSERT(a2 == 3); ASSERT(a3 == 2); ASSERT(a4 == 6); ASSERT(a5 == 2); } void test_FuncCall2(void) { unsigned char lx,ly; lx = passthrough(2); ly = lx<<1; zeroAndKeepAddress(&lx); /* Cannot CSE local variable if a pointer to it was passed as parameter */ a1 = lx; /* ASSERT(lx == 0) */ incIndirect(); /* Cannot CSE local variable across function call even if its address */ /* was not a parameter if its address was taken at all */ a2 = lx; /* ASSERT(lx == 1) */ a3 = ly; /* ASSERT(ly == 4) */ ly = lx<<1; /* Cannot CSE expression based on local variable whose address was */ /* taken across function call */ a4 = ly; /* ASSERT(ly == 2) */ ASSERT(a1 == 0); ASSERT(a2 == 1); ASSERT(a3 == 4); ASSERT(a4 == 2); } void test_FuncCall3(void) { unsigned char lx,ly; lx = passthrough(2); ly = lx<<1; px = &lx; incIndirect(); /* Cannot CSE local variable across function call if address was taken at all */ a1 = lx; /* ASSERT(lx == 3) */ a2 = ly; /* ASSERT(ly == 4) */ ly = lx<<1; /* Cannot CSE expression based on local variable whose address was */ /* taken across function call */ a3 = ly; /* ASSERT(ly == 6) */ ASSERT(a1 == 3); ASSERT(a2 == 4); ASSERT(a3 == 6); } void test_FuncCall4(void) { packet_t packet1,packet2; packet1.p = &packet2.x1; keepAddressFromPacket(&packet1); packet2.x1 = 1; packet2.x2 = 3; incIndirect(); /* Cannot CSE members of local variables whose address was taken */ /* across function call */ a1 = packet2.x1; /* ASSERT(packet2.x1 == 2) */ a2 = packet2.x2; /* ASSERT(packet2.x2 == 3) */ ASSERT(a1 == 2); ASSERT(a2 == 3); } void test_FuncCall5(void) { packet_t *pp; unsigned char i; gpacket.x1 = 1; gpacket.x2 = 10; pp = getaddrgpacket(); i = pp->x1; a1 = i; /* ASSERT(i == 1) */ incx1(pp); i = pp->x1; a2 = i; /* ASSERT(i == 2) */ i = pp->x2; a3 = i; /* ASSERT(i == 10) */ incx2(pp); i = pp->x2; a4 = i; /* ASSERT(i == 11) */ ASSERT(a1 == 1); ASSERT(a2 == 2); ASSERT(a3 == 10); ASSERT(a4 == 11); } void test_FuncCall6(void) { unsigned char lx; lx = passthrough(1); gpacket.x1 = lx; incgpx1(); lx = gpacket.x1; ASSERT(lx == 2); } /* test_Struct1 through test_Struct4 are checking for cases in which */ /* operand keys are erroneously equal or unassigned, causing CSE to */ /* use the wrong substitution. */ void test_Struct1(void) { packet_t packet; unsigned char one; unsigned char ten; one = passthrough(1); ten = passthrough(10); gx = 100; packet.x1 = one; packet.x2 = ten; setgx(packet.x2); /* CSE of "apacket.x2" allowed above , but make sure it was x2 not x1*/ ASSERT(gx == 10); } void test_Struct2(void) { unsigned char one; unsigned char ten; one = passthrough(1); ten = passthrough(10); gx = 100; gpacket.x1 = one; gpacket.x2 = ten; setgx(gpacket.x2); /* CSE of "apacket.x2" allowed above , but make sure it was x2 not x1*/ ASSERT(gx == 10); } void test_Struct3(void) { #ifdef ABSADDR unsigned char one; unsigned char ten; one = passthrough(1); ten = passthrough(10); apacket1.x2 = ten; setgx(100); apacket1.x1 = one; setgx(apacket1.x2); /* CSE must not substitute x1 in place of x2 */ ASSERT(gx == 10); #endif } void test_Struct4(void) { #ifdef ABSADDR unsigned char one; unsigned char ten; one = passthrough(1); ten = passthrough(10); apacket2.x2 = ten; setgx(100); apacket2.x1 = one; setgx(apacket2.x2); /* CSE must not substitute x1 in place of x2 */ ASSERT(gx == 10); #endif } void test_Struct5(void) { #ifndef __SDCC_pdk14 // Lack of memory - see RFE #605 packet_t *pp1; packet_t *pp2; unsigned char lx; pp1 = getaddrgpacket(); pp2 = getaddrgpacket(); /* Since the declarations of pp1 and pp2 did not use the restrict */ /* qualifier, the compiler must assume they might point to the */ /* same object and so should not CSE pointer dereferences across */ /* other pointer dereferenced assingments of the same type. */ gpacket.x1 = 0; pp1->x1 = 1; lx = pp2->x1; a1 = lx; pp1->x1 = 2; lx = pp2->x1; a2 = lx; pp2->x1 = 3; lx = pp1->x1; a3 = lx; ASSERT(a1 == 1); ASSERT(a2 == 2); ASSERT(a3 == 3); #endif } void test_Global1(void) { #ifndef __SDCC_pdk14 // Lack of memory - see RFE #605 unsigned char lx; unsigned char *px; /* Since the declaration of px did not use the restrict */ /* qualifier, the compiler must assume it might point */ /* to a global variable and so should not CSE any */ /* read of or expression using a global variable. */ gx = 0; px = getaddrgx(); lx = gx<<1; a1 = lx; *px = 1; lx = gx<<1; a2 = lx; ASSERT(a1 == 0); ASSERT(a2 == 2); #endif } /* Hide taking the address of gx by defining this function last */ unsigned char *getaddrgx(void) { return &gx; }