1
0
mirror of https://github.com/cc65/cc65.git synced 2024-11-19 06:31:31 +00:00

Generate info about zp register usage

git-svn-id: svn://svn.cc65.org/cc65/trunk@964 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
cuz 2001-09-21 21:52:50 +00:00
parent 5532a80beb
commit a6aa5512d5
7 changed files with 256 additions and 140 deletions

View File

@ -40,6 +40,7 @@
#include "chartype.h"
#include "check.h"
#include "xmalloc.h"
#include "xsprintf.h"
/* cc65 */
#include "codeinfo.h"
@ -136,6 +137,8 @@ static int NumArg (const char* Arg, unsigned long* Num)
static void SetUseChgInfo (CodeEntry* E, const OPCDesc* D)
/* Set the Use and Chg in E */
{
unsigned short Use;
/* If this is a subroutine call, or a jump to an external function,
* lookup the information about this function and use it. The jump itself
* does not change any registers, so we don't need to use the data from D.
@ -145,10 +148,45 @@ static void SetUseChgInfo (CodeEntry* E, const OPCDesc* D)
GetFuncInfo (E->Arg, &E->Use, &E->Chg);
} else {
/* Some other instruction. Use the values from the opcode description
* plus addressing mode info
* plus addressing mode info.
*/
E->Use = D->Use | GetAMUseInfo (E->AM);
E->Chg = D->Chg;
/* Check for special zero page registers used */
switch (E->AM) {
case AM65_ZP:
case AM65_ABS:
/* Be conservative: */
case AM65_ZPX:
case AM65_ABSX:
case AM65_ABSY:
if (IsZPName (E->Arg, &Use) && Use != REG_NONE) {
if (E->OPC == OP65_INC || E->OPC == OP65_DEC) {
E->Chg |= Use;
E->Use |= Use;
} else if ((E->Info & OF_STORE) != 0) {
E->Chg |= Use;
} else {
E->Use |= Use;
}
}
break;
case AM65_ZPX_IND:
case AM65_ZP_INDY:
case AM65_ZP_IND:
if (IsZPName (E->Arg, &Use) && Use != REG_NONE) {
/* These addressing modes will never change the zp loc */
E->Use |= Use;
}
break;
default:
/* Keep gcc silent */
break;
}
}
}
@ -265,14 +303,35 @@ void CE_MoveLabel (CodeLabel* L, CodeEntry* E)
void CE_SetArg (CodeEntry* E, const char* Arg)
/* Set a new argument for the given code entry. An old string is deleted. */
void CE_SetNumArg (CodeEntry* E, long Num)
/* Set a new numeric argument for the given code entry that must already
* have a numeric argument.
*/
{
char Buf[16];
/* Check that the entry has a numerical argument */
CHECK (E->Flags & CEF_NUMARG);
/* Make the new argument string */
if (E->Size == 2) {
Num &= 0xFF;
xsprintf (Buf, sizeof (Buf), "$%02X", (unsigned) Num);
} else if (E->Size == 3) {
Num &= 0xFFFF;
xsprintf (Buf, sizeof (Buf), "$%04X", (unsigned) Num);
} else {
Internal ("Invalid instruction size in CE_SetNumArg");
}
/* Free the old argument */
FreeArg (E->Arg);
/* Assign the new one */
E->Arg = GetArgCopy (Arg);
E->Arg = GetArgCopy (Buf);
/* Use the new numerical value */
E->Num = Num;
}
@ -655,6 +714,48 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs)
static char* RegInfoDesc (unsigned U, char* Buf)
/* Return a string containing register info */
{
Buf[0] = '\0';
if (U & REG_SREG) {
strcat (Buf, "E");
}
if (U & REG_A) {
strcat (Buf, "A");
}
if (U & REG_X) {
strcat (Buf, "X");
}
if (U & REG_Y) {
strcat (Buf, "Y");
}
if (U & REG_TMP1) {
strcat (Buf, "T1");
}
if (U & REG_TMP2) {
strcat (Buf, "T2");
}
if (U & REG_TMP3) {
strcat (Buf, "T3");
}
if (U & REG_PTR1) {
strcat (Buf, "P1");
}
if (U & REG_PTR2) {
strcat (Buf, "P2");
}
if (U & REG_PTR3) {
strcat (Buf, "P3");
}
if (U & REG_PTR4) {
strcat (Buf, "P4");
}
return Buf;
}
void CE_Output (const CodeEntry* E, FILE* F)
/* Output the code entry to a file */
{
@ -699,7 +800,7 @@ void CE_Output (const CodeEntry* E, FILE* F)
case AM65_ABS:
/* zeropage and absolute */
Chars += fprintf (F, "%*s%s", 9-Chars, "", E->Arg);
break;
break;
case AM65_ZPX:
case AM65_ABSX:
@ -740,16 +841,14 @@ void CE_Output (const CodeEntry* E, FILE* F)
/* Print usage info if requested by the debugging flag */
if (Debug) {
fprintf (F,
"%*s; USE: %c%c%c CHG: %c%c%c SIZE: %u\n",
30-Chars, "",
(E->Use & REG_A)? 'A' : '_',
(E->Use & REG_X)? 'X' : '_',
(E->Use & REG_Y)? 'Y' : '_',
(E->Chg & REG_A)? 'A' : '_',
(E->Chg & REG_X)? 'X' : '_',
(E->Chg & REG_Y)? 'Y' : '_',
E->Size);
char Use [128];
char Chg [128];
fprintf (F,
"%*s; USE: %-18s CHG: %-18s SIZE: %u\n",
30-Chars, "",
RegInfoDesc (E->Use, Use),
RegInfoDesc (E->Chg, Chg),
E->Size);
} else {
/* Terminate the line */
fprintf (F, "\n");

View File

@ -169,8 +169,10 @@ INLINE void CE_ResetMark (CodeEntry* E)
# define CE_ResetMark(E) ((E)->Flags &= ~CEF_USERMARK)
#endif
void CE_SetArg (CodeEntry* E, const char* Arg);
/* Set a new argument for the given code entry. An old string is deleted. */
void CE_SetNumArg (CodeEntry* E, long Num);
/* Set a new numeric argument for the given code entry that must already
* have a numeric argument.
*/
int CE_KnownImm (const CodeEntry* E);
/* Return true if the argument of E is a known immediate value */

View File

@ -62,117 +62,123 @@ typedef struct FuncInfo FuncInfo;
struct FuncInfo {
const char* Name; /* Function name */
unsigned short Use; /* Register usage */
unsigned short Chg; /* Changed/destroyed registers */
unsigned short Chg; /* Changed/destroyed registers */
};
static const FuncInfo FuncInfoTable[] = {
{ "addysp", REG_Y, REG_NONE },
{ "aslax1", REG_AX, REG_AX },
{ "aslax2", REG_AX, REG_AX },
{ "aslax3", REG_AX, REG_AX },
{ "aslax4", REG_AX, REG_AX },
{ "bnega", REG_A, REG_AX },
{ "bnegax", REG_AX, REG_AX },
{ "bnegeax", REG_AX, REG_AX },
{ "booleq", REG_NONE, REG_AX },
{ "boolge", REG_NONE, REG_AX },
{ "boolgt", REG_NONE, REG_AX },
{ "boolle", REG_NONE, REG_AX },
{ "boollt", REG_NONE, REG_AX },
{ "boolne", REG_NONE, REG_AX },
{ "booluge", REG_NONE, REG_AX },
{ "boolugt", REG_NONE, REG_AX },
{ "boolule", REG_NONE, REG_AX },
{ "boolult", REG_NONE, REG_AX },
{ "complax", REG_AX, REG_AX },
{ "decax1", REG_AX, REG_AX },
{ "decax2", REG_AX, REG_AX },
{ "decax3", REG_AX, REG_AX },
{ "decax4", REG_AX, REG_AX },
{ "decax5", REG_AX, REG_AX },
{ "decax6", REG_AX, REG_AX },
{ "decax7", REG_AX, REG_AX },
{ "decax8", REG_AX, REG_AX },
{ "decaxy", REG_AXY, REG_AX },
{ "decsp1", REG_NONE, REG_Y },
{ "decsp2", REG_NONE, REG_A },
{ "decsp3", REG_NONE, REG_A },
{ "decsp4", REG_NONE, REG_A },
{ "decsp5", REG_NONE, REG_A },
{ "decsp6", REG_NONE, REG_A },
{ "decsp7", REG_NONE, REG_A },
{ "decsp8", REG_NONE, REG_A },
{ "incax1", REG_AX, REG_AX },
{ "incax2", REG_AX, REG_AX },
{ "incsp1", REG_NONE, REG_NONE },
{ "incsp2", REG_NONE, REG_Y },
{ "incsp3", REG_NONE, REG_Y },
{ "incsp4", REG_NONE, REG_Y },
{ "incsp5", REG_NONE, REG_Y },
{ "incsp6", REG_NONE, REG_Y },
{ "incsp7", REG_NONE, REG_Y },
{ "incsp8", REG_NONE, REG_Y },
{ "ldaidx", REG_AXY, REG_AX },
{ "ldauidx", REG_AXY, REG_AX },
{ "ldax0sp", REG_Y, REG_AX },
{ "ldaxi", REG_AX, REG_AXY },
{ "ldaxidx", REG_AXY, REG_AX },
{ "ldaxysp", REG_Y, REG_AX },
{ "leaasp", REG_A, REG_AX },
{ "negax", REG_AX, REG_AX },
{ "pusha", REG_A, REG_Y },
{ "pusha0", REG_A, REG_XY },
{ "pushax", REG_AX, REG_Y },
{ "pusheax", REG_AX, REG_Y },
{ "pushw0sp", REG_NONE, REG_AXY },
{ "pushwysp", REG_Y, REG_AXY },
{ "shlax1", REG_AX, REG_AX },
{ "shlax2", REG_AX, REG_AX },
{ "shlax3", REG_AX, REG_AX },
{ "shlax4", REG_AX, REG_AX },
{ "shrax1", REG_AX, REG_AX },
{ "shrax2", REG_AX, REG_AX },
{ "shrax3", REG_AX, REG_AX },
{ "shrax4", REG_AX, REG_AX },
{ "shreax1", REG_AX, REG_AX },
{ "shreax2", REG_AX, REG_AX },
{ "shreax3", REG_AX, REG_AX },
{ "shreax4", REG_AX, REG_AX },
{ "staspidx", REG_A | REG_Y, REG_Y },
{ "stax0sp", REG_AX, REG_Y },
{ "tosicmp", REG_AX, REG_AXY },
{ "tosdiva0", REG_AX, REG_AXY },
{ "tosdivax", REG_AX, REG_AXY },
{ "tosdiveax", REG_AX, REG_AXY },
{ "tosmula0", REG_AX, REG_AXY },
{ "tosmulax", REG_AX, REG_AXY },
{ "tosmuleax", REG_AX, REG_AXY },
{ "tosshreax", REG_AX, REG_AXY },
{ "tosumula0", REG_AX, REG_AXY },
{ "tosumulax", REG_AX, REG_AXY },
{ "tosumuleax", REG_AX, REG_AXY },
{ "addysp", REG_Y, REG_NONE },
{ "aslax1", REG_AX, REG_AX | REG_TMP1 },
{ "aslax2", REG_AX, REG_AX | REG_TMP1 },
{ "aslax3", REG_AX, REG_AX | REG_TMP1 },
{ "aslax4", REG_AX, REG_AX | REG_TMP1 },
{ "bnega", REG_A, REG_AX },
{ "bnegax", REG_AX, REG_AX },
{ "bnegeax", REG_EAX, REG_EAX },
{ "booleq", REG_NONE, REG_AX },
{ "boolge", REG_NONE, REG_AX },
{ "boolgt", REG_NONE, REG_AX },
{ "boolle", REG_NONE, REG_AX },
{ "boollt", REG_NONE, REG_AX },
{ "boolne", REG_NONE, REG_AX },
{ "booluge", REG_NONE, REG_AX },
{ "boolugt", REG_NONE, REG_AX },
{ "boolule", REG_NONE, REG_AX },
{ "boolult", REG_NONE, REG_AX },
{ "complax", REG_AX, REG_AX },
{ "decax1", REG_AX, REG_AX },
{ "decax2", REG_AX, REG_AX },
{ "decax3", REG_AX, REG_AX },
{ "decax4", REG_AX, REG_AX },
{ "decax5", REG_AX, REG_AX },
{ "decax6", REG_AX, REG_AX },
{ "decax7", REG_AX, REG_AX },
{ "decax8", REG_AX, REG_AX },
{ "decaxy", REG_AXY, REG_AX | REG_TMP1 },
{ "decsp1", REG_NONE, REG_Y },
{ "decsp2", REG_NONE, REG_A },
{ "decsp3", REG_NONE, REG_A },
{ "decsp4", REG_NONE, REG_A },
{ "decsp5", REG_NONE, REG_A },
{ "decsp6", REG_NONE, REG_A },
{ "decsp7", REG_NONE, REG_A },
{ "decsp8", REG_NONE, REG_A },
{ "incax1", REG_AX, REG_AX },
{ "incax2", REG_AX, REG_AX },
{ "incsp1", REG_NONE, REG_NONE },
{ "incsp2", REG_NONE, REG_Y },
{ "incsp3", REG_NONE, REG_Y },
{ "incsp4", REG_NONE, REG_Y },
{ "incsp5", REG_NONE, REG_Y },
{ "incsp6", REG_NONE, REG_Y },
{ "incsp7", REG_NONE, REG_Y },
{ "incsp8", REG_NONE, REG_Y },
{ "ldaidx", REG_AXY, REG_AX | REG_PTR1 },
{ "ldauidx", REG_AXY, REG_AX | REG_PTR1 },
{ "ldax0sp", REG_Y, REG_AX },
{ "ldaxi", REG_AX, REG_AXY | REG_PTR1 },
{ "ldaxidx", REG_AXY, REG_AX | REG_PTR1 },
{ "ldaxysp", REG_Y, REG_AX },
{ "leaasp", REG_A, REG_AX },
{ "negax", REG_AX, REG_AX },
{ "pusha", REG_A, REG_Y },
{ "pusha0", REG_A, REG_XY },
{ "pushax", REG_AX, REG_Y },
{ "pusheax", REG_EAX, REG_Y },
{ "pushw0sp", REG_NONE, REG_AXY },
{ "pushwysp", REG_Y, REG_AXY },
{ "shlax1", REG_AX, REG_AX | REG_TMP1 },
{ "shlax2", REG_AX, REG_AX | REG_TMP1 },
{ "shlax3", REG_AX, REG_AX | REG_TMP1 },
{ "shlax4", REG_AX, REG_AX | REG_TMP1 },
{ "shrax1", REG_AX, REG_AX | REG_TMP1 },
{ "shrax2", REG_AX, REG_AX | REG_TMP1 },
{ "shrax3", REG_AX, REG_AX | REG_TMP1 },
{ "shrax4", REG_AX, REG_AX | REG_TMP1 },
{ "shreax1", REG_EAX, REG_AX | REG_TMP1 },
{ "shreax2", REG_EAX, REG_AX | REG_TMP1 },
{ "shreax3", REG_EAX, REG_AX | REG_TMP1 },
{ "shreax4", REG_EAX, REG_AX | REG_TMP1 },
{ "staspidx", REG_A | REG_Y, REG_Y | REG_TMP1 | REG_PTR1 },
{ "stax0sp", REG_AX, REG_Y },
{ "tosicmp", REG_AX, REG_AXY | REG_SREG },
{ "tosdiva0", REG_AX, REG_ALL },
{ "tosdivax", REG_AX, REG_ALL },
{ "tosdiveax", REG_EAX, REG_ALL },
{ "tosmula0", REG_AX, REG_ALL },
{ "tosmulax", REG_AX, REG_ALL },
{ "tosmuleax", REG_EAX, REG_ALL },
{ "tosshreax", REG_EAX, REG_EAXY | REG_PTR1 | REG_PTR2 },
{ "tosumula0", REG_AX, REG_ALL },
{ "tosumulax", REG_AX, REG_ALL },
{ "tosumuleax", REG_EAX, REG_ALL },
};
#define FuncInfoCount (sizeof(FuncInfoTable) / sizeof(FuncInfoTable[0]))
/* Table with names of zero page locations used by the compiler */
typedef struct ZPInfo ZPInfo;
struct ZPInfo {
unsigned char Len; /* Length of the following string */
char Name[11]; /* Name of zero page symbol */
unsigned char Len; /* Length of the following string */
char Name[11]; /* Name of zero page symbol */
unsigned short RegInfo; /* Register info for this symbol */
};
static const ZPInfo ZPInfoTable[] = {
{ 4, "ptr1" },
{ 7, "regbank" },
{ 7, "regsave" },
{ 2, "sp" },
{ 4, "sreg" },
{ 4, "tmp1" },
{ 4, "ptr1", REG_PTR1 },
{ 4, "ptr2", REG_PTR2 },
{ 4, "ptr3", REG_PTR3 },
{ 4, "ptr4", REG_PTR4 },
{ 7, "regbank", REG_NONE },
{ 7, "regsave", REG_NONE },
{ 2, "sp", REG_NONE },
{ 4, "sreg", REG_SREG },
{ 4, "tmp1", REG_TMP1 },
{ 4, "tmp2", REG_TMP2 },
{ 4, "tmp3", REG_TMP3 },
};
#define ZPInfoCount (sizeof(ZPInfoTable) / sizeof(ZPInfoTable[0]))
/*****************************************************************************/
/* Code */
/* Code */
/*****************************************************************************/
@ -227,7 +233,7 @@ void GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg)
}
/* Will destroy all registers */
*Chg = REG_AXY;
*Chg = REG_ALL;
/* Done */
return;
@ -248,15 +254,20 @@ void GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg)
}
}
/* Function not found - assume all registers used */
/* Function not found - assume all CPU registers are input, and all
* registers are changed
*/
*Use = REG_AXY;
*Chg = REG_AXY;
*Chg = REG_ALL;
}
int IsZPName (const char* Name)
/* Return true if the given name is a zero page symbol */
int IsZPName (const char* Name, unsigned short* RegInfo)
/* Return true if the given name is a zero page symbol. If the RegInfo
* pointer is not NULL, it is filled with the register info for the
* zero page location found.
*/
{
unsigned I;
const ZPInfo* Info;
@ -264,10 +275,13 @@ int IsZPName (const char* Name)
/* Because of the low number of symbols, we do a linear search here */
for (I = 0, Info = ZPInfoTable; I < ZPInfoCount; ++I, ++Info) {
if (strncmp (Name, Info->Name, Info->Len) == 0 &&
(Name[Info->Len] == '\0' || Name[Info->Len] == '+')) {
/* Found */
return 1;
}
(Name[Info->Len] == '\0' || Name[Info->Len] == '+')) {
/* Found */
if (RegInfo) {
*RegInfo = Info->RegInfo;
}
return 1;
}
}
/* Not found */
@ -277,9 +291,9 @@ int IsZPName (const char* Name)
static unsigned GetRegInfo1 (CodeSeg* S,
CodeEntry* E,
int Index,
Collection* Visited,
CodeEntry* E,
int Index,
Collection* Visited,
unsigned Used,
unsigned Unused);
/* Recursively called subfunction for GetRegInfo. */

