mirror of
https://github.com/cc65/cc65.git
synced 2024-11-19 06:31:31 +00:00
Re-added register variables.
Changed/added several optimizer steps to detect register variables correctly or to handle them in a special way. git-svn-id: svn://svn.cc65.org/cc65/trunk@1636 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
parent
1d94d18fa5
commit
70755921a9
@ -461,11 +461,11 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs)
|
||||
/* We don't know the value of the carry, so the result is
|
||||
* always unknown.
|
||||
*/
|
||||
Out->RegA = -1;
|
||||
Out->RegA = UNKNOWN_REGVAL;
|
||||
break;
|
||||
|
||||
case OP65_AND:
|
||||
if (In->RegA >= 0) {
|
||||
if (RegValIsKnown (In->RegA)) {
|
||||
if (CE_KnownImm (E)) {
|
||||
Out->RegA = In->RegA & (short) E->Num;
|
||||
} else if (E->AM == AM65_ZP) {
|
||||
@ -480,11 +480,11 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs)
|
||||
Out->RegA = In->RegA & In->SRegHi;
|
||||
break;
|
||||
default:
|
||||
Out->RegA = -1;
|
||||
Out->RegA = UNKNOWN_REGVAL;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
Out->RegA = -1;
|
||||
Out->RegA = UNKNOWN_REGVAL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -565,7 +565,7 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs)
|
||||
break;
|
||||
|
||||
case OP65_DEA:
|
||||
if (In->RegA >= 0) {
|
||||
if (RegValIsKnown (In->RegA)) {
|
||||
Out->RegA = (In->RegA - 1) & 0xFF;
|
||||
}
|
||||
break;
|
||||
@ -592,19 +592,19 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs)
|
||||
break;
|
||||
|
||||
case OP65_DEX:
|
||||
if (In->RegX >= 0) {
|
||||
if (RegValIsKnown (In->RegX)) {
|
||||
Out->RegX = (In->RegX - 1) & 0xFF;
|
||||
}
|
||||
break;
|
||||
|
||||
case OP65_DEY:
|
||||
if (In->RegY >= 0) {
|
||||
if (RegValIsKnown (In->RegY)) {
|
||||
Out->RegY = (In->RegY - 1) & 0xFF;
|
||||
}
|
||||
break;
|
||||
|
||||
case OP65_EOR:
|
||||
if (In->RegA >= 0) {
|
||||
if (RegValIsKnown (In->RegA)) {
|
||||
if (CE_KnownImm (E)) {
|
||||
Out->RegA = In->RegA ^ (short) E->Num;
|
||||
} else if (E->AM == AM65_ZP) {
|
||||
@ -619,17 +619,17 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs)
|
||||
Out->RegA = In->RegA ^ In->SRegHi;
|
||||
break;
|
||||
default:
|
||||
Out->RegA = -1;
|
||||
Out->RegA = UNKNOWN_REGVAL;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
Out->RegA = -1;
|
||||
Out->RegA = UNKNOWN_REGVAL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case OP65_INA:
|
||||
if (In->RegA >= 0) {
|
||||
if (RegValIsKnown (In->RegA)) {
|
||||
Out->RegA = (In->RegA + 1) & 0xFF;
|
||||
}
|
||||
break;
|
||||
@ -656,13 +656,13 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs)
|
||||
break;
|
||||
|
||||
case OP65_INX:
|
||||
if (In->RegX >= 0) {
|
||||
if (RegValIsKnown (In->RegX)) {
|
||||
Out->RegX = (In->RegX + 1) & 0xFF;
|
||||
}
|
||||
break;
|
||||
|
||||
case OP65_INY:
|
||||
if (In->RegY >= 0) {
|
||||
if (RegValIsKnown (In->RegY)) {
|
||||
Out->RegY = (In->RegY + 1) & 0xFF;
|
||||
}
|
||||
break;
|
||||
@ -692,22 +692,22 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs)
|
||||
/* Get the code info for the function */
|
||||
GetFuncInfo (E->Arg, &Use, &Chg);
|
||||
if (Chg & REG_A) {
|
||||
Out->RegA = -1;
|
||||
Out->RegA = UNKNOWN_REGVAL;
|
||||
}
|
||||
if (Chg & REG_X) {
|
||||
Out->RegX = -1;
|
||||
Out->RegX = UNKNOWN_REGVAL;
|
||||
}
|
||||
if (Chg & REG_Y) {
|
||||
Out->RegY = -1;
|
||||
Out->RegY = UNKNOWN_REGVAL;
|
||||
}
|
||||
if (Chg & REG_TMP1) {
|
||||
Out->Tmp1 = -1;
|
||||
Out->Tmp1 = UNKNOWN_REGVAL;
|
||||
}
|
||||
if (Chg & REG_SREG_LO) {
|
||||
Out->SRegLo = -1;
|
||||
Out->SRegLo = UNKNOWN_REGVAL;
|
||||
}
|
||||
if (Chg & REG_SREG_HI) {
|
||||
Out->SRegHi = -1;
|
||||
Out->SRegHi = UNKNOWN_REGVAL;
|
||||
}
|
||||
/* ## FIXME: Quick hack for some known functions: */
|
||||
if (strcmp (E->Arg, "tosandax") == 0) {
|
||||
@ -750,12 +750,12 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs)
|
||||
Out->RegA = In->SRegHi;
|
||||
break;
|
||||
default:
|
||||
Out->RegA = -1;
|
||||
Out->RegA = UNKNOWN_REGVAL;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* A is now unknown */
|
||||
Out->RegA = -1;
|
||||
Out->RegA = UNKNOWN_REGVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -774,12 +774,12 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs)
|
||||
Out->RegX = In->SRegHi;
|
||||
break;
|
||||
default:
|
||||
Out->RegX = -1;
|
||||
Out->RegX = UNKNOWN_REGVAL;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* X is now unknown */
|
||||
Out->RegX = -1;
|
||||
Out->RegX = UNKNOWN_REGVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -798,12 +798,12 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs)
|
||||
Out->RegY = In->SRegHi;
|
||||
break;
|
||||
default:
|
||||
Out->RegY = -1;
|
||||
Out->RegY = UNKNOWN_REGVAL;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* Y is now unknown */
|
||||
Out->RegY = -1;
|
||||
Out->RegY = UNKNOWN_REGVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -832,7 +832,7 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs)
|
||||
break;
|
||||
|
||||
case OP65_ORA:
|
||||
if (In->RegA >= 0) {
|
||||
if (RegValIsKnown (In->RegA)) {
|
||||
if (CE_KnownImm (E)) {
|
||||
Out->RegA = In->RegA | (short) E->Num;
|
||||
} else if (E->AM == AM65_ZP) {
|
||||
@ -847,12 +847,12 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs)
|
||||
Out->RegA = In->RegA | In->SRegHi;
|
||||
break;
|
||||
default:
|
||||
Out->RegA = -1;
|
||||
Out->RegA = UNKNOWN_REGVAL;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* A is now unknown */
|
||||
Out->RegA = -1;
|
||||
Out->RegA = UNKNOWN_REGVAL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -887,17 +887,17 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs)
|
||||
case OP65_ROL:
|
||||
/* We don't know the value of the carry bit */
|
||||
if (E->AM == AM65_ACC) {
|
||||
Out->RegA = -1;
|
||||
Out->RegA = UNKNOWN_REGVAL;
|
||||
} else if (E->AM == AM65_ZP) {
|
||||
switch (GetKnownReg (E->Chg, In)) {
|
||||
case REG_TMP1:
|
||||
Out->Tmp1 = -1;
|
||||
Out->Tmp1 = UNKNOWN_REGVAL;
|
||||
break;
|
||||
case REG_SREG_LO:
|
||||
Out->SRegLo = -1;
|
||||
Out->SRegLo = UNKNOWN_REGVAL;
|
||||
break;
|
||||
case REG_SREG_HI:
|
||||
Out->SRegHi = -1;
|
||||
Out->SRegHi = UNKNOWN_REGVAL;
|
||||
break;
|
||||
}
|
||||
} else if (E->AM == AM65_ZPX) {
|
||||
@ -909,17 +909,17 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs)
|
||||
case OP65_ROR:
|
||||
/* We don't know the value of the carry bit */
|
||||
if (E->AM == AM65_ACC) {
|
||||
Out->RegA = -1;
|
||||
Out->RegA = UNKNOWN_REGVAL;
|
||||
} else if (E->AM == AM65_ZP) {
|
||||
switch (GetKnownReg (E->Chg, In)) {
|
||||
case REG_TMP1:
|
||||
Out->Tmp1 = -1;
|
||||
Out->Tmp1 = UNKNOWN_REGVAL;
|
||||
break;
|
||||
case REG_SREG_LO:
|
||||
Out->SRegLo = -1;
|
||||
Out->SRegLo = UNKNOWN_REGVAL;
|
||||
break;
|
||||
case REG_SREG_HI:
|
||||
Out->SRegHi = -1;
|
||||
Out->SRegHi = UNKNOWN_REGVAL;
|
||||
break;
|
||||
}
|
||||
} else if (E->AM == AM65_ZPX) {
|
||||
@ -1052,13 +1052,13 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs)
|
||||
} else {
|
||||
switch (GetKnownReg (E->Chg, In)) {
|
||||
case REG_TMP1:
|
||||
Out->Tmp1 = -1;
|
||||
Out->Tmp1 = UNKNOWN_REGVAL;
|
||||
break;
|
||||
case REG_SREG_LO:
|
||||
Out->SRegLo = -1;
|
||||
Out->SRegLo = UNKNOWN_REGVAL;
|
||||
break;
|
||||
case REG_SREG_HI:
|
||||
Out->SRegHi = -1;
|
||||
Out->SRegHi = UNKNOWN_REGVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1085,13 +1085,13 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs)
|
||||
} else {
|
||||
switch (GetKnownReg (E->Chg, In)) {
|
||||
case REG_TMP1:
|
||||
Out->Tmp1 = -1;
|
||||
Out->Tmp1 = UNKNOWN_REGVAL;
|
||||
break;
|
||||
case REG_SREG_LO:
|
||||
Out->SRegLo = -1;
|
||||
Out->SRegLo = UNKNOWN_REGVAL;
|
||||
break;
|
||||
case REG_SREG_HI:
|
||||
Out->SRegHi = -1;
|
||||
Out->SRegHi = UNKNOWN_REGVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1099,7 +1099,7 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs)
|
||||
break;
|
||||
|
||||
case OP65_TSX:
|
||||
Out->RegX = -1;
|
||||
Out->RegX = UNKNOWN_REGVAL;
|
||||
break;
|
||||
|
||||
case OP65_TXA:
|
||||
|
@ -132,9 +132,9 @@ static const char* GetLabelName (unsigned Flags, unsigned long Label, long Offs)
|
||||
break;
|
||||
|
||||
default:
|
||||
Internal ("Invalid address flags");
|
||||
Internal ("Invalid address flags: %04X", Flags);
|
||||
}
|
||||
|
||||
|
||||
/* Return a pointer to the static buffer */
|
||||
return Buf;
|
||||
}
|
||||
@ -485,11 +485,51 @@ void g_leave (void)
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Register variables */
|
||||
/* Register variables */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
void g_swap_regvars (int StackOffs, int RegOffs, unsigned Bytes)
|
||||
/* Swap a register variable with a location on the stack */
|
||||
{
|
||||
/* Calculate the actual stack offset and check it */
|
||||
StackOffs -= oursp;
|
||||
CheckLocalOffs (StackOffs);
|
||||
|
||||
/* Generate code */
|
||||
if (Bytes == 1) {
|
||||
|
||||
if (CodeSizeFactor < 165) {
|
||||
ldyconst (StackOffs);
|
||||
ldxconst (RegOffs);
|
||||
AddCodeLine ("jsr regswap1");
|
||||
} else {
|
||||
ldyconst (StackOffs);
|
||||
AddCodeLine ("lda (sp),y");
|
||||
AddCodeLine ("ldx regbank%+d", RegOffs);
|
||||
AddCodeLine ("sta regbank%+d", RegOffs);
|
||||
AddCodeLine ("txa");
|
||||
AddCodeLine ("sta (sp),y");
|
||||
}
|
||||
|
||||
} else if (Bytes == 2) {
|
||||
|
||||
ldyconst (StackOffs);
|
||||
ldxconst (RegOffs);
|
||||
AddCodeLine ("jsr regswap2");
|
||||
|
||||
} else {
|
||||
|
||||
ldyconst (StackOffs);
|
||||
ldxconst (RegOffs);
|
||||
ldaconst (Bytes);
|
||||
AddCodeLine ("jsr regswap");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void g_save_regvars (int RegOffs, unsigned Bytes)
|
||||
/* Save register variables */
|
||||
{
|
||||
|
@ -232,6 +232,9 @@ void g_leave (void);
|
||||
|
||||
|
||||
|
||||
void g_swap_regvars (int StackOffs, int RegOffs, unsigned Bytes);
|
||||
/* Swap a register variable with a location on the stack */
|
||||
|
||||
void g_save_regvars (int RegOffs, unsigned Bytes);
|
||||
/* Save register variables */
|
||||
|
||||
|
@ -165,6 +165,9 @@ static const FuncInfo FuncInfoTable[] = {
|
||||
{ "pushw0sp", REG_NONE, REG_AXY },
|
||||
{ "pushwidx", REG_AXY, REG_AXY | REG_PTR1 },
|
||||
{ "pushwysp", REG_Y, REG_AXY },
|
||||
{ "regswap", REG_AXY, REG_AXY | REG_TMP1 },
|
||||
{ "regswap1", REG_XY, REG_A },
|
||||
{ "regswap2", REG_XY, REG_A | REG_Y },
|
||||
{ "shlax1", REG_AX, REG_AX | REG_TMP1 },
|
||||
{ "shlax2", REG_AX, REG_AX | REG_TMP1 },
|
||||
{ "shlax3", REG_AX, REG_AX | REG_TMP1 },
|
||||
|
@ -1391,19 +1391,22 @@ void CS_GenRegInfo (CodeSeg* S)
|
||||
break;
|
||||
}
|
||||
if (J->RI->Out2.RegA != Regs.RegA) {
|
||||
Regs.RegA = -1;
|
||||
Regs.RegA = UNKNOWN_REGVAL;
|
||||
}
|
||||
if (J->RI->Out2.RegX != Regs.RegX) {
|
||||
Regs.RegX = -1;
|
||||
Regs.RegX = UNKNOWN_REGVAL;
|
||||
}
|
||||
if (J->RI->Out2.RegY != Regs.RegY) {
|
||||
Regs.RegY = -1;
|
||||
Regs.RegY = UNKNOWN_REGVAL;
|
||||
}
|
||||
if (J->RI->Out2.SRegLo != Regs.SRegLo) {
|
||||
Regs.SRegLo = -1;
|
||||
Regs.SRegLo = UNKNOWN_REGVAL;
|
||||
}
|
||||
if (J->RI->Out2.SRegHi != Regs.SRegHi) {
|
||||
Regs.SRegHi = -1;
|
||||
Regs.SRegHi = UNKNOWN_REGVAL;
|
||||
}
|
||||
if (J->RI->Out2.Tmp1 != Regs.Tmp1) {
|
||||
Regs.Tmp1 = UNKNOWN_REGVAL;
|
||||
}
|
||||
++Entry;
|
||||
}
|
||||
|
@ -288,7 +288,7 @@ unsigned OptAdd3 (CodeSeg* S)
|
||||
*
|
||||
* jsr pushax
|
||||
* lda xxx
|
||||
* ldy yyy
|
||||
* ldx yyy
|
||||
* jsr tosaddax
|
||||
*
|
||||
* and replace it by
|
||||
|
@ -101,7 +101,7 @@ unsigned OptAdd3 (CodeSeg* S);
|
||||
*
|
||||
* jsr pushax
|
||||
* lda xxx
|
||||
* ldy yyy
|
||||
* ldx yyy
|
||||
* jsr tosaddax
|
||||
*
|
||||
* and replace it by
|
||||
@ -113,7 +113,7 @@ unsigned OptAdd3 (CodeSeg* S);
|
||||
* adc yyy
|
||||
* tax
|
||||
* pla
|
||||
*/
|
||||
*/
|
||||
|
||||
unsigned OptAdd4 (CodeSeg* S);
|
||||
/* Search for the sequence
|
||||
|
@ -34,6 +34,7 @@
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/* cc65 */
|
||||
#include "codeent.h"
|
||||
@ -66,7 +67,7 @@ struct StackOpData {
|
||||
|
||||
/* Flags returned by DirectOp */
|
||||
#define OP_DIRECT 0x01 /* Direct op may be used */
|
||||
#define OP_ONSTACK 0x02 /* Operand is on stack */
|
||||
#define OP_RELOAD_Y 0x02 /* Must reload index register Y */
|
||||
|
||||
|
||||
|
||||
@ -207,11 +208,9 @@ static void CheckDirectOp (StackOpData* D)
|
||||
if (E->AM == AM65_IMM || E->AM == AM65_ZP || E->AM == AM65_ABS) {
|
||||
/* These insns are all ok and replaceable */
|
||||
D->Flags |= OP_DIRECT;
|
||||
} else if (E->AM == AM65_ZP_INDY &&
|
||||
RegValIsKnown (E->RI->In.RegY) &&
|
||||
(E->Use & REG_SP) != 0) {
|
||||
/* Load from stack with known offset is also ok */
|
||||
D->Flags |= (OP_DIRECT | OP_ONSTACK);
|
||||
} else if (E->AM == AM65_ZP_INDY && RegValIsKnown (E->RI->In.RegY)) {
|
||||
/* Load indirect with known offset is also ok */
|
||||
D->Flags |= (OP_DIRECT | OP_RELOAD_Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -222,7 +221,7 @@ static void ReplacePushByStore (StackOpData* D)
|
||||
/* Replace the call to the push subroutine by a store into the zero page
|
||||
* location (actually, the push is not replaced, because we need it for
|
||||
* later, but the name is still ok since the push will get removed at the
|
||||
* end of each routine.
|
||||
* end of each routine).
|
||||
*/
|
||||
{
|
||||
CodeEntry* X;
|
||||
@ -240,7 +239,7 @@ static void ReplacePushByStore (StackOpData* D)
|
||||
|
||||
static void AddOpLow (StackOpData* D, opc_t OPC)
|
||||
/* Add an op for the low byte of an operator. This function honours the
|
||||
* OP_DIRECT and OP_ONSTACK flags and generates the necessary instructions.
|
||||
* OP_DIRECT and OP_RELOAD_Y flags and generates the necessary instructions.
|
||||
* All code is inserted at the current insertion point.
|
||||
*/
|
||||
{
|
||||
@ -250,7 +249,7 @@ static void AddOpLow (StackOpData* D, opc_t OPC)
|
||||
/* Op with a variable location. If the location is on the stack, we
|
||||
* need to reload the Y register.
|
||||
*/
|
||||
if ((D->Flags & OP_ONSTACK) != 0) {
|
||||
if ((D->Flags & OP_RELOAD_Y) != 0) {
|
||||
const char* Arg = MakeHexArg (D->PrevEntry->RI->In.RegY);
|
||||
X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
|
||||
InsertEntry (D, X, D->IP++);
|
||||
@ -297,6 +296,33 @@ static void RemovePushAndOp (StackOpData* D)
|
||||
|
||||
|
||||
|
||||
static const char* IsRegVar (const StackOpData* D)
|
||||
/* If the value pushed is that of a register variable, return the name of the
|
||||
* entry in the register bank. Otherwise return NULL.
|
||||
*/
|
||||
{
|
||||
CodeEntry* P;
|
||||
|
||||
if (D->PushIndex >= 2 &&
|
||||
(P = D->PrevEntry) != 0 &&
|
||||
P->OPC == OP65_LDX &&
|
||||
P->AM == AM65_ZP &&
|
||||
strncmp (P->Arg, "regbank+", 7) == 0 &&
|
||||
isdigit (P->Arg[8]) &&
|
||||
(P = CS_GetEntry (D->Code, D->PushIndex-2)) != 0 &&
|
||||
P->OPC == OP65_LDA &&
|
||||
P->AM == AM65_ZP &&
|
||||
strncmp (P->Arg, "regbank+", 7) == 0 &&
|
||||
isdigit (P->Arg[8])) {
|
||||
/* Ok, it loads the register variable */
|
||||
return P->Arg;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Actual optimization functions */
|
||||
/*****************************************************************************/
|
||||
@ -307,12 +333,20 @@ static unsigned Opt_staspidx (StackOpData* D)
|
||||
/* Optimize the staspidx sequence if possible */
|
||||
{
|
||||
CodeEntry* X;
|
||||
const char* ZPLo;
|
||||
|
||||
/* Store the value into the zeropage instead of pushing it */
|
||||
ReplacePushByStore (D);
|
||||
/* Check if we're using a register variable */
|
||||
if ((ZPLo = IsRegVar (D)) == 0) {
|
||||
|
||||
/* Store the value into the zeropage instead of pushing it */
|
||||
ReplacePushByStore (D);
|
||||
|
||||
/* Use the given zero page loc */
|
||||
ZPLo = D->ZPLo;
|
||||
}
|
||||
|
||||
/* Replace the store subroutine call by a direct op */
|
||||
X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, D->ZPLo, 0, D->OpEntry->LI);
|
||||
X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, D->OpEntry->LI);
|
||||
InsertEntry (D, X, D->OpIndex+1);
|
||||
|
||||
/* Remove the push and the call to the staspidx function */
|
||||
@ -328,12 +362,20 @@ static unsigned Opt_staxspidx (StackOpData* D)
|
||||
/* Optimize the staxspidx sequence if possible */
|
||||
{
|
||||
CodeEntry* X;
|
||||
const char* ZPLo;
|
||||
|
||||
/* Store the value into the zeropage instead of pushing it */
|
||||
ReplacePushByStore (D);
|
||||
/* Check if we're using a register variable */
|
||||
if ((ZPLo = IsRegVar (D)) == 0) {
|
||||
|
||||
/* Store the value into the zeropage instead of pushing it */
|
||||
ReplacePushByStore (D);
|
||||
|
||||
/* Use the given zero page loc */
|
||||
ZPLo = D->ZPLo;
|
||||
}
|
||||
|
||||
/* Inline the store */
|
||||
X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, D->ZPLo, 0, D->OpEntry->LI);
|
||||
X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, D->OpEntry->LI);
|
||||
InsertEntry (D, X, D->OpIndex+1);
|
||||
X = NewCodeEntry (OP65_INY, AM65_IMP, 0, 0, D->OpEntry->LI);
|
||||
InsertEntry (D, X, D->OpIndex+2);
|
||||
@ -346,7 +388,7 @@ static unsigned Opt_staxspidx (StackOpData* D)
|
||||
X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, D->OpEntry->LI);
|
||||
}
|
||||
InsertEntry (D, X, D->OpIndex+3);
|
||||
X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, D->ZPLo, 0, D->OpEntry->LI);
|
||||
X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, D->OpEntry->LI);
|
||||
InsertEntry (D, X, D->OpIndex+4);
|
||||
|
||||
/* Remove the push and the call to the staspidx function */
|
||||
@ -607,7 +649,7 @@ static int HarmlessCall (const char* Name)
|
||||
*/
|
||||
{
|
||||
static const char* Tab[] = {
|
||||
"ldaxidx",
|
||||
"ldaxidx",
|
||||
"ldaxysp",
|
||||
};
|
||||
|
||||
|
@ -83,7 +83,7 @@ static type OptionalQualifiers (type Q)
|
||||
switch (CurTok.Tok) {
|
||||
|
||||
case TOK_CONST:
|
||||
if (Q & T_QUAL_CONST) {
|
||||
if (Q & T_QUAL_CONST) {
|
||||
Error ("Duplicate qualifier: `const'");
|
||||
}
|
||||
Q |= T_QUAL_CONST;
|
||||
@ -339,7 +339,7 @@ static void ParseTypeSpec (DeclSpec* D, int Default)
|
||||
ident Ident;
|
||||
SymEntry* Entry;
|
||||
type StructType;
|
||||
type Qualifiers; /* Type qualifiers */
|
||||
type Qualifiers; /* Type qualifiers */
|
||||
|
||||
/* Assume we have an explicit type */
|
||||
D->Flags &= ~DS_DEF_TYPE;
|
||||
@ -427,7 +427,7 @@ static void ParseTypeSpec (DeclSpec* D, int Default)
|
||||
/* FALL THROUGH */
|
||||
|
||||
default:
|
||||
D->Type[0] = T_INT;
|
||||
D->Type[0] = T_INT;
|
||||
D->Type[1] = T_END;
|
||||
break;
|
||||
}
|
||||
@ -663,14 +663,15 @@ static void ParseAnsiParamList (FuncDesc* F)
|
||||
/* Read the declaration specifier */
|
||||
ParseDeclSpec (&Spec, SC_AUTO, T_INT);
|
||||
|
||||
/* We accept only auto and register as storage class specifiers, but
|
||||
* we ignore all this and use auto.
|
||||
*/
|
||||
if ((Spec.StorageClass & SC_AUTO) == 0 &&
|
||||
(Spec.StorageClass & SC_REGISTER) == 0) {
|
||||
/* We accept only auto and register as storage class specifiers */
|
||||
if ((Spec.StorageClass & SC_AUTO) == SC_AUTO) {
|
||||
Spec.StorageClass = SC_AUTO | SC_PARAM | SC_DEF;
|
||||
} else if ((Spec.StorageClass & SC_REGISTER) == SC_REGISTER) {
|
||||
Spec.StorageClass = SC_REGISTER | SC_STATIC | SC_PARAM | SC_DEF;
|
||||
} else {
|
||||
Error ("Illegal storage class");
|
||||
Spec.StorageClass = SC_AUTO | SC_PARAM | SC_DEF;
|
||||
}
|
||||
Spec.StorageClass = SC_AUTO | SC_PARAM | SC_DEF;
|
||||
|
||||
/* Allow parameters without a name, but remember if we had some to
|
||||
* eventually print an error message later.
|
||||
@ -755,10 +756,10 @@ static FuncDesc* ParseFuncDecl (const DeclSpec* Spec)
|
||||
|
||||
/* Check for an implicit int return in the K&R function */
|
||||
if ((Spec->Flags & DS_DEF_TYPE) != 0 &&
|
||||
Spec->Type[0] == T_INT &&
|
||||
Spec->Type[1] == T_END) {
|
||||
/* Function has an implicit int return */
|
||||
F->Flags |= FD_OLDSTYLE_INTRET;
|
||||
Spec->Type[0] == T_INT &&
|
||||
Spec->Type[1] == T_END) {
|
||||
/* Function has an implicit int return */
|
||||
F->Flags |= FD_OLDSTYLE_INTRET;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -779,7 +780,11 @@ static FuncDesc* ParseFuncDecl (const DeclSpec* Spec)
|
||||
Sym = GetSymTab()->SymTail;
|
||||
while (Sym) {
|
||||
unsigned Size = CheckedSizeOf (Sym->Type);
|
||||
Sym->V.Offs = Offs;
|
||||
if (SymIsRegVar (Sym)) {
|
||||
Sym->V.R.SaveOffs = Offs;
|
||||
} else {
|
||||
Sym->V.Offs = Offs;
|
||||
}
|
||||
Offs += Size;
|
||||
F->ParamSize += Size;
|
||||
Sym = Sym->PrevSym;
|
||||
|
@ -6,7 +6,7 @@
|
||||
/* */
|
||||
/* */
|
||||
/* */
|
||||
/* (C) 2000-2001 Ullrich von Bassewitz */
|
||||
/* (C) 2000-2002 Ullrich von Bassewitz */
|
||||
/* Wacholderweg 14 */
|
||||
/* D-70597 Stuttgart */
|
||||
/* EMail: uz@cc65.org */
|
||||
@ -60,14 +60,18 @@
|
||||
|
||||
|
||||
|
||||
/* Maximum register variable size */
|
||||
#define MAX_REG_SPACE 6
|
||||
|
||||
/* Structure that holds all data needed for function activation */
|
||||
struct Function {
|
||||
struct SymEntry* FuncEntry; /* Symbol table entry */
|
||||
type* ReturnType; /* Function return type */
|
||||
struct FuncDesc* Desc; /* Function descriptor */
|
||||
int Reserved; /* Reserved local space */
|
||||
struct SymEntry* FuncEntry; /* Symbol table entry */
|
||||
type* ReturnType; /* Function return type */
|
||||
struct FuncDesc* Desc; /* Function descriptor */
|
||||
int Reserved; /* Reserved local space */
|
||||
unsigned RetLab; /* Return code label */
|
||||
int TopLevelSP; /* SP at function top level */
|
||||
unsigned RegOffs; /* Register variable space offset */
|
||||
};
|
||||
|
||||
/* Pointer to current function */
|
||||
@ -94,6 +98,7 @@ static Function* NewFunction (struct SymEntry* Sym)
|
||||
F->Reserved = 0;
|
||||
F->RetLab = GetLocalLabel ();
|
||||
F->TopLevelSP = 0;
|
||||
F->RegOffs = MAX_REG_SPACE;
|
||||
|
||||
/* Return the new structure */
|
||||
return F;
|
||||
@ -220,6 +225,108 @@ void F_AllocLocalSpace (Function* F)
|
||||
|
||||
|
||||
|
||||
int F_AllocRegVar (Function* F, const type* Type)
|
||||
/* Allocate a register variable for the given variable type. If the allocation
|
||||
* was successful, return the offset of the register variable in the register
|
||||
* bank (zero page storage). If there is no register space left, return -1.
|
||||
*/
|
||||
{
|
||||
/* Allow register variables only on top level and if enabled */
|
||||
if (EnableRegVars && GetLexicalLevel () == LEX_LEVEL_FUNCTION) {
|
||||
|
||||
/* Get the size of the variable */
|
||||
unsigned Size = CheckedSizeOf (Type);
|
||||
|
||||
/* Do we have space left? */
|
||||
if (F->RegOffs >= Size) {
|
||||
/* Space left. We allocate the variables from high to low addresses,
|
||||
* so the adressing is compatible with the saved values on stack.
|
||||
* This allows shorter code when saving/restoring the variables.
|
||||
*/
|
||||
F->RegOffs -= Size;
|
||||
return F->RegOffs;
|
||||
}
|
||||
}
|
||||
|
||||
/* No space left or no allocation */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void F_RestoreRegVars (Function* F)
|
||||
/* Restore the register variables for the local function if there are any. */
|
||||
{
|
||||
const SymEntry* Sym;
|
||||
|
||||
/* If we don't have register variables in this function, bail out early */
|
||||
if (F->RegOffs == MAX_REG_SPACE) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Save the accumulator if needed */
|
||||
if (!F_HasVoidReturn (F)) {
|
||||
g_save (CF_CHAR | CF_FORCECHAR);
|
||||
}
|
||||
|
||||
/* Get the first symbol from the function symbol table */
|
||||
Sym = F->FuncEntry->V.F.Func->SymTab->SymHead;
|
||||
|
||||
/* Walk through all symbols checking for register variables */
|
||||
while (Sym) {
|
||||
if (SymIsRegVar (Sym)) {
|
||||
|
||||
/* Check for more than one variable */
|
||||
int Offs = Sym->V.R.SaveOffs;
|
||||
unsigned Bytes = CheckedSizeOf (Sym->Type);
|
||||
|
||||
while (1) {
|
||||
|
||||
/* Find next register variable */
|
||||
const SymEntry* NextSym = Sym->NextSym;
|
||||
while (NextSym && !SymIsRegVar (NextSym)) {
|
||||
NextSym = NextSym->NextSym;
|
||||
}
|
||||
|
||||
/* If we have a next one, compare the stack offsets */
|
||||
if (NextSym) {
|
||||
|
||||
/* We have a following register variable. Get the size */
|
||||
int Size = CheckedSizeOf (NextSym->Type);
|
||||
|
||||
/* Adjacent variable? */
|
||||
if (NextSym->V.R.SaveOffs + Size != Offs) {
|
||||
/* No */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Adjacent variable */
|
||||
Bytes += Size;
|
||||
Offs -= Size;
|
||||
Sym = NextSym;
|
||||
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore the memory range */
|
||||
g_restore_regvars (Offs, Sym->V.R.RegOffs, Bytes);
|
||||
|
||||
}
|
||||
|
||||
/* Check next symbol */
|
||||
Sym = Sym->NextSym;
|
||||
}
|
||||
|
||||
/* Restore the accumulator if needed */
|
||||
if (!F_HasVoidReturn (F)) {
|
||||
g_restore (CF_CHAR | CF_FORCECHAR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* code */
|
||||
/*****************************************************************************/
|
||||
@ -230,8 +337,8 @@ void NewFunc (SymEntry* Func)
|
||||
/* Parse argument declarations and function body. */
|
||||
{
|
||||
int HadReturn;
|
||||
int IsVoidFunc;
|
||||
SymEntry* LastParam;
|
||||
SymEntry* Param;
|
||||
|
||||
/* Get the function descriptor from the function entry */
|
||||
FuncDesc* D = Func->V.F.Func;
|
||||
@ -262,9 +369,6 @@ void NewFunc (SymEntry* Func)
|
||||
/* Function body now defined */
|
||||
Func->Flags |= SC_DEF;
|
||||
|
||||
/* Setup register variables */
|
||||
InitRegVars ();
|
||||
|
||||
/* Allocate code and data segments for this function */
|
||||
Func->V.F.Seg = PushSegments (Func);
|
||||
|
||||
@ -281,19 +385,50 @@ void NewFunc (SymEntry* Func)
|
||||
/* Pointer to function */
|
||||
Flags = CF_PTR;
|
||||
} else {
|
||||
Flags = TypeOf (LastParam->Type) | CF_FORCECHAR;
|
||||
Flags = TypeOf (LastParam->Type) | CF_FORCECHAR;
|
||||
}
|
||||
g_push (Flags, 0);
|
||||
}
|
||||
|
||||
/* If stack checking code is requested, emit a call to the helper routine */
|
||||
if (CheckStack) {
|
||||
g_stackcheck ();
|
||||
}
|
||||
|
||||
/* Generate function entry code if needed */
|
||||
g_enter (TypeOf (Func->Type), F_GetParamSize (CurrentFunc));
|
||||
|
||||
/* If stack checking code is requested, emit a call to the helper routine */
|
||||
if (CheckStack) {
|
||||
g_stackcheck ();
|
||||
}
|
||||
|
||||
/* Walk through the parameter list and allocate register variable space
|
||||
* for parameters declared as register. Generate code to swap the contents
|
||||
* of the register bank with the save area on the stack.
|
||||
*/
|
||||
Param = D->SymTab->SymHead;
|
||||
while (Param && (Param->Flags & SC_PARAM) != 0) {
|
||||
|
||||
/* Check for a register variable */
|
||||
if (SymIsRegVar (Param)) {
|
||||
|
||||
/* Allocate space */
|
||||
int Reg = F_AllocRegVar (CurrentFunc, Param->Type);
|
||||
|
||||
/* Could we allocate a register? */
|
||||
if (Reg < 0) {
|
||||
/* No register available: Convert parameter to auto */
|
||||
CvtRegVarToAuto (Param);
|
||||
} else {
|
||||
/* Remember the register offset */
|
||||
Param->V.R.RegOffs = Reg;
|
||||
|
||||
/* Generate swap code */
|
||||
g_swap_regvars (Param->V.R.SaveOffs, Reg, CheckedSizeOf (Param->Type));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Next parameter */
|
||||
Param = Param->NextSym;
|
||||
}
|
||||
|
||||
/* Setup the stack */
|
||||
oursp = 0;
|
||||
|
||||
@ -318,22 +453,11 @@ void NewFunc (SymEntry* Func)
|
||||
}
|
||||
}
|
||||
|
||||
/* If the function has a return type but no return statement, flag
|
||||
* a warning
|
||||
*/
|
||||
IsVoidFunc = F_HasVoidReturn (CurrentFunc);
|
||||
#if 0
|
||||
/* Does not work reliably */
|
||||
if (!F_IsVoidFunc && !HadReturn) {
|
||||
Warning ("Function `%s' should return a value", Func->Name);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Output the function exit code label */
|
||||
g_defcodelabel (F_GetRetLab (CurrentFunc));
|
||||
|
||||
/* Restore the register variables */
|
||||
RestoreRegVars (!IsVoidFunc);
|
||||
F_RestoreRegVars (CurrentFunc);
|
||||
|
||||
/* Generate the exit code */
|
||||
g_leave ();
|
||||
@ -344,9 +468,6 @@ void NewFunc (SymEntry* Func)
|
||||
/* Emit references to imports/exports */
|
||||
EmitExternals ();
|
||||
|
||||
/* Cleanup register variables */
|
||||
DoneRegVars ();
|
||||
|
||||
/* Leave the lexical level */
|
||||
LeaveFunctionLevel ();
|
||||
|
||||
|
@ -98,6 +98,12 @@ void F_AllocLocalSpace (Function* F);
|
||||
* nothing if there is no reserved local space.
|
||||
*/
|
||||
|
||||
int F_AllocRegVar (Function* F, const type* Type);
|
||||
/* Allocate a register variable for the given variable type. If the allocation
|
||||
* was successful, return the offset of the register variable in the register
|
||||
* bank (zero page storage). If there is no register space left, return -1.
|
||||
*/
|
||||
|
||||
void NewFunc (struct SymEntry* Func);
|
||||
/* Parse argument declarations and function body. */
|
||||
|
||||
|
@ -51,93 +51,12 @@
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Data */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/* Register variable management */
|
||||
unsigned MaxRegSpace = 6; /* Maximum space available */
|
||||
static unsigned RegOffs = 0; /* Offset into register space */
|
||||
static const SymEntry** RegSyms = 0; /* The register variables */
|
||||
static unsigned RegSymCount = 0; /* Number of register variables */
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Code */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
void InitRegVars (void)
|
||||
/* Initialize register variable control data */
|
||||
{
|
||||
/* If the register space is zero, bail out */
|
||||
if (MaxRegSpace == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* The maximum number of register variables is equal to the register
|
||||
* variable space available. So allocate one pointer per byte. This
|
||||
* will usually waste some space but we don't need to dynamically
|
||||
* grow the array.
|
||||
*/
|
||||
RegSyms = (const SymEntry**) xmalloc (MaxRegSpace * sizeof (RegSyms[0]));
|
||||
RegOffs = MaxRegSpace;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DoneRegVars (void)
|
||||
/* Free the register variables */
|
||||
{
|
||||
xfree (RegSyms);
|
||||
RegSyms = 0;
|
||||
RegOffs = MaxRegSpace;
|
||||
RegSymCount = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int AllocRegVar (const type* Type)
|
||||
/* Allocate a register variable for the given variable type. If the allocation
|
||||
* was successful, return the offset of the register variable in the register
|
||||
* bank (zero page storage). If there is no register space left, return -1.
|
||||
*/
|
||||
{
|
||||
/* Maybe register variables are disabled... */
|
||||
if (EnableRegVars) {
|
||||
|
||||
/* Get the size of the variable */
|
||||
unsigned Size = CheckedSizeOf (Type);
|
||||
|
||||
/* Do we have space left? */
|
||||
if (RegOffs >= Size) {
|
||||
/* Space left. We allocate the variables from high to low addresses,
|
||||
* so the adressing is compatible with the saved values on stack.
|
||||
* This allows shorter code when saving/restoring the variables.
|
||||
*/
|
||||
RegOffs -= Size;
|
||||
return RegOffs;
|
||||
}
|
||||
}
|
||||
|
||||
/* No space left or no allocation */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void RememberRegVar (const SymEntry* Sym)
|
||||
/* Remember the given register variable */
|
||||
{
|
||||
RegSyms[RegSymCount++] = Sym;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static unsigned ParseRegisterDecl (Declaration* Decl, unsigned* SC, int Reg)
|
||||
/* Parse the declaration of a register variable. The function returns the
|
||||
* symbol data, which is the offset of the variable in the register bank.
|
||||
@ -185,7 +104,7 @@ static unsigned ParseRegisterDecl (Declaration* Decl, unsigned* SC, int Reg)
|
||||
} else {
|
||||
|
||||
/* Setup the type flags for the assignment */
|
||||
Flags = CF_REGVAR;
|
||||
Flags = CF_NONE;
|
||||
if (Size == SIZEOF_CHAR) {
|
||||
Flags |= CF_FORCECHAR;
|
||||
}
|
||||
@ -195,12 +114,15 @@ static unsigned ParseRegisterDecl (Declaration* Decl, unsigned* SC, int Reg)
|
||||
/* Constant expression. Adjust the types */
|
||||
assignadjust (Decl->Type, &lval);
|
||||
Flags |= CF_CONST;
|
||||
/* Load it into the primary */
|
||||
exprhs (Flags, 0, &lval);
|
||||
} else {
|
||||
/* Expression is not constant and in the primary */
|
||||
assignadjust (Decl->Type, &lval);
|
||||
}
|
||||
|
||||
/* Store the value into the variable */
|
||||
Flags |= CF_REGVAR;
|
||||
g_putstatic (Flags | TypeOf (Decl->Type), Reg, 0);
|
||||
|
||||
}
|
||||
@ -456,7 +378,6 @@ static void ParseOneDecl (const DeclSpec* Spec)
|
||||
unsigned SC; /* Storage class for symbol */
|
||||
unsigned SymData = 0; /* Symbol data (offset, label name, ...) */
|
||||
Declaration Decl; /* Declaration data structure */
|
||||
SymEntry* Sym; /* Symbol declared */
|
||||
|
||||
|
||||
/* Remember the storage class for the new symbol */
|
||||
@ -467,10 +388,10 @@ static void ParseOneDecl (const DeclSpec* Spec)
|
||||
|
||||
/* Set the correct storage class for functions */
|
||||
if (IsTypeFunc (Decl.Type)) {
|
||||
/* Function prototypes are always external */
|
||||
if ((SC & SC_EXTERN) == 0) {
|
||||
/* Function prototypes are always external */
|
||||
if ((SC & SC_EXTERN) == 0) {
|
||||
Warning ("Function must be extern");
|
||||
}
|
||||
}
|
||||
SC |= SC_FUNC | SC_EXTERN;
|
||||
|
||||
}
|
||||
@ -489,19 +410,19 @@ static void ParseOneDecl (const DeclSpec* Spec)
|
||||
* convert the declaration to "auto" if this is not possible.
|
||||
*/
|
||||
int Reg = 0; /* Initialize to avoid gcc complains */
|
||||
if ((SC & SC_REGISTER) != 0 && (Reg = AllocRegVar (Decl.Type)) < 0) {
|
||||
if ((SC & SC_REGISTER) != 0 && (Reg = F_AllocRegVar (CurrentFunc, Decl.Type)) < 0) {
|
||||
/* No space for this register variable, convert to auto */
|
||||
SC = (SC & ~SC_REGISTER) | SC_AUTO;
|
||||
}
|
||||
|
||||
/* Check the variable type */
|
||||
if (SC & SC_REGISTER) {
|
||||
if ((SC & SC_REGISTER) == SC_REGISTER) {
|
||||
/* Register variable */
|
||||
SymData = ParseRegisterDecl (&Decl, &SC, Reg);
|
||||
} else if (SC & SC_AUTO) {
|
||||
} else if ((SC & SC_AUTO) == SC_AUTO) {
|
||||
/* Auto variable */
|
||||
SymData = ParseAutoDecl (&Decl, &SC);
|
||||
} else if (SC & SC_STATIC) {
|
||||
} else if ((SC & SC_STATIC) == SC_STATIC) {
|
||||
/* Static variable */
|
||||
SymData = ParseStaticDecl (&Decl, &SC);
|
||||
} else {
|
||||
@ -515,12 +436,7 @@ static void ParseOneDecl (const DeclSpec* Spec)
|
||||
}
|
||||
|
||||
/* Add the symbol to the symbol table */
|
||||
Sym = AddLocalSym (Decl.Ident, Decl.Type, SC, SymData);
|
||||
|
||||
/* If we had declared a register variable, remember it now */
|
||||
if (SC & SC_REGISTER) {
|
||||
RememberRegVar (Sym);
|
||||
}
|
||||
AddLocalSym (Decl.Ident, Decl.Type, SC, SymData);
|
||||
}
|
||||
|
||||
|
||||
@ -587,70 +503,3 @@ void DeclareLocals (void)
|
||||
|
||||
|
||||
|
||||
void RestoreRegVars (int HaveResult)
|
||||
/* Restore the register variables for the local function if there are any.
|
||||
* The parameter tells us if there is a return value in ax, in that case,
|
||||
* the accumulator must be saved across the restore.
|
||||
*/
|
||||
{
|
||||
unsigned I, J;
|
||||
int Bytes, Offs;
|
||||
|
||||
/* If we don't have register variables in this function, bail out early */
|
||||
if (RegSymCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Save the accumulator if needed */
|
||||
if (!F_HasVoidReturn (CurrentFunc) && HaveResult) {
|
||||
g_save (CF_CHAR | CF_FORCECHAR);
|
||||
}
|
||||
|
||||
/* Walk through all variables. If there are several variables in a row
|
||||
* (that is, with increasing stack offset), restore them in one chunk.
|
||||
*/
|
||||
I = 0;
|
||||
while (I < RegSymCount) {
|
||||
|
||||
/* Check for more than one variable */
|
||||
const SymEntry* Sym = RegSyms[I];
|
||||
Offs = Sym->V.R.SaveOffs;
|
||||
Bytes = CheckedSizeOf (Sym->Type);
|
||||
J = I+1;
|
||||
|
||||
while (J < RegSymCount) {
|
||||
|
||||
/* Get the next symbol */
|
||||
const SymEntry* NextSym = RegSyms [J];
|
||||
|
||||
/* Get the size */
|
||||
int Size = CheckedSizeOf (NextSym->Type);
|
||||
|
||||
/* Adjacent variable? */
|
||||
if (NextSym->V.R.SaveOffs + Size != Offs) {
|
||||
/* No */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Adjacent variable */
|
||||
Bytes += Size;
|
||||
Offs -= Size;
|
||||
Sym = NextSym;
|
||||
++J;
|
||||
}
|
||||
|
||||
/* Restore the memory range */
|
||||
g_restore_regvars (Offs, Sym->V.R.RegOffs, Bytes);
|
||||
|
||||
/* Next round */
|
||||
I = J;
|
||||
}
|
||||
|
||||
/* Restore the accumulator if needed */
|
||||
if (!F_HasVoidReturn (CurrentFunc) && HaveResult) {
|
||||
g_restore (CF_CHAR | CF_FORCECHAR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -50,12 +50,12 @@
|
||||
void RC_Invalidate (RegContents* C)
|
||||
/* Invalidate all registers */
|
||||
{
|
||||
C->RegA = -1;
|
||||
C->RegX = -1;
|
||||
C->RegY = -1;
|
||||
C->SRegLo = -1;
|
||||
C->SRegHi = -1;
|
||||
C->Tmp1 = -1;
|
||||
C->RegA = UNKNOWN_REGVAL;
|
||||
C->RegX = UNKNOWN_REGVAL;
|
||||
C->RegY = UNKNOWN_REGVAL;
|
||||
C->SRegLo = UNKNOWN_REGVAL;
|
||||
C->SRegHi = UNKNOWN_REGVAL;
|
||||
C->Tmp1 = UNKNOWN_REGVAL;
|
||||
}
|
||||
|
||||
|
||||
@ -63,9 +63,9 @@ void RC_Invalidate (RegContents* C)
|
||||
void RC_InvalidateZP (RegContents* C)
|
||||
/* Invalidate all ZP registers */
|
||||
{
|
||||
C->SRegLo = -1;
|
||||
C->SRegHi = -1;
|
||||
C->Tmp1 = -1;
|
||||
C->SRegLo = UNKNOWN_REGVAL;
|
||||
C->SRegHi = UNKNOWN_REGVAL;
|
||||
C->Tmp1 = UNKNOWN_REGVAL;
|
||||
}
|
||||
|
||||
|
||||
@ -104,4 +104,4 @@ void FreeRegInfo (RegInfo* RI)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -49,6 +49,9 @@
|
||||
|
||||
|
||||
|
||||
/* Encoding for an unknown register value */
|
||||
#define UNKNOWN_REGVAL -1
|
||||
|
||||
/* Register contents */
|
||||
typedef struct RegContents RegContents;
|
||||
struct RegContents {
|
||||
|
@ -149,6 +149,18 @@ void DumpSymEntry (FILE* F, const SymEntry* E)
|
||||
|
||||
|
||||
|
||||
void CvtRegVarToAuto (SymEntry* Sym)
|
||||
/* Convert a register variable to an auto variable */
|
||||
{
|
||||
/* Change the storage class */
|
||||
Sym->Flags = (Sym->Flags & ~(SC_REGISTER | SC_STATIC | SC_EXTERN)) | SC_AUTO;
|
||||
|
||||
/* Transfer the stack offset from register save area to actual offset */
|
||||
Sym->V.Offs = Sym->V.R.SaveOffs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ChangeSymType (SymEntry* Entry, type* Type)
|
||||
/* Change the type of the given symbol */
|
||||
{
|
||||
|
@ -186,6 +186,19 @@ INLINE int SymIsRef (const SymEntry* Sym)
|
||||
# define SymIsRef(Sym) (((Sym)->Flags & SC_REF) == SC_REF)
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_INLINE)
|
||||
INLINE int SymIsRegVar (const SymEntry* Sym)
|
||||
/* Return true if the given entry is a register variable */
|
||||
{
|
||||
return ((Sym->Flags & SC_REGISTER) == SC_REGISTER);
|
||||
}
|
||||
#else
|
||||
# define SymIsRegVar(Sym) (((Sym)->Flags & SC_REGISTER) == SC_REGISTER)
|
||||
#endif
|
||||
|
||||
void CvtRegVarToAuto (SymEntry* Sym);
|
||||
/* Convert a register variable to an auto variable */
|
||||
|
||||
void ChangeSymType (SymEntry* Entry, type* Type);
|
||||
/* Change the type of the given symbol */
|
||||
|
||||
|
@ -81,9 +81,6 @@ SymTable EmptySymTab = {
|
||||
#define SYMTAB_SIZE_STRUCT 19U
|
||||
#define SYMTAB_SIZE_LABEL 7U
|
||||
|
||||
/* Predefined lexical levels */
|
||||
#define LEX_LEVEL_GLOBAL 1U
|
||||
|
||||
/* The current and root symbol tables */
|
||||
static unsigned LexicalLevel = 0; /* For safety checks */
|
||||
static SymTable* SymTab0 = 0;
|
||||
@ -198,6 +195,14 @@ static void CheckSymTable (SymTable* Tab)
|
||||
|
||||
|
||||
|
||||
unsigned GetLexicalLevel (void)
|
||||
/* Return the current lexical level */
|
||||
{
|
||||
return LexicalLevel;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void EnterGlobalLevel (void)
|
||||
/* Enter the program global lexical level */
|
||||
{
|
||||
@ -241,7 +246,7 @@ void EnterFunctionLevel (void)
|
||||
SymTable* S;
|
||||
|
||||
/* New lexical level */
|
||||
++LexicalLevel;
|
||||
PRECONDITION (++LexicalLevel == LEX_LEVEL_FUNCTION);
|
||||
|
||||
/* Get a new symbol table and make it current */
|
||||
S = NewSymTable (SYMTAB_SIZE_FUNCTION);
|
||||
@ -263,7 +268,7 @@ void RememberFunctionLevel (struct FuncDesc* F)
|
||||
/* Remember the symbol tables for the level and leave the level without checks */
|
||||
{
|
||||
/* Leave the lexical level */
|
||||
--LexicalLevel;
|
||||
PRECONDITION (LexicalLevel-- == LEX_LEVEL_FUNCTION);
|
||||
|
||||
/* Remember the tables */
|
||||
F->SymTab = SymTab;
|
||||
@ -280,7 +285,7 @@ void ReenterFunctionLevel (struct FuncDesc* F)
|
||||
/* Reenter the function lexical level using the existing tables from F */
|
||||
{
|
||||
/* New lexical level */
|
||||
++LexicalLevel;
|
||||
PRECONDITION (++LexicalLevel == LEX_LEVEL_FUNCTION);
|
||||
|
||||
/* Make the tables current again */
|
||||
F->SymTab->PrevTab = SymTab;
|
||||
@ -299,7 +304,7 @@ void LeaveFunctionLevel (void)
|
||||
/* Leave function lexical level */
|
||||
{
|
||||
/* Leave the lexical level */
|
||||
--LexicalLevel;
|
||||
PRECONDITION (LexicalLevel-- == LEX_LEVEL_FUNCTION);
|
||||
|
||||
/* Check the tables */
|
||||
CheckSymTable (SymTab);
|
||||
|
@ -68,6 +68,10 @@ extern SymTable EmptySymTab;
|
||||
/* Forwards */
|
||||
struct FuncDesc;
|
||||
|
||||
/* Predefined lexical levels */
|
||||
#define LEX_LEVEL_GLOBAL 1U
|
||||
#define LEX_LEVEL_FUNCTION 2U
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
@ -76,6 +80,9 @@ struct FuncDesc;
|
||||
|
||||
|
||||
|
||||
unsigned GetLexicalLevel (void);
|
||||
/* Return the current lexical level */
|
||||
|
||||
void EnterGlobalLevel (void);
|
||||
/* Enter the program global lexical level */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user