Rewrote the switch statement
git-svn-id: svn://svn.cc65.org/cc65/trunk@1021 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
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 */
|
/* 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 */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@@ -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 */
|
/* 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 */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|||||||
@@ -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)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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 \
|
||||||
|
|||||||
@@ -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 \
|
||||||
@@ -104,7 +105,7 @@ OBJS = anonname.obj \
|
|||||||
lineinfo.obj \
|
lineinfo.obj \
|
||||||
litpool.obj \
|
litpool.obj \
|
||||||
locals.obj \
|
locals.obj \
|
||||||
loop.obj \
|
loop.obj \
|
||||||
macrotab.obj \
|
macrotab.obj \
|
||||||
main.obj \
|
main.obj \
|
||||||
opcodes.obj \
|
opcodes.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
|
||||||
|
|||||||
@@ -61,17 +61,6 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* Data */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Maximum count of cases */
|
|
||||||
#define CASE_MAX 257
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* Helper functions */
|
/* Helper functions */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -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 */
|
/* 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) {
|
while (CurTok.Tok == TOK_CASE || CurTok.Tok == TOK_DEFAULT) {
|
||||||
|
|
||||||
/* If the code for the previous selector did not end with a
|
/* Parse the selector */
|
||||||
* break statement, we must jump over the next selector test.
|
if (CurTok.Tok == TOK_CASE) {
|
||||||
*/
|
|
||||||
if (!HaveBreak) {
|
|
||||||
/* Define a label for the code */
|
|
||||||
if (CodeLab == 0) {
|
|
||||||
CodeLab = GetLocalLabel ();
|
|
||||||
}
|
|
||||||
g_jump (CodeLab);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we have a cascade label, emit it */
|
/* Skip the "case" token */
|
||||||
if (NextLab) {
|
NextToken ();
|
||||||
g_defcodelabel (NextLab);
|
|
||||||
NextLab = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (CurTok.Tok == TOK_CASE || CurTok.Tok == TOK_DEFAULT) {
|
/* Read the selector expression */
|
||||||
|
constexpr (&CaseExpr);
|
||||||
/* Parse the selector */
|
if (!IsClassInt (CaseExpr.Type)) {
|
||||||
if (CurTok.Tok == TOK_CASE) {
|
Error ("Switch quantity not an integer");
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
/* 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 */
|
case T_UCHAR:
|
||||||
if (CodeLab) {
|
if (Val < 0 || Val > 255) {
|
||||||
g_defcodelabel (CodeLab);
|
Error ("Range error");
|
||||||
CodeLab = 0;
|
}
|
||||||
}
|
break;
|
||||||
|
|
||||||
/* Parse statements */
|
case T_INT:
|
||||||
if (CurTok.Tok != TOK_RCURLY) {
|
if (Val < -32768 || Val > 32767) {
|
||||||
HaveBreak = Statement (0);
|
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 */
|
/* 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user