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