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


/* 80x86/instr.c */

/**********************************************************************
$Author: pwe $
$Date: 1998/03/15 16:00:19 $
$Revision: 1.2 $
$Log: instr.c,v $
 * Revision 1.2  1998/03/15  16:00:19  pwe
 * regtrack dwarf dagnostics added
 *
 * Revision 1.1.1.1  1998/01/17  15:55:51  release
 * First version to be checked into rolling release.
 *
 * Revision 1.28  1997/04/17  11:55:47  pwe
 * dwarf2 improvements
 *
 * Revision 1.27  1997/03/24  11:15:13  pwe
 * dwarf2 option/default
 *
 * Revision 1.26  1997/03/20  16:23:41  pwe
 * dwarf2
 *
 * Revision 1.25  1997/02/18  11:42:52  pwe
 * NEWDIAGS for debugging optimised code
 *
 * Revision 1.24  1996/12/13  14:39:27  pwe
 * prep NEWDIAGS
 *
 * Revision 1.23  1996/07/09  09:43:39  pwe
 * caller env_offset if callees present, and tidy
 *
 * Revision 1.22  1996/05/20  14:30:11  pwe
 * improved 64-bit handling
 *
 * Revision 1.21  1996/05/13  12:51:55  pwe
 * undo premature commit
 *
 * Revision 1.19  1996/01/17  11:24:31  pwe
 * resurrect performance
 *
 * Revision 1.18  1995/11/01  18:41:14  pwe
 * PIC tail_call and exception handling
 *
 * Revision 1.17  1995/10/24  17:02:43  pwe
 * local calls to avoid PLT, Solaris constraint
 *
 * Revision 1.16  1995/10/16  14:55:19  pwe
 * stack change v fpucon
 *
 * Revision 1.15  1995/09/26  16:46:48  pwe
 * compare with zero to ignore previous overflow
 *
 * Revision 1.14  1995/09/15  17:39:13  pwe
 * tidy and correct fistp
 *
 * Revision 1.13  1995/09/08  12:51:07  pwe
 * exceptions improved
 *
 * Revision 1.12  1995/09/06  16:29:20  pwe
 * exceptions now OK
 *
 * Revision 1.11  1995/09/05  16:24:51  pwe
 * specials and exception changes
 *
 * Revision 1.10  1995/09/01  17:30:05  pwe
 * traps and Build scripts
 *
 * Revision 1.9  1995/08/30  16:06:33  pwe
 * prepare exception trapping
 *
 * Revision 1.8  1995/08/23  09:42:42  pwe
 * track fpu control word for trap etc
 *
 * Revision 1.7  1995/08/14  13:53:36  pwe
 * several corrections, tail calls and error jumps
 *
 * Revision 1.6  1995/08/04  08:29:23  pwe
 * 4.0 general procs implemented
 *
 * Revision 1.5  1995/04/12  17:05:54  pwe
 * name_prefix required for call_libfn
 *
 * Revision 1.4  1995/02/16  18:47:08  pwe
 * transformed subtract inverts, sets and adds carry in case of error_jump
 *
 * Revision 1.3  1995/01/30  12:56:18  pwe
 * Ownership -> PWE, tidy banners
 *
 * Revision 1.2  1994/11/08  09:55:03  jmf
 * Unknown?
 *
 * Revision 1.1  1994/10/27  14:15:22  jmf
 * Initial revision
 *
 * Revision 1.4  1994/08/08  15:54:29  jmf
 * Dont keep_short after label
 *
 * Revision 1.3  1994/07/15  13:59:23  jmf
 * Change fstack popping to use fstp st(0).
 *
 * Revision 1.2  1994/07/12  15:19:18  jmf
 * No change
 *
 * Revision 1.1  1994/07/12  14:33:55  jmf
 * Initial revision
 *
**********************************************************************/


/**********************************************************************
                           instr.c


   defines the general routines for outputting instructions and labels:



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


#include "config.h"
#include "common_types.h"
#include "out.h"
#include "operand.h"
#include "instrmacs.h"
#include "expmacs.h"
#include "exp.h"
#include "instr386.h"
#include "flags.h"
#include "tags.h"
#include "shapemacs.h"
#include "flpt.h"
#include "flpttypes.h"
#include "coder.h"
#include "basicread.h"
#include "reg_record.h"
#include "installglob.h"
#include "table_fns.h"
#include "codermacs.h"
#include "install_fns.h"
#include "machine.h"
#include "localflags.h"
#include "assembler.h"
#include "messages_8.h"
#include "readglob.h"
#include "check.h"
#include "label_ops.h"
#include "externs.h"
#include "xalloc.h"

#include "instr.h"

#ifdef NEWDWARF
#include "dw2_config.h"
#include "dw2_extra.h"
#endif


/* LOCAL TYPE */

typedef union eu_u {int i; exp e;} punner;

/* MACROS */

#define fstack_base 8

/* VARIABLES */
/* All variables initialised */

int  extra_stack = 0;	/* init by init_all */
int max_extra_stack = 0;	/* init by cproc */
int no_frame;		/* init by cproc */
#ifndef NEWDIAGS
static long  last_jump_pos;	/* set locally */
#endif
int  last_jump_label;	/* cleared to -1 by outnl */

