/* Subroutines for insn-output.c for System/370.
   Copyright (C) 1989, 1993, 1995, 1997, 1998, 1999, 2000, 2002
   Free Software Foundation, Inc.
   Contributed by Jan Stein (jan@cd.chalmers.se).
   Modified for OS/390 LanguageEnvironment C by Dave Pitts (dpitts@cozx.com)
   Hacked for Linux-ELF/390 by Linas Vepstas (linas@linas.org) 

This file is part of GCC.

GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "tree.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "output.h"
#include "insn-attr.h"
#include "function.h"
#include "expr.h"
#include "flags.h"
#include "recog.h"
#include "toplev.h"
#include "cpplib.h"
#include "tm_p.h"
#include "target.h"
#include "target-def.h"

extern FILE *asm_out_file;

/* Label node.  This structure is used to keep track of labels 
      on the various pages in the current routine.
   The label_id is the numeric ID of the label,
   The label_page is the page on which it actually appears,
   The first_ref_page is the page on which the true first ref appears.
   The label_addr is an estimate of its location in the current routine,
   The label_first & last_ref are estimates of where the earliest and
      latest references to this label occur.  */

typedef struct label_node
  {
    struct label_node *label_next;
    int label_id;
    int label_page;
    int first_ref_page;

    int label_addr;
    int label_first_ref;
    int label_last_ref;
  }
label_node_t;

/* Is 1 when a label has been generated and the base register must be reloaded.  */
int mvs_need_base_reload = 0;

/* Current function starting base page.  */
int function_base_page;

/* Length of the current page code.  */
int mvs_page_code;

/* Length of the current page literals.  */
int mvs_page_lit;

/* Current function name.  */
char *mvs_function_name = 0;

/* Current function name length.  */
size_t mvs_function_name_length = 0;

/* Page number for multi-page functions.  */
int mvs_page_num = 0;

/* Label node list anchor.  */
static label_node_t *label_anchor = 0;

/* Label node free list anchor.  */
static label_node_t *free_anchor = 0;

/* Assembler source file descriptor.  */
static FILE *assembler_source = 0;

static label_node_t * mvs_get_label (int);
static void i370_label_scan (void);
#ifdef TARGET_HLASM
static bool i370_hlasm_assemble_integer (rtx, unsigned int, int);
static void i370_globalize_label (FILE *, const char *);
#endif
static void i370_output_function_prologue (FILE *, HOST_WIDE_INT);
static void i370_output_function_epilogue (FILE *, HOST_WIDE_INT);
static void i370_file_start (void);
static void i370_file_end (void);

#ifdef LONGEXTERNAL
static int mvs_hash_alias (const char *);
#endif
static void i370_internal_label (FILE *, const char *, unsigned long);
static bool i370_rtx_costs (rtx, int, int, int *);

/* ===================================================== */
/* defines and functions specific to the HLASM assembler */
#ifdef TARGET_HLASM

#define MVS_HASH_PRIME 999983
#if HOST_CHARSET == HOST_CHARSET_EBCDIC
#define MVS_SET_SIZE 256
#else
#define MVS_SET_SIZE 128
#endif

#ifndef MAX_MVS_LABEL_SIZE
#define MAX_MVS_LABEL_SIZE 8
#endif

#define MAX_LONG_LABEL_SIZE 255

/* Alias node, this structure is used to keep track of aliases to external
   variables. The IBM assembler allows an alias to an external name 
   that is longer that 8 characters; but only once per assembly.
   Also, this structure stores the #pragma map info.  */
typedef struct alias_node
  {
    struct alias_node *alias_next;
    int  alias_emitted;
    char alias_name [MAX_MVS_LABEL_SIZE + 1];
    char real_name [MAX_LONG_LABEL_SIZE + 1];
  }
alias_node_t;

/* Alias node list anchor.  */
static alias_node_t *alias_anchor = 0;

/* Define the length of the internal MVS function table.  */
#define MVS_FUNCTION_TABLE_LENGTH 32

/* C/370 internal function table.  These functions use non-standard linkage
   and must handled in a special manner.  */
static const char *const mvs_function_table[MVS_FUNCTION_TABLE_LENGTH] =
{
#if HOST_CHARSET == HOST_CHARSET_EBCDIC /* Changed for EBCDIC collating sequence */
   "ceil",     "edc_acos", "edc_asin", "edc_atan", "edc_ata2", "edc_cos",
   "edc_cosh", "edc_erf",  "edc_erfc", "edc_exp",  "edc_gamm", "edc_lg10",
   "edc_log",  "edc_sin",  "edc_sinh", "edc_sqrt", "edc_tan",  "edc_tanh",
   "fabs",     "floor",    "fmod",     "frexp",    "hypot",    "jn",
   "j0",       "j1",       "ldexp",    "modf",     "pow",      "yn",
   "y0",       "y1"
#else
   "ceil",     "edc_acos", "edc_asin", "edc_ata2", "edc_atan", "edc_cos",
   "edc_cosh", "edc_erf",  "edc_erfc", "edc_exp",  "edc_gamm", "edc_lg10",
   "edc_log",  "edc_sin",  "edc_sinh", "edc_sqrt", "edc_tan",  "edc_tanh",
   "fabs",     "floor",    "fmod",     "frexp",    "hypot",    "j0",
   "j1",       "jn",       "ldexp",    "modf",     "pow",      "y0",
   "y1",       "yn"
#endif
};

#endif /* TARGET_HLASM */
/* ===================================================== */


/* Initialize the GCC target structure.  */
#ifdef TARGET_HLASM
#undef TARGET_ASM_BYTE_OP
#define TARGET_ASM_BYTE_OP NULL
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP NULL
#undef TARGET_ASM_ALIGNED_SI_OP
#define TARGET_ASM_ALIGNED_SI_OP NULL
#undef TARGET_ASM_INTEGER
#define TARGET_ASM_INTEGER i370_hlasm_assemble_integer
#undef TARGET_ASM_GLOBALIZE_LABEL
#define TARGET_ASM_GLOBALIZE_LABEL i370_globalize_label
#endif

