1
0
mirror of https://github.com/cc65/cc65.git synced 2024-06-08 15:29:37 +00:00

Add support for computed gotos

This is a GCC extension that allows C to use fast jump tables.
This commit is contained in:
Lauri Kasanen 2019-04-09 15:49:52 +03:00 committed by greg-king5
parent c2220f3c30
commit 3b3b16ee9c
5 changed files with 182 additions and 21 deletions

View 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);

View File

@ -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 ();

View File

@ -101,6 +101,7 @@ struct CodeEntry;
#define SC_GOTO 0x20000U
#define SC_SPADJUSTMENT 0x40000U
#define SC_GOTO_IND 0x80000U /* Indirect goto */

View 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);
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);
/* 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.

55
test/val/computedgoto.c Normal file
View File

@ -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;
}