static exp cont_err_handler = nilexp;


/* IDENTITIES */

char *margin = " ";		/* instruction left margin */
char *spx = " ";		/* separates instruction from operands */
char *sep = ",";		/* separates operands */

char *reg_name_long[8] = {
  "%eax", "%edx", "%ecx", "%ebx", "%edi", "%esi", "%ebp", "%esp"
};
char *reg_name_word[7] = {
  "%ax", "%dx", "%cx", "%bx", "%di", "%si", "%bp"
};
char *reg_name_byte[7] = {
  "%al", "%dl", "%cl", "%bl", "%??", "%??", "%??"
};

char *fl_reg_name[8] = {
  "%st", "%st(1)", "%st(2)", "%st(3)", "%st(4)", "%st(5)", "%st(6)",
  "%st(7)",
};

/* PROCEDURES */

void temp_push_fl
    PROTO_Z ()
{
  ++fstack_pos;
  return;
}
void temp_pop_fl
    PROTO_Z ()
{
  --fstack_pos;
  return;
}


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

outreal outputs a floating point number

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

void outreal
    PROTO_N ( (e) )
    PROTO_T ( exp e )
{
  flt * f = &flptnos[no(e)];
  int sw = name(sh(e)) - shrealhd;
  r2l longs;

  longs = real2longs_IEEE(f, sw);

  switch (sw) {
    case 0:
      outhex(longs.i1);
      outnl();
      break;
    case 1:
      outhex(longs.i1);
      outs(",");
      outhex(longs.i2);
      outnl();
      break;
    case 2:
      outhex(longs.i1);
      outs(",");
      outhex(longs.i2);
      outs(",");
      outhex(longs.i3);
      outnl();
      break;
  };

  return;
}

/* output operand i (in bytes) relative to
   stack pointer  uses address relative to
   frame pointer if it might be shorter */
void rel_sp
    PROTO_N ( (i, b) )
    PROTO_T ( int i X int b )
{
  int  n = i + (extra_stack / 8);
  if (!must_use_bp) {
				/* if we might use alloca all
				   displacements must be relative to frame
				   pointer */
    if (n == 0) {
      outs ("(%esp");
      if (b)
	outs (")");
      return;
    };
    if (n <= 127 || no_frame || stack_aligned_8byte) {
				/* use stack pointer if displacement from
				   it is small */
      outn ((long)n);
      outs ("(%esp");
      if (b)
	outs (")");
      return;
    };
  };
  /* otherwise use displacement from frame pointer */
  outn ((long)(i + (stack_dec / 8)));
  outs("-");
  outs(local_prefix);
  outs ("disp");
  outn ((long)crt_proc_id);
  outs ("(%ebp");
  if (b)
    outs (")");
  return;
}

/* output operand i (in bytes) relative to
   stack pointer */
void rel_cp
    PROTO_N ( (i, b) )
    PROTO_T ( int i X int b )
{
  int  n = i + (extra_stack / 8);
  if (n == 0) {
    outs ("(%esp");
    if (b)
      outs (")");
    return;
  };
  outn ((long)n);
  outs ("(%esp");
  if (b)
    outs (")");
  return;
}

/* output operand relative to frame
   pointer */
void rel_ap
    PROTO_N ( (i, b) )
    PROTO_T ( int i X int b )
{
  if (no_frame) {
    outn ((long)(i + ((extra_stack - stack_dec) / 8)));
    outs("+");
    outs(local_prefix);
    outs ("disp");
    outn ((long)crt_proc_id);
    outs ("(%esp");
    if (b)
      outs (")");
    return;
  }
  else {
    outn ((long)i + 4);
    outs ("(%ebp");
    if (b)
      outs (")");
    return;
  };
}

/* output operand relative to frame
   pointer and push space*/
void rel_ap1
    PROTO_N ( (i, b) )
    PROTO_T ( int i X int b )
{
  if (no_frame) {
    outn ((long)(i + ((extra_stack - stack_dec) / 8)));
    outs("+");
    outs(local_prefix);
    outs ("fcwdisp");
    outn ((long)crt_proc_id);
    outs ("(%esp");
    if (b)
      outs (")");
    return;
  }
  else {
    outn ((long)i);
    outs("-");
    outs(local_prefix);
    outs ("fcwdisp");
    outn ((long)crt_proc_id);
    outs ("(%ebp");
    if (b)
      outs (")");
    return;
  };
}

int  get_reg_no
    PROTO_N ( (regs) )
    PROTO_T ( int regs )
{
  frr fr;
  /* find the registers associated with the bit pattern regs */

  fr = first_reg (regs);
  if (regs == 0x10000 || fr.fr_no == (fstack_pos))
    return (fstack_pos);
  return (fr.fr_no);		/* this is the register number */
}

/* output a register address, regs is a
   bit pattern, rdisp is an offset in bit
   units. le tells us how to refer to the
   register (eg al or ax or eax) */
