Rewrote the switch statement

git-svn-id: svn://svn.cc65.org/cc65/trunk@1021 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
cuz
2001-10-11 08:02:03 +00:00
parent 5e7e3d4b81
commit 41d2cc8f91
9 changed files with 574 additions and 357 deletions

183
src/cc65/casenode.c Normal file
View File

@@ -0,0 +1,183 @@
/*****************************************************************************/
/* */
/* casenode.c */
/* */
/* Node for the tree that is generated for a switch statement */
/* */
/* */
/* */
/* (C) 2001 Ullrich von Bassewitz */
/* Wacholderweg 14 */
/* D-70597 Stuttgart */
/* EMail: uz@cc65.org */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#include <limits.h>
/* common */
#include "coll.h"
#include "xmalloc.h"
/* cc65 */
#include "asmlabel.h"
#include "error.h"
#include "casenode.h"
/*****************************************************************************/
/* Code */
/*****************************************************************************/
CaseNode* NewCaseNode (unsigned char Value)
/* Create and initialize a new CaseNode */
{
/* Allocate memory */
CaseNode* N = xmalloc (sizeof (CaseNode));
/* Initialize the fields */
N->Value = Value;
N->Label = GetLocalLabel ();
N->Nodes = 0;
/* Return the new node */
return N;
}
void FreeCaseNode (CaseNode* N)
/* Delete a case node plus all sub nodes */
{
if (N->Nodes) {
FreeCaseNodeColl (N->Nodes);
}
xfree (N);
}
void FreeCaseNodeColl (Collection* Nodes)
/* Free a collection of case nodes */
{
unsigned I;
for (I = 0; I < CollCount (Nodes); ++I) {
FreeCaseNode (CollAtUnchecked (Nodes, I));
}
FreeCollection (Nodes);
}
int SearchCaseNode (const Collection* Nodes, unsigned char Key, int* Index)
/* Search for a node in the given collection. If the node has been found,
* set Index to the index of the node and return true. If the node was not
* found, set Index the the insertion position of the node and return
* false.
*/
{
/* Do a binary search */
int First = 0;
int Last = CollCount (Nodes) - 1;
int S = 0;
while (First <= Last) {
/* Set current to mid of range */
int Current = (Last + First) / 2;
/* Get the entry from this position */
const CaseNode* N = CollConstAt (Nodes, Current);
/* Compare the values */
if (N->Value < Key) {
First = Current + 1;
} else {
Last = Current - 1;
if (N->Value == Key) {
/* Found. We cannot have duplicates, so end the search here. */
S = 1;
First = Current;
}
}
}
*Index = First;
return S;
}
unsigned InsertCaseValue (Collection* Nodes, unsigned long Val, unsigned Depth)
/* Insert a new case value into a CaseNode tree with the given depth. Return
* the code label for the value.
*/
{
CaseNode* N = 0;
while (Depth--) {
/* Get the key */
unsigned char Key = (Val >> (Depth * CHAR_BIT)) & 0xFF;
/* Search for the node in the collection */
int Index;
if (SearchCaseNode (Nodes, Key, &Index) == 0) {
/* Node not found - insert one */
N = NewCaseNode (Key);
CollInsert (Nodes, N, Index);
/* If this is not the last round, create the collection for
* the subnodes.
*/
if (Depth > 0) {
N->Nodes = NewCollection ();
}
} else {
/* Node found, get it */
N = CollAt (Nodes, Index);
/* If this is the last round and we found a node, we have a
* duplicate case label in a switch.
*/
if (Depth == 0) {
Error ("Duplicate case label");
}
}
/* Get the collection from the node for the next round. */
Nodes = N->Nodes;
}
/* Return the label of the node we found/created */
return N->Label;
}

144
src/cc65/casenode.h Normal file
View File

