]> git.sur5r.net Git - cc65/blob - src/cc65/locals.c
Fixed a bug in the last change. For variables that have no initializer, no
[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-2009, 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 unsigned AllocStorage (void (*UseSeg) (), unsigned Size)
83 /* Reserve Size bytes of BSS storage prefixed by a local label. Return the
84  * label.
85  */
86 {
87     /* Switch to the segment and define the label */
88     unsigned Label = AllocLabel (UseSeg);
89
90     /* Reserve space for the data */
91     g_res (Size);
92
93     /* Return the label */
94     return Label;
95 }
96
97
98
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.
102  */
103 {
104     unsigned InitLabel;
105
106     /* Determine if this is a compound variable */
107     int IsCompound = IsClassStruct (Decl->Type) || IsTypeArray (Decl->Type);
108
109     /* Get the size of the variable */
110     unsigned Size = SizeOf (Decl->Type);
111
112     /* Save the current contents of the register variable on stack */
113     F_AllocLocalSpace (CurrentFunc);
114     g_save_regvars (Reg, Size);
115
116     /* Check for an optional initialization */
117     if (CurTok.Tok == TOK_ASSIGN) {
118
119         ExprDesc Expr;
120
121         /* Skip the '=' */
122         NextToken ();
123
124         /* Special handling for compound types */
125         if (IsCompound) {
126
127             /* Switch to read only data and define a label for the
128              * initialization data.
129              */
130             InitLabel = AllocLabel (g_userodata);
131
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.
139              */
140             if (ParseInit (Decl->Type) != Size) {
141                 Error ("Cannot initialize flexible array members of storage class `register'");
142             }
143
144             /* Generate code to copy this data into the variable space */
145             g_initregister (InitLabel, Reg, Size);
146
147         } else {
148
149             /* Parse the expression */
150             hie1 (&Expr);
151
152             /* Convert it to the target type */
153             TypeConversion (&Expr, Decl->Type);
154
155             /* Load the value into the primary */
156             LoadExpr (CF_NONE, &Expr);
157
158             /* Store the value into the variable */
159             g_putstatic (CF_REGVAR | TypeOf (Decl->Type), Reg, 0);
160
161         }
162
163         /* Mark the variable as referenced */
164         *SC |= SC_REF;
165     }
166
167     /* Cannot allocate a variable of zero size */
168     if (Size == 0) {
169         Error ("Variable `%s' has unknown size", Decl->Ident);
170     }
171
172     /* Return the symbol data */
173     return Reg;
174 }
175
176
177
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
181  * static variables.
182  */
183 {
184     unsigned Flags;
185     unsigned SymData;
186
187     /* Determine if this is a compound variable */
188     int IsCompound = IsClassStruct (Decl->Type) || IsTypeArray (Decl->Type);
189
190     /* Get the size of the variable */
191     unsigned Size = SizeOf (Decl->Type);
192
193     /* Check if this is a variable on the stack or in static memory */
194     if (IS_Get (&StaticLocals) == 0) {
195
196         /* Check for an optional initialization */
197         if (CurTok.Tok == TOK_ASSIGN) {
198
199             ExprDesc Expr;
200
201             /* Skip the '=' */
202             NextToken ();
203
204             /* Special handling for compound types */
205             if (IsCompound) {
206
207                 /* Switch to read only data and define a label for the
208                  * initialization data.
209                  */
210                 unsigned InitLabel = AllocLabel (g_userodata);
211
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
217                  * ANSI mode.
218                  */
219                 Size = ParseInit (Decl->Type);
220
221                 /* Now reserve space for the variable on the stack */
222                 SymData = F_ReserveLocalSpace (CurrentFunc, Size);
223
224                 /* Next, allocate the space on the stack. This means that the
225                  * variable is now located at offset 0 from the current sp.
226                  */
227                 F_AllocLocalSpace (CurrentFunc);
228
229                 /* Generate code to copy the initialization data into the
230                  * variable space
231                  */
232                 g_initauto (InitLabel, Size);
233
234             } else {
235
236                 /* Allocate previously reserved local space */
237                 F_AllocLocalSpace (CurrentFunc);
238
239                 /* Setup the type flags for the assignment */
240                 Flags = (Size == SIZEOF_CHAR)? CF_FORCECHAR : CF_NONE;
241
242                 /* Parse the expression */
243                 hie1 (&Expr);
244
245                 /* Convert it to the target type */
246                 TypeConversion (&Expr, Decl->Type);
247
248                 /* If the value is not const, load it into the primary.
249                  * Otherwise pass the information to the code generator.
250                  */
251                 if (ED_IsConstAbsInt (&Expr)) {
252                     Flags |= CF_CONST;
253                 } else {
254                     LoadExpr (CF_NONE, &Expr);
255                     ED_MakeRVal (&Expr);
256                 }
257
258                 /* Push the value */
259                 g_push (Flags | TypeOf (Decl->Type), Expr.IVal);
260
261             }
262
263             /* Mark the variable as referenced */
264             *SC |= SC_REF;
265
266             /* Variable is located at the current SP */
267             SymData = StackPtr;
268
269         } else {
270             /* Non-initialized local variable. Just keep track of
271              * the space needed.
272              */
273             SymData = F_ReserveLocalSpace (CurrentFunc, Size);
274         }
275
276     } else {
277
278         /* Static local variables. */
279         *SC = (*SC & ~SC_AUTO) | SC_STATIC;
280
281         /* Allow assignments */
282         if (CurTok.Tok == TOK_ASSIGN) {
283
284             ExprDesc Expr;
285
286             /* Skip the '=' */
287             NextToken ();
288
289             if (IsCompound) {
290
291                 /* Switch to read only data and define a label for the
292                  * initialization data.
293                  */
294                 unsigned InitLabel = AllocLabel (g_userodata);
295
296                 /* Parse the initialization generating a memory image of the
297                  * data in the RODATA segment.
298                  */
299                 Size = ParseInit (Decl->Type);
300
301                 /* Allocate a label and space for the variable */
302                 SymData = AllocStorage (g_usebss, Size);
303
304                 /* Generate code to copy this data into the variable space */
305                 g_initstatic (InitLabel, SymData, Size);
306
307             } else {
308
309                 /* Allocate a label and space for the variable */
310                 SymData = AllocStorage (g_usebss, Size);
311
312                 /* Parse the expression */
313                 hie1 (&Expr);
314
315                 /* Convert it to the target type */
316                 TypeConversion (&Expr, Decl->Type);
317
318                 /* Load the value into the primary */
319                 LoadExpr (CF_NONE, &Expr);
320
321                 /* Store the value into the variable */
322                 g_putstatic (TypeOf (Decl->Type), SymData, 0);
323             }
324
325             /* Mark the variable as referenced */
326             *SC |= SC_REF;
327
328         } else {
329
330             /* No assignment - allocate a label and space for the variable */
331             SymData = AllocStorage (g_usebss, Size);
332
333         }
334     }
335
336     /* Cannot allocate a variable of zero size */
337     if (Size == 0) {
338         Error ("Variable `%s' has unknown size", Decl->Ident);
339     }
340
341     /* Return the symbol data */
342     return SymData;
343 }
344
345
346
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.
350  */
351 {
352     unsigned SymData;
353     unsigned Size;
354
355     /* Static data */
356     if (CurTok.Tok == TOK_ASSIGN) {
357
358         /* Initialization ahead, switch to data segment and define a label */
359         if (IsQualConst (Decl->Type)) {
360             SymData = AllocLabel (g_userodata);
361         } else {
362             SymData = AllocLabel (g_usedata);
363         }
364
365         /* Skip the '=' */
366         NextToken ();
367
368         /* Allow initialization of static vars */
369         Size = ParseInit (Decl->Type);
370
371         /* Mark the variable as referenced */
372         *SC |= SC_REF;
373
374     } else {
375
376         /* Get the size of the variable */
377         Size = SizeOf (Decl->Type);
378
379         /* Allocate a label and space for the variable in the BSS segment */
380         SymData = AllocStorage (g_usebss, Size);
381
382     }
383
384     /* Cannot allocate a variable of zero size */
385     if (Size == 0) {
386         Error ("Variable `%s' has unknown size", Decl->Ident);
387     }
388
389     /* Return the symbol data */
390     return SymData;
391 }
392
393
394
395 static void ParseOneDecl (const DeclSpec* Spec)
396 /* Parse one variable declaration */
397 {
398     unsigned    SymData = 0;    /* Symbol data (offset, label name, ...) */
399     Declaration Decl;           /* Declaration data structure */
400
401
402     /* Read the declaration */
403     ParseDecl (Spec, &Decl, DM_NEED_IDENT);
404
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");
410         }
411         Decl.StorageClass |= SC_EXTERN;
412     }
413
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.
416      */
417     if (Decl.Ident[0] == '\0') {
418         AnonName (Decl.Ident, "param");
419     }
420
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) {
424
425         /* If we have a register variable, try to allocate a register and
426          * convert the declaration to "auto" if this is not possible.
427          */
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;
433         }
434
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) {
440             /* Auto variable */
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");
446             }
447             SymData = 0;
448         } else if ((Decl.StorageClass & SC_STATIC) == SC_STATIC) {
449             /* Static variable */
450             SymData = ParseStaticDecl (&Decl, &Decl.StorageClass);
451         } else {
452             Internal ("Invalid storage class in ParseOneDecl: %04X", Decl.StorageClass);
453         }
454     }
455
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;
459     }
460
461     /* Add the symbol to the symbol table */
462     AddLocalSym (Decl.Ident, Decl.Type, Decl.StorageClass, SymData);
463 }
464
465
466
467 void DeclareLocals (void)
468 /* Declare local variables and types. */
469 {
470     /* Remember the current stack pointer */
471     int InitialStack = StackPtr;
472
473     /* Loop until we don't find any more variables */
474     while (1) {
475
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
481          * declarations.
482          */
483         DeclSpec Spec;
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 */
488             break;
489         }
490
491         /* Accept type only declarations */
492         if (CurTok.Tok == TOK_SEMI) {
493             /* Type declaration only */
494             CheckEmptyDecl (&Spec);
495             NextToken ();
496             continue;
497         }
498
499         /* Parse a comma separated variable list */
500         while (1) {
501
502             /* Parse one declaration */
503             ParseOneDecl (&Spec);
504
505             /* Check if there is more */
506             if (CurTok.Tok == TOK_COMMA) {
507                 /* More to come */
508                 NextToken ();
509             } else {
510                 /* Done */
511                 break;
512             }
513         }
514
515         /* A semicolon must follow */
516         ConsumeSemi ();
517     }
518
519     /* Be sure to allocate any reserved space for locals */
520     F_AllocLocalSpace (CurrentFunc);
521
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.
524      */
525     if (IS_Get (&CheckStack) && InitialStack != StackPtr) {
526         g_cstackcheck ();
527     }
528 }
529
530
531