void regn
    PROTO_N ( (regs, rdisp, ldname, le) )
    PROTO_T ( int regs X int rdisp X exp ldname X int le )
{
  int  z;
  char **rn;
  UNUSED(rdisp);
  z = get_reg_no (regs);

  if (name (ldname) == name_tag && islastuse(ldname))
    regsinuse = regsinuse & ~regs;

  if (z >= first_fl_reg) {
    if (z == first_fl_reg) {
      outs (fl_reg_name[0]);
      return;
    };
    if (fstack_pos > 16) {
      failer (BAD_FSTACK);
      exit(EXIT_FAILURE);
    };
    outs (fl_reg_name[fstack_pos - z]);
    /* variables held in the floating point registers have to be addressed
       relative to the current stack position, because the registers are a
       stack as well as a register bank */
    return;
  };

  switch (le) {
    case 8:
      rn = reg_name_byte;
      break;
    case 16:
      rn = reg_name_word;
      break;
    default:
      rn = reg_name_long;
      break;
  };
  outs (rn[z]);			/* this outputs the register name */
  return;
}


/* output a displacement from register operand */
void ind_reg
    PROTO_N ( (regs, rdisp, offset, ldname, b) )
    PROTO_T ( int regs X int rdisp X int offset X exp ldname X int b )
{
  if (regs == 128)
    offset += extra_stack;

  if (offset == 0) {
    outs ("(");
    regn (regs, rdisp, ldname, 32);
    if (b)
      outs (")");
  }
  else {
    outn ((long)offset / 8);
    outs ("(");
    regn (regs, rdisp, ldname, 32);
    if (b)
      outs (")");
  };
  return;
}

/* use indexed addressing */
void index_opnd
    PROTO_N ( (whmain, wh, sc) )
    PROTO_T ( where whmain X where wh X int sc )
{
  exp m = whmain.where_exp;
  if ((name (m) == name_tag && ptno (son (m)) == reg_pl) ||
      (name (m) == cont_tag && name (son (m)) == name_tag &&
	isvar (son (son (m))) && ptno (son (son (m))) == reg_pl))
    outs ("(");
  operand (32, whmain, 0, 0);
  outs (",");
  operand (32, wh, 1, 0);
  if (sc != 1) {
    outs (",");
    outn ((long)sc);
  };
  outs (")");
  return;
}


/* output an external operand */
void extn
    PROTO_N ( (id, off, b) )
    PROTO_T ( exp id X int off X int b )
{
  dec * et;

  et = brog (id);

  if (PIC_code)
   {
     char * got;
     if (et -> dec_u.dec_val.extnamed)
        got = "GOT";
     else
        got = "GOTOFF";
     outs (et -> dec_u.dec_val.dec_id);
     outs("@");
     outs(got);
     if (off != 0)
      {
        outs ("+");
        outn ((long)off / 8);
      };
     outs("(%ebx");
     if (b)
       outs(")");
     return;
   };

  if (off == 0)
    outs (et -> dec_u.dec_val.dec_id);
  else {
    outs (et -> dec_u.dec_val.dec_id);
    outs ("+");
    outn ((long)off / 8);
  };
  if (!b)
    outs ("(");
  return;
}

/* an integer constant */
void int_operand
    PROTO_N ( (k, l) )
    PROTO_T ( int k X int l )
{
  int  mask;
  switch (l) {
    case 8:
      mask = 0xff;
      break;
    case 16:
      mask = 0xffff;
      break;
    default:
      mask = 0xffffffff;
  };
  outs ("$");
  outn ((long)k & mask);
  return;
}


/* an external literal */
void const_extn
    PROTO_N ( (ident, noff) )
    PROTO_T ( exp ident X int noff )
{
  if (!PIC_code)
    outs ("$");
  extn (ident, noff, 1);
  return;
}

/* an external literal */
void proc_extn
    PROTO_N ( (id, off) )
    PROTO_T ( exp id X int off )
{
  if (PIC_code)
   {
     dec * et;
     et = brog (id);
     if (off == 0)
       outs (et -> dec_u.dec_val.dec_id);
     else {
        outn ((long)off / 8);
        outs ("+");
        outs (et -> dec_u.dec_val.dec_id);
     };
     if (et -> dec_u.dec_val.extnamed)
        outs("@PLT");
   }
  else
   {
     outs ("$");
     extn (id, off, 1);
   };

  return;
}

void ldisp
    PROTO_Z ()
{
   outs(local_prefix);
   outs("disp");
   outn((long)crt_proc_id);
}

void label_operand
    PROTO_N ( (e) )
    PROTO_T ( exp e )
{
  punner l;
  l.e = pt(e);
  outs("$");
  outs(local_prefix);
  outs("V");
  outn((long)l.i);
  return;
}

void set_lv_label
    PROTO_N ( (e) )
    PROTO_T ( exp e )
{
  punner l;
  l.e = e;
  min_rfree |= 0x78;  /* save all callee registers */

  outs(local_prefix);
  outs("V");
  outn((long)l.i);
  outs(":");
  outnl();
  return;
}

void set_env_off
    PROTO_N ( (s, n) )
    PROTO_T ( int s X exp n )
{
  punner l;
  l.e = n;
  outs(".set ");
  outs(local_prefix);
  outs("O");
  outn((long)l.i);		/* produce an identifying number */
  outs(",");
  if (s<4)
   {
    outn((long)-s/8);
    outs("-");
    outs(local_prefix);
    outs ("disp");
    outn ((long)crt_proc_id);
   }
  else
   outn((long)s/8);
  outnl();
}

