Logo Search packages:      
Sourcecode: pcc version File versions  Download package

code.c

/*    $Id: code.c,v 1.52 2011/04/19 17:19:02 ragge Exp $    */
/*
 * Copyright (c) 2008 Michael Shalayeff
 * Copyright (c) 2003 Anders Magnusson (ragge@ludd.luth.se).
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


# include "pass1.h"

static int nsse, ngpr, nrsp, rsaoff;
static int thissse, thisgpr, thisrsp;
enum { INTEGER = 1, INTMEM, SSE, SSEMEM, X87, STRREG, STRMEM, STRCPX };
static const int argregsi[] = { RDI, RSI, RDX, RCX, R08, R09 };
/*
 * The Register Save Area looks something like this.
 * It is put first on stack with fixed offsets.
 * struct {
 *    long regs[6];
 *    double xmm[8][2]; // 16 byte in width
 * };
 */
#define     RSASZ       (6*SZLONG+8*2*SZDOUBLE)
#define     RSALONGOFF(x)     (RSASZ-(x)*SZLONG)
#define     RSADBLOFF(x)      ((8*2*SZDOUBLE)-(x)*SZDOUBLE*2)
/* va_list */
#define     VAARGSZ           (SZINT*2+SZPOINT(CHAR)*2)
#define     VAGPOFF(x)  (x)
#define     VAFPOFF(x)  (x-SZINT)
#define     VAOFA(x)    (x-SZINT-SZINT)
#define     VARSA(x)    (x-SZINT-SZINT-SZPOINT(0))

int lastloc = -1;
static int stroffset;

static int varneeds;
#define     NEED_GPNEXT 001
#define     NEED_FPNEXT 002
#define     NEED_1REGREF      004
#define     NEED_2REGREF      010
#define     NEED_MEMREF 020

static int argtyp(TWORD t, union dimfun *df, struct attr *ap);
static NODE *movtomem(NODE *p, int off, int reg);
static NODE *movtoreg(NODE *p, int rno);
void varattrib(char *name, struct attr *sap);

static char *sectail;

/*
 * Define everything needed to print out some data (or text).
 * This means segment, alignment, visibility, etc.
 */
void
defloc(struct symtab *sp)
{
      extern char *nextsect;
      static char *loctbl[] = { "text", "data", "section .rodata" };
      extern int tbss;
      char *name;
      TWORD t;
      int s;

      if (sp == NULL) {
            lastloc = -1;
            return;
      }
      if (kflag) {
#ifdef MACHOABI
            loctbl[DATA] = "section .data.rel.rw,\"aw\"";
            loctbl[RDATA] = "section .data.rel.ro,\"aw\"";
#else
            loctbl[DATA] = "section .data.rel.rw,\"aw\",@progbits";
            loctbl[RDATA] = "section .data.rel.ro,\"aw\",@progbits";
#endif
      }
      t = sp->stype;
      s = ISFTN(t) ? PROG : ISCON(cqual(t, sp->squal)) ? RDATA : DATA;
      if ((name = sp->soname) == NULL)
            name = exname(sp->sname);

      if (sp->sflags & STLS) {
            if (s != DATA)
                  cerror("non-data symbol in tls section");
            if (tbss)
                  nextsect = ".tbss,\"awT\",@nobits";
            else
                  nextsect = ".tdata,\"awT\",@progbits";
            tbss = 0;
            lastloc = -1;
      }

      varattrib(name, sp->sap);

      if (nextsect) {
            printf("    .section %s%s\n", nextsect, sectail);
            sectail = nextsect = NULL;
            s = -1;
      } else if (s != lastloc)
            printf("    .%s\n", loctbl[s]);
      lastloc = s;
      while (ISARY(t))
            t = DECREF(t);
      s = ISFTN(t) ? ALINT : talign(t, sp->sap);
      if (s > ALCHAR)
            printf("    .align %d\n", s/ALCHAR);
      if (sp->sclass == EXTDEF) {
            printf("\t.globl %s\n", name);
#ifndef MACHOABI
            printf("\t.type %s,@%s\n", name,
                ISFTN(t)? "function" : "object");
#endif
      }
      if (sp->slevel == 0)
            printf("%s:\n", name);
      else
            printf(LABFMT ":\n", sp->soffset);
}

