mirror of
https://github.com/cc65/cc65.git
synced 2025-01-16 13:31:16 +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:
parent
c2220f3c30
commit
3b3b16ee9c
@ -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);
|
||||
|
105
src/cc65/goto.c
105
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 ();
|
||||
|
@ -101,6 +101,7 @@ struct CodeEntry;
|
||||
|
||||
#define SC_GOTO 0x20000U
|
||||
#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++) {
|
||||
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
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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user