void envoff_operand
    PROTO_N ( (e, off) )
    PROTO_T ( exp e X int off )
{
  punner l;
  l.e = e;
  if (off != 0)
   {
    outn((long)off);
    outs("+");
   };
  outs(local_prefix);
  outs("O");
  outn((long)l.i);		/* produce an identifying number */
  return;
}

void envsize_operand
    PROTO_N ( (e) )
    PROTO_T ( exp e )
{
  dec * et = brog(e);
  outs (local_prefix);
  outs ("ESZ");
  outs (et -> dec_u.dec_val.dec_id);
  return;
}

/* 80386 instruction with no operands */
void ins0
    PROTO_N ( (i) )
    PROTO_T ( char *i )
{
  outs (margin);
  outs (i);
  outnl ();
  return;
}

/* one operand */
void ins1
    PROTO_N ( (i, le1, a1) )
    PROTO_T ( char *i X int le1 X where a1 )
{
  outs (margin);
  outs (i);
  outs (spx);
  operand (le1, a1, 1, 0);
  outnl ();
  return;
}

/* one operand, which is indirect */
void ins1ind
    PROTO_N ( (i, le1, a1) )
    PROTO_T ( char *i X int le1 X where a1 )
{
  outs (margin);
  outs (i);
  outs (spx);
  outs ("*");
  operand (le1, a1, 1, 0);
  outnl ();
  return;
}

/* one operand, which is immediate */
void ins1lit
    PROTO_N ( (i, le1, a1) )
    PROTO_T ( char *i X int le1 X where a1 )
{
  outs (margin);
  outs (i);
  outs (spx);
  operand (le1, a1, 1, 1);
  outnl ();
  return;
}

/* two operands */
void ins2
    PROTO_N ( (i, le1, le2, a1, a2) )
    PROTO_T ( char *i X int le1 X int le2 X where a1 X where a2 )
{
  outs (margin);
  outs (i);
  outs (spx);
  operand (le1, a1, 1, 0);
  outs (sep);
  operand (le2, a2, 1, 0);
  outnl ();
  return;
}

/* three operands */
void ins3
    PROTO_N ( (i, le1, le2, le3, a1, a2, a3) )
    PROTO_T ( char *i X int le1 X int le2 X int le3 X where a1 X where a2 X where a3 )
{
  outs (margin);
  outs (i);
  outs (spx);
  operand (le1, a1, 1, 0);
  outs (sep);
  operand (le2, a2, 1, 0);
  outs (sep);
  operand (le3, a3, 1, 0);
  outnl ();
  return;
}


void simplest_set_lab
    PROTO_N ( (labno) )
    PROTO_T ( int labno )
{
  outs(local_prefix);
  outn ((long)labno);
  outs (":");
  outnl ();
}


void simple_set_label
    PROTO_N ( (labno) )
    PROTO_T ( int labno )
{
#ifdef CHECKIMPROVE
  if (labno == last_jump_label)
    failer("redundant jump");
#endif
#ifndef NEWDIAGS
  int   st = 0;
  if (!diagnose && labno == last_jump_label) {
    st = fseek (fpout, last_jump_pos, 0);
  };
  /* eliminate immediately previous jump to this label */
  if (st == -1) {
    failer (SEEK_FAILURE);
    exit(EXIT_FAILURE);
  };
#endif

  cond1_set = 0;
  cond2_set = 0;
  outs(local_prefix);
  outn ((long)labno);		/* the label no is held in the ptr field
				*/
  outs (":");
  outnl ();
/* Removed for experiments: improves compress?
  keep_short = 1;
*/
  return;
}

/* set label described by the jump record jr */
void set_label
    PROTO_N ( (jr) )
    PROTO_T ( exp jr )
{
  simple_set_label (ptno (jr));
}

/*  jump record: exp
    pt - label;
    last - forward;
    son - stack_dec;
    prop - floating stack position
*/

void discard_fstack
    PROTO_Z ()
{
  outs (" fstp %st(0)");
  outnl ();
  pop_fl;
  return;
}

void discard_st1
    PROTO_Z ()
{
  outs (" fstp %st(1)");
  outnl ();
  pop_fl;
}


/* output a jump to the label described by
   jump record jr */
void jump
    PROTO_N ( (jr, with_fl_reg) )
    PROTO_T ( exp jr X int with_fl_reg )
{
  int  fs_dest = (int)fstack_pos_of (jr);
  int  good_fs = fstack_pos;
  int  good_sd = stack_dec;
  if (fs_dest < first_fl_reg)
    failer (FSTACK_UNSET);
  if (with_fl_reg) {		/* jumping with a floating value */
    /* clear off any unwanted stack registers */
    while (fstack_pos > (fs_dest + 1))
      discard_st1 ();
    fstack_pos = good_fs - 1;
  }
  else {
    /* clear off any unwanted stack registers */
    while (fstack_pos > fs_dest)
      discard_fstack ();
    fstack_pos = good_fs;
  };

  if (sonno(jr) > stack_dec) {
    add(slongsh, mw (zeroe, (sonno(jr)-stack_dec) / 8), sp, sp);
    stack_dec = sonno(jr);
  }

  reset_fpucon();
  stack_dec = good_sd;

#ifndef NEWDIAGS
  if (flush_before_tell)
    IGNORE fflush(fpout);
  last_jump_pos = ftell (fpout);
#endif
  outs (margin);
  outs (jmp);
  outs (spx);
  outs(local_prefix);
  outn ((long)ptno (jr));
  outnl ();
  last_jump_label = ptno (jr);
  return;
}

