Subversion Repositories tendra.SVN

Rev

Rev 2 | 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: inst_fmt.c,v $
 * Revision 1.1.1.1  1998/01/17  15:56:02  release
 * First version to be checked into rolling release.
 *
 * Revision 1.8  1997/01/29  10:19:10  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.7  1996/12/09  12:52:43  wfs
 *    Prevented the superfluous "loading" to GR0.
 *
 * Revision 1.6  1996/11/20  17:26:47  wfs
 *    Fixed bug in makecode.c's case_tag involving unsigned control variable.
 *
 * Revision 1.5  1996/11/14  15:22:17  wfs
 *    Fixed a bug in regexps.c which was common to most of the installers and
 * has only just come to light due to PWE's work on powertrans. (There was
 * previously only a patch.) Cosmetic changes to other files.
 *
 * Revision 1.4  1996/11/07  14:48:16  wfs
 * A bug fix to "round_tag" - was clearing wrong freg. Cosmetic changes to
 * "inst_fmt.c".
 *
 * Revision 1.3  1996/08/06  10:50:09  wfs
 * bug fixes to pic code, enum diagnostics enabled.
 *
 * Revision 1.2  1995/12/18  13:11:38  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.5  1995/10/30  14:26:53  wfs
 * Refined the limit on the number of exp's which determines the type of
 * branches used.
 *
 * Revision 5.4  1995/10/20  13:57:25  wfs
 * Changes for gcc compilation.
 *
 * Revision 5.3  1995/10/12  17:20:44  wfs
 * *** empty log message ***
 *
 * Revision 5.2  1995/10/09  12:57:46  wfs
 * Cosmetic changes.
 *
 * Revision 5.1  1995/09/15  12:34:24  wfs
 * Made what, I hope, is a temporary fix to "set_ins()" + changes to
 * stop gcc compiler from complaining.
 *
 * Revision 5.0  1995/08/25  13:42:58  wfs
 * Preperation for August 25 Glue release
 *
 * Revision 3.3  1995/08/25  09:42:29  wfs
 * internal register synonyms changed. much superfluous code was
 * removed after changes were made to register allocation
 *
 * Revision 3.3  1995/08/25  09:42:29  wfs
 * internal register synonyms changed. much superfluous code was
 * removed after changes were made to register allocation
 *
 * Revision 3.1  95/04/10  16:26:55  16:26:55  wfs (William Simmonds)
 * Apr95 tape version.
 *
 * Revision 3.0  95/03/30  11:17:29  11:17:29  wfs (William Simmonds)
 * Mar95 tape version with CRCR95_178 bug fix.
 *
 * Revision 2.0  95/03/15  15:27:40  15:27:40  wfs (William Simmonds)
 * spec 3.1 changes implemented, tests outstanding.
 *
 * Revision 1.2  95/01/17  17:23:19  17:23:19  wfs (William Simmonds)
 * Changes to outp made whilst debugging bootstrap
 *
 * Revision 1.1  95/01/11  13:09:32  13:09:32  wfs (William Simmonds)
 * Initial revision
 *
*/


#define HPPATRANS_CODE
/******************************************************************
                inst_formats.c

        Procs for outputting various HPPA RISC instruction formats to the
external file - outf. Each procedure produces assembler code for a family
of HPPA RISC operations, the actual member is passed as the string
understood by the assembler.
******************************************************************/

#include "config.h"
#include "myassert.h"
#include "xalloc.h"
#include "addrtypes.h"
#include "regexps.h"
#include "regmacs.h"
#include "hppains.h"
#include "maxminmacs.h"
#include "comment.h"
#include "translat.h"
#include "inst_fmt.h"
#include "out.h"
#include "misc.h"
#include "frames.h"
#include "new_tags.h"
#include "tags.h"
#include "expmacs.h"
#include "flags.h"
#include "eval.h"

#define name( x ) ( ( x )->namef )
#define sh( x ) ( ( x )->shf )
#define son( x ) ( ( x )->sonf.e )

#define GAP "                                                                "
#define prokhd 24

#define plabel( X ) ( (X)<0 ?\
    ( son(main_globals[((-X))-1]->dec_u.dec_val.dec_exp)==nilexp ?\
    name(sh(main_globals[((-X))-1]->dec_u.dec_val.dec_exp))==prokhd :\
    ( IS_A_PROC(son(main_globals[((-X))-1]->dec_u.dec_val.dec_exp)) ) ) : 0 )

#define globaldata(X) ( (X)<0 ?\
    ( (name(sh(main_globals[((-X))-1]->dec_u.dec_val.dec_exp))==proc_tag ||\
    name(sh(main_globals[((-X))-1]->dec_u.dec_val.dec_exp))==general_proc_tag)\
    ? 0 : 1 ) : 0 )


extern int firstlab, labno;
extern int nexps;
char last_ins[96];

int zops[]={0,0,0,0};
int last_line;
char last_symb[128];
int last_o;

static CONST char reg_name_tab[32][6] =
{ "%r0"  ,
  "%r1"  ,
  "%r2"  ,
  "%r19" ,
  "%r20" ,
  "%r21" ,
  "%r22" ,
  "%r26" ,
  "%r25" ,
  "%r24" ,
  "%r23" ,
  "%dp"  , /* %dp=%r27 */
  "%r28" ,
  "%r29" ,
  "%sp"  , /* %sp=%r30 */
  "%r31" ,
  "%r3"  ,
  "%r4"  ,
  "%r5"  ,
  "%r6"  ,
  "%r7"  ,
  "%r8"  ,
  "%r9"  ,
  "%r10" ,
  "%r11" ,
  "%r12" ,
  "%r13" ,
  "%r14" ,
  "%r15" ,
  "%r16" ,
  "%r17" ,
  "%r18"    };