#undef TARGET_ASM_FUNCTION_PROLOGUE
#define TARGET_ASM_FUNCTION_PROLOGUE i370_output_function_prologue
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE i370_output_function_epilogue
#undef TARGET_ASM_FILE_START
#define TARGET_ASM_FILE_START i370_file_start
#undef TARGET_ASM_FILE_END
#define TARGET_ASM_FILE_END i370_file_end
#undef TARGET_ASM_INTERNAL_LABEL
#define  TARGET_ASM_INTERNAL_LABEL i370_internal_label
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS i370_rtx_costs

struct gcc_target targetm = TARGET_INITIALIZER;

/* Set global variables as needed for the options enabled.  */

void
override_options ()
{
  /* We're 370 floating point, not IEEE floating point.  */
  memset (real_format_for_mode, 0, sizeof real_format_for_mode);
  REAL_MODE_FORMAT (SFmode) = &i370_single_format;
  REAL_MODE_FORMAT (DFmode) = &i370_double_format;
}

/* ===================================================== */
/* The following three routines are used to determine whther 
   forward branch is on this page, or is a far jump.  We use
   the "length" attr on an insn [(set_atter "length" "4")]
   to store the largest possible code length that insn
   could have.  This gives us a hint of the address of a
   branch destination, and from that, we can work out 
   the length of the jump, and whether its on page or not. 
 */

/* Return the destination address of a branch.  */

int
i370_branch_dest (branch)
     rtx branch;
{
  rtx dest = SET_SRC (PATTERN (branch));
  int dest_uid;
  int dest_addr;

  /* first, compute the estimated address of the branch target */
  if (GET_CODE (dest) == IF_THEN_ELSE)
    dest = XEXP (dest, 1);
  dest = XEXP (dest, 0);
  dest_uid = INSN_UID (dest);
  dest_addr = INSN_ADDRESSES (dest_uid);

  /* next, record the address of this insn as the true addr of first ref */
  {
     label_node_t *lp;
     rtx label = JUMP_LABEL (branch);
     int labelno = CODE_LABEL_NUMBER (label);

     if (!label || CODE_LABEL != GET_CODE (label)) abort ();

     lp = mvs_get_label (labelno);
     if (-1 == lp -> first_ref_page) lp->first_ref_page = mvs_page_num;
  }
  return dest_addr;
}

int
i370_branch_length (insn)
     rtx insn;
{
  int here, there;
  here = INSN_ADDRESSES (INSN_UID (insn));
  there = i370_branch_dest (insn);
  return (there - here);
}


int
i370_short_branch (insn)
     rtx insn;
{
  int base_offset;

  base_offset = i370_branch_length(insn);
  if (0 > base_offset) 
    {
      base_offset += mvs_page_code;
    } 
  else 
    {
      /* avoid bumping into lit pool; use 2x to estimate max possible lits */
      base_offset *= 2;
      base_offset += mvs_page_code + mvs_page_lit;
    }
  
  /* make a conservative estimate of room left on page */
  if ((4060 >base_offset) && ( 0 < base_offset)) return 1;
  return 0;
}

/* The i370_label_scan() routine is supposed to loop over
   all labels and label references in a compilation unit,
   and determine whether all label refs appear on the same 
   code page as the label. If they do, then we can avoid 
   a reload of the base register for that label.
  
   Note that the instruction addresses used here are only 
   approximate, and make the sizes of the jumps appear
   farther apart then they will actually be.  This makes 
   this code far more conservative than it needs to be.
 */

#define I370_RECORD_LABEL_REF(label,addr) {				\
	label_node_t *lp;						\
	int labelno = CODE_LABEL_NUMBER (label);			\
	lp = mvs_get_label (labelno);					\
	if (addr < lp -> label_first_ref) lp->label_first_ref = addr;	\
	if (addr > lp -> label_last_ref) lp->label_last_ref = addr;	\
}

static void 
i370_label_scan () 
{
   rtx insn;
   label_node_t *lp;
   int tablejump_offset = 0;

   for (insn = get_insns(); insn; insn = NEXT_INSN(insn))
     {
       int here = INSN_ADDRESSES (INSN_UID (insn));
       enum rtx_code code = GET_CODE(insn);

       /* ??? adjust for tables embedded in the .text section that
        * the compiler didn't take into account */
       here += tablejump_offset;
       INSN_ADDRESSES (INSN_UID (insn)) = here;

       /* check to see if this insn is a label ...  */
       if (CODE_LABEL == code)
         {
           int labelno = CODE_LABEL_NUMBER (insn);

           lp = mvs_get_label (labelno);
           lp -> label_addr = here;
#if 0
           /* Supposedly, labels are supposed to have circular
              lists of label-refs that reference them, 
              setup in flow.c, but this does not appear to be the case.  */
           rtx labelref = LABEL_REFS (insn);
           rtx ref = labelref;
           do 
             {
               rtx linsn = CONTAINING_INSN(ref);
               ref =  LABEL_NEXTREF(ref);
             } while (ref && (ref != labelref));
#endif
         }
       else
       if (JUMP_INSN == code)
         {
           rtx label = JUMP_LABEL (insn);

           /* If there is no label for this jump, then this
              had better be a ADDR_VEC or an ADDR_DIFF_VEC
              and there had better be a vector of labels.  */
           if (!label) 
             {
               int j;
               rtx body = PATTERN (insn);
               if (ADDR_VEC == GET_CODE(body)) 
                 {
                    for (j=0; j < XVECLEN (body, 0); j++)
                      {
                         rtx lref = XVECEXP (body, 0, j);
                         if (LABEL_REF != GET_CODE (lref)) abort ();
                         label = XEXP (lref,0);
                         if (CODE_LABEL != GET_CODE (label)) abort ();
                         tablejump_offset += 4;
                         here += 4;
                         I370_RECORD_LABEL_REF(label,here);
                      }
                    /* finished with the vector go do next insn */
                    continue;
                 }
               else
               if (ADDR_DIFF_VEC == GET_CODE(body))
                 {
/* XXX hack alert.
   Right now, we leave this as a no-op, but strictly speaking,
   this is incorrect.  It is possible that a table-jump
   driven off of a relative address could take us off-page,
   to a place where we need to reload the base reg.  So really,
   we need to examing both labels, and compare thier values
   to the current basereg value.
  
   More generally, this brings up a troubling issue overall:
   what happens if a tablejump is split across two pages? I do 
   not beleive that this case is handled correctly at all, and
   can only lead to horrible results if this were to occur.
  
   However, the current situation is not any worse than it was 
   last week, and so we punt for now.  */

                    debug_rtx (insn);
                    for (j=0; j < XVECLEN (body, 0); j++)
                      {
                      }
                    /* finished with the vector go do next insn */
                    continue;
                 }
               else 
                 {
/* XXX hack alert.
   Compiling the exception handling (L_eh) in libgcc2.a will trip
   up right here, with something that looks like
   (set (pc) (mem:SI (plus:SI (reg/v:SI 1 r1) (const_int 4))))
      {indirect_jump} 
   I'm not sure of what leads up to this, but it looks like
   the makings of a long jump which will surely get us into trouble
   because the base & page registers don't get reloaded.  For now
   I'm not sure of what to do ... again we punt ... we are not worse
   off than yesterday.  */

                    /* print_rtl_single (stdout, insn); */
                    debug_rtx (insn);
                    /* abort(); */
                    continue;
                 }
            }
          else
            {
              /* At this point, this jump_insn had better be a plain-old
                 ordinary one, grap the label id and go */
              if (CODE_LABEL != GET_CODE (label)) abort ();
              I370_RECORD_LABEL_REF(label,here);
            }
        }

      /* Sometimes, we take addresses of labels and use them
         as instruction operands ... these show up as REG_NOTES */
      else
      if (INSN == code)
       {
         if ('i' == GET_RTX_CLASS (code)) 
           {
              rtx note;
              for (note = REG_NOTES (insn); note;  note = XEXP(note,1))
                {
                   if (REG_LABEL == REG_NOTE_KIND(note))
                     {
                        rtx label = XEXP (note,0);
                        if (!label || CODE_LABEL != GET_CODE (label)) abort ();

                        I370_RECORD_LABEL_REF(label,here);
                     }
                }
           }
       }
   }
}

