]> git.sur5r.net Git - cc65/blob - src/cc65/locals.c
63b6b86117b44ba0fdde343fc84cc27cf57018c9
[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     Ullrich von Bassewitz                                        */
10 /*              Wacholderweg 14                                              */
11 /*              D-70597 Stuttgart                                            */
12 /* EMail:       uz@musoftware.de                                             */
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 #include "anonname.h"
37 #include "asmlabel.h"
38 #include "codegen.h"
39 #include "declare.h"
40 #include "expr.h"
41 #include "function.h"   /* ## */
42 #include "global.h"
43 #include "mem.h"
44 #include "symtab.h"
45 #include "locals.h"
46
47
48
49 /*****************************************************************************/
50 /*                                   Data                                    */
51 /*****************************************************************************/
52
53
54
55 /* Register variable management */
56 unsigned MaxRegSpace            = 6;    /* Maximum space available */
57 static int RegOffs              = 0;    /* Offset into register space */
58 static const SymEntry** RegSyms = 0;    /* The register variables */
59 static unsigned RegSymCount     = 0;    /* Number of register variables */
60
61
62
63 /*****************************************************************************/
64 /*                                   Code                                    */
65 /*****************************************************************************/
66
67
68
69 void InitRegVars (void)
70 /* Initialize register variable control data */
71 {
72     /* If the register space is zero, bail out */
73     if (MaxRegSpace == 0) {
74         return;
75     }
76
77     /* The maximum number of register variables is equal to the register
78      * variable space available. So allocate one pointer per byte. This
79      * will usually waste some space but we don't need to dynamically
80      * grow the array.
81      */
82     RegSyms = xmalloc (MaxRegSpace * sizeof (RegSyms[0]));
83     RegOffs = MaxRegSpace;
84 }
85
86
87
88 void DoneRegVars (void)
89 /* Free the register variables */
90 {
91     xfree (RegSyms);
92     RegSyms = 0;
93     RegOffs = MaxRegSpace;
94     RegSymCount = 0;
95 }
96
97
98
99 static int AllocRegVar (const SymEntry* Sym, const type* tarray)
100 /* Allocate a register variable with the given amount of storage. If the
101  * allocation was successful, return the offset of the register variable in
102  * the register bank (zero page storage). If there is no register space left,
103  * return -1.
104  */
105 {
106     /* Maybe register variables are disabled... */
107     if (EnableRegVars) {
108
109         /* Get the size of the variable */
110         unsigned Size = SizeOf (tarray);
111
112         /* Do we have space left? */
113         if (RegOffs >= Size) {
114
115             /* Space left. We allocate the variables from high to low addresses,
116              * so the adressing is compatible with the saved values on stack.
117              * This allows shorter code when saving/restoring the variables.
118              */
119             RegOffs -= Size;
120             RegSyms [RegSymCount++] = Sym;
121             return RegOffs;
122         }
123     }
124
125     /* No space left or no allocation */
126     return -1;
127 }
128
129
130
131 void DeclareLocals (void)
132 /* Declare local variables and types. */
133 {
134     int offs = oursp;           /* Current stack offset for variable */
135     int AutoSpace = 0;          /* Unallocated space on the stack */
136     int Size;                   /* Size of an auto variable */
137     int Reg;                    /* Register variable offset */
138     unsigned flags = 0;         /* Code generator flags */
139     int SymbolSC;               /* Storage class for symbol */
140     int ldata = 0;              /* Local symbol data temp storage */
141
142     /* Loop until we don't find any more variables */
143     while (1) {
144
145         /* Check variable declarations. We need to distinguish between a
146          * default int type and the end of variable declarations. So we
147          * will do the following: If there is no explicit storage class
148          * specifier *and* no explicit type given, it is assume that we
149          * have reached the end of declarations.
150          */
151         DeclSpec Spec;
152         ParseDeclSpec (&Spec, SC_AUTO, T_INT);
153         if ((Spec.Flags & DS_DEF_STORAGE) != 0 && (Spec.Flags & DS_DEF_TYPE) != 0) {
154             break;
155         }
156
157         /* Accept type only declarations */
158         if (curtok == SEMI) {
159             /* Type declaration only ### Check struct/union here */
160             gettok ();
161             continue;
162         }
163
164         /* Parse a comma separated variable list */
165         while (1) {
166
167             Declaration Decl;
168
169             /* Remember the storage class for the new symbol */
170             SymbolSC = Spec.StorageClass;
171
172             /* Read the declaration */
173             ParseDecl (&Spec, &Decl, DM_NEED_IDENT);
174
175             /* If we don't have a name, this was flagged as an error earlier.
176              * To avoid problems later, use an anonymous name here.
177              */
178             if (Decl.Ident[0] == '\0') {
179                 AnonName (Decl.Ident, "param");
180             }
181
182             if (!IsFunc (Decl.Type) && (SymbolSC & SC_TYPEDEF) != SC_TYPEDEF) {
183
184                 /* Get the size of the variable */
185                 Size = SizeOf (Decl.Type);
186
187 #if 0
188                 /* Check the storage class */
189                 if ((SymbolSC & SC_REGISTER) && (Reg = AllocRegVar (psym, tarray)) >= 0) {
190
191                     /* We will store the current value of the register onto the
192                      * stack, thus making functions with register variables
193                      * reentrant. If we have pending auto variables, emit them
194                      * now.
195                      */
196                     g_usecode ();
197                     g_space (AutoSpace);
198                     oursp -= AutoSpace;
199                     AutoSpace = 0;
200
201                     /* Remember the register bank offset */
202                     ldata = Reg;
203
204                     /* Save the current register value onto the stack */
205                     g_save_regvars (Reg, Size);
206
207                     /* Allow variable initialization */
208                     if (curtok == ASGN) {
209
210                         struct expent lval;
211
212                         /* Skip the '=' */
213                         gettok ();
214
215                         /* Get the expression into the primary */
216                         expression1 (&lval);
217
218                         /* Make type adjustments if needed */
219                         assignadjust (tarray, &lval);
220
221                         /* Setup the type flags for the assignment */
222                         flags = TypeOf (tarray) | CF_REGVAR;
223                         if (Size == 1) {
224                             flags |= CF_FORCECHAR;
225                         }
226
227                         /* Store the value into the register */
228                         g_putstatic (flags, Reg, 0);
229
230                         /* Mark the variable as referenced */
231                         SymbolSC |= SC_REF;
232
233                     }
234
235                     /* Account for the stack space needed and remember the
236                      * stack offset of the save area.
237                      */
238                     offs -= Size;
239                     psym->h_lattr = offs;
240
241                 } else if (SymbolSC & (SC_AUTO | SC_REGISTER)) {
242 #endif
243                 if (SymbolSC & (SC_AUTO | SC_REGISTER)) {
244
245                     /* Auto variable */
246                     if (LocalsAreStatic == 0) {
247
248                         /* Change SC in case it was register */
249                         SymbolSC = (SymbolSC & ~SC_REGISTER) | SC_AUTO;
250                         if (curtok == ASGN) {
251
252                             struct expent lval;
253
254                             /* Switch to the code segment, allocate space for
255                              * uninitialized variables.
256                              */
257                             g_usecode ();
258                             g_space (AutoSpace);
259                             oursp -= AutoSpace;
260                             AutoSpace = 0;
261
262                             /* Skip the '=' */
263                             gettok ();
264
265                             /* Setup the type flags for the assignment */
266                             flags = Size == 1? CF_FORCECHAR : CF_NONE;
267
268                             /* Get the expression into the primary */
269                             if (evalexpr (flags, hie1, &lval) == 0) {
270                                 /* Constant expression. Adjust the types */
271                                 assignadjust (Decl.Type, &lval);
272                                 flags |= CF_CONST;
273                             } else {
274                                 /* Expression is not constant and in the primary */
275                                 assignadjust (Decl.Type, &lval);
276                             }
277
278                             /* Push the value */
279                             g_push (flags | TypeOf (Decl.Type), lval.e_const);
280
281                             /* Mark the variable as referenced */
282                             SymbolSC |= SC_REF;
283
284                         } else {
285                             /* Non-initialized local variable. Just keep track of
286                              * the space needed.
287                              */
288                             AutoSpace += Size;
289                         }
290
291                         /* Allocate space on the stack, assign the offset */
292                         offs -= Size;
293                         ldata = offs;
294
295                     } else {
296
297                         /* Static local variables. */
298                         SymbolSC = (SymbolSC & ~(SC_REGISTER | SC_AUTO)) | SC_STATIC;
299
300                         /* Put them into the BSS */
301                         g_usebss ();
302
303                         /* Define the variable label */
304                         g_defloclabel (ldata = GetLabel ());
305
306                         /* Reserve space for the data */
307                         g_res (Size);
308
309                         /* Allow assignments */
310                         if (curtok == ASGN) {
311
312                             struct expent lval;
313
314                             /* Switch to the code segment. */
315                             g_usecode ();
316
317                             /* Skip the '=' */
318                             gettok ();
319
320                             /* Get the expression into the primary */
321                             expression1 (&lval);
322
323                             /* Make type adjustments if needed */
324                             assignadjust (Decl.Type, &lval);
325
326                             /* Setup the type flags for the assignment */
327                             flags = TypeOf (Decl.Type);
328                             if (Size == 1) {
329                                 flags |= CF_FORCECHAR;
330                             }
331
332                             /* Store the value into the variable */
333                             g_putstatic (flags, ldata, 0);
334
335                             /* Mark the variable as referenced */
336                             SymbolSC |= SC_REF;
337                         }
338                     }
339
340                 } else if ((SymbolSC & SC_STATIC) == SC_STATIC) {
341
342                     /* Static data */
343                     if (curtok == ASGN) {
344
345                         /* Initialization ahead, switch to data segment */
346                         g_usedata ();
347
348                         /* Define the variable label */
349                         g_defloclabel (ldata = GetLabel ());
350
351                         /* Skip the '=' */
352                         gettok ();
353
354                         /* Allow initialization of static vars */
355                         ParseInit (Decl.Type);
356
357                         /* Mark the variable as referenced */
358                         SymbolSC |= SC_REF;
359
360                     } else {
361
362                         /* Uninitialized data, use BSS segment */
363                         g_usebss ();
364
365                         /* Define the variable label */
366                         g_defloclabel (ldata = GetLabel ());
367
368                         /* Reserve space for the data */
369                         g_res (Size);
370
371                     }
372                 }
373
374             }
375
376             /* If the symbol is not marked as external, it will be defined */
377             if ((SymbolSC & SC_EXTERN) == 0) {
378                 SymbolSC |= SC_DEF;
379             }
380
381             /* Add the symbol to the symbol table */
382             AddLocalSym (Decl.Ident, Decl.Type, SymbolSC, ldata);
383
384             if (curtok != COMMA) {
385                 break;
386             }
387             gettok ();
388         }
389         if (curtok == SEMI) {
390             gettok ();
391         }
392     }
393
394     /* In case we switched away from code segment, switch back now */
395     g_usecode ();
396
397     /* Create space for locals */
398     g_space (AutoSpace);
399     oursp -= AutoSpace;
400 }
401
402
403
404 void RestoreRegVars (int HaveResult)
405 /* Restore the register variables for the local function if there are any.
406  * The parameter tells us if there is a return value in ax, in that case,
407  * the accumulator must be saved across the restore.
408  */
409 {
410     int I, J, Bytes, Offs;
411
412     /* If we don't have register variables in this function, bail out early */
413     if (RegSymCount == 0) {
414         return;
415     }
416
417     /* Save the accumulator if needed */
418     if (!HasVoidReturn (CurrentFunc) && HaveResult) {
419         g_save (CF_CHAR | CF_FORCECHAR);
420     }
421
422     /* Walk through all variables. If there are several variables in a row
423      * (that is, with increasing stack offset), restore them in one chunk.
424      */
425     I = 0;
426     while (I < RegSymCount) {
427
428         /* Check for more than one variable */
429         const SymEntry* Sym = RegSyms[I];
430         Offs  = Sym->V.Offs;
431         Bytes = SizeOf (Sym->Type);
432         J = I+1;
433
434         while (J < RegSymCount) {
435
436             /* Get the next symbol */
437             const SymEntry* NextSym = RegSyms [J];
438
439             /* Get the size */
440             int Size = SizeOf (NextSym->Type);
441
442             /* Adjacent variable? */
443             if (NextSym->V.Offs + Size != Offs) {
444                 /* No */
445                 break;
446             }
447
448             /* Adjacent variable */
449             Bytes += Size;
450             Offs  -= Size;
451             Sym   = NextSym;
452             ++J;
453         }
454
455         /* Restore the memory range */
456         g_restore_regvars (Offs, Sym->V.Offs, Bytes);
457
458         /* Next round */
459         I = J;
460     }
461
462     /* Restore the accumulator if needed */
463     if (!HasVoidReturn (CurrentFunc) && HaveResult) {
464         g_restore (CF_CHAR | CF_FORCECHAR);
465     }
466 }
467
468
469