Пример #1
0
int F_AllocRegVar (Function* F, const Type* Type)
/* Allocate a register variable for the given variable type. If the allocation
 * was successful, return the offset of the register variable in the register
 * bank (zero page storage). If there is no register space left, return -1.
 */
{
    /* Allow register variables only on top level and if enabled */
    if (IS_Get (&EnableRegVars) && GetLexicalLevel () == LEX_LEVEL_FUNCTION) {

        /* Get the size of the variable */
        unsigned Size = CheckedSizeOf (Type);

        /* Do we have space left? */
        if (F->RegOffs >= Size) {
            /* Space left. We allocate the variables from high to low addresses,
             * so the adressing is compatible with the saved values on stack.
             * This allows shorter code when saving/restoring the variables.
             */
            F->RegOffs -= Size;
            return F->RegOffs;
        }
    }

    /* No space left or no allocation */
    return -1;
}
Пример #2
0
void GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg)
/* For the given function, lookup register information and store it into
** the given variables. If the function is unknown, assume it will use and
** load all registers.
*/
{
    /* If the function name starts with an underline, it is an external
    ** function. Search for it in the symbol table. If the function does
    ** not start with an underline, it may be a runtime support function.
    ** Search for it in the list of builtin functions.
    */
    if (Name[0] == '_') {
        /* Search in the symbol table, skip the leading underscore */
        SymEntry* E = FindGlobalSym (Name+1);

        /* Did we find it in the top-level table? */
        if (E && IsTypeFunc (E->Type)) {
            FuncDesc* D = E->V.F.Func;

            /* A variadic function will use the Y register (the parameter list
            ** size is passed there). A fastcall function will use the A or A/X
            ** registers. In all other cases, no registers are used. However,
            ** we assume that any function will destroy all registers.
            */
            if ((D->Flags & FD_VARIADIC) != 0) {
                *Use = REG_Y;
            } else if (D->ParamCount > 0 &&
                       (AutoCDecl ?
                        IsQualFastcall (E->Type) :
                        !IsQualCDecl (E->Type))) {
                /* Will use registers depending on the last param. */
                switch (CheckedSizeOf (D->LastParam->Type)) {
                    case 1u:
                        *Use = REG_A;
                        break;
                    case 2u:
                        *Use = REG_AX;
                        break;
                    default:
                        *Use = REG_EAX;
                }
            } else {
                /* Will not use any registers */
                *Use = REG_NONE;
            }

            /* Will destroy all registers */
            *Chg = REG_ALL;

            /* Done */
            return;
        }

    } else if (IsDigit (Name[0]) || Name[0] == '$') {

        /* A call to a numeric address. Assume that anything gets used and
        ** destroyed. This is not a real problem, since numeric addresses
        ** are used mostly in inline assembly anyway.
        */
        *Use = REG_ALL;
        *Chg = REG_ALL;
        return;

    } else {

        /* Search for the function in the list of builtin functions */
        const FuncInfo* Info = bsearch (Name, FuncInfoTable, FuncInfoCount,
                                        sizeof(FuncInfo), CompareFuncInfo);

        /* Do we know the function? */
        if (Info) {
            /* Use the information we have */
            *Use = Info->Use;
            *Chg = Info->Chg;
        } else {
            /* It's an internal function we have no information for. If in
            ** debug mode, output an additional warning, so we have a chance
            ** to fix it. Otherwise assume that the internal function will
            ** use and change all registers.
            */
            if (Debug) {
                fprintf (stderr, "No info about internal function `%s'\n", Name);
            }
            *Use = REG_ALL;
            *Chg = REG_ALL;
        }
        return;
    }

    /* Function not found - assume that the primary register is input, and all
    ** registers are changed
    */
    *Use = REG_EAXY;
    *Chg = REG_ALL;
}
Пример #3
0
void NewFunc (SymEntry* Func)
/* Parse argument declarations and function body. */
{
    int         C99MainFunc = 0;/* Flag for C99 main function returning int */
    SymEntry*   Param;

    /* Get the function descriptor from the function entry */
    FuncDesc* D = Func->V.F.Func;

    /* Allocate the function activation record for the function */
    CurrentFunc = NewFunction (Func);

    /* Reenter the lexical level */
    ReenterFunctionLevel (D);

    /* Check if the function header contains unnamed parameters. These are
     * only allowed in cc65 mode.
     */
    if ((D->Flags & FD_UNNAMED_PARAMS) != 0 && (IS_Get (&Standard) != STD_CC65)) {
        Error ("Parameter name omitted");
    }

    /* Declare two special functions symbols: __fixargs__ and __argsize__.
     * The latter is different depending on the type of the function (variadic
     * or not).
     */
    AddConstSym ("__fixargs__", type_uint, SC_DEF | SC_CONST, D->ParamSize);
    if (D->Flags & FD_VARIADIC) {
        /* Variadic function. The variable must be const. */
        static const Type T[] = { TYPE(T_UCHAR | T_QUAL_CONST), TYPE(T_END) };
        AddLocalSym ("__argsize__", T, SC_DEF | SC_REF | SC_AUTO, 0);
    } else {
        /* Non variadic */
        AddConstSym ("__argsize__", type_uchar, SC_DEF | SC_CONST, D->ParamSize);
    }

    /* Function body now defined */
    Func->Flags |= SC_DEF;

    /* Special handling for main() */
    if (strcmp (Func->Name, "main") == 0) {

        /* Mark this as the main function */
        CurrentFunc->Flags |= FF_IS_MAIN;

        /* Main cannot be a fastcall function */
        if (IsQualFastcall (Func->Type)) {
            Error ("`main' cannot be declared as __fastcall__");
        }

        /* If cc65 extensions aren't enabled, don't allow a main function that
         * doesn't return an int.
         */
        if (IS_Get (&Standard) != STD_CC65 && CurrentFunc->ReturnType[0].C != T_INT) {
            Error ("`main' must always return an int");
        }

        /* Add a forced import of a symbol that is contained in the startup
         * code. This will force the startup code to be linked in.
         */
        g_importstartup ();

        /* If main() takes parameters, generate a forced import to a function
         * that will setup these parameters. This way, programs that do not
         * need the additional code will not get it.
         */
        if (D->ParamCount > 0 || (D->Flags & FD_VARIADIC) != 0) {
            g_importmainargs ();
        }

        /* Determine if this is a main function in a C99 environment that
         * returns an int.
         */
        if (IsTypeInt (F_GetReturnType (CurrentFunc)) &&
            IS_Get (&Standard) == STD_C99) {
            C99MainFunc = 1;
        }
    }

    /* Allocate code and data segments for this function */
    Func->V.F.Seg = PushSegments (Func);

    /* Allocate a new literal pool */
    PushLiteralPool (Func);

    /* If this is a fastcall function, push the last parameter onto the stack */
    if (IsQualFastcall (Func->Type) && D->ParamCount > 0) {

        unsigned Flags;

        /* Fastcall functions may never have an ellipsis or the compiler is buggy */
        CHECK ((D->Flags & FD_VARIADIC) == 0);

        /* Generate the push */
        if (IsTypeFunc (D->LastParam->Type)) {
            /* Pointer to function */
            Flags = CF_PTR;
        } else {
            Flags = TypeOf (D->LastParam->Type) | CF_FORCECHAR;
        }
        g_push (Flags, 0);
    }

    /* Generate function entry code if needed */
    g_enter (TypeOf (Func->Type), F_GetParamSize (CurrentFunc));

    /* If stack checking code is requested, emit a call to the helper routine */
    if (IS_Get (&CheckStack)) {
        g_stackcheck ();
    }

    /* Setup the stack */
    StackPtr = 0;

    /* Walk through the parameter list and allocate register variable space
     * for parameters declared as register. Generate code to swap the contents
     * of the register bank with the save area on the stack.
     */
    Param = D->SymTab->SymHead;
    while (Param && (Param->Flags & SC_PARAM) != 0) {

        /* Check for a register variable */
        if (SymIsRegVar (Param)) {

            /* Allocate space */
            int Reg = F_AllocRegVar (CurrentFunc, Param->Type);

            /* Could we allocate a register? */
            if (Reg < 0) {
                /* No register available: Convert parameter to auto */
                CvtRegVarToAuto (Param);
            } else {
                /* Remember the register offset */
                Param->V.R.RegOffs = Reg;

                /* Generate swap code */
                g_swap_regvars (Param->V.R.SaveOffs, Reg, CheckedSizeOf (Param->Type));
            }
        }

        /* Next parameter */
        Param = Param->NextSym;
    }

    /* Need a starting curly brace */
    ConsumeLCurly ();

    /* Parse local variable declarations if any */
    DeclareLocals ();

    /* Remember the current stack pointer. All variables allocated elsewhere
     * must be dropped when doing a return from an inner block.
     */
    CurrentFunc->TopLevelSP = StackPtr;

    /* Now process statements in this block */
    while (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) {
        Statement (0);
    }

    /* If this is not a void function, and not the main function in a C99
     * environment returning int, output a warning if we didn't see a return
     * statement.
     */
    if (!F_HasVoidReturn (CurrentFunc) && !F_HasReturn (CurrentFunc) && !C99MainFunc) {
        Warning ("Control reaches end of non-void function");
    }

    /* If this is the main function in a C99 environment returning an int, let
     * it always return zero. Note: Actual return statements jump to the return
     * label defined below.
     * The code is removed by the optimizer if unused.
     */
    if (C99MainFunc) {
        g_getimmed (CF_INT | CF_CONST, 0, 0);
    }

    /* Output the function exit code label */
    g_defcodelabel (F_GetRetLab (CurrentFunc));

    /* Restore the register variables */
    F_RestoreRegVars (CurrentFunc);

    /* Generate the exit code */
    g_leave ();

    /* Emit references to imports/exports */
    EmitExternals ();

    /* Emit function debug info */
    F_EmitDebugInfo ();
    EmitDebugInfo ();

    /* Leave the lexical level */
    LeaveFunctionLevel ();

    /* Eat the closing brace */
    ConsumeRCurly ();

    /* Restore the old literal pool, remembering the one for the function */
    Func->V.F.LitPool = PopLiteralPool ();

    /* Switch back to the old segments */
    PopSegments ();

    /* Reset the current function pointer */
    FreeFunction (CurrentFunc);
    CurrentFunc = 0;
}
Пример #4
0
static void F_RestoreRegVars (Function* F)
/* Restore the register variables for the local function if there are any. */
{
    const SymEntry* Sym;

    /* If we don't have register variables in this function, bail out early */
    if (F->RegOffs == RegisterSpace) {
        return;
    }

    /* Save the accumulator if needed */
    if (!F_HasVoidReturn (F)) {
        g_save (CF_CHAR | CF_FORCECHAR);
    }

    /* Get the first symbol from the function symbol table */
    Sym = F->FuncEntry->V.F.Func->SymTab->SymHead;

    /* Walk through all symbols checking for register variables */
    while (Sym) {
        if (SymIsRegVar (Sym)) {

            /* Check for more than one variable */
            int Offs       = Sym->V.R.SaveOffs;
            unsigned Bytes = CheckedSizeOf (Sym->Type);

            while (1) {

                /* Find next register variable */
                const SymEntry* NextSym = Sym->NextSym;
                while (NextSym && !SymIsRegVar (NextSym)) {
                    NextSym = NextSym->NextSym;
                }

                /* If we have a next one, compare the stack offsets */
                if (NextSym) {

                    /* We have a following register variable. Get the size */
                    int Size = CheckedSizeOf (NextSym->Type);

                    /* Adjacent variable? */
                    if (NextSym->V.R.SaveOffs + Size != Offs) {
                        /* No */
                        break;
                    }

                    /* Adjacent variable */
                    Bytes += Size;
                    Offs  -= Size;
                    Sym   = NextSym;

                } else {
                    break;
                }
            }

            /* Restore the memory range */
            g_restore_regvars (Offs, Sym->V.R.RegOffs, Bytes);

        }

        /* Check next symbol */
        Sym = Sym->NextSym;
    }

    /* Restore the accumulator if needed */
    if (!F_HasVoidReturn (F)) {
        g_restore (CF_CHAR | CF_FORCECHAR);
    }
}
Пример #5
0
static void DoConversion (ExprDesc* Expr, const Type* NewType)
/* Emit code to convert the given expression to a new type. */
{
    Type*    OldType;
    unsigned OldSize;
    unsigned NewSize;


    /* Remember the old type */
    OldType = Expr->Type;

    /* If we're converting to void, we're done. Note: This does also cover a
     * conversion void -> void.
     */
    if (IsTypeVoid (NewType)) {
        ED_MakeRVal (Expr);     /* Never an lvalue */
        goto ExitPoint;
    }

    /* Don't allow casts from void to something else. */
    if (IsTypeVoid (OldType)) {
        Error ("Cannot convert from `void' to something else");
        goto ExitPoint;
    }

    /* Get the sizes of the types. Since we've excluded void types, checking
     * for known sizes makes sense here.
     */
    OldSize = CheckedSizeOf (OldType);
    NewSize = CheckedSizeOf (NewType);

    /* lvalue? */
    if (ED_IsLVal (Expr)) {

        /* We have an lvalue. If the new size is smaller than the new one,
         * we don't need to do anything. The compiler will generate code
         * to load only the portion of the value that is actually needed.
         * This works only on a little endian architecture, but that's
         * what we support.
         * If both sizes are equal, do also leave the value alone.
         * If the new size is larger, we must convert the value.
         */
        if (NewSize > OldSize) {
            /* Load the value into the primary */
            LoadExpr (CF_NONE, Expr);

            /* Emit typecast code */
            g_typecast (TypeOf (NewType), TypeOf (OldType) | CF_FORCECHAR);

            /* Value is now in primary and an rvalue */
            ED_MakeRValExpr (Expr);
        }

    } else if (ED_IsLocAbs (Expr)) {

        /* A cast of a constant numeric value to another type. Be sure
         * to handle sign extension correctly.
         */

        /* Get the current and new size of the value */
        unsigned OldBits = OldSize * 8;
        unsigned NewBits = NewSize * 8;

        /* Check if the new datatype will have a smaller range. If it
         * has a larger range, things are ok, since the value is
         * internally already represented by a long.
         */
        if (NewBits <= OldBits) {

            /* Cut the value to the new size */
            Expr->IVal &= (0xFFFFFFFFUL >> (32 - NewBits));

            /* If the new type is signed, sign extend the value */
            if (IsSignSigned (NewType)) {
                if (Expr->IVal & (0x01UL << (NewBits-1))) {
                    /* Beware: Use the safe shift routine here. */
                    Expr->IVal |= shl_l (~0UL, NewBits);
                }
            }
        }

    } else {