@@ -0,0 +1,144 @@
/*****************************************************************************/
/* */
/* casenode.h */
/* */
/* Node for the tree that is generated for a switch statement */
/* */
/* */
/* */
/* (C) 2001 Ullrich von Bassewitz */
/* Wacholderweg 14 */
/* D-70597 Stuttgart */
/* EMail: uz@cc65.org */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#ifndef CASENODE_H
#define CASENODE_H
/* common */
#include "coll.h"
/*****************************************************************************/
/* Data */
/*****************************************************************************/
typedef struct CaseNode CaseNode;
struct CaseNode {
unsigned char Value;
unsigned Label;
Collection* Nodes;
};
/*****************************************************************************/
/* Code */
/*****************************************************************************/
CaseNode* NewCaseNode (unsigned char Value);
/* Create and initialize a new CaseNode */
void FreeCaseNode (CaseNode* N);
/* Delete a case node plus all sub nodes */
#if defined(HAVE_INLINE)
INLINE unsigned CN_GetSubNodeCount (const CaseNode* N)
/* Return the number of subnodes in N */
{
return N->Nodes? CollCount (N->Nodes) : 0;
}
#else
# define CN_GetSubNodeCount(N) ((N)->Nodes? CollCount (&(N)->Nodes) : 0)
#endif
#if defined(HAVE_INLINE)
INLINE CaseNode* CN_GetSubNode (CaseNode* N, unsigned Index)
/* Get a sub node of the given node */
{
return CollAt (N->Nodes, Index);
}
#else
# define CN_GetSubNode(N, Index) CollAt (&(N)->Nodes, Index)
#endif
#if defined(HAVE_INLINE)
INLINE unsigned char CN_GetValue (const CaseNode* N)
/* Return the value for a case node */
{
return N->Value;
}
#else
# define CN_GetValue(N) ((N)->Value)
#endif
#if defined(HAVE_INLINE)
INLINE unsigned CN_GetLabel (const CaseNode* N)
/* Return the label for a case node */
{
return N->Label;
}
#else
# define CN_GetLabel(N) ((N)->Label)
#endif
#if defined(HAVE_INLINE)
INLINE int CN_IsLeafNode (const CaseNode* N)
/* Return true if this is a leaf node */
{
return (N->Nodes == 0);
}
#else
# define CN_IsLeafNode(N) ((N)->Nodes == 0)
#endif
void FreeCaseNodeColl (Collection* Nodes);
/* Free a collection of case nodes */
int SearchCaseNode (const Collection* Nodes, unsigned char Key, int* Index);
/* Search for a node in the given collection. If the node has been found,
* set Index to the index of the node and return true. If the node was not
* found, set Index the the insertion position of the node and return
* false.
*/
unsigned InsertCaseValue (Collection* Nodes, unsigned long Val, unsigned Depth);
/* Insert a new case value into a CaseNode tree with the given depth. Return
* the code label for the value.
*/
/* End of casenode.h */
#endif

View File

@@ -47,6 +47,7 @@
/* cc65 */ /* cc65 */
#include "asmcode.h" #include "asmcode.h"
#include "asmlabel.h" #include "asmlabel.h"
#include "casenode.h"
#include "codeseg.h" #include "codeseg.h"
#include "cpu.h" #include "cpu.h"
#include "dataseg.h" #include "dataseg.h"
@@ -2354,53 +2355,6 @@ void g_jump (unsigned Label)
void g_switch (unsigned Flags)
/* Output switch statement preamble */
{
switch (Flags & CF_TYPE) {
case CF_CHAR:
case CF_INT:
AddCodeLine ("jsr switch");
break;
case CF_LONG:
AddCodeLine ("jsr lswitch");
break;
default:
typeerror (Flags);
}
}
void g_case (unsigned flags, unsigned label, unsigned long val)
/* Create table code for one case selector */
{
switch (flags & CF_TYPE) {
case CF_CHAR:
case CF_INT:
AddCodeLine (".word $%04X, %s",
(unsigned)(val & 0xFFFF),
LocalLabelName (label));
break;
case CF_LONG:
AddCodeLine (".dword $%08lX", val);
AddCodeLine (".word %s", LocalLabelName (label));
break;
default:
typeerror (flags);
}
}
void g_truejump (unsigned flags attribute ((unused)), unsigned label) void g_truejump (unsigned flags attribute ((unused)), unsigned label)
/* Jump to label if zero flag clear */ /* Jump to label if zero flag clear */
{ {
@@ -3798,7 +3752,7 @@ void g_defdata (unsigned flags, unsigned long val, unsigned offs)
const char* Label = GetLabelName (flags, val, offs); const char* Label = GetLabelName (flags, val, offs);
/* Labels are always 16 bit */ /* Labels are always 16 bit */
AddDataLine ("\t.word\t%s", Label); AddDataLine ("\t.addr\t%s", Label);
} }
} }
@@ -3849,6 +3803,84 @@ void g_zerobytes (unsigned n)
/*****************************************************************************/
/* Switch statement */
/*****************************************************************************/
void g_switch (Collection* Nodes, unsigned DefaultLabel, unsigned Depth)
/* Generate code for a switch statement */
{
unsigned NextLabel = 0;
unsigned I;
/* Setup registers and determine which compare insn to use */
const char* Compare;
switch (Depth) {
case 1:
Compare = "cmp #$%02X";
break;
case 2:
Compare = "cpx #$%02X";
break;
case 3:
AddCodeLine ("ldy sreg");
Compare = "cpy #$%02X";
break;
case 4:
AddCodeLine ("ldy sreg+1");
Compare = "cpy #$%02X";
break;
default:
Internal ("Invalid depth in g_switch: %u", Depth);
}
/* Walk over all nodes */
for (I = 0; I < CollCount (Nodes); ++I) {
/* Get the next case node */
CaseNode* N = CollAtUnchecked (Nodes, I);
/* If we have a next label, define it */
if (NextLabel) {
g_defcodelabel (NextLabel);
NextLabel = 0;
}
/* Do the compare */
AddCodeLine (Compare, CN_GetValue (N));
/* If this is the last level, jump directly to the case code if found */
if (Depth == 1) {
/* Branch if equal */
g_falsejump (0, CN_GetLabel (N));
} else {
/* Determine the next label */
if (I == CollCount (Nodes) - 1) {
/* Last node means not found */
g_truejump (0, DefaultLabel);
} else {
/* Jump to the next check */
NextLabel = GetLocalLabel ();
g_truejump (0, NextLabel);
}
/* Check the next level */
g_switch (N->Nodes, DefaultLabel, Depth-1);
}
}
/* If we go here, we haven't found the label */
g_jump (DefaultLabel);
}
/*****************************************************************************/ /*****************************************************************************/
/* User supplied assembler code */ /* User supplied assembler code */
/*****************************************************************************/ /*****************************************************************************/

