root/src/cmd/gc/align.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. rnd
  2. offmod
  3. widstruct
  4. dowidth
  5. checkwidth
  6. defercheckwidth
  7. resumecheckwidth
  8. typeinit
  9. argsize

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include <u.h>
#include <libc.h>
#include "go.h"

/*
 * machine size and rounding
 * alignment is dictated around
 * the size of a pointer, set in betypeinit
 * (see ../6g/galign.c).
 */

static int defercalc;

vlong
rnd(vlong o, vlong r)
{
        if(r < 1 || r > 8 || (r&(r-1)) != 0)
                fatal("rnd");
        return (o+r-1)&~(r-1);
}

static void
offmod(Type *t)
{
        Type *f;
        int32 o;

        o = 0;
        for(f=t->type; f!=T; f=f->down) {
                if(f->etype != TFIELD)
                        fatal("offmod: not TFIELD: %lT", f);
                f->width = o;
                o += widthptr;
                if(o >= MAXWIDTH) {
                        yyerror("interface too large");
                        o = widthptr;
                }
        }
}

static vlong
widstruct(Type *errtype, Type *t, vlong o, int flag)
{
        Type *f;
        int64 w;
        int32 maxalign;
        
        maxalign = flag;
        if(maxalign < 1)
                maxalign = 1;
        for(f=t->type; f!=T; f=f->down) {
                if(f->etype != TFIELD)
                        fatal("widstruct: not TFIELD: %lT", f);
                if(f->type == T) {
                        // broken field, just skip it so that other valid fields
                        // get a width.
                        continue;
                }
                dowidth(f->type);
                if(f->type->align > maxalign)
                        maxalign = f->type->align;
                if(f->type->width < 0)
                        fatal("invalid width %lld", f->type->width);
                w = f->type->width;
                if(f->type->align > 0)
                        o = rnd(o, f->type->align);
                f->width = o;   // really offset for TFIELD
                if(f->nname != N) {
                        // this same stackparam logic is in addrescapes
                        // in typecheck.c.  usually addrescapes runs after
                        // widstruct, in which case we could drop this,
                        // but function closure functions are the exception.
                        if(f->nname->stackparam) {
                                f->nname->stackparam->xoffset = o;
                                f->nname->xoffset = 0;
                        } else
                                f->nname->xoffset = o;
                }
                o += w;
                if(o >= MAXWIDTH) {
                        yyerror("type %lT too large", errtype);
                        o = 8;  // small but nonzero
                }
        }
        // final width is rounded
        if(flag)
                o = rnd(o, maxalign);
        t->align = maxalign;

        // type width only includes back to first field's offset
        if(t->type == T)
                t->width = 0;
        else
                t->width = o - t->type->width;
        return o;
}

