Subversion Repositories tendra.SVN

Rev

Blame | 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/oprators.c,v 1.1.1.1 1998/01/17 15:55:55 release Exp $
--------------------------------------------------------------------------
$Log: oprators.c,v $
 * Revision 1.1.1.1  1998/01/17  15:55:55  release
 * First version to be checked into rolling release.
 *
 * Revision 1.9  1997/10/10  18:32:50  pwe
 * prep ANDF-DE revision
 *
 * Revision 1.8  1997/08/23  13:54:24  pwe
 * initial ANDF-DE
 *
 * Revision 1.7  1997/02/18  11:48:13  pwe
 * NEWDIAGS for debugging optimised code
 *
 * Revision 1.6  1996/08/27  14:54:38  pwe
 * Q_functions destroy flpt regs
 *
 * Revision 1.5  1996/03/20  16:12:37  john
 * Reformatting
 *
 * Revision 1.4  1995/07/27  16:32:04  john
 * Fix for quad op
 *
 * Revision 1.3  1995/07/14  16:33:04  john
 * Changes for new error handling
 *
 * Revision 1.2  1995/05/26  13:00:16  john
 * Reformatting
 *
 * Revision 1.1.1.1  1995/03/13  10:18:50  john
 * Entered into CVS
 *
 * Revision 1.5  1994/12/21  11:47:13  djch
 * added labeldecs.h to declare set_label
 *
 * Revision 1.4  1994/12/01  13:18:59  djch
 * Altered abs code to generate a label, and pass to br_abs. .+8 is allowed by
 * the SPARC asm manual, not by /bin/as...
 *
 * Revision 1.3  1994/11/28  16:55:06  djch
 * added code for absop to generate efficient abs code
 *
 * Revision 1.2  1994/07/07  16:11:33  djch
 * Jul94 tape
 *
 * Revision 1.1  1994/05/03  14:49:47  djch
 * Initial revision
 *
 * Revision 1.5  93/09/27  14:52:20  14:52:20  ra (Robert Andrews)
 * Added quad_op, the main routine for dealing with long double
 * operations.  Modified fop to allow for long doubles.
 * 
 * Revision 1.4  93/08/27  11:34:44  11:34:44  ra (Robert Andrews)
 * A couple of lint-like changes.
 * 
 * Revision 1.3  93/07/08  18:22:32  18:22:32  ra (Robert Andrews)
 * Reformatted.
 * 
 * Revision 1.2  93/06/29  14:30:03  14:30:03  ra (Robert Andrews)
 * Now use ins_p to represent instructions.
 * 
 * Revision 1.1  93/06/24  14:58:57  14:58:57  ra (Robert Andrews)
 * Initial revision
 * 
--------------------------------------------------------------------------
*/


#define SPARCTRANS_CODE
#include "config.h"
#include "common_types.h"
#include "codehere.h"
#include "expmacs.h"
#include "addrtypes.h"
#include "inst_fmt.h"
#include "move.h"
#include "maxminmacs.h"
#include "getregs.h"
#include "guard.h"
#include "tags.h"
#include "shapemacs.h"
#include "bitsmacs.h"
#include "myassert.h"
#include "externs.h"
#include "install_fns.h"
#include "regmacs.h"
#include "regexps.h"
#include "exp.h"
#include "out.h"
#include "locate.h"
#include "eval.h"
#include "muldvrem.h"
#include "proc.h"
#include "labels.h"
#include "oprators.h"

/*
    CORRECT POSSIBLE OVERFLOWS IN REGISTER r
*/

void tidyshort 
    PROTO_N ( ( r, s ) )
    PROTO_T ( int r X shape s ){
  if ( name ( s ) == ucharhd ) {
    rir_ins ( i_and, r, 0xff, r ) ;
  } else if ( name ( s ) == uwordhd ) {
    rir_ins ( i_and, r, 0xffff, r ) ;
  }
  return ;
}


