Subversion Repositories tendra.SVN

Rev

Rev 5 | 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.
*/


/*
$Log: oprators.c,v $
 * Revision 1.1.1.1  1998/01/17  15:56:03  release
 * First version to be checked into rolling release.
 *
 * Revision 1.9  1997/01/29  10:19:14  wfs
 *    Fixed a minor bug in "move.c" and "oprators.c" due to immediates of  >
 * 14 bits appearing in the field of ldo instrcutions.
 *
 * Revision 1.8  1996/11/25  13:43:25  wfs
 *    Fixed the comm_op register tracking bug in "oprators.c" and removed a
 * few superfluous "#if 0"s.
 *
 * Revision 1.7  1996/08/30  09:02:30  wfs
 * Various fixes of bugs arising from avs and pl_tdf tests.
 *
 * Revision 1.6  1996/02/15  10:09:40  wfs
 * Incorrect decrement - which I introduced in last bug fix - removed.
 *
 * Revision 1.5  1996/02/14  17:19:20  wfs
 * "next_caller_offset" and "next_callee_offset" have become special tokens
 * defined in "spec_tok.c". Bug fix to a "and_tag" optimization in
 * "oprators.c". A few bug fixes in "makecode.c" arising from the variable
 * caller tests. "promote_pars" defined in "config.h".
 *
 * Revision 1.4  1996/01/22  17:26:02  wfs
 * Bug fix to "make_stack_limit_tag".
 *
 * Revision 1.3  1996/01/17  13:51:02  wfs
 * Another adjustment to "round_tag" - avoid "_U_Qfcnvfxt_dbl_to_sgl" if the
 * error_treatment is "continue".
 *
 * Revision 1.2  1995/12/18  13:12:14  wfs
 * Put hppatrans uder cvs control. Major Changes made since last release
 * include:
 * (i) PIC code generation.
 * (ii) Profiling.
 * (iii) Dynamic Initialization.
 * (iv) Debugging of Exception Handling and Diagnostics.
 *
 * Revision 5.6  1995/10/20  14:08:29  wfs
 * gcc compilation changes.
 *
 * Revision 5.5  1995/10/13  10:44:34  wfs
 * Partial fix of a "round_with_mode" bug.
 *
 * Revision 5.4  1995/10/12  17:22:21  wfs
 * A "=" where there should have been "==".
 *
 * Revision 5.3  1995/10/10  16:50:25  wfs
 * There is a problem in the common code which means that floating_test's
 * error_treatment cannot be implemented for the time being.
 *
 * Revision 5.2  1995/10/09  13:09:29  wfs
 * Cosmetic changes.
 *
 * Revision 5.1  1995/09/15  13:04:52  wfs
 * Rewrote "quad_op" to ease reading and implementation of the quad
 * error jumps.
 *
 * Revision 5.0  1995/08/25  13:42:58  wfs
 * Preperation for August 25 Glue release
 *
 * Revision 3.4  1995/08/25  10:19:50  wfs
 * Register synonyms changed
 *
 * Revision 3.4  1995/08/25  10:19:50  wfs
 * Register synonyms changed
 *
 * Revision 3.1  95/04/10  16:27:38  16:27:38  wfs (William Simmonds)
 * Apr95 tape version.
 * 
 * Revision 3.0  95/03/30  11:18:31  11:18:31  wfs (William Simmonds)
 * Mar95 tape version with CRCR95_178 bug fix.
 * 
 * Revision 2.0  95/03/15  15:28:22  15:28:22  wfs (William Simmonds)
 * spec 3.1 changes implemented, tests outstanding.
 * 
 * Revision 1.7  95/02/10  11:41:20  11:41:20  wfs (William Simmonds)
 * Removed call to evaluated() - initialising expressions are now
 * stored in a linked list and written to outf after the procedure
 * body has been translated (c.f. translate_capsule).
 * 
 * Revision 1.6  95/01/25  13:37:44  13:37:44  wfs (William Simmonds)
 * Refined error_jump of float plus, minus, mult, div.
 * 
 * Revision 1.5  95/01/25  10:31:56  10:31:56  wfs (William Simmonds)
 * First attempt at installing error_jump in the float plus, minus, mult
 * and div tags.
 * 
 * Revision 1.4  95/01/23  18:58:04  18:58:04  wfs (William Simmonds)
 * Cosmetic changes to do_comm and non_comm_op.
 * 
 * Revision 1.3  95/01/17  17:30:00  17:30:00  wfs (William Simmonds)
 * Changed name of an included header file.
 * 
 * Revision 1.2  95/01/12  11:27:16  11:27:16  wfs (William Simmonds)
 * Corrected bug in `logical_op' which was causing hppatrans
 * to fail to bootstrap.
 * 
 * Revision 1.1  95/01/11  13:14:24  13:14:24  wfs (William Simmonds)
 * Initial revision
 * 
*/