/* ===================================================== */

/* Emit reload of base register if indicated.  This is to eliminate multiple
   reloads when several labels are generated pointing to the same place
   in the code.  

   The page table is written at the end of the function. 
   The entries in the page table look like
     .LPGT0:          // PGT0 EQU *
     .long .LPG0      // DC A(PG0)
     .long .LPG1      // DC A(PG1)
  while the prologue generates
      L       r4,=A(.LPGT0)

  Note that this paging scheme breaks down if a single subroutine 
  has more than about 10MB of code in it ... as long as humans write
  code, this shouldn't be a problem ...
 */

void
check_label_emit ()
{
  if (mvs_need_base_reload)
    {
      mvs_need_base_reload = 0;

      mvs_page_code += 4;
      fprintf (assembler_source, "\tL\t%d,%d(,%d)\n",
	  BASE_REGISTER, (mvs_page_num - function_base_page) * 4,
	  PAGE_REGISTER);
    }
}

/* Add the label to the current page label list.  If a free element is available
   it will be used for the new label.  Otherwise, a label element will be
   allocated from memory.
   ID is the label number of the label being added to the list.  */

static label_node_t *
mvs_get_label (id)
     int id;
{
  label_node_t *lp;

  /* first, lets see if we already go one, if so, use that.  */
  for (lp = label_anchor; lp; lp = lp->label_next)
    {
      if (lp->label_id == id) return lp;
    }

  /* not found, get a new one */
  if (free_anchor)
    {
      lp = free_anchor;
      free_anchor = lp->label_next;
    }
  else
    {
      lp = (label_node_t *) xmalloc (sizeof (label_node_t));
    }

  /* initialize for new label */
  lp->label_id = id;
  lp->label_page = -1;
  lp->label_next = label_anchor;
  lp->label_first_ref = 2000123123;
  lp->label_last_ref = -1;
  lp->label_addr = -1;
  lp->first_ref_page = -1;
  label_anchor = lp;

  return lp;
}

void
mvs_add_label (id)
     int id;
{
  label_node_t *lp;
  int fwd_distance;

  lp = mvs_get_label (id);
  lp->label_page = mvs_page_num;

  /* OK, we just saw the label.  Determine if this label
   * needs a reload of the base register */
  if ((-1 != lp->first_ref_page) && 
      (lp->first_ref_page != mvs_page_num)) 
    {
      /* Yep; the first label_ref was on a different page.  */
      mvs_need_base_reload ++;
      return;
    }

  /* Hmm.  Try to see if the estimated address of the last
     label_ref is on the current page.  If it is, then we
     don't need a base reg reload.  Note that this estimate
     is very conservatively handled; we'll tend to have 
     a good bit more reloads than actually needed.  Someday,
     we should tighten the estimates (which are driven by
     the (set_att "length") insn attibute.
    
     Currently, we estimate that number of page literals 
     same as number of insns, which is a vast overestimate,
     esp that the estimate of each insn size is its max size.  */

  /* if latest ref comes before label, we are clear */
  if (lp->label_last_ref < lp->label_addr) return;

  fwd_distance = lp->label_last_ref - lp->label_addr;

  if (mvs_page_code + 2 * fwd_distance + mvs_page_lit < 4060) return;

  mvs_need_base_reload ++;
}

/* Check to see if the label is in the list and in the current
   page.  If not found, we have to make worst case assumption 
   that label will be on a different page, and thus will have to
   generate a load and branch on register.  This is rather
   ugly for forward-jumps, but what can we do? For backward
   jumps on the same page we can branch directly to address.
   ID is the label number of the label being checked.  */

int
mvs_check_label (id)
     int id;
{
  label_node_t *lp;

  for (lp = label_anchor; lp; lp = lp->label_next)
    {
      if (lp->label_id == id) 
        {
          if (lp->label_page == mvs_page_num) 
            {
               return 1;
            } 
          else 
            {
	       return 0;
            } 
        }
    }
  return 0;
}

/* Get the page on which the label sits.  This will be used to 
   determine is a register reload is really needed.  */

#if 0
int
mvs_get_label_page(int id)
{
  label_node_t *lp;

  for (lp = label_anchor; lp; lp = lp->label_next)
    {
      if (lp->label_id == id)
	return lp->label_page;
    }
  return -1;
}
#endif

/* The label list for the current page freed by linking the list onto the free
   label element chain.  */

void
mvs_free_label_list ()
{

  if (label_anchor)
    {
      label_node_t *last_lp = label_anchor;
      while (last_lp->label_next) last_lp = last_lp->label_next;
      last_lp->label_next = free_anchor;
      free_anchor = label_anchor;
    }
  label_anchor = 0;
}