/*
  REMOVE AN EXPRESSION CONTAINING A REGISTER
  Given a list of expressions, seq, find one whose value is in 
  register reg.  If this is found, it is removed from seq and 1 
  is returned.  Otherwise 0 is returned.
*/
bool regremoved 
    PROTO_N ( ( seq, reg ) )
    PROTO_T ( exp * seq X int reg ){
  exp s = *seq ;
  exp t = bro ( s ) ;
  if ( ABS_OF ( regofval ( s ) ) == reg ) {
    ( *seq ) = t ;
    return ( 1 ) ;
  }
  for ( ; ; ) {
    if ( ABS_OF ( regofval ( t ) ) == reg ) {
      bro ( s ) = bro ( t ) ;
      if ( last ( t ) ) setlast ( s ) ;
      return ( 1 ) ;
    }
    if ( last ( t ) ) return ( 0 ) ;
    s = t ;
    t = bro ( t ) ;
  }
  /* NOT REACHED */
}


/*
  EVALUATE A COMMUTATIVE OPERATION
  Evaluates reg = seq_1 @ seq_2 @ ... where @ is a binary commutative
  operation given by rins.  sp may be used for free t-registers.
*/
void do_comm 
    PROTO_N ( ( seq, sp, final, rins ) )
    PROTO_T ( exp seq X space sp X int final X ins_p rins ){
  int r = 0 ;
  space nsp ;
  int a1, a2 ;
  /* should have been optimised in scan... */
  assert ( !( rins == i_add && name ( seq ) == neg_tag &&
              name ( bro ( seq ) ) != val_tag ) ) ;
  /* evaluate first operand into a1 */
  a1 = reg_operand ( seq, sp ) ;
  for ( ; ; ) {
    nsp = guardreg ( a1, sp ) ;
    seq = bro ( seq ) ;
    if ( name ( seq ) == val_tag ) {
      /* next operand is a constant */
      if ( last ( seq ) ) {
        rir_ins ( rins, a1, ( long ) no ( seq ), final ) ;
        return ;
      } 
      else {
        if ( r == 0 ) r = getreg ( sp.fixed ) ;
        rir_ins ( rins, a1, ( long ) no ( seq ), r ) ;
      }
    } 
    else {
      /* evaluate next operand */
      exp sq = seq ;
      ins_p ins = rins ;
      a2 = reg_operand ( sq, nsp ) ;
      if ( last ( seq ) ) {
        rrr_ins ( ins, a1, a2, final ) ;
        return ;
      } 
      else {
        if ( r == 0 ) r = getreg ( sp.fixed ) ;
        rrr_ins ( ins, a1, a2, r ) ;
      }
    }
    a1 = r ;
  }
  /* NOT REACHED */
}


/*
  EVALUATE A COMMUTATIVE OPERATION
  The commutative operation, rrins, given by e is evaluated into d,
  using sp to get free t-registers.
*/

int comm_op 
    PROTO_N ( ( e, sp, d, rrins ) )
    PROTO_T ( exp e X space sp X where d X ins_p rrins ){
  ins_p rins = rrins ;
  switch ( discrim ( d.answhere ) ) {
    case inreg : {
      int dest = regalt ( d.answhere ) ;
      bool usesdest = regremoved ( &son ( e ), dest ) ;
      exp seq = son ( e ) ;
      if(dest == R_G0) {
        dest = getreg(sp.fixed);
      }

      /* the destination is in a register, take care that we don't
         alter it before possible use as an operand ... */
      if ( usesdest && last ( seq ) ) {
        /* used, but there is only one other operand */
        if ( name ( seq ) == val_tag ) {
          rir_ins ( rins, dest, ( long ) no ( seq ), dest ) ;
        } 
        else {
          rrr_ins ( rins, dest, reg_operand ( seq, sp ), dest ) ;
        }
        if(optop(e)) tidyshort ( dest, sh ( e ) ) ;
        return ( dest ) ;
      } 
      else if ( usesdest ) {
        /* dest used, use temp */
        int r = getreg ( sp.fixed ) ;
        do_comm ( seq, sp, r, rins ) ;
        rrr_ins ( rins, dest, r, dest ) ;
        if (optop(e)) tidyshort ( dest, sh ( e ) ) ;
        return ( dest ) ;
      } 
      else {
        /* dest not used, evaluate into dest */
        do_comm ( seq, sp, dest, rins ) ;
        if (optop(e)) tidyshort ( dest, sh ( e ) ) ;
        return ( dest ) ;
      }
    }
    default : {
      ans a ;
      space nsp ;
      int r = getreg ( sp.fixed ) ;
      setregalt ( a, r ) ;
      /* evaluate the expression into r ... */
      do_comm ( son ( e ), sp, r, rins ) ;
      if(optop(e)) tidyshort ( r, sh ( e ) ) ;
      nsp = guardreg ( r, sp ) ;
      /* ... and move into a */
      ( void ) move ( a, d, nsp.fixed, 1 ) ;
      return ( r ) ;
    }
  }
  /* NOT REACHED */
}