#define HPPATRANS_CODE
#include "config.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 "common_types.h"
#include "myassert.h"
#include "labels.h"
#include "frames.h"
#include "oprators.h"

#define isdbl(e) ( ( bool ) ( name ( e ) != shrealhd ) )


#if use_long_double
#include "externs.h"
#include "install_fns.h"
#include "regmacs.h"
#include "exp.h"
#include "out.h"
#include "locate.h"
#include "eval.h"
#include "muldvrem.h"
#include "proc.h"
#include "basicread.h"
#include "inst_fmt.h"
#endif

extern long trap_label PROTO_S ((exp));
extern void trap_handler PROTO_S ((baseoff,int,int));
extern baseoff zero_exception_register PROTO_S ((space));
extern labexp current,first;


int long_double_0 = 0;

/* corrects possible overflows of chars and shorts in reg r */
void tidyshort 
    PROTO_N ( ( r, s ) )
    PROTO_T ( int r X shape s )
{
  if (name(s) == ucharhd)
     riir_ins(i_dep,c_,0,23,24,r);
  else if (name(s) == uwordhd)
     riir_ins(i_dep,c_,0,15,16,r);
}


 /*
  * given a list of expressions seq which contains one whose value is in
  * register reg, removes that exp from seq and delivers 1; otherwise delivers
  * 0
  */
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);
  }
}



/*
 *   logical operation, lop, with operands immediate, i, and register, r
 */
