Subversion Repositories tendra.SVN

Rev

Rev 2 | Go to most recent revision | 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.
*/




/*
                            VERSION INFORMATION
                            ===================

--------------------------------------------------------------------------
$Header: /u/g/release/CVSROOT/Source/src/installers/sparc/common/move.c,v 1.1.1.1 1998/01/17 15:55:54 release Exp $
--------------------------------------------------------------------------
$Log: move.c,v $
 * Revision 1.1.1.1  1998/01/17  15:55:54  release
 * First version to be checked into rolling release.
 *
 * Revision 1.8  1997/08/23  13:54:10  pwe
 * initial ANDF-DE
 *
 * Revision 1.7  1996/10/03  08:51:12  pwe
 * PIC global/large offset, and PIC case guardregs
 *
 * Revision 1.6  1996/08/22  16:47:05  pwe
 * correct accessing for double params
 *
 * Revision 1.5  1996/02/29  17:39:30  john
 * Fix to double load
 *
 * Revision 1.4  1996/02/27  11:20:28  john
 * *** empty log message ***
 *
 * Revision 1.3  1996/01/24  18:11:05  john
 * Removed assertion
 *
 * Revision 1.2  1995/05/26  12:59:39  john
 * Reformatting
 *
 * Revision 1.1.1.1  1995/03/13  10:18:44  john
 * Entered into CVS
 *
 * Revision 1.7  1994/12/01  13:35:24  djch
 * va_alist can generate 4byte aligned doubles on the stack... Always indexed by
 * -8, so generated 2 loads for these..
 *
 * Revision 1.6  1994/07/07  16:11:33  djch
 * Jul94 tape
 *
 * Revision 1.5  1994/06/22  09:52:52  djch
 * temporary fix to put temporary bitfields in the bottom of the word - one day
 * much of the code could go
 *
 * Revision 1.4  1994/05/25  14:13:03  djch
 * Added CREATE_instore_bits to shut up tcc
 *
 * Revision 1.3  1994/05/19  08:59:05  djch
 * added empty {} to pacify tcc
 *
 * Revision 1.2  1994/05/13  12:39:32  djch
 * Incorporates improvements from expt version
 * use new macros from addrtypes.h, added defaults to switchs
 *
 * Revision 1.1  1994/05/03  14:49:44  djch
 * Initial revision
 *
 * Revision 1.4  93/08/27  11:31:58  11:31:58  ra (Robert Andrews)
 * Added a couple of explicit integer casts.
 * 
 * Revision 1.3  93/07/15  12:32:46  12:32:46  ra (Robert Andrews)
 * Reformatted.  Changed critical value for introducing a loop for a large
 * move from 8 to 12.
 * 
 * Revision 1.2  93/07/05  18:22:05  18:22:05  ra (Robert Andrews)
 * Reformatted slightly.
 * 
 * Revision 1.1  93/06/24  14:58:45  14:58:45  ra (Robert Andrews)
 * Initial revision
 * 
--------------------------------------------------------------------------
*/


#define SPARCTRANS_CODE

/*
  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 instore or
  bitad 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.  The routine returns the
  register used if a single word destination is instore, and
  NOREG otherwise.
*/

#include "config.h"
#include "common_types.h"
#include "myassert.h"
#include "sparcins.h"
#include "inst_fmt.h"
#include "addrtypes.h"
#include "proctypes.h"
#include "proc.h"
#include "getregs.h"
#include "labels.h"
#include "comment.h"
#include "bitsmacs.h"
#include "regmacs.h"
#include "maxminmacs.h"
#include "makecode.h"
#include "flags.h"
#include "move.h"


/*
  MAXIMUM SIZE FOR INLINED MOVES
*/

#define MAX_STEPS_INLINE_MOVE   12


/*
  BITMASK : NBITMASK ( n ) GIVES n 1'S
*/

#define NBITMASK( n )  ((unsigned long)((n)==32 ?~0L:((1<<(n))-1)))


/*
  TYPE REPRESENTING INSTRUCTION PAIRS
  Unsigned version, signed version.
*/

typedef ins_p ins_sgn_pair [2] ;


/*
  ARRAY OF LOAD INSTRUCTIONS
*/

static CONST ins_sgn_pair ld_ins_sz [] = {
  { I_NIL, I_NIL },
  { i_ldub, i_ldsb },
  { i_lduh, i_ldsh },
  { I_NIL, I_NIL },
  { i_ld, i_ld },
  { I_NIL, I_NIL },
  { I_NIL, I_NIL },
  { I_NIL, I_NIL },
  { i_ldd, i_ldd }
} ;