static char* xse = "<=0";	/* no corresponding jump instruction */
static char* xnse = ">0";


/* output code for a branch instruction
   determined by test_no. The test is
   signed if sg is true */
static char *out_branch
    PROTO_N ( (sg, test_no, shnm) )
    PROTO_T ( int sg X int test_no X int shnm )
{
  if (shnm >= shrealhd && shnm <= doublehd) {
    switch (test_no) {
      case 1:
	return (jne);

      case 2:
	return (jne);

      case 3:
	return (jpe);

      case 4:
	return (jpe);

      case 5:
	return (jpe);

      case 6:
	return (jpo);

      case 7:
	return (jpo);

      case 8:
	return (jpo);

      case 9:
	return (je);

      case 10:
	return (je);

      case 11:
	return (jne);

      case 12:
	return (je);

      case 13:
	return (jne);

      case 14:
	return (je);

      default:
	failer (BAD_TESTNO);
    };
  };

  if (sg) {
    switch (test_no) {
      case 1:
	return (sg<0 ? xse : jle);
      case 2:
	return (sg<0 ? js : jl);

      case 3:
	return (sg<0 ? jns : jge);

      case 4:
	return (sg<0 ? xnse : jg);

      case 5:
	return (jne);

      case 6:
	return (je);

      default:
	failer (BAD_TESTNO);
    };
  }
  else {
    switch (test_no) {
      case 1:
	return (jbe);

      case 2:
	return (jb);

      case 3:
	return (jae);

      case 4:
	return (ja);

      case 5:
	return (jne);

      case 6:
	return (je);

      default:
	failer (BAD_TESTNO);
    };
  };
  return ((char *) 0);
}

void simple_branch
    PROTO_N ( (j, labno) )
    PROTO_T ( char *j X int labno )
{
  outs (margin);
  outs (j);
  outs (spx);
  outs(local_prefix);
  outn ((long)labno);
  outnl ();

}


/* output conditional jump to jr. testno
   specifies kind of test. sg is 1 if
   signed arithmetic, 0 unsigned, -1 if
   signed vs zero (ignoring overflow).
   shnm name of shape */
void branch
    PROTO_N ( (test_no, jr, sg, shnm) )
    PROTO_T ( int test_no X exp jr X int sg X int shnm )
{
  int  fs_dest = (int)fstack_pos_of (jr);
  int  good_fs = fstack_pos;
  int  good_fpucon = fpucon;
  if (fs_dest < first_fl_reg)
    failer (FSTACK_UNSET);
  if (fstack_pos > fs_dest || sonno(jr) != stack_dec || fpucon != normal_fpucon
	|| cmp_64hilab >= 0) {
	/* floating point stack or call stack need attention */
    int  nl = next_lab ();
    int inv_test_no = (flpt_always_comparable ||
			 (shnm < shrealhd || shnm > doublehd))
				? (int)int_inverse_ntest[test_no]
				: (int)real_inverse_ntest[test_no];

    char* cj = out_branch ((cmp_64hilab >= 0 ? 0 : sg), inv_test_no, shnm);
    if (*cj == 'j') {
      simple_branch (cj, nl);
    }
    else	/* compare with zero, ignoring overflow */
    if (*cj == '>') {
      int nl1 = next_lab ();
      simple_branch (js, nl1);
      simple_branch (jne, nl);
      simplest_set_lab (nl1);
    }
    else {
      simple_branch (js, nl);
      simple_branch (je, nl);
    }

    if (cmp_64hilab >= 0) {
      int nl2 = ptno (jr);
      if (shnm != s64hd)
	failer ("uncompleted 64-bit comparison");
      if (fstack_pos > fs_dest || sonno(jr) != stack_dec || fpucon != normal_fpucon) {
	nl2 = next_lab ();
	simplest_set_lab (nl2);
      }
      jump (jr, 0);
      simplest_set_lab (cmp_64hilab);
      simple_branch (out_branch (1, test_no, shnm), nl2);
      cmp_64hilab = -1;
    }
    else
      jump (jr, 0);

    fstack_pos = good_fs;
    fpucon = good_fpucon;
    simplest_set_lab (nl);
    return;
  };

  {
    char* cj = out_branch (sg, test_no, shnm);
    if (*cj == 'j') {
      simple_branch (cj, ptno (jr));
    }
    else	/* compare with zero, ignoring overflow */
    if (*cj == '>') {
      int nl1 = next_lab ();
      simple_branch (js, nl1);
      simple_branch (jne, ptno (jr));
      simplest_set_lab (nl1);
    }
    else {
      simple_branch (js, ptno (jr));
      simple_branch (je, ptno (jr));
    }
  }
  return;
}