/* ====================================================================== */
/* If the page size limit is reached a new code page is started, and the base
   register is set to it.  This page break point is counted conservatively,
   most literals that have the same value are collapsed by the assembler.
   True is returned when a new page is started.
   FILE is the assembler output file descriptor.
   CODE is the length, in bytes, of the instruction to be emitted.
   LIT is the length of the literal to be emitted.  */

#ifdef TARGET_HLASM
int
mvs_check_page (file, code, lit)
     FILE *file;
     int code, lit;
{
  if (file)
    assembler_source = file;

  if (mvs_page_code + code + mvs_page_lit + lit > MAX_MVS_PAGE_LENGTH)
    {
      fprintf (assembler_source, "\tB\tPGE%d\n", mvs_page_num);
      fprintf (assembler_source, "\tDS\t0F\n");
      fprintf (assembler_source, "\tLTORG\n");
      fprintf (assembler_source, "\tDS\t0F\n");
      fprintf (assembler_source, "PGE%d\tEQU\t*\n", mvs_page_num);
      fprintf (assembler_source, "\tDROP\t%d\n", BASE_REGISTER);
      mvs_page_num++;
      /* Safe to use BASR not BALR, since we are
       * not switching addressing mode here ...  */
      fprintf (assembler_source, "\tBASR\t%d,0\n", BASE_REGISTER);
      fprintf (assembler_source, "PG%d\tEQU\t*\n", mvs_page_num);
      fprintf (assembler_source, "\tUSING\t*,%d\n", BASE_REGISTER);
      mvs_page_code = code;
      mvs_page_lit = lit;
      return 1;
    }
  mvs_page_code += code;
  mvs_page_lit += lit;
  return 0;
}
#endif /* TARGET_HLASM */


#ifdef TARGET_ELF_ABI
int
mvs_check_page (file, code, lit)
     FILE *file;
     int code, lit;
{
  if (file)
    assembler_source = file;

  if (mvs_page_code + code + mvs_page_lit + lit > MAX_MVS_PAGE_LENGTH)
    {
      /* hop past the literal pool */
      fprintf (assembler_source, "\tB\t.LPGE%d\n", mvs_page_num);

      /* dump the literal pool. The .baligns are optional, since 
       * ltorg will align to the size of the largest literal 
       * (which is possibly 8 bytes) */
      fprintf (assembler_source, "\t.balign\t4\n");
      fprintf (assembler_source, "\t.LTORG\n");
      fprintf (assembler_source, "\t.balign\t4\n");

      /* we continue execution here ...  */
      fprintf (assembler_source, ".LPGE%d:\n", mvs_page_num);
      fprintf (assembler_source, "\t.DROP\t%d\n", BASE_REGISTER);
      mvs_page_num++;

      /* BASR puts the contents of the PSW into r3
       * that is, r3 will be loaded with the address of "." */
      fprintf (assembler_source, "\tBASR\tr%d,0\n", BASE_REGISTER);
      fprintf (assembler_source, ".LPG%d:\n", mvs_page_num);
      fprintf (assembler_source, "\t.USING\t.,r%d\n", BASE_REGISTER);
      mvs_page_code = code;
      mvs_page_lit = lit;
      return 1;
    }
  mvs_page_code += code;
  mvs_page_lit += lit;
  return 0;
}
#endif /* TARGET_ELF_ABI */

/* ===================================================== */
/* defines and functions specific to the HLASM assembler */
#ifdef TARGET_HLASM

/* Check for C/370 runtime function, they don't use standard calling
   conventions.  True is returned if the function is in the table.
   NAME is the name of the current function.  */

int
mvs_function_check (name)
     const char *name;
{
  int lower, middle, upper;
  int i;

  lower = 0;
  upper = MVS_FUNCTION_TABLE_LENGTH - 1;
  while (lower <= upper)
    {
      middle = (lower + upper) / 2;
      i = strcmp (name, mvs_function_table[middle]);
      if (i == 0)
	return 1;
      if (i < 0)
	upper = middle - 1;
      else
	lower = middle + 1;
    }
  return 0;
}

/* Generate a hash for a given key.  */

#ifdef LONGEXTERNAL
static int
mvs_hash_alias (key)
     const char *key;
{
  int h;
  int i;
  int l = strlen (key);

  h = key[0];
  for (i = 1; i < l; i++)
    h = ((h * MVS_SET_SIZE) + key[i]) % MVS_HASH_PRIME;
  return (h);
}
#endif

/* Add the alias to the current alias list.  */

void
mvs_add_alias (realname, aliasname, emitted)
     const char *realname;
     const char *aliasname;
     int   emitted;
{
  alias_node_t *ap;

  ap = (alias_node_t *) xmalloc (sizeof (alias_node_t));
  if (strlen (realname) > MAX_LONG_LABEL_SIZE)
    {
      warning ("real name is too long - alias ignored");
      return;
    }
  if (strlen (aliasname) > MAX_MVS_LABEL_SIZE)
    {
      warning ("alias name is too long - alias ignored");
      return;
    }
      
  strcpy (ap->real_name, realname);
  strcpy (ap->alias_name, aliasname);
  ap->alias_emitted = emitted;
  ap->alias_next = alias_anchor;
  alias_anchor = ap;
}

/* Check to see if the name needs aliasing. ie. the name is either:
     1. Longer than 8 characters
     2. Contains an underscore
     3. Is mixed case */

int
mvs_need_alias (realname)
      const char *realname;
{
   int i, j = strlen (realname);

   if (mvs_function_check (realname))
     return 0;
#if 0
   if (!strcmp (realname, "gccmain"))
     return 0;
   if (!strcmp (realname, "main"))
     return 0;
#endif
   if (j > MAX_MVS_LABEL_SIZE)
     return 1;
   if (strchr (realname, '_') != 0)
     return 1;
   if (ISUPPER (realname[0]))
     {
       for (i = 1; i < j; i++)
	 {
	   if (ISLOWER (realname[i]))
	     return 1;
	 }
     }
   else
     {
       for (i = 1; i < j; i++)
         {
	   if (ISUPPER (realname[i]))
	     return 1;
	 }
     }

   return 0;
}

/* Get the alias from the list. 
   If 1 is returned then it's in the alias list, 0 if it was not */