static CONST char float_reg_name_tab[32][3][7] =
{
  { "%fr0L", "%fr0", "%fr0R" },
  { "%fr1L", "%fr1", "%fr1R" },
  { "%fr2L", "%fr2", "%fr2R" },
  { "%fr3L", "%fr3", "%fr3R" },
  { "%fr4L", "%fr4", "%fr4R" },
  { "%fr5L", "%fr5", "%fr5R" },
  { "%fr6L", "%fr6", "%fr6R" },
  { "%fr7L", "%fr7", "%fr7R" },
  { "%fr8L", "%fr8", "%fr8R" },
  { "%fr9L", "%fr9", "%fr9R" },
  { "%fr10L", "%fr10", "%fr10R" },
  { "%fr11L", "%fr11", "%fr11R" },
  { "%fr12L", "%fr12", "%fr12R" },
  { "%fr13L", "%fr13", "%fr13R" },
  { "%fr14L", "%fr14", "%fr14R" },
  { "%fr15L", "%fr15", "%fr15R" },
  { "%fr16L", "%fr16", "%fr16R" },
  { "%fr17L", "%fr17", "%fr17R" },
  { "%fr18L", "%fr18", "%fr18R" },
  { "%fr19L", "%fr19", "%fr19R" },
  { "%fr20L", "%fr20", "%fr20R" },
  { "%fr21L", "%fr21", "%fr21R" },
  { "%fr22L", "%fr22", "%fr22R" },
  { "%fr23L", "%fr23", "%fr23R" },
  { "%fr24L", "%fr24", "%fr24R" },
  { "%fr25L", "%fr25", "%fr25R" },
  { "%fr26L", "%fr26", "%fr26R" },
  { "%fr27L", "%fr27", "%fr27R" },
  { "%fr28L", "%fr28", "%fr28R" },
  { "%fr29L", "%fr29", "%fr29R" },
  { "%fr30L", "%fr30", "%fr30R" },
  { "%fr31L", "%fr31", "%fr31R" }
};

static CONST char space_reg_name_tab[8][5] =
{ "%sr0" ,
  "%sr1" ,
  "%sr2" ,
  "%sr3" ,
  "%sr4" ,
  "%sr5" ,
  "%sr6" ,
  "%sr7"    };


#define RN(reg) reg_name( reg )
#define FN(reg) float_reg_name(reg)
#define SN(reg) space_reg_name_tab[ reg ]

#define NA -1

ins_p NOCOND=0;


CONST char *reg_name
    PROTO_N ( (r) )
    PROTO_T ( int r )
{
  assert(IS_FIXREG(r));
  return reg_name_tab[r];
}

static CONST char *float_reg_name
    PROTO_N ( (reg) )
    PROTO_T ( int reg )
{
   return float_reg_name_tab[0][0]+(7*reg);
}


int outp
    PROTO_N ( (ins,cc,ops,lab) )
    PROTO_T ( ins_p ins X ins_p cc X int *ops X int lab )
{
   if (!OPTIM)
   {
      return 1;
   }
   if ( line>(lines-16) )
   {
      /* Increment number of assembler instructions allowed for by INCR */
      int i;
      pIn *temp=pCode;
      pCode = (pIn*) malloc( (lines+INCR)*sizeof(pIn) );
      for(i=0;i<lines;i++)
         pCode[i]=temp[i];
      free(temp);
      lines+=INCR;
   }
   if (ins==i_add && line>0)
   {
      pIn lst=pCode[line-1];
      ins_p lI=lst->ins;
      if (lst->op[1]==0 && (lI==i_sh1add || lI==i_sh2add || lI==i_sh3add))
      {
         int r=lst->op[2];
         int r0=ops[0];
         int r1=ops[1];
         if ( r==ops[2] && (r==r0 || r==r1) )
         {
            if (r==r1)
               r1=r0;
            r0=lst->op[0];
            SET_FILE_POSN(outf,(lst->fpos));
            fprintf(outf,"\t%s\t%s,%s,%s\n",lI,RN(r0),RN(r1),RN(r));
            lst->op[1]=r1;
            line--;
            return 0;
         }
      }
   }
   else if (ins==i_ub && line>0)
   {
      if (pCode[line-1]->ins==i_ub)
      {
         if (line==1)
            return 0;
         else if (pCode[line-2]->ins!=i_ftest && pCode[line-2]->cc==c_)
            return 0;
      }
   }
   else if (ins==i_lab && line>0)
   {
      int l=line-1;
      pIn j=pCode[l];
      while ( (j->ins==i_ub || j->ins==i_cj || j->ins==i_cij || j->ins==i_bb)                 && j->lab==lab && l>=0 )
      {
         SET_FILE_POSN(outf,(j->fpos));
         free(j);
         l--;
         if (l>=0)
         {
            j=pCode[l];
            if (j->ins==i_ftest)
            {
              free(j);
              j=pCode[l-1];
              SET_FILE_POSN(outf,(j->fpos));
              free(j);
              l-=2;
              if (l>=0)
                 j=pCode[l];
            }
         }
      }
      line=l+1;
      if ( line>1 && j->ins==i_lab )
      {
         ins_p jIns;
         j=pCode[line-2];
         jIns=j->ins;
         if ( (jIns==i_cj || jIns==i_cij || jIns==i_ub) && j->lab==lab )
         {
            SET_FILE_POSN(outf,(j->fpos));
            fprintf(outf,"L$$%d\n",pCode[l]->lab);
            labIntro[pCode[l]->lab-firstlab]=line-2;
            pCode[l-1]=pCode[l];
            free(j);
            line--;
         }
      }
      else
      if ( line>1 && j->ins!=i_ub && j->ins!=i_cj && j->ins!=i_cij
           && j->ins!=i_bb && j->ins!=i_lab )
      {
         ins_p jIns;
         j=pCode[line-2];
         jIns=j->ins;
         if ( (jIns==i_cj || jIns==i_cij) && j->lab==lab )
         {
             FILE_POSN pos;
             char s[64];
             int a,b,i;
             a=j->op[0];
             b=j->op[1];
             GET_FILE_POSN(outf,pos);
             SET_FILE_POSN(outf,(j->fpos));
             if (jIns==i_cj)
             {
                IGNORE sprintf(s,"\tcomclr%s\t%s,%s,0",j->cc,RN(a),RN(b));
                i=(int)strlen(s);
                j->ins=i_comclr;
             }
             else
             {
                IGNORE sprintf(s,"\tcomiclr%s\t%d,%s,0",j->cc,a,RN(b));
                i=(int)strlen(s);
                j->ins=i_comiclr;
             }
             for(;i<63;i++)
                s[i]=' ';
             s[63]=0;
             fprintf(outf,"%s\n",s);
             j->op[2]=0;
             j->lab=NA;
             SET_FILE_POSN(outf,pos);
         }
      }
      else
      if (j->ins==i_ub && line>1 && ((pCode[line-2]->ins==i_cij ||
                                        pCode[line-2]->ins==i_cj) &&
                                       pCode[line-2]->lab==lab))
      {
         pCode[line-2]->lab=j->lab;
         pCode[line-2]->cc=opp(pCode[line-2]->cc);
         SET_FILE_POSN(outf,(j->fpos));
         free(j);
         line--;
      }
   }

   {
      pIn pI;
      pI = (pIn) malloc( sizeof(psuedoIn) );
      GET_FILE_POSN(outf,(pI->fpos));
      pI->ins=ins;
      pI->cc=cc;
      pI->lab=lab;
      pI->op[0]=ops[0];
      pI->op[1]=ops[1];
      pI->op[2]=ops[2];
      pI->op[3]=ops[3];
      pCode[line]=pI;
      if (ins==i_lab)
      {
         if ( (lab-firstlab)>(nLabels-16) )
         {
            /* Increment number of labels allowed for by INCR */
            int i;
            int *temp=labIntro;
            labIntro = (int*) malloc( (nLabels+INCR)*sizeof(int) );
            for(i=0;i<nLabels;i++)
               labIntro[i]=temp[i];
            for(i=nLabels;i<nLabels+INCR;i++)
               labIntro[i]=-1;
            free(temp);
            nLabels+=INCR;
         }
         labIntro[lab-firstlab]=line;
      }
      line++;
      fflush(outf);
      return 1;
   }
}


