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 if (IsQualConst (Decl->Type)) {
360 SymData = AllocLabel (g_userodata);
362 SymData = AllocLabel (g_usedata);
368 /* Allow initialization of static vars */
369 Size = ParseInit (Decl->Type);
371 /* Mark the variable as referenced */
376 /* Get the size of the variable */
377 Size = SizeOf (Decl->Type);
379 /* Allocate a label and space for the variable in the BSS segment */
380 SymData = AllocStorage (g_usebss, Size);
384 /* Cannot allocate a variable of zero size */
386 Error ("Variable `%s' has unknown size", Decl->Ident);
389 /* Return the symbol data */
395 static void ParseOneDecl (const DeclSpec* Spec)
396 /* Parse one variable declaration */
398 unsigned SymData = 0; /* Symbol data (offset, label name, ...) */
399 Declaration Decl; /* Declaration data structure */
402 /* Read the declaration */
403 ParseDecl (Spec, &Decl, DM_NEED_IDENT);
405 /* Set the correct storage class for functions */
406 if ((Decl.StorageClass & SC_FUNC) == SC_FUNC) {
407 /* Function prototypes are always external */
408 if ((Decl.StorageClass & SC_EXTERN) == 0) {
409 Warning ("Function must be extern");
411 Decl.StorageClass |= SC_EXTERN;
414 /* If we don't have a name, this was flagged as an error earlier.
415 * To avoid problems later, use an anonymous name here.
417 if (Decl.Ident[0] == '\0') {
418 AnonName (Decl.Ident, "param");
421 /* Handle anything that needs storage (no functions, no typdefs) */
422 if ((Decl.StorageClass & SC_FUNC) != SC_FUNC &&
423 (Decl.StorageClass & SC_TYPEDEF) != SC_TYPEDEF) {
425 /* If we have a register variable, try to allocate a register and
426 * convert the declaration to "auto" if this is not possible.
428 int Reg = 0; /* Initialize to avoid gcc complains */
429 if ((Decl.StorageClass & SC_REGISTER) != 0 &&
430 (Reg = F_AllocRegVar (CurrentFunc, Decl.Type)) < 0) {
431 /* No space for this register variable, convert to auto */
432 Decl.StorageClass = (Decl.StorageClass & ~SC_REGISTER) | SC_AUTO;
435 /* Check the variable type */
436 if ((Decl.StorageClass & SC_REGISTER) == SC_REGISTER) {
437 /* Register variable */
438 SymData = ParseRegisterDecl (&Decl, &Decl.StorageClass, Reg);
439 } else if ((Decl.StorageClass & SC_AUTO) == SC_AUTO) {
441 SymData = ParseAutoDecl (&Decl, &Decl.StorageClass);
442 } else if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN) {
443 /* External identifier - may not get initialized */
444 if (CurTok.Tok == TOK_ASSIGN) {
445 Error ("Cannot initialize externals");
448 } else if ((Decl.StorageClass & SC_STATIC) == SC_STATIC) {
449 /* Static variable */
450 SymData = ParseStaticDecl (&Decl, &Decl.StorageClass);
452 Internal ("Invalid storage class in ParseOneDecl: %04X", Decl.StorageClass);
456 /* If the symbol is not marked as external, it will be defined now */
457 if ((Decl.StorageClass & SC_EXTERN) == 0) {
458 Decl.StorageClass |= SC_DEF;
461 /* Add the symbol to the symbol table */
462 AddLocalSym (Decl.Ident, Decl.Type, Decl.StorageClass, SymData);
467 void DeclareLocals (void)
468 /* Declare local variables and types. */
470 /* Remember the current stack pointer */
471 int InitialStack = StackPtr;
473 /* Loop until we don't find any more variables */
476 /* Check variable declarations. We need to distinguish between a
477 * default int type and the end of variable declarations. So we
478 * will do the following: If there is no explicit storage class
479 * specifier *and* no explicit type given, *and* no type qualifiers
480 * have been read, it is assumed that we have reached the end of
484 ParseDeclSpec (&Spec, SC_AUTO, T_INT);
485 if ((Spec.Flags & DS_DEF_STORAGE) != 0 && /* No storage spec */
486 (Spec.Flags & DS_DEF_TYPE) != 0 && /* No type given */
487 GetQualifier (Spec.Type) == T_QUAL_NONE) { /* No type qualifier */
491 /* Accept type only declarations */
492 if (CurTok.Tok == TOK_SEMI) {
493 /* Type declaration only */
494 CheckEmptyDecl (&Spec);
499 /* Parse a comma separated variable list */
502 /* Parse one declaration */
503 ParseOneDecl (&Spec);
505 /* Check if there is more */
506 if (CurTok.Tok == TOK_COMMA) {
515 /* A semicolon must follow */
519 /* Be sure to allocate any reserved space for locals */
520 F_AllocLocalSpace (CurrentFunc);
522 /* In case we've allocated local variables in this block, emit a call to
523 * the stack checking routine if stack checks are enabled.
525 if (IS_Get (&CheckStack) && InitialStack != StackPtr) {