]> git.sur5r.net Git - cc65/blob - src/cc65/locals.c
Made several options that can be changed by #pragmas stackable.
[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             int k = hie1 (InitExprDesc (&lval));
115
116             /* Convert it to the target type */
117             k = TypeConversion (&lval, k, Decl->Type);
118
119             /* Load the value into the primary */
120             ExprLoad (CF_NONE, k, &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                 int k;
204
205                 /* Allocate previously reserved local space */
206                 F_AllocLocalSpace (CurrentFunc);
207
208                 /* Setup the type flags for the assignment */
209                 Flags = (Size == SIZEOF_CHAR)? CF_FORCECHAR : CF_NONE;
210
211                 /* Parse the expression */
212                 k = hie1 (InitExprDesc (&lval));
213
214                 /* Convert it to the target type */
215                 k = TypeConversion (&lval, k, Decl->Type);
216
217                 /* If the value is not const, load it into the primary.
218                  * Otherwise pass the information to the code generator.
219                  */
220                 if (k != 0 || lval.Flags != E_MCONST) {
221                     ExprLoad (CF_NONE, k, &lval);
222                     k = 0;
223                 } else {
224                     Flags |= CF_CONST;
225                 }
226
227                 /* Push the value */
228                 g_push (Flags | TypeOf (Decl->Type), lval.ConstVal);
229
230             }
231
232             /* Mark the variable as referenced */
233             *SC |= SC_REF;
234
235             /* Variable is located at the current SP */
236             SymData = oursp;
237
238         } else {
239             /* Non-initialized local variable. Just keep track of
240              * the space needed.
241              */
242             SymData = F_ReserveLocalSpace (CurrentFunc, Size);
243         }
244
245     } else {
246
247         /* Static local variables. */
248         *SC = (*SC & ~SC_AUTO) | SC_STATIC;
249
250         /* Put them into the BSS */
251         g_usebss ();
252
253         /* Define the variable label */
254         SymData = GetLocalLabel ();
255         g_defdatalabel (SymData);
256
257         /* Reserve space for the data */
258         g_res (Size);
259
260         /* Allow assignments */
261         if (CurTok.Tok == TOK_ASSIGN) {
262
263             ExprDesc lval;
264
265             /* Skip the '=' */
266             NextToken ();
267
268             if (IsCompound) {
269
270                 /* Switch to read only data */
271                 g_userodata ();
272
273                 /* Define a label for the initialization data */
274                 InitLabel = GetLocalLabel ();
275                 g_defdatalabel (InitLabel);
276
277                 /* Parse the initialization generating a memory image of the
278                  * data in the RODATA segment.
279                  */
280                 ParseInit (Decl->Type);
281
282                 /* Generate code to copy this data into the variable space */
283                 g_initstatic (InitLabel, SymData, Size);
284
285             } else {
286
287                 /* Parse the expression */
288                 int k = hie1 (InitExprDesc (&lval));
289
290                 /* Convert it to the target type */
291                 k = TypeConversion (&lval, k, Decl->Type);
292
293                 /* Load the value into the primary */
294                 ExprLoad (CF_NONE, k, &lval);
295
296                 /* Store the value into the variable */
297                 g_putstatic (TypeOf (Decl->Type), SymData, 0);
298             }
299
300             /* Mark the variable as referenced */
301             *SC |= SC_REF;
302         }
303     }
304
305     /* Cannot allocate a variable of zero size */
306     if (Size == 0) {
307         Error ("Variable `%s' has unknown size", Decl->Ident);
308     }
309
310     /* Return the symbol data */
311     return SymData;
312 }
313
314
315
316 static unsigned ParseStaticDecl (Declaration* Decl, unsigned* SC)
317 /* Parse the declaration of a static variable. The function returns the symbol
318  * data, which is the asm label of the variable.
319  */
320 {
321     unsigned SymData;
322
323     /* Get the size of the variable */
324     unsigned Size = SizeOf (Decl->Type);
325
326     /* Static data */
327     if (CurTok.Tok == TOK_ASSIGN) {
328
329         /* Initialization ahead, switch to data segment */
330         if (IsQualConst (Decl->Type)) {
331             g_userodata ();
332         } else {
333             g_usedata ();
334         }
335
336         /* Define the variable label */
337         SymData = GetLocalLabel ();
338         g_defdatalabel (SymData);
339
340         /* Skip the '=' */
341         NextToken ();
342
343         /* Allow initialization of static vars */
344         ParseInit (Decl->Type);
345
346         /* If the previous size has been unknown, it must be known now */
347         if (Size == 0) {
348             Size = SizeOf (Decl->Type);
349         }
350
351         /* Mark the variable as referenced */
352         *SC |= SC_REF;
353
354     } else {
355
356         /* Uninitialized data, use BSS segment */
357         g_usebss ();
358
359         /* Define the variable label */
360         SymData = GetLocalLabel ();
361         g_defdatalabel (SymData);
362
363         /* Reserve space for the data */
364         g_res (Size);
365
366     }
367
368     /* Cannot allocate a variable of zero size */
369     if (Size == 0) {
370         Error ("Variable `%s' has unknown size", Decl->Ident);
371     }
372
373     /* Return the symbol data */
374     return SymData;
375 }
376
377
378
379 static void ParseOneDecl (const DeclSpec* Spec)
380 /* Parse one variable declaration */
381 {
382     unsigned    SC;             /* Storage class for symbol */
383     unsigned    SymData = 0;    /* Symbol data (offset, label name, ...) */
384     Declaration Decl;           /* Declaration data structure */
385
386
387     /* Remember the storage class for the new symbol */
388     SC = Spec->StorageClass;
389
390     /* Read the declaration */
391     ParseDecl (Spec, &Decl, DM_NEED_IDENT);
392
393     /* Set the correct storage class for functions */
394     if (IsTypeFunc (Decl.Type)) {
395         /* Function prototypes are always external */
396         if ((SC & SC_EXTERN) == 0) {
397             Warning ("Function must be extern");
398         }
399         SC |= SC_FUNC | SC_EXTERN;
400
401     }
402
403     /* If we don't have a name, this was flagged as an error earlier.
404      * To avoid problems later, use an anonymous name here.
405      */
406     if (Decl.Ident[0] == '\0') {
407         AnonName (Decl.Ident, "param");
408     }
409
410     /* Handle anything that needs storage (no functions, no typdefs) */
411     if ((SC & SC_FUNC) != SC_FUNC && (SC & SC_TYPEDEF) != SC_TYPEDEF) {
412
413         /* If we have a register variable, try to allocate a register and
414          * convert the declaration to "auto" if this is not possible.
415          */
416         int Reg = 0;    /* Initialize to avoid gcc complains */
417         if ((SC & SC_REGISTER) != 0 && (Reg = F_AllocRegVar (CurrentFunc, Decl.Type)) < 0) {
418             /* No space for this register variable, convert to auto */
419             SC = (SC & ~SC_REGISTER) | SC_AUTO;
420         }
421
422         /* Check the variable type */
423         if ((SC & SC_REGISTER) == SC_REGISTER) {
424             /* Register variable */
425             SymData = ParseRegisterDecl (&Decl, &SC, Reg);
426         } else if ((SC & SC_AUTO) == SC_AUTO) {
427             /* Auto variable */
428             SymData = ParseAutoDecl (&Decl, &SC);
429         } else if ((SC & SC_STATIC) == SC_STATIC) {
430             /* Static variable */
431             SymData = ParseStaticDecl (&Decl, &SC);
432         } else {
433             Internal ("Invalid storage class in ParseOneDecl: %04X", SC);
434         }
435     }
436
437     /* If the symbol is not marked as external, it will be defined now */
438     if ((SC & SC_EXTERN) == 0) {
439         SC |= SC_DEF;
440     }
441
442     /* Add the symbol to the symbol table */
443     AddLocalSym (Decl.Ident, Decl.Type, SC, SymData);
444 }
445
446
447
448 void DeclareLocals (void)
449 /* Declare local variables and types. */
450 {
451     /* Remember the current stack pointer */
452     int InitialStack = oursp;
453
454     /* Loop until we don't find any more variables */
455     while (1) {
456
457         /* Check variable declarations. We need to distinguish between a
458          * default int type and the end of variable declarations. So we
459          * will do the following: If there is no explicit storage class
460          * specifier *and* no explicit type given, *and* no type qualifiers
461          * have been read, it is assumed that we have reached the end of
462          * declarations.
463          */
464         DeclSpec Spec;
465         ParseDeclSpec (&Spec, SC_AUTO, T_INT);
466         if ((Spec.Flags & DS_DEF_STORAGE) != 0 &&       /* No storage spec */
467             (Spec.Flags & DS_DEF_TYPE) != 0    &&       /* No type given */
468             GetQualifier (Spec.Type) == T_QUAL_NONE) {  /* No type qualifier */
469             break;
470         }
471
472         /* Accept type only declarations */
473         if (CurTok.Tok == TOK_SEMI) {
474             /* Type declaration only */
475             CheckEmptyDecl (&Spec);
476             NextToken ();
477             continue;
478         }
479
480         /* Parse a comma separated variable list */
481         while (1) {
482
483             /* Parse one declaration */
484             ParseOneDecl (&Spec);
485
486             /* Check if there is more */
487             if (CurTok.Tok == TOK_COMMA) {
488                 /* More to come */
489                 NextToken ();
490             } else {
491                 /* Done */
492                 break;
493             }
494         }
495
496         /* A semicolon must follow */
497         ConsumeSemi ();
498     }
499
500     /* Be sure to allocate any reserved space for locals */
501     F_AllocLocalSpace (CurrentFunc);
502
503     /* In case we've allocated local variables in this block, emit a call to
504      * the stack checking routine if stack checks are enabled.
505      */
506     if (IS_Get (&CheckStack) && InitialStack != oursp) {
507         g_cstackcheck ();
508     }
509 }
510
511
512