void setcc
    PROTO_N ( (test_no, sg, shnm) )
    PROTO_T ( int test_no X int sg X int shnm )
{
  char * b;
  if (cmp_64hilab >= 0) {
    int chl = cmp_64hilab;
    int nl = next_lab ();
    if (shnm != s64hd)
      failer ("uncompleted 64-bit comparison");
    cmp_64hilab = -1;
    setcc (test_no, 0, ulonghd);
    simple_branch (jmp, nl);
    simplest_set_lab (chl);
    setcc (test_no, sg, slonghd);
    simplest_set_lab (nl);
  }

  b = out_branch (sg, test_no, shnm);
  if (*b != 'j')
    failer(NO_SETCC);
  outs(margin);
  outs("set");
  outs(&b[1]);
  outs(spx);
  outs(reg_name_byte[0]);
  outnl();
  return;
}

/* output conditional jump to jr if overflow
   sg is 1 if signed arithmetic, 0 unsigned */
void jmp_overflow
    PROTO_N ( (jr, sg, inv) )
    PROTO_T ( exp jr X int sg X int inv )
{
  int  fs_dest = (int)fstack_pos_of (jr);
  int  good_fs = fstack_pos;
  int  good_fpucon = fpucon;
  if (fs_dest < first_fl_reg)
    failer (FSTACK_UNSET);
  if (fstack_pos > fs_dest || sonno(jr) != stack_dec || fpucon != normal_fpucon) {
	/* floating point stack or call stack need attention */
    int  nl = next_lab ();
    if (sg)
      simple_branch(jno, nl);
    else
      simple_branch((inv ? jb : jae), nl);
    jump (jr, 0);
    fstack_pos = good_fs;
    fpucon = good_fpucon;
    simplest_set_lab (nl);
    return;
  };
  if (sg)
    simple_branch(jo, ptno(jr));
  else
    simple_branch((inv ? jae : jb), ptno(jr));
  return;
}


/* software interrupt */
void trap_ins
    PROTO_N ( (s) )
    PROTO_T ( int s )
{
#ifndef AVOID_INTOV
  if (s == f_overflow) {
    ins0 ("int $4");	/* numeric interrupt */
    return;
  }
#else
#if (AVOID_INTOV == 16)
  if (s == f_overflow) {
    ins0 ("int $16");	/* mimic floating point interrupt */
    return;
  }
#endif
#endif
  if (cont_err_handler == nilexp) {
    cont_err_handler = make_extn ("__trans386_errhandler", f_proc, 1);
    if (!PIC_code)
      cont_err_handler = getexp (f_proc, nilexp, 1, cont_err_handler, nilexp, 0, 0, cont_tag);
  }
  ins1 (pushl, 32, mw (zeroe, s));
#ifdef NEWDWARF
  if (diagnose && dwarf2 && no_frame)
    dw2_track_push();
#endif
  ins2 (movl, 32, 32, mw(cont_err_handler, 0), reg0);
  if (PIC_code)
    ins1ind (call, 32, ind_reg0);
  else
    ins1ind (call, 32, reg0);
  return;
}


/* output software interrupt if overflow
   sg is 1 if signed arithmetic, 0 unsigned */
void trap_overflow
    PROTO_N ( (sg, inv) )
    PROTO_T ( int sg X int inv )
{
#ifdef AVOID_INTOV
    int nl = next_lab ();
    if (sg)
      simple_branch(jno, nl);
    else
      simple_branch((inv ? jb : jae), nl);
    trap_ins(f_overflow);
    simplest_set_lab (nl);
#else
  if (sg)
    ins0(into);
  else {
    int nl = next_lab ();
    simple_branch((inv ? jb : jae), nl);
    trap_ins(f_overflow);
    simplest_set_lab (nl);
  }
#endif
  return;
}


/* conditional software interrupt
   sg is 1 if signed arithmetic
   shnm name of shape */
void test_trap
    PROTO_N ( (test_no, sg, shnm) )
    PROTO_T ( int test_no X int sg X int shnm )
{
  int nl = next_lab ();
  int inv_test_no = (flpt_always_comparable ||
			 (shnm < shrealhd || shnm > doublehd))
				? (int)int_inverse_ntest[test_no]
				: (int)real_inverse_ntest[test_no];
  simple_branch (out_branch (sg, inv_test_no, shnm), nl);
  trap_ins(f_overflow);
  simplest_set_lab (nl);
  return;
}



/* special output for doing multiply by
   using index instructions */
void mult_op
    PROTO_N ( (inc, rmain, rind, sc, dest) )
    PROTO_T ( int inc X where rmain X where rind X int sc X where dest )
{
  outs (margin);
  outs ("leal");
  outs (spx);
  if (inc != 0)
    outn ((long)inc);
  outs ("(");
  if (name (rmain.where_exp) != val_tag ||
      (no (rmain.where_exp) + rmain.where_off) != 0)
    operand (32, rmain, 1, 0);
  outs (",");
  operand (32, rind, 1, 0);
  if (sc != 1) {
    outs (",");
    outn ((long)sc);
  };
  outs ("),");

  if (inmem (dest)) {
    operand (32, reg0, 1, 0);
    outnl ();
    invalidate_dest (reg0);
    end_contop ();
    move (slongsh, reg0, dest);
  }
  else {
    operand (32, dest, 1, 0);
    outnl ();
    end_contop ();
  };
  return;
}