View File

@ -73,15 +73,16 @@ struct CodeSeg;
#define REG_PTR4_LO 0x4000U
#define REG_PTR4_HI 0x8000U
#define REG_AX (REG_A | REG_X)
#define REG_EAX (REG_A | REG_X | REG_SREG_LO | REG_SREG_HI)
#define REG_XY (REG_X | REG_Y)
#define REG_AXY (REG_A | REG_X | REG_Y)
#define REG_SREG (REG_SREG_LO | REG_SREG_HI)
#define REG_EAX (REG_AX | REG_SREG)
#define REG_XY (REG_X | REG_Y)
#define REG_AXY (REG_AX | REG_Y)
#define REG_EAXY (REG_EAX | REG_Y)
#define REG_PTR1 (REG_PTR1_LO | REG_PTR1_HI)
#define REG_PTR2 (REG_PTR2_LO | REG_PTR2_HI)
#define REG_PTR3 (REG_PTR3_LO | REG_PTR3_HI)
#define REG_PTR4 (REG_PTR4_LO | REG_PTR4_HI)
#define REG_ALL 0xFFFFU
/*****************************************************************************/
@ -96,8 +97,11 @@ void GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg);
* load all registers.
*/
int IsZPName (const char* Name);
/* Return true if the given name is a zero page symbol */
int IsZPName (const char* Name, unsigned short* RegInfo);
/* Return true if the given name is a zero page symbol. If the RegInfo
* pointer is not NULL, it is filled with the register info for the
* zero page location found.
*/
unsigned GetRegInfo (struct CodeSeg* S, unsigned Index);
/* Determine register usage information for the instructions starting at the

View File

@ -591,15 +591,12 @@ static unsigned OptAdd1 (CodeSeg* S)
CodeEntry* X;
CodeLabel* Label;
char Buf [16];
/* Remove the call to pushax */
CS_DelEntry (S, I);
/* Correct the stack offset (needed since pushax was removed) */
L[0]->Num -= 2;
xsprintf (Buf, sizeof (Buf), "$%02lX", L[0]->Num);
CE_SetArg (L[0], Buf);
CE_SetNumArg (L[0], L[0]->Num - 2);
/* Add the clc . */
X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, L[3]->LI);
@ -1885,7 +1882,7 @@ static unsigned OptPtrStore2 (CodeSeg* S)
{
unsigned Changes = 0;
/* Walk over the entries */
/* Walk over the entries */
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {

View File

@ -323,7 +323,7 @@ static CodeEntry* ParseInsn (CodeSeg* S, LineInfo* LI, const char* L)
if ((OPC->Info & OF_BRA) != 0) {
/* Branch */
AM = AM65_BRA;
} else if (IsZPName (Arg)) {
} else if (IsZPName (Arg, 0)) {
AM = AM65_ZP;
} else {
AM = AM65_ABS;
@ -338,7 +338,7 @@ static CodeEntry* ParseInsn (CodeSeg* S, LineInfo* LI, const char* L)
Reg = toupper (*L);
L = SkipSpace (L+1);
if (Reg == 'X') {
if (IsZPName (Arg)) {
if (IsZPName (Arg, 0)) {
AM = AM65_ZPX;
} else {
AM = AM65_ABSX;
@ -484,7 +484,7 @@ void CS_AddVLine (CodeSeg* S, LineInfo* LI, const char* Format, va_list ap)
void CS_AddLine (CodeSeg* S, LineInfo* LI, const char* Format, ...)
/* Add a line to the given code segment */
{
{
va_list ap;
va_start (ap, Format);
CS_AddVLine (S, LI, Format, ap);

View File

@ -197,8 +197,8 @@ typedef struct {
opc_t OPC; /* Opcode */
char Mnemo[9]; /* Mnemonic */
unsigned char Size; /* Size, 0 = check addressing mode */
unsigned char Use; /* Registers used by this insn */
unsigned char Chg; /* Registers changed by this insn */
unsigned short Use; /* Registers used by this insn */
unsigned short Chg; /* Registers changed by this insn */
unsigned short Info; /* Additional information */
} OPCDesc;