/*
 * Print out variable attributes.
 */
void
varattrib(char *name, struct attr *sap)
{
      extern char *nextsect;
      struct attr *ga;

      if ((ga = attr_find(sap, GCC_ATYP_SECTION)) != NULL) {
            nextsect = ga->sarg(0);
            sectail = ",\"aw\",@progbits";
      }
      if ((ga = attr_find(sap, GCC_ATYP_WEAK)) != NULL)
            printf("    .weak %s\n", name);
      if (attr_find(sap, GCC_ATYP_DESTRUCTOR)) {
            printf("\t.section\t.dtors,\"aw\",@progbits\n");
            printf("\t.align 8\n\t.quad\t%s\n", name);
            lastloc = -1;
      }
      if (attr_find(sap, GCC_ATYP_CONSTRUCTOR)) {
            printf("\t.section\t.ctors,\"aw\",@progbits\n");
            printf("\t.align 8\n\t.quad\t%s\n", name);
            lastloc = -1;
      }
      if ((ga = attr_find(sap, GCC_ATYP_VISIBILITY)) &&
          strcmp(ga->sarg(0), "default"))
            printf("\t.%s %s\n", ga->sarg(0), name);
      if ((ga = attr_find(sap, GCC_ATYP_ALIASWEAK))) {
            printf("    .weak %s\n", ga->sarg(0));
            printf("    .set %s,%s\n", ga->sarg(0), name);
      }
}

/*
 * code for the end of a function
 * deals with struct return here
 * The return value is in (or pointed to by) RETREG.
 */
void
efcode()
{
      struct symtab *sp;
      extern int gotnr;
      TWORD t;
      NODE *p, *r, *l;
      int typ, ssz, rno;

      gotnr = 0;  /* new number for next fun */
      sp = cftnsp;
      t = DECREF(sp->stype);
      if (t != STRTY && t != UNIONTY)
            return;

      /* XXX should have one routine for this */
      ngpr = nsse = 0;
      if ((typ = argtyp(t, sp->sdf, sp->sap)) == STRREG || typ == STRCPX) {
            /* Cast to long pointer and move to the registers */
            /* XXX can overrun struct size */
            /* XXX check carefully for SSE members */

            if ((ssz = tsize(t, sp->sdf, sp->sap)) > SZLONG*2)
                  cerror("efcode1");

            if (typ == STRCPX) {
                  t = DOUBLE;
                  rno = XMM0;
            } else {
                  t = LONG;
                  rno = RAX;
            }
            if (ssz > SZLONG) {
                  p = block(REG, NIL, NIL, INCREF(t), 0, 0);
                  regno(p) = RAX;
                  p = buildtree(UMUL, buildtree(PLUS, p, bcon(1)), NIL);
                  ecomp(movtoreg(p, rno+1));
            }
            p = block(REG, NIL, NIL, INCREF(t), 0, 0);
            regno(p) = RAX;
            p = buildtree(UMUL, p, NIL);
            ecomp(movtoreg(p, rno));
      } else if (typ == STRMEM) {
            r = block(REG, NIL, NIL, INCREF(t), sp->sdf, sp->sap);
            regno(r) = RAX;
            r = buildtree(UMUL, r, NIL);
            l = tempnode(stroffset, INCREF(t), sp->sdf, sp->sap);
            l = buildtree(UMUL, l, NIL);
            ecomp(buildtree(ASSIGN, l, r));
            l = block(REG, NIL, NIL, LONG, 0, 0);
            regno(l) = RAX;
            r = tempnode(stroffset, LONG, 0, 0);
            ecomp(buildtree(ASSIGN, l, r));
      } else
            cerror("efcode");
}

/*
 * code for the beginning of a function; a is an array of
 * indices in symtab for the arguments; n is the number
 */
