]> git.sur5r.net Git - cc65/blob - src/cc65/locals.c
Fixed error "variable has unknown size" for a local array where the size
[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-2002 Ullrich von Bassewitz                                       */
10 /*               Wacholderweg 14                                             */
11 /*               D-70597 Stuttgart                                           */
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 "symtab.h"
50 #include "locals.h"
51
52
53
54 /*****************************************************************************/
55 /*                                   Data                                    */
56 /*****************************************************************************/
57
58
59
60 /* Register variable management */
61 unsigned MaxRegSpace            = 6;    /* Maximum space available */
62 static unsigned RegOffs         = 0;    /* Offset into register space */
63 static const SymEntry** RegSyms = 0;    /* The register variables */
64 static unsigned RegSymCount     = 0;    /* Number of register variables */
65
66
67
68 /*****************************************************************************/
69 /*                                   Code                                    */
70 /*****************************************************************************/
71
72
73
74 void InitRegVars (void)
75 /* Initialize register variable control data */
76 {
77     /* If the register space is zero, bail out */
78     if (MaxRegSpace == 0) {
79         return;
80     }
81
82     /* The maximum number of register variables is equal to the register
83      * variable space available. So allocate one pointer per byte. This
84      * will usually waste some space but we don't need to dynamically
85      * grow the array.
86      */
87     RegSyms = (const SymEntry**) xmalloc (MaxRegSpace * sizeof (RegSyms[0]));
88     RegOffs = MaxRegSpace;
89 }
90
91
92
93 void DoneRegVars (void)
94 /* Free the register variables */
95 {
96     xfree (RegSyms);
97     RegSyms = 0;
98     RegOffs = MaxRegSpace;
99     RegSymCount = 0;
100 }
101
102
103
104 static int AllocRegVar (const SymEntry* Sym, const type* tarray)
105 /* Allocate a register variable with the given amount of storage. If the
106  * allocation was successful, return the offset of the register variable in
107  * the register bank (zero page storage). If there is no register space left,
108  * return -1.
109  */
110 {
111     /* Maybe register variables are disabled... */
112     if (EnableRegVars) {
113
114         /* Get the size of the variable */
115         unsigned Size = CheckedSizeOf (tarray);
116
117         /* Do we have space left? */
118         if (RegOffs >= Size) {
119
120             /* Space left. We allocate the variables from high to low addresses,
121              * so the adressing is compatible with the saved values on stack.
122              * This allows shorter code when saving/restoring the variables.
123              */
124             RegOffs -= Size;
125             RegSyms [RegSymCount++] = Sym;
126             return RegOffs;
127         }
128     }
129
130     /* No space left or no allocation */
131     return -1;
132 }
133
134
135
136 static unsigned ParseAutoDecl (Declaration* Decl, unsigned* SC)
137 /* Parse the declaration of an auto variable. The function returns the symbol
138  * data, which is the offset for variables on the stack, and the label for
139  * static variables.
140  */
141 {
142     unsigned Flags;
143     unsigned SymData;
144     unsigned InitLabel;
145
146     /* Determine if this is a compound variable */
147     int IsCompound = IsClassStruct (Decl->Type) || IsTypeArray (Decl->Type);
148
149     /* Get the size of the variable */
150     unsigned Size = SizeOf (Decl->Type);
151
152     /* Check if this is a variable on the stack or in static memory */
153     if (StaticLocals == 0) {
154
155         /* Change SC in case it was register */
156         *SC = (*SC & ~SC_REGISTER) | SC_AUTO;
157         if (CurTok.Tok == TOK_ASSIGN) {
158
159             ExprDesc lval;
160
161             /* Skip the '=' */
162             NextToken ();
163
164             /* Special handling for compound types */
165             if (IsCompound) {
166
167                 /* First reserve space for the variable */
168                 SymData = F_ReserveLocalSpace (CurrentFunc, Size);
169
170                 /* Next, allocate the space on the stack. This means that the
171                  * variable is now located at offset 0 from the current sp.
172                  */
173                 F_AllocLocalSpace (CurrentFunc);
174
175                 /* Switch to read only data */
176                 g_userodata ();
177
178                 /* Define a label for the initialization data */
179                 InitLabel = GetLocalLabel ();
180                 g_defdatalabel (InitLabel);
181
182                 /* Parse the initialization generating a memory image of the
183                  * data in the RODATA segment.
184                  */
185                 ParseInit (Decl->Type);
186
187                 /* Generate code to copy this data into the variable space */
188                 g_initauto (InitLabel, Size);
189
190             } else {
191
192                 /* Allocate previously reserved local space */
193                 F_AllocLocalSpace (CurrentFunc);
194
195                 /* Setup the type flags for the assignment */
196                 Flags = (Size == 1)? CF_FORCECHAR : CF_NONE;
197
198                 /* Get the expression into the primary */
199                 if (evalexpr (Flags, hie1, &lval) == 0) {
200                     /* Constant expression. Adjust the types */
201                     assignadjust (Decl->Type, &lval);
202                     Flags |= CF_CONST;
203                 } else {
204                     /* Expression is not constant and in the primary */
205                     assignadjust (Decl->Type, &lval);
206                 }
207
208                 /* Push the value */
209                 g_push (Flags | TypeOf (Decl->Type), lval.ConstVal);
210
211             }
212
213             /* Mark the variable as referenced */
214             *SC |= SC_REF;
215
216             /* Variable is located at the current SP */
217             SymData = oursp;
218
219         } else {
220             /* Non-initialized local variable. Just keep track of
221              * the space needed.
222              */
223             SymData = F_ReserveLocalSpace (CurrentFunc, Size);
224         }
225
226     } else {
227
228         /* Static local variables. */
229         *SC = (*SC & ~(SC_REGISTER | SC_AUTO)) | SC_STATIC;
230
231         /* Put them into the BSS */
232         g_usebss ();
233
234         /* Define the variable label */
235         SymData = GetLocalLabel ();
236         g_defdatalabel (SymData);
237
238         /* Reserve space for the data */
239         g_res (Size);
240
241         /* Allow assignments */
242         if (CurTok.Tok == TOK_ASSIGN) {
243
244             ExprDesc lval;
245
246             /* Skip the '=' */
247             NextToken ();
248
249             if (IsCompound) {
250
251                 /* Switch to read only data */
252                 g_userodata ();
253
254                 /* Define a label for the initialization data */
255                 InitLabel = GetLocalLabel ();
256                 g_defdatalabel (InitLabel);
257
258                 /* Parse the initialization generating a memory image of the
259                  * data in the RODATA segment.
260                  */
261                 ParseInit (Decl->Type);
262
263                 /* Generate code to copy this data into the variable space */
264                 g_initstatic (InitLabel, SymData, Size);
265
266             } else {
267
268                 /* Setup the type flags for the assignment */
269                 Flags = (Size == 1)? CF_FORCECHAR : CF_NONE;
270
271                 /* Get the expression into the primary */
272                 if (evalexpr (Flags, hie1, &lval) == 0) {
273                     /* Constant expression. Adjust the types */
274                     assignadjust (Decl->Type, &lval);
275                     Flags |= CF_CONST;
276                     /* Load it into the primary */
277                     exprhs (Flags, 0, &lval);
278                 } else {
279                     /* Expression is not constant and in the primary */
280                     assignadjust (Decl->Type, &lval);
281                 }
282
283                 /* Store the value into the variable */
284                 g_putstatic (Flags | TypeOf (Decl->Type), SymData, 0);
285
286             }
287
288             /* Mark the variable as referenced */
289             *SC |= SC_REF;
290         }
291     }
292
293     /* Cannot allocate a variable of zero size */
294     if (Size == 0) {
295         Error ("Variable `%s' has unknown size", Decl->Ident);
296     }
297
298     /* Return the symbol data */
299     return SymData;
300 }
301
302
303
304 static unsigned ParseStaticDecl (Declaration* Decl, unsigned* SC)
305 /* Parse the declaration of a static variable. The function returns the symbol
306  * data, which is the asm label of the variable.
307  */
308 {
309     unsigned SymData;
310
311     /* Get the size of the variable */
312     unsigned Size = SizeOf (Decl->Type);
313
314     /* Static data */
315     if (CurTok.Tok == TOK_ASSIGN) {
316
317         /* Initialization ahead, switch to data segment */
318         if (IsQualConst (Decl->Type)) {
319             g_userodata ();
320         } else {
321             g_usedata ();
322         }
323
324         /* Define the variable label */
325         SymData = GetLocalLabel ();
326         g_defdatalabel (SymData);
327
328         /* Skip the '=' */
329         NextToken ();
330
331         /* Allow initialization of static vars */
332         ParseInit (Decl->Type);
333
334         /* If the previous size has been unknown, it must be known now */
335         if (Size == 0) {
336             Size = SizeOf (Decl->Type);
337         }
338
339         /* Mark the variable as referenced */
340         *SC |= SC_REF;
341
342     } else {
343
344         /* Uninitialized data, use BSS segment */
345         g_usebss ();
346
347         /* Define the variable label */
348         SymData = GetLocalLabel ();
349         g_defdatalabel (SymData);
350
351         /* Reserve space for the data */
352         g_res (Size);
353
354     }
355
356     /* Cannot allocate a variable of zero size */
357     if (Size == 0) {
358         Error ("Variable `%s' has unknown size", Decl->Ident);
359     }
360
361     /* Return the symbol data */
362     return SymData;
363 }
364
365
366
367 static void ParseOneDecl (const DeclSpec* Spec)
368 /* Parse one variable declaration */
369 {
370     unsigned    SC;             /* Storage class for symbol */
371     unsigned    SymData = 0;    /* Symbol data (offset, label name, ...) */
372     Declaration Decl;           /* Declaration data structure */
373
374     /* Remember the storage class for the new symbol */
375     SC = Spec->StorageClass;
376
377     /* Read the declaration */
378     ParseDecl (Spec, &Decl, DM_NEED_IDENT);
379
380     /* Set the correct storage class for functions */
381     if (IsTypeFunc (Decl.Type)) {
382         /* Function prototypes are always external */
383         if ((SC & SC_EXTERN) == 0) {
384             Warning ("Function must be extern");
385         }
386         SC |= SC_FUNC | SC_EXTERN;
387
388     }
389
390     /* If we don't have a name, this was flagged as an error earlier.
391      * To avoid problems later, use an anonymous name here.
392      */
393     if (Decl.Ident[0] == '\0') {
394         AnonName (Decl.Ident, "param");
395     }
396
397     /* Handle anything that needs storage (no functions, no typdefs) */
398     if ((SC & SC_FUNC) != SC_FUNC && (SC & SC_TYPEDEF) != SC_TYPEDEF) {
399
400         /* */
401         if (SC & (SC_AUTO | SC_REGISTER)) {
402
403             /* Auto variable */
404             SymData = ParseAutoDecl (&Decl, &SC);
405
406         } else if ((SC & SC_STATIC) == SC_STATIC) {
407
408             /* Static variable */
409             SymData = ParseStaticDecl (&Decl, &SC);
410
411         }
412     }
413
414     /* If the symbol is not marked as external, it will be defined */
415     if ((SC & SC_EXTERN) == 0) {
416         SC |= SC_DEF;
417     }
418
419     /* Add the symbol to the symbol table */
420     AddLocalSym (Decl.Ident, Decl.Type, SC, SymData);
421 }
422
423
424
425 void DeclareLocals (void)
426 /* Declare local variables and types. */
427 {
428     /* Remember the current stack pointer */
429     int InitialStack = oursp;
430
431     /* Loop until we don't find any more variables */
432     while (1) {
433
434         /* Check variable declarations. We need to distinguish between a
435          * default int type and the end of variable declarations. So we
436          * will do the following: If there is no explicit storage class
437          * specifier *and* no explicit type given, it is assume that we
438          * have reached the end of declarations.
439          */
440         DeclSpec Spec;
441         ParseDeclSpec (&Spec, SC_AUTO, T_INT);
442         if ((Spec.Flags & DS_DEF_STORAGE) != 0 && (Spec.Flags & DS_DEF_TYPE) != 0) {
443             break;
444         }
445
446         /* Accept type only declarations */
447         if (CurTok.Tok == TOK_SEMI) {
448             /* Type declaration only */
449             CheckEmptyDecl (&Spec);
450             NextToken ();
451             continue;
452         }
453
454         /* Parse a comma separated variable list */
455         while (1) {
456
457             /* Parse one declaration */
458             ParseOneDecl (&Spec);
459
460             /* Check if there is more */
461             if (CurTok.Tok == TOK_COMMA) {
462                 /* More to come */
463                 NextToken ();
464             } else {
465                 /* Done */
466                 break;
467             }
468         }
469
470         /* A semicolon must follow */
471         ConsumeSemi ();
472     }
473
474     /* Be sure to allocate any reserved space for locals */
475     F_AllocLocalSpace (CurrentFunc);
476
477     /* In case we've allocated local variables in this block, emit a call to
478      * the stack checking routine if stack checks are enabled.
479      */
480     if (CheckStack && InitialStack != oursp) {
481         g_cstackcheck ();
482     }
483 }
484
485
486
487 void RestoreRegVars (int HaveResult)
488 /* Restore the register variables for the local function if there are any.
489  * The parameter tells us if there is a return value in ax, in that case,
490  * the accumulator must be saved across the restore.
491  */
492 {
493     unsigned I, J;
494     int Bytes, Offs;
495
496     /* If we don't have register variables in this function, bail out early */
497     if (RegSymCount == 0) {
498         return;
499     }
500
501     /* Save the accumulator if needed */
502     if (!F_HasVoidReturn (CurrentFunc) && HaveResult) {
503         g_save (CF_CHAR | CF_FORCECHAR);
504     }
505
506     /* Walk through all variables. If there are several variables in a row
507      * (that is, with increasing stack offset), restore them in one chunk.
508      */
509     I = 0;
510     while (I < RegSymCount) {
511
512         /* Check for more than one variable */
513         const SymEntry* Sym = RegSyms[I];
514         Offs  = Sym->V.Offs;
515         Bytes = CheckedSizeOf (Sym->Type);
516         J = I+1;
517
518         while (J < RegSymCount) {
519
520             /* Get the next symbol */
521             const SymEntry* NextSym = RegSyms [J];
522
523             /* Get the size */
524             int Size = CheckedSizeOf (NextSym->Type);
525
526             /* Adjacent variable? */
527             if (NextSym->V.Offs + Size != Offs) {
528                 /* No */
529                 break;
530             }
531
532             /* Adjacent variable */
533             Bytes += Size;
534             Offs  -= Size;
535             Sym   = NextSym;
536             ++J;
537         }
538
539         /* Restore the memory range */
540         g_restore_regvars (Offs, Sym->V.Offs, Bytes);
541
542         /* Next round */
543         I = J;
544     }
545
546     /* Restore the accumulator if needed */
547     if (!F_HasVoidReturn (CurrentFunc) && HaveResult) {
548         g_restore (CF_CHAR | CF_FORCECHAR);
549     }
550 }
551
552
553