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