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 (GetBaseElementType (Decl->Type))) {
363 SymData = AllocLabel (g_userodata);
365 SymData = AllocLabel (g_usedata);
371 /* Allow initialization of static vars */
372 Size = ParseInit (Decl->Type);
374 /* Mark the variable as referenced */
379 /* Get the size of the variable */
380 Size = SizeOf (Decl->Type);
382 /* Allocate a label and space for the variable in the BSS segment */
383 SymData = AllocStorage (g_usebss, Size);
387 /* Cannot allocate a variable of zero size */
389 Error ("Variable `%s' has unknown size", Decl->Ident);
392 /* Return the symbol data */
398 static void ParseOneDecl (const DeclSpec* Spec)
399 /* Parse one variable declaration */
401 unsigned SymData = 0; /* Symbol data (offset, label name, ...) */
402 Declaration Decl; /* Declaration data structure */
405 /* Read the declaration */
406 ParseDecl (Spec, &Decl, DM_NEED_IDENT);
408 /* Set the correct storage class for functions */
409 if ((Decl.StorageClass & SC_FUNC) == SC_FUNC) {
410 /* Function prototypes are always external */
411 if ((Decl.StorageClass & SC_EXTERN) == 0) {
412 Warning ("Function must be extern");
414 Decl.StorageClass |= SC_EXTERN;
417 /* If we don't have a name, this was flagged as an error earlier.
418 * To avoid problems later, use an anonymous name here.
420 if (Decl.Ident[0] == '\0') {
421 AnonName (Decl.Ident, "param");
424 /* Handle anything that needs storage (no functions, no typdefs) */
425 if ((Decl.StorageClass & SC_FUNC) != SC_FUNC &&
426 (Decl.StorageClass & SC_TYPEDEF) != SC_TYPEDEF) {
428 /* If we have a register variable, try to allocate a register and
429 * convert the declaration to "auto" if this is not possible.
431 int Reg = 0; /* Initialize to avoid gcc complains */
432 if ((Decl.StorageClass & SC_REGISTER) != 0 &&
433 (Reg = F_AllocRegVar (CurrentFunc, Decl.Type)) < 0) {
434 /* No space for this register variable, convert to auto */
435 Decl.StorageClass = (Decl.StorageClass & ~SC_REGISTER) | SC_AUTO;
438 /* Check the variable type */
439 if ((Decl.StorageClass & SC_REGISTER) == SC_REGISTER) {
440 /* Register variable */
441 SymData = ParseRegisterDecl (&Decl, &Decl.StorageClass, Reg);
442 } else if ((Decl.StorageClass & SC_AUTO) == SC_AUTO) {
444 SymData = ParseAutoDecl (&Decl, &Decl.StorageClass);
445 } else if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN) {
446 /* External identifier - may not get initialized */
447 if (CurTok.Tok == TOK_ASSIGN) {
448 Error ("Cannot initialize externals");
451 } else if ((Decl.StorageClass & SC_STATIC) == SC_STATIC) {
452 /* Static variable */
453 SymData = ParseStaticDecl (&Decl, &Decl.StorageClass);
455 Internal ("Invalid storage class in ParseOneDecl: %04X", Decl.StorageClass);
459 /* If the symbol is not marked as external, it will be defined now */
460 if ((Decl.StorageClass & SC_EXTERN) == 0) {
461 Decl.StorageClass |= SC_DEF;
464 /* Add the symbol to the symbol table */
465 AddLocalSym (Decl.Ident, Decl.Type, Decl.StorageClass, SymData);
470 void DeclareLocals (void)
471 /* Declare local variables and types. */
473 /* Remember the current stack pointer */
474 int InitialStack = StackPtr;
476 /* Loop until we don't find any more variables */
479 /* Check variable declarations. We need to distinguish between a
480 * default int type and the end of variable declarations. So we
481 * will do the following: If there is no explicit storage class
482 * specifier *and* no explicit type given, *and* no type qualifiers
483 * have been read, it is assumed that we have reached the end of
487 ParseDeclSpec (&Spec, SC_AUTO, T_INT);
488 if ((Spec.Flags & DS_DEF_STORAGE) != 0 && /* No storage spec */
489 (Spec.Flags & DS_DEF_TYPE) != 0 && /* No type given */
490 GetQualifier (Spec.Type) == T_QUAL_NONE) { /* No type qualifier */
494 /* Accept type only declarations */
495 if (CurTok.Tok == TOK_SEMI) {
496 /* Type declaration only */
497 CheckEmptyDecl (&Spec);
502 /* Parse a comma separated variable list */
505 /* Parse one declaration */
506 ParseOneDecl (&Spec);
508 /* Check if there is more */
509 if (CurTok.Tok == TOK_COMMA) {
518 /* A semicolon must follow */
522 /* Be sure to allocate any reserved space for locals */
523 F_AllocLocalSpace (CurrentFunc);
525 /* In case we've allocated local variables in this block, emit a call to
526 * the stack checking routine if stack checks are enabled.
528 if (IS_Get (&CheckStack) && InitialStack != StackPtr) {