void
dowidth(Type *t)
{
        int32 et;
        int64 w;
        int lno;
        Type *t1;

        if(widthptr == 0)
                fatal("dowidth without betypeinit");

        if(t == T)
                return;

        if(t->width > 0)
                return;

        if(t->width == -2) {
                lno = lineno;
                lineno = t->lineno;
                if(!t->broke)
                        yyerror("invalid recursive type %T", t);
                t->width = 0;
                lineno = lno;
                return;
        }

        // defer checkwidth calls until after we're done
        defercalc++;

        lno = lineno;
        lineno = t->lineno;
        t->width = -2;
        t->align = 0;

        et = t->etype;
        switch(et) {
        case TFUNC:
        case TCHAN:
        case TMAP:
        case TSTRING:
                break;

        default:
                /* simtype == 0 during bootstrap */
                if(simtype[t->etype] != 0)
                        et = simtype[t->etype];
                break;
        }

        w = 0;
        switch(et) {
        default:
                fatal("dowidth: unknown type: %T", t);
                break;

        /* compiler-specific stuff */
        case TINT8:
        case TUINT8:
        case TBOOL:             // bool is int8
                w = 1;
                break;
        case TINT16:
        case TUINT16:
                w = 2;
                break;
        case TINT32:
        case TUINT32:
        case TFLOAT32:
                w = 4;
                break;
        case TINT64:
        case TUINT64:
        case TFLOAT64:
        case TCOMPLEX64:
                w = 8;
                t->align = widthreg;
                break;
        case TCOMPLEX128:
                w = 16;
                t->align = widthreg;
                break;
        case TPTR32:
                w = 4;
                checkwidth(t->type);
                break;
        case TPTR64:
                w = 8;
                checkwidth(t->type);
                break;
        case TUNSAFEPTR:
                w = widthptr;
                break;
        case TINTER:            // implemented as 2 pointers
                w = 2*widthptr;
                t->align = widthptr;
                offmod(t);
                break;
        case TCHAN:             // implemented as pointer
                w = widthptr;
                checkwidth(t->type);

                // make fake type to check later to
                // trigger channel argument check.
                t1 = typ(TCHANARGS);
                t1->type = t;
                checkwidth(t1);
                break;
        case TCHANARGS:
                t1 = t->type;
                dowidth(t->type);       // just in case
                if(t1->type->width >= (1<<16))
                        yyerror("channel element type too large (>64kB)");
                t->width = 1;
                break;
        case TMAP:              // implemented as pointer
                w = widthptr;
                checkwidth(t->type);
                checkwidth(t->down);
                break;
        case TFORW:             // should have been filled in
                if(!t->broke)
                        yyerror("invalid recursive type %T", t);
                w = 1;  // anything will do
                break;
        case TANY:
                // dummy type; should be replaced before use.
                if(!debug['A'])
                        fatal("dowidth any");
                w = 1;  // anything will do
                break;
        case TSTRING:
                if(sizeof_String == 0)
                        fatal("early dowidth string");
                w = sizeof_String;
                t->align = widthptr;
                break;
        case TARRAY:
                if(t->type == T)
                        break;
                if(t->bound >= 0) {
                        uint64 cap;

                        dowidth(t->type);
                        if(t->type->width != 0) {
                                cap = (MAXWIDTH-1) / t->type->width;
                                if(t->bound > cap)
                                        yyerror("type %lT larger than address space", t);
                        }
                        w = t->bound * t->type->width;
                        t->align = t->type->align;
                }
                else if(t->bound == -1) {
                        w = sizeof_Array;
                        checkwidth(t->type);
                        t->align = widthptr;
                }
                else if(t->bound == -100) {
                        if(!t->broke) {
                                yyerror("use of [...] array outside of array literal");
                                t->broke = 1;
                        }
                }
                else
                        fatal("dowidth %T", t); // probably [...]T
                break;

        case TSTRUCT:
                if(t->funarg)
                        fatal("dowidth fn struct %T", t);
                w = widstruct(t, t, 0, 1);
                break;

        case TFUNC:
                // make fake type to check later to
                // trigger function argument computation.
                t1 = typ(TFUNCARGS);
                t1->type = t;
                checkwidth(t1);

                // width of func type is pointer
                w = widthptr;
                break;

        case TFUNCARGS:
                // function is 3 cated structures;
                // compute their widths as side-effect.
                t1 = t->type;
                w = widstruct(t->type, *getthis(t1), 0, 0);
                w = widstruct(t->type, *getinarg(t1), w, widthreg);
                w = widstruct(t->type, *getoutarg(t1), w, widthreg);
                t1->argwid = w;
                if(w%widthreg)
                        warn("bad type %T %d\n", t1, w);
                t->align = 1;
                break;
        }

        if(widthptr == 4 && w != (int32)w)
                yyerror("type %T too large", t);

        t->width = w;
        if(t->align == 0) {
                if(w > 8 || (w&(w-1)) != 0)
                        fatal("invalid alignment for %T", t);
                t->align = w;
        }
        lineno = lno;

        if(defercalc == 1)
                resumecheckwidth();
        else
                --defercalc;
}

/*
 * when a type's width should be known, we call checkwidth
 * to compute it.  during a declaration like
 *
 *      type T *struct { next T }
 *
 * it is necessary to defer the calculation of the struct width
 * until after T has been initialized to be a pointer to that struct.
 * similarly, during import processing structs may be used
 * before their definition.  in those situations, calling
 * defercheckwidth() stops width calculations until
 * resumecheckwidth() is called, at which point all the
 * checkwidths that were deferred are executed.
 * dowidth should only be called when the type's size
 * is needed immediately.  checkwidth makes sure the
 * size is evaluated eventually.
 */
typedef struct TypeList TypeList;
struct TypeList {
        Type *t;
        TypeList *next;
};

static TypeList *tlfree;
static TypeList *tlq;

void
checkwidth(Type *t)
{
        TypeList *l;

        if(t == T)
                return;

        // function arg structs should not be checked
        // outside of the enclosing function.
        if(t->funarg)
                fatal("checkwidth %T", t);

        if(!defercalc) {
                dowidth(t);
                return;
        }
        if(t->deferwidth)
                return;
        t->deferwidth = 1;

        l = tlfree;
        if(l != nil)
                tlfree = l->next;
        else
                l = mal(sizeof *l);

        l->t = t;
        l->next = tlq;
        tlq = l;
}

void
defercheckwidth(void)
{
        // we get out of sync on syntax errors, so don't be pedantic.
        if(defercalc && nerrors == 0)
                fatal("defercheckwidth");
        defercalc = 1;
}

void
resumecheckwidth(void)
{
        TypeList *l;

        if(!defercalc)
                fatal("resumecheckwidth");
        for(l = tlq; l != nil; l = tlq) {
                l->t->deferwidth = 0;
                tlq = l->next;
                dowidth(l->t);
                l->next = tlfree;
                tlfree = l;
        }
        defercalc = 0;
}