char *ext_name
    PROTO_N ( (id) )
    PROTO_T ( long id )
{
  static char *sp;
  static int sizeof_space = 0;

  if (sizeof_space == 0)
  {
    /* init sp */
    sizeof_space = 64;          /* generous first guess */
    sp = (char *) malloc(sizeof_space);
  }

  if (id < 0)
  {
    char *ext = main_globals[(-id) - 1]->dec_u.dec_val.dec_id;

    int len_needed = 1 + strlen(ext) + 1;       /* _NAME\0 */

    if (len_needed > sizeof_space)
    {
      sizeof_space = len_needed;
      sp = (char *) xrealloc(sp, sizeof_space);
    }

    sprintf(sp, "%s", ext);

    return sp;
  }
  else
  {
    assert(id > R_LAST);        /* not a confused register */

    sprintf(sp, "LD$%ld", id);
    return sp;
  }
}


/*
*   3 register instructions
 */
void rrr_ins
    PROTO_N ( (ins,cond,a,b,c) )
    PROTO_T ( ins_p ins X ins_p cond X int a X int b X int c )
{
  int p;
  int ops[4];
  clear_reg(c);

   if (ins==i_add && a==b)
   {
      ops[0]=a;
      ops[1]=0;
      ops[2]=c;
      p=outp(i_sh1add,cond,ops,NA);
      if (p)
         fprintf(outf,"\tsh1add%s\t%s,0,%s\n",cond,RN(a),RN(c));
   }
   else
   {
      ops[0]=a;
      ops[1]=b;
      ops[2]=c;
      p=outp(ins,cond,ops,NA);
      if (p)
         fprintf(outf,"\t%s%s\t%s,%s,%s\n",ins,cond,RN(a),RN(b),RN(c));
   }
}



/*
*   1 register pseudo instructions
 */
void r_ins
    PROTO_N ( (ins,a) )
    PROTO_T ( ins_p ins X int a )
{
   outp(ins,NOCOND,zops,NA);
   fprintf(outf,"\t%s\t%s\n",ins,RN(a));
}



 /*
*    non-store register, immediate, destination register instructions
 */
void rir_ins
    PROTO_N ( (ins,cc,a,imm,d) )
    PROTO_T ( ins_p ins X ins_p cc X int a X long imm X int d )
{
   clear_reg(d);
   outp(ins,cc,zops,NA);
   fprintf(outf,"\t%s%s\t%s,%ld,%s\n",ins,cc,RN(a),imm,RN(d));
}




/*
*   register to register pseudo instructions
 */
void rr_ins
    PROTO_N ( (ins,s,d) )
    PROTO_T ( ins_p ins X int s X int d )
{
  clear_reg(d);
  outp(ins,NOCOND,zops,NA);
  fprintf(outf,"\t%s\t%s,%s\n",ins,RN(s),RN(d));
}


/*
*   non-load immediate, register, register instructions
 */