View File

@@ -38,6 +38,9 @@
/* common */
#include "coll.h"
/* cc65 */ /* cc65 */
#include "segments.h" #include "segments.h"
@@ -376,12 +379,6 @@ void g_callind (unsigned Flags, unsigned ArgSize, int Offs);
void g_jump (unsigned Label); void g_jump (unsigned Label);
/* Jump to specified internal label number */ /* Jump to specified internal label number */
void g_switch (unsigned Flags);
/* Output switch statement preamble */
void g_case (unsigned flags, unsigned label, unsigned long val);
/* Create table code for one case selector */
void g_truejump (unsigned flags, unsigned label); void g_truejump (unsigned flags, unsigned label);
/* Jump to label if zero flag clear */ /* Jump to label if zero flag clear */
@@ -434,6 +431,17 @@ void g_zerobytes (unsigned n);
/*****************************************************************************/
/* Switch statement */
/*****************************************************************************/
void g_switch (Collection* Nodes, unsigned DefaultLabel, unsigned Depth);
/* Generate code for a switch statement */
/*****************************************************************************/ /*****************************************************************************/
/* User supplied assembler code */ /* User supplied assembler code */
/*****************************************************************************/ /*****************************************************************************/

View File

@@ -101,11 +101,11 @@ void CL_MoveRefs (CodeLabel* OldLabel, CodeLabel* NewLabel)
*/ */
{ {
/* Walk through all instructions referencing the old label */ /* Walk through all instructions referencing the old label */
unsigned Count = CollCount (&OldLabel->JumpFrom); unsigned Count = CL_GetRefCount (OldLabel);
while (Count--) { while (Count--) {
/* Get the instruction that references the old label */ /* Get the instruction that references the old label */
CodeEntry* E = CollAt (&OldLabel->JumpFrom, Count); CodeEntry* E = CL_GetRef (OldLabel, Count);
/* Change the reference to the new label */ /* Change the reference to the new label */
CHECK (E->JumpTo == OldLabel); CHECK (E->JumpTo == OldLabel);
@@ -127,3 +127,4 @@ void CL_Output (const CodeLabel* L, FILE* F)

View File

@@ -26,6 +26,7 @@ OBJS = anonname.o \
asmcode.o \ asmcode.o \
asmlabel.o \ asmlabel.o \
asmstmt.o \ asmstmt.o \
casenode.o \
codeent.o \ codeent.o \
codegen.o \ codegen.o \
codelab.o \ codelab.o \

View File

@@ -71,6 +71,7 @@ OBJS = anonname.obj \
asmcode.obj \ asmcode.obj \
asmlabel.obj \ asmlabel.obj \
asmstmt.obj \ asmstmt.obj \
casenode.obj \
codeent.obj \ codeent.obj \
codegen.obj \ codegen.obj \
codelab.obj \ codelab.obj \
@@ -147,6 +148,7 @@ FILE anonname.obj
FILE asmcode.obj FILE asmcode.obj
FILE asmlabel.obj FILE asmlabel.obj
FILE asmstmt.obj FILE asmstmt.obj
FILE casenode.obj
FILE codeent.obj FILE codeent.obj
FILE codegen.obj FILE codegen.obj
FILE codelab.obj FILE codelab.obj

View File

@@ -61,17 +61,6 @@
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Maximum count of cases */
#define CASE_MAX 257
/*****************************************************************************/ /*****************************************************************************/
/* Helper functions */ /* Helper functions */
/*****************************************************************************/ /*****************************************************************************/

View File

@@ -33,12 +33,16 @@
#include <limits.h>
/* common */ /* common */
#include "coll.h" #include "coll.h"
#include "xmalloc.h" #include "xmalloc.h"
/* cc65 */ /* cc65 */
#include "asmcode.h"
#include "asmlabel.h" #include "asmlabel.h"
#include "casenode.h"
#include "codegen.h" #include "codegen.h"
#include "datatype.h" #include "datatype.h"
#include "error.h" #include "error.h"
@@ -57,76 +61,74 @@
static void CascadeSwitch (ExprDesc* Expr) void SwitchStatement (void)
/* Handle a switch statement for chars with a cmp cascade for the selector */ /* Handle a switch statement for chars with a cmp cascade for the selector */
{ {
unsigned ExitLab; /* Exit label */ Collection* Nodes; /* CaseNode tree */
unsigned NextLab; /* Next case label */ ExprDesc SwitchExpr; /* Switch statement expression */
unsigned CodeLab; /* Label that starts the actual selector code */ ExprDesc CaseExpr; /* Case label expression */
int HaveBreak; /* Remember if we exited with break */ type SwitchExprType; /* Basic switch expression type */
int HaveDefault; /* Remember if we had a default label */ CodeMark CaseCodeStart; /* Start of code marker */
int lcount; /* Label count */ unsigned Depth; /* Number of bytes the selector type has */
unsigned Flags; /* Code generator flags */ unsigned ExitLabel; /* Exit label */
ExprDesc lval; /* Case label expression */ unsigned CaseLabel; /* Label for case */
unsigned DefaultLabel; /* Label for the default branch */
long Val; /* Case label value */ long Val; /* Case label value */
/* Eat the "switch" token */
NextToken ();
/* Read the switch expression */
ConsumeLParen ();
intexpr (&SwitchExpr);
ConsumeRParen ();
/* Opening curly brace */
ConsumeLCurly ();
/* Remember the current code position */
CaseCodeStart = GetCodePos();
/* Get the unqualified type of the switch expression */ /* Get the unqualified type of the switch expression */
type ExprType = UnqualifiedType (Expr->Type[0]); SwitchExprType = UnqualifiedType (SwitchExpr.Type[0]);
/* Create a loop so we may break out, init labels */ /* Get the number of bytes the selector type has */
ExitLab = GetLocalLabel (); Depth = SizeOf (SwitchExpr.Type);
AddLoop (oursp, 0, ExitLab, 0, 0); CHECK (Depth == 1 || Depth == 2 || Depth == 4);
/* Setup some variables needed in the loop below */ /* Get the exit label for the switch statement */
Flags = TypeOf (Expr->Type) | CF_CONST | CF_FORCECHAR; ExitLabel = GetLocalLabel ();
CodeLab = NextLab = 0;
HaveBreak = 1; /* Create a loop so we may use break. */
HaveDefault = 0; AddLoop (oursp, 0, ExitLabel, 0, 0);
/* Create the collection for the case node tree */
Nodes = NewCollection ();
/* Clear the label for the default branch */
DefaultLabel = 0;
/* Parse the labels */ /* Parse the labels */
lcount = 0;
while (CurTok.Tok != TOK_RCURLY) { while (CurTok.Tok != TOK_RCURLY) {
if (CurTok.Tok == TOK_CASE || CurTok.Tok == TOK_DEFAULT) {
/* If the code for the previous selector did not end with a
* break statement, we must jump over the next selector test.
*/
if (!HaveBreak) {
/* Define a label for the code */
if (CodeLab == 0) {
CodeLab = GetLocalLabel ();
}
g_jump (CodeLab);
}
/* If we have a cascade label, emit it */
if (NextLab) {
g_defcodelabel (NextLab);
NextLab = 0;
}
while (CurTok.Tok == TOK_CASE || CurTok.Tok == TOK_DEFAULT) { while (CurTok.Tok == TOK_CASE || CurTok.Tok == TOK_DEFAULT) {
/* Parse the selector */ /* Parse the selector */
if (CurTok.Tok == TOK_CASE) { if (CurTok.Tok == TOK_CASE) {
/* Count labels */
++lcount;
/* Skip the "case" token */ /* Skip the "case" token */
NextToken (); NextToken ();
/* Read the selector expression */ /* Read the selector expression */
constexpr (&lval); constexpr (&CaseExpr);
if (!IsClassInt (lval.Type)) { if (!IsClassInt (CaseExpr.Type)) {
Error ("Switch quantity not an integer"); Error ("Switch quantity not an integer");
} }
/* Check the range of the expression */ /* Check the range of the expression */
Val = lval.ConstVal; Val = CaseExpr.ConstVal;
switch (ExprType) { switch (SwitchExprType) {
case T_SCHAR: case T_SCHAR:
/* Signed char */ /* Signed char */
@@ -153,31 +155,19 @@ static void CascadeSwitch (ExprDesc* Expr)
} }
break; break;
case T_LONG:
case T_ULONG:
break;
default: default:
Internal ("Invalid type: %04X", ExprType); Internal ("Invalid type: %04X", SwitchExprType);
} }
/* Emit a compare */ /* Insert the case selector into the selector table */
g_cmp (Flags, Val); CaseLabel = InsertCaseValue (Nodes, Val, Depth);
/* If another case follows after the colon (which is /* Define this label */
* currently pending and cannot be skipped since otherwise g_defcodelabel (CaseLabel);
* the debug infos will get wrong), we will jump to the
* code if the condition is true.
*/
if (NextTok.Tok == TOK_CASE) {
/* Create a code label if needed */
if (CodeLab == 0) {
CodeLab = GetLocalLabel ();
}
g_falsejump (CF_NONE, CodeLab);
} else if (NextTok.Tok != TOK_DEFAULT) {
/* No case follows, jump to next selector */
if (NextLab == 0) {
NextLab = GetLocalLabel ();
}
g_truejump (CF_NONE, NextLab);
}
/* Skip the colon */ /* Skip the colon */
ConsumeColon (); ConsumeColon ();
@@ -187,49 +177,51 @@ static void CascadeSwitch (ExprDesc* Expr)
/* Default case */ /* Default case */
NextToken (); NextToken ();
/* Handle the pathologic case: DEFAULT followed by CASE */ /* Check if we do already have a default branch */
if (NextTok.Tok == TOK_CASE) { if (DefaultLabel == 0) {
if (CodeLab == 0) {
CodeLab = GetLocalLabel (); /* Generate and emit the default label */
} DefaultLabel = GetLocalLabel ();
g_jump (CodeLab); g_defcodelabel (DefaultLabel);
} else {
/* We had the default label already */
Error ("Duplicate `default' case");
} }
/* Skip the colon */ /* Skip the colon */
ConsumeColon (); ConsumeColon ();
/* Remember that we had a default label */
HaveDefault = 1;
} }
} }
}
/* Emit a code label if we have one */
if (CodeLab) {
g_defcodelabel (CodeLab);
CodeLab = 0;
}
/* Parse statements */ /* Parse statements */
if (CurTok.Tok != TOK_RCURLY) { if (CurTok.Tok != TOK_RCURLY) {
HaveBreak = Statement (0); Statement (0);
} }
} }
/* Check if we have any labels */ /* Check if we had any labels */
if (lcount == 0 && !HaveDefault) { if (CollCount (Nodes) == 0 && DefaultLabel == 0) {
Warning ("No case labels"); Warning ("No case labels");
} else {
/* Remember the current position */
CodeMark SwitchCodeStart = GetCodePos();
/* Generate code */
g_switch (Nodes, DefaultLabel? DefaultLabel : ExitLabel, Depth);
/* Move the code to the front */
MoveCode (SwitchCodeStart, GetCodePos(), CaseCodeStart);
} }
/* Define the exit label and, if there's a next label left, create this /* Define the exit label */
* one, too. g_defcodelabel (ExitLabel);
*/
if (NextLab) {
g_defcodelabel (NextLab);
}
g_defcodelabel (ExitLab);
/* Eat the closing curly brace */ /* Eat the closing curly brace */
NextToken (); NextToken ();
@@ -240,138 +232,3 @@ static void CascadeSwitch (ExprDesc* Expr)
static void TableSwitch (ExprDesc* Expr)
/* Handle a switch statement via table based selector */
{
/* Entry for one case in a switch statement */
typedef struct {
long Value; /* selector value */
unsigned Label; /* label for this selector */
} SwitchEntry;
unsigned DefaultLabel; /* Label for default case */
unsigned ExitLabel; /* exit label */
int lcase; /* label for compares */
int HaveBreak; /* Last statement has a break */
unsigned Flags; /* Code generator flags */
ExprDesc lval; /* Case label expression */
unsigned I;
SwitchEntry* P;
Collection SwitchTab;
/* Initialize the collection for the switch entries */
InitCollection (&SwitchTab);
/* Create a look so we may break out, init labels */
HaveBreak = 0; /* Keep gcc silent */
DefaultLabel = 0; /* No default case until now */
ExitLabel = GetLocalLabel (); /* get exit */
AddLoop (oursp, 0, ExitLabel, 0, 0);
/* Jump behind the code for the CASE labels */
g_jump (lcase = GetLocalLabel ());
while (CurTok.Tok != TOK_RCURLY) {
if (CurTok.Tok == TOK_CASE || CurTok.Tok == TOK_DEFAULT) {
do {
if (CurTok.Tok == TOK_CASE) {
NextToken ();
constexpr (&lval);
if (!IsClassInt (lval.Type)) {
Error ("Switch quantity not an integer");
}
P = xmalloc (sizeof (SwitchEntry));
P->Value = lval.ConstVal;
P->Label = GetLocalLabel ();
CollAppend (&SwitchTab, P);
g_defcodelabel (P->Label);
} else if (DefaultLabel == 0) {
NextToken ();
DefaultLabel = GetLocalLabel ();
g_defcodelabel (DefaultLabel);
} else {
/* We already had a default label */
Error ("Multiple default labels in one switch");
/* Try to recover */
NextToken ();
}
ConsumeColon ();
} while (CurTok.Tok == TOK_CASE || CurTok.Tok == TOK_DEFAULT);
HaveBreak = 0;
}
if (CurTok.Tok != TOK_RCURLY) {
HaveBreak = Statement (0);
}
}
/* Check if we have any labels */
if (CollCount(&SwitchTab) == 0 && DefaultLabel == 0) {
Warning ("No case labels");
}
/* Eat the closing curly brace */
NextToken ();
/* If the last statement doesn't have a break or return, add one */
if (!HaveBreak) {
g_jump (ExitLabel);
}
/* Actual selector code goes here */
g_defcodelabel (lcase);
/* Create the call to the switch subroutine */
Flags = TypeOf (Expr->Type);
g_switch (Flags);
/* First entry is negative of label count */
g_defdata (CF_INT | CF_CONST, -((int)CollCount(&SwitchTab))-1, 0);
/* Create the case selector table */
for (I = 0; I < CollCount (&SwitchTab); ++I) {
P = CollAt (&SwitchTab, I);
g_case (Flags, P->Label, P->Value); /* Create one label */
}
if (DefaultLabel != 0) {
g_jump (DefaultLabel);
}
g_defcodelabel (ExitLabel);
DelLoop ();
/* Free the allocated space for the labels */
for (I = 0; I < CollCount (&SwitchTab); ++I) {
xfree (CollAt (&SwitchTab, I));
}
/* Free the collection itself */
DoneCollection (&SwitchTab);
}
void SwitchStatement (void)
/* Handle a 'switch' statement */
{
ExprDesc Expr; /* Switch statement expression */
/* Eat the "switch" */
NextToken ();
/* Read the switch expression */
ConsumeLParen ();
intexpr (&Expr);
ConsumeRParen ();
/* result of expr is in P */
ConsumeLCurly ();
/* Now decide which sort of switch we will create: */
if (IsTypeChar (Expr.Type) || (CodeSizeFactor >= 200 && IsClassInt (Expr.Type))) {
CascadeSwitch (&Expr);
} else {
TableSwitch (&Expr);
}
}