]> git.sur5r.net Git - cc65/commitdiff
Add support for computed gotos
authorLauri Kasanen <cand@gmx.com>
Tue, 9 Apr 2019 12:49:52 +0000 (15:49 +0300)
committergreg-king5 <greg.king5@verizon.net>
Sun, 21 Apr 2019 16:46:03 +0000 (12:46 -0400)
This is a GCC extension that allows C to use fast jump tables.

src/cc65/expr.c
src/cc65/goto.c
src/cc65/symentry.h
src/cc65/symtab.c
test/val/computedgoto.c [new file with mode: 0644]

index 320ac23213c211d2f0349540395faf5e6f1bc6af..a4b58b7230e26405fac23f8ce6e9c42d8060c925 100644 (file)
@@ -696,6 +696,22 @@ static void Primary (ExprDesc* E)
 
     switch (CurTok.Tok) {
 
+        case TOK_BOOL_AND:
+            /* A computed goto label address */
+            if (IS_Get (&Standard) >= STD_CC65) {
+                NextToken ();
+                SymEntry* Entry = AddLabelSym (CurTok.Ident, SC_REF | SC_GOTO_IND);
+                /* output its label */
+                E->Flags = E_RTYPE_RVAL | E_LOC_STATIC;
+                E->Name = Entry->V.L.Label;
+                E->Type = PointerTo(type_void);
+                NextToken ();
+            } else {
+                Error ("Computed gotos are a C extension, not supported with this --standard");
+                ED_MakeConstAbsInt (E, 1);
+            }
+            break;
+
         case TOK_IDENT:
             /* Identifier. Get a pointer to the symbol table entry */
             Sym = E->Sym = FindSym (CurTok.Ident);
index ae9e6096d48536bce61fa44ce97b51117d224e65..ae509e643d9a99411d4518a96aa90ef5bd1dab52 100644 (file)
 
 
 
+#include "asmlabel.h"
+#include "codeent.h"
 #include "codegen.h"
+#include "codeseg.h"
+#include "cpu.h"
 #include "error.h"
+#include "exprdesc.h"
+#include "expr.h"
+#include "loadexpr.h"
 #include "scanner.h"
+#include "standard.h"
 #include "symtab.h"
 #include "goto.h"
 
@@ -54,21 +62,97 @@ void GotoStatement (void)
     NextToken ();
 
     /* Label name must follow */
-    if (CurTok.Tok != TOK_IDENT) {
-
-        Error ("Label name expected");
-
-    } else {
+    if (CurTok.Tok == TOK_IDENT) {
 
         /* Add a new label symbol if we don't have one until now */
         SymEntry* Entry = AddLabelSym (CurTok.Ident, SC_REF | SC_GOTO);
 
         /* Jump to the label */
         g_jump (Entry->V.L.Label);
-    }
 
-    /* Eat the label name */
-    NextToken ();
+        /* Eat the label name */
+        NextToken ();
+
+    } else if (CurTok.Tok == TOK_STAR && IS_Get (&Standard) >= STD_CC65) {
+        SymEntry *arr, *idx, *cur;
+        SymTable *tab;
+        ExprDesc desc;
+        CodeEntry *E;
+        unsigned char val;
+        unsigned I;
+
+        NextToken ();
+
+        /* arr[foo], we only support simple foo for now */
+        if (CurTok.Tok == TOK_IDENT &&
+            (arr = FindSym (CurTok.Ident))) {
+            NextToken ();
+            ConsumeLBrack ();
+
+            /* Find array size */
+            if (!IsTypeArray(arr->Type) || SizeOf(arr->Type) == 0 ||
+                SizeOf(GetElementType(arr->Type)) != 2)
+                Error ("Expected array");
+            if (GetElementCount(arr->Type) > 127)
+                Error ("Only arrays with <= 127 labels are supported, got %lu",
+                       GetElementCount(arr->Type));
+
+            if (CurTok.Tok == TOK_ICONST) {
+                val = CurTok.IVal;
+                NextToken ();
+
+                if (CPUIsets[CPU] & CPU_ISET_65SC02) {
+                    AddCodeLine ("ldx #$%02X", val * 2);
+                    AddCodeLine ("jmp (%s,x)", arr->AsmName);
+                } else {
+                    AddCodeLine ("ldy #$%02X", val * 2);
+                    AddCodeLine ("lda %s,y", arr->AsmName);
+                    AddCodeLine ("ldx %s+1,y", arr->AsmName);
+                    AddCodeLine ("jmp callax");
+                }
+            } else if (CurTok.Tok == TOK_IDENT &&
+                       (idx = FindSym (CurTok.Ident))) {
+                hie10 (&desc);
+                LoadExpr (CF_NONE, &desc);
+                AddCodeLine ("asl a");
+
+                if (CPUIsets[CPU] & CPU_ISET_65SC02) {
+                    AddCodeLine ("tax");
+                    AddCodeLine ("jmp (%s,x)", arr->AsmName);
+                } else {
+                    AddCodeLine ("tay");
+                    AddCodeLine ("lda %s,y", arr->AsmName);
+                    AddCodeLine ("ldx %s+1,y", arr->AsmName);
+                    AddCodeLine ("jmp callax");
+                }
+            } else {
+                Error ("Only simple expressions are supported for computed goto");
+            }
+
+            ConsumeRBrack ();
+
+            /* Loop over all target labels, specifying this as a jump point.
+            ** It's not exact - if there's multiple gotos, the last will be used,
+            ** but it's only needed so the optimizer does not remove the labels.
+            */
+            I = CS_GetEntryCount (CS->Code) - 1;
+            E = CS_GetEntry (CS->Code, I);
+
+            tab = GetLabelSymTab ();
+            if (tab) {
+                cur = tab->SymHead;
+                while (cur) {
+                    if ((cur->Flags & (SC_LABEL|SC_GOTO_IND)) == (SC_LABEL|SC_GOTO_IND)) {
+                        cur->V.L.IndJumpFrom = E;
+                    }
+                    cur = cur->NextSym;
+                }
+            }
+        }
+    } else {
+
+        Error ("Label name expected");
+    }
 }
 
 
