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