/* output the case switch jump and the jump table */
void caseins
    PROTO_N ( (sz, arg, min, max, v, exhaustive, in_eax, case_exp) )
    PROTO_T ( int sz X exp arg X int min X int max X int *v X int exhaustive X int in_eax X exp case_exp )
{
  int tab;
  int absent;
  where a;
  int need_label_flag=0;
  exp next= short_next_jump(case_exp);
  if (next != nilexp && name(next)==goto_tag)
  {
    exp lab=final_dest(pt(next));
    absent=ptno(pt(son(lab)));
  }
  else
  {
    absent = (exhaustive) ? -1 : next_lab ();
    need_label_flag=1;
  }

  tab = next_lab ();
  a = mw (arg, 0);

  if (inmem (mw (arg, 0)) || sz != 32) {
    if (!in_eax)
      change_var (slongsh, a, reg0);
    a = reg0;
  }

  /* the switch jump */
  out_switch_jump(tab, a, min);

  /* table of offsets */
  out_switch_table(tab, min, max, v, absent);

  if (!exhaustive && need_label_flag==1) {
    /*  label for default of switch; continue here */
    outs(local_prefix);
    outn ((long)absent);
    outs (":");
    outnl ();
#ifdef NEWDWARF
    START_BB ();
#endif
  };
  return;
}


void const_intnl
    PROTO_N ( (addr, lab, off) )
    PROTO_T ( int addr X int lab X int off )
{
  if (PIC_code)
   {
    outs(local_prefix);
    outn ((long)lab);
    outs("@GOTOFF");
    if (off != 0) {
      outs ("+");
      outn ((long)off / 8);
    };
    outs("(%ebx)");
    return;
   }
  else
   {
    if (addr)
      outs ("$");
    outs(local_prefix);
    outn ((long)lab);
    if (off != 0) {
      outs ("+");
      outn ((long)off / 8);
    };
    return;
  };
}

void load_stack0
    PROTO_Z ()
{
  outs (" fld %st(0)");
  outnl ();
  return;
}

void outbp
    PROTO_Z ()
{
  outs("%ebp");
}

void set_stack_from_bp
    PROTO_Z ()
{
  outs (margin);
  outs (leal);
  outs (spx);
  outn((long)stack_dec/8);
  outs("-");
  outs(local_prefix);
  outs("disp");
  outn((long)crt_proc_id);
  outs ("(%ebp)");
  outs (sep);
  outs("%esp");
  outnl ();
  return;
}

void testah
    PROTO_N ( (mask) )
    PROTO_T ( int mask )
{
  outs(" testb $");
  outn((long)mask);
  outs(",%ah");
  outnl();
  return;
}

exp make_extn
    PROTO_N ( (n, s, v) )
    PROTO_T ( char * n X shape s X int v )
{
  dec * g = (dec *) (xmalloc (sizeof(dec)));
  exp id = getexp (s, nilexp, 1, nilexp, nilexp, 0, 0, ident_tag);
  exp nme = getexp (s, nilexp, 1, id, nilexp, 0, 0, name_tag);
  setglob (id);
  if (v) {
#if keep_PIC_vars
        setvar(id);
#else
        if (PIC_code)
          sh(id) = f_pointer(f_alignment(s));
        else
          setvar(id);
#endif
  }
  brog(id) = g;
  if (prefix_length != 0) {
    int nl = (int) strlen (n);
    int j;
    char * newn = (char *) xcalloc ((nl + prefix_length + 1), sizeof (char));
    for (j = 0; j < prefix_length; ++j)
      newn[j] = name_prefix[j];
    for (j = 0; j < nl; ++j)
      newn[j+prefix_length] = n[j];
    newn[nl+prefix_length] = 0;
    n = newn;
  }
  g -> dec_u.dec_val.dec_exp = id;
  g -> dec_u.dec_val.dec_id = n;
  g -> dec_u.dec_val.extnamed = 1;
  return (nme);
}



