]> git.sur5r.net Git - cc65/blobdiff - src/cc65/declare.c
Optimization for __bzero.
[cc65] / src / cc65 / declare.c
index 20db1f6afcee378ba13087ad002a092ab631ee4d..9f0a94a27d9f267b4fadc266d0fdd5222e44a5b8 100644 (file)
@@ -6,8 +6,8 @@
 /*                                                                           */
 /*                                                                           */
 /*                                                                           */
-/* (C) 1998-2003 Ullrich von Bassewitz                                       */
-/*               Römerstrasse 52                                             */
+/* (C) 1998-2004 Ullrich von Bassewitz                                       */
+/*               Römerstraße 52                                              */
 /*               D-70794 Filderstadt                                         */
 /* EMail:        uz@cc65.org                                                 */
 /*                                                                           */
 #include <errno.h>
 
 /* common */
+#include "addrsize.h"
+#include "mmodel.h"
 #include "xmalloc.h"
 
 /* cc65 */
 #include "anonname.h"
 #include "codegen.h"
 #include "datatype.h"
+#include "declare.h"
 #include "declattr.h"
 #include "error.h"
 #include "expr.h"
@@ -54,7 +57,7 @@
 #include "pragma.h"
 #include "scanner.h"
 #include "symtab.h"
-#include "declare.h"
+#include "typeconv.h"
 
 
 
@@ -67,6 +70,9 @@
 static void ParseTypeSpec (DeclSpec* D, int Default);
 /* Parse a type specificier */
 
+static unsigned ParseInitInternal (type* T, int AllowFlexibleMembers);
+/* Parse initialization of variables. Return the number of data bytes. */
+
 
 
 /*****************************************************************************/
@@ -325,7 +331,7 @@ static SymEntry* ParseStructDecl (const char* Name, type StructType)
                     }
                     FlexibleMember = 1;
                     /* Assume zero for size calculations */
-                    Encode (Decl.Type + 1, 0);
+                    Encode (Decl.Type + 1, FLEXIBLE);
                 } else {
                     StructSize += CheckedSizeOf (Decl.Type);
                 }
@@ -418,7 +424,7 @@ static void ParseTypeSpec (DeclSpec* D, int Default)
                optionalsigned ();
                optionalint ();
                D->Type[0] = T_SHORT;
-               D->Type[1] = T_END;
+               D->Type[1] = T_END;
            }
            break;
 
@@ -456,7 +462,7 @@ static void ParseTypeSpec (DeclSpec* D, int Default)
                    NextToken ();
                    /* FALL THROUGH */
 
-               default:
+               default:
                    D->Type[0] = T_INT;
                    D->Type[1] = T_END;
                    break;
@@ -498,6 +504,18 @@ static void ParseTypeSpec (DeclSpec* D, int Default)
            }
            break;
 
+        case TOK_FLOAT:
+           NextToken ();
+           D->Type[0] = T_FLOAT;
+           D->Type[1] = T_END;
+           break;
+
+        case TOK_DOUBLE:
+           NextToken ();
+           D->Type[0] = T_DOUBLE;
+           D->Type[1] = T_END;
+           break;
+
        case TOK_STRUCT:
        case TOK_UNION:
            StructType = (CurTok.Tok == TOK_STRUCT)? T_STRUCT : T_UNION;
@@ -803,11 +821,18 @@ static FuncDesc* ParseFuncDecl (const DeclSpec* Spec)
        ParseOldStyleParamList (F);
     }
 
+    /* Remember the last function parameter. We need it later for several
+     * purposes, for example when passing stuff to fastcall functions. Since
+     * more symbols are added to the table, it is easier if we remember it
+     * now, since it is currently the last entry in the symbol table.
+     */
+    F->LastParam = GetSymTab()->SymTail;
+
     /* Assign offsets. If the function has a variable parameter list,
      * there's one additional byte (the arg size).
      */
     Offs = (F->Flags & FD_VARIADIC)? 1 : 0;
