X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fcc65%2Flocals.c;h=02155f412b8a84c916616e06e953e2d5fe6152b9;hb=96d0b00a76acd2eae545ac470aac9d8d5645f931;hp=3a6e6a5ab3e9e4cc8f8c46142944f201709bcd87;hpb=d171b3dac96e836a6b952b14951d0479c31ab280;p=cc65 diff --git a/src/cc65/locals.c b/src/cc65/locals.c index 3a6e6a5ab..02155f412 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -1,15 +1,15 @@ /*****************************************************************************/ /* */ -/* locals.c */ +/* locals.c */ /* */ -/* Local variable handling for the cc65 C compiler */ +/* Local variable handling for the cc65 C compiler */ /* */ /* */ /* */ -/* (C) 2000 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ +/* (C) 2000-2013, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ @@ -33,8 +33,11 @@ -#include "../common/xmalloc.h" +/* common */ +#include "xmalloc.h" +#include "xsprintf.h" +/* cc65 */ #include "anonname.h" #include "asmlabel.h" #include "codegen.h" @@ -43,398 +46,519 @@ #include "expr.h" #include "function.h" #include "global.h" -#include "symtab.h" +#include "loadexpr.h" #include "locals.h" +#include "stackptr.h" +#include "standard.h" +#include "symtab.h" +#include "typeconv.h" +#include "input.h" /*****************************************************************************/ -/* Data */ +/* Code */ /*****************************************************************************/ -/* 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 */ +static unsigned AllocLabel (void (*UseSeg) ()) +/* Switch to a segment, define a local label and return it */ +{ + unsigned Label; + /* Switch to the segment */ + UseSeg (); + /* Define the variable label */ + Label = GetLocalLabel (); + g_defdatalabel (Label); -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ + /* Return the label */ + return Label; +} -void InitRegVars (void) -/* Initialize register variable control data */ +static void AllocStorage (unsigned Label, void (*UseSeg) (), unsigned Size) +/* Reserve Size bytes of BSS storage prefixed by a local label. */ { - /* If the register space is zero, bail out */ - if (MaxRegSpace == 0) { - return; - } + /* Switch to the segment */ + UseSeg (); + + /* Define the variable label */ + g_defdatalabel (Label); - /* 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 = xmalloc (MaxRegSpace * sizeof (RegSyms[0])); - RegOffs = MaxRegSpace; + /* Reserve space for the data */ + g_res (Size); } -void DoneRegVars (void) -/* Free the register variables */ +static void ParseRegisterDecl (Declaration* Decl, int Reg) +/* Parse the declaration of a register variable. Reg is the offset of the +** variable in the register bank. +*/ { - xfree (RegSyms); - RegSyms = 0; - RegOffs = MaxRegSpace; - RegSymCount = 0; -} + SymEntry* Sym; + /* Determine if this is a compound variable */ + int IsCompound = IsClassStruct (Decl->Type) || IsTypeArray (Decl->Type); + /* Get the size of the variable */ + unsigned Size = SizeOf (Decl->Type); -static int AllocRegVar (const SymEntry* Sym, const type* tarray) -/* Allocate a register variable with the given amount of storage. 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 = SizeOf (tarray); - - /* 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; - RegSyms [RegSymCount++] = Sym; - return RegOffs; - } - } + /* Save the current contents of the register variable on stack */ + F_AllocLocalSpace (CurrentFunc); + g_save_regvars (Reg, Size); - /* No space left or no allocation */ - return -1; -} + /* Add the symbol to the symbol table. We do that now, because for register + ** variables the current stack pointer is implicitly used as location for + ** the save area. + */ + Sym = AddLocalSym (Decl->Ident, Decl->Type, Decl->StorageClass, Reg); + /* Check for an optional initialization */ + if (CurTok.Tok == TOK_ASSIGN) { + ExprDesc Expr; -static void ParseOneDecl (const DeclSpec* Spec) -/* Parse one variable declaration */ -{ - int Size; /* Size of an auto variable */ - int SC; /* Storage class for symbol */ - int SymData = 0; /* Symbol data (offset, label name, ...) */ - unsigned flags = 0; /* Code generator flags */ - Declaration Decl; /* Declaration data structure */ + /* Skip the '=' */ + NextToken (); - /* Remember the storage class for the new symbol */ - SC = Spec->StorageClass; + /* Special handling for compound types */ + if (IsCompound) { - /* Read the declaration */ - ParseDecl (Spec, &Decl, DM_NEED_IDENT); + /* Switch to read only data and define a label for the + ** initialization data. + */ + unsigned InitLabel = AllocLabel (g_userodata); - /* Set the correct storage class for functions */ - if (IsTypeFunc (Decl.Type)) { - /* Function prototypes are always external */ - if ((SC & SC_EXTERN) == 0) { - Warning (WARN_FUNC_MUST_BE_EXTERN); - } - SC |= SC_FUNC | SC_EXTERN; + /* Parse the initialization generating a memory image of the + ** data in the RODATA segment. The function does return the size + ** of the initialization data, which may be greater than the + ** actual size of the type, if the type is a structure with a + ** flexible array member that has been initialized. Since we must + ** know the size of the data in advance for register variables, + ** we cannot allow that here. + */ + if (ParseInit (Sym->Type) != Size) { + Error ("Cannot initialize flexible array members of storage class 'register'"); + } + + /* Generate code to copy this data into the variable space */ + g_initregister (InitLabel, Reg, Size); + + } else { + /* Parse the expression */ + hie1 (&Expr); + + /* Convert it to the target type */ + TypeConversion (&Expr, Sym->Type); + + /* Load the value into the primary */ + LoadExpr (CF_NONE, &Expr); + + /* Store the value into the variable */ + g_putstatic (CF_REGVAR | TypeOf (Sym->Type), Reg, 0); + + } + + /* Mark the variable as referenced */ + Sym->Flags |= SC_REF; } - /* If we don't have a name, this was flagged as an error earlier. - * To avoid problems later, use an anonymous name here. - */ - if (Decl.Ident[0] == '\0') { - AnonName (Decl.Ident, "param"); + /* Cannot allocate a variable of zero size */ + if (Size == 0) { + Error ("Variable '%s' has unknown size", Decl->Ident); } +} + - /* Handle anything that needs storage (no functions, no typdefs) */ - if ((SC & SC_FUNC) != SC_FUNC && (SC & SC_TYPEDEF) != SC_TYPEDEF) { - /* Get the size of the variable */ - Size = SizeOf (Decl.Type); +static void ParseAutoDecl (Declaration* Decl) +/* Parse the declaration of an auto variable. */ +{ + unsigned Flags; + SymEntry* Sym; + + /* Determine if this is a compound variable */ + int IsCompound = IsClassStruct (Decl->Type) || IsTypeArray (Decl->Type); + + /* Get the size of the variable */ + unsigned Size = SizeOf (Decl->Type); + + /* Check if this is a variable on the stack or in static memory */ + if (IS_Get (&StaticLocals) == 0) { - if (SC & (SC_AUTO | SC_REGISTER)) { + /* Add the symbol to the symbol table. The stack offset we use here + ** may get corrected later. + */ + Sym = AddLocalSym (Decl->Ident, Decl->Type, + Decl->StorageClass, + F_GetStackPtr (CurrentFunc) - (int) Size); - /* Auto variable */ - if (StaticLocals == 0) { + /* Check for an optional initialization */ + if (CurTok.Tok == TOK_ASSIGN) { - /* Change SC in case it was register */ - SC = (SC & ~SC_REGISTER) | SC_AUTO; - if (curtok == TOK_ASSIGN) { + ExprDesc Expr; - struct expent lval; + /* Skip the '=' */ + NextToken (); - /* Allocate previously reserved local space */ - AllocLocalSpace (CurrentFunc); + /* Special handling for compound types */ + if (IsCompound) { - /* Switch to the code segment. */ - g_usecode (); + /* Switch to read only data and define a label for the + ** initialization data. + */ + unsigned InitLabel = AllocLabel (g_userodata); - /* Skip the '=' */ - NextToken (); + /* Parse the initialization generating a memory image of the + ** data in the RODATA segment. The function will return the + ** actual size of the initialization data, which may be + ** greater than the size of the variable if it is a struct + ** that contains a flexible array member and we're not in + ** ANSI mode. + */ + Size = ParseInit (Sym->Type); - /* Setup the type flags for the assignment */ - flags = Size == 1? CF_FORCECHAR : CF_NONE; + /* Now reserve space for the variable on the stack and correct + ** the offset in the symbol table entry. + */ + Sym->V.Offs = F_ReserveLocalSpace (CurrentFunc, Size); - /* Get the expression into the primary */ - if (evalexpr (flags, hie1, &lval) == 0) { - /* Constant expression. Adjust the types */ - assignadjust (Decl.Type, &lval); - flags |= CF_CONST; - } else { - /* Expression is not constant and in the primary */ - assignadjust (Decl.Type, &lval); - } + /* Next, allocate the space on the stack. This means that the + ** variable is now located at offset 0 from the current sp. + */ + F_AllocLocalSpace (CurrentFunc); - /* Push the value */ - g_push (flags | TypeOf (Decl.Type), lval.e_const); + /* Generate code to copy the initialization data into the + ** variable space + */ + g_initauto (InitLabel, Size); - /* Mark the variable as referenced */ - SC |= SC_REF; + } else { - /* Variable is located at the current SP */ - SymData = oursp; + /* Allocate previously reserved local space */ + F_AllocLocalSpace (CurrentFunc); - } else { - /* Non-initialized local variable. Just keep track of - * the space needed. - */ - SymData = ReserveLocalSpace (CurrentFunc, Size); - } + /* Setup the type flags for the assignment */ + Flags = (Size == SIZEOF_CHAR)? CF_FORCECHAR : CF_NONE; - } else { + /* Parse the expression */ + hie1 (&Expr); - /* Static local variables. */ - SC = (SC & ~(SC_REGISTER | SC_AUTO)) | SC_STATIC; + /* Convert it to the target type */ + TypeConversion (&Expr, Sym->Type); - /* Put them into the BSS */ - g_usebss (); + /* If the value is not const, load it into the primary. + ** Otherwise pass the information to the code generator. + */ + if (ED_IsConstAbsInt (&Expr)) { + Flags |= CF_CONST; + } else { + LoadExpr (CF_NONE, &Expr); + ED_MakeRVal (&Expr); + } - /* Define the variable label */ - SymData = GetLabel (); - g_defloclabel (SymData); + /* Push the value */ + g_push (Flags | TypeOf (Sym->Type), Expr.IVal); - /* Reserve space for the data */ - g_res (Size); + } - /* Allow assignments */ - if (curtok == TOK_ASSIGN) { + /* Mark the variable as referenced */ + Sym->Flags |= SC_REF; - struct expent lval; + /* Make note of auto variables initialized in current block. + ** We abuse the Collection somewhat by using it to store line + ** numbers. + */ + CollReplace (&CurrentFunc->LocalsBlockStack, + (void *)(size_t)GetCurrentLine (), + CollCount (&CurrentFunc->LocalsBlockStack) - 1); - /* Switch to the code segment. */ - g_usecode (); + } else { + /* Non-initialized local variable. Just keep track of + ** the space needed. + */ + F_ReserveLocalSpace (CurrentFunc, Size); + } - /* Skip the '=' */ - NextToken (); + } else { - /* Get the expression into the primary */ - expression1 (&lval); + unsigned DataLabel; - /* Make type adjustments if needed */ - assignadjust (Decl.Type, &lval); - /* Setup the type flags for the assignment */ - flags = TypeOf (Decl.Type); - if (Size == 1) { - flags |= CF_FORCECHAR; - } + /* Static local variables. */ + Decl->StorageClass = (Decl->StorageClass & ~SC_AUTO) | SC_STATIC; - /* Store the value into the variable */ - g_putstatic (flags, SymData, 0); + /* Generate a label, but don't define it */ + DataLabel = GetLocalLabel (); - /* Mark the variable as referenced */ - SC |= SC_REF; - } - } + /* Add the symbol to the symbol table. */ + Sym = AddLocalSym (Decl->Ident, Decl->Type, Decl->StorageClass, DataLabel); - } else if ((SC & SC_STATIC) == SC_STATIC) { + /* Allow assignments */ + if (CurTok.Tok == TOK_ASSIGN) { - /* Static data */ - if (curtok == TOK_ASSIGN) { + ExprDesc Expr; - /* Initialization ahead, switch to data segment */ - if (IsConst (Decl.Type)) { - g_userodata (); - } else { - g_usedata (); - } + /* Skip the '=' */ + NextToken (); - /* Define the variable label */ - SymData = GetLabel (); - g_defloclabel (SymData); + if (IsCompound) { - /* Skip the '=' */ - NextToken (); + /* Switch to read only data and define a label for the + ** initialization data. + */ + unsigned InitLabel = AllocLabel (g_userodata); - /* Allow initialization of static vars */ - ParseInit (Decl.Type); + /* Parse the initialization generating a memory image of the + ** data in the RODATA segment. + */ + Size = ParseInit (Sym->Type); - /* Mark the variable as referenced */ - SC |= SC_REF; + /* Allocate space for the variable */ + AllocStorage (DataLabel, g_usebss, Size); - } else { + /* Generate code to copy this data into the variable space */ + g_initstatic (InitLabel, DataLabel, Size); - /* Uninitialized data, use BSS segment */ - g_usebss (); + } else { - /* Define the variable label */ - SymData = GetLabel (); - g_defloclabel (SymData); + /* Allocate space for the variable */ + AllocStorage (DataLabel, g_usebss, Size); - /* Reserve space for the data */ - g_res (Size); + /* Parse the expression */ + hie1 (&Expr); - } - } + /* Convert it to the target type */ + TypeConversion (&Expr, Sym->Type); - } + /* Load the value into the primary */ + LoadExpr (CF_NONE, &Expr); + + /* Store the value into the variable */ + g_putstatic (TypeOf (Sym->Type), DataLabel, 0); + } + + /* Mark the variable as referenced */ + Sym->Flags |= SC_REF; + + } else { - /* If the symbol is not marked as external, it will be defined */ - if ((SC & SC_EXTERN) == 0) { - SC |= SC_DEF; + /* No assignment - allocate a label and space for the variable */ + AllocStorage (DataLabel, g_usebss, Size); + + } } - /* Add the symbol to the symbol table */ - AddLocalSym (Decl.Ident, Decl.Type, SC, SymData); + /* Cannot allocate a variable of zero size */ + if (Size == 0) { + Error ("Variable '%s' has unknown size", Decl->Ident); + } } -void DeclareLocals (void) -/* Declare local variables and types. */ +static void ParseStaticDecl (Declaration* Decl) +/* Parse the declaration of a static variable. */ { - /* Loop until we don't find any more variables */ - while (1) { + unsigned Size; - /* Check variable declarations. We need to distinguish between a - * default int type and the end of variable declarations. So we - * will do the following: If there is no explicit storage class - * specifier *and* no explicit type given, it is assume that we - * have reached the end of declarations. - */ - DeclSpec Spec; - ParseDeclSpec (&Spec, SC_AUTO, T_INT); - if ((Spec.Flags & DS_DEF_STORAGE) != 0 && (Spec.Flags & DS_DEF_TYPE) != 0) { - break; - } - - /* Accept type only declarations */ - if (curtok == TOK_SEMI) { - /* Type declaration only */ - CheckEmptyDecl (&Spec); - NextToken (); - continue; - } - - /* Parse a comma separated variable list */ - while (1) { - - /* Parse one declaration */ - ParseOneDecl (&Spec); - - /* Check if there is more */ - if (curtok == TOK_COMMA) { - /* More to come */ - NextToken (); - } else { - /* Done */ - break; - } - } - - /* A semicolon must follow */ - ConsumeSemi (); - } + /* Generate a label, but don't define it */ + unsigned DataLabel = GetLocalLabel (); - /* Be sure to allocate any reserved space for locals */ - AllocLocalSpace (CurrentFunc); + /* Add the symbol to the symbol table. */ + SymEntry* Sym = AddLocalSym (Decl->Ident, Decl->Type, + Decl->StorageClass, + DataLabel); - /* In case we switched away from code segment, switch back now */ - g_usecode (); -} + /* Static data */ + if (CurTok.Tok == TOK_ASSIGN) { + /* Initialization ahead, switch to data segment and define the label. + ** For arrays, we need to check the elements of the array for + ** constness, not the array itself. + */ + if (IsQualConst (GetBaseElementType (Sym->Type))) { + g_userodata (); + } else { + g_usedata (); + } + g_defdatalabel (DataLabel); + /* Skip the '=' */ + NextToken (); -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; + /* Allow initialization of static vars */ + Size = ParseInit (Sym->Type); - /* If we don't have register variables in this function, bail out early */ - if (RegSymCount == 0) { - return; - } + /* Mark the variable as referenced */ + Sym->Flags |= SC_REF; + + } else { + + /* Get the size of the variable */ + Size = SizeOf (Decl->Type); + + /* Allocate a label and space for the variable in the BSS segment */ + AllocStorage (DataLabel, g_usebss, Size); - /* Save the accumulator if needed */ - if (!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) { + /* Cannot allocate a variable of zero size */ + if (Size == 0) { + Error ("Variable '%s' has unknown size", Decl->Ident); + } +} - /* Check for more than one variable */ - const SymEntry* Sym = RegSyms[I]; - Offs = Sym->V.Offs; - Bytes = SizeOf (Sym->Type); - J = I+1; - while (J < RegSymCount) { - /* Get the next symbol */ - const SymEntry* NextSym = RegSyms [J]; +static void ParseOneDecl (const DeclSpec* Spec) +/* Parse one variable declaration */ +{ + Declaration Decl; /* Declaration data structure */ - /* Get the size */ - int Size = SizeOf (NextSym->Type); - /* Adjacent variable? */ - if (NextSym->V.Offs + Size != Offs) { - /* No */ - break; - } + /* Read the declaration */ + ParseDecl (Spec, &Decl, DM_NEED_IDENT); - /* Adjacent variable */ - Bytes += Size; - Offs -= Size; - Sym = NextSym; - ++J; - } + /* Set the correct storage class for functions */ + if ((Decl.StorageClass & SC_FUNC) == SC_FUNC) { + /* Function prototypes are always external */ + if ((Decl.StorageClass & SC_EXTERN) == 0) { + Warning ("Function must be extern"); + } + Decl.StorageClass |= SC_EXTERN; + } - /* Restore the memory range */ - g_restore_regvars (Offs, Sym->V.Offs, Bytes); + /* If we don't have a name, this was flagged as an error earlier. + ** To avoid problems later, use an anonymous name here. + */ + if (Decl.Ident[0] == '\0') { + AnonName (Decl.Ident, "param"); + } - /* Next round */ - I = J; + /* If the symbol is not marked as external, it will be defined now */ + if ((Decl.StorageClass & SC_EXTERN) == 0) { + Decl.StorageClass |= SC_DEF; } - /* Restore the accumulator if needed */ - if (!HasVoidReturn (CurrentFunc) && HaveResult) { - g_restore (CF_CHAR | CF_FORCECHAR); + /* Handle anything that needs storage (no functions, no typdefs) */ + if ((Decl.StorageClass & SC_FUNC) != SC_FUNC && + (Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { + + /* If we have a register variable, try to allocate a register and + ** convert the declaration to "auto" if this is not possible. + */ + int Reg = 0; /* Initialize to avoid gcc complains */ + if ((Decl.StorageClass & SC_REGISTER) != 0 && + (Reg = F_AllocRegVar (CurrentFunc, Decl.Type)) < 0) { + /* No space for this register variable, convert to auto */ + Decl.StorageClass = (Decl.StorageClass & ~SC_REGISTER) | SC_AUTO; + } + + /* Check the variable type */ + if ((Decl.StorageClass & SC_REGISTER) == SC_REGISTER) { + /* Register variable */ + ParseRegisterDecl (&Decl, Reg); + } else if ((Decl.StorageClass & SC_AUTO) == SC_AUTO) { + /* Auto variable */ + ParseAutoDecl (&Decl); + } else if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN) { + /* External identifier - may not get initialized */ + if (CurTok.Tok == TOK_ASSIGN) { + Error ("Cannot initialize externals"); + } + /* Add the external symbol to the symbol table */ + AddLocalSym (Decl.Ident, Decl.Type, Decl.StorageClass, 0); + } else if ((Decl.StorageClass & SC_STATIC) == SC_STATIC) { + /* Static variable */ + ParseStaticDecl (&Decl); + } else { + Internal ("Invalid storage class in ParseOneDecl: %04X", Decl.StorageClass); + } + + } else { + + /* Add the symbol to the symbol table */ + AddLocalSym (Decl.Ident, Decl.Type, Decl.StorageClass, 0); + } } +void DeclareLocals (void) +/* Declare local variables and types. */ +{ + /* Remember the current stack pointer */ + int InitialStack = StackPtr; + + /* A place to store info about potential initializations of auto variables */ + CollAppend (&CurrentFunc->LocalsBlockStack, 0); + + /* Loop until we don't find any more variables */ + while (1) { + + /* Check variable declarations. We need to distinguish between a + ** default int type and the end of variable declarations. So we + ** will do the following: If there is no explicit storage class + ** specifier *and* no explicit type given, *and* no type qualifiers + ** have been read, it is assumed that we have reached the end of + ** declarations. + */ + DeclSpec Spec; + ParseDeclSpec (&Spec, SC_AUTO, T_INT); + if ((Spec.Flags & DS_DEF_STORAGE) != 0 && /* No storage spec */ + (Spec.Flags & DS_DEF_TYPE) != 0 && /* No type given */ + GetQualifier (Spec.Type) == T_QUAL_NONE) { /* No type qualifier */ + break; + } + + /* Accept type only declarations */ + if (CurTok.Tok == TOK_SEMI) { + /* Type declaration only */ + CheckEmptyDecl (&Spec); + NextToken (); + continue; + } + + /* Parse a comma separated variable list */ + while (1) { + + /* Parse one declaration */ + ParseOneDecl (&Spec); + + /* Check if there is more */ + if (CurTok.Tok == TOK_COMMA) { + /* More to come */ + NextToken (); + } else { + /* Done */ + break; + } + } + + /* A semicolon must follow */ + ConsumeSemi (); + } + + /* Be sure to allocate any reserved space for locals */ + F_AllocLocalSpace (CurrentFunc); + + /* No auto variables were inited. No new block on the stack then. */ + if (CollLast (&CurrentFunc->LocalsBlockStack) == NULL) { + CollPop (&CurrentFunc->LocalsBlockStack); + } + + /* In case we've allocated local variables in this block, emit a call to + ** the stack checking routine if stack checks are enabled. + */ + if (IS_Get (&CheckStack) && InitialStack != StackPtr) { + g_cstackcheck (); + } +}