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