mirror of git://gcc.gnu.org/git/gcc.git
1662 lines
51 KiB
C
1662 lines
51 KiB
C
/* 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 GNU CC.
|
||
|
||
GNU CC 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.
|
||
|
||
GNU CC 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 GNU CC; 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 PARAMS ((int));
|
||
static void i370_label_scan PARAMS ((void));
|
||
#ifdef TARGET_HLASM
|
||
static bool i370_hlasm_assemble_integer PARAMS ((rtx, unsigned int, int));
|
||
static void i370_globalize_label PARAMS ((FILE *, const char *));
|
||
#endif
|
||
static void i370_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));
|
||
static void i370_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
|
||
static void i370_file_start PARAMS ((void));
|
||
static void i370_file_end PARAMS ((void));
|
||
|
||
#ifdef LONGEXTERNAL
|
||
static int mvs_hash_alias PARAMS ((const char *));
|
||
#endif
|
||
static void i370_internal_label PARAMS ((FILE *, const char *, unsigned long));
|
||
static bool i370_rtx_costs PARAMS ((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 */
|
||
/* ===================================================== */
|
||
|
||
#if defined(TARGET_EBCDIC) && HOST_CHARSET == HOST_CHARSET_ASCII
|
||
/* ASCII to EBCDIC conversion table. */
|
||
static const unsigned char ascebc[256] =
|
||
{
|
||
/*00 NL SH SX EX ET NQ AK BL */
|
||
0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F,
|
||
/*08 BS HT LF VT FF CR SO SI */
|
||
0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||
/*10 DL D1 D2 D3 D4 NK SN EB */
|
||
0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26,
|
||
/*18 CN EM SB EC FS GS RS US */
|
||
0x18, 0x19, 0x3F, 0x27, 0x1C, 0x1D, 0x1E, 0x1F,
|
||
/*20 SP ! " # $ % & ' */
|
||
0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D,
|
||
/*28 ( ) * + , - . / */
|
||
0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61,
|
||
/*30 0 1 2 3 4 5 6 7 */
|
||
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
|
||
/*38 8 9 : ; < = > ? */
|
||
0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F,
|
||
/*40 @ A B C D E F G */
|
||
0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
|
||
/*48 H I J K L M N O */
|
||
0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6,
|
||
/*50 P Q R S T U V W */
|
||
0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6,
|
||
/*58 X Y Z [ \ ] ^ _ */
|
||
0xE7, 0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0x5F, 0x6D,
|
||
/*60 ` a b c d e f g */
|
||
0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
||
/*68 h i j k l m n o */
|
||
0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
|
||
/*70 p q r s t u v w */
|
||
0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6,
|
||
/*78 x y z { | } ~ DL */
|
||
0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07,
|
||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0xFF
|
||
};
|
||
#endif /* target EBCDIC, host ASCII */
|
||
|
||
#if !defined(TARGET_EBCDIC) && HOST_CHARSET == HOST_CHARSET_EBCDIC
|
||
/* EBCDIC to ASCII conversion table. */
|
||
static const unsigned char ebcasc[256] =
|
||
{
|
||
/*00 NU SH SX EX PF HT LC DL */
|
||
0x00, 0x01, 0x02, 0x03, 0x00, 0x09, 0x00, 0x7F,
|
||
/*08 SM VT FF CR SO SI */
|
||
0x00, 0x00, 0x00, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||
/*10 DE D1 D2 TM RS NL BS IL */
|
||
0x10, 0x11, 0x12, 0x13, 0x14, 0x0A, 0x08, 0x00,
|
||
/*18 CN EM CC C1 FS GS RS US */
|
||
0x18, 0x19, 0x00, 0x00, 0x1C, 0x1D, 0x1E, 0x1F,
|
||
/*20 DS SS FS BP LF EB EC */
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x17, 0x1B,
|
||
/*28 SM C2 EQ AK BL */
|
||
0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0x07, 0x00,
|
||
/*30 SY PN RS UC ET */
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
|
||
/*38 C3 D4 NK SU */
|
||
0x00, 0x00, 0x00, 0x00, 0x14, 0x15, 0x00, 0x1A,
|
||
/*40 SP */
|
||
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
/*48 . < ( + | */
|
||
0x00, 0x00, 0x00, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
|
||
/*50 & */
|
||
0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
/*58 ! $ * ) ; ^ */
|
||
0x00, 0x00, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0x5E,
|
||
/*60 - / */
|
||
0x2D, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
/*68 , % _ > ? */
|
||
0x00, 0x00, 0x00, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
|
||
/*70 */
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
/*78 ` : # @ ' = " */
|
||
0x00, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
|
||
/*80 a b c d e f g */
|
||
0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
|
||
/*88 h i { */
|
||
0x68, 0x69, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x00,
|
||
/*90 j k l m n o p */
|
||
0x00, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
|
||
/*98 q r } */
|
||
0x71, 0x72, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x00,
|
||
/*A0 ~ s t u v w x */
|
||
0x00, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
||
/*A8 y z [ */
|
||
0x79, 0x7A, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00,
|
||
/*B0 */
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
/*B8 ] */
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00,
|
||
/*C0 { A B C D E F G */
|
||
0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
|
||
/*C8 H I */
|
||
0x48, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
/*D0 } J K L M N O P */
|
||
0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
|
||
/*D8 Q R */
|
||
0x51, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
/*E0 \ S T U V W X */
|
||
0x5C, 0x00, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
||
/*E8 Y Z */
|
||
0x59, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
/*F0 0 1 2 3 4 5 6 7 */
|
||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
||
/*F8 8 9 */
|
||
0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF
|
||
};
|
||
#endif /* target ASCII, host EBCDIC */
|
||
|
||
/* 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_format_for_mode[SFmode - QFmode] = &i370_single_format;
|
||
real_format_for_mode[DFmode - QFmode] = &i370_double_format;
|
||
}
|
||
|
||
|
||
/* Map characters from one character set to another.
|
||
C is the character to be translated. */
|
||
|
||
char
|
||
mvs_map_char (c)
|
||
int c;
|
||
{
|
||
#if defined(TARGET_EBCDIC) && HOST_CHARSET == HOST_CHARSET_ASCII
|
||
fprintf (stderr, "mvs_map_char: TE & !HE: c = %02x\n", c);
|
||
return ascebc[c];
|
||
#else
|
||
#if !defined(TARGET_EBCDIC) && HOST_CHARSET == HOST_CHARSET_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, 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;
|
||
}
|
||
}
|