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 | } |
564 | |