-    Sym = GetSymTab()->SymTail;
+    Sym = F->LastParam;
     while (Sym) {
        unsigned Size = CheckedSizeOf (Sym->Type);
         if (SymIsRegVar (Sym)) {
@@ -820,6 +845,13 @@ static FuncDesc* ParseFuncDecl (const DeclSpec* Spec)
        Sym = Sym->PrevSym;
     }
 
+    /* Add the default address size for the function */
+    if (CodeAddrSize == ADDR_SIZE_FAR) {
+        F->Flags |= FD_FAR;
+    } else {
+        F->Flags |= FD_NEAR;
+    }
+
     /* Leave the lexical level remembering the symbol tables */
     RememberFunctionLevel (F);
 
@@ -829,39 +861,114 @@ static FuncDesc* ParseFuncDecl (const DeclSpec* Spec)
 
 
 
+static unsigned FunctionModifierFlags (void)
+/* Parse __fastcall__, __near__ and __far__ and return the matching FD_ flags */
+{
+    /* Read the flags */
+    unsigned Flags = FD_NONE;
+    while (CurTok.Tok == TOK_FASTCALL || CurTok.Tok == TOK_NEAR || CurTok.Tok == TOK_FAR) {
+
+        /* Get the flag bit for the next token */
+        unsigned F = FD_NONE;
+        switch (CurTok.Tok) {
+            case TOK_FASTCALL:  F = FD_FASTCALL;       break;
+            case TOK_NEAR:         F = FD_NEAR;        break;
+            case TOK_FAR:          F = FD_FAR;         break;
+            default:            Internal ("Unexpected token: %d", CurTok.Tok);
+        }
+
+        /* Remember the flag for this modifier */
+        if (Flags & F) {
+            Error ("Duplicate modifier");
+        }
+        Flags |= F;
+
+        /* Skip the token */
+        NextToken ();
+    }
+
+    /* Sanity check */
+    if ((Flags & (FD_NEAR | FD_FAR)) == (FD_NEAR | FD_FAR)) {
+        Error ("Cannot specify both, `__near__' and `__far__' modifiers");
+        Flags &= ~(FD_NEAR | FD_FAR);
+    }
+
+    /* Return the flags read */
+    return Flags;
+}
+
+
+
+static void ApplyFunctionModifiers (type* T, unsigned Flags)
+/* Apply a set of function modifier flags to a function */
+{
+    /* Get the function descriptor */
+    FuncDesc* F = GetFuncDesc (T);
+
+    /* Special check for __fastcall__ */
+    if ((Flags & FD_FASTCALL) != 0 && IsVariadicFunc (T)) {
+        Error ("Cannot apply `__fastcall__' to functions with "
+               "variable parameter list");
+        Flags &= ~FD_FASTCALL;
+    }
+
+    /* Remove the default function address size modifiers */
+    F->Flags &= ~(FD_NEAR | FD_FAR);
+
+    /* Add the new modifers */
+    F->Flags |= Flags;
+}
+
+
+
 static void Decl (const DeclSpec* Spec, Declaration* D, unsigned Mode)
 /* Recursively process declarators. Build a type array in reverse order. */
 {
-
+    /* Pointer to something */
     if (CurTok.Tok == TOK_STAR) {
-       type T = T_PTR;
+
+       type T;
+
+        /* Skip the star */
                NextToken ();
+
        /* Allow optional const or volatile qualifiers */
-       T |= OptionalQualifiers (T_QUAL_NONE);
+               T = T_PTR | OptionalQualifiers (T_QUAL_NONE);
+
+        /* Parse the type, the pointer points to */
                Decl (Spec, D, Mode);
+
                *D->T++ = T;
                return;
-    } else if (CurTok.Tok == TOK_LPAREN) {
-               NextToken ();
-               Decl (Spec, D, Mode);
-               ConsumeRParen ();
-    } else if (CurTok.Tok == TOK_FASTCALL) {
+    }
+
+    /* Function modifiers */
+    if (CurTok.Tok == TOK_FASTCALL || CurTok.Tok == TOK_NEAR || CurTok.Tok == TOK_FAR) {
+
        /* Remember the current type pointer */
        type* T = D->T;
-       /* Skip the fastcall token */
-       NextToken ();
+
+       /* Read the flags */
+       unsigned Flags = FunctionModifierFlags ();
+
        /* Parse the function */
        Decl (Spec, D, Mode);
-       /* Set the fastcall flag */
+
+       /* Check that we have a function */
        if (!IsTypeFunc (T) && !IsTypeFuncPtr (T)) {
-           Error ("__fastcall__ modifier applied to non function");
-       } else if (IsVariadicFunc (T)) {
-           Error ("Cannot apply __fastcall__ to functions with variable parameter list");
+           Error ("Function modifier applied to non function");
        } else {
-           FuncDesc* F = GetFuncDesc (T);
-                   F->Flags |= FD_FASTCALL;
-       }
+            ApplyFunctionModifiers (T, Flags);
+        }
+
+       /* Done */
        return;
+    }
+
+    if (CurTok.Tok == TOK_LPAREN) {
+               NextToken ();
+               Decl (Spec, D, Mode);
+               ConsumeRParen ();
     } else {
        /* Things depend on Mode now:
                 *  - Mode == DM_NEED_IDENT means:
@@ -941,7 +1048,6 @@ type* ParseType (type* Type)
     ParseTypeSpec (&Spec, -1);
 
     /* Parse additional declarators */
-    InitDeclaration (&Decl);
     ParseDecl (&Spec, &Decl, DM_NO_IDENT);
 
     /* Copy the type to the target buffer */
@@ -1005,75 +1111,203 @@ void CheckEmptyDecl (const DeclSpec* D)
 
 
 
-static unsigned ParseVoidInit (void)
-/* Parse an initialization of a void variable (special cc65 extension).
- * Return the number of bytes initialized.
+static void SkipInitializer (unsigned BracesExpected)
+/* Skip the remainder of an initializer in case of errors. Try to be somewhat
+ * smart so we don't have too many following errors.
  */
 {
-    ExprDesc lval;
-    unsigned Size;
+    while (CurTok.Tok != TOK_CEOF && CurTok.Tok != TOK_SEMI && BracesExpected > 0) {
+        switch (CurTok.Tok) {
+            case TOK_RCURLY:    --BracesExpected;   break;
+            case TOK_LCURLY:    ++BracesExpected;   break;
+            default:                                break;
+        }
+        NextToken ();
+    }
+}
 
-    ConsumeLCurly ();
 
-    /* Allow an arbitrary list of values */
-    Size = 0;
-    do {
-       ConstExpr (&lval);
-       switch (UnqualifiedType (lval.Type[0])) {
 
-           case T_SCHAR:
-           case T_UCHAR:
-               if ((lval.Flags & E_MCTYPE) == E_TCONST) {
-                   /* Make it byte sized */
-                   lval.ConstVal &= 0xFF;
-               }
-               DefineData (&lval);
-                Size += SIZEOF_CHAR;
-                break;
+static unsigned OpeningCurlyBraces (unsigned BracesNeeded)
+/* Accept any number of opening curly braces around an initialization, skip
+ * them and return the number. If the number of curly braces is less than
+ * BracesNeeded, issue a warning.
+ */
+{
+    unsigned BraceCount = 0;
+    while (CurTok.Tok == TOK_LCURLY) {
+        ++BraceCount;
+        NextToken ();
+    }
+    if (BraceCount < BracesNeeded) {
+        Error ("`{' expected");
+    }
+    return BraceCount;
+}
 
-           case T_SHORT:
-           case T_USHORT:
-           case T_INT:
-           case T_UINT:
-           case T_PTR:
-           case T_ARRAY:
-               if ((lval.Flags & E_MCTYPE) == E_TCONST) {
-                   /* Make it word sized */
-                   lval.ConstVal &= 0xFFFF;
-               }
-               DefineData (&lval);
-               Size += SIZEOF_INT;
-                break;
 
-           case T_LONG:
-           case T_ULONG:
-               DefineData (&lval);
-               Size += SIZEOF_LONG;
-                break;
 
-           default:
-               Error ("Illegal type in initialization");
-               break;
+static void ClosingCurlyBraces (unsigned BracesExpected)
+/* Accept and skip the given number of closing curly braces together with
+ * an optional comma. Output an error messages, if the input does not contain
+ * the expected number of braces.
+ */
+{
+    while (BracesExpected) {
+        if (CurTok.Tok == TOK_RCURLY) {
+            NextToken ();
+        } else if (CurTok.Tok == TOK_COMMA && NextTok.Tok == TOK_RCURLY) {
+            NextToken ();
+            NextToken ();
+        } else {
+            Error ("`}' expected");
+            return;
+        }
+        --BracesExpected;
+    }
+}
 
-       }
 
-       if (CurTok.Tok != TOK_COMMA) {
-           break;
-       }
-       NextToken ();
 
-    } while (CurTok.Tok != TOK_RCURLY);
+static unsigned ParseScalarInit (type* T)
+/* Parse initializaton for scalar data types. Return the number of data bytes. */
+{
+    ExprDesc ED;
 
-    /* Closing brace */
-    ConsumeRCurly ();
+    /* Optional opening brace */
+    unsigned BraceCount = OpeningCurlyBraces (0);
 
-    /* Return the number of bytes initialized */
-    return Size;
+    /* We warn if an initializer for a scalar contains braces, because this is
+     * quite unusual and often a sign for some problem in the input.
+     */
+    if (BraceCount > 0) {
+        Warning ("Braces around scalar initializer");
+    }
+
+    /* Get the expression and convert it to the target type */
+    ConstExpr (&ED);
+    TypeConversion (&ED, 0, T);
+
+    /* Output the data */
+    DefineData (&ED);
+
+    /* Close eventually opening braces */
+    ClosingCurlyBraces (BraceCount);
+
+    /* Done */
+    return SizeOf (T);
 }
 
 
 
-static unsigned ParseStructInit (type* Type)
+static unsigned ParsePointerInit (type* T)
+/* Parse initializaton for pointer data types. Return the number of data bytes. */
+{
+    /* Optional opening brace */
+    unsigned BraceCount = OpeningCurlyBraces (0);
+
+    /* Expression */
+    ExprDesc ED;
+    ConstExpr (&ED);
+    if ((ED.Flags & E_MCTYPE) == E_TCONST) {
+        /* Make the const value the correct size */
+        ED.ConstVal &= 0xFFFF;
+    }
+    TypeConversion (&ED, 0, T);
+
+    /* Output the data */
+    DefineData (&ED);
+
+    /* Close eventually opening braces */
+    ClosingCurlyBraces (BraceCount);
+
+    /* Done */
+    return SIZEOF_PTR;
+}
+
+
+
+static unsigned ParseArrayInit (type* T, int AllowFlexibleMembers)
+/* Parse initializaton for arrays. Return the number of data bytes. */
+{
+    int Count;
+
+    /* Get the array data */
+    type* ElementType    = GetElementType (T);
+    unsigned ElementSize = CheckedSizeOf (ElementType);
+    long ElementCount    = GetElementCount (T);
+
+    /* Special handling for a character array initialized by a literal */
+    if (IsTypeChar (ElementType) && CurTok.Tok == TOK_SCONST) {
+
+        /* Char array initialized by string constant */
+        const char* Str = GetLiteral (CurTok.IVal);
+        Count = GetLiteralPoolOffs () - CurTok.IVal;
+
+        /* Translate into target charset */
+        TranslateLiteralPool (CurTok.IVal);
+
+        /* If the array is one too small for the string literal, omit the
+         * trailing zero.
+         */
+        if (ElementCount != UNSPECIFIED &&
+            ElementCount != FLEXIBLE    &&
+            Count        == ElementCount + 1) {
+            /* Omit the trailing zero */
+            --Count;
+        }
+
+        /* Output the data */
+        g_defbytes (Str, Count);
+
+        /* Remove string from pool */
+        ResetLiteralPoolOffs (CurTok.IVal);
+        NextToken ();
+
+    } else {
+
+        /* Curly brace */
+        ConsumeLCurly ();
+
+        /* Initialize the array members */
+        Count = 0;
+        while (CurTok.Tok != TOK_RCURLY) {
+            /* Flexible array members may not be initialized within
+             * an array (because the size of each element may differ
+             * otherwise).
+             */
+            ParseInitInternal (ElementType, 0);
+            ++Count;
+            if (CurTok.Tok != TOK_COMMA)
+                break;
+            NextToken ();
+        }
+
+        /* Closing curly braces */
+        ConsumeRCurly ();
+    }
+
+
+    if (ElementCount == UNSPECIFIED) {
+        /* Number of elements determined by initializer */
+        Encode (T + 1, Count);
+        ElementCount = Count;
+    } else if (ElementCount == FLEXIBLE && AllowFlexibleMembers) {
+        /* In non ANSI mode, allow initialization of flexible array
+         * members.
+         */
+        ElementCount = Count;
+    } else if (Count < ElementCount) {
+        g_zerobytes ((ElementCount - Count) * ElementSize);
+    } else if (Count > ElementCount) {
+        Error ("Too many initializers");
+    }
+    return ElementCount * ElementSize;
+}
+
+
+
+static unsigned ParseStructInit (type* Type, int AllowFlexibleMembers)
 /* Parse initialization of a struct or union. Return the number of data bytes. */
 {
     SymEntry* Entry;
@@ -1081,6 +1315,7 @@ static unsigned ParseStructInit (type* Type)
     unsigned  StructSize;
     unsigned  Size;
 
+
     /* Consume the opening curly brace */
     ConsumeLCurly ();
 
@@ -1096,7 +1331,9 @@ static unsigned ParseStructInit (type* Type)
     Tab = Entry->V.S.SymTab;
     if (Tab == 0) {
        Error ("Cannot initialize variables with incomplete type");
-       /* Returning here will cause lots of errors, but recovery is difficult */
+        /* Try error recovery */
+        SkipInitializer (1);
+       /* Nothing initialized */
        return 0;
     }
 
@@ -1108,9 +1345,14 @@ static unsigned ParseStructInit (type* Type)
     while (CurTok.Tok != TOK_RCURLY) {
        if (Entry == 0) {
            Error ("Too many initializers");
+            SkipInitializer (1);
            return Size;
        }
-       Size += ParseInit (Entry->Type);
+        /* Parse initialization of one field. Flexible array members may
+         * only be initialized if they are the last field (or part of the
+         * last struct field).
+         */
+       Size += ParseInitInternal (Entry->Type, AllowFlexibleMembers && Entry->NextSym == 0);
        Entry = Entry->NextSym;
        if (CurTok.Tok != TOK_COMMA)
            break;
@@ -1123,97 +1365,111 @@ static unsigned ParseStructInit (type* Type)
     /* If there are struct fields left, reserve additional storage */
     if (Size < StructSize) {
        g_zerobytes (StructSize - Size);
+        Size = StructSize;
     }
 
+    /* Return the actual number of bytes initialized. This number may be
+     * larger than StructSize if flexible array members are present and were
+     * initialized (possible in non ANSI mode).
+     */
+    return Size;
+}
+
+
+
+static unsigned ParseVoidInit (void)
+/* Parse an initialization of a void variable (special cc65 extension).
+ * Return the number of bytes initialized.
+ */
+{
+    ExprDesc lval;
+    unsigned Size;
+
+    /* Opening brace */
+    ConsumeLCurly ();
+
+    /* Allow an arbitrary list of values */
+    Size = 0;
+    do {
+       ConstExpr (&lval);
+       switch (UnqualifiedType (lval.Type[0])) {
+
+           case T_SCHAR:
+           case T_UCHAR:
+               if ((lval.Flags & E_MCTYPE) == E_TCONST) {
+                   /* Make it byte sized */
+                   lval.ConstVal &= 0xFF;
+               }
+               DefineData (&lval);
+                Size += SIZEOF_CHAR;
+                break;
+
+           case T_SHORT:
+           case T_USHORT:
+           case T_INT:
+           case T_UINT:
+           case T_PTR:
+           case T_ARRAY:
+               if ((lval.Flags & E_MCTYPE) == E_TCONST) {
+                   /* Make it word sized */
+                   lval.ConstVal &= 0xFFFF;
+               }
+               DefineData (&lval);
+               Size += SIZEOF_INT;
+                break;
+
+           case T_LONG:
+           case T_ULONG:
+               DefineData (&lval);
+               Size += SIZEOF_LONG;
+                break;
+
+           default:
+               Error ("Illegal type in initialization");
+               break;
+
+       }
+
+       if (CurTok.Tok != TOK_COMMA) {
+           break;
+       }
+       NextToken ();
+
+    } while (CurTok.Tok != TOK_RCURLY);
+
+    /* Closing brace */
+    ConsumeRCurly ();
+
     /* Return the number of bytes initialized */
-    return StructSize;
+    return Size;
 }
 
 
 
-unsigned ParseInit (type* T)
+static unsigned ParseInitInternal (type* T, int AllowFlexibleMembers)
 /* Parse initialization of variables. Return the number of data bytes. */
 {
-    ExprDesc    lval;
-    type*       t;
-    const char* str;
-    int         Count;
-    long        ElementCount;
-    unsigned    ElementSize;
-
     switch (UnqualifiedType (*T)) {
 
        case T_SCHAR:
        case T_UCHAR:
-           ConstExpr (&lval);
-           if ((lval.Flags & E_MCTYPE) == E_TCONST) {
-               /* Make it byte sized */
-               lval.ConstVal &= 0xFF;
-           }
-           assignadjust (T, &lval);
-           DefineData (&lval);
-           return SIZEOF_CHAR;
-
        case T_SHORT:
        case T_USHORT:
        case T_INT:
        case T_UINT:
-       case T_PTR:
-           ConstExpr (&lval);
-           if ((lval.Flags & E_MCTYPE) == E_TCONST) {
-               /* Make it word sized */
-               lval.ConstVal &= 0xFFFF;
-           }
-           assignadjust (T, &lval);
-           DefineData (&lval);
-           return SIZEOF_INT;
-
        case T_LONG:
        case T_ULONG:
-           ConstExpr (&lval);
-           if ((lval.Flags & E_MCTYPE) == E_TCONST) {
-               /* Make it long sized */
-               lval.ConstVal &= 0xFFFFFFFF;
-           }
-           assignadjust (T, &lval);
-           DefineData (&lval);
-           return SIZEOF_LONG;
+            return ParseScalarInit (T);
+
+       case T_PTR:
+            return ParsePointerInit (T);
 
        case T_ARRAY:
-           ElementCount = GetElementCount (T);
-            ElementSize  = CheckedSizeOf (T + DECODE_SIZE + 1);
-           t = T + DECODE_SIZE + 1;
-                   if (IsTypeChar (t) && CurTok.Tok == TOK_SCONST) {
-               str = GetLiteral (CurTok.IVal);
-               Count = GetLiteralPoolOffs () - CurTok.IVal;
-               TranslateLiteralPool (CurTok.IVal);     /* Translate into target charset */
-               g_defbytes (str, Count);
-               ResetLiteralPoolOffs (CurTok.IVal);     /* Remove string from pool */
-               NextToken ();
-           } else {
-               ConsumeLCurly ();
-               Count = 0;
-               while (CurTok.Tok != TOK_RCURLY) {
-                   ParseInit (T + DECODE_SIZE + 1);
-                   ++Count;
-                   if (CurTok.Tok != TOK_COMMA)
-                       break;
-                   NextToken ();
-               }
-               ConsumeRCurly ();
-           }
-           if (ElementCount == UNSPECIFIED) {
-               Encode (T + 1, Count);
-           } else if (Count < ElementCount) {
-               g_zerobytes ((ElementCount - Count) * ElementSize);
-           } else if (Count > ElementCount) {
-               Error ("Too many initializers");
-           }
-            return ElementCount * ElementSize;
+            return ParseArrayInit (T, AllowFlexibleMembers);
 
         case T_STRUCT:
         case T_UNION:
-           return ParseStructInit (T);
+           return ParseStructInit (T, AllowFlexibleMembers);
 
        case T_VOID:
            if (!ANSI) {
@@ -1231,4 +1487,23 @@ unsigned ParseInit (type* T)
 
 
 
+unsigned ParseInit (type* T)
+/* Parse initialization of variables. Return the number of data bytes. */
+{
+    /* Parse the initialization */
+    unsigned Size = ParseInitInternal (T, !ANSI);
+
+    /* The initialization may not generate code on global level, because code
+     * outside function scope will never get executed.
+     */
+    if (HaveGlobalCode ()) {
+        Error ("Non constant initializers");
+        RemoveGlobalCode ();
+    }
+
+    /* Return the size needed for the initialization */
+    return Size;
+}
+
+