int
mvs_get_alias (realname, aliasname)
     const char *realname;
     char *aliasname;
{
#ifdef LONGEXTERNAL
  alias_node_t *ap;

  for (ap = alias_anchor; ap; ap = ap->alias_next)
    {
      if (!strcmp (ap->real_name, realname))
	{
	  strcpy (aliasname, ap->alias_name);
	  return 1;
	}
    }
  if (mvs_need_alias (realname))
    {
      char c1, c2;

      c1 = realname[0];
      c2 = realname[1];
      if (ISLOWER (c1)) c1 = TOUPPER (c1);
      else if (c1 == '_') c1 = 'A';
      if (ISLOWER (c2)) c2 = TOUPPER (c2);
      else if (c2 == '_' || c2 == '\0') c2 = '#';

      sprintf (aliasname, "%c%c%06d", c1, c2, mvs_hash_alias (realname));
      mvs_add_alias (realname, aliasname, 0);
      return 1;
    }
#else
  if (strlen (realname) > MAX_MVS_LABEL_SIZE)
    {
      strncpy (aliasname, realname, MAX_MVS_LABEL_SIZE);
      aliasname[MAX_MVS_LABEL_SIZE] = '\0';
      return 1;
    }
#endif
  return 0;
}

/* Check to see if the alias is in the list. 
   If 1 is returned then it's in the alias list, 2 it was emitted  */

int
mvs_check_alias (realname, aliasname)
     const char *realname;
     char *aliasname;
{
#ifdef LONGEXTERNAL
  alias_node_t *ap;

  for (ap = alias_anchor; ap; ap = ap->alias_next)
    {
      if (!strcmp (ap->real_name, realname))
	{
	  int rc = (ap->alias_emitted == 1) ? 1 : 2; 
	  strcpy (aliasname, ap->alias_name);
	  ap->alias_emitted = 1; 
	  return rc;
	}
    }
  if (mvs_need_alias (realname))
    {
      char c1, c2;

      c1 = realname[0];
      c2 = realname[1];
      if (ISLOWER (c1)) c1 = TOUPPER (c1);
      else if (c1 == '_') c1 = 'A';
      if (ISLOWER (c2)) c2 = TOUPPER (c2);
      else if (c2 == '_' || c2 == '\0') c2 = '#';

      sprintf (aliasname, "%c%c%06d", c1, c2, mvs_hash_alias (realname));
      mvs_add_alias (realname, aliasname, 0);
      alias_anchor->alias_emitted = 1;
      return 2;
    }
#else
  if (strlen (realname) > MAX_MVS_LABEL_SIZE)
    {
      strncpy (aliasname, realname, MAX_MVS_LABEL_SIZE);
      aliasname[MAX_MVS_LABEL_SIZE] = '\0';
      return 1;
    }
#endif
  return 0;
}

/* defines and functions specific to the HLASM assembler */
#endif /* TARGET_HLASM */
/* ===================================================== */
/* ===================================================== */
/* defines and functions specific to the gas assembler */
#ifdef TARGET_ELF_ABI

/* Check for C/370 runtime function, they don't use standard calling
   conventions.  True is returned if the function is in the table.
   NAME is the name of the current function.  */
/* no special calling conventions (yet ??) */

int
mvs_function_check (name)
     const char *name ATTRIBUTE_UNUSED;
{
   return 0;
}

#endif /* TARGET_ELF_ABI */
/* ===================================================== */


/* Return 1 if OP is a valid S operand for an RS, SI or SS type instruction.
   OP is the current operation.
   MODE is the current operation mode.  */

int
s_operand (op, mode)
     register rtx op;
     enum machine_mode mode;
{
  extern int volatile_ok;
  register enum rtx_code code = GET_CODE (op);

  if (CONSTANT_ADDRESS_P (op))
    return 1;
  if (mode == VOIDmode || GET_MODE (op) != mode)
    return 0;
  if (code == MEM)
    {
      register rtx x = XEXP (op, 0);

      if (!volatile_ok && op->volatil)
	return 0;
      if (REG_P (x) && REG_OK_FOR_BASE_P (x))
	return 1;
      if (GET_CODE (x) == PLUS
	  && REG_P (XEXP (x, 0)) && REG_OK_FOR_BASE_P (XEXP (x, 0))
	  && GET_CODE (XEXP (x, 1)) == CONST_INT
	  && (unsigned) INTVAL (XEXP (x, 1)) < 4096)
	return 1;
    }
  return 0;
}


/* Return 1 if OP is a valid R or S operand for an RS, SI or SS type
   instruction.
   OP is the current operation.
   MODE is the current operation mode.  */

int
r_or_s_operand (op, mode)
     register rtx op;
     enum machine_mode mode;
{
  extern int volatile_ok;
  register enum rtx_code code = GET_CODE (op);

  if (CONSTANT_ADDRESS_P (op))
    return 1;
  if (mode == VOIDmode || GET_MODE (op) != mode)
    return 0;
  if (code == REG)
    return 1;
  else if (code == MEM)
    {
      register rtx x = XEXP (op, 0);

      if (!volatile_ok && op->volatil)
	return 0;
      if (REG_P (x) && REG_OK_FOR_BASE_P (x))
	return 1;
      if (GET_CODE (x) == PLUS
	  && REG_P (XEXP (x, 0)) && REG_OK_FOR_BASE_P (XEXP (x, 0))
	  && GET_CODE (XEXP (x, 1)) == CONST_INT
	  && (unsigned) INTVAL (XEXP (x, 1)) < 4096)
	return 1;
    }
  return 0;
}


/* Some remarks about unsigned_jump_follows_p():
   gcc is built around the assumption that branches are signed
   or unsigned, whereas the 370 doesn't care; its the compares that
   are signed or unsigned.  Thus, we need to somehow know if we
   need to do a signed or an unsigned compare, and we do this by 
   looking ahead in the instruction sequence until we find a jump.
   We then note whether this jump is signed or unsigned, and do the 
   compare appropriately.  Note that we have to scan ahead indefinitley,
   as the gcc optimizer may insert any number of instructions between 
   the compare and the jump.
  
   Note that using conditional branch expanders seems to be be a more 
   elegant/correct way of doing this.   See, for instance, the Alpha 
   cmpdi and bgt patterns.  Note also that for the i370, various
   arithmetic insn's set the condition code as well.

   The unsigned_jump_follows_p() routine  returns a 1 if the next jump 
   is unsigned.  INSN is the current instruction.  */

