/*****************************************************************************/ /* */ /* locals.c */ /* */ /* Local variable handling for the cc65 C compiler */ /* */ /* */ /* */ /* (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 */ /* warranty. In no event will the authors be held liable for any damages */ /* arising from the use of this software. */ /* */ /* Permission is granted to anyone to use this software for any purpose, */ /* including commercial applications, and to alter it and redistribute it */ /* freely, subject to the following restrictions: */ /* */ /* 1. The origin of this software must not be misrepresented; you must not */ /* claim that you wrote the original software. If you use this software */ /* in a product, an acknowledgment in the product documentation would be */ /* appreciated but is not required. */ /* 2. Altered source versions must be plainly marked as such, and must not */ /* be misrepresented as being the original software. */ /* 3. This notice may not be removed or altered from any source */ /* distribution. */ /* */ /*****************************************************************************/ /* common */ #include "xmalloc.h" #include "xsprintf.h" /* cc65 */ #include "anonname.h" #include "asmlabel.h" #include "codegen.h" #include "declare.h" #include "error.h" #include "expr.h" #include "function.h" #include "global.h" #include "initdata.h" #include "loadexpr.h" #include "locals.h" #include "stackptr.h" #include "standard.h" #include "staticassert.h" #include "symtab.h" #include "typeconv.h" #include "input.h" /*****************************************************************************/ /* Code */ /*****************************************************************************/ static unsigned AllocLabel (void (*UseSeg) ()) /* Switch to a segment, define a local data label and return it */ { unsigned DataLabel; /* Switch to the segment */ UseSeg (); /* Define the variable label */ DataLabel = GetLocalDataLabel (); g_defdatalabel (DataLabel); /* Return the label */ return DataLabel; } static void AllocStorage (unsigned DataLabel, void (*UseSeg) (), unsigned Size) /* Reserve Size bytes of BSS storage prefixed by a local data label. */ { /* Switch to the segment */ UseSeg (); /* Define the variable label */ g_defdatalabel (DataLabel); /* Reserve space for the data */ g_res (Size); } 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. */ { 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); /* Save the current contents of the register variable on stack */ F_AllocLocalSpace (CurrentFunc); g_save_regvars (Reg, Size); /* 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) { /* Skip the '=' */ NextToken (); /* Special handling for compound types */ if (IsCompound) { /* Switch to read only data and define a label for the ** initialization data. */ unsigned InitLabel = AllocLabel (g_userodata); /* 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 { ExprDesc Expr; ED_Init (&Expr); /* 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); /* This has to be done at sequence point */ DoDeferred (SQP_KEEP_NONE, &Expr); } /* Mark the variable as referenced */ Sym->Flags |= SC_REF; } /* Cannot allocate a variable of unknown size */ if (HasUnknownSize (Sym->Type)) { if (IsTypeArray (Decl->Type)) { Error ("Array '%s' has unknown size", Decl->Ident); } else { Error ("Variable '%s' has unknown size", Decl->Ident); } } } 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) { /* 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); /* Check for an optional initialization */ if (CurTok.Tok == TOK_ASSIGN) { /* Skip the '=' */ NextToken (); /* Special handling for compound types */ if (IsCompound) { /* Switch to read only data and define a label for the ** initialization data. */ unsigned InitLabel = AllocLabel (g_userodata); /* 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); /* 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); /* 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); /* Generate code to copy the initialization data into the ** variable space */ g_initauto (InitLabel, Size); } else { ExprDesc Expr; ED_Init (&Expr); /* Allocate previously reserved local space */ F_AllocLocalSpace (CurrentFunc); /* Setup the type flags for the assignment */ Flags = (Size == SIZEOF_CHAR)? CF_FORCECHAR : CF_NONE; /* Parse the expression */ hie1 (&Expr); /* Convert it to the target type */ TypeConversion (&Expr, Sym->Type); /* 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_MarkExprAsRVal (&Expr); } /* Push the value */ g_push (Flags | TypeOf (Sym->Type), Expr.IVal); /* This has to be done at sequence point */ DoDeferred (SQP_KEEP_NONE, &Expr); } /* Mark the variable as referenced */ Sym->Flags |= SC_REF; /* 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); } else { /* Non-initialized local variable. Just keep track of ** the space needed. */ F_ReserveLocalSpace (CurrentFunc, Size); } } else { unsigned DataLabel; /* Static local variables. */ Decl->StorageClass = (Decl->StorageClass & ~SC_AUTO) | SC_STATIC; /* Generate a label, but don't define it */ DataLabel = GetLocalDataLabel (); /* Add the symbol to the symbol table. */ Sym = AddLocalSym (Decl->Ident, Decl->Type, Decl->StorageClass, DataLabel); /* Allow assignments */ if (CurTok.Tok == TOK_ASSIGN) { /* Skip the '=' */ NextToken (); if (IsCompound) { /* Switch to read only data and define a label for the ** initialization data. */ unsigned InitLabel = AllocLabel (g_userodata); /* Parse the initialization generating a memory image of the ** data in the RODATA segment. */ Size = ParseInit (Sym->Type); /* Allocate space for the variable */ AllocStorage (DataLabel, g_usebss, Size); /* Generate code to copy this data into the variable space */ g_initstatic (InitLabel, DataLabel, Size); } else { ExprDesc Expr; ED_Init (&Expr); /* Allocate space for the variable */ AllocStorage (DataLabel, g_usebss, 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 (CF_STATIC | TypeOf (Sym->Type), DataLabel, 0); /* This has to be done at sequence point */ DoDeferred (SQP_KEEP_NONE, &Expr); } /* Mark the variable as referenced */ Sym->Flags |= SC_REF; } else { /* No assignment - allocate a label and space for the variable */ AllocStorage (DataLabel, g_usebss, Size); } } /* Cannot allocate an incomplete variable */ if (HasUnknownSize (Sym->Type)) { if (IsTypeArray (Decl->Type)) { Error ("Array '%s' has unknown size", Decl->Ident); } else { Error ("Variable '%s' has unknown size", Decl->Ident); } } } static void ParseStaticDecl (Declaration* Decl) /* Parse the declaration of a static variable. */ { unsigned Size; /* Generate a label, but don't define it */ unsigned DataLabel = GetLocalDataLabel (); /* Add the symbol to the symbol table. */ SymEntry* Sym = AddLocalSym (Decl->Ident, Decl->Type, Decl->StorageClass, DataLabel); /* 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 (); /* Allow initialization of static vars */ Size = ParseInit (Sym->Type); /* 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); } /* Cannot allocate an incomplete variable */ if (HasUnknownSize (Sym->Type)) { if (IsTypeArray (Decl->Type)) { Error ("Array '%s' has unknown size", Decl->Ident); } else { Error ("Variable '%s' has unknown size", Decl->Ident); } } } static void ParseOneDecl (const DeclSpec* Spec) /* Parse one variable declaration */ { Declaration Decl; /* Declaration data structure */ /* Read the declaration */ ParseDecl (Spec, &Decl, DM_NEED_IDENT); /* Check if there are any non-extern storage classes set for function ** declarations. Function can only be declared inside functions with the ** 'extern' storage class specifier or no storage class specifier at all. */ if ((Decl.StorageClass & SC_FUNC) == SC_FUNC) { /* Check if there are explicitly specified non-external storage classes */ if ((Spec->Flags & DS_DEF_STORAGE) != DS_DEF_STORAGE && (Decl.StorageClass & SC_EXTERN) == 0 && (Decl.StorageClass & SC_STORAGEMASK) != 0) { Error ("Illegal storage class on function"); } /* The default storage class could be wrong. Just clear them */ Decl.StorageClass &= ~SC_STORAGEMASK; /* This is always a declaration */ Decl.StorageClass |= SC_DECL; } /* 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"); } /* If the symbol is not marked as external, it will be defined now */ if ((Decl.StorageClass & SC_DECL) == 0 && (Decl.StorageClass & SC_EXTERN) == 0) { Decl.StorageClass |= SC_DEF; } /* Handle anything that needs storage (no functions, no typdefs) */ if ((Decl.StorageClass & SC_DEF) == SC_DEF && (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_STATIC) == SC_STATIC) { /* Static variable */ ParseStaticDecl (&Decl); } else { Internal ("Invalid storage class in ParseOneDecl: %04X", Decl.StorageClass); } } else { if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN) { /* External identifier - may not get initialized */ if (CurTok.Tok == TOK_ASSIGN) { Error ("Cannot initialize extern variable '%s'", Decl.Ident); /* Avoid excess errors */ NextToken (); ParseInit (Decl.Type); } } if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN || (Decl.StorageClass & SC_FUNC) == SC_FUNC) { /* Add the global symbol to the local symbol table */ AddGlobalSym (Decl.Ident, Decl.Type, Decl.StorageClass); } else { /* Add the local symbol to the local symbol table */ AddLocalSym (Decl.Ident, Decl.Type, Decl.StorageClass, 0); } } /* Make sure we aren't missing some work */ CheckDeferredOpAllDone (); } 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; /* Check for a _Static_assert */ if (CurTok.Tok == TOK_STATIC_ASSERT) { ParseStaticAssert (); continue; } 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 (); } }