]> git.sur5r.net Git - cc65/blob - src/cc65/stmt.c
Handle intermediate access to local variables in the stack op optimizations.
[cc65] / src / cc65 / stmt.c
1 /*
2  * stmt.c
3  *
4  * Ullrich von Bassewitz, 06.08.1998
5  *
6  * Original by John R. Dunning - see copyleft.jrd
7  */
8
9
10
11 #include <stdio.h>
12 #include <string.h>
13
14 /* common */
15 #include "coll.h"
16 #include "xmalloc.h"
17
18 /* cc65 */
19 #include "asmcode.h"
20 #include "asmlabel.h"
21 #include "codegen.h"
22 #include "datatype.h"
23 #include "error.h"
24 #include "expr.h"
25 #include "function.h"
26 #include "global.h"
27 #include "goto.h"
28 #include "litpool.h"
29 #include "locals.h"
30 #include "loop.h"
31 #include "pragma.h"
32 #include "scanner.h"
33 #include "symtab.h"
34 #include "stmt.h"
35
36
37
38 /*****************************************************************************/
39 /*                                   Data                                    */
40 /*****************************************************************************/
41
42
43
44 /* Maximum count of cases */
45 #define CASE_MAX        257
46
47
48
49 /*****************************************************************************/
50 /*                             Helper functions                              */
51 /*****************************************************************************/
52
53
54
55 static void CheckTok (token_t Tok, const char* Msg, int* PendingToken)
56 /* Helper function for Statement. Will check for Tok and print Msg if not
57  * found. If PendingToken is NULL, it will the skip the token, otherwise
58  * it will store one to PendingToken.
59  */
60 {
61     if (CurTok.Tok != Tok) {
62         Error (Msg);
63     } else if (PendingToken) {
64         *PendingToken = 1;
65     } else {
66         NextToken ();
67     }
68 }
69
70
71
72 static void CheckSemi (int* PendingToken)
73 /* Helper function for Statement. Will call CheckTok with the parameters
74  * for a semicolon.
75  */
76 {
77     CheckTok (TOK_SEMI, "`;' expected", PendingToken);
78 }
79
80
81
82 static void SkipPending (int PendingToken)
83 /* Skip the pending token if we have one */
84 {
85     if (PendingToken) {
86         NextToken ();
87     }
88 }
89
90
91
92 /*****************************************************************************/
93 /*                                   Code                                    */
94 /*****************************************************************************/
95
96
97
98 static int IfStatement (void)
99 /* Handle an 'if' statement */
100 {
101     unsigned Label1;
102     int GotBreak;
103
104     /* Skip the if */
105     NextToken ();
106
107     /* Generate a jump label and parse the condition */
108     Label1 = GetLocalLabel ();
109     test (Label1, 0);
110
111     /* Parse the if body */
112     GotBreak = Statement (0);
113
114     /* Else clause present? */
115     if (CurTok.Tok != TOK_ELSE) {
116
117         g_defcodelabel (Label1);
118
119         /* Since there's no else clause, we're not sure, if the a break
120          * statement is really executed.
121          */
122         return 0;
123
124     } else {
125
126         /* Generate a jump around the else branch */
127         unsigned Label2 = GetLocalLabel ();
128         g_jump (Label2);
129
130         /* Skip the else */
131         NextToken ();
132
133         /* Define the target for the first test */
134         g_defcodelabel (Label1);
135
136         /* Total break only if both branches had a break. */
137         GotBreak &= Statement (0);
138
139         /* Generate the label for the else clause */
140         g_defcodelabel (Label2);
141
142         /* Done */
143         return GotBreak;
144     }
145 }
146
147
148
149 static void DoStatement (void)
150 /* Handle the 'do' statement */
151 {
152     /* Get the loop control labels */
153     unsigned loop = GetLocalLabel ();
154     unsigned lab = GetLocalLabel ();
155
156     /* Skip the while token */
157     NextToken ();
158
159     /* Add the loop to the loop stack */
160     AddLoop (oursp, loop, lab, 0, 0);
161
162     /* Define the head label */
163     g_defcodelabel (loop);
164
165     /* Parse the loop body */
166     Statement (0);
167
168     /* Parse the end condition */
169     Consume (TOK_WHILE, "`while' expected");
170     test (loop, 1);
171     ConsumeSemi ();
172
173     /* Define the break label */
174     g_defcodelabel (lab);
175
176     /* Remove the loop from the loop stack */
177     DelLoop ();
178 }
179
180
181
182 static void WhileStatement (void)
183 /* Handle the 'while' statement */
184 {
185     int PendingToken;
186
187     /* Get the loop control labels */
188     unsigned loop = GetLocalLabel ();
189     unsigned lab = GetLocalLabel ();
190
191     /* Skip the while token */
192     NextToken ();
193
194     /* Add the loop to the loop stack */
195     AddLoop (oursp, loop, lab, 0, 0);
196
197     /* Define the head label */
198     g_defcodelabel (loop);
199
200     /* Test the loop condition */
201     test (lab, 0);
202
203     /* Loop body */
204     Statement (&PendingToken);
205
206     /* Jump back to loop top */
207     g_jump (loop);
208
209     /* Exit label */
210     g_defcodelabel (lab);
211
212     /* Eat remaining tokens that were delay because of line info correctness */
213     SkipPending (PendingToken);
214
215     /* Remove the loop from the loop stack */
216     DelLoop ();
217 }
218
219
220
221 static void ReturnStatement (void)
222 /* Handle the 'return' statement */
223 {
224     ExprDesc lval;
225
226     NextToken ();
227     if (CurTok.Tok != TOK_SEMI) {
228         if (HasVoidReturn (CurrentFunc)) {
229             Error ("Returning a value in function with return type void");
230         }
231
232         /* Evaluate the return expression. Result will be in primary */
233         expression (&lval);
234
235         /* Convert the return value to the type of the function result */
236         if (!HasVoidReturn (CurrentFunc)) {
237             assignadjust (GetReturnType (CurrentFunc), &lval);
238         }
239     } else if (!HasVoidReturn (CurrentFunc)) {
240         Error ("Function `%s' must return a value", GetFuncName (CurrentFunc));
241     }
242
243     /* Cleanup the stack in case we're inside a block with locals */
244     g_space (oursp - GetTopLevelSP (CurrentFunc));
245
246     /* Output a jump to the function exit code */
247     g_jump (GetRetLab (CurrentFunc));
248 }
249
250
251
252 static void BreakStatement (void)
253 /* Handle the 'break' statement */
254 {
255     LoopDesc* L;
256
257     /* Skip the break */
258     NextToken ();
259
260     /* Get the current loop descriptor */
261     L = CurrentLoop ();
262
263     /* Check if we are inside a loop */
264     if (L == 0) {
265         /* Error: No current loop */
266         Error ("`break' statement not within loop or switch");
267         return;
268     }
269
270     /* Correct the stack pointer if needed */
271     g_space (oursp - L->StackPtr);
272
273     /* Jump to the exit label of the loop */
274     g_jump (L->Label);
275 }
276
277
278
279 static void ContinueStatement (void)
280 /* Handle the 'continue' statement */
281 {
282     LoopDesc* L;
283
284     /* Skip the continue */
285     NextToken ();
286
287     /* Get the current loop descriptor */
288     L = CurrentLoop ();
289     if (L) {
290         /* Search for the correct loop */
291         do {
292             if (L->Loop) {
293                 break;
294             }
295             L = L->Next;
296         } while (L);
297     }
298
299     /* Did we find it? */
300     if (L == 0) {
301         Error ("`continue' statement not within a loop");
302         return;
303     }
304
305     /* Correct the stackpointer if needed */
306     g_space (oursp - L->StackPtr);
307
308     /* Output the loop code */
309     if (L->linc) {
310         g_jump (L->linc);
311     } else {
312         g_jump (L->Loop);
313     }
314 }
315
316
317
318 static void CascadeSwitch (ExprDesc* Expr)
319 /* Handle a switch statement for chars with a cmp cascade for the selector */
320 {
321     unsigned ExitLab;           /* Exit label */
322     unsigned NextLab;           /* Next case label */
323     unsigned CodeLab;           /* Label that starts the actual selector code */
324     int HaveBreak;              /* Remember if we exited with break */
325     int HaveDefault;            /* Remember if we had a default label */
326     int lcount;                 /* Label count */
327     unsigned Flags;             /* Code generator flags */
328     ExprDesc lval;              /* Case label expression */
329     long Val;                   /* Case label value */
330
331
332     /* Get the unqualified type of the switch expression */
333     type ExprType = UnqualifiedType (Expr->Type[0]);
334
335     /* Create a loop so we may break out, init labels */
336     ExitLab = GetLocalLabel ();
337     AddLoop (oursp, 0, ExitLab, 0, 0);
338
339     /* Setup some variables needed in the loop  below */
340     Flags = TypeOf (Expr->Type) | CF_CONST | CF_FORCECHAR;
341     CodeLab = NextLab = 0;
342     HaveBreak = 1;
343     HaveDefault = 0;
344
345     /* Parse the labels */
346     lcount = 0;
347     while (CurTok.Tok != TOK_RCURLY) {
348
349         if (CurTok.Tok == TOK_CASE || CurTok.Tok == TOK_DEFAULT) {
350
351             /* If the code for the previous selector did not end with a
352              * break statement, we must jump over the next selector test.
353              */
354             if (!HaveBreak) {
355                 /* Define a label for the code */
356                 if (CodeLab == 0) {
357                     CodeLab = GetLocalLabel ();
358                 }
359                 g_jump (CodeLab);
360             }
361
362             /* If we have a cascade label, emit it */
363             if (NextLab) {
364                 g_defcodelabel (NextLab);
365                 NextLab = 0;
366             }
367
368             while (CurTok.Tok == TOK_CASE || CurTok.Tok == TOK_DEFAULT) {
369
370                 /* Parse the selector */
371                 if (CurTok.Tok == TOK_CASE) {
372
373                     /* Count labels */
374                     ++lcount;
375
376                     /* Skip the "case" token */
377                     NextToken ();
378
379                     /* Read the selector expression */
380                     constexpr (&lval);
381                     if (!IsClassInt (lval.Type)) {
382                         Error ("Switch quantity not an integer");
383                     }
384
385                     /* Check the range of the expression */
386                     Val = lval.ConstVal;
387                     switch (ExprType) {
388
389                         case T_SCHAR:
390                             /* Signed char */
391                             if (Val < -128 || Val > 127) {
392                                 Error ("Range error");
393                             }
394                             break;
395
396                         case T_UCHAR:
397                             if (Val < 0 || Val > 255) {
398                                 Error ("Range error");
399                             }
400                             break;
401
402                         case T_INT:
403                             if (Val < -32768 || Val > 32767) {
404                                 Error ("Range error");
405                             }
406                             break;
407
408                         case T_UINT:
409                             if (Val < 0 || Val > 65535) {
410                                 Error ("Range error");
411                             }
412                             break;
413
414                         default:
415                             Internal ("Invalid type: %04X", ExprType);
416                     }
417
418                     /* Emit a compare */
419                     g_cmp (Flags, Val);
420
421                     /* If another case follows after the colon (which is
422                      * currently pending and cannot be skipped since otherwise
423                      * the debug infos will get wrong), we will jump to the
424                      * code if the condition is true.
425                      */
426                     if (NextTok.Tok == TOK_CASE) {
427                         /* Create a code label if needed */
428                         if (CodeLab == 0) {
429                             CodeLab = GetLocalLabel ();
430                         }
431                         g_falsejump (CF_NONE, CodeLab);
432                     } else if (NextTok.Tok != TOK_DEFAULT) {
433                         /* No case follows, jump to next selector */
434                         if (NextLab == 0) {
435                             NextLab = GetLocalLabel ();
436                         }
437                         g_truejump (CF_NONE, NextLab);
438                     }
439
440                     /* Skip the colon */
441                     ConsumeColon ();
442
443                 } else {
444
445                     /* Default case */
446                     NextToken ();
447
448                     /* Handle the pathologic case: DEFAULT followed by CASE */
449                     if (NextTok.Tok == TOK_CASE) {
450                         if (CodeLab == 0) {
451                             CodeLab = GetLocalLabel ();
452                         }
453                         g_jump (CodeLab);
454                     }
455
456                     /* Skip the colon */
457                     ConsumeColon ();
458
459                     /* Remember that we had a default label */
460                     HaveDefault = 1;
461                 }
462
463             }
464
465         }
466
467         /* Emit a code label if we have one */
468         if (CodeLab) {
469             g_defcodelabel (CodeLab);
470             CodeLab = 0;
471         }
472
473         /* Parse statements */
474         if (CurTok.Tok != TOK_RCURLY) {
475             HaveBreak = Statement (0);
476         }
477     }
478
479     /* Check if we have any labels */
480     if (lcount == 0 && !HaveDefault) {
481         Warning ("No case labels");
482     }
483
484     /* Define the exit label and, if there's a next label left, create this
485      * one, too.
486      */
487     if (NextLab) {
488         g_defcodelabel (NextLab);
489     }
490     g_defcodelabel (ExitLab);
491
492     /* Eat the closing curly brace */
493     NextToken ();
494
495     /* End the loop */
496     DelLoop ();
497 }
498
499
500
501 static void TableSwitch (ExprDesc* Expr)
502 /* Handle a switch statement via table based selector */
503 {
504     /* Entry for one case in a switch statement */
505     typedef struct {
506         long     Value;         /* selector value */
507         unsigned Label;         /* label for this selector */
508     } SwitchEntry;
509
510     unsigned DefaultLabel;      /* Label for default case */
511     unsigned ExitLabel;         /* exit label */
512     int lcase;                  /* label for compares */
513     int HaveBreak;              /* Last statement has a break */
514     unsigned Flags;             /* Code generator flags */
515     ExprDesc lval;              /* Case label expression */
516     unsigned I;
517     SwitchEntry* P;
518     Collection SwitchTab;
519
520     /* Initialize the collection for the switch entries */
521     InitCollection (&SwitchTab);
522
523     /* Create a look so we may break out, init labels */
524     HaveBreak    = 0;                   /* Keep gcc silent */
525     DefaultLabel = 0;                   /* No default case until now */
526     ExitLabel    = GetLocalLabel ();    /* get exit */
527     AddLoop (oursp, 0, ExitLabel, 0, 0);
528
529     /* Jump behind the code for the CASE labels */
530     g_jump (lcase = GetLocalLabel ());
531     while (CurTok.Tok != TOK_RCURLY) {
532         if (CurTok.Tok == TOK_CASE || CurTok.Tok == TOK_DEFAULT) {
533             do {
534                 if (CurTok.Tok == TOK_CASE) {
535                     NextToken ();
536                     constexpr (&lval);
537                     if (!IsClassInt (lval.Type)) {
538                         Error ("Switch quantity not an integer");
539                     }
540                     P = xmalloc (sizeof (SwitchEntry));
541                     P->Value = lval.ConstVal;
542                     P->Label = GetLocalLabel ();
543                     CollAppend (&SwitchTab, P);
544                     g_defcodelabel (P->Label);
545                 } else if (DefaultLabel == 0) {
546                     NextToken ();
547                     DefaultLabel = GetLocalLabel ();
548                     g_defcodelabel (DefaultLabel);
549                 } else {
550                     /* We already had a default label */
551                     Error ("Multiple default labels in one switch");
552                     /* Try to recover */
553                     NextToken ();
554                 }
555                 ConsumeColon ();
556             } while (CurTok.Tok == TOK_CASE || CurTok.Tok == TOK_DEFAULT);
557             HaveBreak = 0;
558         }
559         if (CurTok.Tok != TOK_RCURLY) {
560             HaveBreak = Statement (0);
561         }
562     }
563
564     /* Check if we have any labels */
565     if (CollCount(&SwitchTab) == 0 && DefaultLabel == 0) {
566         Warning ("No case labels");
567     }
568
569     /* Eat the closing curly brace */
570     NextToken ();
571
572     /* If the last statement doesn't have a break or return, add one */
573     if (!HaveBreak) {
574         g_jump (ExitLabel);
575     }
576
577     /* Actual selector code goes here */
578     g_defcodelabel (lcase);
579
580     /* Create the call to the switch subroutine */
581     Flags = TypeOf (Expr->Type);
582     g_switch (Flags);
583
584     /* First entry is negative of label count */
585     g_defdata (CF_INT | CF_CONST, -((int)CollCount(&SwitchTab))-1, 0);
586
587     /* Create the case selector table */
588     for (I = 0; I < CollCount (&SwitchTab); ++I) {
589         P = CollAt (&SwitchTab, I);
590         g_case (Flags, P->Label, P->Value);     /* Create one label */
591     }
592
593     if (DefaultLabel != 0) {
594         g_jump (DefaultLabel);
595     }
596     g_defcodelabel (ExitLabel);
597     DelLoop ();
598
599     /* Free the allocated space for the labels */
600     for (I = 0; I < CollCount (&SwitchTab); ++I) {
601         xfree (CollAt (&SwitchTab, I));
602     }
603
604     /* Free the collection itself */
605     DoneCollection (&SwitchTab);
606 }
607
608
609
610 static void SwitchStatement (void)
611 /* Handle a 'switch' statement */
612 {
613     ExprDesc Expr;              /* Switch statement expression */
614
615     /* Eat the "switch" */
616     NextToken ();
617
618     /* Read the switch expression */
619     ConsumeLParen ();
620     intexpr (&Expr);
621     ConsumeRParen ();
622
623     /* result of expr is in P */
624     ConsumeLCurly ();
625
626     /* Now decide which sort of switch we will create: */
627     if (IsTypeChar (Expr.Type) || (CodeSizeFactor >= 200 && IsClassInt (Expr.Type))) {
628         CascadeSwitch (&Expr);
629     } else {
630         TableSwitch (&Expr);
631     }
632 }
633
634
635
636 static void ForStatement (void)
637 /* Handle a 'for' statement */
638 {
639     ExprDesc lval1;
640     ExprDesc lval2;
641     ExprDesc lval3;
642     int HaveIncExpr;
643     CodeMark IncExprStart;
644     CodeMark IncExprEnd;
645     int PendingToken;
646
647     /* Get several local labels needed later */
648     unsigned TestLabel = GetLocalLabel ();
649     unsigned lab       = GetLocalLabel ();
650     unsigned IncLabel  = GetLocalLabel ();
651     unsigned lstat     = GetLocalLabel ();
652
653     /* Skip the FOR token */
654     NextToken ();
655
656     /* Add the loop to the loop stack */
657     AddLoop (oursp, TestLabel, lab, IncLabel, lstat);
658
659     /* Skip the opening paren */
660     ConsumeLParen ();
661
662     /* Parse the initializer expression */
663     if (CurTok.Tok != TOK_SEMI) {
664         expression (&lval1);
665     }
666     ConsumeSemi ();
667
668     /* Label for the test expressions */
669     g_defcodelabel (TestLabel);
670
671     /* Parse the test expression */
672     if (CurTok.Tok != TOK_SEMI) {
673         boolexpr (&lval2);
674         g_truejump (CF_NONE, lstat);
675         g_jump (lab);
676     } else {
677         g_jump (lstat);
678     }
679     ConsumeSemi ();
680
681     /* Remember the start of the increment expression */
682     IncExprStart = GetCodePos();
683
684     /* Label for the increment expression */
685     g_defcodelabel (IncLabel);
686
687     /* Parse the increment expression */
688     HaveIncExpr = (CurTok.Tok != TOK_RPAREN);
689     if (HaveIncExpr) {
690         expression (&lval3);
691     }
692
693     /* Jump to the test */
694     g_jump (TestLabel);
695
696     /* Remember the end of the increment expression */
697     IncExprEnd = GetCodePos();
698
699     /* Skip the closing paren */
700     ConsumeRParen ();
701
702     /* Loop body */
703     g_defcodelabel (lstat);
704     Statement (&PendingToken);
705
706     /* If we had an increment expression, move the code to the bottom of
707      * the loop. In this case we don't need to jump there at the end of
708      * the loop body.
709      */
710     if (HaveIncExpr) {
711         MoveCode (IncExprStart, IncExprEnd, GetCodePos());
712     } else {
713         /* Jump back to the increment expression */
714         g_jump (IncLabel);
715     }
716
717     /* Skip a pending token if we have one */
718     SkipPending (PendingToken);
719
720     /* Declare the break label */
721     g_defcodelabel (lab);
722
723     /* Remove the loop from the loop stack */
724     DelLoop ();
725 }
726
727
728
729 static int CompoundStatement (void)
730 /* Compound statement. Allow any number of statements inside braces. The
731  * function returns true if the last statement was a break or return.
732  */
733 {
734     int GotBreak;
735
736     /* Remember the stack at block entry */
737     int OldStack = oursp;
738
739     /* Enter a new lexical level */
740     EnterBlockLevel ();
741
742     /* Parse local variable declarations if any */
743     DeclareLocals ();
744
745     /* Now process statements in this block */
746     GotBreak = 0;
747     while (CurTok.Tok != TOK_RCURLY) {
748         if (CurTok.Tok != TOK_CEOF) {
749             GotBreak = Statement (0);
750         } else {
751             break;
752         }
753     }
754
755     /* Clean up the stack. */
756     if (!GotBreak) {
757         g_space (oursp - OldStack);
758     }
759     oursp = OldStack;
760
761     /* Emit references to imports/exports for this block */
762     EmitExternals ();
763
764     /* Leave the lexical level */
765     LeaveBlockLevel ();
766
767     return GotBreak;
768 }
769
770
771
772 int Statement (int* PendingToken)
773 /* Statement parser. Returns 1 if the statement does a return/break, returns
774  * 0 otherwise. If the PendingToken pointer is not NULL, the function will
775  * not skip the terminating token of the statement (closing brace or
776  * semicolon), but store true if there is a pending token, and false if there
777  * is none. The token is always checked, so there is no need for the caller to
778  * check this token, it must be skipped, however. If the argument pointer is
779  * NULL, the function will skip the token.
780  */
781 {
782     ExprDesc lval;
783     int GotBreak;
784
785     /* Assume no pending token */
786     if (PendingToken) {
787         *PendingToken = 0;
788     }
789
790     /* Check for a label */
791     if (CurTok.Tok == TOK_IDENT && NextTok.Tok == TOK_COLON) {
792
793         /* Special handling for a label */
794         DoLabel ();
795
796     } else {
797
798         switch (CurTok.Tok) {
799
800             case TOK_LCURLY:
801                 NextToken ();
802                 GotBreak = CompoundStatement ();
803                 CheckTok (TOK_RCURLY, "`{' expected", PendingToken);
804                 return GotBreak;
805
806             case TOK_IF:
807                 return IfStatement ();
808
809             case TOK_WHILE:
810                 WhileStatement ();
811                 break;
812
813             case TOK_DO:
814                 DoStatement ();
815                 break;
816
817             case TOK_SWITCH:
818                 SwitchStatement ();
819                 break;
820
821             case TOK_RETURN:
822                 ReturnStatement ();
823                 CheckSemi (PendingToken);
824                 return 1;
825
826             case TOK_BREAK:
827                 BreakStatement ();
828                 CheckSemi (PendingToken);
829                 return 1;
830
831             case TOK_CONTINUE:
832                 ContinueStatement ();
833                 CheckSemi (PendingToken);
834                 return 1;
835
836             case TOK_FOR:
837                 ForStatement ();
838                 break;
839
840             case TOK_GOTO:
841                 GotoStatement ();
842                 CheckSemi (PendingToken);
843                 return 1;
844
845             case TOK_SEMI:
846                 /* Ignore it */
847                 NextToken ();
848                 break;
849
850             case TOK_PRAGMA:
851                 DoPragma ();
852                 break;
853
854             default:
855                 /* Actual statement */
856                 expression (&lval);
857                 CheckSemi (PendingToken);
858         }
859     }
860     return 0;
861 }
862
863
864