summaryrefslogtreecommitdiffstats
path: root/gcc/config/i370/i370.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/i370/i370.c')
-rw-r--r--gcc/config/i370/i370.c1514
1 files changed, 1514 insertions, 0 deletions
diff --git a/gcc/config/i370/i370.c b/gcc/config/i370/i370.c
new file mode 100644
index 00000000000..2cfe4fe3269
--- /dev/null
+++ b/gcc/config/i370/i370.c
@@ -0,0 +1,1514 @@
+/* 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;
+ }
+}
OpenPOWER on IntegriCloud