/*
  ARRAY OF STORE INSTRUCTIONS
*/

static CONST ins_sgn_pair st_ins_sz [] = {
  { I_NIL, I_NIL },
  { i_stb, i_stb },
  { i_sth, i_sth },
  { I_NIL, I_NIL },
  { i_st, i_st },
  { I_NIL, I_NIL },
  { I_NIL, I_NIL },
  { I_NIL, I_NIL },
  { i_std, i_std }
} ;


/*
  FIND THE LOAD INSTRUCTION FOR SIZE bits, SIGNEDNESS sgned
*/

ins_p i_ld_sz 
    PROTO_N ( ( bits, sgned ) )
    PROTO_T ( int bits X int sgned ){
  return ( ld_ins_sz [ bits / 8 ] [ sgned ] ) ;
}


/*
  FIND THE STORE INSTRUCTION FOR SIZE bits (UNSIGNED)
*/

ins_p i_st_sz 
    PROTO_N ( ( bits ) )
    PROTO_T ( int bits ){
  return ( st_ins_sz [ bits / 8 ] [0] ) ;
}


/*
  LOAD THE ADDRESS REPRESENTED BY is INTO reg
*/

void ld_addr 
    PROTO_N ( ( is, reg ) )
    PROTO_T ( instore is X int reg ){
  if ( is.adval ) {
    if ( IS_FIXREG ( is.b.base ) ) {
      rir_ins ( i_add, is.b.base, is.b.offset, reg ) ;
    } else {
      set_ins ( is.b, reg ) ;
    }
  } else {
    ld_ins ( i_ld, is.b, reg ) ;
  }
  return ;
}


/*
  LOAD THE ADDRESS REPRESENTED BY is INTO A REGISTER
  The register number is returned.  regs gives the registers to 
  choose from.
*/
int addr_reg 
    PROTO_N ( ( is, regs ) )
    PROTO_T ( instore is X long regs ){
  int r ;
  if ( is.adval && IS_FIXREG ( is.b.base ) && is.b.offset == 0 ) {
    return ( is.b.base ) ;
  }
  r = getreg ( regs ) ;
  ld_addr ( is, r ) ;
  return ( r ) ;
}


/*
  MOVE a INTO dest
  regs gives the free registers, sgned gives the signedness.
*/