int
unsigned_jump_follows_p (insn)
     register rtx insn;
{
  rtx orig_insn = insn;
  while (1) 
    {
      register rtx tmp_insn;
      enum rtx_code coda;
  
      insn = NEXT_INSN (insn);
      if (!insn) fatal_insn ("internal error--no jump follows compare:", orig_insn);
  
      if (GET_CODE (insn) != JUMP_INSN) continue;
    
      tmp_insn = XEXP (insn, 3);
      if (GET_CODE (tmp_insn) != SET) continue;
    
      if (GET_CODE (XEXP (tmp_insn, 0)) != PC) continue;
    
      tmp_insn = XEXP (tmp_insn, 1);
      if (GET_CODE (tmp_insn) != IF_THEN_ELSE) continue;
    
      /* if we got to here, this instruction is a jump.  Is it signed? */
      tmp_insn = XEXP (tmp_insn, 0);
      coda = GET_CODE (tmp_insn);
  
      return coda != GE && coda != GT && coda != LE && coda != LT;
    }
}

#ifdef TARGET_HLASM

/* Target hook for assembling integer objects.  This version handles all
   objects when TARGET_HLASM is defined.  */

static bool
i370_hlasm_assemble_integer (x, size, aligned_p)
     rtx x;
     unsigned int size;
     int aligned_p;
{
  const char *int_format = NULL;

  if (aligned_p)
    switch (size)
      {
      case 1:
	int_format = "\tDC\tX'%02X'\n";
	break;

      case 2:
	int_format = "\tDC\tX'%04X'\n";
	break;

      case 4:
	if (GET_CODE (x) == CONST_INT)
	  {
	    fputs ("\tDC\tF'", asm_out_file);
	    output_addr_const (asm_out_file, x);
	    fputs ("'\n", asm_out_file);
	  }
	else
	  {
	    fputs ("\tDC\tA(", asm_out_file);
	    output_addr_const (asm_out_file, x);
	    fputs (")\n", asm_out_file);
	  }
	return true;
      }

  if (int_format && GET_CODE (x) == CONST_INT)
    {
      fprintf (asm_out_file, int_format, INTVAL (x));
      return true;
    }
  return default_assemble_integer (x, size, aligned_p);
}

/* Generate the assembly code for function entry.  FILE is a stdio
   stream to output the code to.  SIZE is an int: how many units of
   temporary storage to allocate.

   Refer to the array `regs_ever_live' to determine which registers to
   save; `regs_ever_live[I]' is nonzero if register number I is ever
   used in the function.  This function is responsible for knowing
   which registers should not be saved even if used.  */

