4589 lines
136 KiB
C
4589 lines
136 KiB
C
/*-------------------------------------------------------------------------
|
|
SDCCsymt.c - Code file for Symbols table related structures and MACRO's.
|
|
Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998)
|
|
|
|
This program is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License as published by the
|
|
Free Software Foundation; either version 2, or (at your option) any
|
|
later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
In other words, you are welcome to use, share and improve this program.
|
|
You are forbidden to forbid anyone else to use, share and improve
|
|
what you give them. Help stamp out software-hoarding!
|
|
-------------------------------------------------------------------------*/
|
|
|
|
#include "common.h"
|
|
#include "newalloc.h"
|
|
#include "dbuf_string.h"
|
|
|
|
#include "SDCCsymt.h"
|
|
|
|
value *aggregateToPointer (value * val);
|
|
void printTypeChainRaw (sym_link * start, FILE * of);
|
|
|
|
void
|
|
printFromToType (sym_link * from, sym_link * to)
|
|
{
|
|
struct dbuf_s dbuf;
|
|
dbuf_init (&dbuf, 1024);
|
|
dbuf_append_str (&dbuf, "from type '");
|
|
dbuf_printTypeChain (from, &dbuf);
|
|
dbuf_append_str (&dbuf, "'\n to type '");
|
|
dbuf_printTypeChain (to, &dbuf);
|
|
dbuf_append_str (&dbuf, "'\n");
|
|
dbuf_write_and_destroy (&dbuf, stderr);
|
|
}
|
|
|
|
/* noun strings */
|
|
char *
|
|
nounName (sym_link * sl)
|
|
{
|
|
switch (SPEC_NOUN (sl))
|
|
{
|
|
case V_INT:
|
|
{
|
|
if (SPEC_LONGLONG (sl))
|
|
return "long long";
|
|
if (SPEC_LONG (sl))
|
|
return "long";
|
|
if (SPEC_SHORT (sl))
|
|
return "short";
|
|
return "int";
|
|
}
|
|
case V_FLOAT:
|
|
return "float";
|
|
case V_FIXED16X16:
|
|
return "fixed16x16";
|
|
case V_BOOL:
|
|
return "_Bool";
|
|
case V_CHAR:
|
|
return "char";
|
|
case V_VOID:
|
|
return "void";
|
|
case V_STRUCT:
|
|
return "struct";
|
|
case V_LABEL:
|
|
return "label";
|
|
case V_BITFIELD:
|
|
return "bitfield";
|
|
case V_BBITFIELD:
|
|
return "_Boolbitfield";
|
|
case V_BIT:
|
|
return "bit";
|
|
case V_SBIT:
|
|
return "sbit";
|
|
case V_DOUBLE:
|
|
return "double";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
bucket *SymbolTab[256]; /* the symbol table */
|
|
bucket *StructTab[256]; /* the structure table */
|
|
bucket *TypedefTab[256]; /* the typedef table */
|
|
bucket *LabelTab[256]; /* the Label table */
|
|
bucket *enumTab[256]; /* enumerated table */
|
|
bucket *AddrspaceTab[256]; /* the named address space table */
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* initSymt () - initialises symbol table related stuff */
|
|
/*------------------------------------------------------------------*/
|
|
void
|
|
initSymt (void)
|
|
{
|
|
int i = 0;
|
|
|
|
for (i = 0; i < 256; i++)
|
|
SymbolTab[i] = StructTab[i] = (void *) NULL;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------*/
|
|
/* newBucket - allocates & returns a new bucket */
|
|
/*-----------------------------------------------------------------*/
|
|
bucket *
|
|
newBucket (void)
|
|
{
|
|
bucket *bp;
|
|
|
|
bp = Safe_alloc (sizeof (bucket));
|
|
|
|
return bp;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------*/
|
|
/* hashKey - computes the hashkey given a symbol name */
|
|
/*-----------------------------------------------------------------*/
|
|
int
|
|
hashKey (const char *s)
|
|
{
|
|
unsigned long key = 0;
|
|
|
|
while (*s)
|
|
key += *s++;
|
|
return key % 256;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------*/
|
|
/* addSym - adds a symbol to the hash Table */
|
|
/*-----------------------------------------------------------------*/
|
|
void
|
|
addSym (bucket ** stab, void *sym, char *sname, long level, int block, int checkType)
|
|
{
|
|
int i; /* index into the hash Table */
|
|
bucket *bp; /* temp bucket * */
|
|
|
|
if (checkType)
|
|
{
|
|
symbol *csym = (symbol *) sym;
|
|
|
|
if (getenv ("DEBUG_SANITY"))
|
|
{
|
|
fprintf (stderr, "addSym: %s ", sname);
|
|
}
|
|
/* make sure the type is complete and sane */
|
|
checkTypeSanity (csym->etype, csym->name);
|
|
}
|
|
|
|
/* prevent overflow of the (r)name buffers */
|
|
if (strlen (sname) > SDCC_SYMNAME_MAX)
|
|
{
|
|
werror (W_SYMBOL_NAME_TOO_LONG, SDCC_SYMNAME_MAX);
|
|
sname[SDCC_SYMNAME_MAX] = '\0';
|
|
}
|
|
|
|
/* the symbols are always added at the head of the list */
|
|
i = hashKey (sname);
|
|
/* get a free entry */
|
|
bp = Safe_alloc (sizeof (bucket));
|
|
|
|
bp->sym = sym; /* update the symbol pointer */
|
|
bp->level = level; /* update the nest level */
|
|
bp->block = block;
|
|
strncpyz (bp->name, sname, sizeof (bp->name)); /* copy the name into place */
|
|
|
|
/* if this is the first entry */
|
|
if (stab[i] == NULL)
|
|
{
|
|
bp->prev = bp->next = (void *) NULL; /* point to nothing */
|
|
stab[i] = bp;
|
|
}
|
|
/* not first entry then add @ head of list */
|
|
else
|
|
{
|
|
bp->prev = NULL;
|
|
stab[i]->prev = bp;
|
|
bp->next = stab[i];
|
|
stab[i] = bp;
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------------*/
|
|
/* deleteSym - deletes a symbol from the hash Table entry */
|
|
/*-----------------------------------------------------------------*/
|
|
void
|
|
deleteSym (bucket ** stab, void *sym, const char *sname)
|
|
{
|
|
int i = 0;
|
|
bucket *bp;
|
|
|
|
i = hashKey (sname);
|
|
|
|
bp = stab[i];
|
|
/* find the symbol */
|
|
while (bp)
|
|
{
|
|
if (bp->sym == sym) /* found it then break out */
|
|
break; /* of the loop */
|
|
bp = bp->next;
|
|
}
|
|
|
|
if (!bp) /* did not find it */
|
|
return;
|
|
|
|
/* if this is the first one in the chain */
|
|
if (!bp->prev)
|
|
{
|
|
stab[i] = bp->next;
|
|
if (stab[i]) /* if chain ! empty */
|
|
stab[i]->prev = (void *) NULL;
|
|
}
|
|
/* middle || end of chain */
|
|
else
|
|
{
|
|
if (bp->next) /* if not end of chain */
|
|
bp->next->prev = bp->prev;
|
|
|
|
bp->prev->next = bp->next;
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------------*/
|
|
/* findSym - finds a symbol in a table */
|
|
/*-----------------------------------------------------------------*/
|
|
void *
|
|
findSym (bucket ** stab, void *sym, const char *sname)
|
|
{
|
|
bucket *bp;
|
|
|
|
bp = stab[hashKey (sname)];
|
|
while (bp)
|
|
{
|
|
if (bp->sym == sym || strcmp (bp->name, sname) == 0)
|
|
break;
|
|
bp = bp->next;
|
|
}
|
|
|
|
return (bp ? bp->sym : (void *) NULL);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------*/
|
|
/* findSymWithLevel - finds a symbol with a name & level */
|
|
/*-----------------------------------------------------------------*/
|
|
void *
|
|
findSymWithLevel (bucket ** stab, symbol * sym)
|
|
{
|
|
bucket *bp;
|
|
|
|
if (!sym)
|
|
return sym;
|
|
|
|
bp = stab[hashKey (sym->name)];
|
|
|
|
/**
|
|
** do the search from the head of the list since the
|
|
** elements are added at the head it is ensured that
|
|
** we will find the deeper definitions before we find
|
|
** the global ones. we need to check for symbols with
|
|
** level <= to the level given, if levels match then block
|
|
** numbers need to match as well
|
|
**/
|
|
while (bp)
|
|
{
|
|
if (strcmp (bp->name, sym->name) == 0 && bp->level <= sym->level)
|
|
{
|
|
/* if this is parameter then nothing else need to be checked */
|
|
if (((symbol *) (bp->sym))->_isparm)
|
|
return (bp->sym);
|
|
/* if levels match then block numbers should also match */
|
|
if (bp->level && bp->level == sym->level && bp->block == sym->block
|
|
&& ((symbol *)(bp->sym))->seqPoint <= sym->seqPoint)
|
|
return (bp->sym);
|
|
/* if levels don't match then we are okay if the symbol is in scope */
|
|
if (bp->level && bp->level != sym->level && bp->block <= sym->block
|
|
&& ((symbol *) (bp->sym))->isinscope
|
|
&& (stab == LabelTab || ((symbol *)(bp->sym))->seqPoint <= sym->seqPoint))
|
|
return (bp->sym);
|
|
/* if this is a global variable then we are ok too */
|
|
if (bp->level == 0)
|
|
return (bp->sym);
|
|
}
|
|
|
|
bp = bp->next;
|
|
}
|
|
|
|
return (void *) NULL;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------*/
|
|
/* findSymWithBlock - finds a symbol with name in a block */
|
|
/*-----------------------------------------------------------------*/
|
|
void *
|
|
findSymWithBlock (bucket ** stab, symbol * sym, int block, long level)
|
|
{
|
|
bucket *bp;
|
|
|
|
if (!sym)
|
|
return sym;
|
|
|
|
bp = stab[hashKey (sym->name)];
|
|
while (bp)
|
|
{
|
|
if (strcmp (bp->name, sym->name) == 0 && (bp->block == block || (bp->block < block && bp->level < level)))
|
|
break;
|
|
bp = bp->next;
|
|
}
|
|
|
|
return (bp ? bp->sym : (void *) NULL);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* newSymbol () - returns a new pointer to a symbol */
|
|
/*------------------------------------------------------------------*/
|
|
symbol *
|
|
newSymbol (const char *name, long scope)
|
|
{
|
|
symbol *sym;
|
|
|
|
sym = Safe_alloc (sizeof (symbol));
|
|
|
|
strncpyz (sym->name, name, sizeof (sym->name)); /* copy the name */
|
|
sym->level = scope; /* set the level */
|
|
sym->block = currBlockno;
|
|
sym->seqPoint = seqPointNo;
|
|
sym->lineDef = lexLineno; /* set the line number */
|
|
sym->fileDef = lexFilename;
|
|
sym->for_newralloc = 0;
|
|
sym->isinscope = 1;
|
|
sym->usl.spillLoc = 0;
|
|
#warning Please check this
|
|
sym->funcDivFlagSafe = 1;
|
|
sym->funcUsesVolatile = 1;
|
|
|
|
return sym;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* newLink - creates a new link (declarator,specifier) */
|
|
/*------------------------------------------------------------------*/
|
|
sym_link *
|
|
newLink (SYM_LINK_CLASS select)
|
|
{
|
|
sym_link *p;
|
|
|
|
p = Safe_alloc (sizeof (sym_link));
|
|
p->xclass = select;
|
|
p->funcAttrs.z88dk_params_offset = 0;
|
|
|
|
return p;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* newStruct - creats a new structdef from the free list */
|
|
/*------------------------------------------------------------------*/
|
|
structdef *
|
|
newStruct (const char *tag)
|
|
{
|
|
structdef *s;
|
|
|
|
s = Safe_alloc (sizeof (structdef));
|
|
|
|
strncpyz (s->tag, tag, sizeof (s->tag)); /* copy the tag */
|
|
return s;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* sclsFromPtr - Return the storage class a pointer points into. */
|
|
/* S_FIXED is returned for generic pointers or other */
|
|
/* unexpected cases */
|
|
/*------------------------------------------------------------------*/
|
|
STORAGE_CLASS
|
|
sclsFromPtr (sym_link * ptr)
|
|
{
|
|
switch (DCL_TYPE (ptr))
|
|
{
|
|
case POINTER:
|
|
return S_DATA;
|
|
case GPOINTER:
|
|
return S_FIXED;
|
|
case FPOINTER:
|
|
return S_XDATA;
|
|
case CPOINTER:
|
|
return S_CODE;
|
|
case IPOINTER:
|
|
return S_IDATA;
|
|
case PPOINTER:
|
|
return S_PDATA;
|
|
case EEPPOINTER:
|
|
return S_EEPROM;
|
|
case FUNCTION:
|
|
return S_CODE;
|
|
default:
|
|
return S_FIXED;
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* pointerTypes - do the computation for the pointer types */
|
|
/*------------------------------------------------------------------*/
|
|
void
|
|
pointerTypes (sym_link * ptr, sym_link * type)
|
|
{
|
|
sym_link *p;
|
|
sym_link *etype;
|
|
|
|
if (IS_SPEC (ptr))
|
|
return;
|
|
|
|
/* find the last unknown pointer type */
|
|
p = ptr;
|
|
while (p)
|
|
{
|
|
if (IS_PTR (p) && DCL_TYPE (p) == UPOINTER)
|
|
ptr = p;
|
|
p = p->next;
|
|
}
|
|
|
|
/* could not find it */
|
|
if (!ptr || IS_SPEC (ptr) || !IS_PTR (ptr))
|
|
return;
|
|
|
|
if (IS_PTR (ptr) && DCL_TYPE (ptr) != UPOINTER)
|
|
{
|
|
pointerTypes (ptr->next, type);
|
|
return;
|
|
}
|
|
|
|
/* change the pointer type depending on the
|
|
storage class of the etype */
|
|
etype = getSpec (type);
|
|
if (IS_SPEC (etype))
|
|
{
|
|
switch (SPEC_SCLS (etype))
|
|
{
|
|
case S_XDATA:
|
|
DCL_TYPE (ptr) = FPOINTER;
|
|
break;
|
|
case S_IDATA:
|
|
DCL_TYPE (ptr) = IPOINTER;
|
|
break;
|
|
case S_PDATA:
|
|
DCL_TYPE (ptr) = PPOINTER;
|
|
break;
|
|
case S_DATA:
|
|
DCL_TYPE (ptr) = POINTER;
|
|
break;
|
|
case S_CODE:
|
|
DCL_TYPE (ptr) = CPOINTER;
|
|
break;
|
|
case S_EEPROM:
|
|
DCL_TYPE (ptr) = EEPPOINTER;
|
|
break;
|
|
default:
|
|
DCL_TYPE (ptr) = port->unqualified_pointer;
|
|
break;
|
|
}
|
|
/* the storage class of etype ends here */
|
|
SPEC_SCLS (etype) = 0;
|
|
}
|
|
|
|
/* now change all the remaining unknown pointers
|
|
to generic pointers */
|
|
while (ptr)
|
|
{
|
|
if (!IS_SPEC (ptr) && DCL_TYPE (ptr) == UPOINTER)
|
|
DCL_TYPE (ptr) = port->unqualified_pointer;
|
|
ptr = ptr->next;
|
|
}
|
|
|
|
/* same for the type although it is highly unlikely that
|
|
type will have a pointer */
|
|
while (type)
|
|
{
|
|
if (!IS_SPEC (type) && DCL_TYPE (type) == UPOINTER)
|
|
DCL_TYPE (type) = port->unqualified_pointer;
|
|
type = type->next;
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* addDecl - adds a declarator @ the end of a chain */
|
|
/*------------------------------------------------------------------*/
|
|
void
|
|
addDecl (symbol * sym, int type, sym_link * p)
|
|
{
|
|
static sym_link *empty = NULL;
|
|
sym_link *head;
|
|
sym_link *tail;
|
|
sym_link *t;
|
|
|
|
if (getenv ("SDCC_DEBUG_FUNCTION_POINTERS"))
|
|
fprintf (stderr, "SDCCsymt.c:addDecl(%s,%d,%p)\n", sym->name, type, (void *)p);
|
|
|
|
if (empty == NULL)
|
|
empty = newLink (SPECIFIER);
|
|
|
|
/* if we are passed a link then set head & tail */
|
|
if (p)
|
|
{
|
|
tail = head = p;
|
|
while (tail->next)
|
|
tail = tail->next;
|
|
}
|
|
else
|
|
{
|
|
head = tail = newLink (DECLARATOR);
|
|
DCL_TYPE (head) = type;
|
|
}
|
|
|
|
/* if this is the first entry */
|
|
if (!sym->type)
|
|
{
|
|
sym->type = head;
|
|
sym->etype = tail;
|
|
}
|
|
else if (IS_SPEC (sym->etype) && IS_SPEC (head) && head == tail)
|
|
{
|
|
sym->etype = mergeSpec (sym->etype, head, sym->name);
|
|
}
|
|
else if (IS_SPEC (sym->etype) && !IS_SPEC (head) && head == tail)
|
|
{
|
|
t = sym->type;
|
|
while (t->next != sym->etype)
|
|
t = t->next;
|
|
t->next = head;
|
|
tail->next = sym->etype;
|
|
}
|
|
else if (IS_FUNC (sym->type) && IS_SPEC (sym->type->next) && !memcmp (sym->type->next, empty, sizeof (sym_link)))
|
|
{
|
|
sym->type->next = head;
|
|
sym->etype = tail;
|
|
}
|
|
else
|
|
{
|
|
sym->etype->next = head;
|
|
sym->etype = tail;
|
|
}
|
|
|
|
/* if the type is an unknown pointer and has
|
|
a tspec then take the storage class const & volatile
|
|
attribute from the tspec & make it those of this
|
|
symbol */
|
|
if (p && !IS_SPEC (p) &&
|
|
//DCL_TYPE (p) == UPOINTER &&
|
|
DCL_TSPEC (p))
|
|
{
|
|
if (!IS_SPEC (sym->etype))
|
|
{
|
|
sym->etype = sym->etype->next = newLink (SPECIFIER);
|
|
}
|
|
SPEC_SCLS (sym->etype) = SPEC_SCLS (DCL_TSPEC (p));
|
|
SPEC_ABSA (sym->etype) |= SPEC_ABSA (DCL_TSPEC (p));
|
|
SPEC_ADDR (sym->etype) |= SPEC_ADDR (DCL_TSPEC (p));
|
|
DCL_TSPEC (p) = NULL;
|
|
}
|
|
|
|
// if there is a function in this type chain
|
|
if (p && funcInChain (sym->type))
|
|
{
|
|
processFuncArgs (sym);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*------------------------------------------------------------------
|
|
checkTypeSanity: prevent the user from doing e.g.:
|
|
unsigned float uf;
|
|
------------------------------------------------------------------*/
|
|
void
|
|
checkTypeSanity (sym_link *etype, const char *name)
|
|
{
|
|
char *noun;
|
|
|
|
if (!etype)
|
|
{
|
|
if (getenv ("DEBUG_SANITY"))
|
|
{
|
|
fprintf (stderr, "sanity check skipped for %s (etype==0)\n", name);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!IS_SPEC (etype))
|
|
{
|
|
if (getenv ("DEBUG_SANITY"))
|
|
{
|
|
fprintf (stderr, "sanity check skipped for %s (!IS_SPEC)\n", name);
|
|
}
|
|
return;
|
|
}
|
|
|
|
noun = nounName (etype);
|
|
|
|
if (getenv ("DEBUG_SANITY"))
|
|
{
|
|
fprintf (stderr, "checking sanity for %s %p\n", name, (void *)etype);
|
|
}
|
|
|
|
if ((SPEC_NOUN (etype) == V_BOOL ||
|
|
SPEC_NOUN (etype) == V_CHAR ||
|
|
SPEC_NOUN (etype) == V_FLOAT ||
|
|
SPEC_NOUN (etype) == V_FIXED16X16 ||
|
|
SPEC_NOUN (etype) == V_DOUBLE || SPEC_NOUN (etype) == V_VOID) && (SPEC_SHORT (etype) || SPEC_LONG (etype) || SPEC_LONGLONG (etype)))
|
|
{ // long or short for char float double or void
|
|
werror (E_LONG_OR_SHORT_INVALID, noun, name);
|
|
}
|
|
if ((SPEC_NOUN (etype) == V_BOOL ||
|
|
SPEC_NOUN (etype) == V_FLOAT ||
|
|
SPEC_NOUN (etype) == V_FIXED16X16 ||
|
|
SPEC_NOUN (etype) == V_DOUBLE || SPEC_NOUN (etype) == V_VOID) && (etype->select.s.b_signed || SPEC_USIGN (etype)))
|
|
{ // signed or unsigned for float double or void
|
|
werror (E_SIGNED_OR_UNSIGNED_INVALID, noun, name);
|
|
}
|
|
|
|
// special case for "short"
|
|
if (SPEC_SHORT (etype))
|
|
{
|
|
SPEC_NOUN (etype) = V_INT;
|
|
SPEC_SHORT (etype) = 0;
|
|
}
|
|
|
|
/* if no noun e.g.
|
|
"const a;" or "data b;" or "signed s" or "long l"
|
|
assume an int */
|
|
if (!SPEC_NOUN (etype))
|
|
{
|
|
SPEC_NOUN (etype) = V_INT;
|
|
if (!(SPEC_SHORT (etype) || SPEC_LONG (etype) || SPEC_LONGLONG (etype) || SPEC_SIGN (etype) || SPEC_USIGN (etype)))
|
|
werror (options.std_c99 ? E_NO_TYPE_SPECIFIER : W_NO_TYPE_SPECIFIER, name);
|
|
}
|
|
|
|
/* ISO/IEC 9899 J.3.9 implementation defined behaviour: */
|
|
/* a "plain" int bitfield is unsigned */
|
|
if (SPEC_NOUN (etype) == V_BIT || SPEC_NOUN (etype) == V_SBIT)
|
|
{
|
|
if (!etype->select.s.b_signed)
|
|
SPEC_USIGN (etype) = 1;
|
|
}
|
|
|
|
if (etype->select.s.b_signed && SPEC_USIGN (etype))
|
|
{ // signed AND unsigned
|
|
werror (E_SIGNED_AND_UNSIGNED_INVALID, noun, name);
|
|
}
|
|
if (SPEC_SHORT (etype) && SPEC_LONG (etype))
|
|
{ // short AND long
|
|
werror (E_LONG_AND_SHORT_INVALID, noun, name);
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* finalizeSpec */
|
|
/* currently just a V_CHAR is forced to be unsigned */
|
|
/* when it's neither signed nor unsigned */
|
|
/* unless the --fsigned-char command line switch is active */
|
|
/*------------------------------------------------------------------*/
|
|
sym_link *
|
|
finalizeSpec (sym_link * lnk)
|
|
{
|
|
sym_link *p = lnk;
|
|
while (p && !IS_SPEC (p))
|
|
p = p->next;
|
|
if (SPEC_NOUN (p) == V_CHAR && !SPEC_USIGN (p) && !p->select.s.b_signed)
|
|
{
|
|
SPEC_USIGN (p) = !options.signed_char;
|
|
p->select.s.b_implicit_sign = true;
|
|
}
|
|
return lnk;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* mergeSpec - merges two specifiers and returns the new one */
|
|
/*------------------------------------------------------------------*/
|
|
sym_link *
|
|
mergeSpec (sym_link * dest, sym_link * src, const char *name)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (!IS_SPEC (dest) || !IS_SPEC (src))
|
|
{
|
|
#if 0
|
|
werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "cannot merge declarator");
|
|
exit (1);
|
|
#else
|
|
werror (E_SYNTAX_ERROR, yytext);
|
|
// the show must go on
|
|
return newIntLink ();
|
|
#endif
|
|
}
|
|
|
|
if (!options.std_c11 && !options.std_c99)
|
|
{
|
|
if (SPEC_SIGN (dest) && SPEC_SIGN (src))
|
|
werror (W_REPEAT_QUALIFIER, "signed");
|
|
if (SPEC_USIGN (dest) && SPEC_USIGN (src))
|
|
werror (W_REPEAT_QUALIFIER, "unsigned");
|
|
if (SPEC_CONST (dest) && SPEC_CONST (src))
|
|
werror (W_REPEAT_QUALIFIER, "const");
|
|
if (SPEC_VOLATILE (dest) && SPEC_VOLATILE (src))
|
|
werror (W_REPEAT_QUALIFIER, "volatile");
|
|
if (SPEC_STAT (dest) && SPEC_STAT (src))
|
|
werror (W_REPEAT_QUALIFIER, "static");
|
|
if (SPEC_EXTR (dest) && SPEC_EXTR (src))
|
|
werror (W_REPEAT_QUALIFIER, "extern");
|
|
if (SPEC_TYPEDEF (dest) && SPEC_TYPEDEF (src))
|
|
werror (W_REPEAT_QUALIFIER, "typedef");
|
|
if (SPEC_SCLS (dest) == S_REGISTER && SPEC_SCLS (src) == S_REGISTER)
|
|
werror (W_REPEAT_QUALIFIER, "register");
|
|
if (SPEC_SCLS (dest) == S_AUTO && SPEC_SCLS (src) == S_AUTO)
|
|
werror (W_REPEAT_QUALIFIER, "auto");
|
|
}
|
|
|
|
if (SPEC_NOUN (src))
|
|
{
|
|
if (!SPEC_NOUN (dest))
|
|
{
|
|
SPEC_NOUN (dest) = SPEC_NOUN (src);
|
|
}
|
|
else
|
|
{
|
|
/* we shouldn't redeclare the type */
|
|
if (getenv ("DEBUG_SANITY"))
|
|
{
|
|
fprintf (stderr, "mergeSpec: ");
|
|
}
|
|
werror (E_TWO_OR_MORE_DATA_TYPES, name);
|
|
}
|
|
}
|
|
|
|
if ((SPEC_SHORT (src) || SPEC_LONG (src) || SPEC_LONGLONG (src)) &&
|
|
(SPEC_SHORT (dest) || SPEC_LONG (dest) || SPEC_LONGLONG (dest)))
|
|
{
|
|
if (!(options.std_c99 && SPEC_LONG (src) && SPEC_LONG (dest) && !TARGET_PIC_LIKE)) /* C99 has long long */
|
|
werror (E_SHORTLONG, name);
|
|
}
|
|
|
|
if (SPEC_SCLS (src))
|
|
{
|
|
/* if destination has no storage class */
|
|
if (!SPEC_SCLS (dest))
|
|
{
|
|
SPEC_SCLS (dest) = SPEC_SCLS (src);
|
|
}
|
|
else if (SPEC_SCLS (dest) == S_REGISTER && SPEC_SCLS (src) != S_AUTO)
|
|
{
|
|
SPEC_SCLS (dest) = SPEC_SCLS (src);
|
|
}
|
|
else
|
|
{
|
|
if (getenv ("DEBUG_SANITY"))
|
|
{
|
|
fprintf (stderr, "mergeSpec: ");
|
|
}
|
|
werror (E_TWO_OR_MORE_STORAGE_CLASSES, name);
|
|
}
|
|
}
|
|
|
|
/* copy all the specifications */
|
|
|
|
// we really should do:
|
|
#if 0
|
|
if (SPEC_what (src))
|
|
{
|
|
if (SPEC_what (dest))
|
|
{
|
|
werror (W_DUPLICATE_SPEC, "what");
|
|
}
|
|
SPEC_what (dst) |= SPEC_what (src);
|
|
}
|
|
#endif
|
|
// but there are more important thing right now
|
|
|
|
if (options.std_c99 && SPEC_LONG (src) && SPEC_LONG (dest))
|
|
{
|
|
SPEC_LONG (dest) = 0;
|
|
SPEC_LONGLONG (dest) = 1;
|
|
}
|
|
else
|
|
SPEC_LONG (dest) |= SPEC_LONG (src);
|
|
SPEC_LONGLONG (dest) |= SPEC_LONGLONG (src);
|
|
SPEC_SHORT (dest) |= SPEC_SHORT (src);
|
|
SPEC_USIGN (dest) |= SPEC_USIGN (src);
|
|
dest->select.s.b_signed |= src->select.s.b_signed;
|
|
SPEC_STAT (dest) |= SPEC_STAT (src);
|
|
SPEC_EXTR (dest) |= SPEC_EXTR (src);
|
|
SPEC_INLINE (dest) |= SPEC_INLINE (src);
|
|
SPEC_NORETURN (dest) |= SPEC_NORETURN(src);
|
|
SPEC_CONST (dest) |= SPEC_CONST (src);
|
|
SPEC_ABSA (dest) |= SPEC_ABSA (src);
|
|
SPEC_VOLATILE (dest) |= SPEC_VOLATILE (src);
|
|
SPEC_RESTRICT (dest) |= SPEC_RESTRICT (src);
|
|
SPEC_ADDR (dest) |= SPEC_ADDR (src);
|
|
SPEC_OCLS (dest) = SPEC_OCLS (src);
|
|
SPEC_BLEN (dest) |= SPEC_BLEN (src);
|
|
SPEC_BSTR (dest) |= SPEC_BSTR (src);
|
|
SPEC_TYPEDEF (dest) |= SPEC_TYPEDEF (src);
|
|
SPEC_ENUM (dest) |= SPEC_ENUM (src);
|
|
if (SPEC_ARGREG (src) && !SPEC_ARGREG (dest))
|
|
SPEC_ARGREG (dest) = SPEC_ARGREG (src);
|
|
|
|
if (SPEC_STAT (dest) && SPEC_EXTR (dest))
|
|
werror (E_TWO_OR_MORE_STORAGE_CLASSES, name);
|
|
|
|
if (IS_STRUCT (dest) && SPEC_STRUCT (dest) == NULL)
|
|
SPEC_STRUCT (dest) = SPEC_STRUCT (src);
|
|
|
|
if (FUNC_ISISR (dest) && FUNC_ISISR (src))
|
|
werror (E_INT_MULTIPLE, name);
|
|
|
|
/* these are the only function attributes that will be set
|
|
in a specifier while parsing */
|
|
FUNC_NONBANKED (dest) |= FUNC_NONBANKED (src);
|
|
FUNC_BANKED (dest) |= FUNC_BANKED (src);
|
|
FUNC_ISCRITICAL (dest) |= FUNC_ISCRITICAL (src);
|
|
FUNC_ISREENT (dest) |= FUNC_ISREENT (src);
|
|
FUNC_ISNAKED (dest) |= FUNC_ISNAKED (src);
|
|
FUNC_ISISR (dest) |= FUNC_ISISR (src);
|
|
FUNC_ISJAVANATIVE (dest) |= FUNC_ISJAVANATIVE (src);
|
|
FUNC_ISBUILTIN (dest) |= FUNC_ISBUILTIN (src);
|
|
FUNC_ISOVERLAY (dest) |= FUNC_ISOVERLAY (src);
|
|
FUNC_INTNO (dest) |= FUNC_INTNO (src);
|
|
FUNC_REGBANK (dest) |= FUNC_REGBANK (src);
|
|
FUNC_ISINLINE (dest) |= FUNC_ISINLINE (src);
|
|
FUNC_ISNORETURN (dest) |= FUNC_ISNORETURN (src);
|
|
FUNC_ISSMALLC (dest) |= FUNC_ISSMALLC (src);
|
|
FUNC_ISZ88DK_FASTCALL (dest) |= FUNC_ISZ88DK_FASTCALL (src);
|
|
FUNC_ISZ88DK_CALLEE (dest) |= FUNC_ISZ88DK_CALLEE (src);
|
|
for (i = 0; i < 9; i++)
|
|
dest->funcAttrs.preserved_regs[i] |= src->funcAttrs.preserved_regs[i];
|
|
|
|
if (SPEC_ADDRSPACE (src) && SPEC_ADDRSPACE (dest))
|
|
werror (E_TWO_OR_MORE_STORAGE_CLASSES, name);
|
|
if (SPEC_ADDRSPACE (src))
|
|
SPEC_ADDRSPACE (dest) = SPEC_ADDRSPACE (src);
|
|
|
|
if (SPEC_ALIGNAS (src) > SPEC_ALIGNAS (dest))
|
|
SPEC_ALIGNAS (dest) = SPEC_ALIGNAS (src);
|
|
if (SPEC_SCLS (dest) == S_REGISTER && SPEC_ALIGNAS (dest))
|
|
werror (E_ALIGNAS, SPEC_ALIGNAS (dest));
|
|
|
|
return dest;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* mergeDeclSpec - merges a specifier and a declarator */
|
|
/*------------------------------------------------------------------*/
|
|
sym_link *
|
|
mergeDeclSpec (sym_link * dest, sym_link * src, const char *name)
|
|
{
|
|
sym_link *decl, *spec, *lnk;
|
|
|
|
if (IS_SPEC (src))
|
|
{
|
|
if (IS_SPEC (dest))
|
|
{
|
|
return mergeSpec (dest, src, name);
|
|
}
|
|
else
|
|
{
|
|
decl = dest;
|
|
spec = src;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (IS_SPEC (dest))
|
|
{
|
|
decl = src;
|
|
spec = dest;
|
|
}
|
|
else
|
|
{
|
|
werror (E_SYNTAX_ERROR, yytext);
|
|
// the show must go on
|
|
return newIntLink ();
|
|
}
|
|
}
|
|
|
|
DCL_PTR_CONST (decl) |= SPEC_CONST (spec);
|
|
DCL_PTR_VOLATILE (decl) |= SPEC_VOLATILE (spec);
|
|
DCL_PTR_RESTRICT (decl) |= SPEC_RESTRICT (spec);
|
|
if (DCL_PTR_ADDRSPACE (decl) && SPEC_ADDRSPACE (spec) &&
|
|
strcmp (DCL_PTR_ADDRSPACE (decl)->name, SPEC_ADDRSPACE (spec)->name))
|
|
werror (E_SYNTAX_ERROR, yytext);
|
|
if (SPEC_ADDRSPACE (spec))
|
|
DCL_PTR_ADDRSPACE (decl) = SPEC_ADDRSPACE (spec);
|
|
|
|
lnk = decl;
|
|
while (lnk && !IS_SPEC (lnk->next))
|
|
lnk = lnk->next;
|
|
lnk->next = mergeSpec (spec, lnk->next, name);
|
|
return decl;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* genSymName - generates and returns a name used for anonymous vars */
|
|
/*-------------------------------------------------------------------*/
|
|
char *
|
|
genSymName (long level)
|
|
{
|
|
static int gCount = 0;
|
|
static char gname[SDCC_NAME_MAX + 1];
|
|
|
|
SNPRINTF (gname, sizeof (gname), "__%04d%04d", level, gCount++);
|
|
return gname;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* getSpec - returns the specifier part from a declaration chain */
|
|
/*------------------------------------------------------------------*/
|
|
sym_link *
|
|
getSpec (sym_link * p)
|
|
{
|
|
while (p && !(IS_SPEC (p)))
|
|
p = p->next;
|
|
|
|
return p;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* newCharLink() - creates an char type */
|
|
/*------------------------------------------------------------------*/
|
|
sym_link *
|
|
newCharLink ()
|
|
{
|
|
sym_link *p;
|
|
|
|
p = newLink (SPECIFIER);
|
|
SPEC_NOUN (p) = V_CHAR;
|
|
SPEC_USIGN (p) = 1;
|
|
|
|
return p;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* newFloatLink - a new Float type */
|
|
/*------------------------------------------------------------------*/
|
|
sym_link *
|
|
newFloatLink ()
|
|
{
|
|
sym_link *p;
|
|
|
|
p = newLink (SPECIFIER);
|
|
SPEC_NOUN (p) = V_FLOAT;
|
|
|
|
return p;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* newFixed16x16Link - a new Float type */
|
|
/*------------------------------------------------------------------*/
|
|
sym_link *
|
|
newFixed16x16Link ()
|
|
{
|
|
sym_link *p;
|
|
|
|
p = newLink (SPECIFIER);
|
|
SPEC_NOUN (p) = V_FIXED16X16;
|
|
|
|
return p;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* newLongLink() - new long type */
|
|
/*------------------------------------------------------------------*/
|
|
sym_link *
|
|
newLongLink ()
|
|
{
|
|
sym_link *p;
|
|
|
|
p = newLink (SPECIFIER);
|
|
SPEC_NOUN (p) = V_INT;
|
|
SPEC_LONG (p) = 1;
|
|
|
|
return p;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* newLongLongLink() - new long long type */
|
|
/*------------------------------------------------------------------*/
|
|
sym_link *
|
|
newLongLongLink ()
|
|
{
|
|
sym_link *p;
|
|
|
|
p = newLink (SPECIFIER);
|
|
SPEC_NOUN (p) = V_INT;
|
|
SPEC_LONGLONG (p) = 1;
|
|
|
|
return p;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* newIntLink() - creates an int type */
|
|
/*------------------------------------------------------------------*/
|
|
sym_link *
|
|
newIntLink ()
|
|
{
|
|
sym_link *p;
|
|
|
|
p = newLink (SPECIFIER);
|
|
SPEC_NOUN (p) = V_INT;
|
|
|
|
return p;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* newBoolLink() - creates an bool type */
|
|
/*------------------------------------------------------------------*/
|
|
sym_link *
|
|
newBoolLink ()
|
|
{
|
|
sym_link *p;
|
|
|
|
p = newLink (SPECIFIER);
|
|
if (bit)
|
|
SPEC_NOUN (p) = V_BIT;
|
|
else
|
|
SPEC_NOUN (p) = V_BOOL;
|
|
|
|
return p;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* newVoidLink() - creates an void type */
|
|
/*------------------------------------------------------------------*/
|
|
sym_link *
|
|
newVoidLink ()
|
|
{
|
|
sym_link *p;
|
|
|
|
p = newLink (SPECIFIER);
|
|
SPEC_NOUN (p) = V_VOID;
|
|
|
|
return p;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* getSize - returns size of a type chain in bytes */
|
|
/*------------------------------------------------------------------*/
|
|
unsigned int
|
|
getSize (sym_link * p)
|
|
{
|
|
/* if nothing return 0 */
|
|
if (!p)
|
|
return 0;
|
|
if (IS_SPEC (p))
|
|
{ /* if this is the specifier then */
|
|
switch (SPEC_NOUN (p))
|
|
{ /* depending on the specifier type */
|
|
case V_INT:
|
|
return (IS_LONGLONG (p) ? LONGLONGSIZE : (IS_LONG (p) ? LONGSIZE : INTSIZE));
|
|
case V_FLOAT:
|
|
return FLOATSIZE;
|
|
case V_FIXED16X16:
|
|
return (4);
|
|
case V_BOOL:
|
|
return BOOLSIZE;
|
|
case V_CHAR:
|
|
return CHARSIZE;
|
|
case V_VOID:
|
|
return 0;
|
|
case V_STRUCT:
|
|
return SPEC_STRUCT (p)->size;
|
|
case V_LABEL:
|
|
return 0;
|
|
case V_SBIT:
|
|
case V_BIT:
|
|
return BITSIZE;
|
|
case V_BITFIELD:
|
|
case V_BBITFIELD:
|
|
return ((SPEC_BLEN (p) / 8) + (SPEC_BLEN (p) % 8 ? 1 : 0));
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* this is a declarator */
|
|
switch (DCL_TYPE (p))
|
|
{
|
|
case ARRAY:
|
|
if (DCL_ELEM (p))
|
|
{
|
|
return DCL_ELEM (p) * getSize (p->next);
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
case IPOINTER:
|
|
case PPOINTER:
|
|
case POINTER:
|
|
return (NEARPTRSIZE);
|
|
case EEPPOINTER:
|
|
case FPOINTER:
|
|
case CPOINTER:
|
|
case FUNCTION:
|
|
return (IFFUNC_ISBANKEDCALL (p) ? BFUNCPTRSIZE : FUNCPTRSIZE);
|
|
case GPOINTER:
|
|
return (GPTRSIZE);
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#define FLEXARRAY 1
|
|
#define INCOMPLETE 2
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* checkStructFlexArray - check tree behind a struct */
|
|
/*------------------------------------------------------------------*/
|
|
static int
|
|
checkStructFlexArray (symbol * sym, sym_link * p)
|
|
{
|
|
/* if nothing return FALSE */
|
|
if (!p)
|
|
return 0;
|
|
|
|
if (IS_SPEC (p))
|
|
{
|
|
/* (nested) struct with flexible array member? */
|
|
if (IS_STRUCT (p) && SPEC_STRUCT (p)->b_flexArrayMember)
|
|
{
|
|
werror (W_INVALID_FLEXARRAY);
|
|
return INCOMPLETE;
|
|
}
|
|
/* or otherwise incomplete (nested) struct? */
|
|
if (IS_STRUCT (p) && ((SPEC_STRUCT (p)->size == 0) || !SPEC_STRUCT (p)->fields))
|
|
{
|
|
return INCOMPLETE;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* this is a declarator */
|
|
if (IS_ARRAY (p))
|
|
{
|
|
/* flexible array member? */
|
|
if (!DCL_ELEM (p))
|
|
{
|
|
if (!options.std_c99)
|
|
werror (W_C89_NO_FLEXARRAY);
|
|
if (checkStructFlexArray (sym, p->next) == INCOMPLETE)
|
|
werror (E_INCOMPLETE_FIELD, sym->name);
|
|
return FLEXARRAY;
|
|
}
|
|
/* walk tree */
|
|
return checkStructFlexArray (sym, p->next);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* bitsForType - returns # of bits required to store this type */
|
|
/*------------------------------------------------------------------*/
|
|
unsigned int
|
|
bitsForType (sym_link * p)
|
|
{
|
|
/* if nothing return 0 */
|
|
if (!p)
|
|
return 0;
|
|
|
|
if (IS_SPEC (p))
|
|
{ /* if this is the specifier then */
|
|
switch (SPEC_NOUN (p))
|
|
{ /* depending on the specifier type */
|
|
case V_INT:
|
|
if (IS_LONGLONG (p))
|
|
return LONGLONGSIZE * 8;
|
|
if (IS_LONG (p))
|
|
return LONGSIZE * 8;
|
|
return INTSIZE * 8;
|
|
case V_FLOAT:
|
|
return FLOATSIZE * 8;
|
|
case V_FIXED16X16:
|
|
return (32);
|
|
case V_BOOL:
|
|
return BOOLSIZE * 8;
|
|
case V_CHAR:
|
|
return CHARSIZE * 8;
|
|
case V_VOID:
|
|
return 0;
|
|
case V_STRUCT:
|
|
return SPEC_STRUCT (p)->size * 8;
|
|
case V_LABEL:
|
|
return 0;
|
|
case V_SBIT:
|
|
case V_BIT:
|
|
return 1;
|
|
case V_BITFIELD:
|
|
case V_BBITFIELD:
|
|
return SPEC_BLEN (p);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* this is a specifier */
|
|
switch (DCL_TYPE (p))
|
|
{
|
|
case ARRAY:
|
|
return DCL_ELEM (p) * getSize (p->next) * 8;
|
|
case IPOINTER:
|
|
case PPOINTER:
|
|
case POINTER:
|
|
return (NEARPTRSIZE * 8);
|
|
case EEPPOINTER:
|
|
case FPOINTER:
|
|
case CPOINTER:
|
|
case FUNCTION:
|
|
return (FARPTRSIZE * 8);
|
|
case GPOINTER:
|
|
return (GPTRSIZE * 8);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* copySymbolChain - copies a symbol chain */
|
|
/*------------------------------------------------------------------*/
|
|
symbol *
|
|
copySymbolChain (const symbol * src)
|
|
{
|
|
symbol *dest;
|
|
|
|
if (!src)
|
|
return NULL;
|
|
|
|
dest = copySymbol (src);
|
|
dest->next = copySymbolChain (src->next);
|
|
return dest;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* copySymbol - makes a copy of a symbol */
|
|
/*------------------------------------------------------------------*/
|
|
symbol *
|
|
copySymbol (const symbol * src)
|
|
{
|
|
symbol *dest;
|
|
|
|
if (!src)
|
|
return NULL;
|
|
|
|
dest = newSymbol (src->name, src->level);
|
|
memcpy (dest, src, sizeof (symbol));
|
|
dest->level = src->level;
|
|
dest->block = src->block;
|
|
dest->ival = copyIlist (src->ival);
|
|
dest->type = copyLinkChain (src->type);
|
|
dest->etype = getSpec (dest->type);
|
|
dest->next = NULL;
|
|
dest->key = src->key;
|
|
dest->allocreq = src->allocreq;
|
|
return dest;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* reverseSyms - reverses the links for a symbol chain */
|
|
/*------------------------------------------------------------------*/
|
|
symbol *
|
|
reverseSyms (symbol * sym)
|
|
{
|
|
symbol *prev, *curr, *next;
|
|
|
|
if (!sym)
|
|
return NULL;
|
|
|
|
prev = sym;
|
|
curr = sym->next;
|
|
|
|
while (curr)
|
|
{
|
|
next = curr->next;
|
|
curr->next = prev;
|
|
prev = curr;
|
|
curr = next;
|
|
}
|
|
sym->next = (void *) NULL;
|
|
return prev;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* reverseLink - reverses the links for a type chain */
|
|
/*------------------------------------------------------------------*/
|
|
sym_link *
|
|
reverseLink (sym_link * type)
|
|
{
|
|
sym_link *prev, *curr, *next;
|
|
|
|
if (!type)
|
|
return NULL;
|
|
|
|
prev = type;
|
|
curr = type->next;
|
|
|
|
while (curr)
|
|
{
|
|
next = curr->next;
|
|
curr->next = prev;
|
|
prev = curr;
|
|
curr = next;
|
|
}
|
|
type->next = (void *) NULL;
|
|
return prev;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* addSymChain - adds a symbol chain to the symboltable */
|
|
/*------------------------------------------------------------------*/
|
|
void
|
|
addSymChain (symbol ** symHead)
|
|
{
|
|
symbol *sym;
|
|
symbol *csym = NULL;
|
|
symbol **symPtrPtr;
|
|
int error = 0;
|
|
int elemsFromIval = 0;
|
|
|
|
for (sym = *symHead; sym != NULL; sym = sym->next)
|
|
{
|
|
changePointer (sym->type);
|
|
checkTypeSanity (sym->etype, sym->name);
|
|
|
|
if (IS_NORETURN (sym->etype))
|
|
{
|
|
SPEC_NORETURN (sym->etype) = 0;
|
|
FUNC_ISNORETURN (sym->type) = 1;
|
|
}
|
|
|
|
if (!sym->level && !(IS_SPEC (sym->etype) && IS_TYPEDEF (sym->etype)))
|
|
elemsFromIval = checkDecl (sym, 0);
|
|
else
|
|
{
|
|
/* if this is an array without any dimension
|
|
then update the dimension from the initial value */
|
|
if (IS_ARRAY (sym->type) && !DCL_ELEM (sym->type))
|
|
elemsFromIval = DCL_ELEM (sym->type) = getNelements (sym->type, sym->ival);
|
|
}
|
|
|
|
/* if already exists in the symbol table on the same level, ignoring sublevels */
|
|
if ((csym = findSymWithLevel (SymbolTab, sym)) && csym->level / LEVEL_UNIT == sym->level / LEVEL_UNIT)
|
|
{
|
|
/* if not formal parameter and not in file scope
|
|
then show symbol redefined error
|
|
else check if symbols have compatible types */
|
|
if (!sym->_isparm && sym->level > 0)
|
|
error = 1;
|
|
else
|
|
{
|
|
/* If the previous definition was for an array with incomplete
|
|
type, and the new definition has completed the type, update
|
|
the original type to match */
|
|
if (IS_ARRAY (csym->type) && IS_ARRAY (sym->type))
|
|
{
|
|
if (!DCL_ELEM (csym->type) && DCL_ELEM (sym->type))
|
|
DCL_ELEM (csym->type) = DCL_ELEM (sym->type);
|
|
if ((DCL_ELEM (csym->type) > DCL_ELEM (sym->type)) && elemsFromIval)
|
|
DCL_ELEM (sym->type) = DCL_ELEM (csym->type);
|
|
}
|
|
|
|
#if 0
|
|
/* If only one of the definitions used the "at" keyword, copy */
|
|
/* the address to the other. */
|
|
if (IS_SPEC (csym->etype) && SPEC_ABSA (csym->etype) && IS_SPEC (sym->etype) && !SPEC_ABSA (sym->etype))
|
|
{
|
|
SPEC_ABSA (sym->etype) = 1;
|
|
SPEC_ADDR (sym->etype) = SPEC_ADDR (csym->etype);
|
|
}
|
|
if (IS_SPEC (csym->etype) && !SPEC_ABSA (csym->etype) && IS_SPEC (sym->etype) && SPEC_ABSA (sym->etype))
|
|
{
|
|
SPEC_ABSA (csym->etype) = 1;
|
|
SPEC_ADDR (csym->etype) = SPEC_ADDR (sym->etype);
|
|
}
|
|
#endif
|
|
|
|
error = 0;
|
|
if (csym->ival && sym->ival)
|
|
error = 1;
|
|
if (compareTypeExact (csym->type, sym->type, sym->level) != 1)
|
|
error = 1;
|
|
}
|
|
|
|
if (error)
|
|
{
|
|
/* one definition extern ? */
|
|
if (IS_EXTERN (csym->etype) || IS_EXTERN (sym->etype))
|
|
werror (E_EXTERN_MISMATCH, sym->name);
|
|
else
|
|
werror (E_DUPLICATE, sym->name);
|
|
werrorfl (csym->fileDef, csym->lineDef, E_PREVIOUS_DEF);
|
|
#if 0
|
|
fprintf (stderr, "from type '");
|
|
printTypeChain (csym->type, stderr);
|
|
if (IS_SPEC (csym->etype) && SPEC_ABSA (csym->etype))
|
|
fprintf (stderr, " at 0x%x", SPEC_ADDR (csym->etype));
|
|
fprintf (stderr, "'\nto type '");
|
|
printTypeChain (sym->type, stderr);
|
|
if (IS_SPEC (sym->etype) && SPEC_ABSA (sym->etype))
|
|
fprintf (stderr, " at 0x%x", SPEC_ADDR (sym->etype));
|
|
fprintf (stderr, "'\n");
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
if (FUNC_BANKED (csym->type) || FUNC_BANKED (sym->type))
|
|
{
|
|
if (FUNC_NONBANKED (csym->type) || FUNC_NONBANKED (sym->type))
|
|
{
|
|
werror (W_BANKED_WITH_NONBANKED);
|
|
FUNC_BANKED (sym->type) = 0;
|
|
FUNC_NONBANKED (sym->type) = 1;
|
|
}
|
|
else
|
|
{
|
|
FUNC_BANKED (sym->type) = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (FUNC_NONBANKED (csym->type) || FUNC_NONBANKED (sym->type))
|
|
{
|
|
FUNC_NONBANKED (sym->type) = 1;
|
|
}
|
|
}
|
|
|
|
if (csym->ival && !sym->ival)
|
|
sym->ival = csym->ival;
|
|
|
|
if (!csym->cdef && !sym->cdef && IS_EXTERN (sym->etype))
|
|
{
|
|
/* if none of symbols is a compiler defined function
|
|
and at least one is not extern
|
|
then set the new symbol to non extern */
|
|
SPEC_EXTR (sym->etype) = SPEC_EXTR (csym->etype);
|
|
}
|
|
|
|
/* delete current entry */
|
|
deleteSym (SymbolTab, csym, csym->name);
|
|
deleteFromSeg (csym);
|
|
|
|
symPtrPtr = symHead;
|
|
while (*symPtrPtr && *symPtrPtr != csym)
|
|
symPtrPtr = &(*symPtrPtr)->next;
|
|
if (*symPtrPtr == csym)
|
|
*symPtrPtr = csym->next;
|
|
}
|
|
|
|
/* add new entry */
|
|
addSym (SymbolTab, sym, sym->name, sym->level, sym->block, 1);
|
|
}
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* funcInChain - DCL Type 'FUNCTION' found in type chain */
|
|
/*------------------------------------------------------------------*/
|
|
int
|
|
funcInChain (sym_link * lnk)
|
|
{
|
|
while (lnk)
|
|
{
|
|
if (IS_FUNC (lnk))
|
|
return 1;
|
|
lnk = lnk->next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* structElemType - returns the type info of a struct member */
|
|
/*------------------------------------------------------------------*/
|
|
sym_link *
|
|
structElemType (sym_link * stype, value * id)
|
|
{
|
|
symbol *fields = (SPEC_STRUCT (stype) ? SPEC_STRUCT (stype)->fields : NULL);
|
|
sym_link *type, *etype;
|
|
sym_link *petype = getSpec (stype);
|
|
|
|
if (fields && id)
|
|
{
|
|
/* look for the id */
|
|
while (fields)
|
|
{
|
|
if (strcmp (fields->rname, id->name) == 0)
|
|
{
|
|
sym_link *t;
|
|
type = copyLinkChain (fields->type);
|
|
etype = getSpec (type);
|
|
SPEC_SCLS (etype) = (SPEC_SCLS (petype) == S_REGISTER ? SPEC_SCLS (etype) : SPEC_SCLS (petype));
|
|
SPEC_OCLS (etype) = (SPEC_SCLS (petype) == S_REGISTER ? SPEC_OCLS (etype) : SPEC_OCLS (petype));
|
|
/* find the first non-array link */
|
|
t = type;
|
|
while (IS_ARRAY (t))
|
|
t = t->next;
|
|
if (IS_SPEC (t))
|
|
SPEC_CONST (t) |= SPEC_CONST (stype);
|
|
else
|
|
DCL_PTR_CONST (t) |= SPEC_CONST (stype);
|
|
return type;
|
|
}
|
|
fields = fields->next;
|
|
}
|
|
}
|
|
|
|
werror (E_NOT_MEMBER, id->name);
|
|
|
|
// the show must go on
|
|
return newIntLink ();
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* getStructElement - returns element of a tructure definition */
|
|
/*------------------------------------------------------------------*/
|
|
symbol *
|
|
getStructElement (structdef * sdef, symbol * sym)
|
|
{
|
|
symbol *field;
|
|
|
|
for (field = sdef->fields; field; field = field->next)
|
|
if (strcmp (field->name, sym->name) == 0)
|
|
return field;
|
|
|
|
werror (E_NOT_MEMBER, sym->name);
|
|
|
|
return sdef->fields;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* compStructSize - computes the size of a structure */
|
|
/*------------------------------------------------------------------*/
|
|
int
|
|
compStructSize (int su, structdef * sdef)
|
|
{
|
|
int sum = 0, usum = 0;
|
|
int bitOffset = 0;
|
|
symbol *loop;
|
|
const int oldlineno = lineno;
|
|
|
|
if (!sdef->fields)
|
|
{
|
|
werror (E_UNKNOWN_SIZE, sdef->tag);
|
|
}
|
|
|
|
/* for the identifiers */
|
|
loop = sdef->fields;
|
|
while (loop)
|
|
{
|
|
lineno = loop->lineDef;
|
|
|
|
/* create the internal name for this variable */
|
|
SNPRINTF (loop->rname, sizeof (loop->rname), "_%s", loop->name);
|
|
if (su == UNION)
|
|
{
|
|
sum = 0;
|
|
bitOffset = 0;
|
|
}
|
|
SPEC_VOLATILE (loop->etype) |= (su == UNION ? 1 : 0);
|
|
|
|
/* if this is a bit field */
|
|
if (loop->bitVar)
|
|
{
|
|
SPEC_BUNNAMED (loop->etype) = loop->bitUnnamed;
|
|
|
|
/* change it to a unsigned bit */
|
|
switch (SPEC_NOUN (loop->etype))
|
|
{
|
|
case V_BOOL:
|
|
SPEC_NOUN( loop->etype) = V_BBITFIELD;
|
|
if (loop->bitVar > 1)
|
|
werror (E_BITFLD_SIZE, 1);
|
|
break;
|
|
case V_CHAR:
|
|
SPEC_NOUN (loop->etype) = V_BITFIELD;
|
|
if (loop->bitVar > 8)
|
|
werror (E_BITFLD_SIZE , 8);
|
|
break;
|
|
case V_INT:
|
|
SPEC_NOUN (loop->etype) = V_BITFIELD;
|
|
if (loop->bitVar > port->s.int_size * 8)
|
|
werror (E_BITFLD_SIZE , port->s.int_size * 8);
|
|
break;
|
|
default:
|
|
werror (E_BITFLD_TYPE);
|
|
}
|
|
|
|
/* ISO/IEC 9899 J.3.9 implementation defined behaviour: */
|
|
/* a "plain" int bitfield is unsigned */
|
|
if (!loop->etype->select.s.b_signed)
|
|
SPEC_USIGN (loop->etype) = 1;
|
|
|
|
if (loop->bitVar == BITVAR_PAD)
|
|
{
|
|
/* A zero length bitfield forces padding */
|
|
SPEC_BLEN (loop->etype) = 0;
|
|
SPEC_BSTR (loop->etype) = bitOffset;
|
|
if (bitOffset > 0)
|
|
bitOffset = 8; /* padding is not needed when at bit 0 */
|
|
loop->offset = sum;
|
|
}
|
|
else
|
|
{
|
|
SPEC_BLEN (loop->etype) = loop->bitVar;
|
|
|
|
if (bitOffset == 8)
|
|
{
|
|
bitOffset = 0;
|
|
sum++;
|
|
}
|
|
/* check if this fit into the remaining */
|
|
/* bits of this byte else align it to the */
|
|
/* next byte boundary */
|
|
if (loop->bitVar <= (8 - bitOffset))
|
|
{
|
|
/* fits into current byte */
|
|
loop->offset = sum;
|
|
SPEC_BSTR (loop->etype) = bitOffset;
|
|
bitOffset += loop->bitVar;
|
|
}
|
|
else if (!bitOffset)
|
|
{
|
|
/* does not fit, but is already byte aligned */
|
|
loop->offset = sum;
|
|
SPEC_BSTR (loop->etype) = bitOffset;
|
|
bitOffset += loop->bitVar;
|
|
}
|
|
else
|
|
{
|
|
if (TARGET_IS_PIC16 && getenv ("PIC16_PACKED_BITFIELDS"))
|
|
{
|
|
/* if PIC16 && enviroment variable is set, then
|
|
* tightly pack bitfields, this means that when a
|
|
* bitfield goes beyond byte alignment, do not
|
|
* automatically start allocatint from next byte,
|
|
* but also use the available bits first */
|
|
fprintf (stderr, ": packing bitfields in structures\n");
|
|
SPEC_BSTR (loop->etype) = bitOffset;
|
|
bitOffset += loop->bitVar;
|
|
loop->offset = (su == UNION ? sum = 0 : sum);
|
|
}
|
|
else
|
|
{
|
|
/* does not fit; need to realign first */
|
|
sum++;
|
|
loop->offset = (su == UNION ? sum = 0 : sum);
|
|
bitOffset = 0;
|
|
SPEC_BSTR (loop->etype) = bitOffset;
|
|
bitOffset += loop->bitVar;
|
|
}
|
|
}
|
|
while (bitOffset > 8)
|
|
{
|
|
bitOffset -= 8;
|
|
sum++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* This is a non-bit field. Make sure we are */
|
|
/* byte aligned first */
|
|
if (bitOffset)
|
|
{
|
|
sum++;
|
|
loop->offset = (su == UNION ? sum = 0 : sum);
|
|
bitOffset = 0;
|
|
}
|
|
loop->offset = sum;
|
|
checkDecl (loop, 1);
|
|
sum += getSize (loop->type);
|
|
|
|
/* search for "flexible array members" */
|
|
/* and do some syntax checks */
|
|
if (su == STRUCT)
|
|
{
|
|
int ret = checkStructFlexArray (loop, loop->type);
|
|
if (ret == FLEXARRAY)
|
|
{
|
|
/* found a "flexible array member" */
|
|
sdef->b_flexArrayMember = TRUE;
|
|
/* is another struct-member following? */
|
|
if (loop->next)
|
|
werror (E_FLEXARRAY_NOTATEND, loop->name);
|
|
/* is it the first struct-member? */
|
|
else if (loop == sdef->fields)
|
|
werror (E_FLEXARRAY_INEMPTYSTRCT, loop->name);
|
|
}
|
|
else if (ret == INCOMPLETE)
|
|
{
|
|
werror (E_INCOMPLETE_FIELD, loop->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
loop = loop->next;
|
|
|
|
/* if union then size = sizeof largest field */
|
|
if (su == UNION)
|
|
{
|
|
/* For UNION, round up after each field */
|
|
sum += ((bitOffset + 7) / 8);
|
|
usum = max (usum, sum);
|
|
}
|
|
}
|
|
|
|
/* For STRUCT, round up after all fields processed */
|
|
if (su != UNION)
|
|
sum += ((bitOffset + 7) / 8);
|
|
|
|
lineno = oldlineno;
|
|
|
|
return (su == UNION ? usum : sum);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* promoteAnonStructs - promote anonymous struct/union's fields into */
|
|
/* an enclosing struct/union */
|
|
/*-------------------------------------------------------------------*/
|
|
void
|
|
promoteAnonStructs (int su, structdef * sdef)
|
|
{
|
|
symbol *field;
|
|
symbol *subfield;
|
|
symbol **tofield;
|
|
symbol *nextfield;
|
|
symbol *dupfield;
|
|
int base;
|
|
|
|
tofield = &sdef->fields;
|
|
field = sdef->fields;
|
|
while (field)
|
|
{
|
|
nextfield = field->next;
|
|
if (!*field->name && IS_STRUCT (field->type))
|
|
{
|
|
/* Found an anonymous struct/union. Replace it */
|
|
/* with the fields it contains and adjust all */
|
|
/* the offsets */
|
|
|
|
/* tagged anonymous struct/union is rejected here, though gcc allow it */
|
|
if (SPEC_STRUCT (field->type)->tagsym != NULL)
|
|
werrorfl (field->fileDef, field->lineDef, E_ANONYMOUS_STRUCT_TAG, SPEC_STRUCT (field->type)->tag);
|
|
|
|
base = field->offset;
|
|
subfield = copySymbolChain (SPEC_STRUCT (field->type)->fields);
|
|
if (!subfield)
|
|
continue; /* just in case it's empty */
|
|
|
|
*tofield = subfield;
|
|
for (;;)
|
|
{
|
|
/* check for field name conflicts resulting from promotion */
|
|
dupfield = sdef->fields;
|
|
while (dupfield && dupfield != subfield)
|
|
{
|
|
if (*subfield->name && !strcmp (dupfield->name, subfield->name))
|
|
{
|
|
werrorfl (subfield->fileDef, subfield->lineDef,
|
|
E_DUPLICATE_MEMBER, su == STRUCT ? "struct" : "union", subfield->name);
|
|
werrorfl (dupfield->fileDef, dupfield->lineDef, E_PREVIOUS_DEF);
|
|
}
|
|
dupfield = dupfield->next;
|
|
}
|
|
|
|
subfield->offset += base;
|
|
if (subfield->next)
|
|
subfield = subfield->next;
|
|
else
|
|
break;
|
|
}
|
|
subfield->next = nextfield;
|
|
tofield = &subfield->next;
|
|
}
|
|
else
|
|
tofield = &field->next;
|
|
field = nextfield;
|
|
}
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* checkSClass - check the storage class specification */
|
|
/*------------------------------------------------------------------*/
|
|
static void
|
|
checkSClass (symbol *sym, int isProto)
|
|
{
|
|
sym_link *t;
|
|
|
|
if (getenv ("DEBUG_SANITY"))
|
|
{
|
|
fprintf (stderr, "checkSClass: %s \n", sym->name);
|
|
}
|
|
|
|
/* type is literal can happen for enums change to auto */
|
|
if (SPEC_SCLS (sym->etype) == S_LITERAL && !SPEC_ENUM (sym->etype))
|
|
SPEC_SCLS (sym->etype) = S_AUTO;
|
|
|
|
/* if sfr or sbit then must also be volatile */
|
|
if (SPEC_SCLS (sym->etype) == S_SBIT || SPEC_SCLS (sym->etype) == S_SFR)
|
|
{
|
|
SPEC_VOLATILE (sym->etype) = 1;
|
|
}
|
|
|
|
if (SPEC_NEEDSPAR (sym->etype))
|
|
{
|
|
werrorfl (sym->fileDef, sym->lineDef, E_QUALIFIED_ARRAY_NOPARAM);
|
|
SPEC_NEEDSPAR (sym->etype) = 0;
|
|
}
|
|
|
|
/* make sure restrict is only used with pointers */
|
|
if (SPEC_RESTRICT (sym->etype))
|
|
{
|
|
werrorfl (sym->fileDef, sym->lineDef, E_BAD_RESTRICT);
|
|
SPEC_RESTRICT (sym->etype) = 0;
|
|
}
|
|
|
|
t = sym->type;
|
|
while (t)
|
|
{
|
|
if (IS_DECL (t) && DCL_PTR_RESTRICT (t) && !(IS_PTR (t) && !IS_FUNCPTR(t)))
|
|
{
|
|
werrorfl (sym->fileDef, sym->lineDef, E_BAD_RESTRICT);
|
|
DCL_PTR_RESTRICT (t) = 0;
|
|
break;
|
|
}
|
|
t = t->next;
|
|
}
|
|
|
|
/* if absolute address given then it mark it as
|
|
volatile -- except in the PIC port */
|
|
|
|
#if !OPT_DISABLE_PIC14 || !OPT_DISABLE_PIC16
|
|
/* The PIC port uses a different peep hole optimizer based on "pCode" */
|
|
if (!TARGET_PIC_LIKE)
|
|
#endif
|
|
|
|
if (IS_ABSOLUTE (sym->etype))
|
|
SPEC_VOLATILE (sym->etype) = 1;
|
|
|
|
if (TARGET_IS_MCS51 && IS_ABSOLUTE (sym->etype) && SPEC_SCLS (sym->etype) == S_SFR)
|
|
{
|
|
int n, size;
|
|
unsigned addr;
|
|
|
|
if (SPEC_NOUN (sym->etype) == V_CHAR)
|
|
size = 8;
|
|
else if (SPEC_LONG (sym->etype) == 0)
|
|
size = 16;
|
|
else if (SPEC_LONGLONG (sym->etype) == 0)
|
|
size = 32;
|
|
else
|
|
size = 64;
|
|
|
|
addr = SPEC_ADDR (sym->etype);
|
|
for (n = 0; n < size; n += 8)
|
|
if (((addr >> n) & 0xFF) < 0x80)
|
|
werror (W_SFR_ABSRANGE, sym->name);
|
|
}
|
|
else if (TARGET_Z80_LIKE && IS_ABSOLUTE (sym->etype) && SPEC_SCLS (sym->etype) == S_SFR)
|
|
{
|
|
if (SPEC_ADDR (sym->etype) > (FUNC_REGBANK (sym->type) ? 0xffff : 0xff))
|
|
werror (W_SFR_ABSRANGE, sym->name);
|
|
}
|
|
else if (TARGET_IS_PDK13 && IS_ABSOLUTE (sym->etype) && SPEC_SCLS (sym->etype) == S_SFR)
|
|
{
|
|
if (SPEC_ADDR (sym->etype) > 0x1f)
|
|
werror (W_SFR_ABSRANGE, sym->name);
|
|
}
|
|
else if (TARGET_IS_PDK14 && IS_ABSOLUTE (sym->etype) && SPEC_SCLS (sym->etype) == S_SFR)
|
|
{
|
|
if (SPEC_ADDR (sym->etype) > 0x3f)
|
|
werror (W_SFR_ABSRANGE, sym->name);
|
|
}
|
|
else if (TARGET_IS_PDK15 && IS_ABSOLUTE (sym->etype) && SPEC_SCLS (sym->etype) == S_SFR)
|
|
{
|
|
if (SPEC_ADDR (sym->etype) > 0x7f)
|
|
werror (W_SFR_ABSRANGE, sym->name);
|
|
}
|
|
else if (TARGET_IS_PDK16 && IS_ABSOLUTE (sym->etype) && SPEC_SCLS (sym->etype) == S_SFR)
|
|
{
|
|
if (SPEC_ADDR (sym->etype) > 0x1f)
|
|
werror (W_SFR_ABSRANGE, sym->name);
|
|
}
|
|
|
|
/* If code memory is read only, then pointers to code memory */
|
|
/* implicitly point to constants -- make this explicit */
|
|
CodePtrPointsToConst (sym->type);
|
|
|
|
/* global variables declared const put into code */
|
|
/* if no other storage class specified */
|
|
if ((sym->level == 0 || SPEC_STAT(sym->etype)) && SPEC_SCLS (sym->etype) == S_FIXED && !IS_FUNC (sym->type))
|
|
{
|
|
/* find the first non-array link */
|
|
t = sym->type;
|
|
while (IS_ARRAY (t))
|
|
t = t->next;
|
|
if (IS_CONSTANT (t))
|
|
SPEC_SCLS (sym->etype) = S_CODE;
|
|
}
|
|
|
|
/* global variable in code space is a constant */
|
|
if ((sym->level == 0 || SPEC_STAT(sym->etype)) && SPEC_SCLS (sym->etype) == S_CODE && port->mem.code_ro)
|
|
{
|
|
/* find the first non-array link */
|
|
t = sym->type;
|
|
while (IS_ARRAY (t))
|
|
t = t->next;
|
|
if (IS_SPEC (t))
|
|
SPEC_CONST (t) = 1;
|
|
else
|
|
DCL_PTR_CONST (t) = 1;
|
|
}
|
|
|
|
/* if bit variable then no storage class can be */
|
|
/* specified since bit is already a storage */
|
|
if (IS_BITVAR (sym->etype) &&
|
|
(SPEC_SCLS (sym->etype) != S_FIXED && SPEC_SCLS (sym->etype) != S_SBIT && SPEC_SCLS (sym->etype) != S_BIT))
|
|
{
|
|
/* find out if this is the return type of a function */
|
|
t = sym->type;
|
|
while (t && t->next != sym->etype)
|
|
t = t->next;
|
|
if (!t || t->next != sym->etype || !IS_FUNC (t))
|
|
{
|
|
werror (E_BITVAR_STORAGE, sym->name);
|
|
SPEC_SCLS (sym->etype) = S_FIXED;
|
|
}
|
|
}
|
|
|
|
/* if this is an automatic symbol */
|
|
if (sym->level && (options.stackAuto || reentrant))
|
|
{
|
|
if (SPEC_SCLS (sym->etype) != S_BIT &&
|
|
SPEC_SCLS (sym->etype) != S_REGISTER)
|
|
{
|
|
if ((SPEC_SCLS (sym->etype) == S_AUTO ||
|
|
SPEC_SCLS (sym->etype) == S_FIXED ||
|
|
SPEC_SCLS (sym->etype) == S_STACK ||
|
|
SPEC_SCLS (sym->etype) == S_XSTACK))
|
|
{
|
|
SPEC_SCLS (sym->etype) = S_AUTO;
|
|
}
|
|
else
|
|
{
|
|
/* storage class may only be specified for statics */
|
|
if (!IS_STATIC (sym->etype))
|
|
{
|
|
werror (E_AUTO_ASSUMED, sym->name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* automatic symbols cannot be given */
|
|
/* an absolute address ignore it */
|
|
if (sym->level && !IS_STATIC (sym->etype) && SPEC_ABSA (sym->etype) && (options.stackAuto || reentrant))
|
|
{
|
|
werror (E_AUTO_ABSA, sym->name);
|
|
SPEC_ABSA (sym->etype) = 0;
|
|
}
|
|
|
|
if (sym->level && !IS_STATIC (sym->etype) && (IS_DECL (sym->type) ? DCL_PTR_ADDRSPACE (sym->type) : SPEC_ADDRSPACE (sym->type)) && (options.stackAuto || reentrant))
|
|
{
|
|
werror (E_AUTO_ADDRSPACE, sym->name);
|
|
if (IS_DECL (sym->type))
|
|
DCL_PTR_ADDRSPACE (sym->type) = 0;
|
|
else
|
|
SPEC_ADDRSPACE (sym->type) = 0;
|
|
}
|
|
|
|
/* arrays & pointers cannot be defined for bits */
|
|
/* SBITS or SFRs or BIT */
|
|
if ((IS_ARRAY (sym->type) || IS_PTR (sym->type)) &&
|
|
(SPEC_NOUN (sym->etype) == V_BIT || SPEC_NOUN (sym->etype) == V_SBIT ||
|
|
SPEC_NOUN (sym->etype) == V_BITFIELD || SPEC_NOUN (sym->etype) == V_BBITFIELD ||
|
|
SPEC_SCLS (sym->etype) == S_SFR))
|
|
{
|
|
/* find out if this is the return type of a function */
|
|
t = sym->type;
|
|
while (t && t->next != sym->etype)
|
|
t = t->next;
|
|
if (t->next != sym->etype || !IS_FUNC (t))
|
|
{
|
|
werror (E_BIT_ARRAY, sym->name);
|
|
}
|
|
}
|
|
|
|
/* if this is a bit|sbit then set length & start */
|
|
if (SPEC_NOUN (sym->etype) == V_BIT || SPEC_NOUN (sym->etype) == V_SBIT)
|
|
{
|
|
SPEC_BLEN (sym->etype) = 1;
|
|
SPEC_BSTR (sym->etype) = 0;
|
|
}
|
|
|
|
if (!isProto)
|
|
{
|
|
/* variables declared in CODE space must have */
|
|
/* initializers if not an extern, a global or a static */
|
|
if (SPEC_SCLS (sym->etype) == S_CODE && sym->ival == NULL && !sym->_isparm &&
|
|
IS_AUTO(sym) &&
|
|
port->mem.code_ro && !SPEC_ABSA (sym->etype) && !funcInChain (sym->type))
|
|
werror (E_CODE_NO_INIT, sym->name);
|
|
}
|
|
|
|
/* if parameter or local variable then change */
|
|
/* the storage class to reflect where the var will go */
|
|
if (sym->level && SPEC_SCLS (sym->etype) == S_FIXED && !IS_STATIC (sym->etype))
|
|
{
|
|
if (options.stackAuto || (currFunc && IFFUNC_ISREENT (currFunc->type)))
|
|
{
|
|
SPEC_SCLS (sym->etype) = (options.useXstack ? S_XSTACK : S_STACK);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* changePointer - change pointer to functions */
|
|
/*------------------------------------------------------------------*/
|
|
void
|
|
changePointer (sym_link * p)
|
|
{
|
|
/* go thru the chain of declarations */
|
|
/* if we find a pointer to a function */
|
|
/* change it to a ptr to code area */
|
|
/* unless the function is banked. */
|
|
for (; p; p = p->next)
|
|
{
|
|
if (IS_DECL (p) && DCL_TYPE (p) == UPOINTER)
|
|
DCL_TYPE (p) = port->unqualified_pointer;
|
|
if (IS_PTR (p) && IS_FUNC (p->next))
|
|
if (!IFFUNC_ISBANKEDCALL (p->next))
|
|
DCL_TYPE (p) = CPOINTER;
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* checkDecl - does semantic validation of a declaration */
|
|
/*------------------------------------------------------------------*/
|
|
int
|
|
checkDecl (symbol * sym, int isProto)
|
|
{
|
|
checkSClass (sym, isProto); /* check the storage class */
|
|
changePointer (sym->type); /* change pointers if required */
|
|
|
|
/* if this is an array without any dimension
|
|
then update the dimension from the initial value */
|
|
if (IS_ARRAY (sym->type) && !DCL_ELEM (sym->type))
|
|
return DCL_ELEM (sym->type) = getNelements (sym->type, sym->ival);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* copyLinkChain - makes a copy of the link chain & rets ptr 2 head */
|
|
/*------------------------------------------------------------------*/
|
|
sym_link *
|
|
copyLinkChain (const sym_link *p)
|
|
{
|
|
sym_link *head, *loop;
|
|
const sym_link *curr;
|
|
|
|
/* note: v_struct and v_struct->fields are not copied! */
|
|
curr = p;
|
|
head = loop = (curr ? newLink (p->xclass) : (void *) NULL);
|
|
while (curr)
|
|
{
|
|
memcpy (loop, curr, sizeof (sym_link)); /* copy it */
|
|
loop->next = (curr->next ? newLink (curr->next->xclass) : (void *) NULL);
|
|
loop = loop->next;
|
|
curr = curr->next;
|
|
}
|
|
|
|
return head;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* cleanUpBlock - cleansup the symbol table specified for all the */
|
|
/* symbols in the given block */
|
|
/*------------------------------------------------------------------*/
|
|
void
|
|
cleanUpBlock (bucket ** table, int block)
|
|
{
|
|
int i;
|
|
bucket *chain;
|
|
|
|
/* go thru the entire table */
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
for (chain = table[i]; chain; chain = chain->next)
|
|
{
|
|
if (chain->block >= block)
|
|
{
|
|
deleteSym (table, chain->sym, chain->name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* cleanUpLevel - cleans up the symbol table specified for all the */
|
|
/* symbols in the given level */
|
|
/*------------------------------------------------------------------*/
|
|
void
|
|
cleanUpLevel (bucket ** table, long level)
|
|
{
|
|
int i;
|
|
bucket *chain;
|
|
|
|
/* go thru the entire table */
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
for (chain = table[i]; chain; chain = chain->next)
|
|
{
|
|
if (chain->level >= level)
|
|
{
|
|
deleteSym (table, chain->sym, chain->name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
symbol *
|
|
getAddrspace (sym_link *type)
|
|
{
|
|
if (IS_DECL (type))
|
|
return (DCL_PTR_ADDRSPACE (type));
|
|
return (SPEC_ADDRSPACE (type));
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* computeTypeOr - computes the resultant type from two types */
|
|
/*------------------------------------------------------------------*/
|
|
static sym_link *
|
|
computeTypeOr (sym_link * etype1, sym_link * etype2, sym_link * reType)
|
|
{
|
|
/* sanity check */
|
|
assert ((IS_CHAR (etype1) || IS_BOOLEAN (etype1)) &&
|
|
(IS_CHAR (etype2) || IS_BOOLEAN (etype2)));
|
|
|
|
if (SPEC_USIGN (etype1) == SPEC_USIGN (etype2))
|
|
{
|
|
SPEC_USIGN (reType) = SPEC_USIGN (etype1);
|
|
return reType;
|
|
}
|
|
|
|
if (SPEC_USIGN (etype1))
|
|
{
|
|
if (IS_LITERAL (etype2) && floatFromVal (valFromType (etype2)) >= 0)
|
|
SPEC_USIGN (reType) = 1;
|
|
else
|
|
{
|
|
/* promote to int */
|
|
SPEC_USIGN (reType) = 0;
|
|
SPEC_NOUN (reType) = V_INT;
|
|
}
|
|
}
|
|
else /* etype1 signed */
|
|
{
|
|
if (IS_LITERAL (etype2) && floatFromVal (valFromType (etype2)) <= 127)
|
|
SPEC_USIGN (reType) = 0;
|
|
else
|
|
{
|
|
/* promote to int */
|
|
SPEC_USIGN (reType) = 0;
|
|
SPEC_NOUN (reType) = V_INT;
|
|
}
|
|
}
|
|
|
|
if (SPEC_USIGN (etype2))
|
|
{
|
|
if (IS_LITERAL (etype1) && floatFromVal (valFromType (etype1)) >= 0)
|
|
SPEC_USIGN (reType) = 1;
|
|
else
|
|
{
|
|
/* promote to int */
|
|
SPEC_USIGN (reType) = 0;
|
|
SPEC_NOUN (reType) = V_INT;
|
|
}
|
|
}
|
|
else /* etype2 signed */
|
|
{
|
|
if (IS_LITERAL (etype1) && floatFromVal (valFromType (etype1)) <= 127)
|
|
SPEC_USIGN (reType) = 0;
|
|
else
|
|
{
|
|
/* promote to int */
|
|
SPEC_USIGN (reType) = 0;
|
|
SPEC_NOUN (reType) = V_INT;
|
|
}
|
|
}
|
|
return reType;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* computeType - computes the resultant type from two types */
|
|
/*------------------------------------------------------------------*/
|
|
sym_link *
|
|
computeType (sym_link * type1, sym_link * type2, RESULT_TYPE resultType, int op)
|
|
{
|
|
sym_link *rType;
|
|
sym_link *reType;
|
|
sym_link *etype1 = getSpec (type1);
|
|
sym_link *etype2;
|
|
|
|
etype2 = type2 ? getSpec (type2) : type1;
|
|
|
|
/* Conditional operator has some special type conversion rules */
|
|
if (op == ':')
|
|
{
|
|
/* If either type is an array, convert to pointer */
|
|
if (IS_ARRAY(type1))
|
|
{
|
|
value * val = aggregateToPointer (valFromType (type1));
|
|
type1 = val->type;
|
|
Safe_free (val);
|
|
etype1 = getSpec (type1);
|
|
}
|
|
if (IS_ARRAY(type2))
|
|
{
|
|
value * val = aggregateToPointer (valFromType (type2));
|
|
type2 = val->type;
|
|
Safe_free (val);
|
|
etype2 = getSpec (type2);
|
|
}
|
|
|
|
/* If NULL and another pointer, use the non-NULL pointer type. */
|
|
/* Note that NULL can be defined as either 0 or (void *)0. */
|
|
if (IS_LITERAL (etype1) &&
|
|
((IS_PTR (type1) && IS_VOID (type1->next)) || IS_INTEGRAL (type1)) &&
|
|
(floatFromVal (valFromType (etype1)) == 0) &&
|
|
IS_PTR (type2))
|
|
return copyLinkChain (type2);
|
|
else if (IS_LITERAL (etype2) &&
|
|
((IS_PTR (type2) && IS_VOID (type2->next)) || IS_INTEGRAL (type2)) &&
|
|
(floatFromVal (valFromType (etype2)) == 0) &&
|
|
IS_PTR (type1))
|
|
return copyLinkChain (type1);
|
|
|
|
/* If a void pointer, use the void pointer type */
|
|
else if (IS_PTR(type1) && IS_VOID(type1->next))
|
|
return copyLinkChain (type1);
|
|
else if (IS_PTR(type2) && IS_VOID(type2->next))
|
|
return copyLinkChain (type2);
|
|
|
|
/* Otherwise fall through to the general case */
|
|
}
|
|
|
|
/* shift operators have the important type in the left operand */
|
|
if (op == LEFT_OP || op == RIGHT_OP)
|
|
rType = copyLinkChain(type1);
|
|
|
|
/* if one of them is a pointer or array then that prevails */
|
|
else if (IS_PTR (type1) || IS_ARRAY (type1))
|
|
rType = copyLinkChain (type1);
|
|
else if (IS_PTR (type2) || IS_ARRAY (type2))
|
|
rType = copyLinkChain (type2);
|
|
|
|
/* if one of them is a float then result is a float */
|
|
/* here we assume that the types passed are okay */
|
|
/* and can be cast to one another */
|
|
/* which ever is greater in size */
|
|
else if (IS_FLOAT (etype1) || IS_FLOAT (etype2))
|
|
rType = newFloatLink ();
|
|
/* if both are fixed16x16 then result is float */
|
|
else if (IS_FIXED16X16 (etype1) && IS_FIXED16X16 (etype2))
|
|
rType = newFixed16x16Link ();
|
|
else if (IS_FIXED16X16 (etype1) && IS_FLOAT (etype2))
|
|
rType = newFloatLink ();
|
|
else if (IS_FLOAT (etype1) && IS_FIXED16X16 (etype2))
|
|
rType = newFloatLink ();
|
|
|
|
/* if only one of them is a bool variable then the other one prevails */
|
|
else if (IS_BOOLEAN (etype1) && !IS_BOOLEAN (etype2))
|
|
{
|
|
rType = copyLinkChain (type2);
|
|
}
|
|
else if (IS_BOOLEAN (etype2) && !IS_BOOLEAN (etype1))
|
|
{
|
|
rType = copyLinkChain (type1);
|
|
}
|
|
|
|
/* if both are bitvars choose the larger one */
|
|
else if (IS_BITVAR (etype1) && IS_BITVAR (etype2))
|
|
rType = SPEC_BLEN (etype1) >= SPEC_BLEN (etype2) ? copyLinkChain (type1) : copyLinkChain (type2);
|
|
|
|
/* if only one of them is a bit variable then the other one prevails */
|
|
else if (IS_BITVAR (etype1) && !IS_BITVAR (etype2))
|
|
{
|
|
rType = copyLinkChain (type2);
|
|
/* bitfield can have up to 16 bits */
|
|
if (getSize (etype1) > 1)
|
|
SPEC_NOUN (getSpec (rType)) = V_INT;
|
|
}
|
|
else if (IS_BITVAR (etype2) && !IS_BITVAR (etype1))
|
|
{
|
|
rType = copyLinkChain (type1);
|
|
/* bitfield can have up to 16 bits */
|
|
if (getSize (etype2) > 1)
|
|
SPEC_NOUN (getSpec (rType)) = V_INT;
|
|
}
|
|
|
|
else if (bitsForType (type1) > bitsForType (type2))
|
|
rType = copyLinkChain (type1);
|
|
else
|
|
rType = copyLinkChain (type2);
|
|
|
|
reType = getSpec (rType);
|
|
|
|
/* avoid conflicting types */
|
|
reType->select.s.b_signed = 0;
|
|
|
|
/* if result is a literal then make not so */
|
|
if (IS_LITERAL (reType))
|
|
{
|
|
SPEC_SCLS (reType) = S_REGISTER;
|
|
SPEC_CONST (reType) = 0;
|
|
}
|
|
|
|
switch (resultType)
|
|
{
|
|
case RESULT_TYPE_IFX:
|
|
if (TARGET_HC08_LIKE)
|
|
break;
|
|
//fallthrough
|
|
case RESULT_TYPE_BOOL:
|
|
if (op == ':')
|
|
{
|
|
SPEC_NOUN (reType) = V_BIT;
|
|
return rType;
|
|
}
|
|
break;
|
|
case RESULT_TYPE_CHAR:
|
|
if (IS_BOOL (reType) || IS_BITVAR (reType))
|
|
{
|
|
SPEC_NOUN (reType) = V_CHAR;
|
|
SPEC_SCLS (reType) = 0;
|
|
SPEC_USIGN (reType) = 0;
|
|
return rType;
|
|
}
|
|
break;
|
|
case RESULT_TYPE_INT:
|
|
case RESULT_TYPE_NONE:
|
|
case RESULT_TYPE_OTHER:
|
|
case RESULT_TYPE_GPTR:
|
|
if (!IS_SPEC (rType))
|
|
{
|
|
return rType;
|
|
}
|
|
if (IS_BOOLEAN (reType))
|
|
{
|
|
SPEC_NOUN (reType) = V_CHAR;
|
|
SPEC_SCLS (reType) = 0;
|
|
SPEC_USIGN (reType) = 0;
|
|
return rType;
|
|
}
|
|
else if (IS_BITFIELD (reType))
|
|
{
|
|
/* could be smarter, but it depends on the op */
|
|
/* this is for the worst case: a multiplication of 4 * 4 bit */
|
|
SPEC_NOUN (reType) = SPEC_BLEN (reType) <= 4 ? V_CHAR : V_INT;
|
|
SPEC_SCLS (reType) = 0;
|
|
SPEC_USIGN (reType) = 0;
|
|
return rType;
|
|
}
|
|
else if (IS_CHAR (reType))
|
|
{
|
|
/* promotion of some special cases */
|
|
switch (op)
|
|
{
|
|
case '|':
|
|
case '^':
|
|
return computeTypeOr (etype1, etype2, reType);
|
|
case '&':
|
|
case BITWISEAND:
|
|
if (SPEC_USIGN (etype1) != SPEC_USIGN (etype2))
|
|
{
|
|
SPEC_USIGN (reType) = 1;
|
|
return rType;
|
|
}
|
|
break;
|
|
case '*':
|
|
SPEC_NOUN (reType) = V_INT;
|
|
SPEC_USIGN (reType) = 0;
|
|
return rType;
|
|
case '/':
|
|
/* if both are unsigned char then no promotion required */
|
|
if (!(SPEC_USIGN (etype1) && SPEC_USIGN (etype2)))
|
|
{
|
|
SPEC_NOUN (reType) = V_INT;
|
|
SPEC_USIGN (reType) = 0;
|
|
return rType;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* SDCC's sign promotion:
|
|
- if one or both operands are unsigned, the resultant type will be unsigned
|
|
(except char, see below)
|
|
- if an operand is promoted to a larger type (char -> int, int -> long),
|
|
the larger type will be signed
|
|
|
|
SDCC tries hard to avoid promotion to int and does 8 bit calculation as
|
|
much as possible. We're leaving ISO IEC 9899 here and have to extrapolate
|
|
the standard. The standard demands, that the result has to be the same
|
|
"as if" the promotion would have been performed:
|
|
|
|
- if the result of an operation with two char's is promoted to a
|
|
larger type, the result will be signed.
|
|
|
|
More sophisticated are these:
|
|
- if the result of an operation with two char's is a char again,
|
|
the result will only then be unsigned, if both operands are
|
|
unsigned. In all other cases the result will be signed.
|
|
|
|
This seems to be contradictionary to the first two rules, but it makes
|
|
real sense (all types are char's):
|
|
|
|
A signed char can be negative; this must be preserved in the result
|
|
-1 * 100 = -100;
|
|
|
|
Only if both operands are unsigned it's safe to make the result
|
|
unsigned; this helps to avoid overflow:
|
|
2 * 100 = 200;
|
|
|
|
- ToDo: document '|', '^' and '&'
|
|
|
|
Homework: - why is (200 * 200 < 0) true?
|
|
- why is { char l = 200, r = 200; (r * l > 0) } true?
|
|
*/
|
|
|
|
if (!IS_FLOAT (reType) && ((SPEC_USIGN (etype1)
|
|
/* if this operand is promoted to a larger type,
|
|
then it will be promoted to a signed type */
|
|
&& !(bitsForType (etype1) < bitsForType (reType))
|
|
/* char require special handling */
|
|
&& !IS_CHAR (etype1)) || /* same for 2nd operand */
|
|
(SPEC_USIGN (etype2) && !(bitsForType (etype2) < bitsForType (reType)) && !IS_CHAR (etype2)) || /* if both are 'unsigned char' and not promoted
|
|
let the result be unsigned too */
|
|
(SPEC_USIGN (etype1)
|
|
&& SPEC_USIGN (etype2) && IS_CHAR (etype1) && IS_CHAR (etype2) && IS_CHAR (reType))))
|
|
SPEC_USIGN (reType) = 1;
|
|
else
|
|
SPEC_USIGN (reType) = 0;
|
|
|
|
return rType;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* compareFuncType - compare function prototypes */
|
|
/*------------------------------------------------------------------*/
|
|
int
|
|
compareFuncType (sym_link * dest, sym_link * src)
|
|
{
|
|
value *exargs, *acargs;
|
|
value *checkValue;
|
|
int argCnt = 0;
|
|
int i;
|
|
|
|
/* if not type then some kind of error */
|
|
if (!dest || !src)
|
|
return 0;
|
|
|
|
/* check the return value type */
|
|
if (compareType (dest->next, src->next) <= 0)
|
|
return 0;
|
|
|
|
/* Really, reentrant should match regardless of argCnt, but */
|
|
/* this breaks some existing code (the fp lib functions). If */
|
|
/* the first argument is always passed the same way, this */
|
|
/* lax checking is ok (but may not be true for in future ports) */
|
|
if (IFFUNC_ISREENT (dest) != IFFUNC_ISREENT (src) && argCnt > 1)
|
|
{
|
|
//printf("argCnt = %d\n",argCnt);
|
|
return 0;
|
|
}
|
|
|
|
if (IFFUNC_ISWPARAM (dest) != IFFUNC_ISWPARAM (src))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (IFFUNC_ISSHADOWREGS (dest) != IFFUNC_ISSHADOWREGS (src))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (IFFUNC_ISZ88DK_FASTCALL (dest) != IFFUNC_ISZ88DK_FASTCALL (src) ||
|
|
IFFUNC_ISZ88DK_CALLEE (dest) != IFFUNC_ISZ88DK_CALLEE (src))
|
|
return 0;
|
|
|
|
for (i = 0; i < 9; i++)
|
|
if (dest->funcAttrs.preserved_regs[i] > src->funcAttrs.preserved_regs[i])
|
|
return 0;
|
|
|
|
/* compare register bank */
|
|
if (FUNC_REGBANK (dest) != FUNC_REGBANK (src))
|
|
{ /* except for ISR's whose prototype need not match
|
|
since they are the top of a call-tree and
|
|
the prototype is only necessary for its vector in main */
|
|
if (!IFFUNC_ISISR (dest) || !IFFUNC_ISISR (src))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* compare expected args with actual args */
|
|
exargs = FUNC_ARGS (dest);
|
|
acargs = FUNC_ARGS (src);
|
|
|
|
/* for all the expected args do */
|
|
for (argCnt = 1; exargs && acargs; exargs = exargs->next, acargs = acargs->next, argCnt++)
|
|
{
|
|
/* If the actual argument is an array, any prototype
|
|
* will have modified it to a pointer. Duplicate that
|
|
* change here.
|
|
*/
|
|
if (IS_AGGREGATE (acargs->type))
|
|
{
|
|
checkValue = copyValue (acargs);
|
|
aggregateToPointer (checkValue);
|
|
}
|
|
else
|
|
{
|
|
checkValue = acargs;
|
|
}
|
|
if (IFFUNC_ISREENT (dest) && compareType (exargs->type, checkValue->type) <= 0)
|
|
{
|
|
return 0;
|
|
}
|
|
if (!IFFUNC_ISREENT (dest) && compareTypeExact (exargs->type, checkValue->type, 1) <= 0)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* if one them ended we have a problem */
|
|
if ((exargs && !acargs && !IS_VOID (exargs->type)) || (!exargs && acargs && !IS_VOID (acargs->type)))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
comparePtrType (sym_link *dest, sym_link *src, bool bMustCast)
|
|
{
|
|
int res;
|
|
|
|
if (getAddrspace (src->next) != getAddrspace (dest->next))
|
|
bMustCast = 1;
|
|
|
|
if (IS_VOID (src->next) && IS_VOID (dest->next))
|
|
return bMustCast ? -1 : 1;
|
|
if ((IS_VOID (src->next) && !IS_VOID (dest->next)) || (!IS_VOID (src->next) && IS_VOID (dest->next)))
|
|
return -1;
|
|
res = compareType (dest->next, src->next);
|
|
|
|
/* All function pointers can be cast (6.6 in the ISO C11 standard) TODO: What about address spaces? */
|
|
if (res == 0 && !bMustCast && IS_DECL (src) && IS_FUNC (src->next) && IS_DECL (dest) && IS_FUNC (dest->next))
|
|
return -1;
|
|
else if (res == 1)
|
|
return bMustCast ? -1 : 1;
|
|
else if (res == -2)
|
|
return bMustCast ? -1 : -2;
|
|
else
|
|
return res;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/* compareType - will do type check return 1 if match, 0 if no match, */
|
|
/* -1 if castable, -2 if only signedness differs */
|
|
/*--------------------------------------------------------------------*/
|
|
int
|
|
compareType (sym_link *dest, sym_link *src)
|
|
{
|
|
if (!dest && !src)
|
|
return 1;
|
|
|
|
if (dest && !src)
|
|
return 0;
|
|
|
|
if (src && !dest)
|
|
return 0;
|
|
|
|
/* if dest is a declarator then */
|
|
if (IS_DECL (dest))
|
|
{
|
|
if (IS_DECL (src))
|
|
{
|
|
if (IS_GENPTR (dest) && IS_GENPTR (src))
|
|
{
|
|
/* banked function pointer */
|
|
if (IS_FUNC (src->next) && IS_VOID (dest->next))
|
|
return -1;
|
|
if (IS_FUNC (dest->next) && IS_VOID (src->next))
|
|
return -1;
|
|
return comparePtrType (dest, src, FALSE);
|
|
}
|
|
|
|
if (DCL_TYPE (src) == DCL_TYPE (dest))
|
|
{
|
|
if (IS_FUNC (src))
|
|
{
|
|
return compareFuncType (dest, src);
|
|
}
|
|
return comparePtrType (dest, src, FALSE);
|
|
}
|
|
if (IS_PTR (dest) && IS_GENPTR (src) && IS_VOID (src->next))
|
|
{
|
|
return -1;
|
|
}
|
|
if (IS_PTR (src) && (IS_GENPTR (dest) || ((DCL_TYPE (src) == POINTER) && (DCL_TYPE (dest) == IPOINTER))))
|
|
{
|
|
return comparePtrType (dest, src, TRUE);
|
|
}
|
|
if (IS_PTR (dest) && IS_ARRAY (src))
|
|
{
|
|
value *val = aggregateToPointer (valFromType (src));
|
|
int res = compareType (dest, val->type);
|
|
Safe_free (val->type);
|
|
Safe_free (val);
|
|
return res;
|
|
}
|
|
if (IS_PTR (dest) && IS_FUNC (dest->next) && IS_FUNC (src))
|
|
{
|
|
return compareType (dest->next, src);
|
|
}
|
|
if (IS_PTR (dest) && IS_VOID (dest->next) && IS_FUNC (src))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
else if (IS_PTR (dest) && IS_INTEGRAL (src))
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
if (IS_PTR (src) && IS_VOID (dest))
|
|
return -1;
|
|
|
|
/* if one is a specifier and the other is not */
|
|
if ((IS_SPEC (src) && !IS_SPEC (dest)) || (IS_SPEC (dest) && !IS_SPEC (src)))
|
|
return 0;
|
|
|
|
/* if one of them is a void then ok */
|
|
if (SPEC_NOUN (dest) == V_VOID && SPEC_NOUN (src) != V_VOID)
|
|
return -1;
|
|
|
|
if (SPEC_NOUN (dest) != V_VOID && SPEC_NOUN (src) == V_VOID)
|
|
return -1;
|
|
|
|
if (SPEC_NOUN (src) == V_BBITFIELD && SPEC_NOUN (dest) != V_BBITFIELD || SPEC_NOUN (src) != V_BBITFIELD && SPEC_NOUN (dest) == V_BBITFIELD)
|
|
return -1;
|
|
|
|
/* if they are both bitfields then if the lengths
|
|
and starts don't match */
|
|
if (IS_BITFIELD (dest) && IS_BITFIELD (src) && (SPEC_BLEN (dest) != SPEC_BLEN (src) || SPEC_BSTR (dest) != SPEC_BSTR (src)))
|
|
return -1;
|
|
|
|
/* it is a specifier */
|
|
if (SPEC_NOUN (dest) != SPEC_NOUN (src))
|
|
{
|
|
if ((SPEC_USIGN (dest) == SPEC_USIGN (src)) &&
|
|
IS_INTEGRAL (dest) && IS_INTEGRAL (src) &&
|
|
/* I would prefer
|
|
bitsForType (dest) == bitsForType (src))
|
|
instead of the next two lines, but the regression tests fail with
|
|
them; I guess it's a problem with replaceCheaperOp */
|
|
(getSize (dest) == getSize (src)) &&
|
|
(IS_BOOLEAN (dest) == IS_BOOLEAN (src)))
|
|
return 1;
|
|
else if (IS_ARITHMETIC (dest) && IS_ARITHMETIC (src))
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
else if (IS_STRUCT (dest))
|
|
{
|
|
if (SPEC_STRUCT (dest) != SPEC_STRUCT (src))
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
if (SPEC_LONG (dest) != SPEC_LONG (src))
|
|
return -1;
|
|
|
|
if (SPEC_LONGLONG (dest) != SPEC_LONGLONG (src))
|
|
return -1;
|
|
|
|
if (SPEC_USIGN (dest) != SPEC_USIGN (src))
|
|
return -2;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/* compareTypeExact - will do type check return 1 if match exactly */
|
|
/*--------------------------------------------------------------------*/
|
|
int
|
|
compareTypeExact (sym_link * dest, sym_link * src, long level)
|
|
{
|
|
STORAGE_CLASS srcScls, destScls;
|
|
|
|
if (!dest && !src)
|
|
return 1;
|
|
|
|
if (dest && !src)
|
|
return 0;
|
|
|
|
if (src && !dest)
|
|
return 0;
|
|
|
|
/* if dest is a declarator then */
|
|
if (IS_DECL (dest))
|
|
{
|
|
if (IS_DECL (src))
|
|
{
|
|
if (DCL_TYPE (src) == DCL_TYPE (dest))
|
|
{
|
|
if ((DCL_TYPE (src) == ARRAY) && (DCL_ELEM (src) != DCL_ELEM (dest)))
|
|
return 0;
|
|
if (DCL_PTR_CONST (src) != DCL_PTR_CONST (dest))
|
|
return 0;
|
|
if (DCL_PTR_VOLATILE (src) != DCL_PTR_VOLATILE (dest))
|
|
return 0;
|
|
if (IS_FUNC (src))
|
|
{
|
|
value *exargs, *acargs, *checkValue;
|
|
|
|
/* verify function return type */
|
|
if (!compareTypeExact (dest->next, src->next, -1))
|
|
return 0;
|
|
if (FUNC_ISISR (dest) != FUNC_ISISR (src))
|
|
return 0;
|
|
if (FUNC_REGBANK (dest) != FUNC_REGBANK (src))
|
|
return 0;
|
|
if (IFFUNC_ISNAKED (dest) != IFFUNC_ISNAKED (src))
|
|
return 0;
|
|
#if 0
|
|
if (IFFUNC_ISREENT (dest) != IFFUNC_ISREENT (src) && argCnt > 1)
|
|
return 0;
|
|
#endif
|
|
|
|
/* compare expected args with actual args */
|
|
exargs = FUNC_ARGS (dest);
|
|
acargs = FUNC_ARGS (src);
|
|
|
|
/* for all the expected args do */
|
|
for (; exargs && acargs; exargs = exargs->next, acargs = acargs->next)
|
|
{
|
|
//checkTypeSanity(acargs->etype, acargs->name);
|
|
|
|
if (IS_AGGREGATE (acargs->type))
|
|
{
|
|
checkValue = copyValue (acargs);
|
|
aggregateToPointer (checkValue);
|
|
}
|
|
else
|
|
checkValue = acargs;
|
|
|
|
#if 0
|
|
if (!compareTypeExact (exargs->type, checkValue->type, -1))
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/* if one them ended we have a problem */
|
|
if ((exargs && !acargs && !IS_VOID (exargs->type)) || (!exargs && acargs && !IS_VOID (acargs->type)))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
return compareTypeExact (dest->next, src->next, level);
|
|
}
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* if one is a specifier and the other is not */
|
|
if ((IS_SPEC (src) && !IS_SPEC (dest)) || (IS_SPEC (dest) && !IS_SPEC (src)))
|
|
return 0;
|
|
|
|
/* if they have a different noun */
|
|
if (SPEC_NOUN (dest) != SPEC_NOUN (src))
|
|
return 0;
|
|
|
|
/* if they are both bitfields then if the lengths
|
|
and starts don't match */
|
|
if (IS_BITFIELD (dest) && IS_BITFIELD (src) && (SPEC_BLEN (dest) != SPEC_BLEN (src) || SPEC_BSTR (dest) != SPEC_BSTR (src)))
|
|
return 0;
|
|
|
|
if (IS_INTEGRAL (dest))
|
|
{
|
|
/* signedness must match */
|
|
if (SPEC_USIGN (dest) != SPEC_USIGN (src))
|
|
return 0;
|
|
/* size must match */
|
|
if (SPEC_SHORT (dest) != SPEC_SHORT (src))
|
|
return 0;
|
|
if (SPEC_LONG (dest) != SPEC_LONG (src))
|
|
return 0;
|
|
if (SPEC_LONGLONG (dest) != SPEC_LONGLONG (src))
|
|
return 0;
|
|
}
|
|
|
|
if (IS_STRUCT (dest))
|
|
{
|
|
if (SPEC_STRUCT (dest) != SPEC_STRUCT (src))
|
|
return 0;
|
|
}
|
|
|
|
if (SPEC_CONST (dest) != SPEC_CONST (src))
|
|
return 0;
|
|
if (SPEC_VOLATILE (dest) != SPEC_VOLATILE (src))
|
|
return 0;
|
|
if (SPEC_STAT (dest) != SPEC_STAT (src))
|
|
return 0;
|
|
if (SPEC_ABSA (dest) != SPEC_ABSA (src))
|
|
return 0;
|
|
if (SPEC_ABSA (dest) && SPEC_ADDR (dest) != SPEC_ADDR (src))
|
|
return 0;
|
|
|
|
destScls = SPEC_SCLS (dest);
|
|
srcScls = SPEC_SCLS (src);
|
|
|
|
/* Compensate for const to const code change in checkSClass() */
|
|
if (((!level) & port->mem.code_ro) && SPEC_CONST (dest))
|
|
{
|
|
if (srcScls == S_CODE && destScls == S_FIXED)
|
|
destScls = S_CODE;
|
|
if (destScls == S_CODE && srcScls == S_FIXED)
|
|
srcScls = S_CODE;
|
|
}
|
|
|
|
/* compensate for allocGlobal() */
|
|
if ((srcScls == S_FIXED || srcScls == S_AUTO) &&
|
|
(port->mem.default_globl_map == xdata) && (destScls == S_XDATA) && (level <= 0))
|
|
{
|
|
srcScls = S_XDATA;
|
|
}
|
|
|
|
if ((level > 0) && !SPEC_STAT (dest))
|
|
{
|
|
/* Compensate for hack-o-matic in checkSClass() */
|
|
if (options.stackAuto || (currFunc && IFFUNC_ISREENT (currFunc->type)))
|
|
{
|
|
if (destScls == S_FIXED)
|
|
destScls = (options.useXstack ? S_XSTACK : S_STACK);
|
|
if (srcScls == S_FIXED)
|
|
srcScls = (options.useXstack ? S_XSTACK : S_STACK);
|
|
}
|
|
else if (TARGET_IS_DS390 || TARGET_IS_DS400 || options.useXstack || TARGET_IS_HC08 || TARGET_IS_S08)
|
|
{
|
|
if (destScls == S_FIXED)
|
|
destScls = S_XDATA;
|
|
if (srcScls == S_FIXED)
|
|
srcScls = S_XDATA;
|
|
}
|
|
}
|
|
|
|
if (srcScls != destScls)
|
|
{
|
|
#if 0
|
|
printf ("level = %ld:%ld\n", level / LEVEL_UNIT, level % LEVEL_UNIT);
|
|
printf ("SPEC_SCLS (src) = %d, SPEC_SCLS (dest) = %d\n", SPEC_SCLS (src), SPEC_SCLS (dest));
|
|
printf ("srcScls = %d, destScls = %d\n", srcScls, destScls);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* compareTypeInexact - will do type check return 1 if representation is same. */
|
|
/* Useful for redundancy elimination. */
|
|
/*---------------------------------------------------------------------------*/
|
|
int
|
|
compareTypeInexact (sym_link *dest, sym_link *src)
|
|
{
|
|
if (!dest && !src)
|
|
return 1;
|
|
|
|
if (dest && !src)
|
|
return 0;
|
|
|
|
if (src && !dest)
|
|
return 0;
|
|
|
|
if (IS_BITFIELD (dest) != IS_BITFIELD (src))
|
|
return 0;
|
|
|
|
if (IS_BITFIELD (dest) && IS_BITFIELD (src) && (SPEC_BLEN (dest) != SPEC_BLEN (src) || SPEC_BSTR (dest) != SPEC_BSTR (src)))
|
|
return 0;
|
|
|
|
if (getSize (dest) != getSize (src))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* inCalleeSaveList - return 1 if found in callee save list */
|
|
/*------------------------------------------------------------------*/
|
|
static int
|
|
calleeCmp (void *p1, void *p2)
|
|
{
|
|
return (strcmp ((char *) p1, (char *) (p2)) == 0);
|
|
}
|
|
|
|
bool
|
|
inCalleeSaveList (char *s)
|
|
{
|
|
if (options.all_callee_saves)
|
|
return 1;
|
|
return isinSetWith (options.calleeSavesSet, s, calleeCmp);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------*/
|
|
/* aggregateToPointer: change an aggregate type function */
|
|
/* argument to a pointer to that type. */
|
|
/*-----------------------------------------------------------------*/
|
|
value *
|
|
aggregateToPointer (value * val)
|
|
{
|
|
if (IS_AGGREGATE (val->type))
|
|
{
|
|
/* if this is a structure */
|
|
if (IS_STRUCT (val->type))
|
|
{
|
|
werror (E_STRUCT_AS_ARG, val->name);
|
|
return NULL;
|
|
}
|
|
|
|
/* change to a pointer depending on the */
|
|
/* storage class specified */
|
|
switch (SPEC_SCLS (val->etype))
|
|
{
|
|
case S_IDATA:
|
|
DCL_TYPE (val->type) = IPOINTER;
|
|
break;
|
|
case S_PDATA:
|
|
DCL_TYPE (val->type) = PPOINTER;
|
|
break;
|
|
case S_FIXED:
|
|
if (SPEC_OCLS (val->etype))
|
|
{
|
|
DCL_TYPE (val->type) = PTR_TYPE (SPEC_OCLS (val->etype));
|
|
}
|
|
else
|
|
{ // this happens for (external) function parameters
|
|
DCL_TYPE (val->type) = port->unqualified_pointer;
|
|
}
|
|
break;
|
|
case S_AUTO:
|
|
DCL_TYPE (val->type) = PTR_TYPE (SPEC_OCLS (val->etype));
|
|
break;
|
|
case S_DATA:
|
|
case S_REGISTER:
|
|
DCL_TYPE (val->type) = POINTER;
|
|
break;
|
|
case S_CODE:
|
|
DCL_TYPE (val->type) = CPOINTER;
|
|
break;
|
|
case S_XDATA:
|
|
DCL_TYPE (val->type) = FPOINTER;
|
|
break;
|
|
case S_EEPROM:
|
|
DCL_TYPE (val->type) = EEPPOINTER;
|
|
break;
|
|
default:
|
|
DCL_TYPE (val->type) = port->unqualified_pointer;
|
|
}
|
|
|
|
/* is there is a symbol associated then */
|
|
/* change the type of the symbol as well */
|
|
if (val->sym)
|
|
{
|
|
val->sym->type = copyLinkChain (val->type);
|
|
val->sym->etype = getSpec (val->sym->type);
|
|
}
|
|
}
|
|
return val;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* checkFunction - does all kinds of check on a function */
|
|
/*------------------------------------------------------------------*/
|
|
int
|
|
checkFunction (symbol * sym, symbol * csym)
|
|
{
|
|
value *exargs, *acargs;
|
|
value *checkValue;
|
|
int argCnt = 0;
|
|
|
|
if (getenv ("DEBUG_SANITY"))
|
|
{
|
|
fprintf (stderr, "checkFunction: %s ", sym->name);
|
|
}
|
|
|
|
if (!IS_FUNC (sym->type))
|
|
{
|
|
werrorfl (sym->fileDef, sym->lineDef, E_SYNTAX_ERROR, sym->name);
|
|
return 0;
|
|
}
|
|
|
|
/* move function specifier from return type to function attributes */
|
|
if (IS_INLINE (sym->etype))
|
|
{
|
|
SPEC_INLINE (sym->etype) = 0;
|
|
FUNC_ISINLINE (sym->type) = 1;
|
|
}
|
|
if (IS_NORETURN (sym->etype))
|
|
{
|
|
SPEC_NORETURN (sym->etype) = 0;
|
|
FUNC_ISNORETURN (sym->type) = 1;
|
|
}
|
|
|
|
/* make sure the type is complete and sane */
|
|
checkTypeSanity (sym->etype, sym->name);
|
|
|
|
/* if not type then some kind of error */
|
|
if (!sym->type)
|
|
return 0;
|
|
|
|
/* if the function has no type then make it return int */
|
|
if (!sym->type->next)
|
|
sym->type->next = sym->etype = newIntLink ();
|
|
|
|
/* function cannot return aggregate */
|
|
if (IS_AGGREGATE (sym->type->next))
|
|
{
|
|
werrorfl (sym->fileDef, sym->lineDef, E_FUNC_AGGR, sym->name);
|
|
return 0;
|
|
}
|
|
|
|
/* check if this function is defined as calleeSaves
|
|
then mark it as such */
|
|
FUNC_CALLEESAVES (sym->type) = inCalleeSaveList (sym->name);
|
|
|
|
/* if interrupt service routine */
|
|
/* then it cannot have arguments */
|
|
if (IFFUNC_ARGS (sym->type) && FUNC_ISISR (sym->type))
|
|
{
|
|
if (!IS_VOID (FUNC_ARGS (sym->type)->type))
|
|
{
|
|
werrorfl (sym->fileDef, sym->lineDef, E_INT_ARGS, sym->name);
|
|
FUNC_ARGS (sym->type) = NULL;
|
|
}
|
|
}
|
|
|
|
if (IFFUNC_ISSHADOWREGS (sym->type) && !FUNC_ISISR (sym->type))
|
|
{
|
|
werrorfl (sym->fileDef, sym->lineDef, E_SHADOWREGS_NO_ISR, sym->name);
|
|
}
|
|
|
|
for (argCnt = 1, acargs = FUNC_ARGS (sym->type); acargs; acargs = acargs->next, argCnt++)
|
|
{
|
|
if (!acargs->sym)
|
|
{
|
|
// this can happen for reentrant functions
|
|
werrorfl (sym->fileDef, sym->lineDef, E_PARAM_NAME_OMITTED, sym->name, argCnt);
|
|
// the show must go on: synthesize a name and symbol
|
|
SNPRINTF (acargs->name, sizeof (acargs->name), "_%s_PARM_%d", sym->name, argCnt);
|
|
acargs->sym = newSymbol (acargs->name, 1);
|
|
SPEC_OCLS (acargs->etype) = istack;
|
|
acargs->sym->type = copyLinkChain (acargs->type);
|
|
acargs->sym->etype = getSpec (acargs->sym->type);
|
|
acargs->sym->_isparm = 1;
|
|
strncpyz (acargs->sym->rname, acargs->name, sizeof (acargs->sym->rname));
|
|
}
|
|
else if (strcmp (acargs->sym->name, acargs->sym->rname) == 0)
|
|
{
|
|
// synthesized name
|
|
werrorfl (sym->fileDef, sym->lineDef, E_PARAM_NAME_OMITTED, sym->name, argCnt);
|
|
}
|
|
}
|
|
argCnt--;
|
|
|
|
/*JCF: Mark the register bank as used */
|
|
RegBankUsed[FUNC_REGBANK (sym->type)] = 1;
|
|
|
|
if (!csym && !(csym = findSymWithLevel (SymbolTab, sym)))
|
|
return 1; /* not defined nothing more to check */
|
|
|
|
/* check if body already present */
|
|
if (csym && IFFUNC_HASBODY (csym->type))
|
|
{
|
|
werrorfl (sym->fileDef, sym->lineDef, E_FUNC_BODY, sym->name);
|
|
return 0;
|
|
}
|
|
|
|
/* check the return value type */
|
|
if (compareType (csym->type, sym->type) <= 0)
|
|
{
|
|
werrorfl (sym->fileDef, sym->lineDef, E_PREV_DECL_CONFLICT, csym->name, "type", csym->fileDef, csym->lineDef);
|
|
printFromToType (csym->type, sym->type);
|
|
return 0;
|
|
}
|
|
|
|
if (FUNC_ISISR (csym->type) != FUNC_ISISR (sym->type))
|
|
werrorfl (sym->fileDef, sym->lineDef, E_PREV_DECL_CONFLICT, csym->name, "interrupt", csym->fileDef, csym->lineDef);
|
|
|
|
/* I don't think this is necessary for interrupts. An isr is a */
|
|
/* root in the calling tree. */
|
|
if ((FUNC_REGBANK (csym->type) != FUNC_REGBANK (sym->type)) && (!FUNC_ISISR (sym->type)))
|
|
werrorfl (sym->fileDef, sym->lineDef, E_PREV_DECL_CONFLICT, csym->name, "using", csym->fileDef, csym->lineDef);
|
|
|
|
if (IFFUNC_ISNAKED (csym->type) != IFFUNC_ISNAKED (sym->type))
|
|
{
|
|
// disabled since __naked has no influence on the calling convention
|
|
// werror (E_PREV_DECL_CONFLICT, csym->name, "__naked", csym->fileDef, csym->lineDef);
|
|
FUNC_ISNAKED (sym->type) = 1;
|
|
}
|
|
|
|
if (FUNC_BANKED (csym->type) || FUNC_BANKED (sym->type))
|
|
{
|
|
if (FUNC_NONBANKED (csym->type) || FUNC_NONBANKED (sym->type))
|
|
{
|
|
werrorfl (sym->fileDef, sym->lineDef, W_BANKED_WITH_NONBANKED);
|
|
FUNC_BANKED (sym->type) = 0;
|
|
FUNC_NONBANKED (sym->type) = 1;
|
|
}
|
|
else
|
|
{
|
|
FUNC_BANKED (sym->type) = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (FUNC_NONBANKED (csym->type) || FUNC_NONBANKED (sym->type))
|
|
{
|
|
FUNC_NONBANKED (sym->type) = 1;
|
|
}
|
|
}
|
|
|
|
/* Really, reentrant should match regardless of argCnt, but */
|
|
/* this breaks some existing code (the fp lib functions). If */
|
|
/* the first argument is always passed the same way, this */
|
|
/* lax checking is ok (but may not be true for in future backends) */
|
|
if (IFFUNC_ISREENT (csym->type) != IFFUNC_ISREENT (sym->type) && argCnt > 1)
|
|
{
|
|
//printf("argCnt = %d\n",argCnt);
|
|
werrorfl (sym->fileDef, sym->lineDef, E_PREV_DECL_CONFLICT, csym->name, "reentrant", csym->fileDef, csym->lineDef);
|
|
}
|
|
|
|
if (IFFUNC_ISWPARAM (csym->type) != IFFUNC_ISWPARAM (sym->type))
|
|
werrorfl (sym->fileDef, sym->lineDef, E_PREV_DECL_CONFLICT, csym->name, "wparam", csym->fileDef, csym->lineDef);
|
|
|
|
if (IFFUNC_ISSHADOWREGS (csym->type) != IFFUNC_ISSHADOWREGS (sym->type))
|
|
werrorfl (sym->fileDef, sym->lineDef, E_PREV_DECL_CONFLICT, csym->name, "shadowregs", csym->fileDef, csym->lineDef);
|
|
|
|
/* compare expected args with actual args */
|
|
exargs = FUNC_ARGS (csym->type);
|
|
acargs = FUNC_ARGS (sym->type);
|
|
|
|
/* for all the expected args do */
|
|
for (argCnt = 1; exargs && acargs; exargs = exargs->next, acargs = acargs->next, argCnt++)
|
|
{
|
|
if (getenv ("DEBUG_SANITY"))
|
|
{
|
|
fprintf (stderr, "checkFunction: %s ", exargs->name);
|
|
}
|
|
/* make sure the type is complete and sane */
|
|
checkTypeSanity (exargs->etype, exargs->name);
|
|
|
|
/* If the actual argument is an array, any prototype
|
|
* will have modified it to a pointer. Duplicate that
|
|
* change here.
|
|
*/
|
|
if (IS_AGGREGATE (acargs->type))
|
|
{
|
|
checkValue = copyValue (acargs);
|
|
aggregateToPointer (checkValue);
|
|
}
|
|
else
|
|
{
|
|
checkValue = acargs;
|
|
}
|
|
|
|
if (compareType (exargs->type, checkValue->type) <= 0)
|
|
{
|
|
werror (E_ARG_TYPE, argCnt);
|
|
printFromToType (exargs->type, checkValue->type);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* if one of them ended we have a problem */
|
|
if ((exargs && !acargs && !IS_VOID (exargs->type)) || (!exargs && acargs && !IS_VOID (acargs->type)))
|
|
werror (E_ARG_COUNT);
|
|
|
|
/* replace with this definition */
|
|
sym->cdef = csym->cdef;
|
|
deleteSym (SymbolTab, csym, csym->name);
|
|
deleteFromSeg (csym);
|
|
addSym (SymbolTab, sym, sym->name, sym->level, sym->block, 1);
|
|
if (IS_EXTERN (csym->etype) && !IS_EXTERN (sym->etype))
|
|
{
|
|
SPEC_EXTR (sym->etype) = 1;
|
|
addSet (&publics, sym);
|
|
}
|
|
|
|
SPEC_STAT (sym->etype) |= SPEC_STAT (csym->etype);
|
|
if (SPEC_STAT (sym->etype) && SPEC_EXTR (sym->etype))
|
|
{
|
|
werrorfl (sym->fileDef, sym->lineDef, E_TWO_OR_MORE_STORAGE_CLASSES, sym->name);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
/* cdbStructBlock - calls struct printing for a blocks */
|
|
/*------------------------------------------------------------------*/
|
|
void
|
|
cdbStructBlock (int block)
|
|
{
|
|
int i;
|
|
bucket **table = StructTab;
|
|
bucket *chain;
|
|
|
|
/* go thru the entire table */
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
for (chain = table[i]; chain; chain = chain->next)
|
|
{
|
|
if (chain->block >= block)
|
|
{
|
|
if (debugFile)
|
|
debugFile->writeType ((structdef *) chain->sym, chain->block, 0, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------------*/
|
|
/* processFuncPtrArgs - does some processing with args of func ptrs*/
|
|
/*-----------------------------------------------------------------*/
|
|
void
|
|
processFuncPtrArgs (sym_link * funcType)
|
|
{
|
|
value *val = FUNC_ARGS (funcType);
|
|
|
|
/* if it is void then remove parameters */
|
|
if (val && IS_VOID (val->type))
|
|
{
|
|
FUNC_ARGS (funcType) = NULL;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------------*/
|
|
/* processFuncArgs - does some processing with function args */
|
|
/*-----------------------------------------------------------------*/
|
|
void
|
|
processFuncArgs (symbol *func)
|
|
{
|
|
value *val;
|
|
int pNum = 1;
|
|
sym_link *funcType = func->type;
|
|
|
|
if (getenv ("SDCC_DEBUG_FUNCTION_POINTERS"))
|
|
fprintf (stderr, "SDCCsymt.c:processFuncArgs(%s)\n", func->name);
|
|
|
|
/* find the function declaration within the type */
|
|
while (funcType && !IS_FUNC (funcType))
|
|
funcType = funcType->next;
|
|
|
|
/* if this function has variable argument list */
|
|
/* then make the function a reentrant one */
|
|
if (IFFUNC_HASVARARGS (funcType) || (options.stackAuto && !func->cdef))
|
|
FUNC_ISREENT (funcType) = 1;
|
|
|
|
/* check if this function is defined as calleeSaves
|
|
then mark it as such */
|
|
FUNC_CALLEESAVES (funcType) = inCalleeSaveList (func->name);
|
|
|
|
/* loop thru all the arguments */
|
|
val = FUNC_ARGS (funcType);
|
|
|
|
/* if it is void then remove parameters */
|
|
if (val && IS_VOID (val->type))
|
|
{
|
|
FUNC_ARGS (funcType) = NULL;
|
|
return;
|
|
}
|
|
|
|
/* reset regparm for the port */
|
|
(*port->reset_regparms) (funcType);
|
|
|
|
/* if any of the arguments is an aggregate */
|
|
/* change it to pointer to the same type */
|
|
while (val)
|
|
{
|
|
int argreg = 0;
|
|
struct dbuf_s dbuf;
|
|
|
|
if (val->sym && val->sym->name)
|
|
for (value *val2 = val->next; val2; val2 = val2->next)
|
|
if (val2->sym && val2->sym->name && !strcmp (val->sym->name, val2->sym->name))
|
|
werror (E_DUPLICATE_PARAMTER_NAME, val->sym->name, func->name);
|
|
|
|
dbuf_init (&dbuf, 128);
|
|
dbuf_printf (&dbuf, "%s parameter %d", func->name, pNum);
|
|
checkTypeSanity (val->etype, dbuf_c_str (&dbuf));
|
|
dbuf_destroy (&dbuf);
|
|
|
|
if (IS_AGGREGATE (val->type))
|
|
{
|
|
aggregateToPointer (val);
|
|
}
|
|
|
|
/* mark it as a register parameter if
|
|
the function does not have VA_ARG
|
|
and as port dictates */
|
|
if (!IFFUNC_HASVARARGS (funcType) && (argreg = (*port->reg_parm) (val->type, FUNC_ISREENT (funcType))))
|
|
{
|
|
SPEC_REGPARM (val->etype) = 1;
|
|
SPEC_ARGREG (val->etype) = argreg;
|
|
|
|
/* is there is a symbol associated then */
|
|
/* change the type of the symbol as well */
|
|
if (val->sym)
|
|
{
|
|
SPEC_REGPARM (val->sym->etype) = 1;
|
|
SPEC_ARGREG (val->sym->etype) = argreg;
|
|
}
|
|
}
|
|
else if (IFFUNC_ISREENT (funcType))
|
|
{
|
|
FUNC_HASSTACKPARM (funcType) = 1;
|
|
}
|
|
|
|
val = val->next;
|
|
pNum++;
|
|
}
|
|
|
|
/* if this is an internal generated function call */
|
|
if (func->cdef)
|
|
{
|
|
/* ignore --stack-auto for this one, we don't know how it is compiled */
|
|
/* simply trust on --int-long-reent or --float-reent */
|
|
if (IFFUNC_ISREENT (funcType))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* if this function is reentrant or */
|
|
/* automatics r 2b stacked then nothing */
|
|
if (IFFUNC_ISREENT (funcType) || options.stackAuto)
|
|
return;
|
|
}
|
|
|
|
val = FUNC_ARGS (funcType);
|
|
pNum = 1;
|
|
while (val)
|
|
{
|
|
/* if a symbolname is not given */
|
|
/* synthesize a variable name */
|
|
if (!val->sym)
|
|
{
|
|
SNPRINTF (val->name, sizeof (val->name), "_%s_PARM_%d", func->name, pNum++);
|
|
val->sym = newSymbol (val->name, 1);
|
|
val->sym->type = copyLinkChain (val->type);
|
|
val->sym->etype = getSpec (val->sym->type);
|
|
val->sym->_isparm = 1;
|
|
if (!defaultOClass (val->sym))
|
|
SPEC_OCLS (val->sym->etype) = port->mem.default_local_map;
|
|
SPEC_OCLS (val->etype) = SPEC_OCLS (val->sym->etype);
|
|
strncpyz (val->sym->rname, val->name, sizeof (val->sym->rname));
|
|
addSymChain (&val->sym);
|
|
}
|
|
else /* symbol name given create synth name */
|
|
{
|
|
SNPRINTF (val->name, sizeof (val->name), "_%s_PARM_%d", func->name, pNum++);
|
|
strncpyz (val->sym->rname, val->name, sizeof (val->sym->rname));
|
|
val->sym->_isparm = 1;
|
|
if (!defaultOClass (val->sym))
|
|
SPEC_OCLS (val->sym->etype) = port->mem.default_local_map;
|
|
SPEC_OCLS (val->etype) = SPEC_OCLS (val->sym->etype);
|
|
}
|
|
if (SPEC_OCLS (val->sym->etype) == pdata)
|
|
val->sym->iaccess = 1;
|
|
if (!isinSet (operKeyReset, val->sym))
|
|
{
|
|
addSet (&operKeyReset, val->sym);
|
|
applyToSet (operKeyReset, resetParmKey);
|
|
}
|
|
val = val->next;
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------------*/
|
|
/* isSymbolEqual - compares two symbols return 1 if they match */
|
|
/*-----------------------------------------------------------------*/
|
|
int
|
|
isSymbolEqual (const symbol * dest, const symbol * src)
|
|
{
|
|
/* if pointers match then equal */
|
|
if (dest == src)
|
|
return 1;
|
|
|
|
/* if one of them is null then don't match */
|
|
if (!dest || !src)
|
|
return 0;
|
|
|
|
/* if both of them have rname match on rname */
|
|
if (dest->rname[0] && src->rname[0])
|
|
return (!strcmp (dest->rname, src->rname));
|
|
|
|
/* otherwise match on name */
|
|
return (!strcmp (dest->name, src->name));
|
|
}
|
|
|
|
void
|
|
PT (sym_link * type)
|
|
{
|
|
printTypeChain (type, 0);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------*/
|
|
/* printTypeChain - prints the type chain in human readable form */
|
|
/*-----------------------------------------------------------------*/
|
|
void
|
|
printTypeChain (sym_link * start, FILE * of)
|
|
{
|
|
struct dbuf_s dbuf;
|
|
int nlr = 0;
|
|
|
|
if (!of)
|
|
{
|
|
of = stdout;
|
|
nlr = 1;
|
|
}
|
|
|
|
dbuf_init (&dbuf, 1024);
|
|
dbuf_printTypeChain (start, &dbuf);
|
|
dbuf_write_and_destroy (&dbuf, of);
|
|
|
|
if (nlr)
|
|
putc ('\n', of);
|
|
}
|
|
|
|
void
|
|
dbuf_printTypeChain (sym_link * start, struct dbuf_s *dbuf)
|
|
{
|
|
value *args;
|
|
sym_link *type, *search;
|
|
STORAGE_CLASS scls;
|
|
static struct dbuf_s dbuf2;
|
|
|
|
if (start == NULL)
|
|
{
|
|
dbuf_append_str (dbuf, "void");
|
|
return;
|
|
}
|
|
|
|
/* Print the chain as it is written in the source: */
|
|
/* start with the last entry. */
|
|
/* However, the storage class at the end of the */
|
|
/* chain really applies to the first in the chain! */
|
|
|
|
for (type = start; type && type->next; type = type->next)
|
|
;
|
|
if (IS_SPEC (type))
|
|
scls = SPEC_SCLS (type);
|
|
else
|
|
scls = 0;
|
|
while (type)
|
|
{
|
|
if (IS_DECL (type))
|
|
{
|
|
switch (DCL_TYPE (type))
|
|
{
|
|
case FUNCTION:
|
|
dbuf_printf (dbuf, "function %s%s",
|
|
(IFFUNC_ISBUILTIN (type) ? "__builtin__ " : ""),
|
|
(IFFUNC_ISJAVANATIVE (type) ? "_JavaNative " : ""));
|
|
dbuf_append_str (dbuf, "( ");
|
|
for (args = FUNC_ARGS (type); args; args = args->next)
|
|
{
|
|
dbuf_printTypeChain (args->type, dbuf);
|
|
if (args->next)
|
|
dbuf_append_str (dbuf, ", ");
|
|
}
|
|
dbuf_append_str (dbuf, ")");
|
|
if (IFFUNC_ISREENT (type) && isTargetKeyword("__reentrant"))
|
|
dbuf_append_str (dbuf, " __reentrant");
|
|
if (FUNC_REGBANK (type))
|
|
{
|
|
dbuf_set_length (&dbuf2, 0);
|
|
dbuf_printf (&dbuf2, " __using(%d)", FUNC_REGBANK (type));
|
|
dbuf_append_str (dbuf, dbuf_c_str (&dbuf2));
|
|
}
|
|
if (IFFUNC_ISBANKEDCALL (type))
|
|
dbuf_append_str (dbuf, " __banked");
|
|
if (IFFUNC_ISZ88DK_CALLEE (type))
|
|
dbuf_append_str (dbuf, " __z88dk_callee");
|
|
if (IFFUNC_ISZ88DK_FASTCALL (type))
|
|
dbuf_append_str (dbuf, " __z88dk_fastcall");
|
|
for (unsigned char i = 0; i < 9; i++)
|
|
if (type->funcAttrs.preserved_regs[i])
|
|
{
|
|
dbuf_append_str (dbuf, " __preserves_regs(");
|
|
for (; i < 9; i++)
|
|
if (type->funcAttrs.preserved_regs[i])
|
|
dbuf_printf (dbuf, " %d", i);
|
|
dbuf_append_str (dbuf, " )");
|
|
break;
|
|
}
|
|
break;
|
|
case GPOINTER:
|
|
dbuf_append_str (dbuf, "generic*");
|
|
break;
|
|
case CPOINTER:
|
|
dbuf_append_str (dbuf, "code*");
|
|
break;
|
|
case FPOINTER:
|
|
dbuf_append_str (dbuf, "xdata*");
|
|
break;
|
|
case EEPPOINTER:
|
|
dbuf_append_str (dbuf, "eeprom*");
|
|
break;
|
|
case POINTER:
|
|
dbuf_append_str (dbuf, "near*");
|
|
break;
|
|
case IPOINTER:
|
|
dbuf_append_str (dbuf, "idata*");
|
|
break;
|
|
case PPOINTER:
|
|
dbuf_append_str (dbuf, "pdata*");
|
|
break;
|
|
case UPOINTER:
|
|
dbuf_append_str (dbuf, "unknown*");
|
|
break;
|
|
case ARRAY:
|
|
if (DCL_ELEM (type))
|
|
{
|
|
dbuf_printf (dbuf, "[%u]", (unsigned int) DCL_ELEM (type));
|
|
}
|
|
else
|
|
{
|
|
dbuf_append_str (dbuf, "[]");
|
|
}
|
|
break;
|
|
default:
|
|
dbuf_append_str (dbuf, "unknown?");
|
|
break;
|
|
}
|
|
if (!IS_FUNC (type))
|
|
{
|
|
if (DCL_PTR_VOLATILE (type))
|
|
{
|
|
dbuf_append_str (dbuf, " volatile");
|
|
}
|
|
if (DCL_PTR_CONST (type))
|
|
{
|
|
dbuf_append_str (dbuf, " const");
|
|
}
|
|
if (DCL_PTR_RESTRICT (type))
|
|
{
|
|
dbuf_append_str (dbuf, " restrict");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (SPEC_VOLATILE (type))
|
|
dbuf_append_str (dbuf, "volatile-");
|
|
if (SPEC_CONST (type))
|
|
dbuf_append_str (dbuf, "const-");
|
|
if (SPEC_USIGN (type))
|
|
dbuf_append_str (dbuf, "unsigned-");
|
|
else if (SPEC_NOUN (type) == V_CHAR)
|
|
dbuf_append_str (dbuf, "signed-");
|
|
switch (SPEC_NOUN (type))
|
|
{
|
|
case V_INT:
|
|
if (IS_LONGLONG (type))
|
|
dbuf_append_str (dbuf, "longlong-");
|
|
else if (IS_LONG (type))
|
|
dbuf_append_str (dbuf, "long-");
|
|
dbuf_append_str (dbuf, "int");
|
|
break;
|
|
|
|
case V_BOOL:
|
|
dbuf_append_str (dbuf, "_Bool");
|
|
break;
|
|
|
|
case V_CHAR:
|
|
dbuf_append_str (dbuf, "char");
|
|
break;
|
|
|
|
case V_VOID:
|
|
dbuf_append_str (dbuf, "void");
|
|
break;
|
|
|
|
case V_FLOAT:
|
|
dbuf_append_str (dbuf, "float");
|
|
break;
|
|
|
|
case V_FIXED16X16:
|
|
dbuf_append_str (dbuf, "fixed16x16");
|
|
break;
|
|
|
|
case V_STRUCT:
|
|
dbuf_printf (dbuf, "struct %s", SPEC_STRUCT (type)->tag);
|
|
break;
|
|
|
|
case V_SBIT:
|
|
dbuf_append_str (dbuf, "sbit");
|
|
break;
|
|
|
|
case V_BIT:
|
|
dbuf_append_str (dbuf, "bit");
|
|
break;
|
|
|
|
case V_BITFIELD:
|
|
dbuf_printf (dbuf, "bitfield {%d,%d}", SPEC_BSTR (type), SPEC_BLEN (type));
|
|
break;
|
|
|
|
case V_BBITFIELD:
|
|
dbuf_printf (dbuf, "_Boolbitfield {%d,%d}", SPEC_BSTR (type), SPEC_BLEN (type));
|
|
break;
|
|
|
|
case V_DOUBLE:
|
|
dbuf_append_str (dbuf, "double");
|
|
break;
|
|
|
|
default:
|
|
dbuf_append_str (dbuf, "unknown type");
|
|
break;
|
|
}
|
|
}
|
|
if (type == start)
|
|
{
|
|
switch (scls)
|
|
{
|
|
case S_FIXED:
|
|
dbuf_append_str (dbuf, " fixed");
|
|
break;
|
|
case S_AUTO:
|
|
dbuf_append_str (dbuf, " auto");
|
|
break;
|
|
case S_REGISTER:
|
|
dbuf_append_str (dbuf, " register");
|
|
break;
|
|
case S_DATA:
|
|
dbuf_append_str (dbuf, " data");
|
|
break;
|
|
case S_XDATA:
|
|
dbuf_append_str (dbuf, " xdata");
|
|
break;
|
|
case S_SFR:
|
|
dbuf_append_str (dbuf, " sfr");
|
|
break;
|
|
case S_SBIT:
|
|
dbuf_append_str (dbuf, " sbit");
|
|
break;
|
|
case S_CODE:
|
|
dbuf_append_str (dbuf, " code");
|
|
break;
|
|
case S_IDATA:
|
|
dbuf_append_str (dbuf, " idata");
|
|
break;
|
|
case S_PDATA:
|
|
dbuf_append_str (dbuf, " pdata");
|
|
break;
|
|
case S_LITERAL:
|
|
dbuf_append_str (dbuf, " literal");
|
|
break;
|
|
case S_STACK:
|
|
dbuf_append_str (dbuf, " stack");
|
|
break;
|
|
case S_XSTACK:
|
|
dbuf_append_str (dbuf, " xstack");
|
|
break;
|
|
case S_BIT:
|
|
dbuf_append_str (dbuf, " bit");
|
|
break;
|
|
case S_EEPROM:
|
|
dbuf_append_str (dbuf, " eeprom");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* search entry in list before "type" */
|
|
for (search = start; search && search->next != type;)
|
|
search = search->next;
|
|
type = search;
|
|
if (type)
|
|
dbuf_append_char (dbuf, ' ');
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/* printTypeChainRaw - prints the type chain in human readable form */
|
|
/* in the raw data structure ordering */
|
|
/*--------------------------------------------------------------------*/
|
|
void
|
|
printTypeChainRaw (sym_link * start, FILE * of)
|
|
{
|
|
int nlr = 0;
|
|
value *args;
|
|
sym_link *type;
|
|
|
|
if (!of)
|
|
{
|
|
of = stdout;
|
|
nlr = 1;
|
|
}
|
|
|
|
if (start == NULL)
|
|
{
|
|
fprintf (of, "void");
|
|
return;
|
|
}
|
|
|
|
type = start;
|
|
|
|
while (type)
|
|
{
|
|
if (IS_DECL (type))
|
|
{
|
|
if (!IS_FUNC (type))
|
|
{
|
|
if (DCL_PTR_VOLATILE (type))
|
|
{
|
|
fprintf (of, "volatile-");
|
|
}
|
|
if (DCL_PTR_CONST (type))
|
|
{
|
|
fprintf (of, "const-");
|
|
}
|
|
if (DCL_PTR_RESTRICT (type))
|
|
{
|
|
fprintf (of, "restrict-");
|
|
}
|
|
}
|
|
switch (DCL_TYPE (type))
|
|
{
|
|
case FUNCTION:
|
|
if (IFFUNC_ISINLINE (type))
|
|
{
|
|
fprintf (of, "inline-");
|
|
}
|
|
if (IFFUNC_ISNORETURN (type))
|
|
{
|
|
fprintf (of, "_Noreturn-");
|
|
}
|
|
fprintf (of, "function %s %s",
|
|
(IFFUNC_ISBUILTIN (type) ? "__builtin__" : " "), (IFFUNC_ISJAVANATIVE (type) ? "_JavaNative" : " "));
|
|
fprintf (of, "( ");
|
|
for (args = FUNC_ARGS (type); args; args = args->next)
|
|
{
|
|
printTypeChain (args->type, of);
|
|
if (args->next)
|
|
fprintf (of, ", ");
|
|
}
|
|
fprintf (of, ") ");
|
|
break;
|
|
case GPOINTER:
|
|
fprintf (of, "generic* ");
|
|
break;
|
|
case CPOINTER:
|
|
fprintf (of, "code* ");
|
|
break;
|
|
case FPOINTER:
|
|
fprintf (of, "xdata* ");
|
|
break;
|
|
case EEPPOINTER:
|
|
fprintf (of, "eeprom* ");
|
|
break;
|
|
case POINTER:
|
|
fprintf (of, "near* ");
|
|
break;
|
|
case IPOINTER:
|
|
fprintf (of, "idata* ");
|
|
break;
|
|
case PPOINTER:
|
|
fprintf (of, "pdata* ");
|
|
break;
|
|
case UPOINTER:
|
|
fprintf (of, "unknown* ");
|
|
break;
|
|
case ARRAY:
|
|
if (DCL_ELEM (type))
|
|
{
|
|
fprintf (of, "[%ud] ", (unsigned int) DCL_ELEM (type));
|
|
}
|
|
else
|
|
{
|
|
fprintf (of, "[] ");
|
|
}
|
|
break;
|
|
}
|
|
if (DCL_TSPEC (type))
|
|
{
|
|
fprintf (of, "{");
|
|
printTypeChainRaw (DCL_TSPEC (type), of);
|
|
fprintf (of, "}");
|
|
}
|
|
}
|
|
else if (IS_SPEC (type))
|
|
{
|
|
switch (SPEC_SCLS (type))
|
|
{
|
|
case S_DATA:
|
|
fprintf (of, "data-");
|
|
break;
|
|
case S_XDATA:
|
|
fprintf (of, "xdata-");
|
|
break;
|
|
case S_SFR:
|
|
fprintf (of, "sfr-");
|
|
break;
|
|
case S_SBIT:
|
|
fprintf (of, "sbit-");
|
|
break;
|
|
case S_CODE:
|
|
fprintf (of, "code-");
|
|
break;
|
|
case S_IDATA:
|
|
fprintf (of, "idata-");
|
|
break;
|
|
case S_PDATA:
|
|
fprintf (of, "pdata-");
|
|
break;
|
|
case S_LITERAL:
|
|
fprintf (of, "literal-");
|
|
break;
|
|
case S_STACK:
|
|
fprintf (of, "stack-");
|
|
break;
|
|
case S_XSTACK:
|
|
fprintf (of, "xstack-");
|
|
break;
|
|
case S_BIT:
|
|
fprintf (of, "bit-");
|
|
break;
|
|
case S_EEPROM:
|
|
fprintf (of, "eeprom-");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (SPEC_VOLATILE (type))
|
|
fprintf (of, "volatile-");
|
|
if (SPEC_CONST (type))
|
|
fprintf (of, "const-");
|
|
if (SPEC_USIGN (type))
|
|
fprintf (of, "unsigned-");
|
|
switch (SPEC_NOUN (type))
|
|
{
|
|
case V_INT:
|
|
if (IS_LONGLONG (type))
|
|
fprintf (of, "longlong-");
|
|
else if (IS_LONG (type))
|
|
fprintf (of, "long-");
|
|
fprintf (of, "int");
|
|
break;
|
|
|
|
case V_BOOL:
|
|
fprintf (of, "_Bool");
|
|
break;
|
|
|
|
case V_CHAR:
|
|
fprintf (of, "char");
|
|
break;
|
|
|
|
case V_VOID:
|
|
fprintf (of, "void");
|
|
break;
|
|
|
|
case V_FLOAT:
|
|
fprintf (of, "float");
|
|
break;
|
|
|
|
case V_FIXED16X16:
|
|
fprintf (of, "fixed16x16");
|
|
break;
|
|
|
|
case V_STRUCT:
|
|
fprintf (of, "struct %s", SPEC_STRUCT (type)->tag);
|
|
break;
|
|
|
|
case V_SBIT:
|
|
fprintf (of, "sbit");
|
|
break;
|
|
|
|
case V_BIT:
|
|
fprintf (of, "bit");
|
|
break;
|
|
|
|
case V_BITFIELD:
|
|
fprintf (of, "bitfield {%d,%d}", SPEC_BSTR (type), SPEC_BLEN (type));
|
|
break;
|
|
|
|
case V_BBITFIELD:
|
|
fprintf (of, "_Boolbitfield {%d,%d}", SPEC_BSTR (type), SPEC_BLEN (type));
|
|
break;
|
|
|
|
case V_DOUBLE:
|
|
fprintf (of, "double");
|
|
break;
|
|
|
|
default:
|
|
fprintf (of, "unknown type");
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
fprintf (of, "NOT_SPEC_OR_DECL");
|
|
type = type->next;
|
|
if (type)
|
|
fputc (' ', of);
|
|
}
|
|
if (nlr)
|
|
fprintf (of, "\n");
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------*/
|
|
/* powof2 - returns power of two for the number if number is pow 2 */
|
|
/*-----------------------------------------------------------------*/
|
|
int
|
|
powof2 (TYPE_TARGET_ULONG num)
|
|
{
|
|
int nshifts = 0;
|
|
int n1s = 0;
|
|
|
|
while (num)
|
|
{
|
|
if (num & 1)
|
|
n1s++;
|
|
num >>= 1;
|
|
nshifts++;
|
|
}
|
|
|
|
if (n1s > 1 || nshifts == 0)
|
|
return -1;
|
|
return nshifts - 1;
|
|
}
|
|
|
|
symbol *fsadd;
|
|
symbol *fssub;
|
|
symbol *fsmul;
|
|
symbol *fsdiv;
|
|
symbol *fseq;
|
|
symbol *fsneq;
|
|
symbol *fslt;
|
|
|
|
symbol *fps16x16_add;
|
|
symbol *fps16x16_sub;
|
|
symbol *fps16x16_mul;
|
|
symbol *fps16x16_div;
|
|
symbol *fps16x16_eq;
|
|
symbol *fps16x16_neq;
|
|
symbol *fps16x16_lt;
|
|
symbol *fps16x16_lteq;
|
|
symbol *fps16x16_gt;
|
|
symbol *fps16x16_gteq;
|
|
|
|
/* Dims: mul/div/mod, BYTE/WORD/DWORD/QWORD, SIGNED/UNSIGNED/BOTH */
|
|
symbol *muldiv[3][4][4];
|
|
symbol *muls16tos32[2];
|
|
/* Dims: BYTE/WORD/DWORD/QWORD SIGNED/UNSIGNED */
|
|
sym_link *multypes[4][2];
|
|
/* Dims: to/from float, BYTE/WORD/DWORD/QWORD, SIGNED/USIGNED */
|
|
symbol *conv[2][4][2];
|
|
/* Dims: to/from fixed16x16, BYTE/WORD/DWORD/QWORD/FLOAT, SIGNED/USIGNED */
|
|
symbol *fp16x16conv[2][5][2];
|
|
/* Dims: shift left/shift right, BYTE/WORD/DWORD/QWORD, SIGNED/UNSIGNED */
|
|
symbol *rlrr[2][4][2];
|
|
|
|
sym_link *charType;
|
|
sym_link *floatType;
|
|
sym_link *fixed16x16Type;
|
|
|
|
symbol *memcpy_builtin;
|
|
|
|
static const char *
|
|
_mangleFunctionName (const char *in)
|
|
{
|
|
if (port->getMangledFunctionName)
|
|
{
|
|
return port->getMangledFunctionName (in);
|
|
}
|
|
else
|
|
{
|
|
return in;
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------------*/
|
|
/* typeFromStr - create a typechain from an encoded string */
|
|
/* basic types - 'b' - bool */
|
|
/* 'c' - char */
|
|
/* 's' - short */
|
|
/* 'i' - int */
|
|
/* 'l' - long */
|
|
/* 'L' - long long */
|
|
/* 'f' - float */
|
|
/* 'q' - fixed16x16 */
|
|
/* 'v' - void */
|
|
/* '*' - pointer - default (GPOINTER) */
|
|
/* modifiers - 'S' - signed */
|
|
/* 'U' - unsigned */
|
|
/* 'C' - const */
|
|
/* pointer modifiers - 'g' - generic */
|
|
/* 'x' - xdata */
|
|
/* 'p' - code */
|
|
/* 'd' - data */
|
|
/* 'F' - function */
|
|
/* examples : "ig*" - generic int * */
|
|
/* "cx*" - char xdata * */
|
|
/* "Ui" - unsigned int */
|
|
/* "Sc" - signed char */
|
|
/*-----------------------------------------------------------------*/
|
|
sym_link *
|
|
typeFromStr (const char *s)
|
|
{
|
|
sym_link *r = newLink (DECLARATOR);
|
|
int sign = 0;
|
|
int usign = 0;
|
|
int constant = 0;
|
|
|
|
do
|
|
{
|
|
sym_link *nr;
|
|
switch (*s)
|
|
{
|
|
case 'S':
|
|
sign = 1;
|
|
break;
|
|
case 'U':
|
|
usign = 1;
|
|
break;
|
|
case 'C':
|
|
constant = 1;
|
|
break;
|
|
case 'b':
|
|
r->xclass = SPECIFIER;
|
|
SPEC_NOUN (r) = V_BOOL;
|
|
break;
|
|
case 'c':
|
|
r->xclass = SPECIFIER;
|
|
SPEC_NOUN (r) = V_CHAR;
|
|
if (!sign && !usign)
|
|
r->select.s.b_implicit_sign = true;
|
|
if (usign)
|
|
{
|
|
SPEC_USIGN (r) = 1;
|
|
usign = 0;
|
|
}
|
|
else if (!sign && !options.signed_char)
|
|
SPEC_USIGN (r) = 1;
|
|
break;
|
|
case 's':
|
|
case 'i':
|
|
r->xclass = SPECIFIER;
|
|
SPEC_NOUN (r) = V_INT;
|
|
break;
|
|
case 'l':
|
|
r->xclass = SPECIFIER;
|
|
SPEC_NOUN (r) = V_INT;
|
|
SPEC_LONG (r) = 1;
|
|
break;
|
|
case 'L':
|
|
r->xclass = SPECIFIER;
|
|
SPEC_NOUN (r) = V_INT;
|
|
SPEC_LONGLONG (r) = 1;
|
|
break;
|
|
case 'f':
|
|
r->xclass = SPECIFIER;
|
|
SPEC_NOUN (r) = V_FLOAT;
|
|
break;
|
|
case 'q':
|
|
r->xclass = SPECIFIER;
|
|
SPEC_NOUN (r) = V_FIXED16X16;
|
|
break;
|
|
case 'v':
|
|
r->xclass = SPECIFIER;
|
|
SPEC_NOUN (r) = V_VOID;
|
|
break;
|
|
case '*':
|
|
DCL_TYPE (r) = port->unqualified_pointer;
|
|
break;
|
|
case 'g':
|
|
case 'x':
|
|
case 'p':
|
|
case 'd':
|
|
case 'F':
|
|
assert (*(s + 1) == '*');
|
|
nr = newLink (DECLARATOR);
|
|
nr->next = r;
|
|
r = nr;
|
|
switch (*s)
|
|
{
|
|
case 'g':
|
|
DCL_TYPE (r) = GPOINTER;
|
|
break;
|
|
case 'x':
|
|
DCL_TYPE (r) = FPOINTER;
|
|
break;
|
|
case 'p':
|
|
DCL_TYPE (r) = CPOINTER;
|
|
break;
|
|
case 'd':
|
|
DCL_TYPE (r) = POINTER;
|
|
break;
|
|
case 'F':
|
|
DCL_TYPE (r) = FUNCTION;
|
|
nr = newLink (DECLARATOR);
|
|
nr->next = r;
|
|
r = nr;
|
|
DCL_TYPE (r) = CPOINTER;
|
|
break;
|
|
}
|
|
s++;
|
|
break;
|
|
default:
|
|
werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "typeFromStr: unknown type");
|
|
fprintf(stderr, "unknown: %s\n", s);
|
|
break;
|
|
}
|
|
if (usign && sign)
|
|
werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "typeFromStr: both signed and unsigned specified");
|
|
if (IS_SPEC (r) && usign)
|
|
{
|
|
SPEC_USIGN (r) = 1;
|
|
usign = 0;
|
|
}
|
|
if (IS_SPEC (r) && constant)
|
|
{
|
|
SPEC_CONST (r) = 1;
|
|
constant = 0;
|
|
}
|
|
s++;
|
|
}
|
|
while (*s);
|
|
return r;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------*/
|
|
/* initCSupport - create functions for C support routines */
|
|
/*-----------------------------------------------------------------*/
|
|
void
|
|
initCSupport (void)
|
|
{
|
|
const char *smuldivmod[] = {
|
|
"mul", "div", "mod"
|
|
};
|
|
const char *sbwd[] = {
|
|
"char", "int", "long", "longlong", "fixed16x16",
|
|
};
|
|
const char *fp16x16sbwd[] = {
|
|
"char", "int", "long", "longlong", "float",
|
|
};
|
|
const char *ssu[] = {
|
|
"s", "su", "us", "u"
|
|
};
|
|
const char *srlrr[] = {
|
|
"rl", "rr"
|
|
};
|
|
/* type as character codes for typeFromStr() */
|
|
const char *sbwdCodes[] = {
|
|
"c", "i", "l", "L",
|
|
"Uc", "Ui", "Ul", "UL"
|
|
};
|
|
|
|
int bwd, su, muldivmod, tofrom, slsr;
|
|
|
|
if (getenv ("SDCC_NO_C_SUPPORT"))
|
|
{
|
|
/* for debugging only */
|
|
return;
|
|
}
|
|
|
|
for (bwd = 0; bwd < 4; bwd++)
|
|
{
|
|
sym_link *l = NULL;
|
|
switch (bwd)
|
|
{
|
|
case 0:
|
|
l = newCharLink ();
|
|
break;
|
|
case 1:
|
|
l = newIntLink ();
|
|
break;
|
|
case 2:
|
|
l = newLongLink ();
|
|
break;
|
|
case 3:
|
|
l = newLongLongLink ();
|
|
break;
|
|
default:
|
|
assert (0);
|
|
}
|
|
multypes[bwd][0] = l;
|
|
multypes[bwd][1] = copyLinkChain (l);
|
|
SPEC_USIGN (multypes[bwd][0]) = 0;
|
|
SPEC_USIGN (multypes[bwd][1]) = 1;
|
|
}
|
|
|
|
floatType = newFloatLink ();
|
|
fixed16x16Type = newFixed16x16Link ();
|
|
charType = (options.signed_char) ? SCHARTYPE : UCHARTYPE;
|
|
|
|
fsadd = funcOfType ("__fsadd", floatType, floatType, 2, options.float_rent);
|
|
fssub = funcOfType ("__fssub", floatType, floatType, 2, options.float_rent);
|
|
fsmul = funcOfType ("__fsmul", floatType, floatType, 2, options.float_rent);
|
|
fsdiv = funcOfType ("__fsdiv", floatType, floatType, 2, options.float_rent);
|
|
fseq = funcOfType ("__fseq", charType, floatType, 2, options.float_rent);
|
|
fsneq = funcOfType ("__fsneq", charType, floatType, 2, options.float_rent);
|
|
fslt = funcOfType ("__fslt", charType, floatType, 2, options.float_rent);
|
|
|
|
fps16x16_add = funcOfType ("__fps16x16_add", fixed16x16Type, fixed16x16Type, 2, options.float_rent);
|
|
fps16x16_sub = funcOfType ("__fps16x16_sub", fixed16x16Type, fixed16x16Type, 2, options.float_rent);
|
|
fps16x16_mul = funcOfType ("__fps16x16_mul", fixed16x16Type, fixed16x16Type, 2, options.float_rent);
|
|
fps16x16_div = funcOfType ("__fps16x16_div", fixed16x16Type, fixed16x16Type, 2, options.float_rent);
|
|
fps16x16_eq = funcOfType ("__fps16x16_eq", charType, fixed16x16Type, 2, options.float_rent);
|
|
fps16x16_neq = funcOfType ("__fps16x16_neq", charType, fixed16x16Type, 2, options.float_rent);
|
|
fps16x16_lt = funcOfType ("__fps16x16_lt", charType, fixed16x16Type, 2, options.float_rent);
|
|
fps16x16_lteq = funcOfType ("__fps16x16_lteq", charType, fixed16x16Type, 2, options.float_rent);
|
|
fps16x16_gt = funcOfType ("__fps16x16_gt", charType, fixed16x16Type, 2, options.float_rent);
|
|
fps16x16_gteq = funcOfType ("__fps16x16_gteq", charType, fixed16x16Type, 2, options.float_rent);
|
|
|
|
for (tofrom = 0; tofrom < 2; tofrom++)
|
|
{
|
|
for (bwd = 0; bwd < 4; bwd++)
|
|
{
|
|
for (su = 0; su < 2; su++)
|
|
{
|
|
struct dbuf_s dbuf;
|
|
|
|
dbuf_init (&dbuf, 128);
|
|
if (tofrom)
|
|
{
|
|
dbuf_printf (&dbuf, "__fs2%s%s", ssu[su * 3], sbwd[bwd]);
|
|
conv[tofrom][bwd][su] = funcOfType (dbuf_c_str (&dbuf), multypes[bwd][su], floatType, 1, options.float_rent);
|
|
}
|
|
else
|
|
{
|
|
dbuf_printf (&dbuf, "__%s%s2fs", ssu[su * 3], sbwd[bwd]);
|
|
conv[tofrom][bwd][su] = funcOfType (dbuf_c_str (&dbuf), floatType, multypes[bwd][su], 1, options.float_rent);
|
|
}
|
|
dbuf_destroy (&dbuf);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (tofrom = 0; tofrom < 2; tofrom++)
|
|
{
|
|
for (bwd = 0; bwd < 5; bwd++)
|
|
{
|
|
for (su = 0; su < 2; su++)
|
|
{
|
|
struct dbuf_s dbuf;
|
|
|
|
dbuf_init (&dbuf, 128);
|
|
if (tofrom)
|
|
{
|
|
dbuf_printf (&dbuf, "__fps16x162%s%s", ssu[su * 3], fp16x16sbwd[bwd]);
|
|
if (bwd == 4)
|
|
fp16x16conv[tofrom][bwd][su] =
|
|
funcOfType (dbuf_c_str (&dbuf), floatType, fixed16x16Type, 1, options.float_rent);
|
|
else
|
|
fp16x16conv[tofrom][bwd][su] =
|
|
funcOfType (dbuf_c_str (&dbuf), multypes[bwd][su], fixed16x16Type, 1, options.float_rent);
|
|
}
|
|
else
|
|
{
|
|
dbuf_printf (&dbuf, "__%s%s2fps16x16", ssu[su * 3], fp16x16sbwd[bwd]);
|
|
if (bwd == 4)
|
|
fp16x16conv[tofrom][bwd][su] =
|
|
funcOfType (dbuf_c_str (&dbuf), fixed16x16Type, floatType, 1, options.float_rent);
|
|
else
|
|
fp16x16conv[tofrom][bwd][su] =
|
|
funcOfType (dbuf_c_str (&dbuf), fixed16x16Type, multypes[bwd][su], 1, options.float_rent);
|
|
}
|
|
dbuf_destroy (&dbuf);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
for (muldivmod = 0; muldivmod < 3; muldivmod++)
|
|
{
|
|
for (bwd = 0; bwd < 4; bwd++)
|
|
{
|
|
for (su = 0; su < 2; su++)
|
|
{
|
|
struct dbuf_s dbuf;
|
|
|
|
dbuf_init (&dbuf, 128);
|
|
dbuf_printf (&dbuf, "_%s%s%s", smuldivmod[muldivmod], ssu[su*3], sbwd[bwd]);
|
|
muldiv[muldivmod][bwd][su] = funcOfType (_mangleFunctionName(dbuf_c_str (&dbuf)), multypes[bwd][su], multypes[bwd][su], 2, options.intlong_rent);
|
|
dbuf_destroy (&dbuf);
|
|
FUNC_NONBANKED (muldiv[muldivmod][bwd][su]->type) = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
muluint() and mulsint() resp. mululong() and mulslong() return the same result.
|
|
Therefore they've been merged into mulint() and mullong().
|
|
*/
|
|
|
|
/* byte */
|
|
|
|
/* _divschar/_modschar return int, so that both
|
|
* 100 / -4 = -25 and -128 / -1 = 128 can be handled correctly
|
|
* (first one would have to be sign extended, second one must not be).
|
|
* Similarly, modschar should be handled, but the iCode introduces cast
|
|
* here and forces '% : s8 x s8 -> s8' ... */
|
|
bwd = 0;
|
|
for (su = 0; su < 4; su++)
|
|
{
|
|
for (muldivmod = 0; muldivmod < 3; muldivmod++)
|
|
{
|
|
/* muluchar, mulschar, mulsuchar and muluschar are separate functions, because e.g. the z80
|
|
port is sign/zero-extending to int before calling mulint() */
|
|
/* div and mod : s8_t x s8_t -> s8_t should be s8_t x s8_t -> s16_t, see below */
|
|
struct dbuf_s dbuf;
|
|
|
|
dbuf_init (&dbuf, 128);
|
|
dbuf_printf (&dbuf, "_%s%s%s", smuldivmod[muldivmod], ssu[su], sbwd[bwd]);
|
|
muldiv[muldivmod][bwd][su] =
|
|
funcOfType (_mangleFunctionName (dbuf_c_str (&dbuf)), multypes[(TARGET_IS_PIC16 && muldivmod == 1 && bwd == 0 && su == 0 || (TARGET_IS_PIC14 || TARGET_IS_STM8 || TARGET_Z80_LIKE || TARGET_PDK_LIKE) && bwd == 0) ? 1 : bwd][su % 2], multypes[bwd][su / 2], 2,
|
|
options.intlong_rent);
|
|
dbuf_destroy (&dbuf);
|
|
}
|
|
}
|
|
|
|
for (bwd = 1; bwd < 4; bwd++)
|
|
{
|
|
for (su = 0; su < 2; su++)
|
|
{
|
|
for (muldivmod = 1; muldivmod < 3; muldivmod++)
|
|
{
|
|
struct dbuf_s dbuf;
|
|
|
|
dbuf_init (&dbuf, 128);
|
|
dbuf_printf (&dbuf, "_%s%s%s", smuldivmod[muldivmod], ssu[su * 3], sbwd[bwd]);
|
|
muldiv[muldivmod][bwd][su] =
|
|
funcOfType (_mangleFunctionName (dbuf_c_str (&dbuf)), multypes[(TARGET_IS_PIC16 && muldivmod == 1 && bwd == 0 && su == 0 || (TARGET_IS_STM8 || TARGET_Z80_LIKE || TARGET_PDK_LIKE) && bwd == 0) ? 1 : bwd][su], multypes[bwd][su], 2,
|
|
options.intlong_rent);
|
|
dbuf_destroy (&dbuf);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* mul only */
|
|
muldivmod = 0;
|
|
/* signed only */
|
|
su = 0;
|
|
/* word, doubleword, and quadword */
|
|
for (bwd = 1; bwd < 4; bwd++)
|
|
{
|
|
/* mul, int/long */
|
|
struct dbuf_s dbuf;
|
|
|
|
dbuf_init (&dbuf, 128);
|
|
dbuf_printf (&dbuf, "_%s%s", smuldivmod[muldivmod], sbwd[bwd]);
|
|
muldiv[muldivmod][bwd][0] =
|
|
funcOfType (_mangleFunctionName (dbuf_c_str (&dbuf)), multypes[bwd][su], multypes[bwd][su], 2, options.intlong_rent);
|
|
dbuf_destroy (&dbuf);
|
|
/* signed = unsigned */
|
|
muldiv[muldivmod][bwd][1] = muldiv[muldivmod][bwd][0];
|
|
}
|
|
|
|
for (slsr = 0; slsr < 2; slsr++)
|
|
{
|
|
for (bwd = 0; bwd < 4; bwd++)
|
|
{
|
|
for (su = 0; su < 2; su++)
|
|
{
|
|
struct dbuf_s dbuf;
|
|
symbol *sym;
|
|
const char *params[2];
|
|
|
|
params[0] = sbwdCodes[bwd + 4*su];
|
|
params[1] = sbwdCodes[0];
|
|
|
|
dbuf_init (&dbuf, 128);
|
|
dbuf_printf (&dbuf, "_%s%s%s", srlrr[slsr], ssu[su * 3], sbwd[bwd]);
|
|
rlrr[slsr][bwd][su] = sym =
|
|
funcOfTypeVarg (_mangleFunctionName (dbuf_c_str (&dbuf)),
|
|
sbwdCodes[bwd + 4*su], 2, ¶ms[0]);
|
|
FUNC_ISREENT (sym->type) = options.intlong_rent ? 1 : 0;
|
|
FUNC_NONBANKED (sym->type) = 1;
|
|
dbuf_destroy (&dbuf);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
const char *iparams[] = {"i", "i"};
|
|
const char *uiparams[] = {"Ui", "Ui"};
|
|
muls16tos32[0] = port->support.has_mulint2long ? funcOfTypeVarg ("__mulsint2slong", "l", 2, iparams) : 0;
|
|
muls16tos32[1] = port->support.has_mulint2long ? funcOfTypeVarg ("__muluint2ulong", "Ul", 2, uiparams) : 0;
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------------*/
|
|
/* initBuiltIns - create prototypes for builtin functions */
|
|
/*-----------------------------------------------------------------*/
|
|
void
|
|
initBuiltIns ()
|
|
{
|
|
int i;
|
|
symbol *sym;
|
|
|
|
if (port->builtintable)
|
|
{
|
|
for (i = 0; port->builtintable[i].name; i++)
|
|
{
|
|
sym = funcOfTypeVarg (port->builtintable[i].name, port->builtintable[i].rtype,
|
|
port->builtintable[i].nParms, (const char **)port->builtintable[i].parm_types);
|
|
FUNC_ISBUILTIN (sym->type) = 1;
|
|
FUNC_ISREENT (sym->type) = 0; /* can never be reentrant */
|
|
}
|
|
}
|
|
|
|
/* initialize memcpy symbol for struct assignment */
|
|
memcpy_builtin = findSym (SymbolTab, NULL, "__builtin_memcpy");
|
|
/* if there is no __builtin_memcpy, use __memcpy instead of an actual builtin */
|
|
if (!memcpy_builtin)
|
|
{
|
|
const char *argTypeStrs[] = {"vg*", "Cvg*", "Ui"};
|
|
memcpy_builtin = funcOfTypeVarg ("__memcpy", "vg*", 3, argTypeStrs);
|
|
FUNC_ISBUILTIN (memcpy_builtin->type) = 0;
|
|
FUNC_ISREENT (memcpy_builtin->type) = options.stackAuto;
|
|
}
|
|
}
|
|
|
|
sym_link *
|
|
validateLink (sym_link * l, const char *macro, const char *args, const char select, const char *file, unsigned line)
|
|
{
|
|
if (l && l->xclass == select)
|
|
{
|
|
return l;
|
|
}
|
|
fprintf (stderr,
|
|
"Internal error: validateLink failed in %s(%s) @ %s:%u:"
|
|
" expected %s, got %s\n",
|
|
macro, args, file, line, DECLSPEC2TXT (select), l ? DECLSPEC2TXT (l->xclass) : "null-link");
|
|
exit (EXIT_FAILURE);
|
|
return l; // never reached, makes compiler happy.
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/* newEnumType - create an integer type compatible with enumerations */
|
|
/*--------------------------------------------------------------------*/
|
|
sym_link *
|
|
newEnumType (symbol * enumlist)
|
|
{
|
|
int min, max, v;
|
|
symbol *sym;
|
|
sym_link *type;
|
|
|
|
if (!enumlist)
|
|
{
|
|
type = newLink (SPECIFIER);
|
|
SPEC_NOUN (type) = V_INT;
|
|
return type;
|
|
}
|
|
|
|
/* Determine the range of the enumerated values */
|
|
sym = enumlist;
|
|
min = max = (int) ulFromVal (valFromType (sym->type));
|
|
for (sym = sym->next; sym; sym = sym->next)
|
|
{
|
|
v = (int) ulFromVal (valFromType (sym->type));
|
|
if (v < min)
|
|
min = v;
|
|
if (v > max)
|
|
max = v;
|
|
}
|
|
|
|
/* Determine the smallest integer type that is compatible with this range */
|
|
type = newLink (SPECIFIER);
|
|
if (min >= 0 && max <= 255)
|
|
{
|
|
SPEC_NOUN (type) = V_CHAR;
|
|
SPEC_USIGN (type) = 1;
|
|
}
|
|
else if (min >= -128 && max <= 127)
|
|
{
|
|
SPEC_NOUN (type) = V_CHAR;
|
|
SPEC_SIGN (type) = 1;
|
|
}
|
|
else if (min >= 0 && max <= 65535)
|
|
{
|
|
SPEC_NOUN (type) = V_INT;
|
|
SPEC_USIGN (type) = 1;
|
|
}
|
|
else if (min >= -32768 && max <= 32767)
|
|
{
|
|
SPEC_NOUN (type) = V_INT;
|
|
}
|
|
else
|
|
{
|
|
SPEC_NOUN (type) = V_INT;
|
|
SPEC_LONG (type) = 1;
|
|
if (min >= 0)
|
|
SPEC_USIGN (type) = 1;
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* isConstant - check if the type is constant */
|
|
/*-------------------------------------------------------------------*/
|
|
int
|
|
isConstant (sym_link * type)
|
|
{
|
|
if (!type)
|
|
return 0;
|
|
|
|
while (IS_ARRAY (type))
|
|
type = type->next;
|
|
|
|
if (IS_SPEC (type))
|
|
return SPEC_CONST (type);
|
|
else
|
|
return DCL_PTR_CONST (type);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* isVolatile - check if the type is volatile */
|
|
/*-------------------------------------------------------------------*/
|
|
int
|
|
isVolatile (sym_link * type)
|
|
{
|
|
if (!type)
|
|
return 0;
|
|
|
|
while (IS_ARRAY (type))
|
|
type = type->next;
|
|
|
|
if (IS_SPEC (type))
|
|
return SPEC_VOLATILE (type);
|
|
else
|
|
return DCL_PTR_VOLATILE (type);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* isRestrict - check if the type is restricted */
|
|
/*-------------------------------------------------------------------*/
|
|
int
|
|
isRestrict (sym_link * type)
|
|
{
|
|
if (!type)
|
|
return 0;
|
|
|
|
while (IS_ARRAY (type))
|
|
type = type->next;
|
|
|
|
if (IS_SPEC (type))
|
|
return SPEC_RESTRICT (type);
|
|
else
|
|
return DCL_PTR_RESTRICT (type);
|
|
}
|