]> git.sur5r.net Git - cc65/blob - src/cc65/locals.c
Removed (pretty inconsistently used) tab chars from source code base.
[cc65] / src / cc65 / locals.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 locals.c                                  */
4 /*                                                                           */
5 /*              Local variable handling for the cc65 C compiler              */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2000-2013, Ullrich von Bassewitz                                      */
10 /*                Roemerstrasse 52                                           */
11 /*                D-70794 Filderstadt                                        */
12 /* EMail:         uz@cc65.org                                                */
13 /*                                                                           */
14 /*                                                                           */
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.                                    */
18 /*                                                                           */
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:                            */
22 /*                                                                           */
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              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 /* common */
37 #include "xmalloc.h"
38 #include "xsprintf.h"
39
40 /* cc65 */
41 #include "anonname.h"
42 #include "asmlabel.h"
43 #include "codegen.h"
44 #include "declare.h"
45 #include "error.h"
46 #include "expr.h"
47 #include "function.h"
48 #include "global.h"
49 #include "loadexpr.h"
50 #include "locals.h"
51 #include "stackptr.h"
52 #include "standard.h"
53 #include "symtab.h"
54 #include "typeconv.h"
55
56
57
58 /*****************************************************************************/
59 /*                                   Code                                    */
60 /*****************************************************************************/
61
62
63
64 static unsigned AllocLabel (void (*UseSeg) ())
65 /* Switch to a segment, define a local label and return it */
66 {
67     unsigned Label;
68
69     /* Switch to the segment */
70     UseSeg ();
71
72     /* Define the variable label */
73     Label = GetLocalLabel ();
74     g_defdatalabel (Label);
75
76     /* Return the label */
77     return Label;
78 }
79
80
81
82 static void AllocStorage (unsigned Label, void (*UseSeg) (), unsigned Size)
83 /* Reserve Size bytes of BSS storage prefixed by a local label. */
84 {
85     /* Switch to the segment */
86     UseSeg ();
87
88     /* Define the variable label */
89     g_defdatalabel (Label);
90
91     /* Reserve space for the data */
92     g_res (Size);
93 }
94
95
96
97 static void ParseRegisterDecl (Declaration* Decl, int Reg)
98 /* Parse the declaration of a register variable. Reg is the offset of the
99  * variable in the register bank.
100  */
101 {
102     SymEntry* Sym;
103
104     /* Determine if this is a compound variable */
105     int IsCompound = IsClassStruct (Decl->Type) || IsTypeArray (Decl->Type);
106
107     /* Get the size of the variable */
108     unsigned Size = SizeOf (Decl->Type);
109
110     /* Save the current contents of the register variable on stack */
111     F_AllocLocalSpace (CurrentFunc);
112     g_save_regvars (Reg, Size);
113
114     /* Add the symbol to the symbol table. We do that now, because for register
115      * variables the current stack pointer is implicitly used as location for
116      * the save area.
117      */
118     Sym = AddLocalSym (Decl->Ident, Decl->Type, Decl->StorageClass, Reg);
119
120     /* Check for an optional initialization */
121     if (CurTok.Tok == TOK_ASSIGN) {
122
123         ExprDesc Expr;
124
125         /* Skip the '=' */
126         NextToken ();
127
128         /* Special handling for compound types */
129         if (IsCompound) {
130
131             /* Switch to read only data and define a label for the
132              * initialization data.
133              */
134             unsigned InitLabel = AllocLabel (g_userodata);
135
136             /* Parse the initialization generating a memory image of the
137              * data in the RODATA segment. The function does return the size
138              * of the initialization data, which may be greater than the
139              * actual size of the type, if the type is a structure with a
140              * flexible array member that has been initialized. Since we must
141              * know the size of the data in advance for register variables,
142              * we cannot allow that here.
143              */
144             if (ParseInit (Sym->Type) != Size) {
145                 Error ("Cannot initialize flexible array members of storage class `register'");
146             }
147
148             /* Generate code to copy this data into the variable space */
149             g_initregister (InitLabel, Reg, Size);
150
151         } else {
152
153             /* Parse the expression */
154             hie1 (&Expr);
155
156             /* Convert it to the target type */
157             TypeConversion (&Expr, Sym->Type);
158
159             /* Load the value into the primary */
160             LoadExpr (CF_NONE, &Expr);
161
162             /* Store the value into the variable */
163             g_putstatic (CF_REGVAR | TypeOf (Sym->Type), Reg, 0);
164
165         }
166
167         /* Mark the variable as referenced */
168         Sym->Flags |= SC_REF;
169     }
170
171     /* Cannot allocate a variable of zero size */
172     if (Size == 0) {
173         Error ("Variable `%s' has unknown size", Decl->Ident);
174     }
175 }
176
177
178
179 static void ParseAutoDecl (Declaration* Decl)
180 /* Parse the declaration of an auto variable. */
181 {
182     unsigned  Flags;
183     SymEntry* Sym;
184
185     /* Determine if this is a compound variable */
186     int IsCompound = IsClassStruct (Decl->Type) || IsTypeArray (Decl->Type);
187
188     /* Get the size of the variable */
189     unsigned Size = SizeOf (Decl->Type);
190
191     /* Check if this is a variable on the stack or in static memory */
192     if (IS_Get (&StaticLocals) == 0) {
193
194         /* Add the symbol to the symbol table. The stack offset we use here
195          * may get corrected later.
196          */
197         Sym = AddLocalSym (Decl->Ident, Decl->Type,
198                            Decl->StorageClass,
199                            F_GetStackPtr (CurrentFunc) - (int) Size);
200
201         /* Check for an optional initialization */
202         if (CurTok.Tok == TOK_ASSIGN) {
203
204             ExprDesc Expr;
205
206             /* Skip the '=' */
207             NextToken ();
208
209             /* Special handling for compound types */
210             if (IsCompound) {
211
212                 /* Switch to read only data and define a label for the
213                  * initialization data.
214                  */
215                 unsigned InitLabel = AllocLabel (g_userodata);
216
217                 /* Parse the initialization generating a memory image of the
218                  * data in the RODATA segment. The function will return the
219                  * actual size of the initialization data, which may be
220                  * greater than the size of the variable if it is a struct
221                  * that contains a flexible array member and we're not in
222                  * ANSI mode.
223                  */
224                 Size = ParseInit (Sym->Type);
225
226                 /* Now reserve space for the variable on the stack and correct
227                  * the offset in the symbol table entry.
228                  */
229                 Sym->V.Offs = F_ReserveLocalSpace (CurrentFunc, Size);
230
231                 /* Next, allocate the space on the stack. This means that the
232                  * variable is now located at offset 0 from the current sp.
233                  */
234                 F_AllocLocalSpace (CurrentFunc);
235
236                 /* Generate code to copy the initialization data into the
237                  * variable space
238                  */
239                 g_initauto (InitLabel, Size);
240
241             } else {
242
243                 /* Allocate previously reserved local space */
244                 F_AllocLocalSpace (CurrentFunc);
245
246                 /* Setup the type flags for the assignment */
247                 Flags = (Size == SIZEOF_CHAR)? CF_FORCECHAR : CF_NONE;
248
249                 /* Parse the expression */
250                 hie1 (&Expr);
251
252                 /* Convert it to the target type */
253                 TypeConversion (&Expr, Sym->Type);
254
255                 /* If the value is not const, load it into the primary.
256                  * Otherwise pass the information to the code generator.
257                  */
258                 if (ED_IsConstAbsInt (&Expr)) {
259                     Flags |= CF_CONST;
260                 } else {
261                     LoadExpr (CF_NONE, &Expr);
262                     ED_MakeRVal (&Expr);
263                 }
264
265                 /* Push the value */
266                 g_push (Flags | TypeOf (Sym->Type), Expr.IVal);
267
268             }
269
270             /* Mark the variable as referenced */
271             Sym->Flags |= SC_REF;
272
273         } else {
274             /* Non-initialized local variable. Just keep track of
275              * the space needed.
276              */
277             F_ReserveLocalSpace (CurrentFunc, Size);
278         }
279
280     } else {
281
282         unsigned DataLabel;
283
284
285         /* Static local variables. */
286         Decl->StorageClass = (Decl->StorageClass & ~SC_AUTO) | SC_STATIC;
287
288         /* Generate a label, but don't define it */
289         DataLabel = GetLocalLabel ();
290
291         /* Add the symbol to the symbol table. */
292         Sym = AddLocalSym (Decl->Ident, Decl->Type, Decl->StorageClass, DataLabel);
293
294         /* Allow assignments */
295         if (CurTok.Tok == TOK_ASSIGN) {
296
297             ExprDesc Expr;
298
299             /* Skip the '=' */
300             NextToken ();
301
302             if (IsCompound) {
303
304                 /* Switch to read only data and define a label for the
305                  * initialization data.
306                  */
307                 unsigned InitLabel = AllocLabel (g_userodata);
308
309                 /* Parse the initialization generating a memory image of the
310                  * data in the RODATA segment.
311                  */
312                 Size = ParseInit (Sym->Type);
313
314                 /* Allocate space for the variable */
315                 AllocStorage (DataLabel, g_usebss, Size);
316
317                 /* Generate code to copy this data into the variable space */
318                 g_initstatic (InitLabel, DataLabel, Size);
319
320             } else {
321
322                 /* Allocate space for the variable */
323                 AllocStorage (DataLabel, g_usebss, Size);
324
325                 /* Parse the expression */
326                 hie1 (&Expr);
327
328                 /* Convert it to the target type */
329                 TypeConversion (&Expr, Sym->Type);
330
331                 /* Load the value into the primary */
332                 LoadExpr (CF_NONE, &Expr);
333
334                 /* Store the value into the variable */
335                 g_putstatic (TypeOf (Sym->Type), DataLabel, 0);
336             }
337
338             /* Mark the variable as referenced */
339             Sym->Flags |= SC_REF;
340
341         } else {
342
343             /* No assignment - allocate a label and space for the variable */
344             AllocStorage (DataLabel, g_usebss, Size);
345
346         }
347     }
348
349     /* Cannot allocate a variable of zero size */
350     if (Size == 0) {
351         Error ("Variable `%s' has unknown size", Decl->Ident);
352     }
353 }
354
355
356
357 static void ParseStaticDecl (Declaration* Decl)
358 /* Parse the declaration of a static variable. */
359 {
360     unsigned Size;
361
362     /* Generate a label, but don't define it */
363     unsigned DataLabel = GetLocalLabel ();
364
365     /* Add the symbol to the symbol table. */
366     SymEntry* Sym = AddLocalSym (Decl->Ident, Decl->Type,
367                                  Decl->StorageClass,
368                                  DataLabel);
369
370     /* Static data */
371     if (CurTok.Tok == TOK_ASSIGN) {
372
373         /* Initialization ahead, switch to data segment and define the label.
374          * For arrays, we need to check the elements of the array for
375          * constness, not the array itself.
376          */
377         if (IsQualConst (GetBaseElementType (Sym->Type))) {
378             g_userodata ();
379         } else {
380             g_usedata ();
381         }
382         g_defdatalabel (DataLabel);
383
384         /* Skip the '=' */
385         NextToken ();
386
387         /* Allow initialization of static vars */
388         Size = ParseInit (Sym->Type);
389
390         /* Mark the variable as referenced */
391         Sym->Flags |= SC_REF;
392
393     } else {
394
395         /* Get the size of the variable */
396         Size = SizeOf (Decl->Type);
397
398         /* Allocate a label and space for the variable in the BSS segment */
399         AllocStorage (DataLabel, g_usebss, Size);
400
401     }
402
403     /* Cannot allocate a variable of zero size */
404     if (Size == 0) {
405         Error ("Variable `%s' has unknown size", Decl->Ident);
406     }
407 }
408
409
410
411 static void ParseOneDecl (const DeclSpec* Spec)
412 /* Parse one variable declaration */
413 {
414     Declaration Decl;           /* Declaration data structure */
415
416
417     /* Read the declaration */
418     ParseDecl (Spec, &Decl, DM_NEED_IDENT);
419
420     /* Set the correct storage class for functions */
421     if ((Decl.StorageClass & SC_FUNC) == SC_FUNC) {
422         /* Function prototypes are always external */
423         if ((Decl.StorageClass & SC_EXTERN) == 0) {
424             Warning ("Function must be extern");
425         }
426         Decl.StorageClass |= SC_EXTERN;
427     }
428
429     /* If we don't have a name, this was flagged as an error earlier.
430      * To avoid problems later, use an anonymous name here.
431      */
432     if (Decl.Ident[0] == '\0') {
433         AnonName (Decl.Ident, "param");
434     }
435
436     /* If the symbol is not marked as external, it will be defined now */
437     if ((Decl.StorageClass & SC_EXTERN) == 0) {
438         Decl.StorageClass |= SC_DEF;
439     }
440
441     /* Handle anything that needs storage (no functions, no typdefs) */
442     if ((Decl.StorageClass & SC_FUNC) != SC_FUNC &&
443          (Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF) {
444
445         /* If we have a register variable, try to allocate a register and
446          * convert the declaration to "auto" if this is not possible.
447          */
448         int Reg = 0;    /* Initialize to avoid gcc complains */
449         if ((Decl.StorageClass & SC_REGISTER) != 0 &&
450             (Reg = F_AllocRegVar (CurrentFunc, Decl.Type)) < 0) {
451             /* No space for this register variable, convert to auto */
452             Decl.StorageClass = (Decl.StorageClass & ~SC_REGISTER) | SC_AUTO;
453         }
454
455         /* Check the variable type */
456         if ((Decl.StorageClass & SC_REGISTER) == SC_REGISTER) {
457             /* Register variable */
458             ParseRegisterDecl (&Decl, Reg);
459         } else if ((Decl.StorageClass & SC_AUTO) == SC_AUTO) {
460             /* Auto variable */
461             ParseAutoDecl (&Decl);
462         } else if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN) {
463             /* External identifier - may not get initialized */
464             if (CurTok.Tok == TOK_ASSIGN) {
465                 Error ("Cannot initialize externals");
466             }
467             /* Add the external symbol to the symbol table */
468             AddLocalSym (Decl.Ident, Decl.Type, Decl.StorageClass, 0);
469         } else if ((Decl.StorageClass & SC_STATIC) == SC_STATIC) {
470             /* Static variable */
471             ParseStaticDecl (&Decl);
472         } else {
473             Internal ("Invalid storage class in ParseOneDecl: %04X", Decl.StorageClass);
474         }
475
476     } else {
477
478         /* Add the symbol to the symbol table */
479         AddLocalSym (Decl.Ident, Decl.Type, Decl.StorageClass, 0);
480
481     }
482 }
483
484
485
486 void DeclareLocals (void)
487 /* Declare local variables and types. */
488 {
489     /* Remember the current stack pointer */
490     int InitialStack = StackPtr;
491
492     /* Loop until we don't find any more variables */
493     while (1) {
494
495         /* Check variable declarations. We need to distinguish between a
496          * default int type and the end of variable declarations. So we
497          * will do the following: If there is no explicit storage class
498          * specifier *and* no explicit type given, *and* no type qualifiers
499          * have been read, it is assumed that we have reached the end of
500          * declarations.
501          */
502         DeclSpec Spec;
503         ParseDeclSpec (&Spec, SC_AUTO, T_INT);
504         if ((Spec.Flags & DS_DEF_STORAGE) != 0 &&       /* No storage spec */
505             (Spec.Flags & DS_DEF_TYPE) != 0    &&       /* No type given */
506             GetQualifier (Spec.Type) == T_QUAL_NONE) {  /* No type qualifier */
507             break;
508         }
509
510         /* Accept type only declarations */
511         if (CurTok.Tok == TOK_SEMI) {
512             /* Type declaration only */
513             CheckEmptyDecl (&Spec);
514             NextToken ();
515             continue;
516         }
517
518         /* Parse a comma separated variable list */
519         while (1) {
520
521             /* Parse one declaration */
522             ParseOneDecl (&Spec);
523
524             /* Check if there is more */
525             if (CurTok.Tok == TOK_COMMA) {
526                 /* More to come */
527                 NextToken ();
528             } else {
529                 /* Done */
530                 break;
531             }
532         }
533
534         /* A semicolon must follow */
535         ConsumeSemi ();
536     }
537
538     /* Be sure to allocate any reserved space for locals */
539     F_AllocLocalSpace (CurrentFunc);
540
541     /* In case we've allocated local variables in this block, emit a call to
542      * the stack checking routine if stack checks are enabled.
543      */
544     if (IS_Get (&CheckStack) && InitialStack != StackPtr) {
545         g_cstackcheck ();
546     }
547 }
548
549
550