static void
i370_output_function_prologue (f, l)
     FILE *f;
     HOST_WIDE_INT l;
{
#if MACROPROLOGUE == 1
  fprintf (f, "* Function %s prologue\n", mvs_function_name);
  fprintf (f, "\tEDCPRLG USRDSAL=%d,BASEREG=%d\n",
	   STACK_POINTER_OFFSET + l - 120 +
	   current_function_outgoing_args_size, BASE_REGISTER);
#else /* MACROPROLOGUE != 1 */
  static int function_label_index = 1;
  static int function_first = 0;
  static int function_year, function_month, function_day;
  static int function_hour, function_minute, function_second;
#if defined(LE370)
  if (!function_first)
    {
      struct tm *function_time;
      time_t lcltime;
      time (&lcltime);
      function_time = localtime (&lcltime);
      function_year = function_time->tm_year + 1900;
      function_month = function_time->tm_mon + 1;
      function_day = function_time->tm_mday;
      function_hour = function_time->tm_hour;
      function_minute = function_time->tm_min;
      function_second = function_time->tm_sec;
    }
  fprintf (f, "* Function %s prologue\n", mvs_function_name);
  fprintf (f, "FDSE%03d\tDSECT\n", function_label_index);
  fprintf (f, "\tDS\tD\n");
  fprintf (f, "\tDS\tCL(" HOST_WIDE_INT_PRINT_DEC ")\n",
	   STACK_POINTER_OFFSET + l
	   + current_function_outgoing_args_size);
  fprintf (f, "\tORG\tFDSE%03d\n", function_label_index);
  fprintf (f, "\tDS\tCL(120+8)\n");
  fprintf (f, "\tORG\n");
  fprintf (f, "\tDS\t0D\n");
  fprintf (f, "FDSL%03d\tEQU\t*-FDSE%03d-8\n", function_label_index,
	   function_label_index);
  fprintf (f, "\tDS\t0H\n");
  assemble_name (f, mvs_function_name);
  fprintf (f, "\tCSECT\n");
  fprintf (f, "\tUSING\t*,15\n");
  fprintf (f, "\tB\tFENT%03d\n", function_label_index);
  fprintf (f, "\tDC\tAL1(FNAM%03d+4-*)\n", function_label_index);
  fprintf (f, "\tDC\tX'CE',X'A0',AL1(16)\n");
  fprintf (f, "\tDC\tAL4(FPPA%03d)\n", function_label_index);
  fprintf (f, "\tDC\tAL4(0)\n");
  fprintf (f, "\tDC\tAL4(FDSL%03d)\n", function_label_index);
  fprintf (f, "FNAM%03d\tEQU\t*\n", function_label_index);
  fprintf (f, "\tDC\tAL2(%d),C'%s'\n", strlen (mvs_function_name),
	mvs_function_name);
  fprintf (f, "FPPA%03d\tDS\t0F\n", function_label_index);
  fprintf (f, "\tDC\tX'03',X'00',X'33',X'00'\n");
  fprintf (f, "\tDC\tV(CEESTART)\n");
  fprintf (f, "\tDC\tAL4(0)\n");
  fprintf (f, "\tDC\tAL4(FTIM%03d)\n", function_label_index);
  fprintf (f, "FTIM%03d\tDS\t0F\n", function_label_index);
  fprintf (f, "\tDC\tCL4'%d',CL4'%02d%02d',CL6'%02d%02d00'\n",
  		 function_year, function_month, function_day,
    		 function_hour, function_minute);
  fprintf (f, "\tDC\tCL2'01',CL4'0100'\n");
  fprintf (f, "FENT%03d\tDS\t0H\n", function_label_index);
  fprintf (f, "\tSTM\t14,12,12(13)\n");
  fprintf (f, "\tL\t2,76(,13)\n");
  fprintf (f, "\tL\t0,16(,15)\n");
  fprintf (f, "\tALR\t0,2\n");
  fprintf (f, "\tCL\t0,12(,12)\n");
  fprintf (f, "\tBNH\t*+10\n");
  fprintf (f, "\tL\t15,116(,12)\n");
  fprintf (f, "\tBALR\t14,15\n");
  fprintf (f, "\tL\t15,72(,13)\n");
  fprintf (f, "\tSTM\t15,0,72(2)\n");
  fprintf (f, "\tMVI\t0(2),X'10'\n");
  fprintf (f, "\tST\t2,8(,13)\n ");
  fprintf (f, "\tST\t13,4(,2)\n ");
  fprintf (f, "\tLR\t13,2\n");
  fprintf (f, "\tDROP\t15\n");
  fprintf (f, "\tBALR\t%d,0\n", BASE_REGISTER);
  fprintf (f, "\tUSING\t*,%d\n", BASE_REGISTER);
  function_first = 1;
  function_label_index ++;
#else /* !LE370 */
  if (!function_first)
    {
      struct tm *function_time;
      time_t lcltime;
      time (&lcltime);
      function_time = localtime (&lcltime);
      function_year = function_time->tm_year + 1900;
      function_month = function_time->tm_mon + 1;
      function_day = function_time->tm_mday;
      function_hour = function_time->tm_hour;
      function_minute = function_time->tm_min;
      function_second = function_time->tm_sec;
      fprintf (f, "PPA2\tDS\t0F\n");
      fprintf (f, "\tDC\tX'03',X'00',X'33',X'00'\n");
      fprintf (f, "\tDC\tV(CEESTART),A(0)\n");
      fprintf (f, "\tDC\tA(CEETIMES)\n");
      fprintf (f, "CEETIMES\tDS\t0F\n");
      fprintf (f, "\tDC\tCL4'%d',CL4'%02d%02d',CL6'%02d%02d00'\n",
    		 function_year, function_month, function_day,
    		 function_hour, function_minute, function_second);
      fprintf (f, "\tDC\tCL2'01',CL4'0100'\n");
    }
  fprintf (f, "* Function %s prologue\n", mvs_function_name);
  fprintf (f, "FDSD%03d\tDSECT\n", function_label_index);
  fprintf (f, "\tDS\tD\n");
  fprintf (f, "\tDS\tCL(%d)\n", STACK_POINTER_OFFSET + l
			+ current_function_outgoing_args_size);
  fprintf (f, "\tORG\tFDSD%03d\n", function_label_index);
  fprintf (f, "\tDS\tCL(120+8)\n");
  fprintf (f, "\tORG\n");
  fprintf (f, "\tDS\t0D\n");
  fprintf (f, "FDSL%03d\tEQU\t*-FDSD%03d-8\n", function_label_index,
	   function_label_index);
  fprintf (f, "\tDS\t0H\n");
  assemble_name (f, mvs_function_name);
  fprintf (f, "\tCSECT\n");
  fprintf (f, "\tUSING\t*,15\n");
  fprintf (f, "\tB\tFPL%03d\n", function_label_index);
  fprintf (f, "\tDC\tAL1(FPL%03d+4-*)\n", function_label_index + 1);
  fprintf (f, "\tDC\tX'CE',X'A0',AL1(16)\n");
  fprintf (f, "\tDC\tAL4(PPA2)\n");
  fprintf (f, "\tDC\tAL4(0)\n");
  fprintf (f, "\tDC\tAL4(FDSL%03d)\n", function_label_index);
  fprintf (f, "FPL%03d\tEQU\t*\n", function_label_index + 1);
  fprintf (f, "\tDC\tAL2(%d),C'%s'\n", strlen (mvs_function_name),
	mvs_function_name);
  fprintf (f, "FPL%03d\tDS\t0H\n", function_label_index);
  fprintf (f, "\tSTM\t14,12,12(13)\n");
  fprintf (f, "\tL\t2,76(,13)\n");
  fprintf (f, "\tL\t0,16(,15)\n");
  fprintf (f, "\tALR\t0,2\n");
  fprintf (f, "\tCL\t0,12(,12)\n");
  fprintf (f, "\tBNH\t*+10\n");
  fprintf (f, "\tL\t15,116(,12)\n");
  fprintf (f, "\tBALR\t14,15\n");
  fprintf (f, "\tL\t15,72(,13)\n");
  fprintf (f, "\tSTM\t15,0,72(2)\n");
  fprintf (f, "\tMVI\t0(2),X'10'\n");
  fprintf (f, "\tST\t2,8(,13)\n ");
  fprintf (f, "\tST\t13,4(,2)\n ");
  fprintf (f, "\tLR\t13,2\n");
  fprintf (f, "\tDROP\t15\n");
  fprintf (f, "\tBALR\t%d,0\n", BASE_REGISTER);
  fprintf (f, "\tUSING\t*,%d\n", BASE_REGISTER);
  function_first = 1;
  function_label_index += 2;
#endif /* !LE370 */
#endif /* MACROPROLOGUE */
  fprintf (f, "PG%d\tEQU\t*\n", mvs_page_num );
  fprintf (f, "\tLR\t11,1\n"); 
  fprintf (f, "\tL\t%d,=A(PGT%d)\n", PAGE_REGISTER, mvs_page_num);
  fprintf (f, "* Function %s code\n", mvs_function_name);

  mvs_free_label_list ();
  mvs_page_code = 6;
  mvs_page_lit = 4;
  mvs_check_page (f, 0, 0);
  function_base_page = mvs_page_num;

  /* find all labels in this routine */
  i370_label_scan ();
}

static void
i370_globalize_label (stream, name)
     FILE *stream;
     const char *name;
{
  char temp[MAX_MVS_LABEL_SIZE + 1];
  if (mvs_check_alias (name, temp) == 2)
    fprintf (stream, "%s\tALIAS\tC'%s'\n", temp, name);
  fputs ("\tENTRY\t", stream);
  assemble_name (stream, name);
  putc ('\n', stream);
}
#endif /* TARGET_HLASM */


