Subversion Repositories tendra.SVN

Rev

Rev 5 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
                 Crown Copyright (c) 1997

    This TenDRA(r) Computer Program is subject to Copyright
    owned by the United Kingdom Secretary of State for Defence
    acting through the Defence Evaluation and Research Agency
    (DERA).  It is made available to Recipients with a
    royalty-free licence for its use, reproduction, transfer
    to other parties and amendment for any purpose not excluding
    product development provided that any such use et cetera
    shall be deemed to be acceptance of the following conditions:-

        (1) Its Recipients shall ensure that this Notice is
        reproduced upon any copies or amended versions of it;

        (2) Any amended version of it shall be clearly marked to
        show both the nature of and the organisation responsible
        for the relevant amendment or amendments;

        (3) Its onward transfer from a recipient to another
        party shall be deemed to be that party's acceptance of
        these conditions;

        (4) DERA gives no warranty or assurance as to its
        quality or suitability for any purpose and DERA accepts
        no liability whatsoever in relation to any use to which
        it may be put.
*/


/**********************************************************************
$Author: release $
$Date: 1998/01/17 15:56:06 $
$Revision: 1.1.1.1 $
$Log: move.c,v $
 * Revision 1.1.1.1  1998/01/17  15:56:06  release
 * First version to be checked into rolling release.
 *
 * Revision 1.3  1995/09/12  10:59:35  currie
 * gcc pedanttry
 *
 * Revision 1.2  1995/08/16  16:06:55  currie
 * Shortened some .h names
 *
 * Revision 1.1  1995/04/13  09:08:06  currie
 * Initial revision
 *
***********************************************************************/
/**********************************************************************
                move.c

        The procedure move produces code to move a value from a to the
destination dest. This takes the form of a switch test on the parameter a (type
ans) which is either a reg, freg or instore value. In each of the three cases the
ans field of the dest is similarly dealt with to determine the necessary
instructions for the move. Sizes and alignment are taken from the ash field of
the destination.
Delivers register used if 1-word destination is instore; otherwise NOREG.

**********************************************************************/

#include "config.h"
#include "mips_ins.h"
#include "inst_fmt.h"
#include "addrtypes.h"
#include "procrectypes.h"
#include "getregs.h"
#include "labels.h"
#include "psu_ops.h"
#include "bitsmacs.h"
#include "common_types.h"
#include "main.h"
#include "guard.h"
#include "maxminmacs.h"
#include "basicread.h"
#include "move.h"