void
bfcode(struct symtab **s, int cnt)
{
      union arglist *al;
      struct symtab *sp;
      NODE *p, *r;
      TWORD t;
      int i, rno, typ;

      /* recalculate the arg offset and create TEMP moves */
      /* Always do this for reg, even if not optimizing, to free arg regs */
      nsse = ngpr = 0;
      nrsp = ARGINIT;
      if (cftnsp->stype == STRTY+FTN || cftnsp->stype == UNIONTY+FTN) {
            sp = cftnsp;
            if (argtyp(DECREF(sp->stype), sp->sdf, sp->sap) == STRMEM) {
                  r = block(REG, NIL, NIL, LONG, 0, 0);
                  regno(r) = argregsi[ngpr++];
                  p = tempnode(0, r->n_type, r->n_df, r->n_ap);
                  stroffset = regno(p);
                  ecomp(buildtree(ASSIGN, p, r));
            }
      }

      for (i = 0; i < cnt; i++) {
            sp = s[i];

            if (sp == NULL)
                  continue; /* XXX when happens this? */

            switch (typ = argtyp(sp->stype, sp->sdf, sp->sap)) {
            case INTEGER:
            case SSE:
                  if (typ == SSE)
                        rno = XMM0 + nsse++;
                  else
                        rno = argregsi[ngpr++];
                  r = block(REG, NIL, NIL, sp->stype, sp->sdf, sp->sap);
                  regno(r) = rno;
                  p = tempnode(0, sp->stype, sp->sdf, sp->sap);
                  sp->soffset = regno(p);
                  sp->sflags |= STNODE;
                  ecomp(buildtree(ASSIGN, p, r));
                  break;

            case SSEMEM:
                  sp->soffset = nrsp;
                  nrsp += SZDOUBLE;
                  if (xtemps) {
                        p = tempnode(0, sp->stype, sp->sdf, sp->sap);
                        p = buildtree(ASSIGN, p, nametree(sp));
                        sp->soffset = regno(p->n_left);
                        sp->sflags |= STNODE;
                        ecomp(p);
                  }
                  break;

            case INTMEM:
                  sp->soffset = nrsp;
                  nrsp += SZLONG;
                  if (xtemps) {
                        p = tempnode(0, sp->stype, sp->sdf, sp->sap);
                        p = buildtree(ASSIGN, p, nametree(sp));
                        sp->soffset = regno(p->n_left);
                        sp->sflags |= STNODE;
                        ecomp(p);
                  }
                  break;

            case STRMEM: /* Struct in memory */
                  sp->soffset = nrsp;
                  nrsp += tsize(sp->stype, sp->sdf, sp->sap);
                  break;

            case X87: /* long double args */
                  sp->soffset = nrsp;
                  nrsp += SZLDOUBLE;
                  break;

            case STRCPX:
            case STRREG: /* Struct in register */
                  /* Allocate space on stack for the struct */
                  /* For simplicity always fetch two longwords */
                  autooff += (2*SZLONG);

                  if (typ == STRCPX) {
                        t = DOUBLE;
                        rno = XMM0 + nsse++;
                  } else {
                        t = LONG;
                        rno = argregsi[ngpr++];
                  }
                  r = block(REG, NIL, NIL, t, 0, 0);
                  regno(r) = rno;
                  ecomp(movtomem(r, -autooff, FPREG));

                  if (tsize(sp->stype, sp->sdf, sp->sap) > SZLONG) {
                        r = block(REG, NIL, NIL, t, 0, 0);
                        regno(r) = (typ == STRCPX ?
                            XMM0 + nsse++ : argregsi[ngpr++]);
                        ecomp(movtomem(r, -autooff+SZLONG, FPREG));
                  }

                  sp->soffset = -autooff;
                  break;

            default:
                  cerror("bfcode: %d", typ);
            }
      }

      /* Check if there are varargs */
      if (cftnsp->sdf == NULL || cftnsp->sdf->dfun == NULL)
            return; /* no prototype */
      al = cftnsp->sdf->dfun;

      for (; al->type != TELLIPSIS; al++) {
            t = al->type;
            if (t == TNULL)
                  return;
            if (BTYPE(t) == STRTY || BTYPE(t) == UNIONTY)
                  al++;
            for (; t > BTMASK; t = DECREF(t))
                  if (ISARY(t) || ISFTN(t))
                        al++;
      }

      /* fix stack offset */
      SETOFF(autooff, ALMAX);

      /* Save reg arguments in the reg save area */
      p = NIL;
      for (i = ngpr; i < 6; i++) {
            r = block(REG, NIL, NIL, LONG, 0, 0);
            regno(r) = argregsi[i];
            r = movtomem(r, -RSALONGOFF(i)-autooff, FPREG);
            p = (p == NIL ? r : block(COMOP, p, r, INT, 0, 0));
      }
      for (i = nsse; i < 8; i++) {
            r = block(REG, NIL, NIL, DOUBLE, 0, 0);
            regno(r) = i + XMM0;
            r = movtomem(r, -RSADBLOFF(i)-autooff, FPREG);
            p = (p == NIL ? r : block(COMOP, p, r, INT, 0, 0));
      }
      autooff += RSASZ;
      rsaoff = autooff;
      thissse = nsse;
      thisgpr = ngpr;
      thisrsp = nrsp;

      ecomp(p);
}