void logical_op
    PROTO_N ( (lop,i,r,d) )
    PROTO_T ( CONST char *lop X long i X int r X int d )
{
   int t;
   if (r==d)
      t=GR1; 
   else
      t=d;
   if (lop==i_and && i==-1)
   {
      if (r!=d)
         rr_ins(i_copy,r,d);
      return;
   }
   else if ( lop==i_and && IS_POW2((i+1)))
   {
      int p=0;
      while ( i & (1<<p) ) p++;
      if (r==d)
         iiir_ins(i_depi,c_,0,31-p,32-p,d);
      else
         riir_ins(i_extru,c_,r,31,p,d);
      return;
   }
   else if ( lop==i_and && IS_POW2((-i)) )
   {
      int p=0;
      while ( (i & (1<<p))==0 ) p++;
      if (r!=d)
         rr_ins(i_copy,r,d);
      iiir_ins(i_depi,c_,0,31,p,d);
      return;
   }
   else if ( lop==i_or )
   {
      if (r==0)
      {
         imm_to_r(i,d);
         return;
      }
      else 
      if (i==-1)
      {
         ir_ins(i_ldi,fs_,"",-1,d);
         return;
      }
      else
      {
         int j=0;
         unsigned int p=i;
         while ( (p & (1<<j))==0 ) j++;
         p=p>>j;
         if (((p+1)&p)==0)
         {
            int k=0;
            while ( p & (1<<k) ) k++;
            if (r!=d)
               rr_ins(i_copy,r,d);
            iiir_ins(i_depi,c_,-1,31-j,k,d);
            return;
         }
      }
   }
   if (SIMM14(i))
   {
      ir_ins(i_ldi,fs_,"",i,t);
      rrr_ins(lop,c_,r,t,d);
   }
   else
   if (SIMM14(~i) && lop==i_and)
   {
      ir_ins(i_ldi,fs_,"",~i,t);
      rrr_ins(i_andcm,c_,r,t,d);
   }
   else
   if ( ((i&(i+1))==0) && lop==i_and)
   {
       unsigned long ui = i;
       int nbits=0;
       while (ui != 0)
       {
         nbits++;
         ui=ui>>1;
       }
       riir_ins(i_zdep,c_,r,31,nbits,d);
   }
   else
   {
      imm_to_r(i,t);
      rrr_ins(lop,c_,r,t,d);
   }
}



 /*
  * evaluates the fixed operation seq1 rins seq 2 rins...., into reg final,
  * using sp as free t-regs
  */
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;
  int a2;
  exp next = bro(seq);

  if ( name(seq)==not_tag &&
       last(next) &&
       rins==i_and &&
       name(next)!=val_tag )
  {
     a1=reg_operand(son(seq), sp);
     nsp = guardreg(a1, sp);
     a2=reg_operand(next, nsp);
     rrr_ins(i_andcm,c_,a2,a1,final);
     return;
  }

  if ( name(next)==not_tag &&
      last(next) && 
      rins==i_and &&
      name(seq)!=val_tag )
  {
     a1=reg_operand(seq, sp);
     nsp = guardreg(a1, sp);
     a2=reg_operand(son(next), nsp);
     rrr_ins(i_andcm,c_,a1,a2,final);
     return;
  }

  if ( name(next)==val_tag &&
       last(next) &&
       rins==i_and &&
       name(seq)==shr_tag )
  {
     exp shift=bro(son(seq));
     if (name(shift)==val_tag)
     { 
        int n,s;
        n=no(next);
        s=no(shift);
        if ( IS_POW2((n+1)) )
        {
           int p=0;
           a1=reg_operand(son(seq), sp);
           while ( n & (1<<p) ) p++;
           if ( p > (32-s) )
              p = 32-s;
           riir_ins(i_extru,c_,a1,31-s,p,final);
           return;
        }
     }
  }


  /* evaluate 1st operand into a1 */
  
  if ( name(seq)==cont_tag && name(bro(seq))==val_tag && last(bro(seq))
       && !(props(son(seq)) & inreg_bits) )
  {
     reg_operand_here(seq, sp, final);
     a1 = final;
  }
  else
     a1 = reg_operand(seq, sp);

  if ( name(father(seq))==make_stack_limit_tag )
  {
     baseoff b;
     b.offset = FP_BOFF.offset;
     b.base = a1;
     ld_ins(i_lw,0,b,b.base);
  }

  for (;;)
  {
    nsp = guardreg(a1, sp);
    seq = bro(seq);
    if (name(seq) == val_tag)   /* next operand is a constant */
    {
      int n=no(seq);
      if (last(seq))
      {
        if (rins==i_add)
        {
           if (SIMM14(n))
              ld_ir_ins(i_ldo,cmplt_,fs_,empty_ltrl,n,a1,final);
           else
           {
              ir_ins(i_addil,fs_L,empty_ltrl,n,a1);
              ld_ir_ins(i_ldo,cmplt_,fs_R,empty_ltrl,n,GR1,final);
           }
        }
        else
           logical_op(rins,n,a1,final);
        return;
      }
      else
      {
         if (r == 0)
             r = getreg(sp.fixed);
         if (rins==i_add)
         {
           if (SIMM14(n))
              ld_ir_ins(i_ldo,cmplt_,fs_,empty_ltrl,n,a1,r);
           else
           {
              ir_ins(i_addil,fs_L,empty_ltrl,n,a1);
              ld_ir_ins(i_ldo,cmplt_,fs_R,empty_ltrl,n,GR1,r);
           }
         }
         else
            logical_op(rins,n,a1,r);
      }
    }
    else
    {
       exp sq = seq;
       CONST char *ins = rins;

       a2 = reg_operand(sq, nsp);
       /* evaluate next operand */
       if (last(seq))
       {
          rrr_ins(ins,c_,a1,a2,final);
          return;
       }
       else
       {
          if (r == 0)
             r = getreg(sp.fixed);
          rrr_ins(ins,c_,a1,a2,r);
       }
    }
    a1 = r;
  }
}



