]> git.sur5r.net Git - cc65/blob - src/cc65/locals.c
In a function call for all parameters not covered by a prototype, convert
[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 "symtab.h"
53 #include "typeconv.h"
54
55
56
57 /*****************************************************************************/
58 /*                                   Code                                    */
59 /*****************************************************************************/
60
61
62
63 static unsigned ParseRegisterDecl (Declaration* Decl, unsigned* SC, int Reg)
64 /* Parse the declaration of a register variable. The function returns the
65  * symbol data, which is the offset of the variable in the register bank.
66  */
67 {
68     unsigned InitLabel;
69
70     /* Determine if this is a compound variable */
71     int IsCompound = IsClassStruct (Decl->Type) || IsTypeArray (Decl->Type);
72
73     /* Get the size of the variable */
74     unsigned Size = SizeOf (Decl->Type);
75
76     /* Save the current contents of the register variable on stack */
77     F_AllocLocalSpace (CurrentFunc);
78     g_save_regvars (Reg, Size);
79
80     /* Check for an optional initialization */
81     if (CurTok.Tok == TOK_ASSIGN) {
82
83         ExprDesc Expr;
84
85         /* Skip the '=' */
86         NextToken ();
87
88         /* Special handling for compound types */
89         if (IsCompound) {
90
91             /* Switch to read only data */
92             g_userodata ();
93
94             /* Define a label for the initialization data */
95             InitLabel = GetLocalLabel ();
96             g_defdatalabel (InitLabel);
97
98             /* Parse the initialization generating a memory image of the
99              * data in the RODATA segment. The function does return the size
100              * of the initialization data, which may be greater than the
101              * actual size of the type, if the type is a structure with a
102              * flexible array member that has been initialized. Since we must
103              * know the size of the data in advance for register variables,
104              * we cannot allow that here.
105              */
106             if (ParseInit (Decl->Type) != Size) {
107                 Error ("Cannot initialize flexible array members of storage class `register'");
108             }
109
110             /* Generate code to copy this data into the variable space */
111             g_initregister (InitLabel, Reg, Size);
112
113         } else {
114
115             /* Parse the expression */
116             hie1 (&Expr);
117
118             /* Convert it to the target type */
119             TypeConversion (&Expr, Decl->Type);
120
121             /* Load the value into the primary */
122             LoadExpr (CF_NONE, &Expr);
123
124             /* Store the value into the variable */
125             g_putstatic (CF_REGVAR | TypeOf (Decl->Type), Reg, 0);
126
127         }
128
129         /* Mark the variable as referenced */
130         *SC |= SC_REF;
131     }
132
133     /* Cannot allocate a variable of zero size */
134     if (Size == 0) {
135         Error ("Variable `%s' has unknown size", Decl->Ident);
136     }
137
138     /* Return the symbol data */
139     return Reg;
140 }
141
142
143
144 static unsigned ParseAutoDecl (Declaration* Decl, unsigned* SC)
145 /* Parse the declaration of an auto variable. The function returns the symbol
146  * data, which is the offset for variables on the stack, and the label for
147  * static variables.
148  */
149 {
150     unsigned Flags;
151     unsigned SymData;
152     unsigned InitLabel;
153
154     /* Determine if this is a compound variable */
155     int IsCompound = IsClassStruct (Decl->Type) || IsTypeArray (Decl->Type);
156
157     /* Get the size of the variable */
158     unsigned Size = SizeOf (Decl->Type);
159
160     /* Check if this is a variable on the stack or in static memory */
161     if (IS_Get (&StaticLocals) == 0) {
162
163         /* Check for an optional initialization */
164         if (CurTok.Tok == TOK_ASSIGN) {
165
166             ExprDesc Expr;
167
168             /* Skip the '=' */
169             NextToken ();
170
171             /* Special handling for compound types */
172             if (IsCompound) {
173
174                 /* Switch to read only data */
175                 g_userodata ();
176
177                 /* Define a label for the initialization data */
178                 InitLabel = GetLocalLabel ();
179                 g_defdatalabel (InitLabel);
180
181                 /* Parse the initialization generating a memory image of the
182                  * data in the RODATA segment. The function will return the
183                  * actual size of the initialization data, which may be
184                  * greater than the size of the variable if it is a struct
185                  * that contains a flexible array member and we're not in
186                  * ANSI mode.
187                  */
188                 Size = ParseInit (Decl->Type);
189
190                 /* Now reserve space for the variable on the stack */
191                 SymData = F_ReserveLocalSpace (CurrentFunc, Size);
192
193                 /* Next, allocate the space on the stack. This means that the
194                  * variable is now located at offset 0 from the current sp.
195                  */
196                 F_AllocLocalSpace (CurrentFunc);
197
198                 /* Generate code to copy the initialization data into the
199                  * variable space
200                  */
201                 g_initauto (InitLabel, Size);
202
203             } else {
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                 hie1 (&Expr);
213
214                 /* Convert it to the target type */
215                 TypeConversion (&Expr, 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 (ED_IsConstAbsInt (&Expr)) {
221                     Flags |= CF_CONST;
222                 } else {
223                     LoadExpr (CF_NONE, &Expr);
224                     ED_MakeRVal (&Expr);
225                 }
226
227                 /* Push the value */
228                 g_push (Flags | TypeOf (Decl->Type), Expr.IVal);
229
230             }
231
232             /* Mark the variable as referenced */
233             *SC |= SC_REF;
234
235             /* Variable is located at the current SP */
236             SymData = StackPtr;
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 Expr;
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                 hie1 (&Expr);
289
290                 /* Convert it to the target type */
291                 TypeConversion (&Expr, Decl->Type);
292
293                 /* Load the value into the primary */
294                 LoadExpr (CF_NONE, &Expr);
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 = StackPtr;
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 != StackPtr) {
507         g_cstackcheck ();
508     }
509 }
510
511
512