/*
 * by now, the automatics and register variables are allocated
 */
void
bccode()
{
      SETOFF(autooff, SZINT);
}

/* called just before final exit */
/* flag is 1 if errors, 0 if none */
void
ejobcode(int flag )
{
      if (flag)
            return;

#ifdef MACHOAPI
#define PT(x)
#else
#define     PT(x) printf(".type __pcc_" x ",@function\n")
#endif

      /* printout varargs routines if used */
      if (varneeds & NEED_GPNEXT) {
            printf(".text\n.align 4\n");
            PT("gpnext");
            printf("__pcc_gpnext:\n");
            printf("cmpl $48,(%%rdi)\njae 1f\n");
            printf("movl (%%rdi),%%eax\naddq 16(%%rdi),%%rax\n");
            printf("movq (%%rax),%%rax\naddl $8,(%%rdi)\nret\n");
            printf("1:movq 8(%%rdi),%%rax\nmovq (%%rax),%%rax\n");
            printf("addq $8,8(%%rdi)\nret\n");
      }
      if (varneeds & NEED_FPNEXT) {
            printf(".text\n.align 4\n");
            PT("fpnext");
            printf("__pcc_fpnext:\n");
            printf("cmpl $176,4(%%rdi)\njae 1f\n");
            printf("movl 4(%%rdi),%%eax\naddq 16(%%rdi),%%rax\n");
            printf("movsd (%%rax),%%xmm0\naddl $16,4(%%rdi)\nret\n");
            printf("1:movq 8(%%rdi),%%rax\nmovsd (%%rax),%%xmm0\n");
            printf("addq $8,8(%%rdi)\nret\n");
      }
      if (varneeds & NEED_1REGREF) {
            printf(".text\n.align 4\n");
            PT("1regref");
            printf("__pcc_1regref:\n");
            printf("cmpl $48,(%%rdi)\njae 1f\n");
            printf("movl (%%rdi),%%eax\naddq 16(%%rdi),%%rax\n");
            printf("addl $8,(%%rdi)\nret\n");
            printf("1:movq 8(%%rdi),%%rax\n");
            printf("addq $8,8(%%rdi)\nret\n");
      }
      if (varneeds & NEED_2REGREF) {
            printf(".text\n.align 4\n");
            PT("2regref");
            printf("__pcc_2regref:\n");
            printf("cmpl $40,(%%rdi)\njae 1f\n");
            printf("movl (%%rdi),%%eax\naddq 16(%%rdi),%%rax\n");
            printf("addl $16,(%%rdi)\nret\n");
            printf("1:movq 8(%%rdi),%%rax\n");
            printf("addq $16,8(%%rdi)\nret\n");
      }
      if (varneeds & NEED_MEMREF) {
            printf(".text\n.align 4\n");
            PT("memref");
            printf("__pcc_memref:\n");
            printf("movq 8(%%rdi),%%rax\n");
            printf("addq %%rsi,8(%%rdi)\nret\n");
      }
            

#define _MKSTR(x) #x
#define MKSTR(x) _MKSTR(x)
#define OS MKSTR(TARGOS)
#ifdef MACHOABI
      printf("\t.ident \"PCC: %s (%s)\"\n", PACKAGE_STRING, OS);
#else
        printf("\t.ident \"PCC: %s (%s)\"\n\t.end\n", PACKAGE_STRING, OS);
#endif
}