/* evaluate commutative operation rrins given by e into d, using sp to get t-regs */
int comm_op 
    PROTO_N ( ( e, sp, d, rrins ) )
    PROTO_T ( exp e X space sp X where d X ins_p rrins )
{
  CONST char *rins = rrins;

  switch (discrim ( d.answhere ))
  {
  case inreg:
    {
      int dest = regalt(d.answhere);
      bool usesdest = regremoved(&son(e), dest);
      exp seq = son(e);

      /*
       * the destination is in a register; take care that we dont 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)
        {
           int n = no(seq);
           if (rrins==i_add)
           {
              if (SIMM14(n))
                 ld_ir_ins(i_ldo,cmplt_,fs_,empty_ltrl,n,dest,dest);
              else
              {
                 ir_ins(i_addil,fs_L,empty_ltrl,n,dest);
                 ld_ir_ins(i_ldo,cmplt_,fs_R,empty_ltrl,n,GR1,dest);
              }
           }
           else
              logical_op(rins,n,dest,dest);
        }
        else
           rrr_ins(rins,c_,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,c_,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;
      }
    }                           /* end inreg */
  default:
    {
      ans a;
      int r = getreg(sp.fixed);
      space nsp;
      bool rok = 1;
      setregalt(a, r);
      do_comm(son(e), sp, r, rins);
      /* evaluate the expression into r ... */
      if (discrim(d.answhere) != notinreg)
      {
         if (optop(e))
            tidyshort(r, sh(e));
      }
      else
         rok = shape_size(sh(e))==32;
      nsp = guardreg(r, sp);
      move(a, d, nsp.fixed, 1);
      /* ... and move into a */
      return ((rok)?r:NOREG);
    }                           /* notinreg */
  }                             /* end switch */
}



int non_comm_op 
    PROTO_N ( ( e, sp, dest, rins ) )
    PROTO_T ( exp e X space sp X where dest X ins_p rins )
 /* evalate binary operation e with rins into dest */
{
   exp l = son(e);
   exp r = bro(l);
   int a1 = reg_operand(l, sp);
   space nsp;
   int a2;
   CONST char *ins;
   ins=rins;
   nsp = guardreg(a1, sp);
   a2 = reg_operand(r, nsp);
   if (discrim( dest.answhere )==inreg)
   {
      int d = regalt(dest.answhere);
      rrr_ins(ins,c_,a1,a2,d);
      if (optop(e))
         tidyshort(d, sh(e));
      return d;
   }
   else
   {
      /* destination elsewhere */
      ans a;
      int r1 = getreg(nsp.fixed);
      setregalt(a, r1);
      rrr_ins(ins,c_,a1,a2,r1);
      if (optop(e))
         tidyshort(r1, sh(e));
      nsp = guardreg(r1, sp);
      move(a, dest, nsp.fixed, 1);
      return r1;
   }
}

int monop 
    PROTO_N ( ( e, sp, dest, ins ) )
    PROTO_T ( exp e X space sp X where dest X ins_p ins )
 /* evaluate fixed monadic operation e using ins into dest */
{
   int r1 = getreg(sp.fixed);
   int a1 = reg_operand(son(e), sp);

   /* operand in reg a1 */
   space nsp;

   if ( discrim(dest.answhere) == inreg )
   {
      /* destination in register */
      int d = regalt(dest.answhere);
      if (ins==i_subi)
         rrr_ins(i_sub,c_,0,a1,d);
      else
      if (ins==i_sub)
         rrr_ins(i_sub,c_,0,a1,d);
      else
         rrr_ins(i_uaddcm,c_,0,a1,d);
      if (optop(e))
         tidyshort(d,sh(e));
      return d;
   }
   else
   {
      /* destination elsewhere */
      ans a;
      setregalt(a, r1);
      if (ins==i_subi)
         rrr_ins(i_sub,c_,0,a1,r1);
      else
      if (ins==i_sub)
         rrr_ins(i_sub,c_,0,a1,r1);
      else
         rrr_ins(i_uaddcm,c_,0,a1,r1);
      if (optop(e))
         tidyshort(r1, sh(e));
      nsp = guardreg(r1, sp);
      move(a, dest, nsp.fixed, 1);
      return r1;
   }
}


#if use_long_double

/*
    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)
    {
        labexp next;
        next  = (labexp) malloc( sizeof(struct labexp_t) );
        next->e = e;
        next->lab = next_data_lab();
        next->next = (labexp) 0;
        current->next = next;
        current = next;
        is.adval = 0;
        is.b.offset = 0;
        is.b.base = next->lab;
    }
    else
    {
       where w ;
       w=locate1(e,sp,sh(e),0) ;
       if (discrim(w.answhere)!=notinreg) 
          failer ("Illegal expression in quad_addr");
       is=insalt(w.answhere) ;
    }
    if (is.adval)
    {
        failer("Illegal expression in quad_addr") ;
    }
    if (IS_FIXREG(is.b.base))
    {
       if (is.b.offset==0)
       {
          if (is.b.base!=r)
             rr_ins(i_copy,is.b.base,r) ;
       }
       else
          ld_ins(i_lo,1,is.b,r) ;
    }
    else
       set_ins("",is.b,r) ;
    return ;
}


/*
    LONG DOUBLE LIBRARY
*/

static struct {
                  CONST char proc_name[32] ;
                  bool called ;
              } long_double_lib [ 14 ] =
              {
                  { "_U_Qfcmp", 0 },
                  { "_U_Qfadd", 0 },
                  { "_U_Qfsub", 0 },
                  { "_U_Qfmpy", 0 },
                  { "_U_Qfdiv", 0 },
                  { "_U_Qfcnvff_dbl_to_quad", 0 },
                  { "_U_Qfcnvff_sgl_to_quad", 0 },
                  { "_U_Qfcnvxf_dbl_to_quad", 0 },
                  { "_U_Qfcnvxf_sgl_to_quad", 0 },
                  { "_U_Qfcnvff_quad_to_dbl", 0 },
                  { "_U_Qfcnvff_quad_to_sgl", 0 },
                  { "_U_Qfabs", 0 },
                  { "_U_Qfcnvfxt_quad_to_sgl", 0 },
                  { "_U_Qfrnd", 0 }
              } ;


void import_long_double_lib
    PROTO_Z ()
{
   int n;
   for(n=0; n<14; n++)
      if ( long_double_lib[n].called )
         fprintf(outf,"\t.IMPORT\t%s,CODE\n",long_double_lib[n].proc_name);
   if (long_double_0)
   {
      outnl();
      outs("\t.DATA\n");
      outs("$qfp_lit_sym$\n");
      outs("\t.ALIGN\t8\n");
      outs("\t.STRINGZ \"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n");
      outs("\t.STRINGZ \"?\\xFF\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n");
   }
}


/*
    DO A QUAD FLOAT OPERATION
*/
void quad_op
    PROTO_N ( ( e, sp, dest ) )
    PROTO_T ( exp e X space sp X where dest )
{
   char *s=0,*stub=0;
   bool quad_ret = 1 ;

   switch ( name(e) )
   {
      case test_tag:
      {
         /* Quad comparisons */
         exp l,r;
         int tn;
         quad_ret = 0 ;
         s = "_U_Qfcmp";
         stub = "ARGW0=GR,ARGW1=GR,ARGW2=GR";
         long_double_lib[0].called=1;
         sp = guardreg(ARG2,sp);
         tn = (int)test_number(e);
         if ( tn < 1 || tn > 6 )
         {
            fail ( "Illegal floating-point test" ) ;
         }
         ir_ins(i_ldi, fs_, empty_ltrl, tn==1 ? 17 : tn==2 ? 21 : tn==3 ? 9 : tn==4 ? 13 : tn==5 ? 4 : 25,               ARG2);
         if ( IsRev(e) )
         {
            r = son(e);
            l = bro(r);
         }
         else
         {
            l = son(e);
            r = bro(l);
         }
         quad_addr(l,ARG0,sp) ;
         sp = guardreg(ARG0,sp) ;
         quad_addr(r,ARG1,sp) ;
         break;
      }
      case fneg_tag : 
      {
         baseoff b;
         b.base=0; b.offset=0;
         s = "_U_Qfsub" ;
         long_double_lib[2].called=1;
         set_ins("$qfp_lit_sym$",b,ARG0);
         sp = guardreg(ARG0,sp);
         quad_addr(son(e),ARG1,sp) ;
         sp = guardreg(ARG1,sp);
         stub = "ARGW0=GR,ARGW1=GR";
         long_double_0 = 1;
         break ;
      }
      case fabs_tag :
      {
         s = "_U_Qfabs" ;
         long_double_lib[11].called=1;
         stub = "ARGW0=GR";
         quad_addr(son(e),ARG0,sp) ;
         break ;
      }
      case chfl_tag :
      {
         ans aa ;
         where w ;
         freg frg ;
         exp l;
         if ( name(sh(e)) == doublehd )
         {
            baseoff b;
            b.base=SP;
            l = son(e);
            if ( name(sh(l)) == doublehd )
               return;
            else 
            if  (name(sh(l))==realhd)
            {
               s = "_U_Qfcnvff_dbl_to_quad" ;
               long_double_lib[5].called=1;
               frg.dble=1;
               frg.fr=5;
               stub = "ARGW0=FR,ARGW1=FU";
            }
            else
            {
               s = "_U_Qfcnvff_sgl_to_quad" ;
               long_double_lib[6].called=1;
               frg.dble=0;
               frg.fr=4;
               stub = "ARGW0=FR";
            }
            setfregalt ( aa, frg ) ;
            w.answhere = aa ;
            w.ashwhere = ashof (sh(l)) ;
            code_here(l,sp,w);
            if (frg.dble)
            {
               b.offset=-40;
               stf_ins(i_fstd,(5*3)+1,b);
               ld_ins(i_ldw,1,b,ARG1);
               b.offset+=4;
               ld_ins(i_ldw,1,b,ARG0);
            }
            else
            {
               b.offset=-36;
               stf_ins(i_fstw,(4*3)+0,b);
               ld_ins(i_ldw,1,b,ARG0);
            }
         }
         else
         {
            if ( isdbl(sh(e)) )
            {
               s = "_U_Qfcnvff_quad_to_dbl";
               long_double_lib[9].called=1;
            }
            else
            {
               s = "_U_Qfcnvff_quad_to_sgl";
               long_double_lib[10].called=1;
            }
            stub = "ARGW0=GR";
            quad_ret = 0;
            quad_addr(son(e),ARG0,sp) ;
         }
         break ;
      }
      case float_tag :
      {
         exp l = son(e);
         reg_operand_here(l,sp,ARG0);
         sp = guardreg(ARG0,sp);
         if ( name(sh(l))==ulonghd )
         {
            rr_ins(i_copy,0,ARG1);
            long_double_lib[7].called=1;
            s = "_U_Qfcnvxf_dbl_to_quad" ;
            stub = "ARGW0=GR,ARGW1=GR";
         }
         else
         {
            s = "_U_Qfcnvxf_sgl_to_quad" ;
            long_double_lib[8].called=1;
            stub = "ARGW0=GR";
         }
         break ;
      }
      case round_tag :
      {
         if ( round_number(e)==3 && errhandle(e)<2 )
         {
            s = "_U_Qfcnvfxt_quad_to_sgl";
            long_double_lib[12].called=1;
         }
         else
         {
            s = "_U_Qfcnvff_quad_to_dbl";
            long_double_lib[9].called=1;
         }
         stub = "ARGW0=GR";
         quad_ret = 0;
         quad_addr(son(e),ARG0,sp) ;
         break;
      }
#if 0
      /* Binary operations */
      {
         stub = "ARGW0=GR,ARGW1=GR";
         break ;
      }
#endif
      case fplus_tag :
      case fminus_tag : 
      case fmult_tag :
      case fdiv_tag :
      {
         exp l,r;
         if ( name(e) == fplus_tag )
         {
            s = "_U_Qfadd" ;
            long_double_lib[1].called=1;
         }
         else
         if ( name(e) == fminus_tag )
         {
           s = "_U_Qfsub" ; 
           long_double_lib[2].called=1;
         }
         else
         if ( name(e) == fmult_tag )
         {
            s = "_U_Qfmpy" ; 
            long_double_lib[3].called=1;
         }
         else
         {
            s = "_U_Qfdiv" ;
            long_double_lib[4].called=1;
         }
         stub = "ARGW0=GR,ARGW1=GR";
         if ( IsRev(e) )
         {
            r = son(e);
            l = bro(r);
         }
         else
         {
            l = son(e);
            r = bro(l);
         }
         quad_addr(l,ARG0,sp) ;
         sp = guardreg(ARG0,sp) ;
         quad_addr(r,ARG1,sp) ;
         break ;
      }
      default :
        fail ( "Illegal floating-point operation" ) ;
   }
   if (quad_ret)
   {
      instore is ;
      is = insalt(dest.answhere);
      if (discrim(dest.answhere)!=notinreg)
          failer("Illegal expression in quad_op");
      if (is.adval)
      {
         if (IS_FIXREG(is.b.base))
         {
            if (is.b.offset==0)
               rr_ins(i_copy,is.b.base,RET0) ;
            else
               ld_ins(i_lo,1,is.b,RET0) ;
         } 
         else
            set_ins("",is.b,RET0) ;
      }
      else
         ld_ins(i_lw,1,is.b,RET0) ;
   }
   /* ..and make call */
   call_ins(cmplt_,s,RP,stub) ;
#if 1
   if (!optop(e) && name(e)!=test_tag)
   {
      int trap = trap_label(e);
      baseoff b;
      int end;
      if (quad_ret)
      {
         instore is ;
         end=new_label();
         is = insalt(dest.answhere);
         if (discrim(dest.answhere)!=notinreg)
            failer("Illegal expression in quad_op");
         if (is.adval)
         {
            if (IS_FIXREG(is.b.base))
            {
               if (is.b.offset==0)
                  rr_ins(i_copy,is.b.base,RET0) ;
               else
                  ld_ins(i_lo,1,is.b,RET0) ;
            } 
            else
               set_ins("",is.b,RET0) ;
         }
         else
            ld_ins(i_lw,1,is.b,RET0) ;
         b.base =  RET0; b.offset = 4;
         ld_ins(i_lw,1,b,T3);
         cj_ins( c_neq, 0, T3, end ) ;         
         b.offset+=4;
         ld_ins(i_lw,1,b,T3);
         cj_ins( c_neq, 0, T3, end ) ;         
         b.offset+=4;
         ld_ins(i_lw,1,b,T3);
         cj_ins( c_neq, 0, T3, end ) ;         
         b.offset=0;
         ld_ins(i_lw,1,b,T3);
         imm_to_r(2147418112,T4);
         cj_ins( c_eq, T4, T3, trap ) ;         
         imm_to_r(-65536,T4);
         cj_ins( c_eq, T4, T3, trap ) ;         
         outlab("L$$",end);
      }
      else
      if ( name(e) == chfl_tag )
      {
         if ( isdbl(sh(e)) )
         {
            baseoff b;
            b = mem_temp(0);
            end = new_label();
            stf_ins(i_fstd,3*4+1,b);
            b.offset+=4;
            ld_ins(i_lw,1,b,T3);
            cj_ins( c_neq, 0, T3, end ) ;         
            b.offset-=4;
            ld_ins(i_lw,1,b,T3);
            imm_to_r(2146435072,T4);
            cj_ins( c_eq, T4, T3, trap ) ;         
            imm_to_r(-1048576,T4);
            cj_ins( c_eq, T4, T3, trap ) ;         
            outlab("L$$",end);
         }
         else
         {
            baseoff b;
            b = mem_temp(0);
            stf_ins(i_fstw,3*4,b);
            ld_ins(i_lw,1,b,T3);
            imm_to_r(2139095040,T4);
            cj_ins( c_eq, T4, T3, trap ) ;         
            imm_to_r(-8388608,T4);
            cj_ins( c_eq, T4, T3, trap ) ;         
         }
      }
   }
#endif
   clear_t_regs() ;
   return ;
}

#endif


int fop
    PROTO_N ( (e, sp, dest, ins) )
    PROTO_T ( exp e X space sp X where dest X ins_p ins )
{
   /* Evaluate floating dyadic operation e using ins into dest. If
      !optop(e), then we have two fixed point registers at our disposal */
   exp l = son(e);
   exp r = bro(l);
   int a1,a2,dble;
   space nsp;
   freg fr;
   ans aa;
   baseoff b;

#if use_long_double
   if (name(sh(e))==doublehd)
   {
      /* i.e. quads */
      quad_op( e, sp, dest );
      return (NOREG) ;
   }
#endif


   dble=( name(sh(e))==realhd ? 1 : 0 );
   if (IsRev(e))
   {
      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));
   }
   if ( (discrim(dest.answhere)) == infreg )
      fr = fregalt(dest.answhere);
   else
   {
      fr.fr = getfreg(nsp.flt);
      fr.dble = (dest.ashwhere.ashsize == 64) ? 1 : 0;
      setfregalt(aa, fr);
   }
   if (!optop(e))
   {
      b = zero_exception_register(nsp);
   }
   if (dble)
      rrrf_ins(ins,f_dbl,(3*a1)+1,(3*a2)+1,(3*fr.fr)+1);
   else
      rrrf_ins(ins,f_sgl,3*a1,3*a2,3*fr.fr);
   if (!optop(e))
   {
      trap_handler(b,trap_label(e),EXCEPTION_CODE);
   }
   if ( (discrim(dest.answhere)) != infreg )
      move(aa, dest, sp.fixed, 1);
   return ( dble ? -(fr.fr + 32) : (fr.fr + 32) );
}