Add support for computed gotos
This is a GCC extension that allows C to use fast jump tables.
This commit is contained in:
committed by
greg-king5
parent
c2220f3c30
commit
3b3b16ee9c
@@ -696,6 +696,22 @@ static void Primary (ExprDesc* E)
|
|||||||
|
|
||||||
switch (CurTok.Tok) {
|
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:
|
case TOK_IDENT:
|
||||||
/* Identifier. Get a pointer to the symbol table entry */
|
/* Identifier. Get a pointer to the symbol table entry */
|
||||||
Sym = E->Sym = FindSym (CurTok.Ident);
|
Sym = E->Sym = FindSym (CurTok.Ident);
|
||||||
|
|||||||
105
src/cc65/goto.c
105
src/cc65/goto.c
@@ -33,9 +33,17 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "asmlabel.h"
|
||||||
|
#include "codeent.h"
|
||||||
#include "codegen.h"
|
#include "codegen.h"
|
||||||
|
#include "codeseg.h"
|
||||||
|
#include "cpu.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
#include "exprdesc.h"
|
||||||
|
#include "expr.h"
|
||||||
|
#include "loadexpr.h"
|
||||||
#include "scanner.h"
|
#include "scanner.h"
|
||||||
|
#include "standard.h"
|
||||||
#include "symtab.h"
|
#include "symtab.h"
|
||||||
#include "goto.h"
|
#include "goto.h"
|
||||||
|
|
||||||
@@ -54,21 +62,97 @@ void GotoStatement (void)
|
|||||||
NextToken ();
|
NextToken ();
|
||||||
|
|
||||||
/* Label name must follow */
|
/* Label name must follow */
|
||||||
if (CurTok.Tok != TOK_IDENT) {
|
if (CurTok.Tok == TOK_IDENT) {
|
||||||
|
|
||||||
Error ("Label name expected");
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
/* Add a new label symbol if we don't have one until now */
|
/* Add a new label symbol if we don't have one until now */
|
||||||
SymEntry* Entry = AddLabelSym (CurTok.Ident, SC_REF | SC_GOTO);
|
SymEntry* Entry = AddLabelSym (CurTok.Ident, SC_REF | SC_GOTO);
|
||||||
|
|
||||||
/* Jump to the label */
|
/* Jump to the label */
|
||||||
g_jump (Entry->V.L.Label);
|
g_jump (Entry->V.L.Label);
|
||||||
}
|
|
||||||
|
|
||||||
/* Eat the label name */
|
/* Eat the label name */
|
||||||
NextToken ();
|
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);
|
SymEntry* Entry = AddLabelSym (CurTok.Ident, SC_DEF);
|
||||||
|
|
||||||
/* Emit the jump label */
|
/* 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 */
|
/* Eat the ident and colon */
|
||||||
NextToken ();
|
NextToken ();
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ struct CodeEntry;
|
|||||||
|
|
||||||
#define SC_GOTO 0x20000U
|
#define SC_GOTO 0x20000U
|
||||||
#define SC_SPADJUSTMENT 0x40000U
|
#define SC_SPADJUSTMENT 0x40000U
|
||||||
|
#define SC_GOTO_IND 0x80000U /* Indirect goto */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -717,7 +717,7 @@ SymEntry* AddLabelSym (const char* Name, unsigned Flags)
|
|||||||
for (i = 0; i < CollCount (Entry->V.L.DefsOrRefs); i++) {
|
for (i = 0; i < CollCount (Entry->V.L.DefsOrRefs); i++) {
|
||||||
DOR = CollAt (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.
|
/* We're processing a goto and here is its destination label.
|
||||||
** This means the difference between SP values is already known,
|
** This means the difference between SP values is already known,
|
||||||
** so we simply emit the SP adjustment code.
|
** 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
|
/* We're processing a label, let's update all gotos encountered
|
||||||
** so far
|
** so far
|
||||||
*/
|
*/
|
||||||
SymEntry *E;
|
if (DOR->Flags & SC_GOTO) {
|
||||||
g_userodata();
|
SymEntry *E;
|
||||||
g_defdatalabel (DOR->LateSP_Label);
|
g_userodata();
|
||||||
g_defdata (CF_CONST | CF_INT, StackPtr - DOR->StackPtr, 0);
|
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
|
/* Optimizer will need the information about the value of SP adjustment
|
||||||
** later, so let's preserve it.
|
** later, so let's preserve it.
|
||||||
*/
|
*/
|
||||||
E = NewSymEntry (LocalLabelName (DOR->LateSP_Label), SC_SPADJUSTMENT);
|
E = NewSymEntry (LocalLabelName (DOR->LateSP_Label), SC_SPADJUSTMENT);
|
||||||
E->V.SPAdjustment = StackPtr - DOR->StackPtr;
|
E->V.SPAdjustment = StackPtr - DOR->StackPtr;
|
||||||
AddSymEntry (SPAdjustTab, E);
|
AddSymEntry (SPAdjustTab, E);
|
||||||
|
}
|
||||||
|
|
||||||
/* Are we jumping into a block with initalization of an object that
|
/* Are we jumping into a block with initalization of an object that
|
||||||
** has automatic storage duration? Let's emit a warning.
|
** has automatic storage duration? Let's emit a warning.
|
||||||
|
|||||||
55
test/val/computedgoto.c
Normal file
55
test/val/computedgoto.c
Normal 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user