1 /*****************************************************************************/
5 /* Local variable handling for the cc65 C compiler */
9 /* (C) 2000-2013, 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 /*****************************************************************************/
59 /*****************************************************************************/
61 /*****************************************************************************/
65 static unsigned AllocLabel (void (*UseSeg) ())
66 /* Switch to a segment, define a local label and return it */
70 /* Switch to the segment */
73 /* Define the variable label */
74 Label = GetLocalLabel ();
75 g_defdatalabel (Label);
77 /* Return the label */
83 static void AllocStorage (unsigned Label, void (*UseSeg) (), unsigned Size)
84 /* Reserve Size bytes of BSS storage prefixed by a local label. */
86 /* Switch to the segment */
89 /* Define the variable label */
90 g_defdatalabel (Label);
92 /* Reserve space for the data */
98 static void ParseRegisterDecl (Declaration* Decl, int Reg)
99 /* Parse the declaration of a register variable. Reg is the offset of the
100 ** variable in the register bank.
105 /* Determine if this is a compound variable */
106 int IsCompound = IsClassStruct (Decl->Type) || IsTypeArray (Decl->Type);
108 /* Get the size of the variable */
109 unsigned Size = SizeOf (Decl->Type);
111 /* Save the current contents of the register variable on stack */
112 F_AllocLocalSpace (CurrentFunc);
113 g_save_regvars (Reg, Size);
115 /* Add the symbol to the symbol table. We do that now, because for register
116 ** variables the current stack pointer is implicitly used as location for
119 Sym = AddLocalSym (Decl->Ident, Decl->Type, Decl->StorageClass, Reg);
121 /* Check for an optional initialization */
122 if (CurTok.Tok == TOK_ASSIGN) {
129 /* Special handling for compound types */
132 /* Switch to read only data and define a label for the
133 ** initialization data.
135 unsigned InitLabel = AllocLabel (g_userodata);
137 /* Parse the initialization generating a memory image of the
138 ** data in the RODATA segment. The function does return the size
139 ** of the initialization data, which may be greater than the
140 ** actual size of the type, if the type is a structure with a
141 ** flexible array member that has been initialized. Since we must
142 ** know the size of the data in advance for register variables,
143 ** we cannot allow that here.
145 if (ParseInit (Sym->Type) != Size) {
146 Error ("Cannot initialize flexible array members of storage class 'register'");
149 /* Generate code to copy this data into the variable space */
150 g_initregister (InitLabel, Reg, Size);
154 /* Parse the expression */
157 /* Convert it to the target type */
158 TypeConversion (&Expr, Sym->Type);
160 /* Load the value into the primary */
161 LoadExpr (CF_NONE, &Expr);
163 /* Store the value into the variable */
164 g_putstatic (CF_REGVAR | TypeOf (Sym->Type), Reg, 0);
168 /* Mark the variable as referenced */
169 Sym->Flags |= SC_REF;
172 /* Cannot allocate a variable of zero size */
174 Error ("Variable '%s' has unknown size", Decl->Ident);
180 static void ParseAutoDecl (Declaration* Decl)
181 /* Parse the declaration of an auto variable. */
186 /* Determine if this is a compound variable */
187 int IsCompound = IsClassStruct (Decl->Type) || IsTypeArray (Decl->Type);
189 /* Get the size of the variable */
190 unsigned Size = SizeOf (Decl->Type);
192 /* Check if this is a variable on the stack or in static memory */
193 if (IS_Get (&StaticLocals) == 0) {
195 /* Add the symbol to the symbol table. The stack offset we use here
196 ** may get corrected later.
198 Sym = AddLocalSym (Decl->Ident, Decl->Type,
200 F_GetStackPtr (CurrentFunc) - (int) Size);
202 /* Check for an optional initialization */
203 if (CurTok.Tok == TOK_ASSIGN) {
210 /* Special handling for compound types */
213 /* Switch to read only data and define a label for the
214 ** initialization data.
216 unsigned InitLabel = AllocLabel (g_userodata);
218 /* Parse the initialization generating a memory image of the
219 ** data in the RODATA segment. The function will return the
220 ** actual size of the initialization data, which may be
221 ** greater than the size of the variable if it is a struct
222 ** that contains a flexible array member and we're not in
225 Size = ParseInit (Sym->Type);
227 /* Now reserve space for the variable on the stack and correct
228 ** the offset in the symbol table entry.
230 Sym->V.Offs = F_ReserveLocalSpace (CurrentFunc, Size);
232 /* Next, allocate the space on the stack. This means that the
233 ** variable is now located at offset 0 from the current sp.
235 F_AllocLocalSpace (CurrentFunc);
237 /* Generate code to copy the initialization data into the
240 g_initauto (InitLabel, Size);
244 /* Allocate previously reserved local space */
245 F_AllocLocalSpace (CurrentFunc);
247 /* Setup the type flags for the assignment */
248 Flags = (Size == SIZEOF_CHAR)? CF_FORCECHAR : CF_NONE;
250 /* Parse the expression */
253 /* Convert it to the target type */
254 TypeConversion (&Expr, Sym->Type);
256 /* If the value is not const, load it into the primary.
257 ** Otherwise pass the information to the code generator.
259 if (ED_IsConstAbsInt (&Expr)) {
262 LoadExpr (CF_NONE, &Expr);
267 g_push (Flags | TypeOf (Sym->Type), Expr.IVal);
271 /* Mark the variable as referenced */
272 Sym->Flags |= SC_REF;
274 /* Make note of auto variables initialized in current block.
275 ** We abuse the Collection somewhat by using it to store line
278 CollReplace (&CurrentFunc->LocalsBlockStack,
279 (void *)(size_t)GetCurrentLine (),
280 CollCount (&CurrentFunc->LocalsBlockStack) - 1);
283 /* Non-initialized local variable. Just keep track of
286 F_ReserveLocalSpace (CurrentFunc, Size);
294 /* Static local variables. */
295 Decl->StorageClass = (Decl->StorageClass & ~SC_AUTO) | SC_STATIC;
297 /* Generate a label, but don't define it */
298 DataLabel = GetLocalLabel ();
300 /* Add the symbol to the symbol table. */
301 Sym = AddLocalSym (Decl->Ident, Decl->Type, Decl->StorageClass, DataLabel);
303 /* Allow assignments */
304 if (CurTok.Tok == TOK_ASSIGN) {
313 /* Switch to read only data and define a label for the
314 ** initialization data.
316 unsigned InitLabel = AllocLabel (g_userodata);
318 /* Parse the initialization generating a memory image of the
319 ** data in the RODATA segment.
321 Size = ParseInit (Sym->Type);
323 /* Allocate space for the variable */
324 AllocStorage (DataLabel, g_usebss, Size);
326 /* Generate code to copy this data into the variable space */
327 g_initstatic (InitLabel, DataLabel, Size);
331 /* Allocate space for the variable */
332 AllocStorage (DataLabel, g_usebss, Size);
334 /* Parse the expression */
337 /* Convert it to the target type */
338 TypeConversion (&Expr, Sym->Type);
340 /* Load the value into the primary */
341 LoadExpr (CF_NONE, &Expr);
343 /* Store the value into the variable */
344 g_putstatic (TypeOf (Sym->Type), DataLabel, 0);
347 /* Mark the variable as referenced */
348 Sym->Flags |= SC_REF;
352 /* No assignment - allocate a label and space for the variable */
353 AllocStorage (DataLabel, g_usebss, Size);
358 /* Cannot allocate a variable of zero size */
360 Error ("Variable '%s' has unknown size", Decl->Ident);
366 static void ParseStaticDecl (Declaration* Decl)
367 /* Parse the declaration of a static variable. */
371 /* Generate a label, but don't define it */
372 unsigned DataLabel = GetLocalLabel ();
374 /* Add the symbol to the symbol table. */
375 SymEntry* Sym = AddLocalSym (Decl->Ident, Decl->Type,
380 if (CurTok.Tok == TOK_ASSIGN) {
382 /* Initialization ahead, switch to data segment and define the label.
383 ** For arrays, we need to check the elements of the array for
384 ** constness, not the array itself.
386 if (IsQualConst (GetBaseElementType (Sym->Type))) {
391 g_defdatalabel (DataLabel);
396 /* Allow initialization of static vars */
397 Size = ParseInit (Sym->Type);
399 /* Mark the variable as referenced */
400 Sym->Flags |= SC_REF;
404 /* Get the size of the variable */
405 Size = SizeOf (Decl->Type);
407 /* Allocate a label and space for the variable in the BSS segment */
408 AllocStorage (DataLabel, g_usebss, Size);
412 /* Cannot allocate a variable of zero size */
414 Error ("Variable '%s' has unknown size", Decl->Ident);
420 static void ParseOneDecl (const DeclSpec* Spec)
421 /* Parse one variable declaration */
423 Declaration Decl; /* Declaration data structure */
426 /* Read the declaration */
427 ParseDecl (Spec, &Decl, DM_NEED_IDENT);
429 /* Set the correct storage class for functions */
430 if ((Decl.StorageClass & SC_FUNC) == SC_FUNC) {
431 /* Function prototypes are always external */
432 if ((Decl.StorageClass & SC_EXTERN) == 0) {
433 Warning ("Function must be extern");
435 Decl.StorageClass |= SC_EXTERN;
438 /* If we don't have a name, this was flagged as an error earlier.
439 ** To avoid problems later, use an anonymous name here.
441 if (Decl.Ident[0] == '\0') {
442 AnonName (Decl.Ident, "param");
445 /* If the symbol is not marked as external, it will be defined now */
446 if ((Decl.StorageClass & SC_EXTERN) == 0) {
447 Decl.StorageClass |= SC_DEF;
450 /* Handle anything that needs storage (no functions, no typdefs) */
451 if ((Decl.StorageClass & SC_FUNC) != SC_FUNC &&
452 (Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF) {
454 /* If we have a register variable, try to allocate a register and
455 ** convert the declaration to "auto" if this is not possible.
457 int Reg = 0; /* Initialize to avoid gcc complains */
458 if ((Decl.StorageClass & SC_REGISTER) != 0 &&
459 (Reg = F_AllocRegVar (CurrentFunc, Decl.Type)) < 0) {
460 /* No space for this register variable, convert to auto */
461 Decl.StorageClass = (Decl.StorageClass & ~SC_REGISTER) | SC_AUTO;
464 /* Check the variable type */
465 if ((Decl.StorageClass & SC_REGISTER) == SC_REGISTER) {
466 /* Register variable */
467 ParseRegisterDecl (&Decl, Reg);
468 } else if ((Decl.StorageClass & SC_AUTO) == SC_AUTO) {
470 ParseAutoDecl (&Decl);
471 } else if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN) {
472 /* External identifier - may not get initialized */
473 if (CurTok.Tok == TOK_ASSIGN) {
474 Error ("Cannot initialize externals");
476 /* Add the external symbol to the symbol table */
477 AddLocalSym (Decl.Ident, Decl.Type, Decl.StorageClass, 0);
478 } else if ((Decl.StorageClass & SC_STATIC) == SC_STATIC) {
479 /* Static variable */
480 ParseStaticDecl (&Decl);
482 Internal ("Invalid storage class in ParseOneDecl: %04X", Decl.StorageClass);
487 /* Add the symbol to the symbol table */
488 AddLocalSym (Decl.Ident, Decl.Type, Decl.StorageClass, 0);
495 void DeclareLocals (void)
496 /* Declare local variables and types. */
498 /* Remember the current stack pointer */
499 int InitialStack = StackPtr;
501 /* A place to store info about potential initializations of auto variables */
502 CollAppend (&CurrentFunc->LocalsBlockStack, 0);
504 /* Loop until we don't find any more variables */
507 /* Check variable declarations. We need to distinguish between a
508 ** default int type and the end of variable declarations. So we
509 ** will do the following: If there is no explicit storage class
510 ** specifier *and* no explicit type given, *and* no type qualifiers
511 ** have been read, it is assumed that we have reached the end of
515 ParseDeclSpec (&Spec, SC_AUTO, T_INT);
516 if ((Spec.Flags & DS_DEF_STORAGE) != 0 && /* No storage spec */
517 (Spec.Flags & DS_DEF_TYPE) != 0 && /* No type given */
518 GetQualifier (Spec.Type) == T_QUAL_NONE) { /* No type qualifier */
522 /* Accept type only declarations */
523 if (CurTok.Tok == TOK_SEMI) {
524 /* Type declaration only */
525 CheckEmptyDecl (&Spec);
530 /* Parse a comma separated variable list */
533 /* Parse one declaration */
534 ParseOneDecl (&Spec);
536 /* Check if there is more */
537 if (CurTok.Tok == TOK_COMMA) {
546 /* A semicolon must follow */
550 /* Be sure to allocate any reserved space for locals */
551 F_AllocLocalSpace (CurrentFunc);
553 /* No auto variables were inited. No new block on the stack then. */
554 if (CollLast (&CurrentFunc->LocalsBlockStack) == NULL) {
555 CollPop (&CurrentFunc->LocalsBlockStack);
558 /* In case we've allocated local variables in this block, emit a call to
559 ** the stack checking routine if stack checks are enabled.
561 if (IS_Get (&CheckStack) && InitialStack != StackPtr) {