/*
  EVALUATE A NON-COMMUTATIVE OPERATION
  The non-commutative operation, rins, given by e is evaluated 
  into dest, using sp to get free t-registers.
*/
int non_comm_op 
    PROTO_N ( ( e, sp, dest, rins ) )
    PROTO_T ( exp e X space sp X where dest X ins_p rins ){
  exp l = son ( e ) ;
  exp r = bro ( l ) ;
  int a1 = reg_operand ( l, sp ), a2 ;
  space nsp ;
  nsp = guardreg ( a1, sp ) ;
  a2 = reg_operand ( r, nsp ) ;
  switch ( discrim ( dest.answhere ) ) {
    case inreg : {
      int d = regalt ( dest.answhere ) ;
      if(d == R_G0) {
        d = getreg(sp.fixed);
      }
      rrr_ins ( rins, a1, a2, d ) ;
      if(optop(e)) tidyshort ( d, sh ( e ) ) ;
      return ( d ) ;
    }
    default : {
      ans a ;
      int r1 = getreg ( nsp.fixed ) ;
      setregalt ( a, r1 ) ;
      rrr_ins ( rins, a1, a2, r1 ) ;
      if(optop(e)) tidyshort ( r1, sh ( e ) ) ;
      nsp = guardreg ( r1, sp ) ;
      ( void ) move ( a, dest, nsp.fixed, 1 ) ;
      return ( r1 ) ;
    }
  }
  /* NOT REACHED */
}


/*
  EVALUATE A MONADIC OPERATION
  The monadic operation, ins, given by e is evaluated into dest, 
  using sp to get free t-registers.
*/
int monop 
    PROTO_N ( ( e, sp, dest, ins ) )
    PROTO_T ( exp e X space sp X where dest X ins_p ins ){
  int r1 = getreg ( sp.fixed ) ;
  int a1 = reg_operand ( son ( e ), sp ) ;
  switch ( discrim ( dest.answhere ) ) {
    case inreg : {
      int d = regalt ( dest.answhere ) ;
      if (d == R_G0) d = getreg(sp.fixed);
      rr_ins ( ins, a1, d ) ;
      if(optop(e)) tidyshort ( d, sh ( e ) ) ;
      return ( d ) ;
    }
    default : {
      ans a ;
      space nsp ;
      setregalt ( a, r1 ) ;
      rr_ins ( ins, a1, r1 ) ;
      if(optop(e)) tidyshort ( r1, sh ( e ) ) ;
      nsp = guardreg ( r1, sp ) ;
      ( void ) move ( a, dest, nsp.fixed, 1 ) ;
      return ( r1 ) ;
    }
  }
  /* NOT REACHED */
}

/*
    EVALUATE abs OPERATION as
    move a1 -> d
    subcc %g0, a1 -> R_TMP
    bpos,a L
    move R_TMP -> d
L:

*/
int absop 
    PROTO_N ( ( e, sp, dest ) )
    PROTO_T ( exp e X space sp X where dest ){
  int r1 = getreg ( sp.fixed ) ;
  int a1 = reg_operand ( son ( e ), sp ) ;
  int lab = new_label();
  
  switch ( discrim ( dest.answhere ) ) {
    case inreg : {
      int d = regalt ( dest.answhere ) ;
      if (d == R_G0) {
        d = getreg(sp.fixed);
      }
      rr_ins ( i_mov, a1, d ) ;
      rrr_ins ( i_subcc, R_G0, a1, R_TMP);
      br_abs(lab);
      rr_ins( i_mov, R_TMP, d);
      set_label(lab);
      if(optop(e)) tidyshort ( d, sh ( e ) ) ;
      return ( d ) ;
    }
    default : {
      ans a ;
      space nsp ;
      setregalt ( a, r1 ) ;
      rr_ins ( i_mov, a1, r1 ) ;
      rrr_ins ( i_subcc, R_G0, a1, R_TMP);
      br_abs(lab);
      rr_ins( i_mov, R_TMP, r1);
      set_label(lab);
      if(optop(e)) tidyshort ( r1, sh ( e ) ) ;
      nsp = guardreg ( r1, sp ) ;
      ( void ) move ( a, dest, nsp.fixed, 1 ) ;
      return ( r1 ) ;
    }
  }
  /* NOT REACHED */
}