#ifdef TARGET_ELF_ABI
/*
   The 370_function_prolog() routine generates the current ELF ABI ES/390 prolog.
   It implements a stack that grows downward. 
   It performs the following steps:
   -- saves the callers non-volatile registers on the callers stack.
   -- subtracts stackframe size from the stack pointer.
   -- stores backpointer to old caller stack.
  
   XXX hack alert -- if the global var int leaf_function is nonzero, 
   then this is a leaf, and it might be possible to optimize the prologue
   into doing even less, e.g. not grabbing a new stackframe or maybe just a
   partial stack frame.
  
   XXX hack alert -- the current stack frame is bloated into twice the 
   needed size by unused entries. These entries make it marginally 
   compatible with MVS/OE/USS C environment, but really they're not used
   and could probably chopped out. Modifications to i370.md would be needed
   also, to quite using addresses 136, 140, etc.
 */

static void
i370_output_function_prologue (f, frame_size)
     FILE *f;
     HOST_WIDE_INT frame_size;
{
  static int function_label_index = 1;
  static int function_first = 0;
  int stackframe_size, aligned_size;

  fprintf (f, "# Function prologue\n");
  /* define the stack, put it into its own data segment
     FDSE == Function Stack Entry
     FDSL == Function Stack Length */
  stackframe_size = 
     STACK_POINTER_OFFSET + current_function_outgoing_args_size + frame_size;
  aligned_size = (stackframe_size + 7) >> 3;
  aligned_size <<= 3;
  
  fprintf (f, "# arg_size=0x%x frame_size=" HOST_WIDE_INT_PRINT_HEX
	   " aligned size=0x%x\n", 
     current_function_outgoing_args_size, frame_size, aligned_size);

  fprintf (f, "\t.using\t.,r15\n");

  /* Branch to exectuable part of prologue.  */
  fprintf (f, "\tB\t.LFENT%03d\n", function_label_index);

  /* write the length of the stackframe */
  fprintf (f, "\t.long\t%d\n", aligned_size);

  /* FENT == function prologue entry */
  fprintf (f, "\t.balign 2\n.LFENT%03d:\n",
              function_label_index);

  /* store multiple registers 14,15,0,...12 at 12 bytes from sp */
  fprintf (f, "\tSTM\tr14,r12,12(sp)\n");

  /* r3 == saved callee stack pointer */
  fprintf (f, "\tLR\tr3,sp\n");

  /* 4(r15) == stackframe size */
  fprintf (f, "\tSL\tsp,4(,r15)\n");

  /* r11 points to arg list in callers stackframe; was passed in r2 */
  fprintf (f, "\tLR\tr11,r2\n");

  /* store callee stack pointer at 8(sp) */
  /* fprintf (f, "\tST\tsp,8(,r3)\n ");  wasted cycles, no one uses this ...  */

  /* backchain -- store caller sp at 4(callee_sp)  */
  fprintf (f, "\tST\tr3,4(,sp)\n ");

  fprintf (f, "\t.drop\tr15\n");
  /* Place contents of the PSW into r3
     that is, place the address of "." into r3 */
  fprintf (f, "\tBASR\tr%d,0\n", BASE_REGISTER);
  fprintf (f, "\t.using\t.,r%d\n", BASE_REGISTER);
  function_first = 1;
  function_label_index ++;

  fprintf (f, ".LPG%d:\n", mvs_page_num  );
  fprintf (f, "\tL\tr%d,=A(.LPGT%d)\n", PAGE_REGISTER, mvs_page_num);
  fprintf (f, "# Function code\n");

  mvs_free_label_list ();
  mvs_page_code = 6;
  mvs_page_lit = 4;
  mvs_check_page (f, 0, 0);
  function_base_page = mvs_page_num;

  /* find all labels in this routine */
  i370_label_scan ();
}
#endif /* TARGET_ELF_ABI */

/* This function generates the assembly code for function exit.
   Args are as for output_function_prologue ().

   The function epilogue should not depend on the current stack
   pointer!  It should use the frame pointer only.  This is mandatory
   because of alloca; we also take advantage of it to omit stack
   adjustments before returning.  */

static void
i370_output_function_epilogue (file, l)
     FILE *file;
     HOST_WIDE_INT l ATTRIBUTE_UNUSED;
{
  int i;

  check_label_emit ();
  mvs_check_page (file, 14, 0);
  fprintf (file, "* Function %s epilogue\n", mvs_function_name);
  mvs_page_num++;

#if MACROEPILOGUE == 1
  fprintf (file, "\tEDCEPIL\n");
#else /* MACROEPILOGUE != 1 */
  fprintf (file, "\tL\t13,4(,13)\n");
  fprintf (file, "\tL\t14,12(,13)\n");
  fprintf (file, "\tLM\t2,12,28(13)\n");
  fprintf (file, "\tBALR\t1,14\n");
  fprintf (file, "\tDC\tA(");
  assemble_name (file, mvs_function_name);
  fprintf (file, ")\n" );
#endif /* MACROEPILOGUE */

  fprintf (file, "* Function %s literal pool\n", mvs_function_name);
  fprintf (file, "\tDS\t0F\n" );
  fprintf (file, "\tLTORG\n");
  fprintf (file, "* Function %s page table\n", mvs_function_name);
  fprintf (file, "\tDS\t0F\n");
  fprintf (file, "PGT%d\tEQU\t*\n", function_base_page);

  mvs_free_label_list();
  for (i = function_base_page; i < mvs_page_num; i++)
    fprintf (file, "\tDC\tA(PG%d)\n", i);
}

static void
i370_file_start ()
{
  fputs ("\tRMODE\tANY\n\tCSECT\n", asm_out_file);
}

static void
i370_file_end ()
{
  fputs ("\tEND\n", asm_out_file);
}

static void
i370_internal_label (stream, prefix, labelno)
     FILE *stream;
     const char *prefix;
     unsigned long labelno;
{
  if (!strcmp (prefix, "L"))
    mvs_add_label(labelno);

  default_internal_label (stream, prefix, labelno);
}

static bool
i370_rtx_costs (x, code, outer_code, total)
     rtx x;
     int code;
     int outer_code ATTRIBUTE_UNUSED;
     int *total;
{
  switch (code)
    {
    case CONST_INT:
      if ((unsigned HOST_WIDE_INT) INTVAL (x) < 0xfff)
	{
	  *total = 1;
	  return true;
	}
      /* FALLTHRU */

    case CONST:
    case LABEL_REF:
    case SYMBOL_REF:
      *total = 2;
      return true;

    case CONST_DOUBLE:
      *total = 4;
      return true;

    default:
      return false;
    }
}
