From 70755921a959d71bbf94f4fc0425cdb3ba26d71c Mon Sep 17 00:00:00 2001 From: cuz Date: Mon, 25 Nov 2002 15:05:15 +0000 Subject: [PATCH] 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 --- src/cc65/codeent.c | 86 ++++++++++----------- src/cc65/codegen.c | 46 +++++++++++- src/cc65/codegen.h | 3 + src/cc65/codeinfo.c | 3 + src/cc65/codeseg.c | 13 ++-- src/cc65/coptadd.c | 2 +- src/cc65/coptadd.h | 4 +- src/cc65/coptstop.c | 76 ++++++++++++++----- src/cc65/declare.c | 33 +++++---- src/cc65/function.c | 177 +++++++++++++++++++++++++++++++++++++------- src/cc65/function.h | 6 ++ src/cc65/locals.c | 175 +++---------------------------------------- src/cc65/reginfo.c | 20 ++--- src/cc65/reginfo.h | 3 + src/cc65/symentry.c | 12 +++ src/cc65/symentry.h | 13 ++++ src/cc65/symtab.c | 19 +++-- src/cc65/symtab.h | 7 ++ 18 files changed, 405 insertions(+), 293 deletions(-) diff --git a/src/cc65/codeent.c b/src/cc65/codeent.c index 6e96aaa07..259cea5a5 100644 --- a/src/cc65/codeent.c +++ b/src/cc65/codeent.c @@ -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: diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 6e05351d0..a9f5dd694 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -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 */ { diff --git a/src/cc65/codegen.h b/src/cc65/codegen.h index 649d5e789..17dfcd513 100644 --- a/src/cc65/codegen.h +++ b/src/cc65/codegen.h @@ -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 */ diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index dae3cd0e4..cf04baf9c 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -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 }, diff --git a/src/cc65/codeseg.c b/src/cc65/codeseg.c index ab500eee2..06260ff3a 100644 --- a/src/cc65/codeseg.c +++ b/src/cc65/codeseg.c @@ -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; } diff --git a/src/cc65/coptadd.c b/src/cc65/coptadd.c index 3a11a9c6a..31beea9cf 100644 --- a/src/cc65/coptadd.c +++ b/src/cc65/coptadd.c @@ -288,7 +288,7 @@ unsigned OptAdd3 (CodeSeg* S) * * jsr pushax * lda xxx - * ldy yyy + * ldx yyy * jsr tosaddax * * and replace it by diff --git a/src/cc65/coptadd.h b/src/cc65/coptadd.h index accf3d831..0791afbf8 100644 --- a/src/cc65/coptadd.h +++ b/src/cc65/coptadd.h @@ -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 diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index ed51fe130..7686a7e70 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -34,6 +34,7 @@ #include +#include /* 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", }; diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 0c647c835..22d8c310e 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -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; diff --git a/src/cc65/function.c b/src/cc65/function.c index 66777b189..edf795b8d 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -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,18 +385,49 @@ 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); } + /* 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 (); + g_stackcheck (); } - /* Generate function entry code if needed */ - g_enter (TypeOf (Func->Type), F_GetParamSize (CurrentFunc)); + /* 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 (); diff --git a/src/cc65/function.h b/src/cc65/function.h index 56e532179..70f3dd2ed 100644 --- a/src/cc65/function.h +++ b/src/cc65/function.h @@ -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. */ diff --git a/src/cc65/locals.c b/src/cc65/locals.c index ad87c3ebd..a4411e410 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -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); - } -} - - - diff --git a/src/cc65/reginfo.c b/src/cc65/reginfo.c index e50c35581..8de716ddf 100644 --- a/src/cc65/reginfo.c +++ b/src/cc65/reginfo.c @@ -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) - + diff --git a/src/cc65/reginfo.h b/src/cc65/reginfo.h index ecfe08a70..ad49043d3 100644 --- a/src/cc65/reginfo.h +++ b/src/cc65/reginfo.h @@ -49,6 +49,9 @@ +/* Encoding for an unknown register value */ +#define UNKNOWN_REGVAL -1 + /* Register contents */ typedef struct RegContents RegContents; struct RegContents { diff --git a/src/cc65/symentry.c b/src/cc65/symentry.c index 6745680d3..bac584bec 100644 --- a/src/cc65/symentry.c +++ b/src/cc65/symentry.c @@ -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 */ { diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index e81945d64..b1f864703 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -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 */ diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 988190cd6..850725be9 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -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); diff --git a/src/cc65/symtab.h b/src/cc65/symtab.h index 317d8f8b8..5321ac07b 100644 --- a/src/cc65/symtab.h +++ b/src/cc65/symtab.h @@ -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 */ -- 2.39.5