/*
 * Varargs stuff:
 * The ABI says that va_list should be declared as this typedef.
 * We handcraft it here and then just reference it.
 *
 * typedef struct {
 *    unsigned int gp_offset;
 *    unsigned int fp_offset;
 *    void *overflow_arg_area;
 *    void *reg_save_area;
 * } __builtin_va_list[1];
 *
 * There are a number of asm routines printed out if varargs are used:
 *    long __pcc_gpnext(va)   - get a gpreg value
 *    long __pcc_fpnext(va)   - get a fpreg value
 *    void *__pcc_1regref(va) - get reference to a onereg struct 
 *    void *__pcc_2regref(va) - get reference to a tworeg struct 
 *    void *__pcc_memref(va,sz)     - get reference to a large struct 
 */

static char *gp_offset, *fp_offset, *overflow_arg_area, *reg_save_area;
static char *gpnext, *fpnext, *_1regref, *_2regref, *memref;

void
bjobcode()
{
      struct symtab *sp;
      struct rstack *rp;
      NODE *p, *q;
      char *c;

      gp_offset = addname("gp_offset");
      fp_offset = addname("fp_offset");
      overflow_arg_area = addname("overflow_arg_area");
      reg_save_area = addname("reg_save_area");

      rp = bstruct(NULL, STNAME, NULL);
      p = block(NAME, NIL, NIL, UNSIGNED, 0, 0);
      soumemb(p, gp_offset, 0);
      soumemb(p, fp_offset, 0);
      p->n_type = VOID+PTR;
      p->n_ap = NULL;
      soumemb(p, overflow_arg_area, 0);
      soumemb(p, reg_save_area, 0);
      nfree(p);
      q = dclstruct(rp);
      c = addname("__builtin_va_list");
      p = block(LB, bdty(NAME, c), bcon(1), INT, 0, 0);
      p = tymerge(q, p);
      p->n_sp = lookup(c, 0);
      defid(p, TYPEDEF);
      nfree(q);
      nfree(p);

      /* for the static varargs functions */
#define     MKN(vn, rn, tp) \
      { vn = addname(rn); sp = lookup(vn, SNORMAL); \
        sp->sclass = USTATIC; sp->stype = tp; }

      MKN(gpnext, "__pcc_gpnext", FTN|LONG);
      MKN(fpnext, "__pcc_fpnext", FTN|DOUBLE);
      MKN(_1regref, "__pcc_1regref", FTN|VOID|(PTR<<TSHIFT));
      MKN(_2regref, "__pcc_2regref", FTN|VOID|(PTR<<TSHIFT));
      MKN(memref, "__pcc_memref", FTN|VOID|(PTR<<TSHIFT));
}

static NODE *
mkstkref(int off, TWORD typ)
{
      NODE *p;

      p = block(REG, NIL, NIL, PTR|typ, 0, 0);
      regno(p) = FPREG;
      return buildtree(PLUS, p, bcon(off/SZCHAR));
}

NODE *
amd64_builtin_stdarg_start(NODE *f, NODE *a, TWORD t)
{
      NODE *p, *r;

      /* use the values from the function header */
      p = a->n_left;
      r = buildtree(ASSIGN, structref(ccopy(p), STREF, reg_save_area),
          mkstkref(-rsaoff, VOID));
      r = buildtree(COMOP, r,
          buildtree(ASSIGN, structref(ccopy(p), STREF, overflow_arg_area),
          mkstkref(thisrsp, VOID)));
      r = buildtree(COMOP, r,
          buildtree(ASSIGN, structref(ccopy(p), STREF, gp_offset),
          bcon(thisgpr*(SZLONG/SZCHAR))));
      r = buildtree(COMOP, r,
          buildtree(ASSIGN, structref(ccopy(p), STREF, fp_offset),
          bcon(thissse*(SZDOUBLE*2/SZCHAR)+48)));

      tfree(f);
      tfree(a);
      return r;
}