/*
  GET THE ADDRESS OF A LONG DOUBLE
*/
static void quad_addr 
    PROTO_N ( ( e, r, sp ) )
    PROTO_T ( exp e X int r X space sp ){
  instore is ;
  if ( name ( e ) == real_tag ) {
    is = evaluated ( e, 0, 1 ) ;
  } 
  else {
    where w ;
    w = locate1 ( e, sp, sh ( e ), 0 ) ;
    if ( discrim ( w.answhere ) != notinreg ) {
      fail ( "Illegal expression in quad_addr" ) ;
    }
    is = insalt ( w.answhere ) ;
  }
  if ( is.adval ) {
    fail ( "Illegal expression in quad_addr" ) ;
  }
  if ( IS_FIXREG ( is.b.base ) ) {
    if ( is.b.offset == 0 ) {
      if ( is.b.base != r ) rr_ins ( i_mov, is.b.base, r ) ;
    } 
    else {
      rir_ins ( i_add, is.b.base, is.b.offset, r ) ;
    }
  } 
  else {
    set_ins ( is.b, r ) ;
  }
  return ;
}


/*
  DO A LONG DOUBLE OPERATION
*/
void quad_op 
    PROTO_N ( ( a1, a2, sp, dest, op ) )
    PROTO_T ( exp a1 X exp a2 X space sp X where dest X int op ){
  char *s ;
  bool quad_ret = 1 ;
  if ( op < 0 ) {
    /* Test operations */
    quad_ret = 0 ;
    switch ( -op ) {
      case 1 : s = "_Q_fle,2" ; break ;
      case 2 : s = "_Q_flt,2" ; break ;
      case 3 : s = "_Q_fge,2" ; break ;
      case 4 : s = "_Q_fgt,2" ; break ;
      case 5 : s = "_Q_fne,2" ; break ;
      case 6 : s = "_Q_feq,2" ; break ;
      default : fail ( "Illegal floating-point test" ) ;
    }
  } 
  else {
    /* Binary operations */
    switch ( op ) {
      case fplus_tag : s = "_Q_add,2" ; break ;
      case fminus_tag : s = "_Q_sub,2" ; break ;
      case fmult_tag : s = "_Q_mul,2" ; break ;
      case fdiv_tag : s = "_Q_div,2" ; break ;
      case fneg_tag : s = "_Q_neg,1" ; break ;
      case chfl_tag : s = "_Q_stoq,1" ; break ;
      case float_tag : s = "_Q_itoq,1" ; break ;
      case 100 : s = "_Q_qtod,1" ; quad_ret = 0 ; break ;
      case 101 : s = "_Q_qtos,1" ; quad_ret = 0 ; break ;
      case fabs_tag : 
      /* special case: there is no special operation for 
         this, so it has to be performed by a code sequence*/
      fail("No operation for fabs( long double )");
      s = "_Q_abs,1" ; 
      break ;
      default : fail ( "Illegal floating-point operation" ) ;
    }
  }
  if ( quad_ret ) {
    instore is ;
    baseoff ret_addr ;
    ret_addr.base = R_SP ;
    ret_addr.offset = ( 16 * 4 ) ;
    is = insalt ( dest.answhere ) ;
    if ( discrim ( dest.answhere ) != notinreg ) {
      is.b = mem_temp(0);
      is.adval = 1;
      /*fail ( "Illegal expression in quad_op" ) ;*/
    }
    if ( is.adval ) {
      if ( IS_FIXREG ( is.b.base ) ) {
        if ( is.b.offset == 0 ) {
          st_ro_ins ( i_st, is.b.base, ret_addr ) ;
        } 
        else {
          rir_ins ( i_add, is.b.base, is.b.offset, R_TMP ) ;
          st_ro_ins ( i_st, R_TMP, ret_addr ) ;
        }
      } 
      else {
        set_ins ( is.b, R_TMP ) ;
        st_ro_ins ( i_st, R_TMP, ret_addr ) ;
      }
    } 
    else {
      ld_ins ( i_ld, is.b, R_TMP ) ;
      st_ro_ins ( i_st, R_TMP, ret_addr ) ;
    }
  }
  /* hack for float integer */
  if ( op == float_tag ) {
    int r = reg_operand ( a1, sp ) ;
    if ( name ( sh ( a1 ) ) == ulonghd ) s = "_Q_utoq,1" ;
    if ( r != R_O0 ) rr_ins ( i_mov, r, R_O0 ) ;
    a1 = nilexp ;
  }
  /* hack for change floating variety */
  if ( op == chfl_tag ) {
    ans aa ;
    where w ;
    freg frg ;
    frg.fr = getfreg ( sp.flt ) ;
    if ( name ( sh ( a1 ) ) == realhd ) {
      s = "_Q_dtoq,1" ;
      frg.dble = 1 ;
    } 
    else {
      frg.dble = 0 ;
    }
    setfregalt ( aa, frg ) ;
    w.answhere = aa ;
    w.ashwhere = ashof ( sh ( a1 ) ) ;
    ( void ) code_here ( a1, sp, w ) ;
    if ( frg.dble ) {
      stf_ins ( i_std, frg.fr << 1, mem_temp ( 0 ) ) ;
      ld_ro_ins ( i_ld, mem_temp ( 0 ), R_O0 ) ;
      ld_ro_ins ( i_ld, mem_temp ( 4 ), R_O1 ) ;
    } 
    else {
      stf_ins ( i_st, frg.fr << 1, mem_temp ( 0 ) ) ;
      ld_ro_ins ( i_ld, mem_temp ( 0 ), R_O0 ) ;
    }
    a1 = nilexp ;
  }
  /* put the arguments into the call registers */
  if ( a1 != nilexp ) {
    quad_addr ( a1, R_O0, sp ) ;
    if ( a2 != nilexp ) {
      sp = needreg ( R_O0, sp ) ;
      quad_addr ( a2, R_O1, sp ) ;
    }
  }
  /* output the actual call */
  outs ( "\tcall\t" ) ;
  outs ( s ) ;
  outs ( "\n\tnop\n" ) ;
  if ( quad_ret ) outs ( "\tunimp\t16\n" ) ;
  clear_all () ;
  return ;
}