void irr_ins
    PROTO_N ( (ins,cc,fs,i,a,d) )
    PROTO_T ( ins_p ins X ins_p cc X ins_p fs X long i X int a X int d )
{
   clear_reg(d);
   outp(ins,cc,zops,NA);
   fprintf(outf,"\t%s%s\t%s%ld,%s,%s\n",ins,cc,fs,i,RN(a),RN(d));
}


/*
*   immediate, immediate, register instructions
 */
void iir_ins
    PROTO_N ( (ins,cc,a,b,d) )
    PROTO_T ( ins_p ins X ins_p cc X int a X int b X int d )
{
   clear_reg(d);
   outp(ins,cc,zops,NA);
   fprintf(outf,"\t%s%s\t%d,%d,%s\n",ins,cc,a,b,RN(d));
}


/*
*   immediate, immediate, immediate, register instructions
 */
void iiir_ins
    PROTO_N ( (ins,cond,a,b,c,d) )
    PROTO_T ( ins_p ins X ins_p cond X int a X int b X int c X int d )
{
   clear_reg(d);
   outp(ins,cond,zops,NA);
   fprintf(outf,"\t%s%s\t%d,%d,%d,%s\n",ins,cond,a,b,c,RN(d));
}


/*
*   immediate (i.e. field selector literal+long) to register instructions
 */
void ir_ins
    PROTO_N ( (ins,fs,ltrl,l,d) )
    PROTO_T ( ins_p ins X ins_p fs X CONST char *ltrl X long l X int d )
{
    char I[128];

    /* build immediate, i.e. field selector applied to literal+long */
    if (!(*ltrl))
       sprintf(I,"%s%ld",fs,l);
    else
    if (l>0)
       sprintf(I,"%s%s+%ld",fs,ltrl,l);
    else
    if (l<0)
       sprintf(I,"%s%s-%ld",fs,ltrl,-l);
    else
       sprintf(I,"%s%s",fs,ltrl);

    clear_reg(d);

    outp(ins,NOCOND,zops,NA);
    fprintf(outf,"\t%s\t%s,%s\n",ins,I,RN(d));

}


/*
*   register, register, immediate, register instructions
 */
void rrir_ins
    PROTO_N ( (ins,cc,a,b,i,d) )
    PROTO_T ( ins_p ins X ins_p cc X int a X int b X long i X int d )
{
   int ops[4];
   if (ins==i_shd && b==0 && i>28)
   {
      ins_p ins=(i==29 ? i_sh3add : (i==30 ? i_sh2add : i_sh1add));
      rrr_ins(ins,c_,a,0,d);
      return;
   }
   clear_reg(d);
   ops[0]=a;
   ops[1]=b;
   ops[2]=i;
   ops[3]=d;
   outp(ins,cc,ops,NA);
   fprintf(outf,"\t%s%s\t%s,%s,%ld,%s\n",ins,cc,RN(a),RN(b),i,RN(d));
}



/*
*   zeroadic pseudo instruction, not a branch
 */
void z_ins
    PROTO_N ( (ins) )
    PROTO_T ( ins_p ins )
{
   int b;
   b=outp(ins,NOCOND,zops,NA);
   if (b)
      fprintf(outf, "\t%s\n", ins);
}



/*
*   register, immediate, immediate, register instructions
 */
void riir_ins
    PROTO_N ( (ins,cc,s,a,b,d) )
    PROTO_T ( ins_p ins X ins_p cc X int s X long a X long b X int d )
{
   clear_reg(d);
   outp(ins,cc,zops,NA);
   fprintf(outf,"\t%s%s\t%s,%ld,%ld,%s\n",ins,cc,RN(s),a,b,RN(d));
}


/*
*   fixed point load instructions
 */

void ld_ir_ins
    PROTO_N ( (ins,cmplt,fs,ltrl,l,b,d) )
    PROTO_T ( ins_p ins X ins_p cmplt X ins_p fs X CONST char *ltrl X long l X int b X int d )
{
    char O[128];
    if (d==GR0)
       return;
    /* build offset, i.e. field selector applied to literal+long */
    if (!(*ltrl))
       sprintf(O,"%s%ld",fs,l);
    else
    if (l>0)
       sprintf(O,"%s%s+%ld",fs,ltrl,l);
    else
    if (l<0)
       sprintf(O,"%s%s-%ld",fs,ltrl,-l);
    else
       sprintf(O,"%s%s",fs,ltrl);
   clear_reg(d);
   outp(ins,NOCOND,zops,NA);
   fprintf(outf,"\t%s%s\t%s(%s),%s\n",ins,cmplt,O,RN(b),RN(d));
}


/*
*   loads a long, l, into a register, d, in at most two instructions
 */
void imm_to_r
    PROTO_N ( (l,d) )
    PROTO_T ( long l X int d )
{
   if (d==GR0)
      return;
   if (SIMM14(l))
      ir_ins(i_ldi,fs_,empty_ltrl,l,d);
   else
   {
      int p=0,n,j;
      while ( (l & (1<<p))==0 ) p++;
      n=p;
      while ( l & (1<<n) ) n++;
      j=n;
      while ( j<32 && (l & (1<<j))==0 ) j++;
      if (j==32)
         iiir_ins(i_zdepi,c_,-1,31-p,n-p,d);
      else
      {
         long sll;
         sll=l>>p;
         if (sll>0 && SIMM5(sll))
            iiir_ins(i_zdepi,c_,sll,31-p,5,d);
         else
         {
            ir_ins(i_ldil,fs_L,empty_ltrl,l,d);
            ld_ir_ins(i_ldo,cmplt_,fs_R,empty_ltrl,l,d,d);
         }
      }
   }
}