@@ -80,7 +164,10 @@ void DoLabel (void)
     SymEntry* Entry = AddLabelSym (CurTok.Ident, SC_DEF);
 
     /* Emit the jump label */
-    g_defcodelabel (Entry->V.L.Label);
+    CodeLabel* L = CS_AddLabel (CS->Code, LocalLabelName (Entry->V.L.Label));
+    if (Entry->V.L.IndJumpFrom) {
+        CollAppend (&L->JumpFrom, Entry->V.L.IndJumpFrom);
+    }
 
     /* Eat the ident and colon */
     NextToken ();
index abadc35c2f0fe7be1d74a0225496a49a2ff280dc..62bf0b0e769597caad90af871ec1c01c46ef4380 100644 (file)
@@ -101,6 +101,7 @@ struct CodeEntry;
 
 #define SC_GOTO         0x20000U
 #define SC_SPADJUSTMENT 0x40000U
+#define SC_GOTO_IND     0x80000U        /* Indirect goto */
 
 
 
index 9a767fd0e0ce3c43007b66c7a66ae0b114e38e78..52ef04e4250aa90b4b3446c3f13598deebf4e28c 100644 (file)
@@ -717,7 +717,7 @@ SymEntry* AddLabelSym (const char* Name, unsigned Flags)
         for (i = 0; i < CollCount (Entry->V.L.DefsOrRefs); i++) {
             DOR = CollAt (Entry->V.L.DefsOrRefs, i);
 
-            if ((DOR->Flags & SC_DEF) && (Flags & SC_REF) && (Flags & SC_GOTO)) {
+            if ((DOR->Flags & SC_DEF) && (Flags & SC_REF) && (Flags & (SC_GOTO|SC_GOTO_IND))) {
                 /* We're processing a goto and here is its destination label.
                 ** This means the difference between SP values is already known,
                 ** so we simply emit the SP adjustment code.
@@ -739,21 +739,23 @@ SymEntry* AddLabelSym (const char* Name, unsigned Flags)
             }
 
 
-            if ((DOR->Flags & SC_REF) && (DOR->Flags & SC_GOTO) && (Flags & SC_DEF)) {
+            if ((DOR->Flags & SC_REF) && (DOR->Flags & (SC_GOTO|SC_GOTO_IND)) && (Flags & SC_DEF)) {
                 /* We're processing a label, let's update all gotos encountered
                 ** so far
                 */
-                SymEntry *E;
-                g_userodata();
-                g_defdatalabel (DOR->LateSP_Label);
-                g_defdata (CF_CONST | CF_INT, StackPtr - DOR->StackPtr, 0);
-
-                /* Optimizer will need the information about the value of SP adjustment
-                ** later, so let's preserve it.
-                */
-                E = NewSymEntry (LocalLabelName (DOR->LateSP_Label), SC_SPADJUSTMENT);
-                E->V.SPAdjustment = StackPtr - DOR->StackPtr;
-                AddSymEntry (SPAdjustTab, E);
+                if (DOR->Flags & SC_GOTO) {
+                    SymEntry *E;
+                    g_userodata();
+                    g_defdatalabel (DOR->LateSP_Label);
+                    g_defdata (CF_CONST | CF_INT, StackPtr - DOR->StackPtr, 0);
+
+                    /* Optimizer will need the information about the value of SP adjustment
+                    ** later, so let's preserve it.
+                    */
+                    E = NewSymEntry (LocalLabelName (DOR->LateSP_Label), SC_SPADJUSTMENT);
+                    E->V.SPAdjustment = StackPtr - DOR->StackPtr;
+                    AddSymEntry (SPAdjustTab, E);
+                }
 
                 /* Are we jumping into a block with initalization of an object that
                 ** has automatic storage duration? Let's emit a warning.
diff --git a/test/val/computedgoto.c b/test/val/computedgoto.c
new file mode 100644 (file)
index 0000000..f7a4e28
--- /dev/null
@@ -0,0 +1,55 @@
+static unsigned char val, val2;
+
+static void act(const unsigned char op) {
+
+       static const void * const arr[] = {
+               &&op0,
+               &&op1,
+               &&op2,
+               &&op3,
+               &&op4,
+               &&op5,
+               &&op6,
+       };
+
+       goto *arr[op];
+
+       op0:
+       val += 1;
+       return;
+
+       op1:
+       val += 2;
+       return;
+
+       op2:
+       val += 3;
+       return;
+
+       op3:
+       val2 += 1;
+       return;
+
+       op4:
+       val2 += 5;
+       return;
+
+       op5:
+       val2 += 7;
+       return;
+
+       op6:
+       val2 += 9;
+       return;
+}
+
+int main() {
+
+       val = val2 = 0;
+
+       act(1);
+       act(3);
+       act(5);
+
+       return val == 2 && val2 == 8 ? 0 : 1;
+}