int move 
    PROTO_N ( ( a, dest, regs, sgned ) )
    PROTO_T ( ans a X where dest X long regs X bool sgned ){
  int al = ( int ) dest.ashwhere.ashalign ;
  if ( dest.ashwhere.ashsize == 0 ) return ( NOREG ) ;

  if ( PIC_code && discrim ( dest.answhere ) == notinreg ) {
    instore iss;
    iss = insalt ( dest.answhere ) ;
    if ( !IS_FIXREG (iss.b.base) && !SIMM13_SIZE (iss.b.offset) ) {
        /* global with large offset: we have to avoid double use of R_TMP */
      int ofs = iss.b.offset ;
      int r = getreg ( regs ) ;
      regs |= RMASK ( r ) ;
      iss.b.offset = 0;
      set_ins ( iss.b, r ) ;
      iss.b.offset = ofs;
      iss.b.base = r;
      setinsalt ( dest.answhere, iss ) ;
    }
  }

  start : switch ( discrim ( a ) ) {

  case insomereg :
  case insomefreg : {
    /* Source is in some register */
    fail ( "move : source register not specified" ) ;
    return ( NOREG ) ;
  }
  case inreg : {
    /* Source in fixed point register */
    int r = regalt ( a ) ;

    switch ( discrim ( dest.answhere ) ) {

    case inreg : {
      /* Register to register move */
      int rd = regalt ( dest.answhere ) ;
      if ( rd != R_G0 && rd != r ) {
        rr_ins ( i_mov, r, rd ) ;
      }
      return ( NOREG ) ;
    }

    case insomereg : {
      /* Register to some register move */
      int *sr = someregalt ( dest.answhere ) ;
      if ( *sr != -1 ) fail ( "Illegal register" ) ;
      *sr = r ;
      return ( NOREG ) ;
    }

    case infreg : {
      /* Register to floating register move */
      freg fr ;
      fr = fregalt ( dest.answhere ) ;
      st_ro_ins ( i_st, r, mem_temp ( 0 ) ) ;
      ldf_ro_ins ( i_ld, mem_temp ( 0 ), fr.fr << 1 ) ;
      if ( fr.dble ) {
        st_ro_ins ( i_st, r + 1, mem_temp ( 4 ) ) ;
        ldf_ro_ins ( i_ld, mem_temp ( 4 ),
                     ( fr.fr << 1 ) + 1 ) ;
      }
      return ( NOREG ) ;
    }

    case notinreg : {
      /* Register to store move */
      instore is ;
      ins_p st = i_st_sz ( al ) ;

      if ( al == 1 ) {
        st = i_st_sz ( ( int ) dest.ashwhere.ashsize ) ;
      } else {
        /*assert ( al == dest.ashwhere.ashsize ) ;*/
        st = i_st_sz ( al ) ;
      }

      is = insalt ( dest.answhere ) ;
      if ( is.adval ) {
        st_ins ( st, r, is.b ) ;
      } else {
        baseoff b ;
        b.base = R_TMP ;
        b.offset = 0 ;
        ld_ins ( i_ld, is.b, b.base ) ;
        st_ins ( st, r, b ) ;
      }
      return ( r ) ;
    }
    default:
      fail("fixed -> wrong dest");
    }
    /* NOT REACHED */
  }
  case infreg : {
    /* Source in floating point register */
    freg fr ;
    fr = fregalt ( a ) ;

    switch ( discrim ( dest.answhere ) ) {
    case inreg : {
      /* Floating register to register move */
      int rd = regalt ( dest.answhere ) ;

      if ( rd != 0 ) {
        /* store and load to move to fixed reg */
        stf_ins ( i_st, fr.fr<<1, mem_temp ( 0 ) ) ;
        ld_ro_ins ( i_ld, mem_temp ( 0 ), rd ) ;
        if ( fr.dble ) {
          stf_ins ( i_st, ( fr.fr << 1 ) + 1,
                    mem_temp ( 4 ) ) ;
          ld_ro_ins ( i_ld, mem_temp ( 4 ), rd + 1 ) ;
        }
      }
      return ( NOREG ) ;
    }
    case insomereg : {
      /* Floating register to some register move */
      int *sr = someregalt ( dest.answhere ) ;
      if ( *sr != -1 ) fail ( "Illegal register" ) ;
      *sr = getreg ( regs ) ;
      regs |= RMASK ( *sr ) ;
      setregalt ( dest.answhere, *sr ) ;
      goto start ;
    }
    case infreg : {
      /* Floating register to floating register move */
      freg frd ;
      frd = fregalt ( dest.answhere ) ;

      if ( fr.fr != frd.fr ) {
        rrf_ins ( i_fmovs, fr.fr << 1, frd.fr << 1 ) ;
        if ( frd.dble ) {
          rrf_ins ( i_fmovs, ( fr.fr << 1 ) + 1,
                    ( frd.fr << 1 ) + 1 ) ;
        }
      }
      return ( NOREG ) ;
    }
    case notinreg : {
      /* Floating register to store move */
      instore is ;
      ins_p st = ( fr.dble ? i_std : i_st ) ;

      if ( ( dest.ashwhere.ashsize == 64 && !fr.dble ) ||
           ( dest.ashwhere.ashsize == 32 && fr.dble ) ) {
        fail ( "Inconsistent sizes in move" ) ;
      }

      is = insalt ( dest.answhere ) ;
      if ( is.adval ) {
        if ( fr.dble ) {
          if (( ( is.b.offset & 7 ) == 0 ) && 
                ((is.b.base == R_FP) || (is.b.base == R_SP))) {
            /* double aligned */
            stf_ins ( i_std, fr.fr << 1, is.b ) ;
          } else {
                                /* not double aligned */
            stf_ins ( i_st, fr.fr << 1, is.b ) ;
            is.b.offset += 4 ;
            stf_ins ( i_st, ( fr.fr << 1 ) + 1, is.b ) ;
          }
        } else {
          stf_ins ( i_st, fr.fr << 1, is.b ) ;
        }
      } else {
        baseoff b ;
        b.base = getreg ( regs ) ;
        b.offset = 0 ;
        ld_ins ( i_ld, is.b, b.base ) ;
        stf_ro_ins ( st, fr.fr << 1, b ) ;
      }

      return ( fr.dble ? -( fr.fr + 32 ) : ( fr.fr + 32 ) ) ;
    }
    default:
      fail("Float to wrong dest");
    }
    /* NOT REACHED */
  }
  case notinreg : {
    /* Source in store */
    instore iss ;
    int size = dest.ashwhere.ashsize;
    iss = insalt ( a ) ;

    if ( PIC_code && !IS_FIXREG (iss.b.base) && !SIMM13_SIZE (iss.b.offset) ) {
        /* global with large offset: we have to avoid double use of R_TMP */
      int ofs = iss.b.offset ;
      int r = getreg ( regs ) ;
      regs |= RMASK ( r ) ;
      iss.b.offset = 0;
      set_ins ( iss.b, r ) ;
      iss.b.offset = ofs;
      iss.b.base = r;
    }
            
    if ( iss.adval && iss.b.offset == 0 && IS_FIXREG ( iss.b.base ) ) {
      /* address of [ base_reg + 0 ] is base_reg */
      setregalt ( a, iss.b.base ) ;
      goto start ;
    }
    if (al == 1){
      al = (size<=8)?9: ((size<=16)?16:32);
    }
    if ( al == 64 ) al = 32 ;

    /* determine which load instruction to use from al and adval */
    switch ( discrim ( dest.answhere ) ) {
    case insomereg : {
      /* Move store to some register */
      int *sr = someregalt ( dest.answhere ) ;
      if ( *sr != -1 ) fail ( "Illegal register" ) ;
      *sr = getreg ( regs ) ;
      regs |= RMASK ( *sr ) ;
      setregalt ( dest.answhere, *sr ) ;
      /* FALL THROUGH */
    }
    case inreg : {
      /* Move store to register */
      int rd = regalt ( dest.answhere ) ;
      if ( rd != R_G0 ) {
        if ( iss.adval ) {
          /* generate address of source */
          if ( IS_FIXREG ( iss.b.base ) ) {
            rir_ins ( i_add, iss.b.base,
                      iss.b.offset, rd ) ;
          } else {
            set_ins ( iss.b, rd ) ;
          }
        } else {
          /* load source */
          ld_ins ( i_ld_sz ( al, sgned ), iss.b, rd ) ;
        }
      }
      return ( NOREG ) ;
    }
    case infreg : {
      /* Move store to floating register */
      freg frd ;
      frd = fregalt ( dest.answhere ) ;
      assert ( !iss.adval ) ;
      if ( frd.dble ) {
        /* double precision */
        if (( ( iss.b.offset & 7 ) == 0 ) && ( iss.b.offset != -8 ) && 
                ((iss.b.base == R_FP) || (iss.b.base == R_SP))) {
          /* source is double aligned */
          ldf_ins ( i_ldd, iss.b, frd.fr << 1 ) ;
        } else {
          /* source is not double aligned */
          ldf_ins ( i_ld, iss.b, frd.fr << 1 ) ;
          iss.b.offset += 4 ;
          ldf_ins ( i_ld, iss.b, ( frd.fr << 1 ) + 1 ) ;
        }
      } else {
        /* single precision */
        ldf_ins ( i_ld, iss.b, frd.fr << 1 ) ;
      }
      return ( NOREG ) ;
    }
    case notinreg : {
      /* Move store to store address */
      int bits ;
      int no_steps ;
      int bits_per_step ;
      int bytes_per_step ;
      instore isd ;
      ins_p st, ld ;
      bool unalign = ( bool ) ( al < 32 ) ;

      /* we are limited by 32 bit regs */
      bits_per_step = MIN_OF ( al, 32 ) ;
      bytes_per_step = bits_per_step / 8 ;

      /* round up number of bits to be moved */
      bits = ( int ) ( ( dest.ashwhere.ashsize +
                         bits_per_step - 1 ) &
                       ~( bits_per_step - 1 ) ) ;

      no_steps = ( bits + bits_per_step - 1 ) / bits_per_step ;

      if ( ( al % 8 ) != 0 || ( bits % 8 ) != 0 ) {
        fail ( "move : misaligned store" ) ;
        return ( NOREG ) ;
      }
      assert ( ( bits % al ) == 0 ) ;
      assert ( bytes_per_step > 0 && bytes_per_step <= 4 ) ;
      assert ( no_steps > 0 ) ;
      assert ( ( no_steps * bytes_per_step ) == ( bits / 8 ) ) ;

      ld = i_ld_sz ( bits_per_step, sgned ) ;
      st = i_st_sz ( bits_per_step ) ;

      isd = insalt ( dest.answhere ) ;
      if ( no_steps <= MAX_STEPS_INLINE_MOVE ) {
        /* expand to a number of loads and stores */
        if ( no_steps == 1 ) {
          /* move can be done in one step */
          int r = getreg ( regs ) ;
          regs |= RMASK ( r ) ;
          if ( iss.adval ) {
                                /* generate address of source */
            if ( IS_FIXREG ( iss.b.base ) ) {
              if ( iss.b.offset == 0 ) {
                r = iss.b.base ;
              } else {
                rir_ins ( i_add, iss.b.base,
                          iss.b.offset, r ) ;
              }
            } else {
              set_ins ( iss.b, r ) ;
            }
          } else {
                                /* load source */
            ld_ins ( ld, iss.b, r ) ;
          }

          if ( !isd.adval ) {
            ld_ins ( i_ld, isd.b, R_TMP ) ;
            isd.b.base = R_TMP ;
            isd.b.offset = 0 ;
          }
          st_ins ( st, r, isd.b ) ;
          return ( unalign ? NOREG : r ) ;
        } else {
          /* use two registers, ensuring load delay
             slot is not occupied */
          int r1, r2 ;
          int ld_steps = no_steps ;
          int st_steps = no_steps ;

          assert ( ld_steps >= 2 ) ;
          /*assert ( !iss.adval ) ;*/
          assert ( bits_per_step <= 32 ) ;

          /* find the registers to be used */
          r1 = getreg ( regs ) ;
          regs |= RMASK ( r1 ) ;
          r2 = getreg ( regs ) ;
          regs |= RMASK ( r2 ) ;

          if ( !IS_FIXREG ( iss.b.base ) ) {
                                /* load source ptr in reg */
            int pr = getreg ( regs ) ;
            regs |= RMASK ( pr ) ;
            set_ins ( iss.b, pr ) ;
            iss.b.base = pr ;
            iss.b.offset = 0 ;
          }

          if ( !isd.adval ) {
            int pr = getreg ( regs ) ;
            regs |= RMASK ( pr ) ;
            ld_ins ( i_ld, isd.b, pr ) ;
            isd.b.base = pr ;
            isd.b.offset = 0 ;
          } else if ( !IS_FIXREG ( isd.b.base ) ) {
            int pr = getreg ( regs ) ;
            regs |= RMASK ( pr ) ;
            set_ins ( isd.b, pr ) ;
            isd.b.base = pr ;
            isd.b.offset = 0 ;
          }

          /* first pre-load both registers */
          ld_ro_ins ( ld, iss.b, r1 ) ;
          ld_steps-- ;
          iss.b.offset += bytes_per_step ;

          ld_ro_ins ( ld, iss.b, r2 ) ;
          ld_steps-- ;
          iss.b.offset += bytes_per_step ;

          /* now generate a sequence of instructions
             of the form :

             st r1
             ld r1
             st r2
             ld r2
             */
          while ( st_steps > 0 ) {
                                /* st r1 */
            st_ro_ins ( st, r1, isd.b ) ;
            st_steps-- ;
            isd.b.offset += bytes_per_step ;

                                /* ld r1 */
            if ( ld_steps > 0 ) {
              ld_ro_ins ( ld, iss.b, r1 ) ;
              ld_steps-- ;
              iss.b.offset += bytes_per_step ;
            }

                                /* st r2 */
            if ( st_steps > 0 ) {
              st_ro_ins ( st, r2, isd.b ) ;
              st_steps-- ;
              isd.b.offset += bytes_per_step ;
            }

                                /* ld r2 */
            if ( ld_steps > 0 ) {
              ld_ro_ins ( ld, iss.b, r2 ) ;
              ld_steps-- ;
              iss.b.offset += bytes_per_step ;
            }
          }
          assert ( ld_steps == 0 ) ;
          return ( NOREG ) ;
        }
      } else {
        /* large number of steps - use a loop */
        int cnt_reg ;
        int copy_reg ;
        int srcptr_reg ;
        int destptr_reg ;
        int loop = new_label () ;

        assert ( !iss.adval ) ;
        assert ( bytes_per_step <= 4 ) ;

        /* find control register */
        cnt_reg = getreg ( regs ) ;
        regs |= RMASK ( cnt_reg ) ;

        /* find source register */
        assert ( !iss.adval ) ;
        iss.adval = 1 ;
        srcptr_reg = addr_reg ( iss, regs ) ;
        regs |= RMASK ( srcptr_reg ) ;

        /* find destination register */
        destptr_reg = addr_reg ( isd, regs ) ;
        regs |= RMASK ( destptr_reg ) ;

        /* find copy register */
        copy_reg = R_TMP ;

        /* main loop */
        ir_ins ( i_mov, ( long ) ( bytes_per_step * no_steps ),
                 cnt_reg ) ;
        set_label ( loop ) ;
        rir_ins ( i_subcc, cnt_reg, ( long ) bytes_per_step,
                  cnt_reg ) ;
        ld_rr_ins ( ld, srcptr_reg, cnt_reg, copy_reg ) ;
        st_rr_ins ( st, copy_reg, destptr_reg, cnt_reg ) ;
        br_ins ( i_bne, loop ) ;
        return ( NOREG ) ;
      }
    }
    default:
    {
      /* fall through to fail */
    }
    }
  }
  }
  fail ( "Illegal move" ) ;
  return ( NOREG ) ;
}