void set_ins
    PROTO_N ( (e, a, d) )
    PROTO_T ( char *e X baseoff a X int d )
{
   char *extname;
   long o;
   int p = 0;
   if (d==GR0)
      return;
   clear_reg(d);
   if (e[0])
      extname = e;
   else
      extname = ext_name(a.base);
   o = a.offset;

   if (a.base<0 )
   {
      p = plabel(a.base);
   }
   if (p)
   {
      if (PIC_code)
      {
         ir_ins(i_ldil,fs_LP,extname,o,d);
         ld_ir_ins(i_ldo,cmplt_,fs_RP,extname,o,d,d);
      }
      else
      {
         ir_ins(i_ldil,fs_LP,extname,o,d);
         ld_ir_ins(i_ldo,cmplt_,fs_RP,extname,o,d,d);
         riir_ins(i_extru,c_eq,d,31,1,GR1);
         ld_ir_ins(i_ldw,cmplt_,fs_,empty_ltrl,-4,GR27,GR1);
         rrr_ins(i_add,c_,GR1,d,d);
      }
   }
   else
   {
      if (PIC_code)
      {
         if (plusZ)
         {
            ir_ins(i_addil,fs_LT,extname,0,GR19);
            ld_ir_ins(i_ldw,cmplt_,fs_RT,extname,0,GR1,o==0 ? d : GR1);
         }
         else
         {
            ld_ir_ins(i_ldw,cmplt_,fs_T,extname,0,GR19,o==0 ? d : GR1);
         }
         if (o)
         {
            if (SIMM14(o))
               ld_ir_ins(i_ldo,cmplt_,fs_,empty_ltrl,o,GR1,d);
            else
            {
               ir_ins(i_addil,fs_L,empty_ltrl,o,GR1);
               ld_ir_ins(i_ldo,cmplt_,fs_R,empty_ltrl,o,GR1,d);
            }
         }
      }
      else
      {
         char s[128];
         sprintf(s,"%s-$global$",extname);
         ir_ins(i_addil,fs_LR,s,o,GR27);
         ld_ir_ins(i_ldo,cmplt_,fs_RR,s,o,GR1,d);
      }
   }
}


void ld_rr_ins
    PROTO_N ( (ins,cmplt,a,b,d) )
    PROTO_T ( ins_p ins X ins_p cmplt X int a X int b X int d )
{
   if (d==GR0)
      return;
   clear_reg(d);
   outp(ins,NOCOND,zops,NA);
   fprintf(outf,"\t%s%s\t%s(0,%s),%s\n",ins,cmplt,RN(a),RN(b),RN(d));
}

void ld_ins
    PROTO_N ( (ins,sgnd,a,d) )
    PROTO_T ( ins_p ins X int sgnd X baseoff a X int d )
{
   /* ins must be one of i_lb, i_lh, i_lo, i_lwm or i_lw */
   long o=a.offset;
   CONST char *i_ld;
   if (d==GR0)
      return;
   clear_reg(d);
   if (IS_FIXREG(a.base))
   {
      int b=a.base;
      i_ld=( ins==i_lb ? i_ldb : ( ins==i_lh ? i_ldh : ( ins==i_lo ? i_ldo : ( ins==i_lwm ? i_ldwm : i_ldw ) ) ) ) ;
      if (SIMM14(o))
         ld_ir_ins(i_ld,cmplt_,fs_,empty_ltrl,o,b,d);
      else
      {
         ir_ins(i_addil,fs_L,empty_ltrl,o,b);
         ld_ir_ins(i_ld,cmplt_,fs_R,empty_ltrl,o,GR1,d);
      }
   }
   else
   {
      char *symb = ext_name(a.base);
      i_ld=( ins==i_lb ? i_ldb : ( ins==i_lh ? i_ldh : ( ins==i_lo ? i_ldo :  i_ldw ) ) ) ;
      if (globaldata((long)a.base))
      {
         if (PIC_code)
         {
            if (plusZ)
            {
               ir_ins(i_addil,fs_LT,symb,0,GR19);
               ld_ir_ins(i_ldw,cmplt_,fs_RT,symb,0,GR1,GR1);
            }
            else
            {
               ld_ir_ins(i_ldw,cmplt_,fs_T,symb,0,GR19,GR1);
            }
            if (SIMM14(o))
               ld_ir_ins(i_ld,cmplt_,fs_,empty_ltrl,o,GR1,d);
            else
            {
               ir_ins(i_addil,fs_L,empty_ltrl,o,GR1);
               ld_ir_ins(i_ld,cmplt_,fs_R,empty_ltrl,o,GR1,d);
            }
         }
         else
         {
            char s[128];
            sprintf(s,"%s-$global$",symb);
            ir_ins(i_addil,fs_LR,s,o,GR27);
            ld_ir_ins(i_ld,cmplt_,fs_RR,s,o,GR1,d);
            last_line=line;
            strcpy(last_symb,symb);
            last_o=o;
         }
      }
      else
      {
         ir_ins(i_ldil,fs_L,symb,o,GR1);
         ld_ir_ins(i_ld,cmplt_,fs_R,symb,o,GR1,d);
      }
   }
   if (sgnd)
   {
      if (ins==i_lb)
         riir_ins(i_extrs,c_,d,31,8,d);
      else if (ins==i_lh) /* would have to be, but just checking */
         riir_ins(i_extrs,c_,d,31,16,d);
   }
}



/*
*   fixed point store instructions
 */

