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 unsigned AllocStorage (void (*UseSeg) (), unsigned Size)
83 /* Reserve Size bytes of BSS storage prefixed by a local label. Return the
87 /* Switch to the segment and define the label */
88 unsigned Label = AllocLabel (UseSeg);
90 /* Reserve space for the data */
93 /* Return the label */
99 static unsigned ParseRegisterDecl (Declaration* Decl, unsigned* SC, int Reg)
100 /* Parse the declaration of a register variable. The function returns the
101 * symbol data, which is the offset of the variable in the register bank.
106 /* Determine if this is a compound variable */
107 int IsCompound = IsClassStruct (Decl->Type) || IsTypeArray (Decl->Type);
109 /* Get the size of the variable */
110 unsigned Size = SizeOf (Decl->Type);
112 /* Save the current contents of the register variable on stack */
113 F_AllocLocalSpace (CurrentFunc);
114 g_save_regvars (Reg, Size);
116 /* Check for an optional initialization */
117 if (CurTok.Tok == TOK_ASSIGN) {
124 /* Special handling for compound types */
127 /* Switch to read only data and define a label for the
128 * initialization data.
130 InitLabel = AllocLabel (g_userodata);
132 /* Parse the initialization generating a memory image of the
133 * data in the RODATA segment. The function does return the size
134 * of the initialization data, which may be greater than the
135 * actual size of the type, if the type is a structure with a
136 * flexible array member that has been initialized. Since we must
137 * know the size of the data in advance for register variables,
138 * we cannot allow that here.
140 if (ParseInit (Decl->Type) != Size) {
141 Error ("Cannot initialize flexible array members of storage class `register'");
144 /* Generate code to copy this data into the variable space */
145 g_initregister (InitLabel, Reg, Size);
149 /* Parse the expression */
152 /* Convert it to the target type */
153 TypeConversion (&Expr, Decl->Type);
155 /* Load the value into the primary */
156 LoadExpr (CF_NONE, &Expr);
158 /* Store the value into the variable */
159 g_putstatic (CF_REGVAR | TypeOf (Decl->Type), Reg, 0);
163 /* Mark the variable as referenced */
167 /* Cannot allocate a variable of zero size */
169 Error ("Variable `%s' has unknown size", Decl->Ident);
172 /* Return the symbol data */
178 static unsigned ParseAutoDecl (Declaration* Decl, unsigned* SC)
179 /* Parse the declaration of an auto variable. The function returns the symbol
180 * data, which is the offset for variables on the stack, and the label for
187 /* Determine if this is a compound variable */
188 int IsCompound = IsClassStruct (Decl->Type) || IsTypeArray (Decl->Type);
190 /* Get the size of the variable */
191 unsigned Size = SizeOf (Decl->Type);
193 /* Check if this is a variable on the stack or in static memory */
194 if (IS_Get (&StaticLocals) == 0) {
196 /* Check for an optional initialization */
197 if (CurTok.Tok == TOK_ASSIGN) {
204 /* Special handling for compound types */
207 /* Switch to read only data and define a label for the
208 * initialization data.
210 unsigned InitLabel = AllocLabel (g_userodata);
212 /* Parse the initialization generating a memory image of the
213 * data in the RODATA segment. The function will return the
214 * actual size of the initialization data, which may be
215 * greater than the size of the variable if it is a struct
216 * that contains a flexible array member and we're not in
219 Size = ParseInit (Decl->Type);
221 /* Now reserve space for the variable on the stack */
222 SymData = F_ReserveLocalSpace (CurrentFunc, Size);
224 /* Next, allocate the space on the stack. This means that the
225 * variable is now located at offset 0 from the current sp.
227 F_AllocLocalSpace (CurrentFunc);
229 /* Generate code to copy the initialization data into the
232 g_initauto (InitLabel, Size);
236 /* Allocate previously reserved local space */
237 F_AllocLocalSpace (CurrentFunc);
239 /* Setup the type flags for the assignment */
240 Flags = (Size == SIZEOF_CHAR)? CF_FORCECHAR : CF_NONE;
242 /* Parse the expression */
245 /* Convert it to the target type */
246 TypeConversion (&Expr, Decl->Type);
248 /* If the value is not const, load it into the primary.
249 * Otherwise pass the information to the code generator.
251 if (ED_IsConstAbsInt (&Expr)) {
254 LoadExpr (CF_NONE, &Expr);
259 g_push (Flags | TypeOf (Decl->Type), Expr.IVal);
263 /* Mark the variable as referenced */
266 /* Variable is located at the current SP */
270 /* Non-initialized local variable. Just keep track of
273 SymData = F_ReserveLocalSpace (CurrentFunc, Size);
278 /* Static local variables. */
279 *SC = (*SC & ~SC_AUTO) | SC_STATIC;
281 /* Allow assignments */
282 if (CurTok.Tok == TOK_ASSIGN) {
291 /* Switch to read only data and define a label for the
292 * initialization data.
294 unsigned InitLabel = AllocLabel (g_userodata);
296 /* Parse the initialization generating a memory image of the
297 * data in the RODATA segment.
299 Size = ParseInit (Decl->Type);
301 /* Allocate a label and space for the variable */
302 SymData = AllocStorage (g_usebss, Size);
304 /* Generate code to copy this data into the variable space */
305 g_initstatic (InitLabel, SymData, Size);
309 /* Allocate a label and space for the variable */
310 SymData = AllocStorage (g_usebss, Size);
312 /* Parse the expression */
315 /* Convert it to the target type */
316 TypeConversion (&Expr, Decl->Type);
318 /* Load the value into the primary */
319 LoadExpr (CF_NONE, &Expr);
321 /* Store the value into the variable */
322 g_putstatic (TypeOf (Decl->Type), SymData, 0);
325 /* Mark the variable as referenced */
330 /* No assignment - allocate a label and space for the variable */
331 SymData = AllocStorage (g_usebss, Size);
336 /* Cannot allocate a variable of zero size */
338 Error ("Variable `%s' has unknown size", Decl->Ident);
341 /* Return the symbol data */
347 static unsigned ParseStaticDecl (Declaration* Decl, unsigned* SC)
348 /* Parse the declaration of a static variable. The function returns the symbol
349 * data, which is the asm label of the variable.
356 if (CurTok.Tok == TOK_ASSIGN) {
358 /* Initialization ahead, switch to data segment and define a label.
359 * For arrays, we need to check the elements of the array for
360 * constness, not the array itself.
362 if (IsQualConst (Decl->Type) ||
363 (IsTypeArray (Decl->Type) && IsQualConst (GetElementType (Decl->Type)))) {
364 SymData = AllocLabel (g_userodata);
366 SymData = AllocLabel (g_usedata);
372 /* Allow initialization of static vars */
373 Size = ParseInit (Decl->Type);
375 /* Mark the variable as referenced */
380 /* Get the size of the variable */
381 Size = SizeOf (Decl->Type);
383 /* Allocate a label and space for the variable in the BSS segment */
384 SymData = AllocStorage (g_usebss, Size);
388 /* Cannot allocate a variable of zero size */
390 Error ("Variable `%s' has unknown size", Decl->Ident);
393 /* Return the symbol data */
399 static void ParseOneDecl (const DeclSpec* Spec)
400 /* Parse one variable declaration */
402 unsigned SymData = 0; /* Symbol data (offset, label name, ...) */
403 Declaration Decl; /* Declaration data structure */
406 /* Read the declaration */
407 ParseDecl (Spec, &Decl, DM_NEED_IDENT);
409 /* Set the correct storage class for functions */
410 if ((Decl.StorageClass & SC_FUNC) == SC_FUNC) {
411 /* Function prototypes are always external */
412 if ((Decl.StorageClass & SC_EXTERN) == 0) {
413 Warning ("Function must be extern");
415 Decl.StorageClass |= SC_EXTERN;
418 /* If we don't have a name, this was flagged as an error earlier.
419 * To avoid problems later, use an anonymous name here.
421 if (Decl.Ident[0] == '\0') {
422 AnonName (Decl.Ident, "param");
425 /* Handle anything that needs storage (no functions, no typdefs) */
426 if ((Decl.StorageClass & SC_FUNC) != SC_FUNC &&
427 (Decl.StorageClass & SC_TYPEDEF) != SC_TYPEDEF) {
429 /* If we have a register variable, try to allocate a register and
430 * convert the declaration to "auto" if this is not possible.
432 int Reg = 0; /* Initialize to avoid gcc complains */
433 if ((Decl.StorageClass & SC_REGISTER) != 0 &&
434 (Reg = F_AllocRegVar (CurrentFunc, Decl.Type)) < 0) {
435 /* No space for this register variable, convert to auto */
436 Decl.StorageClass = (Decl.StorageClass & ~SC_REGISTER) | SC_AUTO;
439 /* Check the variable type */
440 if ((Decl.StorageClass & SC_REGISTER) == SC_REGISTER) {
441 /* Register variable */
442 SymData = ParseRegisterDecl (&Decl, &Decl.StorageClass, Reg);
443 } else if ((Decl.StorageClass & SC_AUTO) == SC_AUTO) {
445 SymData = ParseAutoDecl (&Decl, &Decl.StorageClass);
446 } else if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN) {
447 /* External identifier - may not get initialized */
448 if (CurTok.Tok == TOK_ASSIGN) {
449 Error ("Cannot initialize externals");
452 } else if ((Decl.StorageClass & SC_STATIC) == SC_STATIC) {
453 /* Static variable */
454 SymData = ParseStaticDecl (&Decl, &Decl.StorageClass);
456 Internal ("Invalid storage class in ParseOneDecl: %04X", Decl.StorageClass);
460 /* If the symbol is not marked as external, it will be defined now */
461 if ((Decl.StorageClass & SC_EXTERN) == 0) {
462 Decl.StorageClass |= SC_DEF;
465 /* Add the symbol to the symbol table */
466 AddLocalSym (Decl.Ident, Decl.Type, Decl.StorageClass, SymData);
471 void DeclareLocals (void)
472 /* Declare local variables and types. */
474 /* Remember the current stack pointer */
475 int InitialStack = StackPtr;
477 /* Loop until we don't find any more variables */
480 /* Check variable declarations. We need to distinguish between a
481 * default int type and the end of variable declarations. So we
482 * will do the following: If there is no explicit storage class
483 * specifier *and* no explicit type given, *and* no type qualifiers
484 * have been read, it is assumed that we have reached the end of
488 ParseDeclSpec (&Spec, SC_AUTO, T_INT);
489 if ((Spec.Flags & DS_DEF_STORAGE) != 0 && /* No storage spec */
490 (Spec.Flags & DS_DEF_TYPE) != 0 && /* No type given */
491 GetQualifier (Spec.Type) == T_QUAL_NONE) { /* No type qualifier */
495 /* Accept type only declarations */
496 if (CurTok.Tok == TOK_SEMI) {
497 /* Type declaration only */
498 CheckEmptyDecl (&Spec);
503 /* Parse a comma separated variable list */
506 /* Parse one declaration */
507 ParseOneDecl (&Spec);
509 /* Check if there is more */
510 if (CurTok.Tok == TOK_COMMA) {
519 /* A semicolon must follow */
523 /* Be sure to allocate any reserved space for locals */
524 F_AllocLocalSpace (CurrentFunc);
526 /* In case we've allocated local variables in this block, emit a call to
527 * the stack checking routine if stack checks are enabled.
529 if (IS_Get (&CheckStack) && InitialStack != StackPtr) {