/* shift or rotate 64 bits in reg0/reg1 */
void rotshift64
    PROTO_N ( (shft, sig, wshift) )
    PROTO_T ( int shft X int sig X where wshift )
{
  if (name(wshift.where_exp) == val_tag) {	/* no of places is constant */
    int places = no (wshift.where_exp) + wshift.where_off;
    if (places >= 32) {
      places -= 32;
      switch (shft) {
	case 0:
	  if (places)
	    ins2 (shll, 8, 32, mw(zeroe,places), reg0);
	  move (ulongsh, reg0, reg1);
	  move (ulongsh, zero, reg0);
	  return;
	case 1:
	  move (ulongsh, reg1, reg0);
	  if (places)
	    ins2 ((sig ? sarl : shrl), 8, 32, mw(zeroe,places), reg0);
	  if (sig)
	    ins2 (sarl, 8, 32, mw(zeroe,31), reg1);
	  else
	    move (ulongsh, zero, reg1);
	  return;
	default: {
	  if (!places) {
	    ins2 (xchg, 32, 32, reg0, reg1);
	    return;
	  }
	  places = 32 - places;
	  shft = 5 - shft;	/* reverse rotate */
	}
      }
    };
    if (places == 0)
      return;
    switch (shft) {	/* between 1 and 31 places */
      case 0:
	ins3 (shldl, 8, 32, 32, mw(zeroe,places), reg0, reg1);
	ins2 (shll, 8, 32, mw(zeroe,places), reg0);
	return;
      case 1:
	ins3 (shrdl, 8, 32, 32, mw(zeroe,places), reg1, reg0);
	ins2 ((sig ? sarl : shrl), 8, 32, mw(zeroe,places), reg1);
	return;
      default: {
	char * dsh = (shft == 2 ? shrdl : shldl);
        extra_stack += 64;
	check_stack_max;
	ins0 (pushedx);
#ifdef NEWDWARF
	if (diagnose && dwarf2 && no_frame)
	  dw2_track_push();
#endif
	ins0 (pusheax);
#ifdef NEWDWARF
	if (diagnose && dwarf2 && no_frame)
	  dw2_track_push();
#endif
	ins3 (dsh, 8, 32, 32, mw(zeroe,places),
		reg1, mw(ind_sp.where_exp,-32));
	ins3 (dsh, 8, 32, 32, mw(zeroe,places),
		reg0, mw(ind_sp.where_exp,-64));
	ins0 (popeax);
#ifdef NEWDWARF
	if (diagnose && dwarf2 && no_frame)
	  dw2_track_pop();
#endif
	ins0 (popedx);
#ifdef NEWDWARF
	if (diagnose && dwarf2 && no_frame)
	  dw2_track_pop();
#endif
	invalidate_dest (ind_sp);
        extra_stack -= 64;
	return;
      }
    }
  };
  {				/* number of places in reg2 */
    int lablow = next_lab ();
    int labend = next_lab ();
    ins2 (cmpl, 32, 32, mw(zeroe,32), reg2);
    simple_branch (jl, lablow);
    switch (shft) {
      case 0:
	ins2 (subl, 32, 32, mw(zeroe,32), reg2);
	ins2 (shll, 8, 32, reg2, reg0);
	move (ulongsh, reg0, reg1);
	move (ulongsh, zero, reg0);
	break;
      case 1:
	ins2 (subl, 32, 32, mw(zeroe,32), reg2);
	move (ulongsh, reg1, reg0);
	ins2 ((sig ? sarl : shrl), 8, 32, reg2, reg0);
	if (sig)
	  ins2 (sarl, 8, 32, mw(zeroe,31), reg1);
	else
	  move (ulongsh, zero, reg1);
	break;
      default: {
	int labx = next_lab ();
	char * dsh = (shft == 2 ? shldl : shrdl);	/* reversed rotate */
	simple_branch (je, labx);
	ins2 (subl, 32, 32, mw(zeroe,64), reg2);
	ins1 (negl, 32, reg2);
        extra_stack += 64;
	check_stack_max;
	ins0 (pushedx);
#ifdef NEWDWARF
	if (diagnose && dwarf2 && no_frame)
	  dw2_track_push();
#endif
	ins0 (pusheax);
#ifdef NEWDWARF
	if (diagnose && dwarf2 && no_frame)
	  dw2_track_push();
#endif
	ins3 (dsh, 8, 32, 32, reg2, reg1, mw(ind_sp.where_exp,-32));
	ins3 (dsh, 8, 32, 32, reg2, reg0, mw(ind_sp.where_exp,-64));
	ins0 (popeax);
#ifdef NEWDWARF
	if (diagnose && dwarf2 && no_frame)
	  dw2_track_pop();
#endif
	ins0 (popedx);
#ifdef NEWDWARF
	if (diagnose && dwarf2 && no_frame)
	  dw2_track_pop();
#endif
	invalidate_dest (ind_sp);
        extra_stack -= 64;
	simple_branch (jmp, labend);
	simplest_set_lab (labx);
	ins2 (xchg, 32, 32, reg0, reg1);
      }
    }
    simple_branch (jmp, labend);
    simplest_set_lab (lablow);
    switch (shft) {	/* between 0 and 31 places */
      case 0:
	ins3 (shldl, 8, 32, 32, reg2, reg0, reg1);
	ins2 (shll, 8, 32, reg2, reg0);
	break;
      case 1:
	ins3 (shrdl, 8, 32, 32, reg2, reg1, reg0);
	ins2 ((sig ? sarl : shrl), 8, 32, reg2, reg1);
	break;
      default: {
	char * dsh = (shft == 2 ? shrdl : shldl);
        extra_stack += 64;
	check_stack_max;
	ins0 (pushedx);
#ifdef NEWDWARF
	if (diagnose && dwarf2 && no_frame)
	  dw2_track_push();
#endif
	ins0 (pusheax);
#ifdef NEWDWARF
	if (diagnose && dwarf2 && no_frame)
	  dw2_track_push();
#endif
	ins3 (dsh, 8, 32, 32, reg2, reg1, mw(ind_sp.where_exp,-32));
	ins3 (dsh, 8, 32, 32, reg2, reg0, mw(ind_sp.where_exp,-64));
	ins0 (popeax);
#ifdef NEWDWARF
	if (diagnose && dwarf2 && no_frame)
	  dw2_track_pop();
#endif
	ins0 (popedx);
#ifdef NEWDWARF
	if (diagnose && dwarf2 && no_frame)
	  dw2_track_pop();
#endif
	invalidate_dest (ind_sp);
        extra_stack -= 64;
      }
    }
    simplest_set_lab (labend);
  };
  return;
}
