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