void
typeinit(void)
{
        int i, etype, sameas;
        Type *t;
        Sym *s, *s1;

        if(widthptr == 0)
                fatal("typeinit before betypeinit");

        for(i=0; i<NTYPE; i++)
                simtype[i] = i;

        types[TPTR32] = typ(TPTR32);
        dowidth(types[TPTR32]);

        types[TPTR64] = typ(TPTR64);
        dowidth(types[TPTR64]);
        
        t = typ(TUNSAFEPTR);
        types[TUNSAFEPTR] = t;
        t->sym = pkglookup("Pointer", unsafepkg);
        t->sym->def = typenod(t);
        
        dowidth(types[TUNSAFEPTR]);

        tptr = TPTR32;
        if(widthptr == 8)
                tptr = TPTR64;

        for(i=TINT8; i<=TUINT64; i++)
                isint[i] = 1;
        isint[TINT] = 1;
        isint[TUINT] = 1;
        isint[TUINTPTR] = 1;

        isfloat[TFLOAT32] = 1;
        isfloat[TFLOAT64] = 1;

        iscomplex[TCOMPLEX64] = 1;
        iscomplex[TCOMPLEX128] = 1;

        isptr[TPTR32] = 1;
        isptr[TPTR64] = 1;

        isforw[TFORW] = 1;

        issigned[TINT] = 1;
        issigned[TINT8] = 1;
        issigned[TINT16] = 1;
        issigned[TINT32] = 1;
        issigned[TINT64] = 1;

        /*
         * initialize okfor
         */
        for(i=0; i<NTYPE; i++) {
                if(isint[i] || i == TIDEAL) {
                        okforeq[i] = 1;
                        okforcmp[i] = 1;
                        okforarith[i] = 1;
                        okforadd[i] = 1;
                        okforand[i] = 1;
                        okforconst[i] = 1;
                        issimple[i] = 1;
                        minintval[i] = mal(sizeof(*minintval[i]));
                        maxintval[i] = mal(sizeof(*maxintval[i]));
                }
                if(isfloat[i]) {
                        okforeq[i] = 1;
                        okforcmp[i] = 1;
                        okforadd[i] = 1;
                        okforarith[i] = 1;
                        okforconst[i] = 1;
                        issimple[i] = 1;
                        minfltval[i] = mal(sizeof(*minfltval[i]));
                        maxfltval[i] = mal(sizeof(*maxfltval[i]));
                }
                if(iscomplex[i]) {
                        okforeq[i] = 1;
                        okforadd[i] = 1;
                        okforarith[i] = 1;
                        okforconst[i] = 1;
                        issimple[i] = 1;
                }
        }

        issimple[TBOOL] = 1;

        okforadd[TSTRING] = 1;

        okforbool[TBOOL] = 1;

        okforcap[TARRAY] = 1;
        okforcap[TCHAN] = 1;

        okforconst[TBOOL] = 1;
        okforconst[TSTRING] = 1;

        okforlen[TARRAY] = 1;
        okforlen[TCHAN] = 1;
        okforlen[TMAP] = 1;
        okforlen[TSTRING] = 1;

        okforeq[TPTR32] = 1;
        okforeq[TPTR64] = 1;
        okforeq[TUNSAFEPTR] = 1;
        okforeq[TINTER] = 1;
        okforeq[TCHAN] = 1;
        okforeq[TSTRING] = 1;
        okforeq[TBOOL] = 1;
        okforeq[TMAP] = 1;      // nil only; refined in typecheck
        okforeq[TFUNC] = 1;     // nil only; refined in typecheck
        okforeq[TARRAY] = 1;    // nil slice only; refined in typecheck
        okforeq[TSTRUCT] = 1;   // it's complicated; refined in typecheck

        okforcmp[TSTRING] = 1;

        for(i=0; i<nelem(okfor); i++)
                okfor[i] = okfornone;

        // binary
        okfor[OADD] = okforadd;
        okfor[OAND] = okforand;
        okfor[OANDAND] = okforbool;
        okfor[OANDNOT] = okforand;
        okfor[ODIV] = okforarith;
        okfor[OEQ] = okforeq;
        okfor[OGE] = okforcmp;
        okfor[OGT] = okforcmp;
        okfor[OLE] = okforcmp;
        okfor[OLT] = okforcmp;
        okfor[OMOD] = okforand;
        okfor[OMUL] = okforarith;
        okfor[ONE] = okforeq;
        okfor[OOR] = okforand;
        okfor[OOROR] = okforbool;
        okfor[OSUB] = okforarith;
        okfor[OXOR] = okforand;
        okfor[OLSH] = okforand;
        okfor[ORSH] = okforand;

        // unary
        okfor[OCOM] = okforand;
        okfor[OMINUS] = okforarith;
        okfor[ONOT] = okforbool;
        okfor[OPLUS] = okforarith;

        // special
        okfor[OCAP] = okforcap;
        okfor[OLEN] = okforlen;

        // comparison
        iscmp[OLT] = 1;
        iscmp[OGT] = 1;
        iscmp[OGE] = 1;
        iscmp[OLE] = 1;
        iscmp[OEQ] = 1;
        iscmp[ONE] = 1;

        mpatofix(maxintval[TINT8], "0x7f");
        mpatofix(minintval[TINT8], "-0x80");
        mpatofix(maxintval[TINT16], "0x7fff");
        mpatofix(minintval[TINT16], "-0x8000");
        mpatofix(maxintval[TINT32], "0x7fffffff");
        mpatofix(minintval[TINT32], "-0x80000000");
        mpatofix(maxintval[TINT64], "0x7fffffffffffffff");
        mpatofix(minintval[TINT64], "-0x8000000000000000");

        mpatofix(maxintval[TUINT8], "0xff");
        mpatofix(maxintval[TUINT16], "0xffff");
        mpatofix(maxintval[TUINT32], "0xffffffff");
        mpatofix(maxintval[TUINT64], "0xffffffffffffffff");

        /* f is valid float if min < f < max.  (min and max are not themselves valid.) */
        mpatoflt(maxfltval[TFLOAT32], "33554431p103");  /* 2^24-1 p (127-23) + 1/2 ulp*/
        mpatoflt(minfltval[TFLOAT32], "-33554431p103");
        mpatoflt(maxfltval[TFLOAT64], "18014398509481983p970"); /* 2^53-1 p (1023-52) + 1/2 ulp */
        mpatoflt(minfltval[TFLOAT64], "-18014398509481983p970");

        maxfltval[TCOMPLEX64] = maxfltval[TFLOAT32];
        minfltval[TCOMPLEX64] = minfltval[TFLOAT32];
        maxfltval[TCOMPLEX128] = maxfltval[TFLOAT64];
        minfltval[TCOMPLEX128] = minfltval[TFLOAT64];

        /* for walk to use in error messages */
        types[TFUNC] = functype(N, nil, nil);

        /* types used in front end */
        // types[TNIL] got set early in lexinit
        types[TIDEAL] = typ(TIDEAL);
        types[TINTER] = typ(TINTER);

        /* simple aliases */
        simtype[TMAP] = tptr;
        simtype[TCHAN] = tptr;
        simtype[TFUNC] = tptr;
        simtype[TUNSAFEPTR] = tptr;

        /* pick up the backend typedefs */
        for(i=0; typedefs[i].name; i++) {
                s = lookup(typedefs[i].name);
                s1 = pkglookup(typedefs[i].name, builtinpkg);

                etype = typedefs[i].etype;
                if(etype < 0 || etype >= nelem(types))
                        fatal("typeinit: %s bad etype", s->name);
                sameas = typedefs[i].sameas;
                if(sameas < 0 || sameas >= nelem(types))
                        fatal("typeinit: %s bad sameas", s->name);
                simtype[etype] = sameas;
                minfltval[etype] = minfltval[sameas];
                maxfltval[etype] = maxfltval[sameas];
                minintval[etype] = minintval[sameas];
                maxintval[etype] = maxintval[sameas];

                t = types[etype];
                if(t != T)
                        fatal("typeinit: %s already defined", s->name);

                t = typ(etype);
                t->sym = s1;

                dowidth(t);
                types[etype] = t;
                s1->def = typenod(t);
        }

        Array_array = rnd(0, widthptr);
        Array_nel = rnd(Array_array+widthptr, widthint);
        Array_cap = rnd(Array_nel+widthint, widthint);
        sizeof_Array = rnd(Array_cap+widthint, widthptr);

        // string is same as slice wo the cap
        sizeof_String = rnd(Array_nel+widthint, widthptr);

        dowidth(types[TSTRING]);
        dowidth(idealstring);
}

/*
 * compute total size of f's in/out arguments.
 */
int
argsize(Type *t)
{
        Iter save;
        Type *fp;
        int64 w, x;

        w = 0;

        fp = structfirst(&save, getoutarg(t));
        while(fp != T) {
                x = fp->width + fp->type->width;
                if(x > w)
                        w = x;
                fp = structnext(&save);
        }

        fp = funcfirst(&save, t);
        while(fp != T) {
                x = fp->width + fp->type->width;
                if(x > w)
                        w = x;
                fp = funcnext(&save);
        }

        w = (w+widthptr-1) & ~(widthptr-1);
        if((int)w != w)
                fatal("argsize too big");
        return w;
}

/* [<][>][^][v][top][bottom][index][help] */