diff options
Diffstat (limited to 'gcc/config/i370/i370.c')
-rw-r--r-- | gcc/config/i370/i370.c | 1514 |
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; + } +} |