NODE *
amd64_builtin_va_arg(NODE *f, NODE *a, TWORD t)
{
      NODE *ap, *r, *dp;

      ap = a->n_left;
      dp = a->n_right;
      if (dp->n_type <= ULONGLONG || ISPTR(dp->n_type) ||
          dp->n_type == FLOAT || dp->n_type == DOUBLE) {
            /* type might be in general register */
            if (dp->n_type == FLOAT || dp->n_type == DOUBLE) {
                  f->n_sp = lookup(fpnext, SNORMAL);
                  varneeds |= NEED_FPNEXT;
            } else {
                  f->n_sp = lookup(gpnext, SNORMAL);
                  varneeds |= NEED_GPNEXT;
            }
            f->n_type = f->n_sp->stype = INCREF(dp->n_type) + (FTN-PTR);
            f->n_ap = dp->n_ap;
            f->n_df = /* dp->n_df */ NULL;
            f = clocal(f);
            r = buildtree(CALL, f, ccopy(ap));
      } else if (ISSOU(dp->n_type) || dp->n_type == LDOUBLE) {
            /* put a reference directly to the stack */
            int sz = tsize(dp->n_type, dp->n_df, dp->n_ap);
            int al = talign(dp->n_type, dp->n_ap);
            if (al < ALLONG)
                  al = ALLONG;
            if (sz <= SZLONG*2 && al == ALLONG) {
                  if (sz <= SZLONG) {
                        f->n_sp = lookup(_1regref, SNORMAL);
                        varneeds |= NEED_1REGREF;
                  } else {
                        f->n_sp = lookup(_2regref, SNORMAL);
                        varneeds |= NEED_2REGREF;
                  }
                  f->n_type = f->n_sp->stype;
                  f = clocal(f);
                  r = buildtree(CALL, f, ccopy(ap));
                  r = ccast(r, INCREF(dp->n_type), 0, dp->n_df, dp->n_ap);
                  r = buildtree(UMUL, r, NIL);
            } else {
                  f->n_sp = lookup(memref, SNORMAL);
                  varneeds |= NEED_MEMREF;
                  f->n_type = f->n_sp->stype;
                  f = clocal(f);
                  SETOFF(sz, al);
                  r = buildtree(CALL, f,
                      buildtree(CM, ccopy(ap), bcon(sz/SZCHAR)));
                  r = ccast(r, INCREF(dp->n_type), 0, dp->n_df, dp->n_ap);
                  r = buildtree(UMUL, r, NIL);
            }
      } else {
            uerror("amd64_builtin_va_arg not supported type");
            goto bad;
      }
      tfree(a);
      return r;
bad:
      uerror("bad argument to __builtin_va_arg");
      return bcon(0);
}

NODE *
amd64_builtin_va_end(NODE *f, NODE *a, TWORD t)
{
      tfree(f);
      tfree(a);
      return bcon(0); /* nothing */
}

NODE *
amd64_builtin_va_copy(NODE *f, NODE *a, TWORD t)
{
      tfree(f);
      f = buildtree(ASSIGN, buildtree(UMUL, a->n_left, NIL),
          buildtree(UMUL, a->n_right, NIL));
      nfree(a);
      return f;
}

static NODE *
movtoreg(NODE *p, int rno)
{
      NODE *r;

      r = block(REG, NIL, NIL, p->n_type, p->n_df, p->n_ap);
      regno(r) = rno;
      return clocal(buildtree(ASSIGN, r, p));
}  

static NODE *
movtomem(NODE *p, int off, int reg)
{
      struct symtab s;
      NODE *r, *l;

      s.stype = p->n_type;
      s.squal = 0;
      s.sdf = p->n_df;
      s.sap = p->n_ap;
      s.soffset = off;
      s.sclass = AUTO;

      l = block(REG, NIL, NIL, PTR+STRTY, 0, 0);
      l->n_lval = 0;
      regno(l) = reg;

      r = block(NAME, NIL, NIL, p->n_type, p->n_df, p->n_ap);
      r->n_sp = &s;
      r = stref(block(STREF, l, r, 0, 0, 0));

      return clocal(buildtree(ASSIGN, r, p));
}  


/*
 * AMD64 parameter classification.
 */
