1 /*****************************************************************************/
5 /* Local variable handling for the cc65 C compiler */
9 /* (C) 2000-2009, Ullrich von Bassewitz */
10 /* Roemerstrasse 52 */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
15 /* This software is provided 'as-is', without any expressed or implied */
16 /* warranty. In no event will the authors be held liable for any damages */
17 /* arising from the use of this software. */
19 /* Permission is granted to anyone to use this software for any purpose, */
20 /* including commercial applications, and to alter it and redistribute it */
21 /* freely, subject to the following restrictions: */
23 /* 1. The origin of this software must not be misrepresented; you must not */
24 /* claim that you wrote the original software. If you use this software */
25 /* in a product, an acknowledgment in the product documentation would be */
26 /* appreciated but is not required. */
27 /* 2. Altered source versions must be plainly marked as such, and must not */
28 /* be misrepresented as being the original software. */
29 /* 3. This notice may not be removed or altered from any source */
32 /*****************************************************************************/
58 /*****************************************************************************/
60 /*****************************************************************************/
64 static unsigned AllocLabel (void (*UseSeg) ())
65 /* Switch to a segment, define a local label and return it */
69 /* Switch to the segment */
72 /* Define the variable label */
73 Label = GetLocalLabel ();
74 g_defdatalabel (Label);
76 /* Return the label */
82 static void AllocStorage (unsigned Label, void (*UseSeg) (), unsigned Size)
83 /* Reserve Size bytes of BSS storage prefixed by a local label. */
85 /* Switch to the segment */
88 /* Define the variable label */
89 g_defdatalabel (Label);
91 /* Reserve space for the data */
97 static void ParseRegisterDecl (Declaration* Decl, int Reg)
98 /* Parse the declaration of a register variable. Reg is the offset of the
99 * variable in the register bank.
104 /* Determine if this is a compound variable */
105 int IsCompound = IsClassStruct (Decl->Type) || IsTypeArray (Decl->Type);
107 /* Get the size of the variable */
108 unsigned Size = SizeOf (Decl->Type);
110 /* Save the current contents of the register variable on stack */
111 F_AllocLocalSpace (CurrentFunc);
112 g_save_regvars (Reg, Size);
114 /* Add the symbol to the symbol table. We do that now, because for register
115 * variables the current stack pointer is implicitly used as location for
118 Sym = AddLocalSym (Decl->Ident, Decl->Type, Decl->StorageClass, Reg);
120 /* Check for an optional initialization */
121 if (CurTok.Tok == TOK_ASSIGN) {
128 /* Special handling for compound types */
131 /* Switch to read only data and define a label for the
132 * initialization data.
134 unsigned InitLabel = AllocLabel (g_userodata);
136 /* Parse the initialization generating a memory image of the
137 * data in the RODATA segment. The function does return the size
138 * of the initialization data, which may be greater than the
139 * actual size of the type, if the type is a structure with a
140 * flexible array member that has been initialized. Since we must
141 * know the size of the data in advance for register variables,
142 * we cannot allow that here.
144 if (ParseInit (Sym->Type) != Size) {
145 Error ("Cannot initialize flexible array members of storage class `register'");
148 /* Generate code to copy this data into the variable space */
149 g_initregister (InitLabel, Reg, Size);
153 /* Parse the expression */
156 /* Convert it to the target type */
157 TypeConversion (&Expr, Sym->Type);
159 /* Load the value into the primary */
160 LoadExpr (CF_NONE, &Expr);
162 /* Store the value into the variable */
163 g_putstatic (CF_REGVAR | TypeOf (Sym->Type), Reg, 0);
167 /* Mark the variable as referenced */
168 Sym->Flags |= SC_REF;
171 /* Cannot allocate a variable of zero size */
173 Error ("Variable `%s' has unknown size", Decl->Ident);
179 static void ParseAutoDecl (Declaration* Decl)
180 /* Parse the declaration of an auto variable. */
185 /* Determine if this is a compound variable */
186 int IsCompound = IsClassStruct (Decl->Type) || IsTypeArray (Decl->Type);
188 /* Get the size of the variable */
189 unsigned Size = SizeOf (Decl->Type);
191 /* Check if this is a variable on the stack or in static memory */
192 if (IS_Get (&StaticLocals) == 0) {
194 /* Add the symbol to the symbol table. The stack offset we use here
195 * may get corrected later.
197 Sym = AddLocalSym (Decl->Ident, Decl->Type,
199 F_GetStackPtr (CurrentFunc) - (int) Size);
201 /* Check for an optional initialization */
202 if (CurTok.Tok == TOK_ASSIGN) {
209 /* Special handling for compound types */
212 /* Switch to read only data and define a label for the
213 * initialization data.
215 unsigned InitLabel = AllocLabel (g_userodata);
217 /* Parse the initialization generating a memory image of the
218 * data in the RODATA segment. The function will return the
219 * actual size of the initialization data, which may be
220 * greater than the size of the variable if it is a struct
221 * that contains a flexible array member and we're not in
224 Size = ParseInit (Sym->Type);
226 /* Now reserve space for the variable on the stack and correct
227 * the offset in the symbol table entry.
229 Sym->V.Offs = F_ReserveLocalSpace (CurrentFunc, Size);
231 /* Next, allocate the space on the stack. This means that the
232 * variable is now located at offset 0 from the current sp.
234 F_AllocLocalSpace (CurrentFunc);
236 /* Generate code to copy the initialization data into the
239 g_initauto (InitLabel, Size);
243 /* Allocate previously reserved local space */
244 F_AllocLocalSpace (CurrentFunc);
246 /* Setup the type flags for the assignment */
247 Flags = (Size == SIZEOF_CHAR)? CF_FORCECHAR : CF_NONE;
249 /* Parse the expression */
252 /* Convert it to the target type */
253 TypeConversion (&Expr, Sym->Type);
255 /* If the value is not const, load it into the primary.
256 * Otherwise pass the information to the code generator.
258 if (ED_IsConstAbsInt (&Expr)) {
261 LoadExpr (CF_NONE, &Expr);
266 g_push (Flags | TypeOf (Sym->Type), Expr.IVal);
270 /* Mark the variable as referenced */
271 Sym->Flags |= SC_REF;
274 /* Non-initialized local variable. Just keep track of
277 F_ReserveLocalSpace (CurrentFunc, Size);
285 /* Static local variables. */
286 Decl->StorageClass = (Decl->StorageClass & ~SC_AUTO) | SC_STATIC;
288 /* Generate a label, but don't define it */
289 DataLabel = GetLocalLabel ();
291 /* Add the symbol to the symbol table. */
292 Sym = AddLocalSym (Decl->Ident, Decl->Type, Decl->StorageClass, DataLabel);
294 /* Allow assignments */
295 if (CurTok.Tok == TOK_ASSIGN) {
304 /* Switch to read only data and define a label for the
305 * initialization data.
307 unsigned InitLabel = AllocLabel (g_userodata);
309 /* Parse the initialization generating a memory image of the
310 * data in the RODATA segment.
312 Size = ParseInit (Sym->Type);
314 /* Allocate space for the variable */
315 AllocStorage (DataLabel, g_usebss, Size);
317 /* Generate code to copy this data into the variable space */
318 g_initstatic (InitLabel, DataLabel, Size);
322 /* Allocate space for the variable */
323 AllocStorage (DataLabel, g_usebss, Size);
325 /* Parse the expression */
328 /* Convert it to the target type */
329 TypeConversion (&Expr, Sym->Type);
331 /* Load the value into the primary */
332 LoadExpr (CF_NONE, &Expr);
334 /* Store the value into the variable */
335 g_putstatic (TypeOf (Sym->Type), DataLabel, 0);
338 /* Mark the variable as referenced */
339 Sym->Flags |= SC_REF;
343 /* No assignment - allocate a label and space for the variable */
344 AllocStorage (DataLabel, g_usebss, Size);
349 /* Cannot allocate a variable of zero size */
351 Error ("Variable `%s' has unknown size", Decl->Ident);
357 static void ParseStaticDecl (Declaration* Decl)
358 /* Parse the declaration of a static variable. */
362 /* Generate a label, but don't define it */
363 unsigned DataLabel = GetLocalLabel ();
365 /* Add the symbol to the symbol table. */
366 SymEntry* Sym = AddLocalSym (Decl->Ident, Decl->Type,
371 if (CurTok.Tok == TOK_ASSIGN) {
373 /* Initialization ahead, switch to data segment and define the label.
374 * For arrays, we need to check the elements of the array for
375 * constness, not the array itself.
377 if (IsQualConst (GetBaseElementType (Sym->Type))) {
382 g_defdatalabel (DataLabel);
387 /* Allow initialization of static vars */
388 Size = ParseInit (Sym->Type);
390 /* Mark the variable as referenced */
391 Sym->Flags |= SC_REF;
395 /* Get the size of the variable */
396 Size = SizeOf (Decl->Type);
398 /* Allocate a label and space for the variable in the BSS segment */
399 AllocStorage (DataLabel, g_usebss, Size);
403 /* Cannot allocate a variable of zero size */
405 Error ("Variable `%s' has unknown size", Decl->Ident);
411 static void ParseOneDecl (const DeclSpec* Spec)
412 /* Parse one variable declaration */
414 Declaration Decl; /* Declaration data structure */
417 /* Read the declaration */
418 ParseDecl (Spec, &Decl, DM_NEED_IDENT);
420 /* Set the correct storage class for functions */
421 if ((Decl.StorageClass & SC_FUNC) == SC_FUNC) {
422 /* Function prototypes are always external */
423 if ((Decl.StorageClass & SC_EXTERN) == 0) {
424 Warning ("Function must be extern");
426 Decl.StorageClass |= SC_EXTERN;
429 /* If we don't have a name, this was flagged as an error earlier.
430 * To avoid problems later, use an anonymous name here.
432 if (Decl.Ident[0] == '\0') {
433 AnonName (Decl.Ident, "param");
436 /* If the symbol is not marked as external, it will be defined now */
437 if ((Decl.StorageClass & SC_EXTERN) == 0) {
438 Decl.StorageClass |= SC_DEF;
441 /* Handle anything that needs storage (no functions, no typdefs) */
442 if ((Decl.StorageClass & SC_FUNC) != SC_FUNC &&
443 (Decl.StorageClass & SC_TYPEDEF) != SC_TYPEDEF) {
445 /* If we have a register variable, try to allocate a register and
446 * convert the declaration to "auto" if this is not possible.
448 int Reg = 0; /* Initialize to avoid gcc complains */
449 if ((Decl.StorageClass & SC_REGISTER) != 0 &&
450 (Reg = F_AllocRegVar (CurrentFunc, Decl.Type)) < 0) {
451 /* No space for this register variable, convert to auto */
452 Decl.StorageClass = (Decl.StorageClass & ~SC_REGISTER) | SC_AUTO;
455 /* Check the variable type */
456 if ((Decl.StorageClass & SC_REGISTER) == SC_REGISTER) {
457 /* Register variable */
458 ParseRegisterDecl (&Decl, Reg);
459 } else if ((Decl.StorageClass & SC_AUTO) == SC_AUTO) {
461 ParseAutoDecl (&Decl);
462 } else if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN) {
463 /* External identifier - may not get initialized */
464 if (CurTok.Tok == TOK_ASSIGN) {
465 Error ("Cannot initialize externals");
467 /* Add the external symbol to the symbol table */
468 AddLocalSym (Decl.Ident, Decl.Type, Decl.StorageClass, 0);
469 } else if ((Decl.StorageClass & SC_STATIC) == SC_STATIC) {
470 /* Static variable */
471 ParseStaticDecl (&Decl);
473 Internal ("Invalid storage class in ParseOneDecl: %04X", Decl.StorageClass);
478 /* Add the symbol to the symbol table */
479 AddLocalSym (Decl.Ident, Decl.Type, Decl.StorageClass, 0);
486 void DeclareLocals (void)
487 /* Declare local variables and types. */
489 /* Remember the current stack pointer */
490 int InitialStack = StackPtr;
492 /* Loop until we don't find any more variables */
495 /* Check variable declarations. We need to distinguish between a
496 * default int type and the end of variable declarations. So we
497 * will do the following: If there is no explicit storage class
498 * specifier *and* no explicit type given, *and* no type qualifiers
499 * have been read, it is assumed that we have reached the end of
503 ParseDeclSpec (&Spec, SC_AUTO, T_INT);
504 if ((Spec.Flags & DS_DEF_STORAGE) != 0 && /* No storage spec */
505 (Spec.Flags & DS_DEF_TYPE) != 0 && /* No type given */
506 GetQualifier (Spec.Type) == T_QUAL_NONE) { /* No type qualifier */
510 /* Accept type only declarations */
511 if (CurTok.Tok == TOK_SEMI) {
512 /* Type declaration only */
513 CheckEmptyDecl (&Spec);
518 /* Parse a comma separated variable list */
521 /* Parse one declaration */
522 ParseOneDecl (&Spec);
524 /* Check if there is more */
525 if (CurTok.Tok == TOK_COMMA) {
534 /* A semicolon must follow */
538 /* Be sure to allocate any reserved space for locals */
539 F_AllocLocalSpace (CurrentFunc);
541 /* In case we've allocated local variables in this block, emit a call to
542 * the stack checking routine if stack checks are enabled.
544 if (IS_Get (&CheckStack) && InitialStack != StackPtr) {