mirror of
https://github.com/cc65/cc65.git
synced 2025-01-14 16:33:00 +00:00
Rewrote the switch statement
git-svn-id: svn://svn.cc65.org/cc65/trunk@1021 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
parent
5e7e3d4b81
commit
41d2cc8f91
183
src/cc65/casenode.c
Normal file
183
src/cc65/casenode.c
Normal 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
144
src/cc65/casenode.h
Normal 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
|
||||
|
||||
|
||||
|
@ -47,6 +47,7 @@
|
||||
/* cc65 */
|
||||
#include "asmcode.h"
|
||||
#include "asmlabel.h"
|
||||
#include "casenode.h"
|
||||
#include "codeseg.h"
|
||||
#include "cpu.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)
|
||||
/* 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);
|
||||
|
||||
/* 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 */
|
||||
/*****************************************************************************/
|
||||
@ -3864,7 +3896,7 @@ void g_asmcode (struct StrBuf* B)
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Inlined known functions */
|
||||
/* Inlined known functions */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
@ -38,6 +38,9 @@
|
||||
|
||||
|
||||
|
||||
/* common */
|
||||
#include "coll.h"
|
||||
|
||||
/* cc65 */
|
||||
#include "segments.h"
|
||||
|
||||
@ -376,12 +379,6 @@ void g_callind (unsigned Flags, unsigned ArgSize, int Offs);
|
||||
void g_jump (unsigned Label);
|
||||
/* 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);
|
||||
/* 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 */
|
||||
/*****************************************************************************/
|
||||
|
@ -101,11 +101,11 @@ void CL_MoveRefs (CodeLabel* OldLabel, CodeLabel* NewLabel)
|
||||
*/
|
||||
{
|
||||
/* Walk through all instructions referencing the old label */
|
||||
unsigned Count = CollCount (&OldLabel->JumpFrom);
|
||||
unsigned Count = CL_GetRefCount (OldLabel);
|
||||
while (Count--) {
|
||||
|
||||
/* 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 */
|
||||
CHECK (E->JumpTo == OldLabel);
|
||||
@ -127,3 +127,4 @@ void CL_Output (const CodeLabel* L, FILE* F)
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -26,6 +26,7 @@ OBJS = anonname.o \
|
||||
asmcode.o \
|
||||
asmlabel.o \
|
||||
asmstmt.o \
|
||||
casenode.o \
|
||||
codeent.o \
|
||||
codegen.o \
|
||||
codelab.o \
|
||||
|
@ -71,6 +71,7 @@ OBJS = anonname.obj \
|
||||
asmcode.obj \
|
||||
asmlabel.obj \
|
||||
asmstmt.obj \
|
||||
casenode.obj \
|
||||
codeent.obj \
|
||||
codegen.obj \
|
||||
codelab.obj \
|
||||
@ -104,7 +105,7 @@ OBJS = anonname.obj \
|
||||
lineinfo.obj \
|
||||
litpool.obj \
|
||||
locals.obj \
|
||||
loop.obj \
|
||||
loop.obj \
|
||||
macrotab.obj \
|
||||
main.obj \
|
||||
opcodes.obj \
|
||||
@ -147,6 +148,7 @@ FILE anonname.obj
|
||||
FILE asmcode.obj
|
||||
FILE asmlabel.obj
|
||||
FILE asmstmt.obj
|
||||
FILE casenode.obj
|
||||
FILE codeent.obj
|
||||
FILE codegen.obj
|
||||
FILE codelab.obj
|
||||
|
@ -61,17 +61,6 @@
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Data */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/* Maximum count of cases */
|
||||
#define CASE_MAX 257
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Helper functions */
|
||||
/*****************************************************************************/
|
||||
@ -235,8 +224,8 @@ static void WhileStatement (void)
|
||||
/* Exit label */
|
||||
g_defcodelabel (lab);
|
||||
|
||||
/* Eat remaining tokens that were delayed because of line info
|
||||
* correctness
|
||||
/* Eat remaining tokens that were delayed because of line info
|
||||
* correctness
|
||||
*/
|
||||
SkipPending (PendingToken);
|
||||
|
||||
@ -572,4 +561,4 @@ int Statement (int* PendingToken)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -33,12 +33,16 @@
|
||||
|
||||
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
/* common */
|
||||
#include "coll.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
/* cc65 */
|
||||
#include "asmcode.h"
|
||||
#include "asmlabel.h"
|
||||
#include "casenode.h"
|
||||
#include "codegen.h"
|
||||
#include "datatype.h"
|
||||
#include "error.h"
|
||||
@ -52,184 +56,172 @@
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Code */
|
||||
/* Code */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
static void CascadeSwitch (ExprDesc* Expr)
|
||||
void SwitchStatement (void)
|
||||
/* Handle a switch statement for chars with a cmp cascade for the selector */
|
||||
{
|
||||
unsigned ExitLab; /* Exit label */
|
||||
unsigned NextLab; /* Next case label */
|
||||
unsigned CodeLab; /* Label that starts the actual selector code */
|
||||
int HaveBreak; /* Remember if we exited with break */
|
||||
int HaveDefault; /* Remember if we had a default label */
|
||||
int lcount; /* Label count */
|
||||
unsigned Flags; /* Code generator flags */
|
||||
ExprDesc lval; /* Case label expression */
|
||||
Collection* Nodes; /* CaseNode tree */
|
||||
ExprDesc SwitchExpr; /* Switch statement expression */
|
||||
ExprDesc CaseExpr; /* Case label expression */
|
||||
type SwitchExprType; /* Basic switch expression type */
|
||||
CodeMark CaseCodeStart; /* Start of code marker */
|
||||
unsigned Depth; /* Number of bytes the selector type has */
|
||||
unsigned ExitLabel; /* Exit label */
|
||||
unsigned CaseLabel; /* Label for case */
|
||||
unsigned DefaultLabel; /* Label for the default branch */
|
||||
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 */
|
||||
type ExprType = UnqualifiedType (Expr->Type[0]);
|
||||
SwitchExprType = UnqualifiedType (SwitchExpr.Type[0]);
|
||||
|
||||
/* Create a loop so we may break out, init labels */
|
||||
ExitLab = GetLocalLabel ();
|
||||
AddLoop (oursp, 0, ExitLab, 0, 0);
|
||||
/* Get the number of bytes the selector type has */
|
||||
Depth = SizeOf (SwitchExpr.Type);
|
||||
CHECK (Depth == 1 || Depth == 2 || Depth == 4);
|
||||
|
||||
/* Setup some variables needed in the loop below */
|
||||
Flags = TypeOf (Expr->Type) | CF_CONST | CF_FORCECHAR;
|
||||
CodeLab = NextLab = 0;
|
||||
HaveBreak = 1;
|
||||
HaveDefault = 0;
|
||||
/* Get the exit label for the switch statement */
|
||||
ExitLabel = GetLocalLabel ();
|
||||
|
||||
/* Create a loop so we may use break. */
|
||||
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 */
|
||||
lcount = 0;
|
||||
while (CurTok.Tok != TOK_RCURLY) {
|
||||
|
||||
if (CurTok.Tok == TOK_CASE || CurTok.Tok == TOK_DEFAULT) {
|
||||
while (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);
|
||||
}
|
||||
/* Parse the selector */
|
||||
if (CurTok.Tok == TOK_CASE) {
|
||||
|
||||
/* If we have a cascade label, emit it */
|
||||
if (NextLab) {
|
||||
g_defcodelabel (NextLab);
|
||||
NextLab = 0;
|
||||
}
|
||||
/* Skip the "case" token */
|
||||
NextToken ();
|
||||
|
||||
while (CurTok.Tok == TOK_CASE || CurTok.Tok == TOK_DEFAULT) {
|
||||
|
||||
/* Parse the selector */
|
||||
if (CurTok.Tok == TOK_CASE) {
|
||||
|
||||
/* Count labels */
|
||||
++lcount;
|
||||
|
||||
/* Skip the "case" token */
|
||||
NextToken ();
|
||||
|
||||
/* Read the selector expression */
|
||||
constexpr (&lval);
|
||||
if (!IsClassInt (lval.Type)) {
|
||||
Error ("Switch quantity not an integer");
|
||||
}
|
||||
|
||||
/* Check the range of the expression */
|
||||
Val = lval.ConstVal;
|
||||
switch (ExprType) {
|
||||
|
||||
case T_SCHAR:
|
||||
/* Signed char */
|
||||
if (Val < -128 || Val > 127) {
|
||||
Error ("Range error");
|
||||
}
|
||||
break;
|
||||
|
||||
case T_UCHAR:
|
||||
if (Val < 0 || Val > 255) {
|
||||
Error ("Range error");
|
||||
}
|
||||
break;
|
||||
|
||||
case T_INT:
|
||||
if (Val < -32768 || Val > 32767) {
|
||||
Error ("Range error");
|
||||
}
|
||||
break;
|
||||
|
||||
case T_UINT:
|
||||
if (Val < 0 || Val > 65535) {
|
||||
Error ("Range error");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Internal ("Invalid type: %04X", ExprType);
|
||||
}
|
||||
|
||||
/* Emit a compare */
|
||||
g_cmp (Flags, Val);
|
||||
|
||||
/* If another case follows after the colon (which is
|
||||
* currently pending and cannot be skipped since otherwise
|
||||
* 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 */
|
||||
ConsumeColon ();
|
||||
|
||||
} else {
|
||||
|
||||
/* Default case */
|
||||
NextToken ();
|
||||
|
||||
/* Handle the pathologic case: DEFAULT followed by CASE */
|
||||
if (NextTok.Tok == TOK_CASE) {
|
||||
if (CodeLab == 0) {
|
||||
CodeLab = GetLocalLabel ();
|
||||
}
|
||||
g_jump (CodeLab);
|
||||
}
|
||||
|
||||
/* Skip the colon */
|
||||
ConsumeColon ();
|
||||
|
||||
/* Remember that we had a default label */
|
||||
HaveDefault = 1;
|
||||
/* Read the selector expression */
|
||||
constexpr (&CaseExpr);
|
||||
if (!IsClassInt (CaseExpr.Type)) {
|
||||
Error ("Switch quantity not an integer");
|
||||
}
|
||||
|
||||
}
|
||||
/* Check the range of the expression */
|
||||
Val = CaseExpr.ConstVal;
|
||||
switch (SwitchExprType) {
|
||||
|
||||
}
|
||||
case T_SCHAR:
|
||||
/* Signed char */
|
||||
if (Val < -128 || Val > 127) {
|
||||
Error ("Range error");
|
||||
}
|
||||
break;
|
||||
|
||||
/* Emit a code label if we have one */
|
||||
if (CodeLab) {
|
||||
g_defcodelabel (CodeLab);
|
||||
CodeLab = 0;
|
||||
}
|
||||
case T_UCHAR:
|
||||
if (Val < 0 || Val > 255) {
|
||||
Error ("Range error");
|
||||
}
|
||||
break;
|
||||
|
||||
/* Parse statements */
|
||||
if (CurTok.Tok != TOK_RCURLY) {
|
||||
HaveBreak = Statement (0);
|
||||
}
|
||||
case T_INT:
|
||||
if (Val < -32768 || Val > 32767) {
|
||||
Error ("Range error");
|
||||
}
|
||||
break;
|
||||
|
||||
case T_UINT:
|
||||
if (Val < 0 || Val > 65535) {
|
||||
Error ("Range error");
|
||||
}
|
||||
break;
|
||||
|
||||
case T_LONG:
|
||||
case T_ULONG:
|
||||
break;
|
||||
|
||||
default:
|
||||
Internal ("Invalid type: %04X", SwitchExprType);
|
||||
}
|
||||
|
||||
/* Insert the case selector into the selector table */
|
||||
CaseLabel = InsertCaseValue (Nodes, Val, Depth);
|
||||
|
||||
/* Define this label */
|
||||
g_defcodelabel (CaseLabel);
|
||||
|
||||
/* Skip the colon */
|
||||
ConsumeColon ();
|
||||
|
||||
} else {
|
||||
|
||||
/* Default case */
|
||||
NextToken ();
|
||||
|
||||
/* Check if we do already have a default branch */
|
||||
if (DefaultLabel == 0) {
|
||||
|
||||
/* Generate and emit the default label */
|
||||
DefaultLabel = GetLocalLabel ();
|
||||
g_defcodelabel (DefaultLabel);
|
||||
|
||||
} else {
|
||||
/* We had the default label already */
|
||||
Error ("Duplicate `default' case");
|
||||
}
|
||||
|
||||
/* Skip the colon */
|
||||
ConsumeColon ();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Parse statements */
|
||||
if (CurTok.Tok != TOK_RCURLY) {
|
||||
Statement (0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if we have any labels */
|
||||
if (lcount == 0 && !HaveDefault) {
|
||||
Warning ("No case labels");
|
||||
/* Check if we had any labels */
|
||||
if (CollCount (Nodes) == 0 && DefaultLabel == 0) {
|
||||
|
||||
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
|
||||
* one, too.
|
||||
*/
|
||||
if (NextLab) {
|
||||
g_defcodelabel (NextLab);
|
||||
}
|
||||
g_defcodelabel (ExitLab);
|
||||
/* Define the exit label */
|
||||
g_defcodelabel (ExitLabel);
|
||||
|
||||
/* Eat the closing curly brace */
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user