static int
argtyp(TWORD t, union dimfun *df, struct attr *ap)
{
      int cl = 0;

      if (t <= ULONG || ISPTR(t) || t == BOOL) {
            cl = ngpr < 6 ? INTEGER : INTMEM;
      } else if (t == FLOAT || t == DOUBLE || t == FIMAG || t == IMAG) {
            cl = nsse < 8 ? SSE : SSEMEM;
      } else if (t == LDOUBLE || t == LIMAG) {
            cl = X87; /* XXX */
      } else if (t == STRTY || t == UNIONTY) {
            int sz = tsize(t, df, ap);

            if (sz <= 2*SZLONG && attr_find(ap, ATTR_COMPLEX) != NULL) {
                  cl = nsse < 7 ? STRCPX : STRMEM;
            } else if (sz > 2*SZLONG || ((sz+SZLONG-1)/SZLONG)+ngpr > 6 ||
                attr_find(ap, GCC_ATYP_PACKED) != NULL)
                  cl = STRMEM;
            else
                  cl = STRREG;
      } else
            cerror("FIXME: classify");
      return cl;
}

/*
 * Do the "hard work" in assigning correct destination for arguments.
 * Also convert arguments < INT to inte (default argument promotions).
 * XXX - should be dome elsewhere.
 */
static NODE *
argput(NODE *p)
{
      NODE *q;
      TWORD ty;
      int typ, r, ssz;

      if (p->n_op == CM) {
            p->n_left = argput(p->n_left);
            p->n_right = argput(p->n_right);
            return p;
      }

      /* first arg may be struct return pointer */
      /* XXX - check if varargs; setup al */
      switch (typ = argtyp(p->n_type, p->n_df, p->n_ap)) {
      case INTEGER:
      case SSE:
            if (typ == SSE)
                  r = XMM0 + nsse++;
            else
                  r = argregsi[ngpr++];
            if (p->n_type < INT || p->n_type == BOOL)
                  p = cast(p, INT, 0);
            p = movtoreg(p, r);
            break;

      case X87:
            r = nrsp;
            nrsp += SZLDOUBLE;
            p = movtomem(p, r, STKREG);
            break;

      case SSEMEM:
            r = nrsp;
            nrsp += SZDOUBLE;
            p = movtomem(p, r, STKREG);
            break;

      case INTMEM:
            r = nrsp;
            nrsp += SZLONG;
            p = movtomem(p, r, STKREG);
            break;

      case STRCPX:
      case STRREG: /* Struct in registers */
            /* Cast to long pointer and move to the registers */
            /* XXX can overrun struct size */
            /* XXX check carefully for SSE members */
            ssz = tsize(p->n_type, p->n_df, p->n_ap);

            if (typ == STRCPX) {
                  ty = DOUBLE;
                  r = XMM0 + nsse++;
            } else {
                  ty = LONG;
                  r = argregsi[ngpr++];
            }
            if (ssz <= SZLONG) {
                  q = cast(p->n_left, INCREF(ty), 0);
                  nfree(p);
                  q = buildtree(UMUL, q, NIL);
                  p = movtoreg(q, r);
            } else if (ssz <= SZLONG*2) {
                  NODE *ql, *qr;

                  qr = cast(ccopy(p->n_left), INCREF(ty), 0);
                  qr = movtoreg(buildtree(UMUL, qr, NIL), r);

                  ql = cast(p->n_left, INCREF(ty), 0);
                  ql = buildtree(UMUL, buildtree(PLUS, ql, bcon(1)), NIL);
                  r = (typ == STRCPX ? XMM0 + nsse++ : argregsi[ngpr++]);
                  ql = movtoreg(ql, r);

                  nfree(p);
                  p = buildtree(CM, ql, qr);
            } else
                  cerror("STRREG");
            break;

      case STRMEM: {
            struct symtab s;
            NODE *l, *t;

            q = buildtree(UMUL, p->n_left, NIL);

            s.stype = p->n_type;
            s.squal = 0;
            s.sdf = p->n_df;
            s.sap = p->n_ap;
            s.soffset = nrsp;
            s.sclass = AUTO;

            nrsp += tsize(p->n_type, p->n_df, p->n_ap);

            l = block(REG, NIL, NIL, PTR+STRTY, 0, 0);
            l->n_lval = 0;
            regno(l) = STKREG;

            t = block(NAME, NIL, NIL, p->n_type, p->n_df, p->n_ap);
            t->n_sp = &s;
            t = stref(block(STREF, l, t, 0, 0, 0));

            t = (buildtree(ASSIGN, t, q));
            nfree(p);
            p = t->n_left;
            nfree(t);
            break;
            }

      default:
            cerror("argument %d", typ);
      }
      return p;
}