/*
  EVALUATE A FLOATING POINT OPERATION
  The floating point operation, ins, given by e is evaluated 
  into dest, using sp to get free t-registers.
*/
int fop 
    PROTO_N ( ( e, sp, dest, ins ) )
    PROTO_T ( exp e X space sp X where dest X ins_p ins ){
  exp l = son ( e ) ;
  exp r = bro ( l ) ;
  space nsp ;
  int a1, a2 ;

#if use_long_double
  if ( name ( sh ( e ) ) == doublehd ) {
    if ( IsRev ( e ) ) {
      quad_op ( r, l, sp, dest, ( int ) name ( e ) ) ;
    } 
    else {
      quad_op ( l, r, sp, dest, ( int ) name ( e ) ) ;
    }
    return ( NOREG ) ;
  }
#endif
  if ( IsRev ( e ) ) {
    /* reverse operands */
    a2 = freg_operand ( r, sp, getfreg ( sp.flt ) ) ;
    nsp = guardfreg ( a2, sp ) ;
    a1 = freg_operand ( l, nsp, getfreg ( nsp.flt ) ) ;
    } 
  else {
    a1 = freg_operand ( l, sp, getfreg ( sp.flt ) ) ;
    nsp = guardfreg ( a1, sp ) ;
    a2 = freg_operand ( r, nsp, getfreg ( nsp.flt ) ) ;
  }
  switch ( discrim ( dest.answhere ) ) {
    case infreg : {
      freg fr ;
      fr = fregalt ( dest.answhere ) ;
      rrrf_ins ( ins, a1 << 1, a2 << 1, fr.fr << 1 ) ;
      return ( ( fr.dble ) ? -( fr.fr + 32 ) : ( fr.fr + 32 ) ) ;
    }
    default : {
      ans a ;
      freg fr ;
      int r1 = getfreg ( nsp.flt ) ;
      fr.fr = r1 ;
      fr.dble = ( bool ) ( ( dest.ashwhere.ashsize == 64 ) ? 1 : 0 ) ;
      setfregalt ( a, fr ) ;
      rrrf_ins ( ins, a1 << 1, a2 << 1, r1 << 1 ) ;
      ( void ) move ( a, dest, sp.fixed, 1 ) ;
      return ( ( fr.dble ) ? -( fr.fr + 32 ) : ( fr.fr + 32 ) ) ;
    }
  }
    /* NOT REACHED */
}