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
65static 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
83static 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
98static 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
180static 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
365static 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
419static 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
494void 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