From 3b3b16ee9c2632a8feae56cb990f72893d9aea14 Mon Sep 17 00:00:00 2001 From: Lauri Kasanen Date: Tue, 9 Apr 2019 15:49:52 +0300 Subject: [PATCH] Add support for computed gotos This is a GCC extension that allows C to use fast jump tables. --- src/cc65/expr.c | 16 ++++++ src/cc65/goto.c | 105 ++++++++++++++++++++++++++++++++++++---- src/cc65/symentry.h | 1 + src/cc65/symtab.c | 28 ++++++----- test/val/computedgoto.c | 55 +++++++++++++++++++++ 5 files changed, 183 insertions(+), 22 deletions(-) create mode 100644 test/val/computedgoto.c diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 320ac2321..a4b58b723 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -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); diff --git a/src/cc65/goto.c b/src/cc65/goto.c index ae9e6096d..ae509e643 100644 --- a/src/cc65/goto.c +++ b/src/cc65/goto.c @@ -33,9 +33,17 @@ +#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 (); diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index abadc35c2..62bf0b0e7 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -101,6 +101,7 @@ struct CodeEntry; #define SC_GOTO 0x20000U #define SC_SPADJUSTMENT 0x40000U +#define SC_GOTO_IND 0x80000U /* Indirect goto */ diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 9a767fd0e..52ef04e42 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -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 index 000000000..f7a4e28ec --- /dev/null +++ b/test/val/computedgoto.c @@ -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; +} -- 2.39.2