1 /*****************************************************************************/
5 /* Local variable handling for the cc65 C compiler */
9 /* (C) 2000-2004 Ullrich von Bassewitz */
10 /* Römerstrasse 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 /* Cannot allocate a variable of zero size */
332 Error ("Variable `%s' has unknown size", Decl->Ident);
335 /* Return the symbol data */
341 static unsigned ParseStaticDecl (Declaration* Decl, unsigned* SC)
342 /* Parse the declaration of a static variable. The function returns the symbol
343 * data, which is the asm label of the variable.
350 if (CurTok.Tok == TOK_ASSIGN) {
352 /* Initialization ahead, switch to data segment and define a label */
353 if (IsQualConst (Decl->Type)) {
354 SymData = AllocLabel (g_userodata);
356 SymData = AllocLabel (g_usedata);
362 /* Allow initialization of static vars */
363 Size = ParseInit (Decl->Type);
365 /* Mark the variable as referenced */
370 /* Get the size of the variable */
371 Size = SizeOf (Decl->Type);
373 /* Allocate a label and space for the variable in the BSS segment */
374 SymData = AllocStorage (g_usebss, Size);
378 /* Cannot allocate a variable of zero size */
380 Error ("Variable `%s' has unknown size", Decl->Ident);
383 /* Return the symbol data */
389 static void ParseOneDecl (const DeclSpec* Spec)
390 /* Parse one variable declaration */
392 unsigned SymData = 0; /* Symbol data (offset, label name, ...) */
393 Declaration Decl; /* Declaration data structure */
396 /* Read the declaration */
397 ParseDecl (Spec, &Decl, DM_NEED_IDENT);
399 /* Set the correct storage class for functions */
400 if ((Decl.StorageClass & SC_FUNC) == SC_FUNC) {
401 /* Function prototypes are always external */
402 if ((Decl.StorageClass & SC_EXTERN) == 0) {
403 Warning ("Function must be extern");
405 Decl.StorageClass |= SC_EXTERN;
408 /* If we don't have a name, this was flagged as an error earlier.
409 * To avoid problems later, use an anonymous name here.
411 if (Decl.Ident[0] == '\0') {
412 AnonName (Decl.Ident, "param");
415 /* Handle anything that needs storage (no functions, no typdefs) */
416 if ((Decl.StorageClass & SC_FUNC) != SC_FUNC &&
417 (Decl.StorageClass & SC_TYPEDEF) != SC_TYPEDEF) {
419 /* If we have a register variable, try to allocate a register and
420 * convert the declaration to "auto" if this is not possible.
422 int Reg = 0; /* Initialize to avoid gcc complains */
423 if ((Decl.StorageClass & SC_REGISTER) != 0 &&
424 (Reg = F_AllocRegVar (CurrentFunc, Decl.Type)) < 0) {
425 /* No space for this register variable, convert to auto */
426 Decl.StorageClass = (Decl.StorageClass & ~SC_REGISTER) | SC_AUTO;
429 /* Check the variable type */
430 if ((Decl.StorageClass & SC_REGISTER) == SC_REGISTER) {
431 /* Register variable */
432 SymData = ParseRegisterDecl (&Decl, &Decl.StorageClass, Reg);
433 } else if ((Decl.StorageClass & SC_AUTO) == SC_AUTO) {
435 SymData = ParseAutoDecl (&Decl, &Decl.StorageClass);
436 } else if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN) {
437 /* External identifier - may not get initialized */
438 if (CurTok.Tok == TOK_ASSIGN) {
439 Error ("Cannot initialize externals");
442 } else if ((Decl.StorageClass & SC_STATIC) == SC_STATIC) {
443 /* Static variable */
444 SymData = ParseStaticDecl (&Decl, &Decl.StorageClass);
446 Internal ("Invalid storage class in ParseOneDecl: %04X", Decl.StorageClass);
450 /* If the symbol is not marked as external, it will be defined now */
451 if ((Decl.StorageClass & SC_EXTERN) == 0) {
452 Decl.StorageClass |= SC_DEF;
455 /* Add the symbol to the symbol table */
456 AddLocalSym (Decl.Ident, Decl.Type, Decl.StorageClass, SymData);
461 void DeclareLocals (void)
462 /* Declare local variables and types. */
464 /* Remember the current stack pointer */
465 int InitialStack = StackPtr;
467 /* Loop until we don't find any more variables */
470 /* Check variable declarations. We need to distinguish between a
471 * default int type and the end of variable declarations. So we
472 * will do the following: If there is no explicit storage class
473 * specifier *and* no explicit type given, *and* no type qualifiers
474 * have been read, it is assumed that we have reached the end of
478 ParseDeclSpec (&Spec, SC_AUTO, T_INT);
479 if ((Spec.Flags & DS_DEF_STORAGE) != 0 && /* No storage spec */
480 (Spec.Flags & DS_DEF_TYPE) != 0 && /* No type given */
481 GetQualifier (Spec.Type) == T_QUAL_NONE) { /* No type qualifier */
485 /* Accept type only declarations */
486 if (CurTok.Tok == TOK_SEMI) {
487 /* Type declaration only */
488 CheckEmptyDecl (&Spec);
493 /* Parse a comma separated variable list */
496 /* Parse one declaration */
497 ParseOneDecl (&Spec);
499 /* Check if there is more */
500 if (CurTok.Tok == TOK_COMMA) {
509 /* A semicolon must follow */
513 /* Be sure to allocate any reserved space for locals */
514 F_AllocLocalSpace (CurrentFunc);
516 /* In case we've allocated local variables in this block, emit a call to
517 * the stack checking routine if stack checks are enabled.
519 if (IS_Get (&CheckStack) && InitialStack != StackPtr) {