void st_ir_ins
    PROTO_N ( (ins,cmplt,s,fs,ltrl,l,b) )
    PROTO_T ( ins_p ins X ins_p cmplt X int s X ins_p fs X CONST char *ltrl X long l X int b )
{
    char O[128];

    /* build offset, i.e. field selector applied to literal+long */
    if (!(*ltrl))
       sprintf(O,"%s%ld",fs,l);
    else
    if (l>0)
       sprintf(O,"%s%s+%ld",fs,ltrl,l);
    else
    if (l<0)
       sprintf(O,"%s%s-%ld",fs,ltrl,-l);
    else
       sprintf(O,"%s%s",fs,ltrl);

    outp(ins,NOCOND,zops,NA);
    fprintf(outf,"\t%s%s\t%s,%s(%s)\n",ins,cmplt,RN(s),O,RN(b));
}

void st_ins
    PROTO_N ( (ins,s,a) )
    PROTO_T ( ins_p ins X int s X baseoff a )
{
   /* ins must be one of i_sb, i_sh or i_sw */
   long o=a.offset;
   CONST char *i_st;

   if (IS_FIXREG(a.base))
   {
      int b=a.base;
      i_st = ( ins == i_sb ? i_stb : ( ins == i_sh ? i_sth : i_stw ) );
      if (SIMM14(o))
         st_ir_ins(i_st,cmplt_,s,fs_,empty_ltrl,o,b);
      else
      {
         ir_ins(i_addil,fs_L,empty_ltrl,o,b);
         st_ir_ins(i_st,cmplt_,s,fs_R,empty_ltrl,o,GR1);
      }
   }
   else
   {
      char *symb = ext_name(a.base);
      i_st = ( ins == i_sb ? i_stb : ( ins == i_sh ? i_sth : i_stw ) );
      if (globaldata((long)a.base))
      {
         if (PIC_code)
         {
            if (plusZ)
            {
               ir_ins(i_addil,fs_LT,symb,0,GR19);
               ld_ir_ins(i_ldw,cmplt_,fs_RT,symb,0,GR1,GR1);
            }
            else
            {
               ld_ir_ins(i_ldw,cmplt_,fs_T,symb,0,GR19,GR1);
            }
            if (SIMM14(o))
               st_ir_ins(i_st,cmplt_,s,fs_,empty_ltrl,o,GR1);
            else
            {
               ir_ins(i_addil,fs_L,empty_ltrl,o,GR1);
               st_ir_ins(i_st,cmplt_,s,fs_R,empty_ltrl,o,GR1);
            }
         }
         else
         {
            char litrl[128];
            sprintf(litrl,"%s-$global$",symb);
            if (last_line==line-1 && pCode[line-1]->ins==i_ldo && !strcmp(symb,last_symb) && last_o==o)
            {
               st_ir_ins(i_st,cmplt_,s,fs_R,litrl,o,GR1);
            }
            else
            {
               ir_ins(i_addil,fs_L,litrl,o,GR27);
               st_ir_ins(i_st,cmplt_,s,fs_R,litrl,o,GR1);
            }
         }
      }
      else
      {
         ir_ins(i_ldil,fs_L,symb,o,1);
         st_ir_ins(i_st,cmplt_,s,fs_R,symb,o,GR1);
      }
   }
}


void ldsid_in
    PROTO_N ( ( s, b, t ) )
    PROTO_T ( int s X int b X int t )
{
   clear_reg(t);
   outp(i_ldsid,NOCOND,zops,NA);
   fprintf(outf,"\tldsid\t(%s,%s),%s\n",SN(s),RN(b),RN(t));
}


void mtsp_in
    PROTO_N ( ( r, sr ) )
    PROTO_T ( int r X int sr )
{
   outp(i_mtsp,NOCOND,zops,NA);
   fprintf(outf,"\tmtsp\t%s,%s\n",RN(r),SN(sr));
}


/******************************************************************************
Branch instructions. These have labels as destination.
******************************************************************************/

/*
*   Unconditional branch.
 */
void ub_ins
    PROTO_N ( ( cmplt, lab ) )
    PROTO_T ( CONST char *cmplt X int lab )
{
    if (OPTIM)
    {
       int b;
       b=outp(i_ub,NOCOND,zops,lab);
       if (b)
          fprintf(outf,"%s",GAP);
    }
    else
    {
       fprintf(outf,"\tb%s\tL$$%d\n",cmplt,lab);
       z_ins(i_nop);
    }
}


/*
*   Call instructions
 */
void bl_in
    PROTO_N ( (n,target,t) )
    PROTO_T ( ins_p n X char *target X int t )
{
   outp(i_bl,NOCOND,zops,NA);
   fprintf(outf,"\tbl%s\t%s,%s\n",n,target,(t==RP ? "%rp" : RN(t)) );
}

void ble_in
    PROTO_N ( (n,wd,sr,b) )
    PROTO_T ( ins_p n X char* wd X int sr X int b )
{
   outp(i_ble,NOCOND,zops,NA);
   fprintf(outf,"\tble%s\t%s(%s,%s)\n",n,wd,SN(sr),RN(b));
}

void call_ins
    PROTO_N ( (n,target,t,stub) )
    PROTO_T ( ins_p n X char *target X int t X char stub[128] )
{
   if (nexps<40872 /*62327*/)
   {
      out_directive(".CALL",stub);
      bl_in(n,target,t);
      z_ins(i_nop);
   }
   else
   {
      /*
       * Module so large that bl is not guarenteed to reach start of
       * subspace - must generate a long call sequence.
       */
      if (PIC_code)
      {
         int n = next_PIC_pcrel_lab();
         char s[16];
         bl_in(cmplt_,".+",RP);
         sprintf(s,"%s-$PIC_pcrel$%d",target,n);
         ir_ins(i_addil,fs_L,s,4,RP);
         sprintf(s,"%s-$PIC_pcrel$%d",target,n+1);
         ld_ir_ins(i_ldo,cmplt_,fs_R,s,8,GR1,GR1);
         outlab("$PIC_pcrel$",n);
         ldsid_in(SR0,GR1,GR31);
         outlab("$PIC_pcrel$",n+1);
         mtsp_in(GR31,SR0);
         out_directive(".CALL",stub);
         ble_in(cmplt_,"0",SR0,GR1);
         rr_ins(i_copy,GR31,t);
         next_PIC_pcrel_lab();
      }
      else
      {
         char wd[128];
         sprintf(wd,"%s%s",fs_R,target);
         ir_ins(i_ldil,fs_L,target,0,GR31);
         out_directive(".CALL",stub);
         ble_in(cmplt_,wd,SR4,GR31);
         rr_ins(i_copy,GR31,t);
      }
   }
   if (PIC_code)
      rr_ins(i_copy,GR5,GR19);
}