int   move
    PROTO_N ( (a, dest, freeregs, sgned) )
    PROTO_T ( ans a X where dest X space freeregs X bool sgned )
{
  long regs = freeregs.fixed;
  long fregs = freeregs.flt;
  int   al = dest.ashwhere.ashalign;
  int size = dest.ashwhere.ashsize;

  if (size==0) return NOREG;

start:
  switch (a.discrim) {
    case insomereg:
    case insomefreg: {
        failer ("Source reg not specified");
      }

    case inreg:
/***************** source in fixed point register ********************/
      {
        int   r = regalt (a);
        switch (dest.answhere.discrim) {
          case inreg:
            /* ******* source and dest in fixed register ************ */
            {
              int   rd = regalt (dest.answhere);
              if (rd != 0 /* nowhere */ && rd != r) {
                                /* move reg r to reg rd */
                mon_ins (i_move, rd, r);
              }
              return NOREG;
            }                   /* end inreg dest */

          case insomereg:
            /* **** can choose dest register to be source reg **** */
            {
              int  *sr = someregalt (dest.answhere);
              if (*sr != -1) {
                failer ("Somereg *2");
              }
              *sr = r;
              return NOREG;
            }

          case infreg:
            /* ***source in fix reg,  dest in floating point register **********
            */
            {
              freg fr;
              fr = fregalt (dest.answhere);
              cop_ins (i_mtc1, r, fr.fr << 1);
              if (fr.dble) {
                cop_ins (i_mtc1, r + 1, (fr.fr << 1) + 1);
              };
              return NOREG;
            }                   /* end infreg dest */

           case insomefreg: {
              /* source in fixed, can choose flt dest */
               somefreg sfr;
               freg fr;
               sfr = somefregalt(dest.answhere);
               if (*sfr.fr != -1) { failer ("Somefreg *2"); }
               *sfr.fr = getfreg(fregs);
               fr.fr = *sfr.fr;
               fr.dble = sfr.dble;
               setfregalt(dest.answhere, fr);
               goto start;
          }
          case notinreg:
            /* **source in fix reg, dest instore ******************* */
            {
              char *st = (size==al) ?(
                        (size <= 8) ? i_sb : ((size <= 16) ? i_sh : i_sw)):
                        ((size<=8)? i_sb : ((size <= 16) ? i_ush : i_usw));
              instore is;
              is = insalt (dest.answhere);
              if (is.adval) {
                ls_ins (st, r, is.b);
              }                 /* is the value an address? */
              else {
                baseoff b;
                b.base = getreg(regs);
                b.offset = 0;
                ls_ins (i_lw, b.base, is.b);
                ls_ins (st, r, b);
              };

              return r;
            }                   /* end notinreg dest */


        }                       /* end switch dest */
      }                         /* end inreg a */
    case infreg:
/****************** source in floating point register ******************/
      {
        freg fr;
        fr = fregalt (a);
        switch (dest.answhere.discrim) {
          case inreg:
            /* *source in float reg, dest in fixed point register ********
            */
            {
              int   rd = regalt (dest.answhere);
              if (rd != 0) {
                if (BIGEND) {
                        if (fr.dble) {
                          cop_ins (i_mfc1, rd, (fr.fr << 1) + 1);
                        }
                        cop_ins (i_mfc1, rd+1, fr.fr << 1);
                }
                else {
                        cop_ins (i_mfc1, rd, fr.fr << 1);
                        if (fr.dble) {
                          cop_ins (i_mfc1, rd + 1, (fr.fr << 1) + 1);
                        }
                }
              };
              return NOREG;
            }                   /* end inreg dest */
          case insomereg:
            /* *** source in flt reg, can choose dest reg ***** */
            {
              int  *sr = someregalt (dest.answhere);
              if (*sr != -1) {
                failer ("Somereg *2");
              }
              *sr = getreg (regs);
              setregalt (dest.answhere, *sr);
              goto start;
            }
           case insomefreg: {
                /* can choose dest reg to be source reg */
               somefreg sfr;
               sfr = somefregalt(dest.answhere);
               if (*(sfr.fr) !=-1) failer("some freg * 2");
               *(sfr.fr) = fr.fr;
                return NOREG;
           }
          case infreg:
            /* ******* source and dest in floating point registers *******
            */
            {
              freg frd;
              frd = fregalt (dest.answhere);
              if (fr.fr != frd.fr) {
                rrfp_ins ((frd.dble) ? i_mov_d : i_mov_s, frd.fr << 1, fr.fr << 1);
              };
              return NOREG;
            }                   /* end infreg dest */
          case notinreg:
            /* ********** source in flt reg, dest instore ******************
            */
            {
              char *st = (fr.dble) ? i_s_d : i_s_s;
              instore is;

              if ((dest.ashwhere.ashsize == 64 && !fr.dble) ||
                  (dest.ashwhere.ashsize == 32 && fr.dble)) {
                st = (dest.ashwhere.ashsize==64) ? i_s_d:i_s_s;
                /* failer ("INCONSISTENT SIZES"); */
              }

              is = insalt (dest.answhere);
              if (is.adval) {
                lsfp_ins (st, fr.fr << 1, is.b);
              }
              else {
                baseoff b;
                b.base = getreg (regs);
                b.offset = 0;
                ls_ins (i_lw, b.base, is.b);
                lsfp_ins (st, fr.fr << 1, b);
              };

              return ((fr.dble) ? -(fr.fr + 32) : (fr.fr + 32));
            }                   /* end notinreg dest */
        }                       /* end switch dest */
      }                         /* end infreg a */



    case notinreg:
/*********************** source instore *************************/
      {
        instore iss;
        char *ld;
        iss = insalt (a);
        if (iss.adval && iss.b.offset == 0 && iss.b.base > 0 && iss.b.base <= 31) {
          setregalt (a, iss.b.base);
          goto start;
        }

        if (al==1) { /* contained size for naked bitfields */
                al = (size<=8)? 8: ((size<=16)? 16:32);
        }
        ld = (iss.adval) ? i_la : ((al <= 8) ? ((sgned) ? i_lb : i_lbu) :
            ((al <= 16) ? ((sgned) ? i_lh : i_lhu) : i_lw));
        /* determine which load instruction to use from al and adval */

        switch (dest.answhere.discrim) {
          case insomefreg: {
             /* source in store can choose dest freg */
               somefreg sfr;
               freg fr;
               sfr = somefregalt(dest.answhere);
               if (*sfr.fr != -1) { failer ("Somefreg *2"); }
               *sfr.fr = getfreg(fregs);
               fr.fr = *sfr.fr;
               fr.dble = sfr.dble;
               setfregalt(dest.answhere, fr);
               goto start;
          }
          case insomereg:
            /* **** source instore, can choose dest reg ***** */
            {
              int  *sr = someregalt (dest.answhere);
              if (*sr != -1) {
                failer ("Somereg *2");
              }
              *sr = getreg (regs);
              setregalt (dest.answhere, *sr);
              /* and continue to next case */
            }
          case inreg:
            /* ********** source and dest in fixpnt reg ************* */
            {
              int   rd = regalt (dest.answhere);
              if (rd != 0 /* nowhere */ ) {
                ls_ins (ld, rd, iss.b);
              };
              return NOREG;
            }                   /* end inreg dest */


          case infreg:
            /* *********** source instore, dest in floating pnt reg ********
            */
            {
              freg frd;
              frd = fregalt (dest.answhere);
              if (iss.b.base == 0 && iss.b.offset == 0) {
                cop_ins (i_mtc1, 0, frd.fr << 1);
                if (frd.dble) {
                cop_ins (i_mtc1, 0, (frd.fr << 1) + 1);
                };
                return NOREG;
              }
              lsfp_ins ((frd.dble) ? i_l_d : i_l_s, frd.fr << 1, iss.b);
              return NOREG;
            }                   /* end infreg dest */


          case notinreg:
            /* *********** source and dest  instore ************* */
            {
              char *st = (al <= 8) ? i_sb : ((al <= 16) ? i_sh : i_sw);
              /* determine which store instruction from al (align from
                 dest) */
              instore isd;
              int   sunit = min (al, 32);
              int   step = sunit >> 3;
              int   s = (dest.ashwhere.ashsize + sunit - 1) / sunit;
              bool unalign = 0;
              isd = insalt (dest.answhere);
              if (al == 8 && s >= 4) {
                int   r = getreg (regs);
                freeregs = guardreg(r,freeregs);
                /* register for holding values transferred */
                unalign = 1;
                if (s <= 16) {  /* in line unalligned move */
                  if (!isd.adval) {
                    int r3 = getreg(freeregs.fixed);
                    ls_ins (i_lw, r3, isd.b);
                    isd.b.base = r3;
                    isd.b.offset = 0;
                    isd.adval = 1;
                  }
                  for (; s >= 4; s -= 4) {
                    ls_ins (i_ulw, r, iss.b);
                    ls_ins (i_usw, r, isd.b);
                    iss.b.offset += 4;
                    isd.b.offset += 4;
                  }

                }
                else {          /* unaligned loop move *//* copy with
                                   loop, length in r1, to in 2, from in 3
                                */
                  int   l = new_label ();
                  int   r1 = getreg (freeregs.fixed);
                  int r3, r2;
                  baseoff src;
                  baseoff dst;
                  freeregs = guardreg(r1,freeregs);
                  r3 = getreg(freeregs.fixed);
                  r2 = getreg(guardreg(r3,freeregs).fixed);

                  src.base = r3; src.offset = 0;
                  dst.base = r2; dst.offset = 0;

                  ls_ins ((isd.adval) ? i_la : i_lw, r2, isd.b);
                  isd.adval = 1;
                  ls_ins (i_la, r3, iss.b);


                  rri_ins (i_addu, r1, r3, (s & ~3));
                  /* output instruction to initialise counter */
                  set_label (l);
                  ls_ins (i_ulw, r, src);
                  rri_ins (i_addu, r3, r3, 4);
                  ls_ins (i_usw, r, dst);
                  rri_ins (i_addu, r2, r2, 4);
                  condrr_ins (i_bne, r1, r3, l);
                  /* instruction to test end of loop */

                  s = s & 3;
                  iss.b = src;
                  isd.b = dst;
                }

                for(; s>0; s--) {
                        ls_ins(i_lb, r, iss.b);
                        ls_ins(i_sb, r, isd.b);
                        iss.b.offset += 1;
                        isd.b.offset += 1;
                };
                return NOREG;
              }

              if (s <= inlineassign) {/* move in line */
                int   r = getreg (regs);
                /* register for holding values transferred */

                if (!isd.adval) {
                  int r3 = getreg(guardreg(r,freeregs).fixed);
                  freeregs = guardreg(r3,freeregs);
                  ls_ins (i_lw, r3, isd.b);
                  isd.b.base = r3;
                  isd.b.offset = 0;
                }
                if (s == 1) {
                  ls_ins (ld, r, iss.b);
                  ls_ins (st, r, isd.b);

                  return (unalign) ? NOREG : r;
                }
                else {
                  int   er = getreg (freeregs.fixed);
                  int   nr = r;
                  for (; s > 0; --s) {
                    ls_ins (ld, nr, iss.b);
                    ls_ins (st, nr, isd.b);
                    iss.b.offset += step;
                    isd.b.offset += step;
                    if (nr == r) {
                      nr = er;
                    }
                    else {
                      nr = r;
                    }
                  }
                  return NOREG;
                }
              }                 /* inline end */
              else {            /* copy with loop, length in r1, to in r2,
                                   from in r3 */
                int   a = getreg (regs);
                                /* get register for transferring values */
                int   l = new_label ();
                int   r1, r2, r3;
                baseoff src;
                baseoff dst;
                freeregs = guardreg(a,freeregs);
                r1 = getreg(freeregs.fixed);
                freeregs = guardreg(r1,freeregs);
                r2 = getreg(freeregs.fixed);
                freeregs = guardreg(r2,freeregs);
                r3 = getreg(freeregs.fixed);
                src.base = r3; src.offset = 0;
                dst.base = r2; dst.offset = 0;


                ls_ins ((isd.adval) ? i_la : i_lw, r2, isd.b);
                ls_ins (i_la, r3, iss.b);

                ri_ins (i_li, r1, s);
                /* output instruction to initialise counter */
                set_label (l);
                ls_ins (ld, a, src);
                rri_ins (i_addu, r3, r3, step);
                ls_ins (st, a, dst);
                rri_ins (i_addu, r2, r2, step);
                rri_ins (i_subu, r1, r1, 1);
                /* instruction to decrement counter */
                condr_ins (i_bgtz, r1, l);
                /* instruction to test end of loop */

                return NOREG;
              }                 /* loop assign */
            }                   /* end notinreg dest */
        }                       /* end switch dest */
      }                         /* end notinreg a */
  }                             /* end switch a */
}