/*
 * Sort arglist so that register assignments ends up last.
 */
static int
argsort(NODE *p)
{
      NODE *q, *r;
      int rv = 0;

      if (p->n_op != CM) {
            if (p->n_op == ASSIGN && p->n_left->n_op == REG &&
                coptype(p->n_right->n_op) != LTYPE) {
                  q = tempnode(0, p->n_type, p->n_df, p->n_ap);
                  r = ccopy(q);
                  p->n_right = buildtree(COMOP,
                      buildtree(ASSIGN, q, p->n_right), r);
            }
            return rv;
      }
      if (p->n_right->n_op == CM) {
            /* fixup for small structs in regs */
            q = p->n_right->n_left;
            p->n_right->n_left = p->n_left;
            p->n_left = p->n_right;
            p->n_right = q;
      }
      if (p->n_right->n_op == ASSIGN && p->n_right->n_left->n_op == REG &&
          coptype(p->n_right->n_right->n_op) != LTYPE) {
            /* move before everything to avoid reg trashing */
            q = tempnode(0, p->n_right->n_type,
                p->n_right->n_df, p->n_right->n_ap);
            r = ccopy(q);
            p->n_right->n_right = buildtree(COMOP,
                buildtree(ASSIGN, q, p->n_right->n_right), r);
      }
      if (p->n_right->n_op == ASSIGN && p->n_right->n_left->n_op == REG) {
            if (p->n_left->n_op == CM &&
                p->n_left->n_right->n_op == STASG) {
                  q = p->n_left->n_right;
                  p->n_left->n_right = p->n_right;
                  p->n_right = q;
                  rv = 1;
            } else if (p->n_left->n_op == STASG) {
                  q = p->n_left;
                  p->n_left = p->n_right;
                  p->n_right = q;
                  rv = 1;
            }
      }
      return rv | argsort(p->n_left);
}

/*
 * Called with a function call with arguments as argument.
 * This is done early in buildtree() and only done once.
 * Returns p.
 */
NODE *
funcode(NODE *p)
{
      NODE *l, *r;
      TWORD t;

      nsse = ngpr = nrsp = 0;
      /* Check if hidden arg needed */
      /* If so, add it in pass2 */
      if ((l = p->n_left)->n_type == INCREF(FTN)+STRTY ||
          l->n_type == INCREF(FTN)+UNIONTY) {
            int ssz = tsize(BTYPE(l->n_type), l->n_df, l->n_ap);
            if (ssz > 2*SZLONG)
                  ngpr++;
      }

      /* Convert just regs to assign insn's */
      p->n_right = argput(p->n_right);

      /* Must sort arglist so that STASG ends up first */
      /* This avoids registers being clobbered */
      while (argsort(p->n_right))
            ;
      /* Check if there are varargs */
      if (nsse || l->n_df == NULL || l->n_df->dfun == NULL) {
            ; /* Need RAX */
      } else {
            union arglist *al = l->n_df->dfun;

            for (; al->type != TELLIPSIS; al++) {
                  if ((t = al->type) == TNULL)
                        return p; /* No need */
                  if (BTYPE(t) == STRTY || BTYPE(t) == UNIONTY)
                        al++;
                  for (; t > BTMASK; t = DECREF(t))
                        if (ISARY(t) || ISFTN(t))
                              al++;
            }
      }

      /* Always emit number of SSE regs used */
      l = movtoreg(bcon(nsse), RAX);
      if (p->n_right->n_op != CM) {
            p->n_right = block(CM, l, p->n_right, INT, 0, 0);
      } else {
            for (r = p->n_right; r->n_left->n_op == CM; r = r->n_left)
                  ;
            r->n_left = block(CM, l, r->n_left, INT, 0, 0);
      }
      return p;
}

/*
 * return the alignment of field of type t
 */
int
fldal(unsigned int t)
{
      uerror("illegal field type");
      return(ALINT);
}

/* fix up type of field p */
void
fldty(struct symtab *p)
{
}

/*
 * XXX - fix genswitch.
 */
int
mygenswitch(int num, TWORD type, struct swents **p, int n)
{
      return 0;
}

Generated by  Doxygen 1.6.0   Back to index