/*
*   jump/call to compiler generated external identifier, eg .mulI
 */
void extj_special_ins
    PROTO_N ( (nm,r,stub,import) )
    PROTO_T ( CONST char *nm X int r X char stub[128] X int import )
{
   if (import)
   {
      char s[128];
      sprintf(s,"%s,MILLICODE",nm);
      out_directive(".IMPORT",s);
   }
   out_directive(".CALL",stub);
   bl_in(cmplt_,(char*)nm,MRP);
   rr_ins(i_copy,MRP,r);
}


/*
*   jump/call to value of reg
 */
void extj_reg_ins
    PROTO_N ( (ins,reg) )
    PROTO_T ( ins_p ins X int reg )
{
   outp(i_bv,NOCOND,zops,NA);
   fprintf(outf,"\tbv\t%%r0(%s)\n",RN(reg));
}


/*
*   Conditional pseudo instructions.
 */

void addb_in
    PROTO_N ( (cond,l,d,lab) )
    PROTO_T ( char *cond X int l X int d X int lab )
{
   int ops[4];
   ops[0]=l;
   ops[1]=d;
   outp(i_addb,cond,ops,lab);
   fprintf(outf,"\taddb%s,N\t%s,%s,L$$%d\n",cond,RN(l),RN(d),lab);
   z_ins(i_nop);
}


void addib_in
    PROTO_N ( (cond,i,d,lab) )
    PROTO_T ( char *cond X int i X int d X int lab )
{
   int ops[4];
   ops[0]=i;
   ops[1]=d;
   outp(i_addib,cond,ops,lab);
   fprintf(outf,"\taddib%s,N\t%d,%s,L$$%d\n",cond,i,RN(d),lab);
   z_ins(i_nop);
}


/*
*   register comparisons
 */
void comb_ins
    PROTO_N ( (cond,l,r,lab) )
    PROTO_T ( CONST char *cond X int l X int r X int lab )
{
   int ops[4];
   ops[0]=l;
   ops[1]=r;
   outp(i_comb,cond,ops,lab);
   if (lab<0)
      fprintf(outf,"\tcomb%s\t%s,%s,.+%d\n",cond,RN(l),RN(r),-lab);
   else
      fprintf(outf,"\tcomb%s\t%s,%s,L$$%d\n",cond,RN(l),RN(r),lab);
}


/*
*   constant/register comparison
 */
void comib_ins
    PROTO_N ( (cond,l,r,lab) )
    PROTO_T ( ins_p cond X int l X int r X int lab )
{
   int ops[4];
   ops[0]=l;
   ops[1]=r;
   outp(i_comib,cond,ops,lab);
   if (lab<0)
      fprintf(outf,"\tcomib%s\t%d,%s,.+%d\n",cond,l,RN(r),-lab);
   else
      fprintf(outf,"\tcomib%s\t%d,%s,L$$%d\n",cond,l,RN(r),lab);
}


void cj_ins
    PROTO_N ( (cond,l,r,lab) )
    PROTO_T ( CONST char *cond X int l X int r X int lab )
{
   int ops[4];
   ops[0]=l;
   ops[1]=r;
   if (OPTIM)
   {
      outp(i_cj,cond,ops,lab);
      fprintf(outf,"%s",GAP);
   }
   else
   {
      rrr_ins(i_comclr,opp(cond),l,r,0);
      ub_ins(cmplt_,lab);
   }
}


/*
*   register, immediate comparison
 */
void cij_ins
    PROTO_N ( (cond,l,r,lab) )
    PROTO_T ( CONST char *cond X long l X int r X int lab )
{
   int ops[4];
   ops[0]=l;
   ops[1]=r;
   if (SIMM5(l))
   {
      if (OPTIM)
      {
         outp(i_cij,cond,ops,lab);
         fprintf(outf,"%s",GAP);
      }
      else
      {
         irr_ins(i_comiclr,opp(cond),fs_,l,r,0);
         ub_ins(cmplt_,lab);
      }
   }
   else
   {
      /* use temp reg for large constant */
      if (r == GR1)
         /* should not happen */
         fail("ins: temp reg in use when needed for large constant");
      else
      {
         imm_to_r(l,GR1);
         if (OPTIM)
         {
            cj_ins(cond,GR1,r,lab);
         }
         else
         {
            rrr_ins(i_comclr,opp(cond),GR1,r,0);
            ub_ins(cmplt_,lab);
         }
      }
   }
}


/*
*   Branch on bit.
 */
void bb_in
    PROTO_N ( (cond,r,b,lab) )
    PROTO_T ( ins_p cond X int r X int b X int lab )
{
   int ops[4];
   ops[0]=r;
   ops[1]=b;
   outp(i_bb,cond,ops,lab);
   fprintf(outf,"%s",GAP);
}


/*
*   Floating point instructions.
 */


