diff options
author | rth <rth@138bc75d-0d04-0410-961f-82ee72b054a4> | 1999-07-19 19:26:30 +0000 |
---|---|---|
committer | rth <rth@138bc75d-0d04-0410-961f-82ee72b054a4> | 1999-07-19 19:26:30 +0000 |
commit | 02b5b1dc5f89971c3ed183d8ca09f2146423006b (patch) | |
tree | 6ffd326f133aefd3b97453e6d03f49a2989ee9c2 /gcc/config/i370/i370.c | |
parent | 9a094c7f65feb6b02fde8d337eb2d34ba91d0394 (diff) | |
download | gcc-02b5b1dc5f89971c3ed183d8ca09f2146423006b.tar.gz |
Bulk patch from Linas.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@28178 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/config/i370/i370.c')
-rw-r--r-- | gcc/config/i370/i370.c | 1052 |
1 files changed, 980 insertions, 72 deletions
diff --git a/gcc/config/i370/i370.c b/gcc/config/i370/i370.c index 55189825540..8287c92e09f 100644 --- a/gcc/config/i370/i370.c +++ b/gcc/config/i370/i370.c @@ -1,7 +1,8 @@ /* Subroutines for insn-output.c for System/370. Copyright (C) 1989, 1993, 1995, 1997 Free Software Foundation, Inc. Contributed by Jan Stein (jan@cd.chalmers.se). - Modified for MVS C/370 by Dave Pitts (dpitts@nyx.cs.du.edu) + 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 GNU CC. @@ -24,7 +25,11 @@ Boston, MA 02111-1307, USA. */ #include <stdio.h> #include <string.h> #include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> #include "rtl.h" +#include "tree.h" #include "regs.h" #include "hard-reg-set.h" #include "real.h" @@ -35,26 +40,34 @@ Boston, MA 02111-1307, USA. */ #include "insn-attr.h" #include "flags.h" #include "recog.h" -#ifdef sun -#include <sys/types.h> -#include <ctype.h> -#endif #include <time.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. */ -/* Label node, this structure is used to keep track of labels on the - current page. */ 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_label_emitted = 0; +/* 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; @@ -83,6 +96,37 @@ static label_node_t *free_anchor = 0; /* Assembler source file descriptor. */ static FILE *assembler_source = 0; +label_node_t * mvs_get_label (); + +/* ===================================================== */ +/* defines and functions specific to the HLASM assembler */ +#ifdef TARGET_HLASM + +#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; + +/* Alias number */ +static alias_number = 0; + /* Define the length of the internal MVS function table. */ #define MVS_FUNCTION_TABLE_LENGTH 32 @@ -90,16 +134,27 @@ static FILE *assembler_source = 0; and must handled in a special manner. */ static char *mvs_function_table[MVS_FUNCTION_TABLE_LENGTH] = { +#if defined(HOST_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 */ +/* ===================================================== */ + /* ASCII to EBCDIC conversion table. */ -#if defined(TARGET_EBCDIC) && !defined(HOST_EBCDIC) static unsigned char ascebc[256] = { /*00 NL SH SX EX ET NQ AK BL */ @@ -151,10 +206,8 @@ static unsigned char ascebc[256] = 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0xFF }; -#endif /* EBCDIC to ASCII conversion table. */ -#if defined(HOST_EBCDIC) && !defined(TARGET_EBCDIC) unsigned char ebcasc[256] = { /*00 NU SH SX EX PF HT LC DL */ @@ -222,7 +275,6 @@ unsigned char ebcasc[256] = /*F8 8 9 */ 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }; -#endif /* Map characters from one character set to another. C is the character to be translated. */ @@ -232,26 +284,278 @@ mvs_map_char (c) char c; { #if defined(TARGET_EBCDIC) && !defined(HOST_EBCDIC) + fprintf (stderr, "mvs_map_char: TE & !HE: c = %02x\n", c); return ascebc[c]; #else #if defined(HOST_EBCDIC) && !defined(TARGET_EBCDIC) + fprintf (stderr, "mvs_map_char: !TE & HE: c = %02x\n", c); return ebcasc[c]; #else + fprintf (stderr, "mvs_map_char: !TE & !HE: c = %02x\n", c); return c; #endif #endif } +/* ===================================================== */ +/* 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, thenm we can avoid + a reload of the base register for that label. + + Note that the instruciton 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 needed 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; \ +} + +void +i370_label_scan (void) +{ + 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); + int labelno; + + /* 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++) + { + int labelno; + 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); +// abort(); + for (j=0; j < XVECLEN (body, 0); j++) + { + int labelno; + } + /* finished with the vector go do next insn */ + continue; + } + else + { +/* The following appears during make of _eh in libgcc2.a + while not obviously wrong, its weird, so not obviously + right either ... + (jump_insn:HI 125 124 126 (set (pc) + (mem:SI (plus:SI (reg/v:SI 1 r1) + (const_int 4)))) 144 {indirect_jump} (nil) + */ + debug_rtx (insn); +// abort(); + continue; + } + } + + /* 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. */ + 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 ... + */ int check_label_emit (void) { - if (mvs_label_emitted) + if (mvs_need_base_reload) { - mvs_label_emitted = 0; + 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, @@ -264,12 +568,19 @@ check_label_emit (void) allocated from memory. ID is the label number of the label being added to the list. */ -int -mvs_add_label (id) +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; @@ -277,21 +588,70 @@ mvs_add_label (id) } else { - lp = (label_node_t *) malloc (sizeof (label_node_t)); - if (lp == 0) - { - fatal ("virtual memory exhausted\n"); - abort (); - } + lp = (label_node_t *) xmalloc (sizeof (label_node_t)); } + + /* initialize for new label */ lp->label_id = id; - lp->label_page = mvs_page_num; + 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; } -/* Check to see if the label is in the list. If 1 is returned then a load - and branch on register must be generated. +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 @@ -302,27 +662,55 @@ mvs_check_label (id) for (lp = label_anchor; lp; lp = lp->label_next) { - if (lp->label_id == id) - return 1; + 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. */ + +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; +} + /* The label list for the current page freed by linking the list onto the free label element chain. */ -int -mvs_free_label (void) +void +mvs_free_label_list (void) { + if (label_anchor) { - if (free_anchor) - label_anchor->label_next = free_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. @@ -331,6 +719,7 @@ mvs_free_label (void) 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; @@ -348,10 +737,11 @@ mvs_check_page (file, code, lit) fprintf (assembler_source, "PGE%d\tEQU\t*\n", mvs_page_num); fprintf (assembler_source, "\tDROP\t%d\n", BASE_REGISTER); mvs_page_num++; - fprintf (assembler_source, "\tBALR\t%d,0\n", BASE_REGISTER); + /* 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_free_label (); mvs_page_code = code; mvs_page_lit = lit; return 1; @@ -360,6 +750,53 @@ mvs_check_page (file, code, lit) 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. @@ -389,6 +826,253 @@ mvs_function_check (name) } +/* Add the alias to the current alias list. */ + +int +mvs_add_alias (realname, aliasname, emitted) + char *realname; + char *aliasname; + int emitted; +{ + alias_node_t *ap; + + ap = (alias_node_t *) xmalloc (sizeof (alias_node_t)); + 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 */ + +int +mvs_need_alias (realname) + char *realname; +{ + if (mvs_function_check (realname)) + return 0; + if (strlen (realname) > MAX_MVS_LABEL_SIZE) + return 1; + if (strchr (realname, '_') != 0) + 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) + 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)) + { + sprintf (aliasname, "ALS%05d", alias_number++); + 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) + 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)) + { + sprintf (aliasname, "ALS%05d", alias_number++); + 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; +} + +/* Called from check_newline via the macro HANDLE_PRAGMA. + FINPUT is the source file input stream. + NODE is the tree node for the token after the "pragma". + The result is 1 if the pragma was handled. */ + +int +handle_pragma (finput, node) + FILE *finput; + tree node; +{ + int retval = 0; + register int c; + register char *pname; + + if (TREE_CODE (node) != IDENTIFIER_NODE) + return 0; + + pname = IDENTIFIER_POINTER (node); + + if (strcmp (pname, "map") == 0) + { + char realname[MAX_LONG_LABEL_SIZE + 1]; + char aliasname[MAX_MVS_LABEL_SIZE + 1]; + char *s; + + do { + c = getc (finput); + } while (c == ' ' || c == '\t'); + + if (c == '(') + { + s = realname; + do { + c = getc (finput); + } while (c == ' ' || c == '\t'); + if (c == '\n') + goto PRAGMA_WARNING; + do { + *s++ = c; + c = getc (finput); + } while (isalnum(c) || c == '_'); + if (c == '\n') + goto PRAGMA_WARNING; + *s = 0; + + if (c == ' ' || c == '\t') + do { + c = getc (finput); + } while (c == ' ' || c == '\t'); + + if (c == ',') + { + do { + c = getc (finput); + } while (c == ' ' || c == '\t'); + if (c == '"') + { + s = aliasname; + c = getc(finput); + do { + if (c == '\\') + { + int d = 0; + do { + c = getc(finput); + if (c >= '0' && c <= '7') + d = (d << 3) | c - '0'; + } while (c >= '0' && c <= '7'); + ungetc (c, finput); + c = d; + if (d < 1 || d > 255) + warning ("Escape value out of range"); +#ifndef HOST_EBCDIC + c = ebcasc[c]; +#endif + } + *s++ = c; + c = getc (finput); + if (isspace(c) || c == ')') + goto PRAGMA_WARNING; + } while (c != '"'); + *s = 0; + if (strlen (aliasname) > MAX_MVS_LABEL_SIZE) + { + warning ("#pragma map alias is too long, truncated"); + aliasname[MAX_MVS_LABEL_SIZE] = '\0'; + } + do { + c = getc (finput); + } while (c == ' ' || c == '\t'); + if (c == ')') + { + mvs_add_alias (realname, aliasname, 1); + retval = 1; + } + else + goto PRAGMA_WARNING; + } + else + goto PRAGMA_WARNING; + } + else + goto PRAGMA_WARNING; + + } + else + { + PRAGMA_WARNING: + warning ("#pragma map options are missing or incorrect"); + } + + } + + return retval; +} + +/* 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) + char *name; +{ + 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. */ @@ -460,31 +1144,57 @@ r_or_s_operand (op, mode) } -/* Return 1 if the next instruction is an unsigned jump instruction. - INSN is the current instruction. */ +/* 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. */ unsigned_jump_follows_p (insn) register rtx insn; { - insn = NEXT_INSN (insn); - if (GET_CODE (insn) != JUMP_INSN) - return 0; - - insn = XEXP (insn, 3); - if (GET_CODE (insn) != SET) - return 0; + 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; + } +} - if (GET_CODE (XEXP (insn, 0)) != PC) - return 0; - insn = XEXP (insn, 1); - if (GET_CODE (insn) != IF_THEN_ELSE) - return 0; - - insn = XEXP (insn, 0); - return GET_CODE (insn) != GE && GET_CODE (insn) != GT - && GET_CODE (insn) != LE && GET_CODE (insn) != LT; -} +#ifdef TARGET_HLASM void i370_function_prolog (f, l) @@ -492,22 +1202,85 @@ i370_function_prolog (f, l) 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); - 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); - mvs_page_code = 6; - mvs_page_lit = 4; - mvs_check_page (f, 0, 0); - function_base_page = mvs_page_num; #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; int i; +#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(%d)\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, function_second); + 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; @@ -530,26 +1303,27 @@ i370_function_prolog (f, l) function_hour, function_minute, function_second); fprintf (f, "\tDC\tCL2'01',CL4'0100'\n"); } - fprintf (f, "$DSD%03d\tDSECT\n", function_label_index); + 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\t$DSD%03d\n", function_label_index); + 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, "$DSL%03d\tEQU\t*-$DSD%03d-8\n", function_label_index, + 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, "\tEQU\t*\n"); + 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($DSL%03d)\n", function_label_index); + 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); @@ -570,15 +1344,149 @@ i370_function_prolog (f, l) fprintf (f, "\tLR\t13,2\n"); fprintf (f, "\tDROP\t15\n"); fprintf (f, "\tBALR\t%d,0\n", BASE_REGISTER); - fprintf (f, "PG%d\tEQU\t*\n", mvs_page_num ); fprintf (f, "\tUSING\t*,%d\n", BASE_REGISTER); - fprintf (f, "\tLR\t11,1\n"); + 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); - mvs_page_code = 4; + 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 (); +} +#endif /* TARGET_HLASM */ + + +#ifdef TARGET_ELF_ABI +/* + The 370_function_prolog() routine generates the current ELF ABI ES/390 prolog. + It performs the following steps: + -- saves the callers non-volatile registers on the callers stack. + -- computes a new stack top and checks for room for the stack. + -- initializes size and backpointer of new stack frame + -- updates stack pointer to point at new frame. + + XXX hack alert -- if the global var int leaf_function is non-zero, + 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. + */ + +void +i370_function_prolog (f, frame_size) + FILE *f; + int frame_size; +{ + static int function_label_index = 1; + static int function_first = 0; + int i; + int stackframe_size, soffset, 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=0x%x 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, ".LFENT%03d:\n\t.balign 2\n", /* FENT%03d DS 0H */ + function_label_index); + + /* store multiple of registers 14,15,0,...12 at 12 bytes from sp */ + fprintf (f, "\tSTM\tr14,r12,12(sp)\n"); + + /* r11 points to arg list in callers stackframe; was passed in r2 */ + fprintf (f, "\tLR\tr11,r2\n"); + + /* r2 == callee stack pointer ; 76(sp) == caller top of stack */ + fprintf (f, "\tL\tr2,76(,sp)\n"); + + /* 4(r15) == callee stack length */ + fprintf (f, "\tL\tr0,4(,r15)\n"); + + /* add callee stack length to caller top of stack */ + fprintf (f, "\tALR\tr0,r2\n"); + + /* is there enough room for this new stack frame? */ + fprintf (f, "\tCL\tr0,12(,rtca)\n"); + + /* if we've got room, skip next 2 insns */ + fprintf (f, "\tBNH\t*+10\n"); + + /* branch to tca to get more stack */ + fprintf (f, "\tL\tr15,116(,rtca)\n"); + + /* go */ + fprintf (f, "\tBASR\tr14,r15\n"); + + /* 72(sp) is something that is propagated up from the base of the stack. + We don't use this anywhere, so we could chop this out. For the moment, + Lets keep it; it might be handy someday ... */ + fprintf (f, "\tL\tr15,72(,sp)\n"); + + /* store the new top-of-stack at 76(callee_stack) */ + fprintf (f, "\tSTM\tr15,r0,72(r2)\n"); + + /* store some PL/1 compatible eyecatcher ???? why bother ??? */ + fprintf (f, "\tMVI\t0(r2),0x10\n"); + + /* store callee stack pointer at 8(sp) */ + fprintf (f, "\tST\tr2,8(,sp)\n "); + + /* store caller sp at 4(callee_sp) */ + fprintf (f, "\tST\tsp,4(,r2)\n "); + + /* load calle_sp into sp */ + fprintf (f, "\tLR\tsp,r2\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 += 2; -#endif /* MACROPROLOGUE */ + 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 */ |