void ldf_ir_ins
    PROTO_N ( (ins, o, b, d) )
    PROTO_T ( ins_p ins X int o X int b X int d )
{
   clear_freg(d);
   outp(ins,NOCOND,zops,NA);
   fprintf(outf,"\t%s\t%d(%s),%s\n",ins,o,RN(b),FN(d));
}


void ldf_rr_ins
    PROTO_N ( (ins, cmplt, a, b, d) )
    PROTO_T ( ins_p ins X ins_p cmplt X int a X int b X int d )
{
   clear_freg(d);
   outp(ins,NOCOND,zops,NA);
   fprintf(outf,"\t%s%s\t%s(%s),%s\n",ins,cmplt,RN(a),RN(b),FN(d));
}


void ldf_ins
    PROTO_N ( (ins, a, d) )
    PROTO_T ( ins_p ins X baseoff a X int d )
{
   /* ins must be either i_fldw or i_fldd */
   CONST char *i_fld = ( ins==i_fldw ? i_fldws : i_fldds );
   if (IS_FIXREG(a.base))
   {
      if (SIMM5(a.offset))
         ldf_ir_ins(i_fld, a.offset, a.base, d);
      else
      {
         /* offset too big, put in temp reg and use ldf_ir_ins */
         /* +++ arrange stack variable to minimise this */
         assert(a.base != GR1); /* otherwise we corrupt it */
         if (SIMM14(a.offset))
            ld_ir_ins(i_ldo,cmplt_,fs_,empty_ltrl,a.offset,a.base,GR1);
         else
         {
            ir_ins(i_addil,fs_L,empty_ltrl,a.offset,a.base);
            ld_ir_ins(i_ldo,cmplt_,fs_R,empty_ltrl,a.offset,GR1,GR1);
         }
         ldf_ir_ins(i_fld, 0, GR1, d);
      }
   }
   else
   {
      /* global */
      set_ins("",a,GR1);
      ldf_ir_ins(i_fld, 0, GR1, d);
   }
}


void stf_ir_ins
    PROTO_N ( (ins, s, o, b) )
    PROTO_T ( ins_p ins X int s X int o X int b )
{
   outp(ins,NOCOND,zops,NA);
   fprintf(outf,"\t%s\t%s,%d(%s)\n",ins,FN(s),o,RN(b));
}


void stf_rr_ins
    PROTO_N ( (ins, s, a, b) )
    PROTO_T ( ins_p ins X int s X int a X int b )
{
   outp(ins,NOCOND,zops,NA);
   fprintf(outf,"\t%s\t%s,%s(%s)\n",ins,FN(s),RN(a),RN(b));
}


void stf_ins
    PROTO_N ( (ins, s, a) )
    PROTO_T ( ins_p ins X int s X baseoff a )
{
   /* ins must be either i_fstw or i_fstd */


   CONST char *i_fst = ( ins==i_fstw ? i_fstws : i_fstds );
   if (IS_FIXREG(a.base))
   {
      if (SIMM5(a.offset))
         stf_ir_ins(i_fst, s, a.offset, a.base);
      else
      {
         /* offset too big, put in temp reg and use stf_ir_ins */
         /* +++ arrange stack variable to minimise this */
         assert(a.base != GR1); /* otherwise we corrupt it */
         if (SIMM14(a.offset))
            ld_ir_ins(i_ldo,cmplt_,fs_,empty_ltrl,a.offset,a.base,GR1);
         else
         {
            ir_ins(i_addil,fs_L,empty_ltrl,a.offset,a.base);
            ld_ir_ins(i_ldo,cmplt_,fs_R,empty_ltrl,a.offset,GR1,GR1);
         }
         stf_ir_ins(i_fst, s, 0, GR1);
      }
   }
   else
   {
      /* global */
      set_ins("",a,GR1);
      stf_ir_ins(i_fst, s, 0, GR1);
   }
}


void cmp_rrf_ins
    PROTO_N ( (ins, fmt, cond, a, b) )
    PROTO_T ( ins_p ins X ins_p fmt X ins_p cond X int a X int b )
{
   outp(ins,NOCOND,zops,NA);
   fprintf(outf,"\t%s%s%s\t%s,%s\n",ins,fmt,cond,FN(a),FN(b));
}

void rrf_ins
    PROTO_N ( (ins, from_fmt, to_fmt, a, b) )
    PROTO_T ( ins_p ins X ins_p from_fmt X ins_p to_fmt X int a X int b )
{
   outp(ins,NOCOND,zops,NA);
   fprintf(outf,"\t%s%s%s\t%s,%s\n",ins,from_fmt,to_fmt,FN(a),FN(b));
}


void rrrf_ins
    PROTO_N ( (ins, fmt, a, b, dest) )
    PROTO_T ( ins_p ins X ins_p fmt X int a X int b X int dest )
{
   clear_freg(dest);
   fprintf(outf, "\t%s%s\t%s,%s,%s\n",ins,fmt,FN(a),FN(b),FN(dest));
   outp(ins,NOCOND,zops,NA);
}




/*
*   Directives
 */
void out_directive
    PROTO_N ( (d,params) )
    PROTO_T ( CONST char *d X CONST char *params )
{
  /* directive, parameters */
   outp(directive,NOCOND,zops,NA);
   fprintf(outf,"\t%s\t%s\n",d,params);
}



/*
*   Print a label
 */
void outlab
    PROTO_N ( ( prefix, n ) )
    PROTO_T ( char *prefix X int n )
{
    if (prefix[0]=='L' && prefix[1]=='$')
       outp(i_lab,NOCOND,zops,n);
    else
       outp(i_,NOCOND,zops,NA);
    fprintf(outf,"%s%d\n",prefix,n);
}